From 0c1bd6da2d649c5bb445423909940b56459ac5da Mon Sep 17 00:00:00 2001 From: "Justin \"J.R.\" Hill" Date: Mon, 21 Aug 2023 15:43:46 -0700 Subject: [PATCH 1/5] refactor(java): Thread Configuration through request builders --- .../template/libraries/native/api.mustache | 21 ++++++++----------- 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/config/clients/java/template/libraries/native/api.mustache b/config/clients/java/template/libraries/native/api.mustache index 7c64fef9..cb81bf30 100644 --- a/config/clients/java/template/libraries/native/api.mustache +++ b/config/clients/java/template/libraries/native/api.mustache @@ -54,8 +54,6 @@ public class {{classname}} { private final HttpClient memberVarHttpClient; private final ObjectMapper memberVarObjectMapper; private final Configuration memberVarConfiguration; - private final String memberVarBaseUri; - private final Duration memberVarReadTimeout; private final Consumer memberVarInterceptor; private final Consumer> memberVarResponseInterceptor; private final Consumer> memberVarAsyncResponseInterceptor; @@ -64,8 +62,6 @@ public class {{classname}} { memberVarHttpClient = apiClient.getHttpClient(); memberVarObjectMapper = apiClient.getObjectMapper(); memberVarConfiguration = configuration; - memberVarBaseUri = configuration.getApiUrl(); - memberVarReadTimeout = configuration.getReadTimeout(); memberVarInterceptor = apiClient.getRequestInterceptor(); memberVarResponseInterceptor = apiClient.getResponseInterceptor(); memberVarAsyncResponseInterceptor = apiClient.getAsyncResponseInterceptor(); @@ -188,7 +184,7 @@ public class {{classname}} { {{/asyncNative}} {{#asyncNative}} try { - HttpRequest.Builder localVarRequestBuilder = {{operationId}}RequestBuilder({{#allParams}}{{paramName}}{{^-last}}, {{/-last}}{{/allParams}}); + HttpRequest.Builder localVarRequestBuilder = {{operationId}}RequestBuilder({{#allParams}}{{paramName}}{{^-last}}, {{/-last}}{{/allParams}}, memberVarConfiguration); return memberVarHttpClient.sendAsync( localVarRequestBuilder.build(), HttpResponse.BodyHandlers.ofString()).thenComposeAsync(localVarResponse -> { @@ -295,7 +291,7 @@ public class {{classname}} { {{/asyncNative}} {{#asyncNative}} try { - HttpRequest.Builder localVarRequestBuilder = {{operationId}}RequestBuilder({{#allParams}}{{paramName}}{{^-last}}, {{/-last}}{{/allParams}}); + HttpRequest.Builder localVarRequestBuilder = {{operationId}}RequestBuilder({{#allParams}}{{paramName}}{{^-last}}, {{/-last}}{{/allParams}}, memberVarConfiguration); return memberVarHttpClient.sendAsync( localVarRequestBuilder.build(), HttpResponse.BodyHandlers.ofString()).thenComposeAsync(localVarResponse -> { @@ -332,7 +328,7 @@ public class {{classname}} { {{/asyncNative}} } - private HttpRequest.Builder {{operationId}}RequestBuilder({{#allParams}}{{{dataType}}} {{paramName}}{{^-last}}, {{/-last}}{{/allParams}}) throws ApiException { + private HttpRequest.Builder {{operationId}}RequestBuilder({{#allParams}}{{{dataType}}} {{paramName}}{{^-last}}, {{/-last}}{{/allParams}}, Configuration configuration) throws ApiException { {{#allParams}} {{#required}} // verify the required parameter '{{paramName}}' is set @@ -404,13 +400,13 @@ public class {{classname}} { if (localVarQueryStringJoiner.length() != 0) { queryJoiner.add(localVarQueryStringJoiner.toString()); } - localVarRequestBuilder.uri(URI.create(memberVarBaseUri + localVarPath + '?' + queryJoiner.toString())); + localVarRequestBuilder.uri(URI.create(memberVarConfiguration.getApiUrl() + localVarPath + '?' + queryJoiner.toString())); } else { - localVarRequestBuilder.uri(URI.create(memberVarBaseUri + localVarPath)); + localVarRequestBuilder.uri(URI.create(memberVarConfiguration.getApiUrl() + localVarPath)); } {{/hasQueryParams}} {{^hasQueryParams}} - localVarRequestBuilder.uri(URI.create(memberVarBaseUri + localVarPath)); + localVarRequestBuilder.uri(URI.create(memberVarConfiguration.getApiUrl() + localVarPath)); {{/hasQueryParams}} {{#headerParams}} @@ -527,8 +523,9 @@ public class {{classname}} { localVarRequestBuilder.method("{{httpMethod}}", HttpRequest.BodyPublishers.noBody()); {{/hasFormParams}} {{/bodyParam}} - if (memberVarReadTimeout != null) { - localVarRequestBuilder.timeout(memberVarReadTimeout); + Duration readTimeout = memberVarConfiguration.getReadTimeout(); + if (readTimeout != null) { + localVarRequestBuilder.timeout(readTimeout); } if (memberVarInterceptor != null) { memberVarInterceptor.accept(localVarRequestBuilder); From 1dc04417ef7ffcdea98cee7aa8059c862cf47801 Mon Sep 17 00:00:00 2001 From: "Justin \"J.R.\" Hill" Date: Mon, 21 Aug 2023 16:30:58 -0700 Subject: [PATCH 2/5] refactor(java): Thread Configuration through requests --- .../template/libraries/native/api.mustache | 60 ++++++++++++++++++- 1 file changed, 58 insertions(+), 2 deletions(-) diff --git a/config/clients/java/template/libraries/native/api.mustache b/config/clients/java/template/libraries/native/api.mustache index cb81bf30..31df48d9 100644 --- a/config/clients/java/template/libraries/native/api.mustache +++ b/config/clients/java/template/libraries/native/api.mustache @@ -149,6 +149,7 @@ public class {{classname}} { {{/hasParams}} {{/vendorExtensions.x-group-parameters}} + /** * {{summary}} * {{notes}} @@ -176,6 +177,37 @@ public class {{classname}} { @Deprecated {{/isDeprecated}} public {{#returnType}}{{#asyncNative}}CompletableFuture<{{{returnType}}}>{{/asyncNative}}{{^asyncNative}}{{{returnType}}}{{/asyncNative}}{{/returnType}}{{^returnType}}{{#asyncNative}}CompletableFuture{{/asyncNative}}{{^asyncNative}}void{{/asyncNative}}{{/returnType}} {{operationId}}({{#allParams}}{{{dataType}}} {{paramName}}{{^-last}}, {{/-last}}{{/allParams}}) throws ApiException { + return {{operationId}}({{#allParams}}{{paramName}}{{^-last}}, {{/-last}}{{/allParams}}, memberVarConfiguration); + } + + /** + * {{summary}} + * {{notes}} + {{#allParams}} + * @param {{paramName}} {{description}}{{#required}} (required){{/required}}{{^required}} (optional{{^isContainer}}{{#defaultValue}}, default to {{.}}{{/defaultValue}}){{/isContainer}}{{/required}} + {{/allParams}} + * @param configuration Override the configuration this {{classname}} was constructed with + {{#returnType}} + * @return {{#asyncNative}}CompletableFuture<{{/asyncNative}}{{returnType}}{{#asyncNative}}>{{/asyncNative}} + {{/returnType}} + {{^returnType}} + {{#asyncNative}} + * @return CompletableFuture<Void> + {{/asyncNative}} + {{/returnType}} + * @throws ApiException if fails to make API call + {{#isDeprecated}} + * @deprecated + {{/isDeprecated}} + {{#externalDocs}} + * {{description}} + * @see {{summary}} Documentation + {{/externalDocs}} + */ + {{#isDeprecated}} + @Deprecated + {{/isDeprecated}} + public {{#returnType}}{{#asyncNative}}CompletableFuture<{{{returnType}}}>{{/asyncNative}}{{^asyncNative}}{{{returnType}}}{{/asyncNative}}{{/returnType}}{{^returnType}}{{#asyncNative}}CompletableFuture{{/asyncNative}}{{^asyncNative}}void{{/asyncNative}}{{/returnType}} {{operationId}}({{#allParams}}{{{dataType}}} {{paramName}}{{^-last}}, {{/-last}}{{/allParams}}, Configuration configuration) throws ApiException { {{^asyncNative}} {{#returnType}}ApiResponse<{{{.}}}> localVarResponse = {{/returnType}}{{operationId}}WithHttpInfo({{#allParams}}{{paramName}}{{^-last}}, {{/-last}}{{/allParams}}); {{#returnType}} @@ -184,7 +216,7 @@ public class {{classname}} { {{/asyncNative}} {{#asyncNative}} try { - HttpRequest.Builder localVarRequestBuilder = {{operationId}}RequestBuilder({{#allParams}}{{paramName}}{{^-last}}, {{/-last}}{{/allParams}}, memberVarConfiguration); + HttpRequest.Builder localVarRequestBuilder = {{operationId}}RequestBuilder({{#allParams}}{{paramName}}{{^-last}}, {{/-last}}{{/allParams}}, configuration); return memberVarHttpClient.sendAsync( localVarRequestBuilder.build(), HttpResponse.BodyHandlers.ofString()).thenComposeAsync(localVarResponse -> { @@ -232,8 +264,32 @@ public class {{classname}} { @Deprecated {{/isDeprecated}} public {{#asyncNative}}CompletableFuture<{{/asyncNative}}ApiResponse<{{{returnType}}}{{^returnType}}Void{{/returnType}}>{{#asyncNative}}>{{/asyncNative}} {{operationId}}WithHttpInfo({{#allParams}}{{{dataType}}} {{paramName}}{{^-last}}, {{/-last}}{{/allParams}}) throws ApiException { + return {{operationId}}WithHttpInfo({{#allParams}}{{paramName}}{{^-last}}, {{/-last}}{{/allParams}}, memberVarConfiguration); + } + + /** + * {{summary}} + * {{notes}} + {{#allParams}} + * @param {{paramName}} {{description}}{{#required}} (required){{/required}}{{^required}} (optional{{^isContainer}}{{#defaultValue}}, default to {{.}}{{/defaultValue}}){{/isContainer}}{{/required}} + {{/allParams}} + * @param configuration Override the configuration this {{classname}} was constructed with + * @return {{#asyncNative}}CompletableFuture<{{/asyncNative}}ApiResponse<{{returnType}}{{^returnType}}Void{{/returnType}}>{{#asyncNative}}>{{/asyncNative}} + * @throws ApiException if fails to make API call + {{#isDeprecated}} + * @deprecated + {{/isDeprecated}} + {{#externalDocs}} + * {{description}} + * @see {{summary}} Documentation + {{/externalDocs}} + */ + {{#isDeprecated}} + @Deprecated + {{/isDeprecated}} + public {{#asyncNative}}CompletableFuture<{{/asyncNative}}ApiResponse<{{{returnType}}}{{^returnType}}Void{{/returnType}}>{{#asyncNative}}>{{/asyncNative}} {{operationId}}WithHttpInfo({{#allParams}}{{{dataType}}} {{paramName}}{{^-last}}, {{/-last}}{{/allParams}}, Configuration configuration) throws ApiException { {{^asyncNative}} - HttpRequest.Builder localVarRequestBuilder = {{operationId}}RequestBuilder({{#allParams}}{{paramName}}{{^-last}}, {{/-last}}{{/allParams}}); + HttpRequest.Builder localVarRequestBuilder = {{operationId}}RequestBuilder({{#allParams}}{{paramName}}{{^-last}}, {{/-last}}{{/allParams}}, configuration); try { HttpResponse localVarResponse = memberVarHttpClient.send( localVarRequestBuilder.build(), From 2bd8c012b2ccdfaa74c6e2034ea8a9a108663ad7 Mon Sep 17 00:00:00 2001 From: "Justin \"J.R.\" Hill" Date: Tue, 22 Aug 2023 16:47:05 -0700 Subject: [PATCH 3/5] feat(java): Introduce ConfigurationOverride class --- config/clients/java/config.overrides.json | 4 + .../config-BaseConfiguration.java.mustache | 2 - .../config-Configuration.java.mustache | 42 ++++-- ...config-ConfigurationOverride.java.mustache | 127 ++++++++++++++++++ .../template/libraries/native/api.mustache | 45 ++++--- 5 files changed, 193 insertions(+), 27 deletions(-) create mode 100644 config/clients/java/template/config-ConfigurationOverride.java.mustache diff --git a/config/clients/java/config.overrides.json b/config/clients/java/config.overrides.json index 90fe69ef..4eb8e6da 100644 --- a/config/clients/java/config.overrides.json +++ b/config/clients/java/config.overrides.json @@ -60,6 +60,10 @@ "destinationFilename": "src/main/java/dev/openfga/sdk/api/client/Configuration.java", "templateType": "SupportingFiles" }, + "config-ConfigurationOverride.java.mustache" : { + "destinationFilename": "src/main/java/dev/openfga/sdk/api/client/ConfigurationOverride.java", + "templateType": "SupportingFiles" + }, "config-ConfigurationTest.java.mustache" : { "destinationFilename": "src/test/java/dev/openfga/sdk/api/client/ConfigurationTest.java", "templateType": "SupportingFiles" diff --git a/config/clients/java/template/config-BaseConfiguration.java.mustache b/config/clients/java/template/config-BaseConfiguration.java.mustache index e2bd48ac..273abed3 100644 --- a/config/clients/java/template/config-BaseConfiguration.java.mustache +++ b/config/clients/java/template/config-BaseConfiguration.java.mustache @@ -5,8 +5,6 @@ import dev.openfga.sdk.errors.FgaInvalidParameterException; import java.time.Duration; public interface BaseConfiguration { - void assertValid() throws FgaInvalidParameterException; - String getApiUrl(); String getUserAgent(); diff --git a/config/clients/java/template/config-Configuration.java.mustache b/config/clients/java/template/config-Configuration.java.mustache index 2f7be777..402cffea 100644 --- a/config/clients/java/template/config-Configuration.java.mustache +++ b/config/clients/java/template/config-Configuration.java.mustache @@ -11,16 +11,15 @@ import java.net.http.HttpClient; import java.net.http.HttpConnectTimeoutException; import java.net.http.HttpRequest; import java.time.Duration; -import java.util.regex.Pattern; /** - * Configurations for an ApiClient. + * Configurations for an api client. */ public class Configuration implements BaseConfiguration { - public static final String VERSION = "{{packageVersion}}"; + public static final String VERSION = "0.0.1"; private static final String DEFAULT_API_URL = "http://localhost:8080"; - private static final String DEFAULT_USER_AGENT = "{{{userAgent}}}"; + private static final String DEFAULT_USER_AGENT = "openfga-sdk java/0.0.1"; private static final Duration DEFAULT_READ_TIMEOUT = Duration.ofSeconds(10); private static final Duration DEFAULT_CONNECT_TIMEOUT = Duration.ofSeconds(10); @@ -39,7 +38,6 @@ public class Configuration implements BaseConfiguration { /** * Assert that the configuration is valid. */ - @Override public void assertValid() throws FgaInvalidParameterException { // If apiUrl is null/empty/whitespace it will resolve to // DEFAULT_API_URL when getApiUrl is called. @@ -63,13 +61,39 @@ public class Configuration implements BaseConfiguration { } } + /** + * Construct a new {@link Configuration} with any non-null values of a {@link ConfigurationOverride} and remaining values from this {@link Configuration}. + * + * @param configurationOverride The values to override + * @return A new {@link Configuration} with values of this Configuration mixed with non-null values of configurationOverride + */ + public Configuration override(ConfigurationOverride configurationOverride) { + Configuration result = new Configuration(apiUrl); + + String overrideApiUrl = configurationOverride.getApiUrl(); + if (overrideApiUrl != null) { + result.apiUrl(overrideApiUrl); + } + + String overrideUserAgent = configurationOverride.getUserAgent(); + result.userAgent(overrideUserAgent != null ? overrideUserAgent : userAgent); + + Duration overrideReadTimeout = configurationOverride.getReadTimeout(); + result.readTimeout(overrideReadTimeout != null ? overrideReadTimeout : readTimeout); + + Duration overrideConnectTimeout = configurationOverride.getConnectTimeout(); + result.connectTimeout(overrideConnectTimeout != null ? overrideConnectTimeout : connectTimeout); + + return result; + } + /** * Set the API URL for the http client. * * @param apiUrl The URL. * @return This object. */ - public BaseConfiguration apiUrl(String apiUrl) { + public Configuration apiUrl(String apiUrl) { this.apiUrl = apiUrl; return this; } @@ -94,7 +118,7 @@ public class Configuration implements BaseConfiguration { * @param userAgent The user agent. * @return This object. */ - public BaseConfiguration userAgent(String userAgent) { + public Configuration userAgent(String userAgent) { this.userAgent = userAgent; return this; } @@ -120,7 +144,7 @@ public class Configuration implements BaseConfiguration { * effectively infinite value. * @return This object. */ - public BaseConfiguration readTimeout(Duration readTimeout) { + public Configuration readTimeout(Duration readTimeout) { this.readTimeout = readTimeout; return this; } @@ -152,7 +176,7 @@ public class Configuration implements BaseConfiguration { * @param connectTimeout connection timeout in milliseconds * @return This object. */ - public BaseConfiguration connectTimeout(Duration connectTimeout) { + public Configuration connectTimeout(Duration connectTimeout) { this.connectTimeout = connectTimeout; return this; } diff --git a/config/clients/java/template/config-ConfigurationOverride.java.mustache b/config/clients/java/template/config-ConfigurationOverride.java.mustache new file mode 100644 index 00000000..5d1e0a73 --- /dev/null +++ b/config/clients/java/template/config-ConfigurationOverride.java.mustache @@ -0,0 +1,127 @@ +{{>licenseInfo}} +package {{invokerPackage}}; + +import java.net.http.HttpClient; +import java.net.http.HttpConnectTimeoutException; +import java.net.http.HttpRequest; +import java.time.Duration; + +/** + * Configuration overrides for an api client. Values are initialized to null, and any values unset are intended to fall + * through to the values of a {@link Configuration}. + *

+ * More details on intended usage of this class can be found in the documentation of the {@link Configuration#override(ConfigurationOverride)} method. + */ +public class ConfigurationOverride implements BaseConfiguration { + private String apiUrl; + private String userAgent; + private Duration readTimeout; + private Duration connectTimeout; + + public ConfigurationOverride() { + this.apiUrl = null; + this.userAgent = null; + this.readTimeout = null; + this.connectTimeout = null; + } + + /** + * Set the API URL for the http client. + * + * @param apiUrl The URL. + * @return This object. + */ + public BaseConfiguration apiUrl(String apiUrl) { + this.apiUrl = apiUrl; + return this; + } + + /** + * Get the API URL that was set. + * + * @return The url. + */ + @Override + public String getApiUrl() { + return apiUrl; + } + + /** + * Set the user agent. + * + * @param userAgent The user agent. + * @return This object. + */ + public BaseConfiguration userAgent(String userAgent) { + this.userAgent = userAgent; + return this; + } + + /** + * Get the user agent. + * + * @return The user agent. + */ + @Override + public String getUserAgent() { + return userAgent; + } + + /** + * Set the read timeout for the http client. + * + *

This is the value used by default for each request, though it can be + * overridden on a per-request basis with a request interceptor.

+ * + * @param readTimeout The read timeout used by default by the http client. + * Setting this value to null resets the timeout to an + * effectively infinite value. + * @return This object. + */ + public BaseConfiguration readTimeout(Duration readTimeout) { + this.readTimeout = readTimeout; + return this; + } + + /** + * Get the read timeout that was set. + * + * @return The read timeout, or null if no timeout was set. Null represents + * an infinite wait time. + */ + @Override + public Duration getReadTimeout() { + return readTimeout; + } + + /** + * Sets the connect timeout (in milliseconds) for the http client. + * + *

In the case where a new connection needs to be established, if + * the connection cannot be established within the given {@code + * duration}, then {@link HttpClient#send(HttpRequest, BodyHandler) + * HttpClient::send} throws an {@link HttpConnectTimeoutException}, or + * {@link HttpClient#sendAsync(HttpRequest, BodyHandler) + * HttpClient::sendAsync} completes exceptionally with an + * {@code HttpConnectTimeoutException}. If a new connection does not + * need to be established, for example if a connection can be reused + * from a previous request, then this timeout duration has no effect. + * + * @param connectTimeout connection timeout in milliseconds + * @return This object. + */ + public BaseConfiguration connectTimeout(Duration connectTimeout) { + this.connectTimeout = connectTimeout; + return this; + } + + /** + * Get connection timeout (in milliseconds). + * + * @return Timeout in milliseconds + */ + @Override + public Duration getConnectTimeout() { + return connectTimeout; + } +} diff --git a/config/clients/java/template/libraries/native/api.mustache b/config/clients/java/template/libraries/native/api.mustache index 31df48d9..5d343ec6 100644 --- a/config/clients/java/template/libraries/native/api.mustache +++ b/config/clients/java/template/libraries/native/api.mustache @@ -5,7 +5,9 @@ import {{invokerPackage}}.ApiClient; import {{invokerPackage}}.ApiException; import {{invokerPackage}}.ApiResponse; import {{invokerPackage}}.Configuration; +import {{invokerPackage}}.ConfigurationOverride; import {{invokerPackage}}.Pair; +import dev.openfga.sdk.errors.FgaInvalidParameterException; {{#imports}} import {{import}}; @@ -53,7 +55,7 @@ import java.util.concurrent.CompletableFuture; public class {{classname}} { private final HttpClient memberVarHttpClient; private final ObjectMapper memberVarObjectMapper; - private final Configuration memberVarConfiguration; + private final Configuration configuration; private final Consumer memberVarInterceptor; private final Consumer> memberVarResponseInterceptor; private final Consumer> memberVarAsyncResponseInterceptor; @@ -61,7 +63,7 @@ public class {{classname}} { public {{classname}}(ApiClient apiClient, Configuration configuration) { memberVarHttpClient = apiClient.getHttpClient(); memberVarObjectMapper = apiClient.getObjectMapper(); - memberVarConfiguration = configuration; + this.configuration = configuration; memberVarInterceptor = apiClient.getRequestInterceptor(); memberVarResponseInterceptor = apiClient.getResponseInterceptor(); memberVarAsyncResponseInterceptor = apiClient.getAsyncResponseInterceptor(); @@ -176,8 +178,8 @@ public class {{classname}} { {{#isDeprecated}} @Deprecated {{/isDeprecated}} - public {{#returnType}}{{#asyncNative}}CompletableFuture<{{{returnType}}}>{{/asyncNative}}{{^asyncNative}}{{{returnType}}}{{/asyncNative}}{{/returnType}}{{^returnType}}{{#asyncNative}}CompletableFuture{{/asyncNative}}{{^asyncNative}}void{{/asyncNative}}{{/returnType}} {{operationId}}({{#allParams}}{{{dataType}}} {{paramName}}{{^-last}}, {{/-last}}{{/allParams}}) throws ApiException { - return {{operationId}}({{#allParams}}{{paramName}}{{^-last}}, {{/-last}}{{/allParams}}, memberVarConfiguration); + public {{#returnType}}{{#asyncNative}}CompletableFuture<{{{returnType}}}>{{/asyncNative}}{{^asyncNative}}{{{returnType}}}{{/asyncNative}}{{/returnType}}{{^returnType}}{{#asyncNative}}CompletableFuture{{/asyncNative}}{{^asyncNative}}void{{/asyncNative}}{{/returnType}} {{operationId}}({{#allParams}}{{{dataType}}} {{paramName}}{{^-last}}, {{/-last}}{{/allParams}}) throws ApiException, FgaInvalidParameterException { + return {{operationId}}({{#allParams}}{{paramName}}{{^-last}}, {{/-last}}{{/allParams}}, this.configuration); } /** @@ -186,7 +188,7 @@ public class {{classname}} { {{#allParams}} * @param {{paramName}} {{description}}{{#required}} (required){{/required}}{{^required}} (optional{{^isContainer}}{{#defaultValue}}, default to {{.}}{{/defaultValue}}){{/isContainer}}{{/required}} {{/allParams}} - * @param configuration Override the configuration this {{classname}} was constructed with + * @param configurationOverride Override the {@link Configuration} this {{classname}} was constructed with {{#returnType}} * @return {{#asyncNative}}CompletableFuture<{{/asyncNative}}{{returnType}}{{#asyncNative}}>{{/asyncNative}} {{/returnType}} @@ -207,7 +209,11 @@ public class {{classname}} { {{#isDeprecated}} @Deprecated {{/isDeprecated}} - public {{#returnType}}{{#asyncNative}}CompletableFuture<{{{returnType}}}>{{/asyncNative}}{{^asyncNative}}{{{returnType}}}{{/asyncNative}}{{/returnType}}{{^returnType}}{{#asyncNative}}CompletableFuture{{/asyncNative}}{{^asyncNative}}void{{/asyncNative}}{{/returnType}} {{operationId}}({{#allParams}}{{{dataType}}} {{paramName}}{{^-last}}, {{/-last}}{{/allParams}}, Configuration configuration) throws ApiException { + public {{#returnType}}{{#asyncNative}}CompletableFuture<{{{returnType}}}>{{/asyncNative}}{{^asyncNative}}{{{returnType}}}{{/asyncNative}}{{/returnType}}{{^returnType}}{{#asyncNative}}CompletableFuture{{/asyncNative}}{{^asyncNative}}void{{/asyncNative}}{{/returnType}} {{operationId}}({{#allParams}}{{{dataType}}} {{paramName}}{{^-last}}, {{/-last}}{{/allParams}}, ConfigurationOverride configurationOverride) throws ApiException, FgaInvalidParameterException { + return {{operationId}}({{#allParams}}{{paramName}}{{^-last}}, {{/-last}}{{/allParams}}, this.configuration.override(configurationOverride)); + } + + private {{#returnType}}{{#asyncNative}}CompletableFuture<{{{returnType}}}>{{/asyncNative}}{{^asyncNative}}{{{returnType}}}{{/asyncNative}}{{/returnType}}{{^returnType}}{{#asyncNative}}CompletableFuture{{/asyncNative}}{{^asyncNative}}void{{/asyncNative}}{{/returnType}} {{operationId}}({{#allParams}}{{{dataType}}} {{paramName}}{{^-last}}, {{/-last}}{{/allParams}}, Configuration configuration) throws ApiException, FgaInvalidParameterException { {{^asyncNative}} {{#returnType}}ApiResponse<{{{.}}}> localVarResponse = {{/returnType}}{{operationId}}WithHttpInfo({{#allParams}}{{paramName}}{{^-last}}, {{/-last}}{{/allParams}}); {{#returnType}} @@ -263,8 +269,8 @@ public class {{classname}} { {{#isDeprecated}} @Deprecated {{/isDeprecated}} - public {{#asyncNative}}CompletableFuture<{{/asyncNative}}ApiResponse<{{{returnType}}}{{^returnType}}Void{{/returnType}}>{{#asyncNative}}>{{/asyncNative}} {{operationId}}WithHttpInfo({{#allParams}}{{{dataType}}} {{paramName}}{{^-last}}, {{/-last}}{{/allParams}}) throws ApiException { - return {{operationId}}WithHttpInfo({{#allParams}}{{paramName}}{{^-last}}, {{/-last}}{{/allParams}}, memberVarConfiguration); + public {{#asyncNative}}CompletableFuture<{{/asyncNative}}ApiResponse<{{{returnType}}}{{^returnType}}Void{{/returnType}}>{{#asyncNative}}>{{/asyncNative}} {{operationId}}WithHttpInfo({{#allParams}}{{{dataType}}} {{paramName}}{{^-last}}, {{/-last}}{{/allParams}}) throws ApiException, FgaInvalidParameterException { + return {{operationId}}WithHttpInfo({{#allParams}}{{paramName}}{{^-last}}, {{/-last}}{{/allParams}}, this.configuration); } /** @@ -273,7 +279,7 @@ public class {{classname}} { {{#allParams}} * @param {{paramName}} {{description}}{{#required}} (required){{/required}}{{^required}} (optional{{^isContainer}}{{#defaultValue}}, default to {{.}}{{/defaultValue}}){{/isContainer}}{{/required}} {{/allParams}} - * @param configuration Override the configuration this {{classname}} was constructed with + * @param configurationOverride Override the {@link Configuration} this {{classname}} was constructed with * @return {{#asyncNative}}CompletableFuture<{{/asyncNative}}ApiResponse<{{returnType}}{{^returnType}}Void{{/returnType}}>{{#asyncNative}}>{{/asyncNative}} * @throws ApiException if fails to make API call {{#isDeprecated}} @@ -287,7 +293,11 @@ public class {{classname}} { {{#isDeprecated}} @Deprecated {{/isDeprecated}} - public {{#asyncNative}}CompletableFuture<{{/asyncNative}}ApiResponse<{{{returnType}}}{{^returnType}}Void{{/returnType}}>{{#asyncNative}}>{{/asyncNative}} {{operationId}}WithHttpInfo({{#allParams}}{{{dataType}}} {{paramName}}{{^-last}}, {{/-last}}{{/allParams}}, Configuration configuration) throws ApiException { + public {{#asyncNative}}CompletableFuture<{{/asyncNative}}ApiResponse<{{{returnType}}}{{^returnType}}Void{{/returnType}}>{{#asyncNative}}>{{/asyncNative}} {{operationId}}WithHttpInfo({{#allParams}}{{{dataType}}} {{paramName}}{{^-last}}, {{/-last}}{{/allParams}}, ConfigurationOverride configurationOverride) throws ApiException, FgaInvalidParameterException { + return {{operationId}}WithHttpInfo({{#allParams}}{{paramName}}{{^-last}}, {{/-last}}{{/allParams}}, this.configuration.override(configurationOverride)); + } + + private {{#asyncNative}}CompletableFuture<{{/asyncNative}}ApiResponse<{{{returnType}}}{{^returnType}}Void{{/returnType}}>{{#asyncNative}}>{{/asyncNative}} {{operationId}}WithHttpInfo({{#allParams}}{{{dataType}}} {{paramName}}{{^-last}}, {{/-last}}{{/allParams}}, Configuration configuration) throws ApiException, FgaInvalidParameterException { {{^asyncNative}} HttpRequest.Builder localVarRequestBuilder = {{operationId}}RequestBuilder({{#allParams}}{{paramName}}{{^-last}}, {{/-last}}{{/allParams}}, configuration); try { @@ -347,7 +357,7 @@ public class {{classname}} { {{/asyncNative}} {{#asyncNative}} try { - HttpRequest.Builder localVarRequestBuilder = {{operationId}}RequestBuilder({{#allParams}}{{paramName}}{{^-last}}, {{/-last}}{{/allParams}}, memberVarConfiguration); + HttpRequest.Builder localVarRequestBuilder = {{operationId}}RequestBuilder({{#allParams}}{{paramName}}{{^-last}}, {{/-last}}{{/allParams}}, configuration); return memberVarHttpClient.sendAsync( localVarRequestBuilder.build(), HttpResponse.BodyHandlers.ofString()).thenComposeAsync(localVarResponse -> { @@ -384,7 +394,7 @@ public class {{classname}} { {{/asyncNative}} } - private HttpRequest.Builder {{operationId}}RequestBuilder({{#allParams}}{{{dataType}}} {{paramName}}{{^-last}}, {{/-last}}{{/allParams}}, Configuration configuration) throws ApiException { + private HttpRequest.Builder {{operationId}}RequestBuilder({{#allParams}}{{{dataType}}} {{paramName}}{{^-last}}, {{/-last}}{{/allParams}}, Configuration configuration) throws ApiException, FgaInvalidParameterException { {{#allParams}} {{#required}} // verify the required parameter '{{paramName}}' is set @@ -394,6 +404,9 @@ public class {{classname}} { {{/required}} {{/allParams}} + // verify the Configuration is valid + configuration.assertValid(); + HttpRequest.Builder localVarRequestBuilder = HttpRequest.newBuilder(); {{! Switch delimiters for baseName so we can write constants like "{query}" }} @@ -456,13 +469,13 @@ public class {{classname}} { if (localVarQueryStringJoiner.length() != 0) { queryJoiner.add(localVarQueryStringJoiner.toString()); } - localVarRequestBuilder.uri(URI.create(memberVarConfiguration.getApiUrl() + localVarPath + '?' + queryJoiner.toString())); + localVarRequestBuilder.uri(URI.create(configuration.getApiUrl() + localVarPath + '?' + queryJoiner.toString())); } else { - localVarRequestBuilder.uri(URI.create(memberVarConfiguration.getApiUrl() + localVarPath)); + localVarRequestBuilder.uri(URI.create(configuration.getApiUrl() + localVarPath)); } {{/hasQueryParams}} {{^hasQueryParams}} - localVarRequestBuilder.uri(URI.create(memberVarConfiguration.getApiUrl() + localVarPath)); + localVarRequestBuilder.uri(URI.create(configuration.getApiUrl() + localVarPath)); {{/hasQueryParams}} {{#headerParams}} @@ -579,7 +592,7 @@ public class {{classname}} { localVarRequestBuilder.method("{{httpMethod}}", HttpRequest.BodyPublishers.noBody()); {{/hasFormParams}} {{/bodyParam}} - Duration readTimeout = memberVarConfiguration.getReadTimeout(); + Duration readTimeout = configuration.getReadTimeout(); if (readTimeout != null) { localVarRequestBuilder.timeout(readTimeout); } From 288add1dc18e985b36f6cdaebfbdd481b68b96f0 Mon Sep 17 00:00:00 2001 From: "Justin \"J.R.\" Hill" Date: Tue, 22 Aug 2023 18:30:29 -0700 Subject: [PATCH 4/5] test(java): Add tests on ConfigurationOverride --- .../config-Configuration.java.mustache | 11 ++- ...config-ConfigurationOverride.java.mustache | 8 +- .../config-ConfigurationTest.java.mustache | 99 ++++++++++++++++++- 3 files changed, 111 insertions(+), 7 deletions(-) diff --git a/config/clients/java/template/config-Configuration.java.mustache b/config/clients/java/template/config-Configuration.java.mustache index 402cffea..1023f793 100644 --- a/config/clients/java/template/config-Configuration.java.mustache +++ b/config/clients/java/template/config-Configuration.java.mustache @@ -16,10 +16,10 @@ import java.time.Duration; * Configurations for an api client. */ public class Configuration implements BaseConfiguration { - public static final String VERSION = "0.0.1"; + public static final String VERSION = "{{packageVersion}}"; private static final String DEFAULT_API_URL = "http://localhost:8080"; - private static final String DEFAULT_USER_AGENT = "openfga-sdk java/0.0.1"; + private static final String DEFAULT_USER_AGENT = "{{{userAgent}}}"; private static final Duration DEFAULT_READ_TIMEOUT = Duration.ofSeconds(10); private static final Duration DEFAULT_CONNECT_TIMEOUT = Duration.ofSeconds(10); @@ -28,6 +28,13 @@ public class Configuration implements BaseConfiguration { private Duration readTimeout; private Duration connectTimeout; + public Configuration() { + this.apiUrl = DEFAULT_API_URL; + this.userAgent = DEFAULT_USER_AGENT; + this.readTimeout = DEFAULT_READ_TIMEOUT; + this.connectTimeout = DEFAULT_CONNECT_TIMEOUT; + } + public Configuration(String apiUrl) { this.apiUrl = apiUrl; this.userAgent = DEFAULT_USER_AGENT; diff --git a/config/clients/java/template/config-ConfigurationOverride.java.mustache b/config/clients/java/template/config-ConfigurationOverride.java.mustache index 5d1e0a73..60f568a3 100644 --- a/config/clients/java/template/config-ConfigurationOverride.java.mustache +++ b/config/clients/java/template/config-ConfigurationOverride.java.mustache @@ -31,7 +31,7 @@ public class ConfigurationOverride implements BaseConfiguration { * @param apiUrl The URL. * @return This object. */ - public BaseConfiguration apiUrl(String apiUrl) { + public ConfigurationOverride apiUrl(String apiUrl) { this.apiUrl = apiUrl; return this; } @@ -52,7 +52,7 @@ public class ConfigurationOverride implements BaseConfiguration { * @param userAgent The user agent. * @return This object. */ - public BaseConfiguration userAgent(String userAgent) { + public ConfigurationOverride userAgent(String userAgent) { this.userAgent = userAgent; return this; } @@ -78,7 +78,7 @@ public class ConfigurationOverride implements BaseConfiguration { * effectively infinite value. * @return This object. */ - public BaseConfiguration readTimeout(Duration readTimeout) { + public ConfigurationOverride readTimeout(Duration readTimeout) { this.readTimeout = readTimeout; return this; } @@ -110,7 +110,7 @@ public class ConfigurationOverride implements BaseConfiguration { * @param connectTimeout connection timeout in milliseconds * @return This object. */ - public BaseConfiguration connectTimeout(Duration connectTimeout) { + public ConfigurationOverride connectTimeout(Duration connectTimeout) { this.connectTimeout = connectTimeout; return this; } diff --git a/config/clients/java/template/config-ConfigurationTest.java.mustache b/config/clients/java/template/config-ConfigurationTest.java.mustache index 77b75b45..efd7f036 100644 --- a/config/clients/java/template/config-ConfigurationTest.java.mustache +++ b/config/clients/java/template/config-ConfigurationTest.java.mustache @@ -4,8 +4,14 @@ import static org.junit.jupiter.api.Assertions.*; import dev.openfga.sdk.errors.*; import org.junit.jupiter.api.Test; +import java.time.Duration; class ConfigurationTest { + private static final String DEFAULT_API_URL = "http://localhost:8080"; + private static final String DEFAULT_USER_AGENT = "{{{userAgent}}}"; + private static final Duration DEFAULT_READ_TIMEOUT = Duration.ofSeconds(10); + private static final Duration DEFAULT_CONNECT_TIMEOUT = Duration.ofSeconds(10); + @Test void apiUrl_nullDefaults() throws FgaInvalidParameterException { // Given @@ -47,41 +53,132 @@ class ConfigurationTest { @Test void apiUrl_stringNoProtocolFails() { + // Given String apiUrl = "localhost:8080"; + + // When FgaInvalidParameterException e = assertThrows(FgaInvalidParameterException.class, () -> { var config = new Configuration(apiUrl); config.assertValid(); }); + + // Then assertEquals("Required parameter apiUrl was invalid when calling Configuration.", e.getMessage()); } @Test void apiUrl_stringInvalidProtocolFails() { + // Given String apiUrl = "zzz://localhost:8080"; + + // When FgaInvalidParameterException e = assertThrows(FgaInvalidParameterException.class, () -> { var config = new Configuration(apiUrl); config.assertValid(); }); + + // Then assertEquals("Required parameter apiUrl was invalid when calling Configuration.", e.getMessage()); } @Test void apiUrl_stringNoHostFails() { + // Given String apiUrl = "http://"; + + // When FgaInvalidParameterException e = assertThrows(FgaInvalidParameterException.class, () -> { var config = new Configuration(apiUrl); config.assertValid(); }); + + // Then assertEquals("Required parameter apiUrl was invalid when calling Configuration.", e.getMessage()); } @Test void apiUrl_stringBadPortFails() { - String apiUrl = "http://localshost:abcd"; + // Given + String apiUrl = "http://localhost:abcd"; + + // When FgaInvalidParameterException e = assertThrows(FgaInvalidParameterException.class, () -> { var config = new Configuration(apiUrl); config.assertValid(); }); + + // Then assertEquals("Required parameter apiUrl was invalid when calling Configuration.", e.getMessage()); } + + @Test + void defaults() { + // Given + Configuration config = new Configuration(); + + // NOTE: Failures in this test indicate that default values in Configuration have changed. Changing + // the defaults of Configuration can be a suprising and breaking change for consumers. + + // Then + assertEquals(DEFAULT_API_URL, config.getApiUrl()); + assertEquals(DEFAULT_USER_AGENT, config.getUserAgent()); + assertEquals(DEFAULT_READ_TIMEOUT, config.getReadTimeout()); + assertEquals(DEFAULT_CONNECT_TIMEOUT, config.getConnectTimeout()); + } + + @Test + void override_apiUrl() { + // Given + Configuration original = new Configuration(); + ConfigurationOverride configOverride = new ConfigurationOverride().apiUrl("https://override.url"); + + // When + Configuration result = original.override(configOverride); + + // Then + assertEquals("https://override.url", result.getApiUrl()); + assertEquals(DEFAULT_API_URL, original.getApiUrl(), "The Configuration's default apiUrl should be unmodified."); + } + + @Test + void override_userAgent() { + // Given + Configuration original = new Configuration(); + ConfigurationOverride configOverride = new ConfigurationOverride().userAgent("override-agent"); + + // When + Configuration result = original.override(configOverride); + + // Then + assertEquals("override-agent", result.getUserAgent()); + assertEquals(DEFAULT_USER_AGENT, original.getUserAgent(), "The Configuration's default userAgent should be unmodified."); + } + + @Test + void override_readTimeout() { + // Given + Configuration original = new Configuration(); + ConfigurationOverride configOverride = new ConfigurationOverride().readTimeout(Duration.ofDays(7)); + + // When + Configuration result = original.override(configOverride); + + // Then + assertEquals(Duration.ofDays(7), result.getReadTimeout()); + assertEquals(DEFAULT_READ_TIMEOUT, original.getReadTimeout(), "The Configuration's default readTimeout should be unmodified."); + } + + @Test + void override_connectTimeout() { + // Given + Configuration original = new Configuration(); + ConfigurationOverride configOverride = new ConfigurationOverride().connectTimeout(Duration.ofDays(7)); + + // When + Configuration result = original.override(configOverride); + + // Then + assertEquals(Duration.ofDays(7), result.getConnectTimeout()); + assertEquals(DEFAULT_CONNECT_TIMEOUT, original.getConnectTimeout(), "The Configuration's default connectTimeout should be unmodified."); + } } From 26f8b248075be1430f69504975ba8b28d6519a1f Mon Sep 17 00:00:00 2001 From: "Justin \"J.R.\" Hill" Date: Wed, 23 Aug 2023 13:04:02 -0700 Subject: [PATCH 5/5] refactor(java): Consistent code in Configuration.override(...) --- .../java/template/config-Configuration.java.mustache | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/config/clients/java/template/config-Configuration.java.mustache b/config/clients/java/template/config-Configuration.java.mustache index 1023f793..b9d640cb 100644 --- a/config/clients/java/template/config-Configuration.java.mustache +++ b/config/clients/java/template/config-Configuration.java.mustache @@ -75,12 +75,10 @@ public class Configuration implements BaseConfiguration { * @return A new {@link Configuration} with values of this Configuration mixed with non-null values of configurationOverride */ public Configuration override(ConfigurationOverride configurationOverride) { - Configuration result = new Configuration(apiUrl); + Configuration result = new Configuration(); String overrideApiUrl = configurationOverride.getApiUrl(); - if (overrideApiUrl != null) { - result.apiUrl(overrideApiUrl); - } + result.apiUrl(overrideApiUrl != null ? overrideApiUrl : apiUrl); String overrideUserAgent = configurationOverride.getUserAgent(); result.userAgent(overrideUserAgent != null ? overrideUserAgent : userAgent);