@@ -183,7 +185,7 @@
org.projectlombok
lombok
- 1.18.30
+ 1.18.42
diff --git a/prettier.config.js b/prettier.config.js
new file mode 100644
index 0000000..42070c2
--- /dev/null
+++ b/prettier.config.js
@@ -0,0 +1,39 @@
+// Prettier configuration for code-formatter lambda
+// Aligned with sdk-gen/prettier.config.ts
+
+module.exports = {
+ printWidth: 100,
+ tabWidth: 2,
+ useTabs: false,
+ semi: true,
+ singleQuote: true,
+ quoteProps: 'as-needed',
+ jsxSingleQuote: false,
+ trailingComma: 'all',
+ bracketSpacing: true,
+ arrowParens: 'always',
+ overrides: [
+ {
+ files: '.editorconfig',
+ options: { parser: 'yaml' },
+ },
+ {
+ files: 'LICENSE',
+ options: { parser: 'markdown' },
+ },
+ {
+ files: '*.php',
+ options: {
+ parser: 'php',
+ phpVersion: '8.0',
+ trailingCommaPHP: false,
+ },
+ },
+ {
+ files: '*.md',
+ options: {
+ parser: 'markdown',
+ },
+ },
+ ],
+};
diff --git a/src/main/java/com/alohi/signplus/Signplus.java b/src/main/java/com/alohi/signplus/Signplus.java
index 79ef2a6..522601b 100644
--- a/src/main/java/com/alohi/signplus/Signplus.java
+++ b/src/main/java/com/alohi/signplus/Signplus.java
@@ -8,18 +8,29 @@
import java.util.concurrent.TimeUnit;
import okhttp3.OkHttpClient;
-/** Integrate legally-binding electronic signature to your workflow */
+/**
+ * Integrate legally-binding electronic signature to your workflow
+ */
public class Signplus {
public final SignplusService signplus;
private final SignplusConfig config;
+ /**
+ * Constructs a new instance of Signplus with default configuration.
+ */
public Signplus() {
// Default configs
this(SignplusConfig.builder().build());
}
+ /**
+ * Constructs a new instance of Signplus with custom configuration.
+ * Initializes all services, HTTP client, and optional OAuth token manager.
+ *
+ * @param config The SDK configuration including base URL, authentication, timeout, and retry settings
+ */
public Signplus(SignplusConfig config) {
this.config = config;
@@ -32,14 +43,29 @@ public Signplus(SignplusConfig config) {
this.signplus = new SignplusService(httpClient, config);
}
+ /**
+ * Sets the environment for all API requests.
+ *
+ * @param environment The environment to use (e.g., DEFAULT, PRODUCTION, STAGING)
+ */
public void setEnvironment(Environment environment) {
setBaseUrl(environment.getUrl());
}
+ /**
+ * Sets the base URL for all API requests.
+ *
+ * @param baseUrl The base URL to use for API requests
+ */
public void setBaseUrl(String baseUrl) {
this.config.setBaseUrl(baseUrl);
}
+ /**
+ * Sets the access token (Bearer token) for all API requests.
+ *
+ * @param token The access token to use for authentication
+ */
public void setAccessToken(String token) {
this.config.setAccessToken(token);
}
diff --git a/src/main/java/com/alohi/signplus/config/RequestConfig.java b/src/main/java/com/alohi/signplus/config/RequestConfig.java
new file mode 100644
index 0000000..b6b451c
--- /dev/null
+++ b/src/main/java/com/alohi/signplus/config/RequestConfig.java
@@ -0,0 +1,60 @@
+package com.alohi.signplus.config;
+
+import com.alohi.signplus.http.Environment;
+import lombok.Builder;
+import lombok.Data;
+
+/**
+ * Configuration class for request-level, method-level, and service-level overrides.
+ *
+ * Provides a hierarchical configuration override mechanism with the following priority
+ * (highest to lowest):
+ *
+ * - Request config - passed directly to a method call
+ * - Method config - set via {@code setConfig()} on a service
+ * - Service config - set via {@code setConfig()} on a service
+ * - SDK config - set at SDK initialization
+ *
+ *
+ * All fields are optional (nullable). Only non-null fields will override the corresponding
+ * SDK-level configuration values.
+ */
+@Builder
+@Data
+public class RequestConfig {
+
+ /** Override the base URL for API requests. */
+ private String baseUrl;
+
+ /** Override the environment for API requests. */
+ private Environment environment;
+
+ /** Override the request timeout in milliseconds. */
+ private Long timeout;
+
+ /** Override the access token (Bearer token) for authentication. */
+ private String accessToken;
+
+ /** Override the retry configuration. */
+ private RetryConfig retryConfig;
+
+ /**
+ * Merges multiple {@link RequestConfig} instances in order. Later configs take precedence
+ * over earlier ones for any non-null field.
+ *
+ * @param configs The configs to merge (in order of increasing priority)
+ * @return A new merged {@link RequestConfig}
+ */
+ public static RequestConfig merge(RequestConfig... configs) {
+ RequestConfigBuilder builder = RequestConfig.builder();
+ for (RequestConfig cfg : configs) {
+ if (cfg == null) continue;
+ if (cfg.getBaseUrl() != null) builder.baseUrl(cfg.getBaseUrl());
+ if (cfg.getEnvironment() != null) builder.environment(cfg.getEnvironment());
+ if (cfg.getTimeout() != null) builder.timeout(cfg.getTimeout());
+ if (cfg.getAccessToken() != null) builder.accessToken(cfg.getAccessToken());
+ if (cfg.getRetryConfig() != null) builder.retryConfig(cfg.getRetryConfig());
+ }
+ return builder.build();
+ }
+}
diff --git a/src/main/java/com/alohi/signplus/config/RetryConfig.java b/src/main/java/com/alohi/signplus/config/RetryConfig.java
index 99c602c..a2b139f 100644
--- a/src/main/java/com/alohi/signplus/config/RetryConfig.java
+++ b/src/main/java/com/alohi/signplus/config/RetryConfig.java
@@ -5,10 +5,16 @@
import java.net.SocketException;
import java.net.SocketTimeoutException;
import java.util.Arrays;
+import java.util.Collections;
import java.util.List;
import lombok.Builder;
import lombok.Data;
+/**
+ * Configuration for automatic retry behavior on failed requests.
+ * Defines retry limits, backoff strategy, and conditions for retrying requests.
+ * Uses builder pattern for flexible configuration with sensible defaults.
+ */
@Data
@Builder
public class RetryConfig {
@@ -29,7 +35,7 @@ public class RetryConfig {
private int jitter = 50;
@Builder.Default
- private List statusCodesToRetry = Arrays.asList(408, 429, 500, 502, 503, 504);
+ private List statusCodesToRetry = Collections.emptyList();
@Builder.Default
private List httpMethodsToRetry = Arrays.asList(
diff --git a/src/main/java/com/alohi/signplus/config/SignplusConfig.java b/src/main/java/com/alohi/signplus/config/SignplusConfig.java
index 3c0ae4c..b46b0e0 100644
--- a/src/main/java/com/alohi/signplus/config/SignplusConfig.java
+++ b/src/main/java/com/alohi/signplus/config/SignplusConfig.java
@@ -6,13 +6,18 @@
import lombok.NonNull;
import lombok.Setter;
+/**
+ * Configuration class for SDK client settings.
+ * Provides builder pattern for configuring base URLs, authentication, timeouts, and retry behavior.
+ * All configuration options have sensible defaults and can be customized as needed.
+ */
@Builder
@Data
public class SignplusConfig {
@NonNull
@Builder.Default
- private String userAgent = "signplus/1.0.0";
+ private String userAgent = "postman-codegen/1.5.1 signplus/4.0.0 (java)";
@Setter
private String baseUrl;
diff --git a/src/main/java/com/alohi/signplus/http/Environment.java b/src/main/java/com/alohi/signplus/http/Environment.java
index 983c71b..6d91236 100644
--- a/src/main/java/com/alohi/signplus/http/Environment.java
+++ b/src/main/java/com/alohi/signplus/http/Environment.java
@@ -1,10 +1,10 @@
package com.alohi.signplus.http;
import lombok.Getter;
-import okhttp3.HttpUrl;
/**
- * SDK Environments
+ * Predefined environment configurations for the SDK.
+ * Each environment represents a different base URL (e.g., production, staging, development).
*/
@Getter
public enum Environment {
@@ -13,11 +13,6 @@ public enum Environment {
private final String url;
Environment(String url) {
- if (HttpUrl.parse(url) == null) {
- throw new IllegalArgumentException(
- String.format("Environment url [%s] is not valid. Please use the following format https://api.example.com", url)
- );
- }
this.url = url;
}
}
diff --git a/src/main/java/com/alohi/signplus/http/HttpMethod.java b/src/main/java/com/alohi/signplus/http/HttpMethod.java
index 1cc6f4c..134e29c 100644
--- a/src/main/java/com/alohi/signplus/http/HttpMethod.java
+++ b/src/main/java/com/alohi/signplus/http/HttpMethod.java
@@ -3,21 +3,39 @@
import lombok.Getter;
import lombok.RequiredArgsConstructor;
+/**
+ * Enum representing HTTP methods (verbs) used in API requests.
+ * Provides utility methods for determining method characteristics.
+ */
@Getter
@RequiredArgsConstructor
public enum HttpMethod {
+ /** HTTP GET method for retrieving resources */
GET("GET"),
+ /** HTTP POST method for creating resources */
POST("POST"),
+ /** HTTP PUT method for updating/replacing resources */
PUT("PUT"),
+ /** HTTP PATCH method for partially updating resources */
PATCH("PATCH"),
+ /** HTTP DELETE method for removing resources */
DELETE("DELETE"),
+ /** HTTP HEAD method for retrieving headers only */
HEAD("HEAD"),
+ /** HTTP OPTIONS method for describing communication options */
OPTIONS("OPTIONS"),
+ /** HTTP TRACE method for diagnostic purposes */
TRACE("TRACE"),
+ /** HTTP CONNECT method for establishing tunnels */
CONNECT("CONNECT");
private final String method;
+ /**
+ * Determines if this HTTP method requires a request body.
+ *
+ * @return true if POST, PUT, or PATCH; false otherwise
+ */
public boolean requiresRequestBody() {
return method.equals("POST") || method.equals("PUT") || method.equals("PATCH");
}
diff --git a/src/main/java/com/alohi/signplus/http/ModelConverter.java b/src/main/java/com/alohi/signplus/http/ModelConverter.java
index 4cc126f..9474b88 100644
--- a/src/main/java/com/alohi/signplus/http/ModelConverter.java
+++ b/src/main/java/com/alohi/signplus/http/ModelConverter.java
@@ -1,16 +1,29 @@
package com.alohi.signplus.http;
+import com.alohi.signplus.http.util.ContentTypes;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
+import com.fasterxml.jackson.databind.exc.MismatchedInputException;
+import java.lang.reflect.Method;
+import java.nio.charset.StandardCharsets;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import okhttp3.MediaType;
import okhttp3.Response;
import okhttp3.ResponseBody;
import org.openapitools.jackson.nullable.JsonNullableModule;
+/**
+ * Utility class for converting between JSON and Java models.
+ * Uses Jackson ObjectMapper with preconfigured settings for SDK serialization/deserialization.
+ * Handles responses as Class types or TypeReferences with proper error logging.
+ */
public final class ModelConverter {
+ private static final Logger logger = Logger.getLogger(ModelConverter.class.getName());
private static final ObjectMapper mapper = new ObjectMapper();
static {
@@ -27,39 +40,106 @@ public final class ModelConverter {
private ModelConverter() {}
+ /**
+ * Converts an HTTP response body to a Java object of the specified class.
+ *
+ * @param The type of the model class
+ * @param response The HTTP response to convert
+ * @param clazz The target class to deserialize into
+ * @return The deserialized object, or null if conversion fails
+ */
public static T convert(final Response response, final Class clazz) {
- final ResponseBody body = response.body();
try {
- return mapper.readValue(body.string(), clazz);
+ return convert(response.body().bytes(), clazz);
} catch (Exception e) {
- e.printStackTrace();
+ logger.log(Level.SEVERE, "Failed to read response body: " + e.getMessage(), e);
+ return null;
+ }
+ }
+
+ /**
+ * Converts raw byte array to a Java object of the specified class.
+ *
+ * @param The type of the model class
+ * @param bodyBytes The response body as bytes
+ * @param clazz The target class to deserialize into
+ * @return The deserialized object, or null if conversion fails or body is empty
+ */
+ public static T convert(final byte[] bodyBytes, final Class clazz) {
+ try {
+ if (bodyBytes == null || bodyBytes.length == 0) {
+ logger.log(Level.FINE, "Received empty response body, returning null");
+ return null;
+ }
+
+ return mapper.readValue(bodyBytes, clazz);
+ } catch (Exception e) {
+ logger.log(Level.SEVERE, "Failed to read response body: " + e.getMessage(), e);
return null;
}
}
public static T convert(final String response, final Class clazz) {
try {
+ // Handle empty responses
+ if (response == null || response.trim().isEmpty()) {
+ logger.log(Level.FINE, "Received empty response string, returning null");
+ return null;
+ }
return mapper.readValue(response, clazz);
+ } catch (MismatchedInputException e) {
+ logger.log(
+ Level.SEVERE,
+ "Failed to parse response - invalid or malformed JSON: " + e.getMessage(),
+ e
+ );
+ return null;
} catch (Exception e) {
- e.printStackTrace();
+ logger.log(Level.SEVERE, "Unexpected error parsing response: " + e.getMessage(), e);
return null;
}
}
public static T convert(Response response, TypeReference typeReference) {
try {
- return convert(response.body().string(), typeReference);
+ return convert(response.body().bytes(), typeReference);
} catch (Exception e) {
- e.printStackTrace();
+ logger.log(Level.SEVERE, "Failed to read response body: " + e.getMessage(), e);
+ return null;
+ }
+ }
+
+ public static T convert(byte[] bodyBytes, TypeReference typeReference) {
+ try {
+ if (bodyBytes == null || bodyBytes.length == 0) {
+ logger.log(Level.FINE, "Received empty response body, returning null");
+ return null;
+ }
+
+ return mapper.readValue(bodyBytes, typeReference);
+ } catch (Exception e) {
+ logger.log(Level.SEVERE, "Failed to read response body: " + e.getMessage(), e);
return null;
}
}
public static T convert(String response, TypeReference typeReference) {
try {
+ // Handle empty responses
+ if (response == null || response.trim().isEmpty()) {
+ logger.log(Level.FINE, "Received empty response string, returning null");
+ return null;
+ }
return mapper.readValue(response, typeReference);
+ } catch (MismatchedInputException e) {
+ logger.log(
+ Level.SEVERE,
+ "Failed to parse response - invalid or malformed JSON: " + e.getMessage(),
+ e
+ );
+ return null;
} catch (Exception e) {
- e.printStackTrace();
+ logger.log(Level.SEVERE, "Unexpected error parsing response: " + e.getMessage(), e);
return null;
}
}
@@ -68,7 +148,7 @@ public static String readString(Response response) {
try {
return response.body().string();
} catch (Exception e) {
- e.printStackTrace();
+ logger.log(Level.SEVERE, "Failed to read response body as string: " + e.getMessage(), e);
return null;
}
}
@@ -77,17 +157,96 @@ public static byte[] readBytes(Response response) {
try {
return response.body().bytes();
} catch (Exception e) {
- e.printStackTrace();
+ logger.log(Level.SEVERE, "Failed to read response body as bytes: " + e.getMessage(), e);
return null;
}
}
+ public static String toBodyString(byte[] bodyBytes) {
+ try {
+ return new String(bodyBytes, StandardCharsets.UTF_8);
+ } catch (Exception e) {
+ logger.log(Level.SEVERE, "Failed to convert body bytes to string: " + e.getMessage(), e);
+ return "";
+ }
+ }
+
+ /**
+ * Serializes a Java model object to JSON string.
+ *
+ * @param model The model object to serialize
+ * @return The JSON string representation, or null if serialization fails
+ */
public static String modelToJson(final Object model) {
try {
return mapper.writeValueAsString(model);
} catch (Exception e) {
- e.printStackTrace();
+ logger.log(Level.SEVERE, "Failed to serialize model to JSON: " + e.getMessage(), e);
return null;
}
}
+
+ /**
+ * Converts response body bytes to a OneOf type by determining the appropriate factory method
+ * based on the content type and using reflection to invoke it.
+ *
+ * @param The type of the OneOf model class
+ * @param bodyBytes The response body as bytes
+ * @param typeReference The target TypeReference to deserialize into
+ * @param contentType The content type of the response
+ * @return The deserialized object, or null if conversion fails
+ */
+ public static T convertOneOf(
+ final byte[] bodyBytes,
+ final TypeReference typeReference,
+ final MediaType contentType
+ ) {
+ try {
+ String methodName;
+ Class> parameterType;
+ Object data;
+
+ if (ContentTypes.isTextual(contentType)) {
+ methodName = "ofString";
+ parameterType = String.class;
+ data = toBodyString(bodyBytes);
+ } else if (ContentTypes.isBinary(contentType)) {
+ methodName = "ofBinary";
+ parameterType = byte[].class;
+ data = bodyBytes;
+ } else {
+ return convert(bodyBytes, typeReference);
+ }
+
+ Class> clazz = (Class>) typeReference.getType();
+
+ Method method = clazz.getDeclaredMethod(methodName, parameterType);
+ method.setAccessible(true);
+
+ return (T) method.invoke(null, data);
+ } catch (Exception e) {
+ logger.log(Level.SEVERE, "Failed to convert OneOf response: " + e.getMessage(), e);
+ return null;
+ }
+ }
+
+ /**
+ * Converts response body string to a OneOf type by determining the appropriate factory method
+ * based on the content type and using reflection to invoke it.
+ *
+ * @param The type of the OneOf model class
+ * @param body The response body as string
+ * @param typeReference The target TypeReference to deserialize into
+ * @param contentType The content type of the response
+ * @return The deserialized object, or null if conversion fails
+ */
+ public static T convertOneOf(
+ final String body,
+ final TypeReference typeReference,
+ final MediaType contentType
+ ) {
+ byte[] bodyBytes = body.getBytes(StandardCharsets.UTF_8);
+
+ return convertOneOf(bodyBytes, typeReference, contentType);
+ }
}
diff --git a/src/main/java/com/alohi/signplus/http/interceptors/DefaultHeadersInterceptor.java b/src/main/java/com/alohi/signplus/http/interceptors/DefaultHeadersInterceptor.java
index 17b66f8..ab9cc19 100644
--- a/src/main/java/com/alohi/signplus/http/interceptors/DefaultHeadersInterceptor.java
+++ b/src/main/java/com/alohi/signplus/http/interceptors/DefaultHeadersInterceptor.java
@@ -9,19 +9,41 @@
import okhttp3.Request.Builder;
import okhttp3.Response;
+/**
+ * OkHttp interceptor that adds default headers to all requests.
+ * Currently adds the User-Agent header based on SDK configuration.
+ */
public class DefaultHeadersInterceptor implements Interceptor {
private final Map defaultHeaders = new HashMap<>();
+ /**
+ * Constructs a new DefaultHeadersInterceptor.
+ *
+ * @param config The SDK configuration containing default header values
+ */
public DefaultHeadersInterceptor(SignplusConfig config) {
defaultHeaders.put("User-Agent", config.getUserAgent());
}
+ /**
+ * Intercepts a request to add default headers before proceeding.
+ *
+ * @param chain The OkHttp interceptor chain
+ * @return The HTTP response
+ * @throws IOException if an I/O error occurs during request execution
+ */
@Override
public Response intercept(Chain chain) throws IOException {
return chain.proceed(addDefaultHeadersToRequest(chain.request()));
}
+ /**
+ * Adds default headers to a request.
+ *
+ * @param request The HTTP request to modify
+ * @return The modified request with default headers added
+ */
private Request addDefaultHeadersToRequest(Request request) {
if (defaultHeaders.isEmpty()) {
return request;
diff --git a/src/main/java/com/alohi/signplus/http/interceptors/RetryInterceptor.java b/src/main/java/com/alohi/signplus/http/interceptors/RetryInterceptor.java
index 9cf3c8b..bef865e 100644
--- a/src/main/java/com/alohi/signplus/http/interceptors/RetryInterceptor.java
+++ b/src/main/java/com/alohi/signplus/http/interceptors/RetryInterceptor.java
@@ -8,11 +8,23 @@
import okhttp3.Request;
import okhttp3.Response;
+/**
+ * OkHttp interceptor that automatically retries failed HTTP requests.
+ * Supports configurable retry attempts, backoff delays, and specific status codes or exceptions to retry.
+ */
@AllArgsConstructor
public class RetryInterceptor implements Interceptor {
private RetryConfig config;
+ /**
+ * Intercepts an HTTP request and retries it according to the configured retry policy.
+ * Implements exponential backoff between retry attempts.
+ *
+ * @param chain The OkHttp interceptor chain
+ * @return The HTTP response from a successful attempt
+ * @throws IOException if all retry attempts fail or a non-retryable exception occurs
+ */
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
@@ -31,7 +43,10 @@ public Response intercept(Chain chain) throws IOException {
}
tryCount++;
} catch (IOException e) {
- if (!config.getExceptionsToRetry().contains(e.getClass()) || tryCount == config.getMaxRetries()) {
+ if (
+ !config.getExceptionsToRetry().contains(e.getClass()) ||
+ tryCount == config.getMaxRetries()
+ ) {
throw e;
}
}
@@ -48,13 +63,31 @@ public Response intercept(Chain chain) throws IOException {
return response;
}
+ /**
+ * Calculates the delay before the next retry attempt using exponential backoff.
+ *
+ * @param tryCount The current retry attempt number (1-indexed)
+ * @return The delay in milliseconds, capped at the configured maximum delay
+ */
private int calculateDelay(int tryCount) {
- final int delay = (int) (config.getInitialDelay() * Math.pow(config.getBackoffFactor(), tryCount - 1));
+ final int delay = (int) (config.getInitialDelay() *
+ Math.pow(config.getBackoffFactor(), tryCount - 1));
return Math.min(delay, config.getMaxDelay());
}
+ /**
+ * Determines if a response should be retried based on status code and HTTP method.
+ * By default, retries all 5xx server errors and specific 4xx client errors (408 Timeout, 429 Rate Limit).
+ *
+ * @param response The HTTP response to check
+ * @return true if the response should be retried, false otherwise
+ */
private boolean isRetryable(Response response) {
- final boolean isRetryableStatusCode = config.getStatusCodesToRetry().contains(response.code());
+ final int statusCode = response.code();
+ final boolean isRetryableStatusCode = !config.getStatusCodesToRetry().isEmpty()
+ ? config.getStatusCodesToRetry().contains(statusCode)
+ : statusCode >= 500 || statusCode == 408 || statusCode == 429;
+
final boolean isRetryableMethod = config
.getHttpMethodsToRetry()
.stream()
diff --git a/src/main/java/com/alohi/signplus/http/serialization/PathSerializationStyle.java b/src/main/java/com/alohi/signplus/http/serialization/PathSerializationStyle.java
index 82ca9a9..2120022 100644
--- a/src/main/java/com/alohi/signplus/http/serialization/PathSerializationStyle.java
+++ b/src/main/java/com/alohi/signplus/http/serialization/PathSerializationStyle.java
@@ -1,10 +1,18 @@
package com.alohi.signplus.http.serialization;
+/**
+ * Serialization styles supported for path parameters in OpenAPI specifications.
+ * Defines how values are encoded when embedded in URL paths.
+ */
public enum PathSerializationStyle {
+ /** Simple style - comma-separated values (default for path parameters) */
SIMPLE(SerializationStyle.SIMPLE),
+ /** Label style - dot-prefixed values (.value or .id.name) */
LABEL(SerializationStyle.LABEL),
+ /** Matrix style - semicolon-prefixed key-value pairs (;key=value) */
MATRIX(SerializationStyle.MATRIX);
+ /** The underlying serialization style */
public final SerializationStyle style;
PathSerializationStyle(SerializationStyle style) {
diff --git a/src/main/java/com/alohi/signplus/http/serialization/QuerySerializationStyle.java b/src/main/java/com/alohi/signplus/http/serialization/QuerySerializationStyle.java
index 4b58850..0401034 100644
--- a/src/main/java/com/alohi/signplus/http/serialization/QuerySerializationStyle.java
+++ b/src/main/java/com/alohi/signplus/http/serialization/QuerySerializationStyle.java
@@ -1,11 +1,20 @@
package com.alohi.signplus.http.serialization;
+/**
+ * Serialization styles supported for query parameters in OpenAPI specifications.
+ * Defines how values are encoded as URL query strings.
+ */
public enum QuerySerializationStyle {
+ /** Form style - ampersand-separated key-value pairs (default for query parameters) */
FORM(SerializationStyle.FORM),
+ /** Space-delimited style - values separated by spaces (encoded as %20) */
SPACE_DELIMITED(SerializationStyle.SPACE_DELIMITED),
+ /** Pipe-delimited style - values separated by pipe characters (|) */
PIPE_DELIMITED(SerializationStyle.PIPE_DELIMITED),
+ /** Deep object style - nested objects with bracket notation (obj[field]=value) */
DEEP_OBJECT(SerializationStyle.DEEP_OBJECT);
+ /** The underlying serialization style */
public final SerializationStyle style;
QuerySerializationStyle(SerializationStyle style) {
diff --git a/src/main/java/com/alohi/signplus/http/serialization/SerializationStyle.java b/src/main/java/com/alohi/signplus/http/serialization/SerializationStyle.java
index 8ae3e00..db39a31 100644
--- a/src/main/java/com/alohi/signplus/http/serialization/SerializationStyle.java
+++ b/src/main/java/com/alohi/signplus/http/serialization/SerializationStyle.java
@@ -1,11 +1,22 @@
package com.alohi.signplus.http.serialization;
+/**
+ * Enum representing OpenAPI parameter serialization styles.
+ * Defines how parameters are formatted for different parts of HTTP requests.
+ */
public enum SerializationStyle {
+ /** Simple style - comma-separated values (default for path/header parameters) */
SIMPLE,
+ /** Label style - dot-prefixed values for path parameters (.value or .id.name) */
LABEL,
+ /** Matrix style - semicolon-prefixed key-value pairs for path parameters (;key=value) */
MATRIX,
+ /** Form style - ampersand-separated key-value pairs (default for query parameters) */
FORM,
+ /** Space-delimited style - space-separated values for query parameters */
SPACE_DELIMITED,
+ /** Pipe-delimited style - pipe-separated values for query parameters */
PIPE_DELIMITED,
+ /** Deep object style - nested objects with bracket notation (id[key]=value) */
DEEP_OBJECT,
}
diff --git a/src/main/java/com/alohi/signplus/http/serialization/Serializer.java b/src/main/java/com/alohi/signplus/http/serialization/Serializer.java
index 2f13436..4ee6d83 100644
--- a/src/main/java/com/alohi/signplus/http/serialization/Serializer.java
+++ b/src/main/java/com/alohi/signplus/http/serialization/Serializer.java
@@ -4,21 +4,50 @@
import java.util.Map;
import java.util.stream.Collectors;
+/**
+ * Utility class for serializing parameters according to OpenAPI serialization styles.
+ * Supports simple, label, matrix, form, pipe-delimited, space-delimited, and deep object styles.
+ * Handles primitives, strings, lists, and complex objects with proper encoding.
+ */
public class Serializer {
+ /**
+ * Serializes a long value to a string with the specified style and encoding.
+ *
+ * @param key The parameter name
+ * @param value The long value to serialize
+ * @param style The serialization style to use
+ * @param encode Whether to URL-encode the value
+ * @return The serialized parameter string
+ */
public static String serialize(String key, long value, SerializationStyle style, boolean encode) {
return serialize(key, String.valueOf(value), style, encode);
}
- public static String serialize(String key, double value, SerializationStyle style, boolean encode) {
+ public static String serialize(
+ String key,
+ double value,
+ SerializationStyle style,
+ boolean encode
+ ) {
return serialize(key, String.valueOf(value), style, encode);
}
- public static String serialize(String key, boolean value, SerializationStyle style, boolean encode) {
+ public static String serialize(
+ String key,
+ boolean value,
+ SerializationStyle style,
+ boolean encode
+ ) {
return serialize(key, String.valueOf(value), style, encode);
}
- public static String serialize(String key, String value, SerializationStyle style, boolean encode) {
+ public static String serialize(
+ String key,
+ String value,
+ SerializationStyle style,
+ boolean encode
+ ) {
if (value == null) {
value = "null";
}
@@ -35,7 +64,24 @@ public static String serialize(String key, String value, SerializationStyle styl
}
}
- public static String serialize(String key, Object value, SerializationStyle style, boolean explode, boolean encode) {
+ /**
+ * Serializes any object value according to the specified serialization style.
+ * Handles primitives, strings, lists, and complex objects with proper OpenAPI serialization.
+ *
+ * @param key The parameter name
+ * @param value The value to serialize (can be primitive, String, List, or object)
+ * @param style The serialization style to use (SIMPLE, LABEL, MATRIX, FORM, etc.)
+ * @param explode Whether to use exploded form for arrays/objects
+ * @param encode Whether to URL-encode values
+ * @return The serialized parameter string
+ */
+ public static String serialize(
+ String key,
+ Object value,
+ SerializationStyle style,
+ boolean explode,
+ boolean encode
+ ) {
if (value == null) {
return serialize(key, "null", style, encode);
}
@@ -77,7 +123,10 @@ private static String serializeList(
return "";
}
- List serializedValues = value.stream().map(v -> serializeValue(v, encode)).collect(Collectors.toList());
+ List serializedValues = value
+ .stream()
+ .map(v -> serializeValue(v, encode))
+ .collect(Collectors.toList());
switch (style) {
case SIMPLE:
return String.join(",", serializedValues);
@@ -87,7 +136,10 @@ private static String serializeList(
return String.format(".%s", String.join(separator, serializedValues));
case MATRIX:
return explode
- ? serializedValues.stream().map(v -> String.format(";%s=%s", key, v)).collect(Collectors.joining(""))
+ ? serializedValues
+ .stream()
+ .map(v -> String.format(";%s=%s", key, v))
+ .collect(Collectors.joining(""))
: String.format(";%s=", key) + String.join(",", serializedValues);
}
@@ -185,7 +237,22 @@ private static String serializeObject(
}
}
- public static String serializeDeepObject(String key, Object value, boolean topLevel, boolean encode) {
+ /**
+ * Serializes an object using deep object style (e.g., id[key]=value&id[key][deepKey]=deepValue).
+ * Recursively handles nested objects with bracket notation.
+ *
+ * @param key The parameter name
+ * @param value The value to serialize
+ * @param topLevel Whether this is the top-level call (affects bracket notation)
+ * @param encode Whether to URL-encode values
+ * @return The serialized deep object string
+ */
+ public static String serializeDeepObject(
+ String key,
+ Object value,
+ boolean topLevel,
+ boolean encode
+ ) {
if (!Util.isObject(value)) {
return String.format("[%s]=%s", key, serializeValue(value, encode));
}
diff --git a/src/main/java/com/alohi/signplus/http/serialization/Util.java b/src/main/java/com/alohi/signplus/http/serialization/Util.java
index da7d8b2..b29eff0 100644
--- a/src/main/java/com/alohi/signplus/http/serialization/Util.java
+++ b/src/main/java/com/alohi/signplus/http/serialization/Util.java
@@ -12,6 +12,10 @@
import lombok.SneakyThrows;
import org.openapitools.jackson.nullable.JsonNullableModule;
+/**
+ * Utility class for serialization helper methods.
+ * Provides functions for type checking, URL encoding, and property extraction.
+ */
public class Util {
private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
@@ -21,8 +25,18 @@ public class Util {
OBJECT_MAPPER.registerModule(new JsonNullableModule());
}
+ /**
+ * Extracts properties from an object using JSON property names.
+ * Uses Jackson annotations to determine the correct property names.
+ *
+ * @param value The object to extract properties from
+ * @return A map of JSON property names to their values
+ */
public static Map getPropertiesWithJsonPropertyNames(Object value) {
- Map properties = OBJECT_MAPPER.convertValue(value, new TypeReference