Skip to content

Commit

Permalink
CLOUDP-164347: Add --flag file to the atlas cluster index create
Browse files Browse the repository at this point in the history
…command (#2768)
  • Loading branch information
andreaangiolillo committed Mar 14, 2024
1 parent 1181f26 commit 17598c6
Show file tree
Hide file tree
Showing 21 changed files with 254 additions and 22 deletions.
24 changes: 21 additions & 3 deletions docs/command/atlas-clusters-indexes-create.txt
Expand Up @@ -59,20 +59,32 @@ Options
- Name of the cluster.
* - --collection
- string
- true
- false
- Name of the collection.

Mutually exclusive with --file.
* - --db
- string
- true
- false
- Name of the database.

Mutually exclusive with --file.
* - -f, --file
- string
- false
- Path to an optional JSON configuration file that defines index settings.

Mutually exclusive with --db, --collection, --key.
* - -h, --help
-
- false
- help for create
* - --key
- strings
- true
- false
- Field to be indexed and the type of index in the following format: field:type.

Mutually exclusive with --file.
* - --projectId
- string
- false
Expand Down Expand Up @@ -112,3 +124,9 @@ Examples
# Create a compound index named property_room_bedrooms on the
listings collection of the realestate database:
atlas clusters indexes create property_room_bedrooms --clusterName Cluster0 --collection listings --db realestate --key property_type:1 --key room_type:1 --key bedrooms:1


.. code-block::

# Create an index named my_index from a JSON configuration file named myfile.json:
atlas clusters indexes create my_index --clusterName Cluster0 --file file.json
64 changes: 56 additions & 8 deletions internal/cli/atlas/clusters/indexes/create.go
Expand Up @@ -22,9 +22,11 @@ import (
"github.com/mongodb/mongodb-atlas-cli/atlascli/internal/cli"
"github.com/mongodb/mongodb-atlas-cli/atlascli/internal/cli/require"
"github.com/mongodb/mongodb-atlas-cli/atlascli/internal/config"
"github.com/mongodb/mongodb-atlas-cli/atlascli/internal/file"
"github.com/mongodb/mongodb-atlas-cli/atlascli/internal/flag"
"github.com/mongodb/mongodb-atlas-cli/atlascli/internal/store"
"github.com/mongodb/mongodb-atlas-cli/atlascli/internal/usage"
"github.com/spf13/afero"
"github.com/spf13/cobra"
atlasv2 "go.mongodb.org/atlas-sdk/v20231115007/admin"
)
Expand All @@ -35,9 +37,11 @@ type CreateOpts struct {
name string
db string
collection string
filename string
keys []string
sparse bool
store store.IndexCreator
fs afero.Fs
}

func (opts *CreateOpts) initStore(ctx context.Context) func() error {
Expand All @@ -61,11 +65,43 @@ func (opts *CreateOpts) Run() error {
}

func (opts *CreateOpts) newIndex() (*atlasv2.DatabaseRollingIndexRequest, error) {
if opts.filename != "" {
return opts.newIndexViaFile()
}

return opts.newIndexViaFlags()
}

func (opts *CreateOpts) newIndexViaFile() (*atlasv2.DatabaseRollingIndexRequest, error) {
i := new(atlasv2.DatabaseRollingIndexRequest)
if err := file.Load(opts.fs, opts.filename, i); err != nil {
return nil, err
}

if opts.name == "" {
return i, nil
}

if i.Options == nil {
i.Options = &atlasv2.IndexOptions{
Name: &opts.name,
}

return i, nil
}

i.Options.Name = &opts.name

return i, nil
}

func (opts *CreateOpts) newIndexViaFlags() (*atlasv2.DatabaseRollingIndexRequest, error) {
i := new(atlasv2.DatabaseRollingIndexRequest)
keys, err := opts.indexKeys()
if err != nil {
return nil, err
}
i := new(atlasv2.DatabaseRollingIndexRequest)

i.Db = opts.db
i.Collection = opts.collection
i.Keys = &keys
Expand Down Expand Up @@ -100,9 +136,11 @@ func (opts *CreateOpts) indexKeys() ([]map[string]string, error) {
}

// CreateBuilder builds a cobra.Command that can run as:
// mcli atlas clusters index create [indexName] --clusterName clusterName --collection collection --dbName dbName [--key field:type].
// mcli atlas clusters index create [indexName] --clusterName clusterName --collection collection --dbName dbName [--key field:type] --file filename.
func CreateBuilder() *cobra.Command {
opts := &CreateOpts{}
opts := &CreateOpts{
fs: afero.NewOsFs(),
}
cmd := &cobra.Command{
Use: "create [indexName]",
Short: "Create a rolling index for the specified cluster for your project.",
Expand All @@ -116,8 +154,17 @@ func CreateBuilder() *cobra.Command {
# Create a compound index named property_room_bedrooms on the
listings collection of the realestate database:
atlas clusters indexes create property_room_bedrooms --clusterName Cluster0 --collection listings --db realestate --key property_type:1 --key room_type:1 --key bedrooms:1`,
atlas clusters indexes create property_room_bedrooms --clusterName Cluster0 --collection listings --db realestate --key property_type:1 --key room_type:1 --key bedrooms:1
# Create an index named my_index from a JSON configuration file named myfile.json:
atlas clusters indexes create my_index --clusterName Cluster0 --file file.json`,
PreRunE: func(cmd *cobra.Command, _ []string) error {
if opts.filename == "" {
_ = cmd.MarkFlagRequired(flag.Database)
_ = cmd.MarkFlagRequired(flag.Collection)
_ = cmd.MarkFlagRequired(flag.Key)
}

return opts.PreRunE(opts.ValidateProjectID, opts.initStore(cmd.Context()))
},
RunE: func(_ *cobra.Command, args []string) error {
Expand All @@ -133,13 +180,14 @@ func CreateBuilder() *cobra.Command {
cmd.Flags().StringVar(&opts.collection, flag.Collection, "", usage.Collection)
cmd.Flags().StringSliceVar(&opts.keys, flag.Key, []string{}, usage.Key)
cmd.Flags().BoolVar(&opts.sparse, flag.Sparse, false, usage.Sparse)
cmd.Flags().StringVarP(&opts.filename, flag.File, flag.FileShort, "", usage.IndexFilename)

cmd.Flags().StringVar(&opts.ProjectID, flag.ProjectID, "", usage.ProjectID)

_ = cmd.MarkFlagRequired(flag.ClusterName)
_ = cmd.MarkFlagRequired(flag.Database)
_ = cmd.MarkFlagRequired(flag.Collection)
_ = cmd.MarkFlagRequired(flag.Key)
cmd.MarkFlagsMutuallyExclusive(flag.File, flag.Database)
cmd.MarkFlagsMutuallyExclusive(flag.File, flag.Collection)
cmd.MarkFlagsMutuallyExclusive(flag.File, flag.Key)

_ = cmd.MarkFlagRequired(flag.ClusterName)
return cmd
}
50 changes: 49 additions & 1 deletion internal/cli/atlas/clusters/indexes/create_test.go
Expand Up @@ -23,6 +23,7 @@ import (
"github.com/mongodb/mongodb-atlas-cli/atlascli/internal/flag"
"github.com/mongodb/mongodb-atlas-cli/atlascli/internal/mocks"
"github.com/mongodb/mongodb-atlas-cli/atlascli/internal/test"
"github.com/spf13/afero"
)

func TestCreate_Run(t *testing.T) {
Expand Down Expand Up @@ -50,11 +51,58 @@ func TestCreate_Run(t *testing.T) {
}
}

func TestCreateWithFile_Run(t *testing.T) {
ctrl := gomock.NewController(t)
mockStore := mocks.NewMockIndexCreator(ctrl)
appFS := afero.NewMemMapFs()
fileJSON := `
{
"collection": "collectionName",
"db": "dbName",
"options":{
"sparse": true,
"unique": true,
"textIndexVersion": 1,
"name": "myIndex",
"min": 1,
"max": 10,
"language_override": "test",
"hidden": true,
"expireAfterSeconds": 2,
"default_language": "test",
"default_language": "test",
"columnstoreProjection": {"key":1, "key2":2},
"bucketSize": 2,
"bits": 222,
"background": false,
"2dsphereIndexVersion": 2
}
}`
fileName := "atlas_cluster_index_create_test.json"
_ = afero.WriteFile(appFS, fileName, []byte(fileJSON), 0600)
createOpts := &CreateOpts{
filename: fileName,
store: mockStore,
fs: appFS,
}

index, _ := createOpts.newIndex()
mockStore.
EXPECT().
CreateIndex(createOpts.ProjectID, createOpts.clusterName, index).
Return(nil).
Times(1)

if err := createOpts.Run(); err != nil {
t.Fatalf("Run() unexpected error: %v", err)
}
}

func TestCreateBuilder(t *testing.T) {
test.CmdValidator(
t,
CreateBuilder(),
0,
[]string{flag.ClusterName, flag.Database, flag.Collection, flag.Key, flag.Sparse, flag.ProjectID},
[]string{flag.ClusterName, flag.Database, flag.Collection, flag.Key, flag.Sparse, flag.ProjectID, flag.File},
)
}
1 change: 1 addition & 0 deletions internal/usage/usage.go
Expand Up @@ -107,6 +107,7 @@ dbName and collection are required only for built-in roles.`
MaxDate = "Maximum created date. This option returns events whose created date is less than or equal to the specified value."
MinDate = "Minimum created date. This option returns events whose created date is greater than or equal to the specified value."
ClusterFilename = "Path to an optional JSON configuration file that defines cluster settings. To learn more about cluster configuration files for the Atlas CLI, see https://dochub.mongodb.org/core/cluster-config-file-atlascli. To learn more about cluster configuration files for MongoCLI, see https://dochub.mongodb.org/core/mms-cluster-settings-file-mcli."
IndexFilename = "Path to an optional JSON configuration file that defines index settings."
BackupFilename = "Path to an optional JSON configuration file that defines backup schedule settings. To learn about the cloud backup configuration file for the Atlas CLI, see https://dochub.mongodb.org/core/cloud-backup-config-file."
SearchFilename = "Name of the JSON index configuration file to use. To learn about the Atlas Search index configuration file, see https://dochub.mongodb.org/core/search-index-config-file-atlascli. To learn about the Atlas Search index syntax and options that you can define in your configuration file, see https://dochub.mongodb.org/core/index-definitions-fts."
SearchNodesFilename = "Name of the JSON index configuration file to use."
Expand Down
50 changes: 46 additions & 4 deletions test/e2e/atlas/clusters_file_test.go
Expand Up @@ -83,12 +83,54 @@ func TestClustersFile(t *testing.T) {
assert.Contains(t, string(resp), "Cluster available")
})

t.Run("Create Partial Index", func(t *testing.T) {
cmd := exec.Command(cliPath,
clustersEntity,
"indexes",
"create",
"--clusterName", clusterFileName,
"--file=data/create_partial_index.json",
"--projectId", g.projectID,
)
cmd.Env = os.Environ()
resp, err := cmd.CombinedOutput()
require.NoError(t, err, string(resp))
})

t.Run("Create Sparse Index", func(t *testing.T) {
cmd := exec.Command(cliPath,
clustersEntity,
"indexes",
"create",
"--clusterName", clusterFileName,
"--file=data/create_sparse_index.json",
"--projectId", g.projectID,
)
cmd.Env = os.Environ()
resp, err := cmd.CombinedOutput()
require.NoError(t, err, string(resp))
})

t.Run("Create 2dspere Index", func(t *testing.T) {
cmd := exec.Command(cliPath,
clustersEntity,
"indexes",
"create",
"--clusterName", clusterFileName,
"--file=data/create_2dspere_index.json",
"--projectId", g.projectID,
)
cmd.Env = os.Environ()
resp, err := cmd.CombinedOutput()
require.NoError(t, err, string(resp))
})

t.Run("Update via file", func(t *testing.T) {
cmd := exec.Command(cliPath,
clustersEntity,
"update",
clusterFileName,
"--file=update_cluster_test.json",
"--file=data/update_cluster_test.json",
"--projectId", g.projectID,
"-o=json")

Expand Down Expand Up @@ -141,9 +183,9 @@ func generateClusterFile(mdbVersion string) (string, error) {
MongoDBMajorVersion: mdbVersion,
}

templateFile := "create_cluster_test.json"
templateFile := "data/create_cluster_test.json"
if service := os.Getenv("MCLI_SERVICE"); service == config.CloudGovService {
templateFile = "create_cluster_gov_test.json"
templateFile = "data/create_cluster_gov_test.json"
}

tmpl, err := template.ParseFiles(templateFile)
Expand All @@ -156,7 +198,7 @@ func generateClusterFile(mdbVersion string) (string, error) {
return "", err
}

const clusterFile = "create_cluster.json"
const clusterFile = "data/create_cluster.json"
file, err := os.Create(clusterFile)
if err != nil {
return "", err
Expand Down
24 changes: 24 additions & 0 deletions test/e2e/atlas/data/create_2dspere_index.json
@@ -0,0 +1,24 @@
{
"collation": {
"alternate": "non-ignorable",
"backwards": false,
"caseFirst": "lower",
"caseLevel": false,
"locale": "af",
"maxVariable": "punct",
"normalization": false,
"numericOrdering": false,
"strength": 3
},
"collection": "accounts",
"db": "sample_airbnb",
"keys": [
{
"test_field": "2dsphere"
}
],
"options": {
"name": "2dspereIndexTest",
"2dsphereIndexVersion": 2
}
}
File renamed without changes.
File renamed without changes.
27 changes: 27 additions & 0 deletions test/e2e/atlas/data/create_partial_index.json
@@ -0,0 +1,27 @@
{
"collation": {
"alternate": "non-ignorable",
"backwards": false,
"caseFirst": "lower",
"caseLevel": false,
"locale": "af",
"maxVariable": "punct",
"normalization": false,
"numericOrdering": false,
"strength": 3
},
"collection": "accounts",
"db": "sample_airbnb",
"keys": [
{
"property_type": "1",
"room_type": "1"
}
],
"options": {
"name": "PartialIndexTest",
"partialFilterExpression":{
"limit": {"$gt": 900}
}
}
}
24 changes: 24 additions & 0 deletions test/e2e/atlas/data/create_sparse_index.json
@@ -0,0 +1,24 @@
{
"collation": {
"alternate": "non-ignorable",
"backwards": false,
"caseFirst": "lower",
"caseLevel": false,
"locale": "af",
"maxVariable": "punct",
"normalization": false,
"numericOrdering": false,
"strength": 3
},
"collection": "accounts",
"db": "sample_airbnb",
"keys": [
{
"test_field": "1"
}
],
"options": {
"name": "SparseIndexTest",
"sparse": true
}
}
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.

0 comments on commit 17598c6

Please sign in to comment.