diff --git a/README.md b/README.md index 9dd7f4fd..f792eecf 100644 --- a/README.md +++ b/README.md @@ -207,7 +207,7 @@ custom-headers: | [Create a Store](#create-store) | `create` | `--name` | `fga store create --name="FGA Demo Store"` | | [Import a Store](#import-store) | `import` | `--file` | `fga store import --file store.fga.yaml` | | [Export a Store](#export-store) | `export` | `--store-id` | `fga store export --store-id=01H0H015178Y2V4CX10C2KGHF4` | -| [List Stores](#list-stores) | `list` | | `fga store list` | +| [List Stores](#list-stores) | `list` | `--name` | `fga store list --name="FGA Demo Store"` | | [Get a Store](#get-store) | `get` | `--store-id` | `fga store get --store-id=01H0H015178Y2V4CX10C2KGHF4` | | [Delete a Store](#delete-store) | `delete` | `--store-id` | `fga store delete --store-id=01H0H015178Y2V4CX10C2KGHF4` | @@ -364,10 +364,13 @@ fga store **list** ###### Parameters * `--max-pages`: Max number of pages to retrieve (default: 20) +* `--name`: Filter stores by exact name (substrings and regexes are not supported) ###### Example `fga store list` +`fga store list --name="FGA Demo Store"` + ###### Response ```json { diff --git a/cmd/store/list.go b/cmd/store/list.go index 8d7a92b4..40e87d75 100644 --- a/cmd/store/list.go +++ b/cmd/store/list.go @@ -31,7 +31,12 @@ import ( // MaxStoresPagesLength Limit the pages of stores so that we are not paginating indefinitely. var MaxStoresPagesLength = 20 // up to 1000 records -func listStores(ctx context.Context, fgaClient client.SdkClient, maxPages int) (*openfga.ListStoresResponse, error) { +func listStores( + ctx context.Context, + fgaClient client.SdkClient, + maxPages int, + name string, +) (*openfga.ListStoresResponse, error) { stores := []openfga.Store{} continuationToken := "" pageIndex := 0 @@ -40,6 +45,9 @@ func listStores(ctx context.Context, fgaClient client.SdkClient, maxPages int) ( options := client.ClientListStoresOptions{ ContinuationToken: &continuationToken, } + if name != "" { + options.Name = &name + } response, err := fgaClient.ListStores(ctx).Options(options).Execute() if err != nil { @@ -78,7 +86,12 @@ var listCmd = &cobra.Command{ return fmt.Errorf("failed to parse max pages due to %w", err) } - response, err := listStores(cmd.Context(), fgaClient, maxPages) + name, err := cmd.Flags().GetString("name") + if err != nil { + return fmt.Errorf("failed to parse name due to %w", err) + } + + response, err := listStores(cmd.Context(), fgaClient, maxPages, name) if err != nil { return err } @@ -89,4 +102,5 @@ var listCmd = &cobra.Command{ func init() { listCmd.Flags().Int("max-pages", MaxStoresPagesLength, "Max number of pages to get.") + listCmd.Flags().String("name", "", "Filter stores by exact name. Substrings and regexes are not supported.") } diff --git a/cmd/store/list_test.go b/cmd/store/list_test.go index a697a9ea..540946fc 100644 --- a/cmd/store/list_test.go +++ b/cmd/store/list_test.go @@ -36,7 +36,7 @@ func TestListStoresError(t *testing.T) { mockRequest.EXPECT().Options(options).Return(mockExecute) mockFgaClient.EXPECT().ListStores(t.Context()).Return(mockRequest) - _, err := listStores(t.Context(), mockFgaClient, 5) + _, err := listStores(t.Context(), mockFgaClient, 5, "") if err == nil { t.Error("Expect error but there is none") } @@ -67,7 +67,7 @@ func TestListStoresEmpty(t *testing.T) { mockRequest.EXPECT().Options(options).Return(mockExecute) mockFgaClient.EXPECT().ListStores(t.Context()).Return(mockRequest) - output, err := listStores(t.Context(), mockFgaClient, 5) + output, err := listStores(t.Context(), mockFgaClient, 5, "") if err != nil { t.Error(err) } @@ -118,7 +118,7 @@ func TestListStoresSinglePage(t *testing.T) { mockRequest.EXPECT().Options(options).Return(mockExecute) mockFgaClient.EXPECT().ListStores(t.Context()).Return(mockRequest) - output, err := listStores(t.Context(), mockFgaClient, 5) + output, err := listStores(t.Context(), mockFgaClient, 5, "") if err != nil { t.Error(err) } @@ -202,7 +202,7 @@ func TestListStoresMultiPage(t *testing.T) { mockFgaClient.EXPECT().ListStores(t.Context()).Return(mockRequest2), ) - output, err := listStores(t.Context(), mockFgaClient, 5) + output, err := listStores(t.Context(), mockFgaClient, 5, "") if err != nil { t.Error(err) } @@ -256,7 +256,7 @@ func TestListStoresMultiPageMaxPage(t *testing.T) { mockRequest1.EXPECT().Options(options1).Return(mockExecute1) mockFgaClient.EXPECT().ListStores(t.Context()).Return(mockRequest1) - output, err := listStores(t.Context(), mockFgaClient, 1) + output, err := listStores(t.Context(), mockFgaClient, 1, "") if err != nil { t.Error(err) } @@ -272,3 +272,35 @@ func TestListStoresMultiPageMaxPage(t *testing.T) { t.Errorf("Expected output %v actual %v", expectedOutput, string(outputTxt)) } } + +func TestListStoresWithName(t *testing.T) { + t.Parallel() + + mockCtrl := gomock.NewController(t) + defer mockCtrl.Finish() + + const storeName = "my-store" + + mockFgaClient := mockclient.NewMockSdkClient(mockCtrl) + + mockExecute := mockclient.NewMockSdkClientListStoresRequestInterface(mockCtrl) + + response := openfga.ListStoresResponse{ + Stores: []openfga.Store{}, + ContinuationToken: "", + } + mockExecute.EXPECT().Execute().Return(&response, nil) + + mockRequest := mockclient.NewMockSdkClientListStoresRequestInterface(mockCtrl) + options := client.ClientListStoresOptions{ + ContinuationToken: openfga.PtrString(""), + Name: openfga.PtrString(storeName), + } + mockRequest.EXPECT().Options(options).Return(mockExecute) + mockFgaClient.EXPECT().ListStores(t.Context()).Return(mockRequest) + + _, err := listStores(t.Context(), mockFgaClient, 5, storeName) + if err != nil { + t.Error(err) + } +}