Skip to content

Commit 86fcbc7

Browse files
committed
feat: address copilot comments
1 parent be65362 commit 86fcbc7

File tree

6 files changed

+260
-122
lines changed

6 files changed

+260
-122
lines changed

src/main/java/dev/openfga/sdk/api/StreamedListObjectsApi.java

Lines changed: 17 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,14 @@
55
import dev.openfga.sdk.api.client.ApiClient;
66
import dev.openfga.sdk.api.client.ApiResponse;
77
import dev.openfga.sdk.api.client.HttpRequestAttempt;
8-
import dev.openfga.sdk.api.client.StreamingResponseString;
8+
import dev.openfga.sdk.api.client.StreamingResponseBody;
99
import dev.openfga.sdk.api.configuration.Configuration;
1010
import dev.openfga.sdk.api.model.ListObjectsRequest;
1111
import dev.openfga.sdk.errors.ApiException;
1212
import dev.openfga.sdk.errors.FgaInvalidParameterException;
1313
import dev.openfga.sdk.telemetry.Attribute;
1414
import dev.openfga.sdk.telemetry.Attributes;
15+
import java.net.http.HttpRequest;
1516
import java.util.HashMap;
1617
import java.util.Map;
1718
import java.util.concurrent.CompletableFuture;
@@ -21,9 +22,11 @@
2122
* This class is separate from the generated OpenFgaApi to avoid modifications to generated code.
2223
*/
2324
public class StreamedListObjectsApi {
25+
private final Configuration configuration;
2426
private final ApiClient apiClient;
2527

26-
public StreamedListObjectsApi(ApiClient apiClient) {
28+
public StreamedListObjectsApi(Configuration configuration, ApiClient apiClient) {
29+
this.configuration = configuration;
2730
this.apiClient = apiClient;
2831
}
2932

@@ -33,13 +36,13 @@ public StreamedListObjectsApi(ApiClient apiClient) {
3336
*
3437
* @param storeId The store ID (required)
3538
* @param body The list objects request body (required)
36-
* @param configuration The configuration to use for this request
39+
* @param requestConfiguration The configuration to use for this request
3740
* @return CompletableFuture with raw streaming response
3841
* @throws ApiException if fails to make API call
3942
* @throws FgaInvalidParameterException if required parameters are missing
4043
*/
41-
public CompletableFuture<ApiResponse<StreamingResponseString>> streamedListObjects(
42-
String storeId, ListObjectsRequest body, Configuration configuration)
44+
public CompletableFuture<ApiResponse<StreamingResponseBody>> streamedListObjects(
45+
String storeId, ListObjectsRequest body, Configuration requestConfiguration)
4346
throws ApiException, FgaInvalidParameterException {
4447

4548
assertParamExists(storeId, "storeId", "streamedListObjects");
@@ -48,46 +51,25 @@ public CompletableFuture<ApiResponse<StreamingResponseString>> streamedListObjec
4851
String path = "/stores/" + storeId + "/streamed-list-objects";
4952

5053
try {
51-
// Build the HTTP request
5254
byte[] requestBody = apiClient.getObjectMapper().writeValueAsBytes(body);
53-
var bodyPublisher = java.net.http.HttpRequest.BodyPublishers.ofByteArray(requestBody);
55+
HttpRequest.Builder requestBuilder =
56+
ApiClient.requestBuilder("POST", path, requestBody, requestConfiguration);
5457

55-
var requestBuilder = java.net.http.HttpRequest.newBuilder()
56-
.uri(java.net.URI.create(configuration.getApiUrl() + path))
57-
.header("Content-Type", "application/json")
58-
.header("User-Agent", configuration.getUserAgent())
59-
.POST(bodyPublisher);
60-
61-
// Add authorization header if needed
62-
if (configuration.getCredentials() != null
63-
&& configuration.getCredentials().getApiToken() != null) {
64-
requestBuilder.header(
65-
"Authorization",
66-
"Bearer " + configuration.getCredentials().getApiToken());
67-
}
68-
69-
// Add default headers
70-
if (configuration.getDefaultHeaders() != null) {
71-
configuration.getDefaultHeaders().forEach(requestBuilder::header);
72-
}
73-
74-
var httpRequest = requestBuilder.build();
75-
76-
// Build telemetry attributes
77-
Map<String, Object> methodParameters = new HashMap<>();
78-
methodParameters.put("storeId", storeId);
79-
methodParameters.put("body", body);
58+
HttpRequest httpRequest = requestBuilder.build();
8059

8160
Map<Attribute, String> telemetryAttributes = new HashMap<>();
8261
telemetryAttributes.put(Attributes.FGA_CLIENT_REQUEST_METHOD, "StreamedListObjects");
8362

84-
// Use HttpRequestAttempt with StreamingResponseString to get raw response
8563
return new HttpRequestAttempt<>(
86-
httpRequest, "streamedListObjects", StreamingResponseString.class, apiClient, configuration)
64+
httpRequest,
65+
"streamedListObjects",
66+
StreamingResponseBody.class,
67+
apiClient,
68+
requestConfiguration)
8769
.addTelemetryAttributes(telemetryAttributes)
8870
.attemptHttpRequest();
8971
} catch (Exception e) {
9072
return CompletableFuture.failedFuture(new ApiException(e));
9173
}
9274
}
93-
}
75+
}

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

