diff --git a/README.md b/README.md index 6644c24a..d9710940 100644 --- a/README.md +++ b/README.md @@ -738,7 +738,10 @@ var request = new ClientCheckRequest() var options = new ClientCheckOptions() .additionalHeaders(Map.of("Some-Http-Header", "Some value")) // You can rely on the model id set in the configuration or override it for this specific request - .authorizationModelId("01GXSA8YR785C4FYS3C0RTG7B1"); + .authorizationModelId("01GXSA8YR785C4FYS3C0RTG7B1") + // You can rely on the store id set in the configuration or override it for this specific request, + // e.g. to use one client for several stores. Every method that works on a store has this option. + .storeId("01YCP46JKYM8FJCQ37NMBYHE5X"); var response = fgaClient.check(request, options).get(); // response.getAllowed() = true 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 a01a0386..aeb58dbe 100644 --- a/src/main/java/dev/openfga/sdk/api/client/OpenFgaClient.java +++ b/src/main/java/dev/openfga/sdk/api/client/OpenFgaClient.java @@ -175,7 +175,7 @@ public CompletableFuture getStore() throws FgaInvalidPar public CompletableFuture getStore(ClientGetStoreOptions options) throws FgaInvalidParameterException { configuration.assertValid(); - String storeId = configuration.getStoreIdChecked(); + String storeId = getStoreIdChecked(options != null ? options.getStoreId() : null); var overrides = new ConfigurationOverride().addHeaders(options); return call(() -> api.getStore(storeId, overrides)).thenApply(ClientGetStoreResponse::new); } @@ -197,7 +197,7 @@ public CompletableFuture deleteStore() throws FgaInva public CompletableFuture deleteStore(ClientDeleteStoreOptions options) throws FgaInvalidParameterException { configuration.assertValid(); - String storeId = configuration.getStoreIdChecked(); + String storeId = getStoreIdChecked(options != null ? options.getStoreId() : null); var overrides = new ConfigurationOverride().addHeaders(options); return call(() -> api.deleteStore(storeId, overrides)).thenApply(ClientDeleteStoreResponse::new); } @@ -224,7 +224,7 @@ public CompletableFuture readAuthorizatio public CompletableFuture readAuthorizationModels( ClientReadAuthorizationModelsOptions options) throws FgaInvalidParameterException { configuration.assertValid(); - String storeId = configuration.getStoreIdChecked(); + String storeId = getStoreIdChecked(options != null ? options.getStoreId() : null); Integer pageSize; String continuationToken; @@ -263,7 +263,7 @@ public CompletableFuture writeAuthorizati WriteAuthorizationModelRequest request, ClientWriteAuthorizationModelOptions options) throws FgaInvalidParameterException { configuration.assertValid(); - String storeId = configuration.getStoreIdChecked(); + String storeId = getStoreIdChecked(options != null ? options.getStoreId() : null); var overrides = new ConfigurationOverride().addHeaders(options); return call(() -> api.writeAuthorizationModel(storeId, request, overrides)) .thenApply(ClientWriteAuthorizationModelResponse::new); @@ -291,7 +291,7 @@ public CompletableFuture readAuthorization public CompletableFuture readAuthorizationModel( ClientReadAuthorizationModelOptions options) throws FgaInvalidParameterException { configuration.assertValid(); - String storeId = configuration.getStoreIdChecked(); + String storeId = getStoreIdChecked(options != null ? options.getStoreId() : null); // Set authorizationModelId from options if available; otherwise, require a valid configuration value String authorizationModelId; if (options != null && !isNullOrWhitespace(options.getAuthorizationModelId())) { @@ -322,7 +322,7 @@ public CompletableFuture readLatestAuthori public CompletableFuture readLatestAuthorizationModel( ClientReadLatestAuthorizationModelOptions options) throws FgaInvalidParameterException { configuration.assertValid(); - String storeId = configuration.getStoreIdChecked(); + String storeId = getStoreIdChecked(options != null ? options.getStoreId() : null); var overrides = new ConfigurationOverride().addHeaders(options); return call(() -> api.readAuthorizationModels(storeId, 1, null, overrides)) .thenApply(ClientReadAuthorizationModelResponse::latestOf); @@ -351,7 +351,7 @@ public CompletableFuture readChanges( ClientReadChangesRequest request, ClientReadChangesOptions readChangesOptions) throws FgaInvalidParameterException { configuration.assertValid(); - String storeId = configuration.getStoreIdChecked(); + String storeId = getStoreIdChecked(readChangesOptions != null ? readChangesOptions.getStoreId() : null); var options = readChangesOptions != null ? readChangesOptions : new ClientReadChangesOptions(); var overrides = new ConfigurationOverride().addHeaders(options); return call(() -> api.readChanges( @@ -381,7 +381,7 @@ public CompletableFuture read(ClientReadRequest request) thr public CompletableFuture read(ClientReadRequest request, ClientReadOptions options) throws FgaInvalidParameterException { configuration.assertValid(); - String storeId = configuration.getStoreIdChecked(); + String storeId = getStoreIdChecked(options != null ? options.getStoreId() : null); ReadRequest body = new ReadRequest(); @@ -512,7 +512,7 @@ public CompletableFuture write(ClientWriteRequest request) public CompletableFuture write(ClientWriteRequest request, ClientWriteOptions options) throws FgaInvalidParameterException { configuration.assertValid(); - String storeId = configuration.getStoreIdChecked(); + String storeId = getStoreIdChecked(options != null ? options.getStoreId() : null); if (options != null && !options.isTransactionsEnabled()) { return writeNonTransaction(storeId, request, options); @@ -780,7 +780,7 @@ public CompletableFuture writeTuples(List t public CompletableFuture writeTuples( List tupleKeys, ClientWriteTuplesOptions options) throws FgaInvalidParameterException { configuration.assertValid(); - String storeId = configuration.getStoreIdChecked(); + String storeId = getStoreIdChecked(options != null ? options.getStoreId() : null); var body = new WriteRequest(); @@ -821,7 +821,7 @@ public CompletableFuture deleteTuples( List tupleKeys, ClientDeleteTuplesOptions options) throws FgaInvalidParameterException { configuration.assertValid(); - String storeId = configuration.getStoreIdChecked(); + String storeId = getStoreIdChecked(options != null ? options.getStoreId() : null); var body = new WriteRequest(); @@ -870,7 +870,7 @@ public CompletableFuture check(ClientCheckRequest request) public CompletableFuture check(ClientCheckRequest request, ClientCheckOptions options) throws FgaInvalidParameterException { configuration.assertValid(); - String storeId = configuration.getStoreIdChecked(); + String storeId = getStoreIdChecked(options != null ? options.getStoreId() : null); CheckRequest body = request.asCheckRequest(); if (options != null) { @@ -911,7 +911,7 @@ public CompletableFuture> clientBatchCheck( List requests, ClientBatchCheckClientOptions batchCheckOptions) throws FgaInvalidParameterException { configuration.assertValid(); - configuration.assertValidStoreId(); + getStoreIdChecked(batchCheckOptions != null ? batchCheckOptions.getStoreId() : null); var options = batchCheckOptions != null ? batchCheckOptions @@ -964,7 +964,7 @@ public CompletableFuture batchCheck( ClientBatchCheckRequest requests, ClientBatchCheckOptions batchCheckOptions) throws FgaInvalidParameterException, FgaValidationError { configuration.assertValid(); - configuration.assertValidStoreId(); + String storeId = getStoreIdChecked(batchCheckOptions != null ? batchCheckOptions.getStoreId() : null); var options = batchCheckOptions != null ? batchCheckOptions @@ -1048,7 +1048,7 @@ public CompletableFuture batchCheck( body.authorizationModelId(authorizationModelId); } - return api.batchCheck(configuration.getStoreId(), body, override); + return api.batchCheck(storeId, body, override); }) .handle((batchCheckResponseApiResponse, throwable) -> { if (throwable != null) { @@ -1103,7 +1103,7 @@ public CompletableFuture expand(ClientExpandRequest reques public CompletableFuture expand(ClientExpandRequest request, ClientExpandOptions options) throws FgaInvalidParameterException { configuration.assertValid(); - String storeId = configuration.getStoreIdChecked(); + String storeId = getStoreIdChecked(options != null ? options.getStoreId() : null); ExpandRequest body = new ExpandRequest(); @@ -1149,7 +1149,7 @@ public CompletableFuture listObjects(ClientListObject public CompletableFuture listObjects( ClientListObjectsRequest request, ClientListObjectsOptions options) throws FgaInvalidParameterException { configuration.assertValid(); - String storeId = configuration.getStoreIdChecked(); + String storeId = getStoreIdChecked(options != null ? options.getStoreId() : null); ListObjectsRequest body = new ListObjectsRequest(); @@ -1301,7 +1301,7 @@ public CompletableFuture streamedListObjects( throw new FgaInvalidParameterException("consumer", "streamedListObjects"); } configuration.assertValid(); - String storeId = configuration.getStoreIdChecked(); + String storeId = getStoreIdChecked(options != null ? options.getStoreId() : null); ListObjectsRequest body = new ListObjectsRequest(); @@ -1404,7 +1404,7 @@ public CompletableFuture listUsers(ClientListUsersReque public CompletableFuture listUsers( ClientListUsersRequest request, ClientListUsersOptions options) throws FgaInvalidParameterException { configuration.assertValid(); - String storeId = configuration.getStoreIdChecked(); + String storeId = getStoreIdChecked(options != null ? options.getStoreId() : null); ListUsersRequest body = new ListUsersRequest(); @@ -1460,7 +1460,7 @@ public CompletableFuture readAssertions() throws F public CompletableFuture readAssertions(ClientReadAssertionsOptions options) throws FgaInvalidParameterException { configuration.assertValid(); - String storeId = configuration.getStoreIdChecked(); + String storeId = getStoreIdChecked(options != null ? options.getStoreId() : null); String authorizationModelId; if (options != null && options.hasValidAuthorizationModelId()) { @@ -1494,7 +1494,7 @@ public CompletableFuture writeAssertions( List assertions, ClientWriteAssertionsOptions options) throws FgaInvalidParameterException { configuration.assertValid(); - String storeId = configuration.getStoreIdChecked(); + String storeId = getStoreIdChecked(options != null ? options.getStoreId() : null); String authorizationModelId; if (options != null && options.hasValidAuthorizationModelId()) { @@ -1511,6 +1511,16 @@ public CompletableFuture writeAssertions( .thenApply(ClientWriteAssertionsResponse::new); } + /** + * Returns the store ID to send a request to: the per-request override when it is set, otherwise the store ID + * from the client configuration. + * + * @throws FgaInvalidParameterException When neither is set + */ + private String getStoreIdChecked(String storeIdOverride) throws FgaInvalidParameterException { + return !isNullOrWhitespace(storeIdOverride) ? storeIdOverride : configuration.getStoreIdChecked(); + } + /** * A {@link FunctionalInterface} for calling a low-level API from {@link OpenFgaApi}. It wraps exceptions * encountered with {@link CompletableFuture#failedFuture(Throwable)} diff --git a/src/main/java/dev/openfga/sdk/api/client/model/ClientStreamedListObjectsOptions.java b/src/main/java/dev/openfga/sdk/api/client/model/ClientStreamedListObjectsOptions.java index bf4c8c61..73c18444 100644 --- a/src/main/java/dev/openfga/sdk/api/client/model/ClientStreamedListObjectsOptions.java +++ b/src/main/java/dev/openfga/sdk/api/client/model/ClientStreamedListObjectsOptions.java @@ -25,6 +25,7 @@ public class ClientStreamedListObjectsOptions implements AdditionalHeadersSuppli private String authorizationModelId; private ConsistencyPreference consistency; private Map additionalHeaders; + private String storeId; public ClientStreamedListObjectsOptions authorizationModelId(String authorizationModelId) { this.authorizationModelId = authorizationModelId; @@ -53,4 +54,16 @@ public ClientStreamedListObjectsOptions additionalHeaders(Map ad public Map getAdditionalHeaders() { return additionalHeaders; } + + /** + * Sets the store to send this request to, instead of the store in the client configuration. + */ + public ClientStreamedListObjectsOptions storeId(String storeId) { + this.storeId = storeId; + return this; + } + + public String getStoreId() { + return storeId; + } } diff --git a/src/main/java/dev/openfga/sdk/api/configuration/ClientBatchCheckClientOptions.java b/src/main/java/dev/openfga/sdk/api/configuration/ClientBatchCheckClientOptions.java index 42b9cc9a..609f2f58 100644 --- a/src/main/java/dev/openfga/sdk/api/configuration/ClientBatchCheckClientOptions.java +++ b/src/main/java/dev/openfga/sdk/api/configuration/ClientBatchCheckClientOptions.java @@ -6,6 +6,7 @@ public class ClientBatchCheckClientOptions implements AdditionalHeadersSupplier { private Map additionalHeaders; + private String storeId; private Integer maxParallelRequests; private String authorizationModelId; private ConsistencyPreference consistency; @@ -51,6 +52,19 @@ public ClientCheckOptions asClientCheckOptions() { return new ClientCheckOptions() .additionalHeaders(additionalHeaders != null ? new HashMap<>(additionalHeaders) : null) .authorizationModelId(authorizationModelId) + .storeId(storeId) .consistency(consistency); } + + /** + * Sets the store to send this request to, instead of the store in the client configuration. + */ + public ClientBatchCheckClientOptions storeId(String storeId) { + this.storeId = storeId; + return this; + } + + public String getStoreId() { + return storeId; + } } diff --git a/src/main/java/dev/openfga/sdk/api/configuration/ClientBatchCheckOptions.java b/src/main/java/dev/openfga/sdk/api/configuration/ClientBatchCheckOptions.java index 240b01a1..8b683a4b 100644 --- a/src/main/java/dev/openfga/sdk/api/configuration/ClientBatchCheckOptions.java +++ b/src/main/java/dev/openfga/sdk/api/configuration/ClientBatchCheckOptions.java @@ -5,6 +5,7 @@ public class ClientBatchCheckOptions implements AdditionalHeadersSupplier { private Map additionalHeaders; + private String storeId; private Integer maxParallelRequests; private Integer maxBatchSize; private String authorizationModelId; @@ -55,4 +56,16 @@ public ClientBatchCheckOptions consistency(ConsistencyPreference consistency) { public ConsistencyPreference getConsistency() { return consistency; } + + /** + * Sets the store to send this request to, instead of the store in the client configuration. + */ + public ClientBatchCheckOptions storeId(String storeId) { + this.storeId = storeId; + return this; + } + + public String getStoreId() { + return storeId; + } } diff --git a/src/main/java/dev/openfga/sdk/api/configuration/ClientCheckOptions.java b/src/main/java/dev/openfga/sdk/api/configuration/ClientCheckOptions.java index 24c6daa0..b0bf4cc9 100644 --- a/src/main/java/dev/openfga/sdk/api/configuration/ClientCheckOptions.java +++ b/src/main/java/dev/openfga/sdk/api/configuration/ClientCheckOptions.java @@ -5,6 +5,7 @@ public class ClientCheckOptions implements AdditionalHeadersSupplier { private Map additionalHeaders; + private String storeId; private String authorizationModelId; private ConsistencyPreference consistency; @@ -35,4 +36,16 @@ public ClientCheckOptions consistency(ConsistencyPreference consistency) { public ConsistencyPreference getConsistency() { return consistency; } + + /** + * Sets the store to send this request to, instead of the store in the client configuration. + */ + public ClientCheckOptions storeId(String storeId) { + this.storeId = storeId; + return this; + } + + public String getStoreId() { + return storeId; + } } diff --git a/src/main/java/dev/openfga/sdk/api/configuration/ClientDeleteStoreOptions.java b/src/main/java/dev/openfga/sdk/api/configuration/ClientDeleteStoreOptions.java index f55bbb4e..0c940351 100644 --- a/src/main/java/dev/openfga/sdk/api/configuration/ClientDeleteStoreOptions.java +++ b/src/main/java/dev/openfga/sdk/api/configuration/ClientDeleteStoreOptions.java @@ -4,6 +4,7 @@ public class ClientDeleteStoreOptions implements AdditionalHeadersSupplier { private Map additionalHeaders; + private String storeId; public ClientDeleteStoreOptions additionalHeaders(Map additionalHeaders) { this.additionalHeaders = additionalHeaders; @@ -14,4 +15,16 @@ public ClientDeleteStoreOptions additionalHeaders(Map additional public Map getAdditionalHeaders() { return this.additionalHeaders; } + + /** + * Sets the store to send this request to, instead of the store in the client configuration. + */ + public ClientDeleteStoreOptions storeId(String storeId) { + this.storeId = storeId; + return this; + } + + public String getStoreId() { + return storeId; + } } diff --git a/src/main/java/dev/openfga/sdk/api/configuration/ClientDeleteTuplesOptions.java b/src/main/java/dev/openfga/sdk/api/configuration/ClientDeleteTuplesOptions.java index 64a917a1..a9bdbb55 100644 --- a/src/main/java/dev/openfga/sdk/api/configuration/ClientDeleteTuplesOptions.java +++ b/src/main/java/dev/openfga/sdk/api/configuration/ClientDeleteTuplesOptions.java @@ -5,6 +5,7 @@ public class ClientDeleteTuplesOptions implements AdditionalHeadersSupplier { private Map additionalHeaders; + private String storeId; private WriteRequestDeletes.OnMissingEnum onMissing; public ClientDeleteTuplesOptions additionalHeaders(Map additionalHeaders) { @@ -25,4 +26,16 @@ public ClientDeleteTuplesOptions onMissing(WriteRequestDeletes.OnMissingEnum onM public WriteRequestDeletes.OnMissingEnum getOnMissing() { return onMissing; } + + /** + * Sets the store to send this request to, instead of the store in the client configuration. + */ + public ClientDeleteTuplesOptions storeId(String storeId) { + this.storeId = storeId; + return this; + } + + public String getStoreId() { + return storeId; + } } diff --git a/src/main/java/dev/openfga/sdk/api/configuration/ClientExpandOptions.java b/src/main/java/dev/openfga/sdk/api/configuration/ClientExpandOptions.java index 9c0afe21..cc520c79 100644 --- a/src/main/java/dev/openfga/sdk/api/configuration/ClientExpandOptions.java +++ b/src/main/java/dev/openfga/sdk/api/configuration/ClientExpandOptions.java @@ -5,6 +5,7 @@ public class ClientExpandOptions implements AdditionalHeadersSupplier { private Map additionalHeaders; + private String storeId; private String authorizationModelId; private ConsistencyPreference consistency; @@ -35,4 +36,16 @@ public ClientExpandOptions consistency(ConsistencyPreference consistency) { public ConsistencyPreference getConsistency() { return consistency; } + + /** + * Sets the store to send this request to, instead of the store in the client configuration. + */ + public ClientExpandOptions storeId(String storeId) { + this.storeId = storeId; + return this; + } + + public String getStoreId() { + return storeId; + } } diff --git a/src/main/java/dev/openfga/sdk/api/configuration/ClientGetStoreOptions.java b/src/main/java/dev/openfga/sdk/api/configuration/ClientGetStoreOptions.java index 6650ac69..c717852b 100644 --- a/src/main/java/dev/openfga/sdk/api/configuration/ClientGetStoreOptions.java +++ b/src/main/java/dev/openfga/sdk/api/configuration/ClientGetStoreOptions.java @@ -4,6 +4,7 @@ public class ClientGetStoreOptions implements AdditionalHeadersSupplier { private Map additionalHeaders; + private String storeId; public ClientGetStoreOptions additionalHeaders(Map additionalHeaders) { this.additionalHeaders = additionalHeaders; @@ -14,4 +15,16 @@ public ClientGetStoreOptions additionalHeaders(Map additionalHea public Map getAdditionalHeaders() { return this.additionalHeaders; } + + /** + * Sets the store to send this request to, instead of the store in the client configuration. + */ + public ClientGetStoreOptions storeId(String storeId) { + this.storeId = storeId; + return this; + } + + public String getStoreId() { + return storeId; + } } diff --git a/src/main/java/dev/openfga/sdk/api/configuration/ClientListObjectsOptions.java b/src/main/java/dev/openfga/sdk/api/configuration/ClientListObjectsOptions.java index 54ce0e53..b35b9b71 100644 --- a/src/main/java/dev/openfga/sdk/api/configuration/ClientListObjectsOptions.java +++ b/src/main/java/dev/openfga/sdk/api/configuration/ClientListObjectsOptions.java @@ -5,6 +5,7 @@ public class ClientListObjectsOptions implements AdditionalHeadersSupplier { private Map additionalHeaders; + private String storeId; private String authorizationModelId; private ConsistencyPreference consistency; @@ -35,4 +36,16 @@ public ClientListObjectsOptions consistency(ConsistencyPreference consistency) { public ConsistencyPreference getConsistency() { return consistency; } + + /** + * Sets the store to send this request to, instead of the store in the client configuration. + */ + public ClientListObjectsOptions storeId(String storeId) { + this.storeId = storeId; + return this; + } + + public String getStoreId() { + return storeId; + } } diff --git a/src/main/java/dev/openfga/sdk/api/configuration/ClientListRelationsOptions.java b/src/main/java/dev/openfga/sdk/api/configuration/ClientListRelationsOptions.java index 1e0441dc..7cd5e124 100644 --- a/src/main/java/dev/openfga/sdk/api/configuration/ClientListRelationsOptions.java +++ b/src/main/java/dev/openfga/sdk/api/configuration/ClientListRelationsOptions.java @@ -6,6 +6,7 @@ public class ClientListRelationsOptions implements AdditionalHeadersSupplier { private Map additionalHeaders; + private String storeId; private Integer maxParallelRequests; private String authorizationModelId; private ConsistencyPreference consistency; @@ -52,6 +53,19 @@ public ClientBatchCheckClientOptions asClientBatchCheckClientOptions() { .authorizationModelId(authorizationModelId) .additionalHeaders(additionalHeaders != null ? new HashMap<>(additionalHeaders) : null) .maxParallelRequests(maxParallelRequests) + .storeId(storeId) .consistency(consistency); } + + /** + * Sets the store to send this request to, instead of the store in the client configuration. + */ + public ClientListRelationsOptions storeId(String storeId) { + this.storeId = storeId; + return this; + } + + public String getStoreId() { + return storeId; + } } diff --git a/src/main/java/dev/openfga/sdk/api/configuration/ClientListUsersOptions.java b/src/main/java/dev/openfga/sdk/api/configuration/ClientListUsersOptions.java index cf222508..ca52adef 100644 --- a/src/main/java/dev/openfga/sdk/api/configuration/ClientListUsersOptions.java +++ b/src/main/java/dev/openfga/sdk/api/configuration/ClientListUsersOptions.java @@ -5,6 +5,7 @@ public class ClientListUsersOptions implements AdditionalHeadersSupplier { private Map additionalHeaders; + private String storeId; private String authorizationModelId; private ConsistencyPreference consistency; @@ -35,4 +36,16 @@ public ClientListUsersOptions consistency(ConsistencyPreference consistency) { public ConsistencyPreference getConsistency() { return consistency; } + + /** + * Sets the store to send this request to, instead of the store in the client configuration. + */ + public ClientListUsersOptions storeId(String storeId) { + this.storeId = storeId; + return this; + } + + public String getStoreId() { + return storeId; + } } diff --git a/src/main/java/dev/openfga/sdk/api/configuration/ClientReadAssertionsOptions.java b/src/main/java/dev/openfga/sdk/api/configuration/ClientReadAssertionsOptions.java index 2f54974d..be59528f 100644 --- a/src/main/java/dev/openfga/sdk/api/configuration/ClientReadAssertionsOptions.java +++ b/src/main/java/dev/openfga/sdk/api/configuration/ClientReadAssertionsOptions.java @@ -7,6 +7,7 @@ public class ClientReadAssertionsOptions implements AdditionalHeadersSupplier { private Map additionalHeaders; + private String storeId; private String authorizationModelId; public ClientReadAssertionsOptions additionalHeaders(Map additionalHeaders) { @@ -42,4 +43,16 @@ public String getAuthorizationModelIdChecked() throws FgaInvalidParameterExcepti assertValidAuthorizationModelId(); return authorizationModelId; } + + /** + * Sets the store to send this request to, instead of the store in the client configuration. + */ + public ClientReadAssertionsOptions storeId(String storeId) { + this.storeId = storeId; + return this; + } + + public String getStoreId() { + return storeId; + } } diff --git a/src/main/java/dev/openfga/sdk/api/configuration/ClientReadAuthorizationModelOptions.java b/src/main/java/dev/openfga/sdk/api/configuration/ClientReadAuthorizationModelOptions.java index 6918dd3e..aa6123ee 100644 --- a/src/main/java/dev/openfga/sdk/api/configuration/ClientReadAuthorizationModelOptions.java +++ b/src/main/java/dev/openfga/sdk/api/configuration/ClientReadAuthorizationModelOptions.java @@ -7,6 +7,7 @@ public class ClientReadAuthorizationModelOptions implements AdditionalHeadersSupplier { private Map additionalHeaders; + private String storeId; private String authorizationModelId; public ClientReadAuthorizationModelOptions additionalHeaders(Map additionalHeaders) { @@ -38,4 +39,16 @@ public String getAuthorizationModelIdChecked() throws FgaInvalidParameterExcepti assertValidAuthorizationModelId(); return authorizationModelId; } + + /** + * Sets the store to send this request to, instead of the store in the client configuration. + */ + public ClientReadAuthorizationModelOptions storeId(String storeId) { + this.storeId = storeId; + return this; + } + + public String getStoreId() { + return storeId; + } } diff --git a/src/main/java/dev/openfga/sdk/api/configuration/ClientReadAuthorizationModelsOptions.java b/src/main/java/dev/openfga/sdk/api/configuration/ClientReadAuthorizationModelsOptions.java index 92103b32..e8ebbcb0 100644 --- a/src/main/java/dev/openfga/sdk/api/configuration/ClientReadAuthorizationModelsOptions.java +++ b/src/main/java/dev/openfga/sdk/api/configuration/ClientReadAuthorizationModelsOptions.java @@ -4,6 +4,7 @@ public class ClientReadAuthorizationModelsOptions implements AdditionalHeadersSupplier { private Map additionalHeaders; + private String storeId; private Integer pageSize; private String continuationToken; @@ -34,4 +35,16 @@ public ClientReadAuthorizationModelsOptions continuationToken(String continuatio public String getContinuationToken() { return continuationToken; } + + /** + * Sets the store to send this request to, instead of the store in the client configuration. + */ + public ClientReadAuthorizationModelsOptions storeId(String storeId) { + this.storeId = storeId; + return this; + } + + public String getStoreId() { + return storeId; + } } diff --git a/src/main/java/dev/openfga/sdk/api/configuration/ClientReadChangesOptions.java b/src/main/java/dev/openfga/sdk/api/configuration/ClientReadChangesOptions.java index 9a0abf95..a462dc61 100644 --- a/src/main/java/dev/openfga/sdk/api/configuration/ClientReadChangesOptions.java +++ b/src/main/java/dev/openfga/sdk/api/configuration/ClientReadChangesOptions.java @@ -4,6 +4,7 @@ public class ClientReadChangesOptions implements AdditionalHeadersSupplier { private Map additionalHeaders; + private String storeId; private Integer pageSize; private String continuationToken; @@ -34,4 +35,16 @@ public ClientReadChangesOptions continuationToken(String continuationToken) { public String getContinuationToken() { return continuationToken; } + + /** + * Sets the store to send this request to, instead of the store in the client configuration. + */ + public ClientReadChangesOptions storeId(String storeId) { + this.storeId = storeId; + return this; + } + + public String getStoreId() { + return storeId; + } } diff --git a/src/main/java/dev/openfga/sdk/api/configuration/ClientReadLatestAuthorizationModelOptions.java b/src/main/java/dev/openfga/sdk/api/configuration/ClientReadLatestAuthorizationModelOptions.java index 4135f5a9..4cb516d7 100644 --- a/src/main/java/dev/openfga/sdk/api/configuration/ClientReadLatestAuthorizationModelOptions.java +++ b/src/main/java/dev/openfga/sdk/api/configuration/ClientReadLatestAuthorizationModelOptions.java @@ -4,6 +4,7 @@ public class ClientReadLatestAuthorizationModelOptions implements AdditionalHeadersSupplier { private Map additionalHeaders; + private String storeId; public ClientReadLatestAuthorizationModelOptions additionalHeaders(Map additionalHeaders) { this.additionalHeaders = additionalHeaders; @@ -14,4 +15,16 @@ public ClientReadLatestAuthorizationModelOptions additionalHeaders(Map getAdditionalHeaders() { return this.additionalHeaders; } + + /** + * Sets the store to send this request to, instead of the store in the client configuration. + */ + public ClientReadLatestAuthorizationModelOptions storeId(String storeId) { + this.storeId = storeId; + return this; + } + + public String getStoreId() { + return storeId; + } } diff --git a/src/main/java/dev/openfga/sdk/api/configuration/ClientReadOptions.java b/src/main/java/dev/openfga/sdk/api/configuration/ClientReadOptions.java index e949bb87..b7055ca3 100644 --- a/src/main/java/dev/openfga/sdk/api/configuration/ClientReadOptions.java +++ b/src/main/java/dev/openfga/sdk/api/configuration/ClientReadOptions.java @@ -5,6 +5,7 @@ public class ClientReadOptions implements AdditionalHeadersSupplier { private Map additionalHeaders; + private String storeId; private Integer pageSize; private String continuationToken; private ConsistencyPreference consistency; @@ -45,4 +46,16 @@ public ClientReadOptions consistency(ConsistencyPreference consistency) { public ConsistencyPreference getConsistency() { return consistency; } + + /** + * Sets the store to send this request to, instead of the store in the client configuration. + */ + public ClientReadOptions storeId(String storeId) { + this.storeId = storeId; + return this; + } + + public String getStoreId() { + return storeId; + } } diff --git a/src/main/java/dev/openfga/sdk/api/configuration/ClientWriteAssertionsOptions.java b/src/main/java/dev/openfga/sdk/api/configuration/ClientWriteAssertionsOptions.java index 4d7f0fbd..cd0fb5fe 100644 --- a/src/main/java/dev/openfga/sdk/api/configuration/ClientWriteAssertionsOptions.java +++ b/src/main/java/dev/openfga/sdk/api/configuration/ClientWriteAssertionsOptions.java @@ -7,6 +7,7 @@ public class ClientWriteAssertionsOptions implements AdditionalHeadersSupplier { private Map additionalHeaders; + private String storeId; private String authorizationModelId; public ClientWriteAssertionsOptions additionalHeaders(Map additionalHeaders) { @@ -42,4 +43,16 @@ public String getAuthorizationModelIdChecked() throws FgaInvalidParameterExcepti assertValidAuthorizationModelId(); return authorizationModelId; } + + /** + * Sets the store to send this request to, instead of the store in the client configuration. + */ + public ClientWriteAssertionsOptions storeId(String storeId) { + this.storeId = storeId; + return this; + } + + public String getStoreId() { + return storeId; + } } diff --git a/src/main/java/dev/openfga/sdk/api/configuration/ClientWriteAuthorizationModelOptions.java b/src/main/java/dev/openfga/sdk/api/configuration/ClientWriteAuthorizationModelOptions.java index bef5bf63..d9411b1e 100644 --- a/src/main/java/dev/openfga/sdk/api/configuration/ClientWriteAuthorizationModelOptions.java +++ b/src/main/java/dev/openfga/sdk/api/configuration/ClientWriteAuthorizationModelOptions.java @@ -4,6 +4,7 @@ public class ClientWriteAuthorizationModelOptions implements AdditionalHeadersSupplier { private Map additionalHeaders; + private String storeId; public ClientWriteAuthorizationModelOptions additionalHeaders(Map additionalHeaders) { this.additionalHeaders = additionalHeaders; @@ -14,4 +15,16 @@ public ClientWriteAuthorizationModelOptions additionalHeaders(Map getAdditionalHeaders() { return this.additionalHeaders; } + + /** + * Sets the store to send this request to, instead of the store in the client configuration. + */ + public ClientWriteAuthorizationModelOptions storeId(String storeId) { + this.storeId = storeId; + return this; + } + + public String getStoreId() { + return storeId; + } } diff --git a/src/main/java/dev/openfga/sdk/api/configuration/ClientWriteOptions.java b/src/main/java/dev/openfga/sdk/api/configuration/ClientWriteOptions.java index 04686fd9..cb4a22dd 100644 --- a/src/main/java/dev/openfga/sdk/api/configuration/ClientWriteOptions.java +++ b/src/main/java/dev/openfga/sdk/api/configuration/ClientWriteOptions.java @@ -12,6 +12,7 @@ */ public class ClientWriteOptions implements AdditionalHeadersSupplier { private Map additionalHeaders; + private String storeId; private String authorizationModelId; private boolean transactionsEnabled = true; private int transactionChunkSize; @@ -179,4 +180,16 @@ public ClientWriteOptions onMissing(WriteRequestDeletes.OnMissingEnum onMissing) public WriteRequestDeletes.OnMissingEnum getOnMissing() { return onMissing; } + + /** + * Sets the store to send this request to, instead of the store in the client configuration. + */ + public ClientWriteOptions storeId(String storeId) { + this.storeId = storeId; + return this; + } + + public String getStoreId() { + return storeId; + } } diff --git a/src/main/java/dev/openfga/sdk/api/configuration/ClientWriteTuplesOptions.java b/src/main/java/dev/openfga/sdk/api/configuration/ClientWriteTuplesOptions.java index 932bd749..10d8de5c 100644 --- a/src/main/java/dev/openfga/sdk/api/configuration/ClientWriteTuplesOptions.java +++ b/src/main/java/dev/openfga/sdk/api/configuration/ClientWriteTuplesOptions.java @@ -5,6 +5,7 @@ public class ClientWriteTuplesOptions implements AdditionalHeadersSupplier { private Map additionalHeaders; + private String storeId; private WriteRequestWrites.OnDuplicateEnum onDuplicate; public ClientWriteTuplesOptions additionalHeaders(Map additionalHeaders) { @@ -25,4 +26,16 @@ public ClientWriteTuplesOptions onDuplicate(WriteRequestWrites.OnDuplicateEnum o public WriteRequestWrites.OnDuplicateEnum getOnDuplicate() { return onDuplicate; } + + /** + * Sets the store to send this request to, instead of the store in the client configuration. + */ + public ClientWriteTuplesOptions storeId(String storeId) { + this.storeId = storeId; + return this; + } + + public String getStoreId() { + return storeId; + } } diff --git a/src/test/java/dev/openfga/sdk/api/client/OpenFgaClientStoreIdOverrideTest.java b/src/test/java/dev/openfga/sdk/api/client/OpenFgaClientStoreIdOverrideTest.java new file mode 100644 index 00000000..7c4a4d72 --- /dev/null +++ b/src/test/java/dev/openfga/sdk/api/client/OpenFgaClientStoreIdOverrideTest.java @@ -0,0 +1,163 @@ +package dev.openfga.sdk.api.client; + +import static com.github.tomakehurst.wiremock.client.WireMock.*; +import static org.junit.jupiter.api.Assertions.*; + +import com.github.tomakehurst.wiremock.junit5.WireMockRuntimeInfo; +import com.github.tomakehurst.wiremock.junit5.WireMockTest; +import dev.openfga.sdk.api.client.model.ClientBatchCheckItem; +import dev.openfga.sdk.api.client.model.ClientBatchCheckRequest; +import dev.openfga.sdk.api.client.model.ClientCheckRequest; +import dev.openfga.sdk.api.client.model.ClientListObjectsRequest; +import dev.openfga.sdk.api.client.model.ClientListRelationsRequest; +import dev.openfga.sdk.api.client.model.ClientReadRequest; +import dev.openfga.sdk.api.client.model.ClientStreamedListObjectsOptions; +import dev.openfga.sdk.api.client.model.ClientTupleKey; +import dev.openfga.sdk.api.client.model.ClientWriteRequest; +import dev.openfga.sdk.api.configuration.ClientBatchCheckOptions; +import dev.openfga.sdk.api.configuration.ClientCheckOptions; +import dev.openfga.sdk.api.configuration.ClientConfiguration; +import dev.openfga.sdk.api.configuration.ClientListObjectsOptions; +import dev.openfga.sdk.api.configuration.ClientListRelationsOptions; +import dev.openfga.sdk.api.configuration.ClientReadOptions; +import dev.openfga.sdk.api.configuration.ClientWriteOptions; +import dev.openfga.sdk.errors.FgaInvalidParameterException; +import java.util.List; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +/** + * Requests are sent to the store set in the per-request options, and to the configured store otherwise. + */ +@WireMockTest +class OpenFgaClientStoreIdOverrideTest { + private static final String CONFIGURED_STORE_ID = "01YCP46JKYM8FJCQ37NMBYHE5X"; + private static final String OVERRIDE_STORE_ID = "01HVMMBCMGZNT3SED4Z17ECXCA"; + + private String apiUrl; + + @BeforeEach + void setUp(WireMockRuntimeInfo wm) { + apiUrl = wm.getHttpBaseUrl(); + stubFor(post(urlPathMatching("/stores/[^/]+/check")) + .willReturn(okJson("{\"allowed\":true,\"resolution\":\"\"}"))); + stubFor(post(urlPathMatching("/stores/[^/]+/batch-check")).willReturn(okJson("{\"result\":{}}"))); + stubFor(post(urlPathMatching("/stores/[^/]+/write")).willReturn(okJson("{}"))); + stubFor(post(urlPathMatching("/stores/[^/]+/read")) + .willReturn(okJson("{\"tuples\":[],\"continuation_token\":\"\"}"))); + stubFor(post(urlPathMatching("/stores/[^/]+/list-objects")).willReturn(okJson("{\"objects\":[]}"))); + stubFor(post(urlPathMatching("/stores/[^/]+/streamed-list-objects")) + .willReturn(aResponse() + .withStatus(200) + .withHeader("Content-Type", "application/x-ndjson") + .withBody("{\"result\":{\"object\":\"document:1\"}}\n"))); + } + + private OpenFgaClient client(String storeId) throws FgaInvalidParameterException { + return new OpenFgaClient(new ClientConfiguration().apiUrl(apiUrl).storeId(storeId)); + } + + private static ClientCheckRequest checkRequest(String relation) { + return new ClientCheckRequest().user("user:anne").relation(relation)._object("document:1"); + } + + @Test + void usesTheConfiguredStoreWithoutAnOverride() throws Exception { + client(CONFIGURED_STORE_ID).check(checkRequest("viewer")).get(); + + verify(1, postRequestedFor(urlPathEqualTo("/stores/" + CONFIGURED_STORE_ID + "/check"))); + } + + @Test + void checkUsesTheStoreFromTheOptions() throws Exception { + client(CONFIGURED_STORE_ID) + .check(checkRequest("viewer"), new ClientCheckOptions().storeId(OVERRIDE_STORE_ID)) + .get(); + + verify(1, postRequestedFor(urlPathEqualTo("/stores/" + OVERRIDE_STORE_ID + "/check"))); + verify(0, postRequestedFor(urlPathEqualTo("/stores/" + CONFIGURED_STORE_ID + "/check"))); + } + + @Test + void theOverrideWorksWithoutAConfiguredStore() throws Exception { + client(null) + .check(checkRequest("viewer"), new ClientCheckOptions().storeId(OVERRIDE_STORE_ID)) + .get(); + + verify(1, postRequestedFor(urlPathEqualTo("/stores/" + OVERRIDE_STORE_ID + "/check"))); + } + + @Test + void failsWithoutAConfiguredStoreOrAnOverride() throws Exception { + assertThrows( + FgaInvalidParameterException.class, () -> client(null).check(checkRequest("viewer"), null)); + } + + @Test + void writeReadAndListObjectsUseTheStoreFromTheOptions() throws Exception { + var fga = client(CONFIGURED_STORE_ID); + var tuple = new ClientTupleKey().user("user:anne").relation("viewer")._object("document:1"); + + fga.write(new ClientWriteRequest().writes(List.of(tuple)), new ClientWriteOptions().storeId(OVERRIDE_STORE_ID)) + .get(); + fga.read(new ClientReadRequest(), new ClientReadOptions().storeId(OVERRIDE_STORE_ID)) + .get(); + fga.listObjects( + new ClientListObjectsRequest() + .user("user:anne") + .relation("viewer") + .type("document"), + new ClientListObjectsOptions().storeId(OVERRIDE_STORE_ID)) + .get(); + + verify(1, postRequestedFor(urlPathEqualTo("/stores/" + OVERRIDE_STORE_ID + "/write"))); + verify(1, postRequestedFor(urlPathEqualTo("/stores/" + OVERRIDE_STORE_ID + "/read"))); + verify(1, postRequestedFor(urlPathEqualTo("/stores/" + OVERRIDE_STORE_ID + "/list-objects"))); + } + + @Test + void listRelationsPassesTheStoreToItsChecks() throws Exception { + client(CONFIGURED_STORE_ID) + .listRelations( + new ClientListRelationsRequest() + .user("user:anne") + .relations(List.of("viewer", "editor")) + ._object("document:1"), + new ClientListRelationsOptions().storeId(OVERRIDE_STORE_ID)) + .get(); + + verify(2, postRequestedFor(urlPathEqualTo("/stores/" + OVERRIDE_STORE_ID + "/check"))); + verify(0, postRequestedFor(urlPathEqualTo("/stores/" + CONFIGURED_STORE_ID + "/check"))); + } + + @Test + void batchCheckUsesTheStoreFromTheOptions() throws Exception { + client(CONFIGURED_STORE_ID) + .batchCheck( + new ClientBatchCheckRequest() + .checks(List.of(new ClientBatchCheckItem() + .user("user:anne") + .relation("viewer") + ._object("document:1") + .correlationId("1"))), + new ClientBatchCheckOptions().storeId(OVERRIDE_STORE_ID)) + .get(); + + verify(1, postRequestedFor(urlPathEqualTo("/stores/" + OVERRIDE_STORE_ID + "/batch-check"))); + } + + @Test + void streamedListObjectsUsesTheStoreFromTheOptions() throws Exception { + client(CONFIGURED_STORE_ID) + .streamedListObjects( + new ClientListObjectsRequest() + .user("user:anne") + .relation("viewer") + .type("document"), + new ClientStreamedListObjectsOptions().storeId(OVERRIDE_STORE_ID), + response -> {}) + .get(); + + verify(1, postRequestedFor(urlPathEqualTo("/stores/" + OVERRIDE_STORE_ID + "/streamed-list-objects"))); + } +}