Skip to content

Commit 2b33de8

Browse files
committed
Add name filter support to ListStores
- Add name parameter to ClientListStoresOptions - Extend OpenFgaApi.listStores() method signatures - Update OpenFgaClient to pass name parameter - Add comprehensive unit and integration tests - Maintain full backward compatibility Resolves #157
1 parent bea55ac commit 2b33de8

File tree

3 files changed

+109
-64
lines changed

3 files changed

+109
-64
lines changed

src/test-integration/java/dev/openfga/sdk/api/OpenFgaApiIntegrationTest.java

Lines changed: 18 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@
3636
public class OpenFgaApiIntegrationTest {
3737

3838
@Container
39-
private static final OpenFGAContainer openfga = new OpenFGAContainer("openfga/openfga:latest");
39+
private static final OpenFGAContainer openfga = new OpenFGAContainer("openfga/openfga:v1.5.1");
4040

4141
private static final ObjectMapper mapper = new ObjectMapper().findAndRegisterModules();
4242
private static final String DEFAULT_USER = "user:81684243-9356-4421-8fbf-a4f8d36aa31b";
@@ -127,41 +127,25 @@ public void listStores() throws Exception {
127127
@Test
128128
public void listStoresWithNameFilter() throws Exception {
129129
// Given
130-
String targetStore = "test-store-" + System.currentTimeMillis(); // Shorter, unique name
130+
String testName = thisTestName();
131+
String targetStore = testName + "-target-store";
132+
String otherStore1 = testName + "-other-store-1";
133+
String otherStore2 = testName + "-other-store-2";
131134

132-
// Create the target store
135+
// Create multiple stores
133136
createStore(targetStore);
134-
135-
try {
136-
// When - Filter by name
137-
ListStoresResponse response =
138-
api.listStores(100, null, targetStore).get().getData();
139-
140-
// Then - Should return only stores matching the name
141-
List<String> storeNames =
142-
response.getStores().stream().map(Store::getName).collect(java.util.stream.Collectors.toList());
143-
assertTrue(storeNames.contains(targetStore), "Target store should be in the filtered response");
144-
145-
// The assertion should be that the target store is in the results
146-
// Note: If the server doesn't support name filtering, it may return all stores
147-
// In that case, we at least verify our target store exists in the response
148-
assertTrue(storeNames.size() >= 1, "Should return at least one store");
149-
150-
// If filtering is working, it should be exactly 1 store
151-
// If not working, we'll see more stores but still pass the test
152-
if (storeNames.size() == 1) {
153-
assertEquals(
154-
targetStore, storeNames.get(0), "Should return exactly the target store when filtering works");
155-
}
156-
} catch (Exception e) {
157-
// If the name parameter isn't supported by the server version,
158-
// the test should not fail the entire build
159-
System.out.println(
160-
"Note: Name filtering may not be supported by OpenFGA server version. Error: " + e.getMessage());
161-
// Still verify we can list stores normally
162-
ListStoresResponse response = api.listStores(100, null).get().getData();
163-
assertNotNull(response.getStores(), "Should still be able to list stores without name filter");
164-
}
137+
createStore(otherStore1);
138+
createStore(otherStore2);
139+
140+
// When - Filter by name
141+
ListStoresResponse response =
142+
api.listStores(100, null, targetStore).get().getData();
143+
144+
// Then - Should only return the target store
145+
List<String> storeNames =
146+
response.getStores().stream().map(Store::getName).collect(java.util.stream.Collectors.toList());
147+
assertTrue(storeNames.contains(targetStore), "Target store should be in the filtered response");
148+
assertEquals(1, storeNames.size(), "Should return only one store when filtering by exact name");
165149
}
166150

167151
@Test

src/test-integration/java/dev/openfga/sdk/api/client/OpenFgaClientIntegrationTest.java

Lines changed: 16 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@
3737
public class OpenFgaClientIntegrationTest {
3838

3939
@Container
40-
private static final OpenFGAContainer openfga = new OpenFGAContainer("openfga/openfga:latest");
40+
private static final OpenFGAContainer openfga = new OpenFGAContainer("openfga/openfga:v1.5.1");
4141

4242
private static final ObjectMapper mapper = new ObjectMapper().findAndRegisterModules();
4343
private static final String DEFAULT_USER = "user:81684243-9356-4421-8fbf-a4f8d36aa31b";
@@ -144,41 +144,27 @@ public void listStores() throws Exception {
144144
@Test
145145
public void listStoresWithNameFilter() throws Exception {
146146
// Given
147-
String targetStore = "test-store-" + System.currentTimeMillis(); // Shorter, unique name
147+
String testName = thisTestName();
148+
String targetStore = testName + "-target-store";
149+
String otherStore1 = testName + "-other-store-1";
150+
String otherStore2 = testName + "-other-store-2";
148151

149-
// Create the target store
152+
// Create multiple stores
150153
createStore(targetStore);
154+
createStore(otherStore1);
155+
createStore(otherStore2);
151156

152157
ClientListStoresOptions options = new ClientListStoresOptions().name(targetStore);
153158

154-
try {
155-
// When - Filter by name using client options
156-
ClientListStoresResponse response = fga.listStores(options).get();
159+
// When - Filter by name using client options
160+
ClientListStoresResponse response = fga.listStores(options).get();
157161

158-
// Then - Should return only stores matching the name
159-
assertNotNull(response.getStores());
160-
List<String> storeNames =
161-
response.getStores().stream().map(Store::getName).collect(java.util.stream.Collectors.toList());
162-
assertTrue(storeNames.contains(targetStore), "Target store should be in the filtered response");
163-
164-
// The assertion should be that the target store is in the results
165-
// Note: If the server doesn't support name filtering, it may return all stores
166-
assertTrue(storeNames.size() >= 1, "Should return at least one store");
167-
168-
// If filtering is working, it should be exactly 1 store
169-
if (storeNames.size() == 1) {
170-
assertEquals(
171-
targetStore, storeNames.get(0), "Should return exactly the target store when filtering works");
172-
}
173-
} catch (Exception e) {
174-
// If the name parameter isn't supported by the server version,
175-
// the test should not fail the entire build
176-
System.out.println(
177-
"Note: Name filtering may not be supported by OpenFGA server version. Error: " + e.getMessage());
178-
// Still verify we can list stores normally
179-
ClientListStoresResponse response = fga.listStores().get();
180-
assertNotNull(response.getStores(), "Should still be able to list stores without name filter");
181-
}
162+
// Then - Should only return the target store
163+
assertNotNull(response.getStores());
164+
List<String> storeNames =
165+
response.getStores().stream().map(Store::getName).collect(java.util.stream.Collectors.toList());
166+
assertTrue(storeNames.contains(targetStore), "Target store should be in the filtered response");
167+
assertEquals(1, storeNames.size(), "Should return only one store when filtering by exact name");
182168
}
183169

184170
@Test

src/test/java/dev/openfga/sdk/api/OpenFgaApiTest.java

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616
import static org.junit.jupiter.api.Assertions.*;
1717
import static org.mockito.Mockito.*;
1818

19+
import org.mockito.ArgumentMatchers;
20+
1921
import com.fasterxml.jackson.databind.ObjectMapper;
2022
import com.pgssoft.httpclient.HttpClientMock;
2123
import dev.openfga.sdk.api.client.*;
@@ -74,6 +76,8 @@ public void beforeEachTest() throws Exception {
7476
when(mockConfiguration.getMaxRetries()).thenReturn(DEFAULT_MAX_RETRIES);
7577
when(mockConfiguration.getMinimumRetryDelay()).thenReturn(DEFAULT_RETRY_DELAY);
7678
when(mockConfiguration.getTelemetryConfiguration()).thenReturn(DEFAULT_TELEMETRY_CONFIG);
79+
when(mockConfiguration.override(ArgumentMatchers.any(ConfigurationOverride.class))).thenReturn(mockConfiguration);
80+
doNothing().when(mockConfiguration).assertValid();
7781

7882
mockApiClient = mock(ApiClient.class);
7983
when(mockApiClient.getObjectMapper()).thenReturn(mapper);
@@ -175,6 +179,77 @@ public void listStores_500() throws Exception {
175179
"{\"code\":\"internal_error\",\"message\":\"Internal Server Error\"}", exception.getResponseData());
176180
}
177181

182+
@Test
183+
public void listStoresTest_withNameFilter() throws Exception {
184+
// Given
185+
String responseBody =
186+
String.format("{\"stores\":[{\"id\":\"%s\",\"name\":\"%s\"}]}", DEFAULT_STORE_ID, DEFAULT_STORE_NAME);
187+
String storeName = "test-store";
188+
String getUrl = String.format("https://api.fga.example/stores?name=%s", storeName);
189+
mockHttpClient.onGet(getUrl).doReturn(200, responseBody);
190+
Integer pageSize = null; // Input is optional
191+
String continuationToken = null; // Input is optional
192+
193+
// When
194+
var response = fga.listStores(pageSize, continuationToken, storeName).get();
195+
196+
// Then
197+
mockHttpClient.verify().get(getUrl).called(1);
198+
assertNotNull(response.getData());
199+
assertNotNull(response.getData().getStores());
200+
var stores = response.getData().getStores();
201+
assertEquals(1, stores.size());
202+
assertEquals(DEFAULT_STORE_ID, stores.get(0).getId());
203+
assertEquals(DEFAULT_STORE_NAME, stores.get(0).getName());
204+
}
205+
206+
@Test
207+
public void listStoresTest_withNameOnly() throws Exception {
208+
// Given
209+
String responseBody =
210+
String.format("{\"stores\":[{\"id\":\"%s\",\"name\":\"%s\"}]}", DEFAULT_STORE_ID, DEFAULT_STORE_NAME);
211+
String storeName = "test-store";
212+
String getUrl = String.format("https://api.fga.example/stores?name=%s", storeName);
213+
mockHttpClient.onGet(getUrl).doReturn(200, responseBody);
214+
Integer pageSize = null; // Input is optional
215+
String continuationToken = null; // Input is optional
216+
217+
// When - This covers the specific line: return listStores(pageSize, continuationToken, name, this.configuration);
218+
var response = fga.listStores(pageSize, continuationToken, storeName).get();
219+
220+
// Then
221+
mockHttpClient.verify().get(getUrl).called(1);
222+
assertNotNull(response.getData());
223+
assertNotNull(response.getData().getStores());
224+
var stores = response.getData().getStores();
225+
assertEquals(1, stores.size());
226+
assertEquals(DEFAULT_STORE_ID, stores.get(0).getId());
227+
assertEquals(DEFAULT_STORE_NAME, stores.get(0).getName());
228+
}
229+
230+
@Test
231+
public void listStoresTest_withConfigurationOverride() throws Exception {
232+
// Given
233+
String responseBody =
234+
String.format("{\"stores\":[{\"id\":\"%s\",\"name\":\"%s\"}]}", DEFAULT_STORE_ID, DEFAULT_STORE_NAME);
235+
mockHttpClient.onGet("https://api.fga.example/stores").doReturn(200, responseBody);
236+
Integer pageSize = null; // Input is optional
237+
String continuationToken = null; // Input is optional
238+
ConfigurationOverride configOverride = new ConfigurationOverride();
239+
240+
// When - This covers the specific line: return listStores(pageSize, continuationToken, null, this.configuration.override(configurationOverride));
241+
var response = fga.listStores(pageSize, continuationToken, configOverride).get();
242+
243+
// Then
244+
mockHttpClient.verify().get("https://api.fga.example/stores").called(1);
245+
assertNotNull(response.getData());
246+
assertNotNull(response.getData().getStores());
247+
var stores = response.getData().getStores();
248+
assertEquals(1, stores.size());
249+
assertEquals(DEFAULT_STORE_ID, stores.get(0).getId());
250+
assertEquals(DEFAULT_STORE_NAME, stores.get(0).getName());
251+
}
252+
178253
/**
179254
* Create a store.
180255
*/

0 commit comments

Comments
 (0)