Lines changed: 122 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
import dev.openfga.sdk.util.RetryAfterHeaderParser;
1313
import dev.openfga.sdk.util.RetryStrategy;
1414
import java.io.IOException;
15+
import java.io.InputStream;
1516
import java.io.PrintStream;
1617
import java.net.http.*;
1718
import java.nio.ByteBuffer;
@@ -90,18 +91,27 @@ private HttpClient getHttpClient() {
9091

9192
private CompletableFuture<ApiResponse<T>> attemptHttpRequest(
9293
HttpClient httpClient, int retryNumber, Throwable previousError) {
93-
return httpClient
94-
.sendAsync(request, HttpResponse.BodyHandlers.ofString())
95-
.handle((response, throwable) -> {
96-
if (throwable != null) {
97-
// Handle network errors (no HTTP response received)
98-
return handleNetworkError(throwable, retryNumber);
99-
}
100-
101-
// Handle HTTP response (including error status codes)
102-
return processHttpResponse(response, retryNumber, previousError);
103-
})
104-
.thenCompose(Function.identity());
94+
if (clazz == StreamingResponseBody.class) {
95+
return httpClient
96+
.sendAsync(request, HttpResponse.BodyHandlers.ofInputStream())
97+
.handle((response, throwable) -> {
98+
if (throwable != null) {
99+
return handleNetworkError(throwable, retryNumber);
100+
}
101+
return processHttpResponseStreaming(response, retryNumber, previousError);
102+
})
103+
.thenCompose(Function.identity());
104+
} else {
105+
return httpClient
106+
.sendAsync(request, HttpResponse.BodyHandlers.ofString())
107+
.handle((response, throwable) -> {
108+
if (throwable != null) {
109+
return handleNetworkError(throwable, retryNumber);
110+
}
111+
return processHttpResponse(response, retryNumber, previousError);
112+
})
113+
.thenCompose(Function.identity());
114+
}
105115
}
106116

107117
private CompletableFuture<ApiResponse<T>> handleNetworkError(Throwable throwable, int retryNumber) {
@@ -211,6 +221,105 @@ private CompletableFuture<ApiResponse<T>> processHttpResponse(
211221
response.statusCode(), response.headers().map(), response.body(), modeledResponse));
212222
}
213223

