Skip to content

Commit a84d17c

Browse files
authored
fix: ensure headers can be set and overriden (#226)
* fix(ClientConfiguration): override defaultHeaders to return correct type * fix(readAuthorizationModel): correctly handle options with no modelID set * fix(ClientListRelationsOptions): include headers when converting to ClientBatchCheckOptions * test: add tests to cover headers * docs: add docs to readme for header usage * test: update listRelations tests to have correct header value * refactor: address review comments regarding npe, readme update and copying headers * test: add test for null headers * refactor: change header handling to support immutable maps * refactor: update writeNonTransaction and batchCheck to handle immutable map for headers
1 parent d25291a commit a84d17c

File tree

7 files changed

+1088
-32
lines changed

7 files changed

+1088
-32
lines changed

README.md

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ This is an autogenerated Java SDK for OpenFGA. It provides a wrapper around the
1919
- [Installation](#installation)
2020
- [Getting Started](#getting-started)
2121
- [Initializing the API Client](#initializing-the-api-client)
22+
- [Custom Headers](#custom-headers)
2223
- [Get your Store ID](#get-your-store-id)
2324
- [Calling the API](#calling-the-api)
2425
- [Stores](#stores)
@@ -236,6 +237,68 @@ public class Example {
236237
}
237238
```
238239

240+
### Custom Headers
241+
242+
#### Default Headers
243+
244+
You can set default headers to be sent with every request by using the `defaultHeaders` property of the `ClientConfiguration` class.
245+
246+
```java
247+
import com.fasterxml.jackson.databind.ObjectMapper;
248+
import dev.openfga.sdk.api.client.OpenFgaClient;
249+
import dev.openfga.sdk.api.configuration.ClientConfiguration;
250+
251+
import java.net.http.HttpClient;
252+
import java.util.Map;
253+
254+
public class Example {
255+
public static void main(String[] args) throws Exception {
256+
var config = new ClientConfiguration()
257+
.apiUrl(System.getenv("FGA_API_URL"))
258+
.storeId(System.getenv("FGA_STORE_ID"))
259+
.authorizationModelId(System.getenv("FGA_MODEL_ID"))
260+
.defaultHeaders(Map.of(
261+
"X-Custom-Header", "default-value",
262+
"X-Request-Source", "my-app"
263+
));
264+
265+
var fgaClient = new OpenFgaClient(config);
266+
}
267+
}
268+
```
269+
270+
#### Per-request Headers
271+
272+
You can set custom headers to be sent with a specific request by using the `additionalHeaders` property of the options classes (e.g. `ClientReadOptions`, `ClientWriteOptions`, etc.).
273+
274+
```java
275+
import com.fasterxml.jackson.databind.ObjectMapper;
276+
import dev.openfga.sdk.api.client.OpenFgaClient;
277+
import dev.openfga.sdk.api.configuration.ClientConfiguration;
278+
import java.net.http.HttpClient;
279+
280+
public class Example {
281+
public static void main(String[] args) throws Exception {
282+
var config = new ClientConfiguration()
283+
.apiUrl(System.getenv("FGA_API_URL"))
284+
.storeId(System.getenv("FGA_STORE_ID"))
285+
.authorizationModelId(System.getenv("FGA_MODEL_ID"))
286+
.defaultHeaders(Map.of(
287+
"X-Custom-Header", "default-value",
288+
"X-Request-Source", "my-app"
289+
));
290+
291+
var fgaClient = new OpenFgaClient(config);
292+
var options = new ClientReadOptions()
293+
.additionalHeaders(Map.of(
294+
"X-Request-Id", "123e4567-e89b-12d3-a456-426614174000",
295+
"X-Custom-Header", "overridden-value" // this will override the default value for this request only
296+
)
297+
);
298+
var response = fgaClient.read(request, options).get();
299+
}
300+
}
301+
```
239302

240303
### Get your Store ID
241304

src/main/java/dev/openfga/sdk/api/client/OpenFgaClient.java

Lines changed: 34 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -248,7 +248,13 @@ public CompletableFuture<ClientReadAuthorizationModelResponse> readAuthorization
248248
ClientReadAuthorizationModelOptions options) throws FgaInvalidParameterException {
249249
configuration.assertValid();
250250
String storeId = configuration.getStoreIdChecked();
251-
String authorizationModelId = options.getAuthorizationModelIdChecked();
251+
// Set authorizationModelId from options if available; otherwise, require a valid configuration value
252+
String authorizationModelId;
253+
if (options != null && !isNullOrWhitespace(options.getAuthorizationModelId())) {
254+
authorizationModelId = options.getAuthorizationModelIdChecked();
255+
} else {
256+
authorizationModelId = configuration.getAuthorizationModelIdChecked();
257+
}
252258
var overrides = new ConfigurationOverride().addHeaders(options);
253259
return call(() -> api.readAuthorizationModel(storeId, authorizationModelId, overrides))
254260
.thenApply(ClientReadAuthorizationModelResponse::new);
@@ -552,12 +558,12 @@ private CompletableFuture<ClientWriteResponse> writeNonTransaction(
552558
? writeOptions
553559
: new ClientWriteOptions().transactionChunkSize(DEFAULT_MAX_METHOD_PARALLEL_REQS);
554560

555-
if (options.getAdditionalHeaders() == null) {
556-
options.additionalHeaders(new HashMap<>());
557-
}
558-
options.getAdditionalHeaders().putIfAbsent(CLIENT_METHOD_HEADER, "Write");
559-
options.getAdditionalHeaders()
560-
.putIfAbsent(CLIENT_BULK_REQUEST_ID_HEADER, randomUUID().toString());
561+
HashMap<String, String> headers = options.getAdditionalHeaders() != null
562+
? new HashMap<>(options.getAdditionalHeaders())
563+
: new HashMap<>();
564+
headers.putIfAbsent(CLIENT_METHOD_HEADER, "Write");
565+
headers.putIfAbsent(CLIENT_BULK_REQUEST_ID_HEADER, randomUUID().toString());
566+
options.additionalHeaders(headers);
561567

562568
int chunkSize = options.getTransactionChunkSize();
563569

@@ -832,12 +838,13 @@ public CompletableFuture<List<ClientBatchCheckClientResponse>> clientBatchCheck(
832838
var options = batchCheckOptions != null
833839
? batchCheckOptions
834840
: new ClientBatchCheckClientOptions().maxParallelRequests(DEFAULT_MAX_METHOD_PARALLEL_REQS);
835-
if (options.getAdditionalHeaders() == null) {
836-
options.additionalHeaders(new HashMap<>());
837-
}
838-
options.getAdditionalHeaders().putIfAbsent(CLIENT_METHOD_HEADER, "ClientBatchCheck");
839-
options.getAdditionalHeaders()
840-
.putIfAbsent(CLIENT_BULK_REQUEST_ID_HEADER, randomUUID().toString());
841+
842+
HashMap<String, String> headers = options.getAdditionalHeaders() != null
843+
? new HashMap<>(options.getAdditionalHeaders())
844+
: new HashMap<>();
845+
headers.putIfAbsent(CLIENT_METHOD_HEADER, "ClientBatchCheck");
846+
headers.putIfAbsent(CLIENT_BULK_REQUEST_ID_HEADER, randomUUID().toString());
847+
options.additionalHeaders(headers);
841848

842849
int maxParallelRequests = options.getMaxParallelRequests() != null
843850
? options.getMaxParallelRequests()
@@ -892,12 +899,13 @@ public CompletableFuture<ClientBatchCheckResponse> batchCheck(
892899
: new ClientBatchCheckOptions()
893900
.maxParallelRequests(DEFAULT_MAX_METHOD_PARALLEL_REQS)
894901
.maxBatchSize(DEFAULT_MAX_BATCH_SIZE);
895-
if (options.getAdditionalHeaders() == null) {
896-
options.additionalHeaders(new HashMap<>());
897-
}
898-
options.getAdditionalHeaders().putIfAbsent(CLIENT_METHOD_HEADER, "BatchCheck");
899-
options.getAdditionalHeaders()
900-
.putIfAbsent(CLIENT_BULK_REQUEST_ID_HEADER, randomUUID().toString());
902+
903+
HashMap<String, String> headers = options.getAdditionalHeaders() != null
904+
? new HashMap<>(options.getAdditionalHeaders())
905+
: new HashMap<>();
906+
headers.putIfAbsent(CLIENT_METHOD_HEADER, "BatchCheck");
907+
headers.putIfAbsent(CLIENT_BULK_REQUEST_ID_HEADER, randomUUID().toString());
908+
options.additionalHeaders(headers);
901909

902910
Map<String, ClientBatchCheckItem> correlationIdToCheck = new HashMap<>();
903911

@@ -1124,12 +1132,13 @@ public CompletableFuture<ClientListRelationsResponse> listRelations(
11241132
var options = listRelationsOptions != null
11251133
? listRelationsOptions
11261134
: new ClientListRelationsOptions().maxParallelRequests(DEFAULT_MAX_METHOD_PARALLEL_REQS);
1127-
if (options.getAdditionalHeaders() == null) {
1128-
options.additionalHeaders(new HashMap<>());
1129-
}
1130-
options.getAdditionalHeaders().putIfAbsent(CLIENT_METHOD_HEADER, "ListRelations");
1131-
options.getAdditionalHeaders()
1132-
.putIfAbsent(CLIENT_BULK_REQUEST_ID_HEADER, randomUUID().toString());
1135+
1136+
HashMap<String, String> headers = options.getAdditionalHeaders() != null
1137+
? new HashMap<>(options.getAdditionalHeaders())
1138+
: new HashMap<>();
1139+
headers.putIfAbsent(CLIENT_METHOD_HEADER, "ListRelations");
1140+
headers.putIfAbsent(CLIENT_BULK_REQUEST_ID_HEADER, randomUUID().toString());
1141+
options.additionalHeaders(headers);
11331142

11341143
var batchCheckRequests = request.getRelations().stream()
11351144
.map(relation -> new ClientCheckRequest()

src/main/java/dev/openfga/sdk/api/configuration/ClientBatchCheckClientOptions.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
package dev.openfga.sdk.api.configuration;
1414

1515
import dev.openfga.sdk.api.model.ConsistencyPreference;
16+
import java.util.HashMap;
1617
import java.util.Map;
1718

1819
public class ClientBatchCheckClientOptions implements AdditionalHeadersSupplier {
@@ -60,7 +61,7 @@ public ConsistencyPreference getConsistency() {
6061

6162
public ClientCheckOptions asClientCheckOptions() {
6263
return new ClientCheckOptions()
63-
.additionalHeaders(additionalHeaders)
64+
.additionalHeaders(additionalHeaders != null ? new HashMap<>(additionalHeaders) : null)
6465
.authorizationModelId(authorizationModelId)
6566
.consistency(consistency);
6667
}

src/main/java/dev/openfga/sdk/api/configuration/ClientConfiguration.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,4 +140,10 @@ public ClientConfiguration telemetryConfiguration(TelemetryConfiguration telemet
140140
super.telemetryConfiguration(telemetryConfiguration);
141141
return this;
142142
}
143+
144+
@Override
145+
public ClientConfiguration defaultHeaders(java.util.Map<String, String> defaultHeaders) {
146+
super.defaultHeaders(defaultHeaders);
147+
return this;
148+
}
143149
}

src/main/java/dev/openfga/sdk/api/configuration/ClientListRelationsOptions.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
package dev.openfga.sdk.api.configuration;
1414

1515
import dev.openfga.sdk.api.model.ConsistencyPreference;
16+
import java.util.HashMap;
1617
import java.util.Map;
1718

1819
public class ClientListRelationsOptions implements AdditionalHeadersSupplier {
@@ -61,6 +62,7 @@ public ConsistencyPreference getConsistency() {
6162
public ClientBatchCheckClientOptions asClientBatchCheckClientOptions() {
6263
return new ClientBatchCheckClientOptions()
6364
.authorizationModelId(authorizationModelId)
65+
.additionalHeaders(additionalHeaders != null ? new HashMap<>(additionalHeaders) : null)
6466
.maxParallelRequests(maxParallelRequests)
6567
.consistency(consistency);
6668
}

0 commit comments

Comments
 (0)