From c83406a93e47e5d0d0275b98080ee8d25d4e4fed Mon Sep 17 00:00:00 2001 From: Oscmage Date: Wed, 15 Oct 2025 15:48:45 +0100 Subject: [PATCH 1/2] feat: support name filter on ListStores API --- docs/OpenFgaApi.md | 12 ++++--- .../java/dev/openfga/sdk/api/OpenFgaApi.java | 16 +++++---- .../openfga/sdk/api/client/OpenFgaClient.java | 5 +-- .../ClientListStoresOptions.java | 10 ++++++ .../sdk/api/OpenFgaApiIntegrationTest.java | 4 +-- .../dev/openfga/sdk/api/OpenFgaApiTest.java | 34 ++++++++++++++++--- .../sdk/api/client/OpenFgaClientTest.java | 21 ++++++++++++ 7 files changed, 83 insertions(+), 19 deletions(-) diff --git a/docs/OpenFgaApi.md b/docs/OpenFgaApi.md index 0826a09a..80986b1c 100644 --- a/docs/OpenFgaApi.md +++ b/docs/OpenFgaApi.md @@ -1156,7 +1156,7 @@ No authorization required ## listStores -> CompletableFuture listStores(pageSize, continuationToken) +> CompletableFuture listStores(pageSize, continuationToken, name) List all stores @@ -1181,8 +1181,9 @@ public class Example { OpenFgaApi apiInstance = new OpenFgaApi(defaultClient); Integer pageSize = 56; // Integer | String continuationToken = "continuationToken_example"; // String | + String name = "name_example"; // String | The name parameter instructs the API to only include results that match that name.Multiple results may be returned. Only exact matches will be returned; substring matches and regexes will not be evaluated try { - CompletableFuture result = apiInstance.listStores(pageSize, continuationToken); + CompletableFuture result = apiInstance.listStores(pageSize, continuationToken, name); System.out.println(result.get()); } catch (ApiException e) { System.err.println("Exception when calling OpenFgaApi#listStores"); @@ -1202,6 +1203,7 @@ public class Example { |------------- | ------------- | ------------- | -------------| | **pageSize** | **Integer**| | [optional] | | **continuationToken** | **String**| | [optional] | +| **name** | **String**| The name parameter instructs the API to only include results that match that name.Multiple results may be returned. Only exact matches will be returned; substring matches and regexes will not be evaluated | [optional] | ### Return type @@ -1231,7 +1233,7 @@ No authorization required ## listStoresWithHttpInfo -> CompletableFuture> listStores listStoresWithHttpInfo(pageSize, continuationToken) +> CompletableFuture> listStores listStoresWithHttpInfo(pageSize, continuationToken, name) List all stores @@ -1257,8 +1259,9 @@ public class Example { OpenFgaApi apiInstance = new OpenFgaApi(defaultClient); Integer pageSize = 56; // Integer | String continuationToken = "continuationToken_example"; // String | + String name = "name_example"; // String | The name parameter instructs the API to only include results that match that name.Multiple results may be returned. Only exact matches will be returned; substring matches and regexes will not be evaluated try { - CompletableFuture> response = apiInstance.listStoresWithHttpInfo(pageSize, continuationToken); + CompletableFuture> response = apiInstance.listStoresWithHttpInfo(pageSize, continuationToken, name); System.out.println("Status code: " + response.get().getStatusCode()); System.out.println("Response headers: " + response.get().getHeaders()); System.out.println("Response body: " + response.get().getData()); @@ -1287,6 +1290,7 @@ public class Example { |------------- | ------------- | ------------- | -------------| | **pageSize** | **Integer**| | [optional] | | **continuationToken** | **String**| | [optional] | +| **name** | **String**| The name parameter instructs the API to only include results that match that name.Multiple results may be returned. Only exact matches will be returned; substring matches and regexes will not be evaluated | [optional] | ### Return type diff --git a/src/main/java/dev/openfga/sdk/api/OpenFgaApi.java b/src/main/java/dev/openfga/sdk/api/OpenFgaApi.java index 70eddbba..d0589ca0 100644 --- a/src/main/java/dev/openfga/sdk/api/OpenFgaApi.java +++ b/src/main/java/dev/openfga/sdk/api/OpenFgaApi.java @@ -475,12 +475,13 @@ private CompletableFuture> listObjects( * Returns a paginated list of OpenFGA stores and a continuation token to get additional stores. The continuation token will be empty if there are no more stores. * @param pageSize (optional) * @param continuationToken (optional) + * @param name The name parameter instructs the API to only include results that match that name.Multiple results may be returned. Only exact matches will be returned; substring matches and regexes will not be evaluated (optional) * @return CompletableFuture<ApiResponse<ListStoresResponse>> * @throws ApiException if fails to make API call */ - public CompletableFuture> listStores(Integer pageSize, String continuationToken) - throws ApiException, FgaInvalidParameterException { - return listStores(pageSize, continuationToken, this.configuration); + public CompletableFuture> listStores( + Integer pageSize, String continuationToken, String name) throws ApiException, FgaInvalidParameterException { + return listStores(pageSize, continuationToken, name, this.configuration); } /** @@ -488,22 +489,23 @@ public CompletableFuture> listStores(Integer pag * Returns a paginated list of OpenFGA stores and a continuation token to get additional stores. The continuation token will be empty if there are no more stores. * @param pageSize (optional) * @param continuationToken (optional) + * @param name The name parameter instructs the API to only include results that match that name.Multiple results may be returned. Only exact matches will be returned; substring matches and regexes will not be evaluated (optional) * @param configurationOverride Override the {@link Configuration} this OpenFgaApi was constructed with * @return CompletableFuture<ApiResponse<ListStoresResponse>> * @throws ApiException if fails to make API call */ public CompletableFuture> listStores( - Integer pageSize, String continuationToken, ConfigurationOverride configurationOverride) + Integer pageSize, String continuationToken, String name, ConfigurationOverride configurationOverride) throws ApiException, FgaInvalidParameterException { - return listStores(pageSize, continuationToken, this.configuration.override(configurationOverride)); + return listStores(pageSize, continuationToken, name, this.configuration.override(configurationOverride)); } private CompletableFuture> listStores( - Integer pageSize, String continuationToken, Configuration configuration) + Integer pageSize, String continuationToken, String name, Configuration configuration) throws ApiException, FgaInvalidParameterException { String path = "/stores"; - path = pathWithParams(path, "page_size", pageSize, "continuation_token", continuationToken); + path = pathWithParams(path, "page_size", pageSize, "continuation_token", continuationToken, "name", name); Map methodParameters = new HashMap<>(); diff --git a/src/main/java/dev/openfga/sdk/api/client/OpenFgaClient.java b/src/main/java/dev/openfga/sdk/api/client/OpenFgaClient.java index edc058d4..3c1d9699 100644 --- a/src/main/java/dev/openfga/sdk/api/client/OpenFgaClient.java +++ b/src/main/java/dev/openfga/sdk/api/client/OpenFgaClient.java @@ -84,7 +84,7 @@ public void setConfiguration(ClientConfiguration configuration) throws FgaInvali */ public CompletableFuture listStores() throws FgaInvalidParameterException { configuration.assertValid(); - return call(() -> api.listStores(null, null)).thenApply(ClientListStoresResponse::new); + return call(() -> api.listStores(null, null, null)).thenApply(ClientListStoresResponse::new); } /** @@ -94,7 +94,8 @@ public CompletableFuture listStores(ClientListStoresOp throws FgaInvalidParameterException { configuration.assertValid(); var overrides = new ConfigurationOverride().addHeaders(options); - return call(() -> api.listStores(options.getPageSize(), options.getContinuationToken(), overrides)) + return call(() -> api.listStores( + options.getPageSize(), options.getContinuationToken(), options.getName(), overrides)) .thenApply(ClientListStoresResponse::new); } diff --git a/src/main/java/dev/openfga/sdk/api/configuration/ClientListStoresOptions.java b/src/main/java/dev/openfga/sdk/api/configuration/ClientListStoresOptions.java index 81f6c377..c1ffdea2 100644 --- a/src/main/java/dev/openfga/sdk/api/configuration/ClientListStoresOptions.java +++ b/src/main/java/dev/openfga/sdk/api/configuration/ClientListStoresOptions.java @@ -18,6 +18,7 @@ public class ClientListStoresOptions implements AdditionalHeadersSupplier { private Map additionalHeaders; private Integer pageSize; private String continuationToken; + private String name; public ClientListStoresOptions additionalHeaders(Map additionalHeaders) { this.additionalHeaders = additionalHeaders; @@ -46,4 +47,13 @@ public ClientListStoresOptions continuationToken(String continuationToken) { public String getContinuationToken() { return continuationToken; } + + public ClientListStoresOptions name(String name) { + this.name = name; + return this; + } + + public String getName() { + return name; + } } diff --git a/src/test-integration/java/dev/openfga/sdk/api/OpenFgaApiIntegrationTest.java b/src/test-integration/java/dev/openfga/sdk/api/OpenFgaApiIntegrationTest.java index 8d544cae..135ff64b 100644 --- a/src/test-integration/java/dev/openfga/sdk/api/OpenFgaApiIntegrationTest.java +++ b/src/test-integration/java/dev/openfga/sdk/api/OpenFgaApiIntegrationTest.java @@ -84,7 +84,7 @@ public void deleteStore() throws Exception { api.deleteStore(storeId).get(); // Then - ListStoresResponse response = api.listStores(100, null).get().getData(); + ListStoresResponse response = api.listStores(100, null, null).get().getData(); boolean itWasDeleted = response.getStores().stream().map(Store::getId).noneMatch(storeId::equals); assertTrue(itWasDeleted, String.format("No stores should remain with the id %s.", storeId)); } @@ -115,7 +115,7 @@ public void listStores() throws Exception { } // When - ListStoresResponse response = api.listStores(100, null).get().getData(); + ListStoresResponse response = api.listStores(100, null, null).get().getData(); // Then for (String store : stores) { diff --git a/src/test/java/dev/openfga/sdk/api/OpenFgaApiTest.java b/src/test/java/dev/openfga/sdk/api/OpenFgaApiTest.java index 4a0cf7d6..b5d82970 100644 --- a/src/test/java/dev/openfga/sdk/api/OpenFgaApiTest.java +++ b/src/test/java/dev/openfga/sdk/api/OpenFgaApiTest.java @@ -96,7 +96,7 @@ public void listStoresTest() throws Exception { String continuationToken = null; // Input is optional // When - var response = fga.listStores(pageSize, continuationToken).get(); + var response = fga.listStores(pageSize, continuationToken, null).get(); // Then mockHttpClient.verify().get("https://api.fga.example/stores").called(1); @@ -108,6 +108,32 @@ public void listStoresTest() throws Exception { assertEquals(DEFAULT_STORE_NAME, stores.get(0).getName()); } + @Test + public void listStoresTest_withNameFilter() throws Exception { + // Given + String storeName = "test-store-name"; + String responseBody = + String.format("{\"stores\":[{\"id\":\"%s\",\"name\":\"%s\"}]}", DEFAULT_STORE_ID, storeName); + mockHttpClient.onGet("https://api.fga.example/stores?name=" + storeName).doReturn(200, responseBody); + Integer pageSize = null; // Input is optional + String continuationToken = null; // Input is optional + + // When + var response = fga.listStores(pageSize, continuationToken, storeName).get(); + + // Then + mockHttpClient + .verify() + .get("https://api.fga.example/stores?name=" + storeName) + .called(1); + assertNotNull(response.getData()); + assertNotNull(response.getData().getStores()); + var stores = response.getData().getStores(); + assertEquals(1, stores.size()); + assertEquals(DEFAULT_STORE_ID, stores.get(0).getId()); + assertEquals(storeName, stores.get(0).getName()); + } + @Test public void listStores_400() { // Given @@ -119,7 +145,7 @@ public void listStores_400() { // When ExecutionException execException = - assertThrows(ExecutionException.class, () -> fga.listStores(pageSize, continuationToken) + assertThrows(ExecutionException.class, () -> fga.listStores(pageSize, continuationToken, null) .get()); // Then @@ -142,7 +168,7 @@ public void listStores_404() throws Exception { // When ExecutionException execException = - assertThrows(ExecutionException.class, () -> fga.listStores(pageSize, continuationToken) + assertThrows(ExecutionException.class, () -> fga.listStores(pageSize, continuationToken, null) .get()); // Then @@ -164,7 +190,7 @@ public void listStores_500() throws Exception { // When ExecutionException execException = - assertThrows(ExecutionException.class, () -> fga.listStores(pageSize, continuationToken) + assertThrows(ExecutionException.class, () -> fga.listStores(pageSize, continuationToken, null) .get()); // Then diff --git a/src/test/java/dev/openfga/sdk/api/client/OpenFgaClientTest.java b/src/test/java/dev/openfga/sdk/api/client/OpenFgaClientTest.java index 8b964a73..660850a2 100644 --- a/src/test/java/dev/openfga/sdk/api/client/OpenFgaClientTest.java +++ b/src/test/java/dev/openfga/sdk/api/client/OpenFgaClientTest.java @@ -294,6 +294,27 @@ public void listStoresTest_withOptions() throws Exception { assertEquals(DEFAULT_STORE_NAME, response.getStores().get(0).getName()); } + @Test + public void listStoresTest_withNameFilter() throws Exception { + // Given + String storeName = "test-store-name"; + String responseBody = + String.format("{\"stores\":[{\"id\":\"%s\",\"name\":\"%s\"}]}", DEFAULT_STORE_ID, storeName); + String getUrl = String.format("https://api.fga.example/stores?name=%s", storeName); + mockHttpClient.onGet(getUrl).doReturn(200, responseBody); + ClientListStoresOptions options = new ClientListStoresOptions().name(storeName); + + // When + ClientListStoresResponse response = fga.listStores(options).get(); + + // Then + mockHttpClient.verify().get(getUrl).called(1); + assertNotNull(response.getStores()); + assertEquals(1, response.getStores().size()); + assertEquals(DEFAULT_STORE_ID, response.getStores().get(0).getId()); + assertEquals(storeName, response.getStores().get(0).getName()); + } + /** * Create a store. */ From d860f9155298ebf0a941f3129cd1f1bc81df9ce1 Mon Sep 17 00:00:00 2001 From: varkart <44122128+varkart@users.noreply.github.com> Date: Wed, 15 Oct 2025 16:38:43 +0100 Subject: [PATCH 2/2] test: improve test coverage for ListStores --- .../sdk/api/OpenFgaApiIntegrationTest.java | 24 ++++++++ .../client/OpenFgaClientIntegrationTest.java | 26 +++++++++ .../dev/openfga/sdk/api/OpenFgaApiTest.java | 56 +++++++++++++++++++ .../sdk/api/client/OpenFgaClientTest.java | 29 ++++++++++ 4 files changed, 135 insertions(+) diff --git a/src/test-integration/java/dev/openfga/sdk/api/OpenFgaApiIntegrationTest.java b/src/test-integration/java/dev/openfga/sdk/api/OpenFgaApiIntegrationTest.java index 135ff64b..b78aac93 100644 --- a/src/test-integration/java/dev/openfga/sdk/api/OpenFgaApiIntegrationTest.java +++ b/src/test-integration/java/dev/openfga/sdk/api/OpenFgaApiIntegrationTest.java @@ -124,6 +124,30 @@ public void listStores() throws Exception { } } + @Test + public void listStoresWithNameFilter() throws Exception { + // Given + String testName = thisTestName(); + String targetStore = testName + "-target-store"; + String otherStore1 = testName + "-other-store-1"; + String otherStore2 = testName + "-other-store-2"; + + // Create multiple stores + createStore(targetStore); + createStore(otherStore1); + createStore(otherStore2); + + // When - Filter by name + ListStoresResponse response = + api.listStores(100, null, targetStore).get().getData(); + + // Then - Should only return the target store + List storeNames = + response.getStores().stream().map(Store::getName).collect(java.util.stream.Collectors.toList()); + assertTrue(storeNames.contains(targetStore), "Target store should be in the filtered response"); + assertEquals(1, storeNames.size(), "Should return only one store when filtering by exact name"); + } + @Test public void readAuthModel() throws Exception { // Given diff --git a/src/test-integration/java/dev/openfga/sdk/api/client/OpenFgaClientIntegrationTest.java b/src/test-integration/java/dev/openfga/sdk/api/client/OpenFgaClientIntegrationTest.java index 4bd48b28..9fee3049 100644 --- a/src/test-integration/java/dev/openfga/sdk/api/client/OpenFgaClientIntegrationTest.java +++ b/src/test-integration/java/dev/openfga/sdk/api/client/OpenFgaClientIntegrationTest.java @@ -141,6 +141,32 @@ public void listStores() throws Exception { } } + @Test + public void listStoresWithNameFilter() throws Exception { + // Given + String testName = thisTestName(); + String targetStore = testName + "-target"; + String otherStore1 = testName + "-other-1"; + String otherStore2 = testName + "-other-2"; + + // Create multiple stores + createStore(targetStore); + createStore(otherStore1); + createStore(otherStore2); + + ClientListStoresOptions options = new ClientListStoresOptions().name(targetStore); + + // When - Filter by name using client options + ClientListStoresResponse response = fga.listStores(options).get(); + + // Then - Should only return the target store + assertNotNull(response.getStores()); + List storeNames = + response.getStores().stream().map(Store::getName).collect(java.util.stream.Collectors.toList()); + assertTrue(storeNames.contains(targetStore), "Target store should be in the filtered response"); + assertEquals(1, storeNames.size(), "Should return only one store when filtering by exact name"); + } + @Test public void readAuthModel() throws Exception { // Given diff --git a/src/test/java/dev/openfga/sdk/api/OpenFgaApiTest.java b/src/test/java/dev/openfga/sdk/api/OpenFgaApiTest.java index b5d82970..3ae7e47c 100644 --- a/src/test/java/dev/openfga/sdk/api/OpenFgaApiTest.java +++ b/src/test/java/dev/openfga/sdk/api/OpenFgaApiTest.java @@ -14,6 +14,7 @@ import static org.hamcrest.Matchers.*; import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.*; import com.fasterxml.jackson.databind.ObjectMapper; @@ -31,6 +32,7 @@ import java.util.concurrent.ExecutionException; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.mockito.ArgumentMatchers; /** * API tests for OpenFgaApi. @@ -74,6 +76,9 @@ public void beforeEachTest() throws Exception { when(mockConfiguration.getMaxRetries()).thenReturn(DEFAULT_MAX_RETRIES); when(mockConfiguration.getMinimumRetryDelay()).thenReturn(DEFAULT_RETRY_DELAY); when(mockConfiguration.getTelemetryConfiguration()).thenReturn(DEFAULT_TELEMETRY_CONFIG); + when(mockConfiguration.override(ArgumentMatchers.any(ConfigurationOverride.class))) + .thenReturn(mockConfiguration); + doNothing().when(mockConfiguration).assertValid(); mockApiClient = mock(ApiClient.class); when(mockApiClient.getObjectMapper()).thenReturn(mapper); @@ -134,6 +139,57 @@ public void listStoresTest_withNameFilter() throws Exception { assertEquals(storeName, stores.get(0).getName()); } + @Test + public void listStoresTest_withNameOnly() throws Exception { + // Given + String responseBody = + String.format("{\"stores\":[{\"id\":\"%s\",\"name\":\"%s\"}]}", DEFAULT_STORE_ID, DEFAULT_STORE_NAME); + String storeName = "test-store"; + String getUrl = String.format("https://api.fga.example/stores?name=%s", storeName); + mockHttpClient.onGet(getUrl).doReturn(200, responseBody); + Integer pageSize = null; // Input is optional + String continuationToken = null; // Input is optional + + // When - This covers the specific line: return listStores(pageSize, continuationToken, name, + // this.configuration); + var response = fga.listStores(pageSize, continuationToken, storeName).get(); + + // Then + mockHttpClient.verify().get(getUrl).called(1); + assertNotNull(response.getData()); + assertNotNull(response.getData().getStores()); + var stores = response.getData().getStores(); + assertEquals(1, stores.size()); + assertEquals(DEFAULT_STORE_ID, stores.get(0).getId()); + assertEquals(DEFAULT_STORE_NAME, stores.get(0).getName()); + } + + @Test + public void listStoresTest_withConfigurationOverride() throws Exception { + // Given + String responseBody = + String.format("{\"stores\":[{\"id\":\"%s\",\"name\":\"%s\"}]}", DEFAULT_STORE_ID, DEFAULT_STORE_NAME); + mockHttpClient.onGet("https://api.fga.example/stores").doReturn(200, responseBody); + Integer pageSize = null; // Input is optional + String continuationToken = null; // Input is optional + String name = null; + ConfigurationOverride configOverride = new ConfigurationOverride(); + + // When - This covers the specific line: return listStores(pageSize, continuationToken, null, + // this.configuration.override(configurationOverride)); + var response = fga.listStores(pageSize, continuationToken, name, configOverride) + .get(); + + // Then + mockHttpClient.verify().get("https://api.fga.example/stores").called(1); + assertNotNull(response.getData()); + assertNotNull(response.getData().getStores()); + var stores = response.getData().getStores(); + assertEquals(1, stores.size()); + assertEquals(DEFAULT_STORE_ID, stores.get(0).getId()); + assertEquals(DEFAULT_STORE_NAME, stores.get(0).getName()); + } + @Test public void listStores_400() { // Given diff --git a/src/test/java/dev/openfga/sdk/api/client/OpenFgaClientTest.java b/src/test/java/dev/openfga/sdk/api/client/OpenFgaClientTest.java index 660850a2..28899acc 100644 --- a/src/test/java/dev/openfga/sdk/api/client/OpenFgaClientTest.java +++ b/src/test/java/dev/openfga/sdk/api/client/OpenFgaClientTest.java @@ -17,6 +17,7 @@ import static org.hamcrest.core.StringContains.containsString; import static org.junit.jupiter.api.Assertions.*; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.Mockito.*; import com.fasterxml.jackson.databind.ObjectMapper; @@ -315,6 +316,34 @@ public void listStoresTest_withNameFilter() throws Exception { assertEquals(storeName, response.getStores().get(0).getName()); } + @Test + public void listStoresTest_withAllParameters() throws Exception { + // Given + String responseBody = + String.format("{\"stores\":[{\"id\":\"%s\",\"name\":\"%s\"}]}", DEFAULT_STORE_ID, DEFAULT_STORE_NAME); + int pageSize = 10; + String continuationToken = "continuationToken"; + String storeName = "test-store"; + String getUrl = String.format( + "https://api.fga.example/stores?page_size=%d&continuation_token=%s&name=%s", + pageSize, continuationToken, storeName); + mockHttpClient.onGet(getUrl).doReturn(200, responseBody); + ClientListStoresOptions options = new ClientListStoresOptions() + .pageSize(pageSize) + .continuationToken(continuationToken) + .name(storeName); + + // When + ClientListStoresResponse response = fga.listStores(options).get(); + + // Then + mockHttpClient.verify().get(getUrl).called(1); + assertNotNull(response.getStores()); + assertEquals(1, response.getStores().size()); + assertEquals(DEFAULT_STORE_ID, response.getStores().get(0).getId()); + assertEquals(DEFAULT_STORE_NAME, response.getStores().get(0).getName()); + } + /** * Create a store. */