224+
private CompletableFuture<ApiResponse<T>> processHttpResponseStreaming(
225+
HttpResponse<InputStream> response, int retryNumber, Throwable previousError) {
226+
int statusCode = response.statusCode();
227+
228+
if (!HttpStatusCode.isSuccessful(statusCode)) {
229+
try {
230+
String bodyStr = new String(response.body().readAllBytes(), StandardCharsets.UTF_8);
231+
HttpResponse<String> stringResponse = new HttpResponse<>() {
232+
@Override
233+
public int statusCode() {
234+
return response.statusCode();
235+
}
236+
237+
@Override
238+
public HttpRequest request() {
239+
return response.request();
240+
}
241+
242+
@Override
243+
public java.util.Optional<HttpResponse<String>> previousResponse() {
244+
return java.util.Optional.empty();
245+
}
246+
247+
@Override
248+
public HttpHeaders headers() {
249+
return response.headers();
250+
}
251+
252+
@Override
253+
public String body() {
254+
return bodyStr;
255+
}
256+
257+
@Override
258+
public java.util.Optional<javax.net.ssl.SSLSession> sslSession() {
259+
return response.sslSession();
260+
}
261+
262+
@Override
263+
public java.net.URI uri() {
264+
return response.uri();
265+
}
266+
267+
@Override
268+
public HttpClient.Version version() {
269+
return response.version();
270+
}
271+
};
272+
273+
Optional<FgaError> fgaError =
274+
FgaError.getError(name, request, configuration, stringResponse, previousError);
275+
if (fgaError.isPresent()) {
276+
FgaError error = fgaError.get();
277+
if (retryNumber < configuration.getMaxRetries()) {
278+
Optional<Duration> retryAfterDelay = response.headers()
279+
.firstValue(FgaConstants.RETRY_AFTER_HEADER_NAME)
280+
.flatMap(RetryAfterHeaderParser::parseRetryAfter);
281+
if (RetryStrategy.shouldRetry(statusCode)) {
282+
return handleHttpErrorRetry(retryAfterDelay, retryNumber, error);
283+
}
284+
}
285+
return CompletableFuture.failedFuture(error);
286+
}
287+
} catch (IOException e) {
288+
return CompletableFuture.failedFuture(new ApiException(e));
289+
}
290+
}
291+
292+
addTelemetryAttributes(Attributes.fromHttpResponse(response, this.configuration.getCredentials()));
293+
294+
if (retryNumber > 0) {
295+
addTelemetryAttribute(Attributes.HTTP_REQUEST_RESEND_COUNT, String.valueOf(retryNumber));
296+
}
297+
298+
if (response.headers()
299+
.firstValue(FgaConstants.QUERY_DURATION_HEADER_NAME)
300+
.isPresent()) {
301+
String queryDuration = response.headers()
302+
.firstValue(FgaConstants.QUERY_DURATION_HEADER_NAME)
303+
.orElse(null);
304+
305+
if (!isNullOrWhitespace(queryDuration)) {
306+
try {
307+
double queryDurationDouble = Double.parseDouble(queryDuration);
308+
telemetry.metrics().queryDuration(queryDurationDouble, this.getTelemetryAttributes());
309+
} catch (NumberFormatException e) {
310+
}
311+
}
312+
}
313+
314+
Double requestDuration = (double) (System.currentTimeMillis() - requestStarted);
315+
telemetry.metrics().requestDuration(requestDuration, this.getTelemetryAttributes());
316+
317+
@SuppressWarnings("unchecked")
318+
T result = (T) new StreamingResponseBody(response.body());
319+
return CompletableFuture.completedFuture(
320+
new ApiResponse<>(response.statusCode(), response.headers().map(), null, result));
321+
}
322+
214323
private CompletableFuture<T> deserializeResponse(HttpResponse<String> response) {
215324
if (clazz == Void.class && isNullOrWhitespace(response.body())) {
216325
return CompletableFuture.completedFuture(null);
@@ -262,4 +371,4 @@ public void onComplete() {
262371
out.flush();
263372
}
264373
}
265-
}
374+
}

0 commit comments

Comments
 (0)