Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 54 additions & 3 deletions src/main/java/dev/openfga/sdk/api/BaseStreamingApi.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,14 @@
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import dev.openfga.sdk.api.client.ApiClient;
import dev.openfga.sdk.api.client.JsonSerializer;
import dev.openfga.sdk.api.client.SdkTypeToken;
import dev.openfga.sdk.api.configuration.Configuration;
import dev.openfga.sdk.api.model.Status;
import dev.openfga.sdk.api.model.StreamResult;
import dev.openfga.sdk.errors.ApiException;
import dev.openfga.sdk.errors.FgaInvalidParameterException;
import java.lang.reflect.Type;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.util.concurrent.CompletableFuture;
Expand All @@ -38,22 +41,70 @@
public abstract class BaseStreamingApi<T> {
protected final Configuration configuration;
protected final ApiClient apiClient;
protected final JsonSerializer jsonSerializer;
protected final SdkTypeToken<StreamResult<T>> streamResultType;
Comment thread
Adrastopoulos marked this conversation as resolved.

/**
* Jackson mapper, or {@code null} when the serializer does not use Jackson 2.
* @deprecated Use {@link #jsonSerializer}.
*/
@Deprecated(since = "0.11.0")
protected final ObjectMapper objectMapper;

/** @deprecated Use {@link #streamResultType}. */
@Deprecated(since = "0.11.0")
protected final TypeReference<StreamResult<T>> streamResultTypeRef;

/**
* Constructor for BaseStreamingApi
*
* @param configuration The API configuration
* @param apiClient The API client for making HTTP requests
* @param streamResultTypeRef TypeReference for deserializing StreamResult<T>
* @param streamResultType SDK type token for deserializing StreamResult<T>
*/
protected BaseStreamingApi(
Configuration configuration, ApiClient apiClient, SdkTypeToken<StreamResult<T>> streamResultType) {
this.configuration = configuration;
this.apiClient = apiClient;
this.jsonSerializer = apiClient.getJsonSerializer();
this.streamResultType = streamResultType;
ObjectMapper mapper;
try {
mapper = apiClient.getObjectMapper();
} catch (UnsupportedOperationException ignored) {
mapper = null;
}
this.objectMapper = mapper;
this.streamResultTypeRef = new TypeReference<StreamResult<T>>() {
@Override
public Type getType() {
return streamResultType.getType();
}
};
}

/**
* Creates a streaming API with a Jackson type reference.
*
* @param configuration The API configuration
* @param apiClient The API client for making HTTP requests
* @param streamResultTypeRef Type reference for deserializing the stream result
* @deprecated Use {@link #BaseStreamingApi(Configuration, ApiClient, SdkTypeToken)}.
*/
@Deprecated(since = "0.11.0")
protected BaseStreamingApi(
Configuration configuration, ApiClient apiClient, TypeReference<StreamResult<T>> streamResultTypeRef) {
this.configuration = configuration;
this.apiClient = apiClient;
this.objectMapper = apiClient.getObjectMapper();
this.streamResultTypeRef = streamResultTypeRef;
this.jsonSerializer = apiClient.getJsonSerializer();
this.streamResultType = new SdkTypeToken<StreamResult<T>>() {
@Override
public Type getType() {
return streamResultTypeRef.getType();
}
};
}

/**
Expand Down Expand Up @@ -126,7 +177,7 @@ protected CompletableFuture<Void> processStreamingResponse(
private void processLine(String line, Consumer<T> consumer, Consumer<Throwable> errorConsumer) {
try {
// Parse the JSON line to extract the object
StreamResult<T> streamResult = objectMapper.readValue(line, streamResultTypeRef);
StreamResult<T> streamResult = jsonSerializer.readValue(line, streamResultType);

if (streamResult.getError() != null) {
// Handle error in stream
Expand Down Expand Up @@ -165,7 +216,7 @@ private void processLine(String line, Consumer<T> consumer, Consumer<Throwable>
protected HttpRequest buildHttpRequest(String method, String path, Object body, Configuration configuration)
throws ApiException, FgaInvalidParameterException {
try {
byte[] bodyBytes = objectMapper.writeValueAsBytes(body);
byte[] bodyBytes = jsonSerializer.writeValueAsBytes(body);
HttpRequest.Builder requestBuilder = ApiClient.requestBuilder(method, path, bodyBytes, configuration);

apiClient.applyAuthHeader(requestBuilder, configuration);
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/dev/openfga/sdk/api/OpenFgaApi.java
Original file line number Diff line number Diff line change
Expand Up @@ -1265,7 +1265,7 @@ private HttpRequest buildHttpRequest(String method, String path, Configuration c
private <T> HttpRequest buildHttpRequest(String method, String path, T body, Configuration configuration)
throws ApiException, FgaInvalidParameterException {
try {
byte[] localVarPostBody = apiClient.getObjectMapper().writeValueAsBytes(body);
byte[] localVarPostBody = apiClient.getJsonSerializer().writeValueAsBytes(body);
var bodyPublisher = HttpRequest.BodyPublishers.ofByteArray(localVarPostBody);
return buildHttpRequestWithPublisher(method, path, bodyPublisher, configuration);
} catch (IOException e) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@

import static dev.openfga.sdk.util.Validation.assertParamExists;

import com.fasterxml.jackson.core.type.TypeReference;
import dev.openfga.sdk.api.client.ApiClient;
import dev.openfga.sdk.api.client.SdkTypeToken;
import dev.openfga.sdk.api.configuration.Configuration;
import dev.openfga.sdk.api.configuration.ConfigurationOverride;
import dev.openfga.sdk.api.model.ListObjectsRequest;
Expand All @@ -36,7 +36,7 @@
public class StreamedListObjectsApi extends BaseStreamingApi<StreamedListObjectsResponse> {

public StreamedListObjectsApi(Configuration configuration, ApiClient apiClient) {
super(configuration, apiClient, new TypeReference<StreamResult<StreamedListObjectsResponse>>() {});
super(configuration, apiClient, new SdkTypeToken<StreamResult<StreamedListObjectsResponse>>() {});
}

/**
Expand Down
76 changes: 49 additions & 27 deletions src/main/java/dev/openfga/sdk/api/client/ApiClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,7 @@

import static java.nio.charset.StandardCharsets.UTF_8;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import dev.openfga.sdk.api.auth.OAuth2Client;
import dev.openfga.sdk.api.configuration.ClientCredentials;
import dev.openfga.sdk.api.configuration.Configuration;
Expand All @@ -30,7 +26,6 @@
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ExecutionException;
import java.util.function.Consumer;
import org.openapitools.jackson.nullable.JsonNullableModule;

/**
* Configuration and utility class for API clients.
Expand All @@ -49,7 +44,7 @@ public class ApiClient {

private HttpClient.Builder builder;
private HttpClient client;
private ObjectMapper mapper;
private JsonSerializer jsonSerializer;
private Consumer<HttpRequest.Builder> interceptor;
private Consumer<HttpResponse<InputStream>> responseInterceptor;
private Consumer<HttpResponse<String>> asyncResponseInterceptor;
Expand All @@ -60,7 +55,7 @@ public class ApiClient {
*/
public ApiClient() {
this.builder = createDefaultHttpClientBuilder();
this.mapper = createDefaultObjectMapper();
this.jsonSerializer = new Jackson2JsonSerializer(createDefaultObjectMapper());
this.client = this.builder.build();
interceptor = null;
responseInterceptor = null;
Expand All @@ -78,7 +73,7 @@ public ApiClient() {
*/
public ApiClient(HttpClient.Builder builder) {
this.builder = builder;
this.mapper = createDefaultObjectMapper();
this.jsonSerializer = new Jackson2JsonSerializer(createDefaultObjectMapper());
this.client = this.builder.build();
interceptor = null;
responseInterceptor = null;
Expand All @@ -94,10 +89,22 @@ public ApiClient(HttpClient.Builder builder) {
*
* @param builder Http client builder.
* @param mapper Object mapper.
* @deprecated Use {@link #ApiClient(HttpClient.Builder, JsonSerializer)}.
*/
@Deprecated(since = "0.11.0")
public ApiClient(HttpClient.Builder builder, ObjectMapper mapper) {
this(builder, new Jackson2JsonSerializer(mapper));
}

/**
* Create an instance of ApiClient.
*
* @param builder Http client builder.
* @param jsonSerializer JSON serializer.
*/
public ApiClient(HttpClient.Builder builder, JsonSerializer jsonSerializer) {
this.builder = builder;
this.mapper = mapper;
this.jsonSerializer = Objects.requireNonNull(jsonSerializer, "JsonSerializer cannot be null");
this.client = this.builder.build();
interceptor = null;
responseInterceptor = null;
Expand Down Expand Up @@ -168,18 +175,16 @@ public static String urlEncode(String s) {
return URLEncoder.encode(s, UTF_8).replaceAll("\\+", "%20");
}

/**
* Create the default Jackson 2 mapper used by constructors without an explicit serializer.
*
* @return The default object mapper.
* @deprecated Supply a {@link JsonSerializer} through
* {@link #ApiClient(HttpClient.Builder, JsonSerializer)} or {@link #setJsonSerializer(JsonSerializer)}.
*/
@Deprecated(since = "0.11.0")
protected ObjectMapper createDefaultObjectMapper() {
ObjectMapper mapper = new ObjectMapper();
mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
mapper.configure(DeserializationFeature.FAIL_ON_INVALID_SUBTYPE, false);
mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
mapper.enable(SerializationFeature.WRITE_ENUMS_USING_TO_STRING);
mapper.enable(DeserializationFeature.READ_ENUMS_USING_TO_STRING);
mapper.disable(DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE);
mapper.registerModule(new JavaTimeModule());
mapper.registerModule(new JsonNullableModule());
return mapper;
return Jackson2JsonSerializer.createDefaultObjectMapper();
}

protected String getDefaultBaseUri() {
Expand Down Expand Up @@ -230,24 +235,41 @@ public HttpClient.Builder getHttpClientBuilder() {
}

/**
* Set a custom {@link ObjectMapper} to serialize and deserialize the request
* and response bodies.
* Set a custom {@link ObjectMapper} for request and response bodies.
*
* @param mapper Custom object mapper.
* @return This object.
* @deprecated Use {@link #setJsonSerializer(JsonSerializer)}.
*/
@Deprecated(since = "0.11.0")
public ApiClient setObjectMapper(ObjectMapper mapper) {
this.mapper = mapper;
return this;
return setJsonSerializer(new Jackson2JsonSerializer(mapper));
}

/**
* Get current {@link ObjectMapper}.
* Get the current Jackson 2 object mapper.
*
* @return the current object mapper.
* @return Current Jackson 2 object mapper.
* @throws UnsupportedOperationException if the active serializer does not use Jackson 2.
* @deprecated Use {@link #getJsonSerializer()}.
*/
@Deprecated(since = "0.11.0")
public ObjectMapper getObjectMapper() {
return mapper;
if (jsonSerializer instanceof Jackson2JsonSerializer) {
return ((Jackson2JsonSerializer) jsonSerializer).getObjectMapper();
}
throw new UnsupportedOperationException("The active JSON serializer does not use Jackson 2");
}

/** Set the serializer for request and response bodies. */
public ApiClient setJsonSerializer(JsonSerializer jsonSerializer) {
this.jsonSerializer = Objects.requireNonNull(jsonSerializer, "JsonSerializer cannot be null");
return this;
}

/** Get the serializer for request and response bodies. */
public JsonSerializer getJsonSerializer() {
return jsonSerializer;
}

/**
Expand Down
6 changes: 2 additions & 4 deletions src/main/java/dev/openfga/sdk/api/client/ApiExecutor.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
import dev.openfga.sdk.errors.ApiException;
import dev.openfga.sdk.errors.FgaInvalidParameterException;
import dev.openfga.sdk.telemetry.Telemetry;
import java.io.IOException;
import java.net.http.HttpRequest;
import java.util.concurrent.CompletableFuture;

Expand Down Expand Up @@ -103,9 +102,8 @@ public <T> CompletableFuture<ApiResponse<T>> send(ApiExecutorRequestBuilder requ

return new HttpRequestAttempt<>(httpRequest, methodName, responseType, apiClient, configuration, telemetry)
.attemptHttpRequest();

} catch (IOException e) {
return CompletableFuture.failedFuture(new ApiException(e));
} catch (dev.openfga.sdk.errors.SdkSerializationException error) {
return CompletableFuture.failedFuture(new ApiException(error));
}
}
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
package dev.openfga.sdk.api.client;

import com.fasterxml.jackson.core.JsonProcessingException;
import dev.openfga.sdk.api.configuration.ClientConfiguration;
import dev.openfga.sdk.api.configuration.Configuration;
import dev.openfga.sdk.errors.ApiException;
import dev.openfga.sdk.errors.FgaInvalidParameterException;
import dev.openfga.sdk.errors.SdkSerializationException;
import dev.openfga.sdk.util.StringUtil;
import java.net.http.HttpRequest;
import java.nio.charset.StandardCharsets;
Expand Down Expand Up @@ -193,14 +193,14 @@ String buildPath(Configuration configuration) {
* Package-private — used by {@link ApiExecutor} and {@link StreamingApiExecutor}.
*/
HttpRequest buildHttpRequest(Configuration configuration, ApiClient apiClient)
throws ApiException, FgaInvalidParameterException, JsonProcessingException {
throws ApiException, FgaInvalidParameterException, SdkSerializationException {
String resolvedPath = buildPath(configuration);

HttpRequest.Builder httpRequestBuilder;
if (hasBody()) {
byte[] bodyBytes = body instanceof String
? ((String) body).getBytes(StandardCharsets.UTF_8)
: apiClient.getObjectMapper().writeValueAsBytes(body);
: apiClient.getJsonSerializer().writeValueAsBytes(body);
httpRequestBuilder = ApiClient.requestBuilder(method.name(), resolvedPath, bodyBytes, configuration);
} else {
httpRequestBuilder = ApiClient.requestBuilder(method.name(), resolvedPath, configuration);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,8 @@ private CompletableFuture<ApiResponse<T>> delayedRetry(

private CompletableFuture<ApiResponse<T>> processHttpResponse(
HttpResponse<String> response, int retryNumber, Throwable previousError) {
Optional<FgaError> fgaError = FgaError.getError(name, request, configuration, response, previousError);
Optional<FgaError> fgaError =
FgaError.getError(name, request, configuration, response, previousError, apiClient.getJsonSerializer());

if (fgaError.isPresent()) {
FgaError error = fgaError.get();
Expand Down Expand Up @@ -239,7 +240,7 @@ private CompletableFuture<T> deserializeResponse(HttpResponse<String> response)
}

try {
T deserialized = apiClient.getObjectMapper().readValue(response.body(), clazz);
T deserialized = apiClient.getJsonSerializer().readValue(response.body(), clazz);
return CompletableFuture.completedFuture(deserialized);
} catch (IOException e) {
// Malformed response.
Expand Down
Loading