diff --git a/.gitignore b/.gitignore index e3ccf5e..f53d8fa 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,10 @@ *.so *.dylib tigrisdb-cli +tigris + +# JSON files +*.json # Test binary, built with `go test -c` *.test diff --git a/README.md b/README.md index 09016d4..826c2eb 100644 --- a/README.md +++ b/README.md @@ -5,20 +5,32 @@ # Install +## macOS + ```sh -go install github.com/tigrisdata/tigrisdb-cli@latest +curl -sSL https://tigris.dev/cli-macos | sudo tar -xz -C /usr/local/bin +``` + +## Linux + +```sh +curl -sSL https://tigris.dev/cli-linux | sudo tar -xz -C /usr/local/bin ``` # Example ```sh +tigris db local up # brings local TigrisDB up on localhost:8081 + tigris db create database db1 -tigris db create collection db1 \ - '{ "name" : "coll1", "properties": { - "Key1": { "type": "string" }, - "Field1": { "type": "int" } }, - "primary_key": ["Key1"] - }' +tigris db create collection db1 '{ + "name" : "coll1", + "properties": { + "Key1": { "type": "string" }, + "Field1": { "type": "integer" } + }, + "primary_key": ["Key1"] +}' tigris db list databases tigris db list collections db1 @@ -52,6 +64,8 @@ tigris db read "db1" "coll1" '{}' tigris db drop collection db1 coll1 tigris db drop database db1 + +tigris db local down ``` # License diff --git a/cmd/collection.go b/cmd/collection.go index 49b86d4..cd3e992 100644 --- a/cmd/collection.go +++ b/cmd/collection.go @@ -22,12 +22,13 @@ import ( "github.com/spf13/cobra" "github.com/tigrisdata/tigrisdb-cli/client" "github.com/tigrisdata/tigrisdb-cli/util" + api "github.com/tigrisdata/tigrisdb-client-go/api/server/v1" "github.com/tigrisdata/tigrisdb-client-go/driver" ) func createCollection(ctx context.Context, tx driver.Tx, raw driver.Schema) { type Schema struct { - Name string + Name string `json:"title"` } var schema Schema if err := json.Unmarshal(raw, &schema); err != nil { @@ -36,12 +37,46 @@ func createCollection(ctx context.Context, tx driver.Tx, raw driver.Schema) { if schema.Name == "" { util.Error(fmt.Errorf("schema name is missing"), "create collection failed") } - err := tx.CreateOrUpdateCollection(ctx, schema.Name, driver.Schema(raw)) + err := tx.CreateOrUpdateCollection(ctx, schema.Name, raw) if err != nil { util.Error(err, "create collection failed") } } +type DescribeCollectionResponse struct { + Collection string `json:"collection,omitempty"` + Metadata *api.CollectionMetadata `json:"metadata,omitempty"` + Schema json.RawMessage `json:"schema,omitempty"` +} + +var describeCollectionCmd = &cobra.Command{ + Use: "collection {db} {collection}", + Short: "describe collection", + Long: "describe collection returns collection metadata, including schema", + Args: cobra.MinimumNArgs(2), + Run: func(cmd *cobra.Command, args []string) { + ctx, cancel := util.GetContext(cmd.Context()) + defer cancel() + resp, err := client.Get().DescribeCollection(ctx, args[0], args[1]) + if err != nil { + util.Error(err, "describe collection failed") + } + + tr := DescribeCollectionResponse{ + Collection: resp.Collection, + Metadata: resp.Metadata, + Schema: resp.Schema, + } + + b, err := json.Marshal(tr) + if err != nil { + util.Error(err, "describe collection failed") + } + + util.Stdout("%s\n", string(b)) + }, +} + var listCollectionsCmd = &cobra.Command{ Use: "collections {db}", Short: "list database collections", @@ -62,10 +97,10 @@ var listCollectionsCmd = &cobra.Command{ var createCollectionCmd = &cobra.Command{ Use: "collection {db} {schema}...|-", Short: "create collection(s)", - Args: cobra.MinimumNArgs(2), + Args: cobra.MinimumNArgs(1), Run: func(cmd *cobra.Command, args []string) { client.Transact(cmd.Context(), args[0], func(ctx context.Context, tx driver.Tx) { - iterateInput(ctx, 1, args, func(ctx context.Context, args []string, docs []json.RawMessage) { + iterateInput(ctx, cmd, 1, args, func(ctx context.Context, args []string, docs []json.RawMessage) { for _, v := range docs { createCollection(ctx, tx, driver.Schema(v)) } @@ -80,7 +115,7 @@ var dropCollectionCmd = &cobra.Command{ Args: cobra.MinimumNArgs(2), Run: func(cmd *cobra.Command, args []string) { client.Transact(cmd.Context(), args[0], func(ctx context.Context, tx driver.Tx) { - iterateInput(ctx, 1, args, func(ctx context.Context, args []string, docs []json.RawMessage) { + iterateInput(ctx, cmd, 1, args, func(ctx context.Context, args []string, docs []json.RawMessage) { for _, v := range docs { err := tx.DropCollection(ctx, string(v)) if err != nil { @@ -95,10 +130,10 @@ var dropCollectionCmd = &cobra.Command{ var alterCollectionCmd = &cobra.Command{ Use: "collection {db} {collection} {schema}", Short: "update collection schema", - Args: cobra.MinimumNArgs(3), + Args: cobra.MinimumNArgs(1), Run: func(cmd *cobra.Command, args []string) { client.Transact(cmd.Context(), args[0], func(ctx context.Context, tx driver.Tx) { - iterateInput(ctx, 1, args, func(ctx context.Context, args []string, docs []json.RawMessage) { + iterateInput(ctx, cmd, 1, args, func(ctx context.Context, args []string, docs []json.RawMessage) { for _, v := range docs { createCollection(ctx, tx, driver.Schema(v)) } @@ -112,4 +147,5 @@ func init() { createCmd.AddCommand(createCollectionCmd) listCmd.AddCommand(listCollectionsCmd) alterCmd.AddCommand(alterCollectionCmd) + describeCmd.AddCommand(describeCollectionCmd) } diff --git a/cmd/database.go b/cmd/database.go index 873ca37..eea0c80 100644 --- a/cmd/database.go +++ b/cmd/database.go @@ -15,9 +15,12 @@ package cmd import ( + "encoding/json" + "github.com/spf13/cobra" "github.com/tigrisdata/tigrisdb-cli/client" "github.com/tigrisdata/tigrisdb-cli/util" + api "github.com/tigrisdata/tigrisdb-client-go/api/server/v1" ) var listDatabasesCmd = &cobra.Command{ @@ -36,10 +39,62 @@ var listDatabasesCmd = &cobra.Command{ }, } +type DescribeDatabaseResponse struct { + Db string `json:"db,omitempty"` + Metadata *api.DatabaseMetadata `json:"metadata,omitempty"` + Collections []*DescribeCollectionResponse `json:"collections,omitempty"` +} + +var describeDatabaseCmd = &cobra.Command{ + Use: "database {db}", + Short: "describe database", + Long: "describe database returns metadata for all the collections in the database", + Args: cobra.ExactArgs(1), + Run: func(cmd *cobra.Command, args []string) { + ctx, cancel := util.GetContext(cmd.Context()) + defer cancel() + resp, err := client.Get().DescribeDatabase(ctx, args[0]) + if err != nil { + util.Error(err, "describe collection failed") + } + + schemaOnly, err := cmd.Flags().GetBool("schema-only") + if err != nil { + util.Error(err, "error reading the 'schema-only' option") + } + + if schemaOnly { + for _, v := range resp.Collections { + util.Stdout("%s\n", string(v.Schema)) + } + } else { + tr := DescribeDatabaseResponse{ + Db: resp.Db, + Metadata: resp.Metadata, + } + + for _, v := range resp.Collections { + tr.Collections = append(tr.Collections, &DescribeCollectionResponse{ + Collection: v.Collection, + Metadata: v.Metadata, + Schema: v.Schema, + }) + } + + b, err := json.Marshal(tr) + if err != nil { + util.Error(err, "describe database failed") + } + + util.Stdout("%s\n", string(b)) + } + }, +} + var createDatabaseCmd = &cobra.Command{ Use: "database {db}", Short: "create database", - Args: cobra.MinimumNArgs(1), + Args: cobra.ExactArgs(1), Run: func(cmd *cobra.Command, args []string) { ctx, cancel := util.GetContext(cmd.Context()) defer cancel() @@ -53,7 +108,7 @@ var createDatabaseCmd = &cobra.Command{ var dropDatabaseCmd = &cobra.Command{ Use: "database {db}", Short: "drop database", - Args: cobra.MinimumNArgs(1), + Args: cobra.ExactArgs(1), Run: func(cmd *cobra.Command, args []string) { ctx, cancel := util.GetContext(cmd.Context()) defer cancel() @@ -65,7 +120,9 @@ var dropDatabaseCmd = &cobra.Command{ } func init() { + describeDatabaseCmd.Flags().BoolP("schema-only", "s", false, "dump only schema of all database collections") dropCmd.AddCommand(dropDatabaseCmd) createCmd.AddCommand(createDatabaseCmd) listCmd.AddCommand(listDatabasesCmd) + describeCmd.AddCommand(describeDatabaseCmd) } diff --git a/cmd/db.go b/cmd/describe.go similarity index 77% rename from cmd/db.go rename to cmd/describe.go index 47c36c8..eb78980 100644 --- a/cmd/db.go +++ b/cmd/describe.go @@ -18,13 +18,12 @@ import ( "github.com/spf13/cobra" ) -var dbCmd = &cobra.Command{ - Use: "db", - Short: "Database related commands", - Long: `Database related commands. -Inluding database, collection and documents management`, +var describeCmd = &cobra.Command{ + Use: "describe", + Short: "describe database or collection", + Args: cobra.MinimumNArgs(1), } func init() { - rootCmd.AddCommand(dbCmd) + dbCmd.AddCommand(describeCmd) } diff --git a/cmd/generate.go b/cmd/generate.go new file mode 100644 index 0000000..395dffe --- /dev/null +++ b/cmd/generate.go @@ -0,0 +1,177 @@ +package cmd + +import ( + "context" + "fmt" + "io/ioutil" + + "github.com/spf13/cobra" + "github.com/tigrisdata/tigrisdb-cli/client" + "github.com/tigrisdata/tigrisdb-cli/util" + "github.com/tigrisdata/tigrisdb-client-go/driver" +) + +const sampleDBName = "sampledb" + +var schemas = map[string][]byte{ + "products": []byte(`{ + "title": "products", + "description": "Collection of documents with details of products available", + "properties": { + "id": { + "description": "A unique identifier for the product", + "type": "integer" + }, + "name": { + "description": "Name of the product", + "type": "string", + "maxLength": 100 + }, + "quantity": { + "description": "Number of products available in the store", + "type": "integer" + }, + "price": { + "description": "Price of the product", + "type": "number" + } + }, + "primary_key": ["id"] +}`), + "users": []byte(`{ + "title": "users", + "description": "Collection of documents with details of users", + "properties": { + "id": { + "description": "A unique identifier for the user", + "type": "integer" + }, + "name": { + "description": "Name of the user", + "type": "string", + "maxLength": 100 + }, + "balance": { + "description": "User account balance", + "type": "number" + }, + "languages": { + "description": "Languages spoken by the user", + "type": "array", + "items": { + "type": "string" + } + }, + "address": { + "description": "Street address of the user", + "type": "object", + "properties": { + "street": { + "description": "Street number", + "type": "string" + }, + "city": { + "description": "Name of the city", + "type": "string" + }, + "state": { + "description": "Name of the state", + "type": "string" + }, + "zip": { + "description": "The zip code", + "type": "integer" + } + } + } + }, + "primary_key": ["id"] +}`), + "orders": []byte(`{ + "title": "orders", + "description": "Collection of documents with details of an order", + "properties": { + "id": { + "description": "A unique identifier for the order", + "type": "integer" + }, + "user_id": { + "description": "The identifier of the user that placed the order", + "type": "integer" + }, + "order_total": { + "description": "The total cost of the order", + "type": "number" + }, + "products": { + "description": "The list of products that are part of this order", + "type": "array", + "items": { + "type": "object", + "name": "product_item", + "properties": { + "id": { + "description": "The product identifier", + "type": "integer" + }, + "quantity": { + "description": "The quantity of this product in this order", + "type": "integer" + } + } + } + } + }, + "primary_key": ["id"] +}`), +} + +var sampleSchemaCmd = &cobra.Command{ + Use: "sample-schema", + Short: "Generate sample schema consisting of three collections: products, users, orders", + Run: func(cmd *cobra.Command, args []string) { + create, err := cmd.Flags().GetBool("create") + if err != nil { + util.Error(err, "error reading the 'create' option") + } + + if create { + if err := client.Get().CreateDatabase(cmd.Context(), sampleDBName); err != nil { + util.Error(err, "create database failed") + } + client.Transact(cmd.Context(), sampleDBName, func(ctx context.Context, tx driver.Tx) { + for _, schema := range schemas { + createCollection(ctx, tx, schema) + } + }) + + util.Stdout("%v created with the collections", sampleDBName) + } else { + stdout, err := cmd.Flags().GetBool("stdout") + if err != nil { + util.Error(err, "error reading the 'stdout' option") + } + for name, schema := range schemas { + if stdout { + util.Stdout("%s\n", string(schema)) + } else { + if err := ioutil.WriteFile(fmt.Sprintf("%v.json", name), schema, 0644); err != nil { + util.Error(err, "error generating sample schema file") + } + } + } + } + }, +} + +var generateCmd = &cobra.Command{ + Use: "generate", + Short: "Generating helper assets such as sample schema", +} + +func init() { + sampleSchemaCmd.Flags().BoolP("create", "c", false, "create the sample database and collections") + sampleSchemaCmd.Flags().BoolP("stdout", "s", false, "dump sample schemas to stdout") + generateCmd.AddCommand(sampleSchemaCmd) + dbCmd.AddCommand(generateCmd) +} diff --git a/cmd/insert.go b/cmd/insert.go index db909b9..c52e631 100644 --- a/cmd/insert.go +++ b/cmd/insert.go @@ -26,13 +26,13 @@ import ( ) var insertCmd = &cobra.Command{ - Use: "insert {db} {collection} {document}...|{-}", + Use: "insert {db} {collection} {document}...|-", Short: "insert document", Long: `insert one or multiple documents from command line or standard input`, - Args: cobra.MinimumNArgs(3), + Args: cobra.MinimumNArgs(2), Run: func(cmd *cobra.Command, args []string) { - iterateInput(cmd.Context(), 2, args, func(ctx context.Context, args []string, docs []json.RawMessage) { + iterateInput(cmd.Context(), cmd, 2, args, func(ctx context.Context, args []string, docs []json.RawMessage) { ptr := unsafe.Pointer(&docs) _, err := client.Get().Insert(ctx, args[0], args[1], *(*[]driver.Document)(ptr)) if err != nil { diff --git a/cmd/iterate.go b/cmd/iterate.go index 295ae55..4c392a4 100644 --- a/cmd/iterate.go +++ b/cmd/iterate.go @@ -19,11 +19,13 @@ import ( "bytes" "context" "encoding/json" + "fmt" "io" "io/ioutil" "os" "unicode" + "github.com/spf13/cobra" "github.com/tigrisdata/tigrisdb-cli/util" ) @@ -65,12 +67,17 @@ func iterateArray(r []byte) []json.RawMessage { } func iterateStream(ctx context.Context, args []string, r io.Reader, fn func(ctx2 context.Context, args []string, docs []json.RawMessage)) { - s := bufio.NewScanner(r) + dec := json.NewDecoder(r) for { docs := make([]json.RawMessage, 0, BatchSize) var i int32 - for ; i < BatchSize && s.Scan(); i++ { - docs = append(docs, s.Bytes()) + for ; i < BatchSize && dec.More(); i++ { + var v json.RawMessage + err := dec.Decode(&v) + if err != nil { + util.Error(err, "reading documents from stream of documents") + } + docs = append(docs, v) } if i > 0 { fn(ctx, args, docs) @@ -78,15 +85,26 @@ func iterateStream(ctx context.Context, args []string, r io.Reader, fn func(ctx2 break } } - if err := s.Err(); err != nil { - util.Error(err, "reading documents from stdin") - } } // iterateInput reads repeated command parameters from standard input or args // Support newline delimited stream of objects and arrays of objects -func iterateInput(ctx context.Context, docsPosition int, args []string, fn func(ctx2 context.Context, args []string, docs []json.RawMessage)) { - if args[docsPosition] == "-" { +func iterateInput(ctx context.Context, cmd *cobra.Command, docsPosition int, args []string, fn func(ctx2 context.Context, args []string, docs []json.RawMessage)) { + if len(args) > docsPosition && args[docsPosition] != "-" { + docs := make([]json.RawMessage, 0, len(args)) + for _, v := range args[docsPosition:] { + if detectArray(bufio.NewReader(bytes.NewReader([]byte(v)))) { + docs = append(docs, iterateArray([]byte(v))...) + } else { + docs = append(docs, json.RawMessage(v)) + } + } + fn(ctx, args, docs) + } else if len(args) <= docsPosition && util.IsTTY(os.Stdin) { + fmt.Fprintf(os.Stderr, "not enougn arguments\n") + _ = cmd.Usage() + os.Exit(1) + } else { r := bufio.NewReader(os.Stdin) if detectArray(r) { buf, err := ioutil.ReadAll(r) @@ -98,15 +116,5 @@ func iterateInput(ctx context.Context, docsPosition int, args []string, fn func( } else { iterateStream(ctx, args, r, fn) } - } else { - docs := make([]json.RawMessage, 0, len(args)) - for _, v := range args[docsPosition:] { - if detectArray(bufio.NewReader(bytes.NewReader([]byte(v)))) { - docs = append(docs, iterateArray([]byte(v))...) - } else { - docs = append(docs, json.RawMessage(v)) - } - } - fn(ctx, args, docs) } } diff --git a/cmd/local.go b/cmd/local.go index 9f89176..d2454bf 100644 --- a/cmd/local.go +++ b/cmd/local.go @@ -45,7 +45,7 @@ const ( ContainerName = "tigrisdb-local-server" ) -var ImageTag = "1.0.0-alpha.7" +var ImageTag = "latest" func ensureVolume(cli *client.Client) { ctx := context.Background() @@ -266,7 +266,7 @@ var serverLogsCmd = &cobra.Command{ follow, err := cmd.Flags().GetBool("follow") if err != nil { - util.Error(err, "error getting 'follow' flag") + util.Error(err, "error reading 'follow' option") } logs, err := cli.ContainerLogs(ctx, ContainerName, types.ContainerLogsOptions{ diff --git a/cmd/read.go b/cmd/read.go index 7f707a3..5b51b29 100644 --- a/cmd/read.go +++ b/cmd/read.go @@ -40,7 +40,7 @@ if fields is not provided or has special {} value, read returns all the fields o if len(args) > 3 { fields = args[3] } - it, err := client.Get().Read(ctx, args[0], args[1], driver.Filter(filter), driver.Fields(fields)) + it, err := client.Get().Read(ctx, args[0], args[1], driver.Filter(filter), driver.Projection(fields)) if err != nil { util.Error(err, "read documents failed") } diff --git a/cmd/replace.go b/cmd/replace.go index 226fbf9..4289d99 100644 --- a/cmd/replace.go +++ b/cmd/replace.go @@ -26,14 +26,14 @@ import ( ) var replaceCmd = &cobra.Command{ - Use: "replace {db} {collection} {document}...|{-}", + Use: "replace {db} {collection} {document}...|-", Aliases: []string{"insert_or_replace"}, Short: "replace document", Long: `replace or insert one or multiple documents from command line or standard input`, - Args: cobra.MinimumNArgs(3), + Args: cobra.MinimumNArgs(2), Run: func(cmd *cobra.Command, args []string) { - iterateInput(cmd.Context(), 2, args, func(ctx context.Context, args []string, docs []json.RawMessage) { + iterateInput(cmd.Context(), cmd, 2, args, func(ctx context.Context, args []string, docs []json.RawMessage) { ptr := unsafe.Pointer(&docs) _, err := client.Get().Replace(ctx, args[0], args[1], *(*[]driver.Document)(ptr)) if err != nil { diff --git a/cmd/root.go b/cmd/root.go index d40dd20..adb4913 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -25,6 +25,8 @@ var rootCmd = &cobra.Command{ Short: "tigris is a command line interface of TigrisDB database", } +var dbCmd = rootCmd + func Execute() { err := rootCmd.Execute() if err != nil { diff --git a/cmd/transact.go b/cmd/transact.go index 794f65d..86a8c12 100644 --- a/cmd/transact.go +++ b/cmd/transact.go @@ -70,7 +70,7 @@ func execTxOp(ctx context.Context, db string, tp string, op *Op) { ptr := unsafe.Pointer(&op.Documents) _, err = client.Get().Insert(ctx, db, op.Collection, *(*[]driver.Document)(ptr)) case Update: - _, err = client.Get().Update(ctx, db, op.Collection, driver.Filter(op.Filter), driver.Fields(op.Fields)) + _, err = client.Get().Update(ctx, db, op.Collection, driver.Filter(op.Filter), driver.Update(op.Fields)) case Delete: _, err = client.Get().Delete(ctx, db, op.Collection, driver.Filter(op.Filter)) case Replace, InsertOrReplace: @@ -98,7 +98,7 @@ func execTxOp(ctx context.Context, db string, tp string, op *Op) { if len(op.Fields) > 0 { fields = op.Fields } - it, err := client.Get().Read(ctx, db, op.Collection, driver.Filter(filter), driver.Fields(fields)) + it, err := client.Get().Read(ctx, db, op.Collection, driver.Filter(filter), driver.Projection(fields)) if err != nil { log.Fatal().Err(err).Str("op", op.Operation).Msgf("transact operation failed") } @@ -116,16 +116,16 @@ func execTxOp(ctx context.Context, db string, tp string, op *Op) { } var transactCmd = &cobra.Command{ - Use: "transact {db} begin|commit|rollback|{operation}...|-", + Use: "transact {db} {operation}...|-", Aliases: []string{"tx"}, Short: "run a set of operations in a transaction", Long: `Run a set of operations in a transaction Operations can be provided in the command line or from standard input`, - Args: cobra.MinimumNArgs(2), + Args: cobra.MinimumNArgs(1), Run: func(cmd *cobra.Command, args []string) { db := args[0] client.Transact(cmd.Context(), db, func(ctx context.Context, tx driver.Tx) { - iterateInput(cmd.Context(), 1, args, func(ctx context.Context, args []string, ops []json.RawMessage) { + iterateInput(ctx, cmd, 1, args, func(ctx context.Context, args []string, ops []json.RawMessage) { for _, iop := range ops { var op TxOp if err := json.Unmarshal(iop, &op); err != nil { diff --git a/cmd/update.go b/cmd/update.go index ca9c535..4231f93 100644 --- a/cmd/update.go +++ b/cmd/update.go @@ -30,7 +30,7 @@ var updateCmd = &cobra.Command{ ctx, cancel := util.GetContext(cmd.Context()) defer cancel() - _, err := client.Get().Update(ctx, args[0], args[1], driver.Filter(args[2]), driver.Fields(args[3])) + _, err := client.Get().Update(ctx, args[0], args[1], driver.Filter(args[2]), driver.Update(args[3])) if err != nil { util.Error(err, "update documents failed") } diff --git a/cmd/version.go b/cmd/version.go index 448fc46..5b1548b 100644 --- a/cmd/version.go +++ b/cmd/version.go @@ -23,7 +23,7 @@ var versionCmd = &cobra.Command{ Use: "version", Short: "show tigrisdb-cli version", Run: func(cmd *cobra.Command, args []string) { - util.Stdout("tigrisdb-cli version %s\n", util.Version) + util.Stdout("tigris version %s\n", util.Version) }, } diff --git a/go.mod b/go.mod index a578a86..7135e3e 100644 --- a/go.mod +++ b/go.mod @@ -8,7 +8,7 @@ require ( github.com/rs/zerolog v1.26.1 github.com/spf13/cobra v1.4.0 github.com/spf13/viper v1.10.1 - github.com/tigrisdata/tigrisdb-client-go v1.0.0-alpha.4 + github.com/tigrisdata/tigrisdb-client-go v1.0.0-alpha.7 gopkg.in/yaml.v2 v2.4.0 ) diff --git a/go.sum b/go.sum index db24ef3..0a69c52 100644 --- a/go.sum +++ b/go.sum @@ -324,8 +324,8 @@ github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5Cc github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= -github.com/tigrisdata/tigrisdb-client-go v1.0.0-alpha.4 h1:bxmxXRVzsD9vGlFgfpMe1WtWB0wzUV2rE66ir7wacdY= -github.com/tigrisdata/tigrisdb-client-go v1.0.0-alpha.4/go.mod h1:lP4RhNml4kRHULYY+nb+nmvGz0THlwUx9w5BI7Qus7I= +github.com/tigrisdata/tigrisdb-client-go v1.0.0-alpha.7 h1:lt+eoxID4oluG+OMdI6sGAMIDFCFbMwrj7Fdqgqf7k8= +github.com/tigrisdata/tigrisdb-client-go v1.0.0-alpha.7/go.mod h1:lP4RhNml4kRHULYY+nb+nmvGz0THlwUx9w5BI7Qus7I= github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= github.com/ugorji/go v1.2.6/go.mod h1:anCg0y61KIhDlPZmnH+so+RQbysYVyDko0IMgJv0Nn0= github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= diff --git a/main.go b/main.go index 24dd46f..634f8ea 100644 --- a/main.go +++ b/main.go @@ -30,7 +30,7 @@ func main() { util.DefaultTimeout = config.DefaultConfig.Timeout - if len(os.Args) > 2 && (os.Args[1] == "db" && os.Args[2] != "local") { + if len(os.Args) > 1 && (os.Args[1] != "local") { if err := client.Init(config.DefaultConfig); err != nil { util.Error(err, "tigrisdb client initialization failed") } diff --git a/tests/db.sh b/tests/db.sh index 28f4287..c90b7a7 100644 --- a/tests/db.sh +++ b/tests/db.sh @@ -15,7 +15,7 @@ set -ex -cli="./tigris db" +cli="./tigris" make @@ -29,20 +29,32 @@ db_tests() { $cli create database db1 + coll1='{"title":"coll1","properties":{"Key1":{"type":"string"},"Field1":{"type":"integer"},"Field2":{"type":"integer"}},"primary_key":["Key1"]}' + coll111='{"title":"coll111","properties":{"Key1":{"type":"string"},"Field1":{"type":"integer"}},"primary_key":["Key1"]}' + #reading schemas from command line parameters - $cli create collection db1 \ - '{ "name" : "coll1", "properties": { "Key1": { "type": "string" }, "Field1": { "type": "integer" }, "Field2": { "type": "integer" } }, "primary_key": ["Key1"] }' \ - '{ "name" : "coll111", "properties": { "Key1": { "type": "string" }, "Field1": { "type": "integer" } }, "primary_key": ["Key1"] }' + $cli create collection db1 "$coll1" "$coll111" + + out=$($cli describe collection db1 coll1) + diff -w -u <(echo '{"collection":"coll1","metadata":{},"schema":'"$coll1"'}') <(echo "$out") + + out=$($cli describe database db1) + # The output order is not-deterministic, try both combinations: + diff -u <(echo '{"db":"db1","metadata":{},"collections":[{"collection":"coll1","metadata":{},"schema":'"$coll1"'},{"collection":"coll111","metadata":{},"schema":'"$coll111"'}]}') <(echo "$out") || + diff -u <(echo '{"db":"db1","metadata":{},"collections":[{"collection":"coll111","metadata":{},"schema":'"$coll111"'},{"collection":"coll1","metadata":{},"schema":'"$coll1"'}]}') <(echo "$out") #reading schemas from stream - echo '{ "name" : "coll2", "properties": { "Key1": { "type": "string" }, "Field1": { "type": "integer" }, "Field2": { "type": "integer" } }, "primary_key": ["Key1"] }' | $cli create collection db1 - + # \n at the end to test empty line skipping + # this also test multi-line streams + echo -e '{ "title" : "coll2", + "properties": { "Key1": { "type": "string" }, + "Field1": { "type": "integer" }, "Field2": { "type": "integer" } }, "primary_key": ["Key1"] }\n \n\n' | $cli create collection db1 - #reading array of schemas - echo '[{ "name" : "coll3", "properties": { "Key1": { "type": "string" }, "Field1": { "type": "integer" } }, "primary_key": ["Key1"] }, { "name" : "coll4", "properties": { "Key1": { "type": "string" }, "Field1": { "type": "integer" } }, "primary_key": ["Key1"] }]' | $cli create collection db1 - -#reading schemas from command line array - $cli create collection db1 '[{ "name" : "coll5", "properties": { "Key1": { "type": "string" }, "Field1": { "type": "integer" } }, "primary_key": ["Key1"] }, { "name" : "coll6", "properties": { "Key1": { "type": "string" }, "Field1": { "type": "integer" } }, "primary_key": ["Key1"] }]' '{ "name" : "coll7", "properties": { "Key1": { "type": "string" }, "Field1": { "type": "integer" } }, "primary_key": ["Key1"] }' - - #FIXME: implement after server implements it - #$cli describe collection db1 coll1 + echo '[{ "title" : "coll3", "properties": { "Key1": { "type": "string" }, "Field1": { "type": "integer" } }, "primary_key": ["Key1"] }, { "title" : "coll4", "properties": { "Key1": { "type": "string" }, "Field1": { "type": "integer" } }, "primary_key": ["Key1"] }]' | $cli create collection db1 - + #reading schemas from command line array + $cli create collection db1 '[{ "title" : "coll5", "properties": { "Key1": { "type": "string" }, "Field1": { "type": "integer" } }, "primary_key": ["Key1"] }, { "title" : "coll6", "properties": { "Key1": { "type": "string" }, "Field1": { "type": "integer" } }, "primary_key": ["Key1"] }]' '{ "title" : "coll7", "properties": { "Key1": { "type": "string" }, "Field1": { "type": "integer" } }, "primary_key": ["Key1"] }' + # allow to skip - in non interactive input + $cli create collection db1 <<< '[{ "title" : "coll8", "properties": { "Key1": { "type": "string" }, "Field1": { "type": "integer" } }, "primary_key": ["Key1"] }, { "title" : "coll9", "properties": { "Key1": { "type": "string" }, "Field1": { "type": "integer" } }, "primary_key": ["Key1"] }]' $cli list databases $cli list collections db1 @@ -65,13 +77,13 @@ db_tests() { '[{"Key1": "vK2", "Field1": 10}]' #insert from standard input stream - cat <