Skip to content

Commit 8320174

Browse files
authored
chore: refactor HttpMethods to enum (#277)
* chore: refactor HttpMethods to enum * fix: duplicate lines and adress comments
1 parent 9cb5be1 commit 8320174

File tree

9 files changed

+149
-85
lines changed

9 files changed

+149
-85
lines changed

docs/ApiExecutor.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ Direct HTTP access to OpenFGA endpoints.
88
OpenFgaClient client = new OpenFgaClient(config);
99

1010
// Build request
11-
ApiExecutorRequestBuilder request = ApiExecutorRequestBuilder.builder("POST", "/stores/{store_id}/check")
11+
ApiExecutorRequestBuilder request = ApiExecutorRequestBuilder.builder(HttpMethod.POST, "/stores/{store_id}/check")
1212
.pathParam("store_id", storeId)
1313
.body(Map.of("tuple_key", Map.of("user", "user:jon", "relation", "reader", "object", "doc:1")))
1414
.build();
@@ -26,7 +26,7 @@ ApiResponse<String> rawResponse = client.apiExecutor().send(request).get();
2626

2727
**Factory:**
2828
```java
29-
ApiExecutorRequestBuilder.builder(String method, String path)
29+
ApiExecutorRequestBuilder.builder(HttpMethod method, String path)
3030
```
3131

3232
**Methods:**
@@ -40,7 +40,7 @@ ApiExecutorRequestBuilder.builder(String method, String path)
4040

4141
**Example:**
4242
```java
43-
ApiExecutorRequestBuilder request = ApiExecutorRequestBuilder.builder("POST", "/stores/{store_id}/write")
43+
ApiExecutorRequestBuilder request = ApiExecutorRequestBuilder.builder(HttpMethod.POST, "/stores/{store_id}/write")
4444
.pathParam("store_id", "01ABC")
4545
.queryParam("dry_run", "true")
4646
.header("X-Request-ID", "uuid")
@@ -74,7 +74,7 @@ T getData() // Deserialized data
7474

7575
### GET Request
7676
```java
77-
ApiExecutorRequestBuilder request = ApiExecutorRequestBuilder.builder("GET", "/stores/{store_id}/feature")
77+
ApiExecutorRequestBuilder request = ApiExecutorRequestBuilder.builder(HttpMethod.GET, "/stores/{store_id}/feature")
7878
.pathParam("store_id", storeId)
7979
.build();
8080

@@ -84,7 +84,7 @@ client.apiExecutor().send(request, FeatureResponse.class)
8484

8585
### POST with Body
8686
```java
87-
ApiExecutorRequestBuilder request = ApiExecutorRequestBuilder.builder("POST", "/stores/{store_id}/bulk-delete")
87+
ApiExecutorRequestBuilder request = ApiExecutorRequestBuilder.builder(HttpMethod.POST, "/stores/{store_id}/bulk-delete")
8888
.pathParam("store_id", storeId)
8989
.queryParam("force", "true")
9090
.body(new BulkDeleteRequest("2023-01-01", "user", 1000))

examples/api-executor/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ All requests will succeed (except #5 which intentionally triggers an error for d
5656
Build requests using the builder pattern:
5757

5858
```java
59-
ApiExecutorRequestBuilder request = ApiExecutorRequestBuilder.builder("POST", "/stores/{store_id}/custom-endpoint")
59+
ApiExecutorRequestBuilder request = ApiExecutorRequestBuilder.builder(HttpMethod.POST, "/stores/{store_id}/custom-endpoint")
6060
.pathParam("store_id", storeId)
6161
.queryParam("page_size", "20")
6262
.queryParam("continuation_token", "eyJwayI6...")

examples/api-executor/src/main/java/dev/openfga/sdk/example/ApiExecutorExample.java

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package dev.openfga.sdk.example;
22

3+
import dev.openfga.sdk.api.client.HttpMethod;
34
import dev.openfga.sdk.api.client.OpenFgaClient;
45
import dev.openfga.sdk.api.client.ApiExecutorRequestBuilder;
56
import dev.openfga.sdk.api.configuration.ClientConfiguration;
@@ -63,7 +64,7 @@ public static void main(String[] args) throws Exception {
6364
private static String listStoresExample(OpenFgaClient fgaClient) {
6465
try {
6566
// Build the raw request for GET /stores
66-
ApiExecutorRequestBuilder request = ApiExecutorRequestBuilder.builder("GET", "/stores").build();
67+
ApiExecutorRequestBuilder request = ApiExecutorRequestBuilder.builder(HttpMethod.GET, "/stores").build();
6768

6869
// Execute with typed response
6970
var response = fgaClient
@@ -101,7 +102,7 @@ private static String listStoresExample(OpenFgaClient fgaClient) {
101102
*/
102103
private static String createStoreForExamples(OpenFgaClient fgaClient) throws Exception {
103104
String storeName = "api-executor-example-" + UUID.randomUUID().toString().substring(0, 8);
104-
ApiExecutorRequestBuilder request = ApiExecutorRequestBuilder.builder("POST", "/stores")
105+
ApiExecutorRequestBuilder request = ApiExecutorRequestBuilder.builder(HttpMethod.POST, "/stores")
105106
.body(Map.of("name", storeName))
106107
.build();
107108

@@ -116,7 +117,7 @@ private static String createStoreForExamples(OpenFgaClient fgaClient) throws Exc
116117
*/
117118
private static void getStoreRawJsonExample(OpenFgaClient fgaClient, String storeId) {
118119
try {
119-
ApiExecutorRequestBuilder request = ApiExecutorRequestBuilder.builder("GET", "/stores/{store_id}")
120+
ApiExecutorRequestBuilder request = ApiExecutorRequestBuilder.builder(HttpMethod.GET, "/stores/{store_id}")
120121
.pathParam("store_id", storeId)
121122
.build();
122123

@@ -137,7 +138,7 @@ private static void getStoreRawJsonExample(OpenFgaClient fgaClient, String store
137138
*/
138139
private static void listStoresWithPaginationExample(OpenFgaClient fgaClient) {
139140
try {
140-
ApiExecutorRequestBuilder request = ApiExecutorRequestBuilder.builder("GET", "/stores")
141+
ApiExecutorRequestBuilder request = ApiExecutorRequestBuilder.builder(HttpMethod.GET, "/stores")
141142
.queryParam("page_size", "2")
142143
.build();
143144

@@ -167,7 +168,7 @@ private static void listStoresWithPaginationExample(OpenFgaClient fgaClient) {
167168
private static void createStoreWithHeadersExample(OpenFgaClient fgaClient) {
168169
try {
169170
String storeName = "raw-api-custom-headers-" + UUID.randomUUID().toString().substring(0, 8);
170-
ApiExecutorRequestBuilder request = ApiExecutorRequestBuilder.builder("POST", "/stores")
171+
ApiExecutorRequestBuilder request = ApiExecutorRequestBuilder.builder(HttpMethod.POST, "/stores")
171172
.header("X-Example-Header", "custom-value")
172173
.header("X-Request-ID", "req-" + UUID.randomUUID())
173174
.body(Map.of("name", storeName))
@@ -192,8 +193,7 @@ private static void createStoreWithHeadersExample(OpenFgaClient fgaClient) {
192193
*/
193194
private static void errorHandlingExample(OpenFgaClient fgaClient) {
194195
try {
195-
// Try to get a non-existent store
196-
ApiExecutorRequestBuilder request = ApiExecutorRequestBuilder.builder("GET", "/stores/{store_id}")
196+
ApiExecutorRequestBuilder request = ApiExecutorRequestBuilder.builder(HttpMethod.GET, "/stores/{store_id}")
197197
.pathParam("store_id", "01ZZZZZZZZZZZZZZZZZZZZZZZ9")
198198
.build();
199199

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
*
1515
* <p>Example:</p>
1616
* <pre>{@code
17-
* ApiExecutorRequestBuilder request = ApiExecutorRequestBuilder.builder("POST", "/stores/{store_id}/endpoint")
17+
* ApiExecutorRequestBuilder request = ApiExecutorRequestBuilder.builder(HttpMethod.POST, "/stores/{store_id}/endpoint")
1818
* .pathParam("store_id", storeId)
1919
* .body(requestData)
2020
* .build();

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

Lines changed: 10 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
import java.util.HashMap;
44
import java.util.Map;
5-
import java.util.Set;
65

76
/**
87
* Fluent builder for constructing HTTP requests to OpenFGA API endpoints.
@@ -11,25 +10,22 @@
1110
*
1211
* <p>Example:</p>
1312
* <pre>{@code
14-
* ApiExecutorRequestBuilder request = ApiExecutorRequestBuilder.builder("POST", "/stores/{store_id}/endpoint")
13+
* ApiExecutorRequestBuilder request = ApiExecutorRequestBuilder.builder(HttpMethod.POST, "/stores/{store_id}/endpoint")
1514
* .pathParam("store_id", storeId)
1615
* .queryParam("limit", "50")
1716
* .body(requestObject)
1817
* .build();
1918
* }</pre>
2019
*/
2120
public class ApiExecutorRequestBuilder {
22-
private static final Set<String> VALID_HTTP_METHODS =
23-
Set.of("GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS");
24-
25-
private final String method;
21+
private final HttpMethod method;
2622
private final String path;
2723
private final Map<String, String> pathParams;
2824
private final Map<String, String> queryParams;
2925
private final Map<String, String> headers;
3026
private Object body;
3127

32-
private ApiExecutorRequestBuilder(String method, String path) {
28+
private ApiExecutorRequestBuilder(HttpMethod method, String path) {
3329
this.method = method;
3430
this.path = path;
3531
this.pathParams = new HashMap<>();
@@ -41,26 +37,20 @@ private ApiExecutorRequestBuilder(String method, String path) {
4137
/**
4238
* Creates a new ApiExecutorRequestBuilder instance.
4339
*
44-
* @param method HTTP method (GET, POST, PUT, PATCH, DELETE, HEAD, OPTIONS)
40+
* @param method HTTP method enum value (GET, POST, PUT, PATCH, DELETE, HEAD, OPTIONS)
4541
* @param path API path with optional placeholders like {store_id}
4642
* @return New ApiExecutorRequestBuilder instance
4743
* @throws IllegalArgumentException if method or path is invalid
4844
*/
49-
public static ApiExecutorRequestBuilder builder(String method, String path) {
50-
if (method == null || method.trim().isEmpty()) {
51-
throw new IllegalArgumentException("HTTP method cannot be null or empty");
45+
public static ApiExecutorRequestBuilder builder(HttpMethod method, String path) {
46+
if (method == null) {
47+
throw new IllegalArgumentException("HTTP method cannot be null");
5248
}
5349
if (path == null || path.trim().isEmpty()) {
5450
throw new IllegalArgumentException("Path cannot be null or empty");
5551
}
5652

57-
String upperMethod = method.toUpperCase();
58-
if (!VALID_HTTP_METHODS.contains(upperMethod)) {
59-
throw new IllegalArgumentException(
60-
"Invalid HTTP method: " + method + ". Valid methods: " + VALID_HTTP_METHODS);
61-
}
62-
63-
return new ApiExecutorRequestBuilder(upperMethod, path);
53+
return new ApiExecutorRequestBuilder(method, path);
6454
}
6555

6656
/**
@@ -115,6 +105,7 @@ public ApiExecutorRequestBuilder body(Object body) {
115105
this.body = body;
116106
return this;
117107
}
108+
118109
/**
119110
* Builds and returns the request for use with the API Executor.
120111
* This method must be called to complete request construction.
@@ -129,7 +120,7 @@ public ApiExecutorRequestBuilder build() {
129120
}
130121

131122
String getMethod() {
132-
return method;
123+
return method.name();
133124
}
134125

135126
String getPath() {
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
package dev.openfga.sdk.api.client;
2+
3+
/**
4+
* Enumeration of standard HTTP methods supported by the OpenFGA API.
5+
* This enum provides type safety and prevents invalid HTTP methods from being used.
6+
*
7+
* @since 0.8.0
8+
*/
9+
public enum HttpMethod {
10+
/**
11+
* HTTP GET method - used for retrieving resources.
12+
*/
13+
GET,
14+
15+
/**
16+
* HTTP POST method - used for creating resources or submitting data.
17+
*/
18+
POST,
19+
20+
/**
21+
* HTTP PUT method - used for updating or replacing resources.
22+
*/
23+
PUT,
24+
25+
/**
26+
* HTTP DELETE method - used for deleting resources.
27+
*/
28+
DELETE,
29+
30+
/**
31+
* HTTP PATCH method - used for partially updating resources.
32+
*/
33+
PATCH,
34+
35+
/**
36+
* HTTP HEAD method - used for retrieving resource metadata without the body.
37+
*/
38+
HEAD,
39+
40+
/**
41+
* HTTP OPTIONS method - used for describing communication options.
42+
*/
43+
OPTIONS
44+
}

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

Lines changed: 17 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ public void rawRequest_listStores() throws Exception {
5151

5252
// Use ApiExecutor to list stores (equivalent to GET /stores)
5353
ApiExecutorRequestBuilder request =
54-
ApiExecutorRequestBuilder.builder("GET", "/stores").build();
54+
ApiExecutorRequestBuilder.builder(HttpMethod.GET, "/stores").build();
5555

5656
ApiResponse<ListStoresResponse> response =
5757
fga.apiExecutor().send(request, ListStoresResponse.class).get();
@@ -84,7 +84,7 @@ public void rawRequest_createStore_typedResponse() throws Exception {
8484
requestBody.put("name", storeName);
8585

8686
// Use ApiExecutor to create store (equivalent to POST /stores)
87-
ApiExecutorRequestBuilder request = ApiExecutorRequestBuilder.builder("POST", "/stores")
87+
ApiExecutorRequestBuilder request = ApiExecutorRequestBuilder.builder(HttpMethod.POST, "/stores")
8888
.body(requestBody)
8989
.build();
9090

@@ -115,7 +115,7 @@ public void rawRequest_createStore_rawJsonResponse() throws Exception {
115115
requestBody.put("name", storeName);
116116

117117
// Use ApiExecutor to create store and get raw JSON response
118-
ApiExecutorRequestBuilder request = ApiExecutorRequestBuilder.builder("POST", "/stores")
118+
ApiExecutorRequestBuilder request = ApiExecutorRequestBuilder.builder(HttpMethod.POST, "/stores")
119119
.body(requestBody)
120120
.build();
121121

@@ -147,7 +147,7 @@ public void rawRequest_getStore_withPathParams() throws Exception {
147147
String storeId = createStoreUsingRawRequest(storeName);
148148

149149
// Use ApiExecutor to get store details (equivalent to GET /stores/{store_id})
150-
ApiExecutorRequestBuilder request = ApiExecutorRequestBuilder.builder("GET", "/stores/{store_id}")
150+
ApiExecutorRequestBuilder request = ApiExecutorRequestBuilder.builder(HttpMethod.GET, "/stores/{store_id}")
151151
.pathParam("store_id", storeId)
152152
.build();
153153

@@ -176,8 +176,8 @@ public void rawRequest_automaticStoreIdReplacement() throws Exception {
176176
fga.setStoreId(storeId);
177177

178178
// Use ApiExecutor WITHOUT providing store_id path param - it should be auto-replaced
179-
ApiExecutorRequestBuilder request =
180-
ApiExecutorRequestBuilder.builder("GET", "/stores/{store_id}").build();
179+
ApiExecutorRequestBuilder request = ApiExecutorRequestBuilder.builder(HttpMethod.GET, "/stores/{store_id}")
180+
.build();
181181

182182
ApiResponse<GetStoreResponse> response =
183183
fga.apiExecutor().send(request, GetStoreResponse.class).get();
@@ -228,7 +228,7 @@ public void rawRequest_writeAuthorizationModel() throws Exception {
228228

229229
// Use ApiExecutor to write authorization model
230230
ApiExecutorRequestBuilder request = ApiExecutorRequestBuilder.builder(
231-
"POST", "/stores/{store_id}/authorization-models")
231+
HttpMethod.POST, "/stores/{store_id}/authorization-models")
232232
.body(requestBody)
233233
.build();
234234

@@ -261,7 +261,7 @@ public void rawRequest_readAuthorizationModels_withQueryParams() throws Exceptio
261261

262262
// Use ApiExecutor to read authorization models with query parameters
263263
ApiExecutorRequestBuilder request = ApiExecutorRequestBuilder.builder(
264-
"GET", "/stores/{store_id}/authorization-models")
264+
HttpMethod.GET, "/stores/{store_id}/authorization-models")
265265
.queryParam("page_size", "10")
266266
.queryParam("continuation_token", "")
267267
.build();
@@ -308,7 +308,8 @@ public void rawRequest_check() throws Exception {
308308
tupleKey.put("object", "document:budget");
309309
checkBody.put("tuple_key", tupleKey);
310310

311-
ApiExecutorRequestBuilder request = ApiExecutorRequestBuilder.builder("POST", "/stores/{store_id}/check")
311+
ApiExecutorRequestBuilder request = ApiExecutorRequestBuilder.builder(
312+
HttpMethod.POST, "/stores/{store_id}/check")
312313
.body(checkBody)
313314
.build();
314315

@@ -331,7 +332,7 @@ public void rawRequest_withCustomHeaders() throws Exception {
331332
requestBody.put("name", storeName);
332333

333334
// Use ApiExecutor with custom headers
334-
ApiExecutorRequestBuilder request = ApiExecutorRequestBuilder.builder("POST", "/stores")
335+
ApiExecutorRequestBuilder request = ApiExecutorRequestBuilder.builder(HttpMethod.POST, "/stores")
335336
.body(requestBody)
336337
.header("X-Custom-Header", "custom-value")
337338
.header("X-Request-ID", "test-123")
@@ -353,7 +354,7 @@ public void rawRequest_withCustomHeaders() throws Exception {
353354
@Test
354355
public void rawRequest_errorHandling_notFound() throws Exception {
355356
// Try to get a non-existent store
356-
ApiExecutorRequestBuilder request = ApiExecutorRequestBuilder.builder("GET", "/stores/{store_id}")
357+
ApiExecutorRequestBuilder request = ApiExecutorRequestBuilder.builder(HttpMethod.GET, "/stores/{store_id}")
357358
.pathParam("store_id", "non-existent-store-id")
358359
.build();
359360

@@ -383,7 +384,7 @@ public void rawRequest_listStores_withPagination() throws Exception {
383384
}
384385

385386
// Use ApiExecutor to list stores with pagination
386-
ApiExecutorRequestBuilder request = ApiExecutorRequestBuilder.builder("GET", "/stores")
387+
ApiExecutorRequestBuilder request = ApiExecutorRequestBuilder.builder(HttpMethod.GET, "/stores")
387388
.queryParam("page_size", "2")
388389
.build();
389390

@@ -409,7 +410,7 @@ private String createStoreUsingRawRequest(String storeName) throws Exception {
409410
Map<String, Object> requestBody = new HashMap<>();
410411
requestBody.put("name", storeName);
411412

412-
ApiExecutorRequestBuilder request = ApiExecutorRequestBuilder.builder("POST", "/stores")
413+
ApiExecutorRequestBuilder request = ApiExecutorRequestBuilder.builder(HttpMethod.POST, "/stores")
413414
.body(requestBody)
414415
.build();
415416

@@ -445,7 +446,7 @@ private String writeSimpleAuthorizationModel(String storeId) throws Exception {
445446
requestBody.put("type_definitions", typeDefinitions);
446447

447448
ApiExecutorRequestBuilder request = ApiExecutorRequestBuilder.builder(
448-
"POST", "/stores/{store_id}/authorization-models")
449+
HttpMethod.POST, "/stores/{store_id}/authorization-models")
449450
.pathParam("store_id", storeId)
450451
.body(requestBody)
451452
.build();
@@ -467,7 +468,8 @@ private void writeTupleUsingRawRequest(String storeId, String user, String relat
467468
Map<String, Object> requestBody = new HashMap<>();
468469
requestBody.put("writes", Map.of("tuple_keys", List.of(tupleKey)));
469470

470-
ApiExecutorRequestBuilder request = ApiExecutorRequestBuilder.builder("POST", "/stores/{store_id}/write")
471+
ApiExecutorRequestBuilder request = ApiExecutorRequestBuilder.builder(
472+
HttpMethod.POST, "/stores/{store_id}/write")
471473
.pathParam("store_id", storeId)
472474
.body(requestBody)
473475
.build();

0 commit comments

Comments
 (0)