Skip to content

Commit f169187

Browse files
committed
feat: use build buulder chain for consistency
1 parent 1f08814 commit f169187

8 files changed

Lines changed: 126 additions & 71 deletions

File tree

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1199,7 +1199,8 @@ RawRequestBuilder request = RawRequestBuilder.builder("POST", "/stores/{store_id
11991199
.queryParam("page_size", "20")
12001200
.queryParam("continuation_token", "eyJwayI6...")
12011201
.body(requestBody)
1202-
.header("X-Experimental-Feature", "enabled");
1202+
.header("X-Experimental-Feature", "enabled")
1203+
.build();
12031204
```
12041205

12051206
#### Example: Calling a new "Custom Endpoint" endpoint and handling raw response

docs/RawApi.md

Lines changed: 31 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@ OpenFgaClient client = new OpenFgaClient(config);
1010
// Build request
1111
RawRequestBuilder request = RawRequestBuilder.builder("POST", "/stores/{store_id}/check")
1212
.pathParam("store_id", storeId)
13-
.body(Map.of("tuple_key", Map.of("user", "user:jon", "relation", "reader", "object", "doc:1")));
13+
.body(Map.of("tuple_key", Map.of("user", "user:jon", "relation", "reader", "object", "doc:1")))
14+
.build();
1415

1516
// Execute - typed response
1617
ApiResponse<CheckResponse> response = client.raw().send(request, CheckResponse.class).get();
@@ -34,15 +35,17 @@ RawRequestBuilder.builder(String method, String path)
3435
.queryParam(String key, String value) // Add query parameter, URL-encoded
3536
.header(String key, String value) // Add HTTP header
3637
.body(Object body) // Set request body (auto-serialized to JSON)
38+
.build() // Complete the builder (required)
3739
```
3840

3941
**Example:**
4042
```java
41-
RawRequestBuilder.builder("POST", "/stores/{store_id}/write")
43+
RawRequestBuilder request = RawRequestBuilder.builder("POST", "/stores/{store_id}/write")
4244
.pathParam("store_id", "01ABC")
4345
.queryParam("dry_run", "true")
4446
.header("X-Request-ID", "uuid")
45-
.body(requestObject);
47+
.body(requestObject)
48+
.build();
4649
```
4750

4851
### RawApi
@@ -69,10 +72,11 @@ T getData() // Deserialized data
6972

7073
## Examples
7174

72-
### Typed Response
75+
### GET Request
7376
```java
7477
RawRequestBuilder request = RawRequestBuilder.builder("GET", "/stores/{store_id}/feature")
75-
.pathParam("store_id", storeId);
78+
.pathParam("store_id", storeId)
79+
.build();
7680

7781
client.raw().send(request, FeatureResponse.class)
7882
.thenAccept(r -> System.out.println("Status: " + r.getStatusCode()));
@@ -83,7 +87,8 @@ client.raw().send(request, FeatureResponse.class)
8387
RawRequestBuilder request = RawRequestBuilder.builder("POST", "/stores/{store_id}/bulk-delete")
8488
.pathParam("store_id", storeId)
8589
.queryParam("force", "true")
86-
.body(new BulkDeleteRequest("2023-01-01", "user", 1000));
90+
.body(new BulkDeleteRequest("2023-01-01", "user", 1000))
91+
.build();
8792

8893
client.raw().send(request, BulkDeleteResponse.class).get();
8994
```
@@ -100,15 +105,17 @@ RawRequestBuilder.builder("GET", "/stores/{store_id}/items")
100105
.pathParam("store_id", storeId)
101106
.queryParam("page", "1")
102107
.queryParam("limit", "50")
103-
.queryParam("sort", "created_at");
108+
.queryParam("sort", "created_at")
109+
.build();
104110
```
105111

106112
### Custom Headers
107113
```java
108114
RawRequestBuilder.builder("POST", "/stores/{store_id}/action")
109115
.header("X-Request-ID", UUID.randomUUID().toString())
110116
.header("X-Idempotency-Key", "key-123")
111-
.body(data);
117+
.body(data)
118+
.build();
112119
```
113120

114121
### Error Handling
@@ -125,29 +132,34 @@ client.raw().send(request, ResponseType.class)
125132

126133
### Map as Request Body
127134
```java
128-
.body(Map.of(
129-
"setting", "value",
130-
"enabled", true,
131-
"threshold", 100,
132-
"options", List.of("opt1", "opt2")
133-
))
135+
RawRequestBuilder.builder("POST", "/stores/{store_id}/settings")
136+
.pathParam("store_id", storeId)
137+
.body(Map.of(
138+
"setting", "value",
139+
"enabled", true,
140+
"threshold", 100,
141+
"options", List.of("opt1", "opt2")
142+
))
143+
.build();
134144
```
135145

136146
## Notes
137147

138148
- Path/query parameters are URL-encoded automatically
139149
- Authentication tokens injected from client config
140-
- Retries on 429, 5xx errors
141150
- `{store_id}` auto-replaced if not provided via `.pathParam()`
142151

143152
## Migration to Typed Methods
144153

154+
When SDK adds typed methods for an endpoint, you can migrate from Raw API:
155+
145156
```java
146157
// Raw API
147-
client.raw().send(
148-
RawRequestBuilder.builder("POST", "/stores/{store_id}/check").body(req),
149-
CheckResponse.class
150-
).get();
158+
RawRequestBuilder request = RawRequestBuilder.builder("POST", "/stores/{store_id}/check")
159+
.body(req)
160+
.build();
161+
162+
client.raw().send(request, CheckResponse.class).get();
151163

152164
// Typed SDK (when available)
153165
client.check(req).get();

examples/raw-api/README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,8 @@ RawRequestBuilder request = RawRequestBuilder.builder("POST", "/stores/{store_id
6161
.queryParam("page_size", "20")
6262
.queryParam("continuation_token", "eyJwayI6...")
6363
.body(requestBody)
64-
.header("X-Custom-Header", "value");
64+
.header("X-Custom-Header", "value")
65+
.build();
6566
```
6667

6768
### Response Handling

examples/raw-api/src/main/java/dev/openfga/sdk/example/RawApiExample.java

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ public static void main(String[] args) throws Exception {
6363
private static String listStoresExample(OpenFgaClient fgaClient) {
6464
try {
6565
// Build the raw request for GET /stores
66-
RawRequestBuilder request = RawRequestBuilder.builder("GET", "/stores");
66+
RawRequestBuilder request = RawRequestBuilder.builder("GET", "/stores").build();
6767

6868
// Execute with typed response
6969
var response = fgaClient
@@ -102,7 +102,8 @@ private static String listStoresExample(OpenFgaClient fgaClient) {
102102
private static String createStoreForExamples(OpenFgaClient fgaClient) throws Exception {
103103
String storeName = "raw-api-example-" + UUID.randomUUID().toString().substring(0, 8);
104104
RawRequestBuilder request = RawRequestBuilder.builder("POST", "/stores")
105-
.body(Map.of("name", storeName));
105+
.body(Map.of("name", storeName))
106+
.build();
106107

107108
// Use typed response instead of manual JSON parsing
108109
var response = fgaClient.raw().send(request, CreateStoreResponse.class).get();
@@ -116,7 +117,8 @@ private static String createStoreForExamples(OpenFgaClient fgaClient) throws Exc
116117
private static void getStoreRawJsonExample(OpenFgaClient fgaClient, String storeId) {
117118
try {
118119
RawRequestBuilder request = RawRequestBuilder.builder("GET", "/stores/{store_id}")
119-
.pathParam("store_id", storeId);
120+
.pathParam("store_id", storeId)
121+
.build();
120122

121123
// Execute and get raw JSON string
122124
var response = fgaClient.raw().send(request).get();
@@ -136,7 +138,8 @@ private static void getStoreRawJsonExample(OpenFgaClient fgaClient, String store
136138
private static void listStoresWithPaginationExample(OpenFgaClient fgaClient) {
137139
try {
138140
RawRequestBuilder request = RawRequestBuilder.builder("GET", "/stores")
139-
.queryParam("page_size", "2");
141+
.queryParam("page_size", "2")
142+
.build();
140143

141144
var response = fgaClient
142145
.raw()
@@ -167,7 +170,8 @@ private static void createStoreWithHeadersExample(OpenFgaClient fgaClient) {
167170
RawRequestBuilder request = RawRequestBuilder.builder("POST", "/stores")
168171
.header("X-Example-Header", "custom-value")
169172
.header("X-Request-ID", "req-" + UUID.randomUUID())
170-
.body(Map.of("name", storeName));
173+
.body(Map.of("name", storeName))
174+
.build();
171175

172176
var response = fgaClient.raw().send(request).get();
173177

@@ -190,7 +194,8 @@ private static void errorHandlingExample(OpenFgaClient fgaClient) {
190194
try {
191195
// Try to get a non-existent store
192196
RawRequestBuilder request = RawRequestBuilder.builder("GET", "/stores/{store_id}")
193-
.pathParam("store_id", "01ZZZZZZZZZZZZZZZZZZZZZZZ9");
197+
.pathParam("store_id", "01ZZZZZZZZZZZZZZZZZZZZZZZ9")
198+
.build();
194199

195200
var response = fgaClient.raw().send(request).get();
196201

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@
1616
* <pre>{@code
1717
* RawRequestBuilder request = RawRequestBuilder.builder("POST", "/stores/{store_id}/endpoint")
1818
* .pathParam("store_id", storeId)
19-
* .body(requestData);
19+
* .body(requestData)
20+
* .build();
2021
*
2122
* // Typed response
2223
* ApiResponse<ResponseType> response = client.raw().send(request, ResponseType.class).get();

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

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@
1414
* RawRequestBuilder request = RawRequestBuilder.builder("POST", "/stores/{store_id}/endpoint")
1515
* .pathParam("store_id", storeId)
1616
* .queryParam("limit", "50")
17-
* .body(requestObject);
17+
* .body(requestObject)
18+
* .build();
1819
* }</pre>
1920
*/
2021
public class RawRequestBuilder {
@@ -114,6 +115,18 @@ public RawRequestBuilder body(Object body) {
114115
this.body = body;
115116
return this;
116117
}
118+
/**
119+
* Builds and returns the request for use with the Raw API.
120+
* This method must be called to complete request construction.
121+
*
122+
* <p>This is required for consistency with other OpenFGA SDKs (e.g., Go SDK)
123+
* and follows the standard builder pattern.</p>
124+
*
125+
* @return This builder instance (ready to be passed to {@link RawApi#send})
126+
*/
127+
public RawRequestBuilder build() {
128+
return this;
129+
}
117130

118131
String getMethod() {
119132
return method;

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

Lines changed: 31 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ public void rawRequest_listStores() throws Exception {
5050
createStoreUsingRawRequest(storeName);
5151

5252
// Use raw API to list stores (equivalent to GET /stores)
53-
RawRequestBuilder request = RawRequestBuilder.builder("GET", "/stores");
53+
RawRequestBuilder request = RawRequestBuilder.builder("GET", "/stores").build();
5454

5555
ApiResponse<ListStoresResponse> response =
5656
fga.raw().send(request, ListStoresResponse.class).get();
@@ -83,7 +83,8 @@ public void rawRequest_createStore_typedResponse() throws Exception {
8383
requestBody.put("name", storeName);
8484

8585
// Use raw API to create store (equivalent to POST /stores)
86-
RawRequestBuilder request = RawRequestBuilder.builder("POST", "/stores").body(requestBody);
86+
RawRequestBuilder request =
87+
RawRequestBuilder.builder("POST", "/stores").body(requestBody).build();
8788

8889
ApiResponse<CreateStoreResponse> response =
8990
fga.raw().send(request, CreateStoreResponse.class).get();
@@ -112,7 +113,8 @@ public void rawRequest_createStore_rawJsonResponse() throws Exception {
112113
requestBody.put("name", storeName);
113114

114115
// Use raw API to create store and get raw JSON response
115-
RawRequestBuilder request = RawRequestBuilder.builder("POST", "/stores").body(requestBody);
116+
RawRequestBuilder request =
117+
RawRequestBuilder.builder("POST", "/stores").body(requestBody).build();
116118

117119
ApiResponse<String> response = fga.raw().send(request).get();
118120

@@ -142,8 +144,9 @@ public void rawRequest_getStore_withPathParams() throws Exception {
142144
String storeId = createStoreUsingRawRequest(storeName);
143145

144146
// Use raw API to get store details (equivalent to GET /stores/{store_id})
145-
RawRequestBuilder request =
146-
RawRequestBuilder.builder("GET", "/stores/{store_id}").pathParam("store_id", storeId);
147+
RawRequestBuilder request = RawRequestBuilder.builder("GET", "/stores/{store_id}")
148+
.pathParam("store_id", storeId)
149+
.build();
147150

148151
ApiResponse<GetStoreResponse> response =
149152
fga.raw().send(request, GetStoreResponse.class).get();
@@ -170,7 +173,8 @@ public void rawRequest_automaticStoreIdReplacement() throws Exception {
170173
fga.setStoreId(storeId);
171174

172175
// Use raw API WITHOUT providing store_id path param - it should be auto-replaced
173-
RawRequestBuilder request = RawRequestBuilder.builder("GET", "/stores/{store_id}");
176+
RawRequestBuilder request =
177+
RawRequestBuilder.builder("GET", "/stores/{store_id}").build();
174178

175179
ApiResponse<GetStoreResponse> response =
176180
fga.raw().send(request, GetStoreResponse.class).get();
@@ -221,7 +225,8 @@ public void rawRequest_writeAuthorizationModel() throws Exception {
221225

222226
// Use raw API to write authorization model
223227
RawRequestBuilder request = RawRequestBuilder.builder("POST", "/stores/{store_id}/authorization-models")
224-
.body(requestBody);
228+
.body(requestBody)
229+
.build();
225230

226231
ApiResponse<WriteAuthorizationModelResponse> response =
227232
fga.raw().send(request, WriteAuthorizationModelResponse.class).get();
@@ -252,7 +257,8 @@ public void rawRequest_readAuthorizationModels_withQueryParams() throws Exceptio
252257
// Use raw API to read authorization models with query parameters
253258
RawRequestBuilder request = RawRequestBuilder.builder("GET", "/stores/{store_id}/authorization-models")
254259
.queryParam("page_size", "10")
255-
.queryParam("continuation_token", "");
260+
.queryParam("continuation_token", "")
261+
.build();
256262

257263
ApiResponse<ReadAuthorizationModelsResponse> response =
258264
fga.raw().send(request, ReadAuthorizationModelsResponse.class).get();
@@ -295,16 +301,12 @@ public void rawRequest_check() throws Exception {
295301
tupleKey.put("object", "document:budget");
296302
checkBody.put("tuple_key", tupleKey);
297303

298-
RawRequestBuilder request =
299-
RawRequestBuilder.builder("POST", "/stores/{store_id}/check").body(checkBody);
304+
RawRequestBuilder request = RawRequestBuilder.builder("POST", "/stores/{store_id}/check")
305+
.body(checkBody)
306+
.build();
300307

301308
ApiResponse<CheckResponse> response =
302309
fga.raw().send(request, CheckResponse.class).get();
303-
304-
// Verify response
305-
assertNotNull(response);
306-
assertEquals(200, response.getStatusCode());
307-
assertNotNull(response.getData());
308310
assertTrue(response.getData().getAllowed(), "Alice should be allowed to read the document");
309311

310312
System.out.println("✓ Successfully performed check using raw request");
@@ -325,7 +327,8 @@ public void rawRequest_withCustomHeaders() throws Exception {
325327
RawRequestBuilder request = RawRequestBuilder.builder("POST", "/stores")
326328
.body(requestBody)
327329
.header("X-Custom-Header", "custom-value")
328-
.header("X-Request-ID", "test-123");
330+
.header("X-Request-ID", "test-123")
331+
.build();
329332

330333
ApiResponse<CreateStoreResponse> response =
331334
fga.raw().send(request, CreateStoreResponse.class).get();
@@ -343,8 +346,9 @@ public void rawRequest_withCustomHeaders() throws Exception {
343346
@Test
344347
public void rawRequest_errorHandling_notFound() throws Exception {
345348
// Try to get a non-existent store
346-
RawRequestBuilder request =
347-
RawRequestBuilder.builder("GET", "/stores/{store_id}").pathParam("store_id", "non-existent-store-id");
349+
RawRequestBuilder request = RawRequestBuilder.builder("GET", "/stores/{store_id}")
350+
.pathParam("store_id", "non-existent-store-id")
351+
.build();
348352

349353
// Should throw an exception
350354
try {
@@ -372,7 +376,9 @@ public void rawRequest_listStores_withPagination() throws Exception {
372376
}
373377

374378
// Use raw API to list stores with pagination
375-
RawRequestBuilder request = RawRequestBuilder.builder("GET", "/stores").queryParam("page_size", "2");
379+
RawRequestBuilder request = RawRequestBuilder.builder("GET", "/stores")
380+
.queryParam("page_size", "2")
381+
.build();
376382

377383
ApiResponse<ListStoresResponse> response =
378384
fga.raw().send(request, ListStoresResponse.class).get();
@@ -396,7 +402,8 @@ private String createStoreUsingRawRequest(String storeName) throws Exception {
396402
Map<String, Object> requestBody = new HashMap<>();
397403
requestBody.put("name", storeName);
398404

399-
RawRequestBuilder request = RawRequestBuilder.builder("POST", "/stores").body(requestBody);
405+
RawRequestBuilder request =
406+
RawRequestBuilder.builder("POST", "/stores").body(requestBody).build();
400407

401408
ApiResponse<CreateStoreResponse> response =
402409
fga.raw().send(request, CreateStoreResponse.class).get();
@@ -431,7 +438,8 @@ private String writeSimpleAuthorizationModel(String storeId) throws Exception {
431438

432439
RawRequestBuilder request = RawRequestBuilder.builder("POST", "/stores/{store_id}/authorization-models")
433440
.pathParam("store_id", storeId)
434-
.body(requestBody);
441+
.body(requestBody)
442+
.build();
435443

436444
ApiResponse<WriteAuthorizationModelResponse> response =
437445
fga.raw().send(request, WriteAuthorizationModelResponse.class).get();
@@ -451,7 +459,8 @@ private void writeTupleUsingRawRequest(String storeId, String user, String relat
451459

452460
RawRequestBuilder request = RawRequestBuilder.builder("POST", "/stores/{store_id}/write")
453461
.pathParam("store_id", storeId)
454-
.body(requestBody);
462+
.body(requestBody)
463+
.build();
455464

456465
fga.raw().send(request, Object.class).get();
457466
}

0 commit comments

Comments
 (0)