diff --git a/packages/type-safe-api/scripts/type-safe-api/generators/generate-next.ts b/packages/type-safe-api/scripts/type-safe-api/generators/generate-next.ts index 8dcd80bdb..924f0bfd7 100755 --- a/packages/type-safe-api/scripts/type-safe-api/generators/generate-next.ts +++ b/packages/type-safe-api/scripts/type-safe-api/generators/generate-next.ts @@ -178,6 +178,41 @@ const toPythonName = (namedEntity: 'model' | 'property' | 'operation', name: str return nameSnakeCase; }; +// @see https://github.com/OpenAPITools/openapi-generator/blob/8f2676c5c2bcbcc41942307e5c8648cee38bcc44/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractJavaCodegen.java#L179 +const JAVA_KEYWORDS = new Set([ + // special words + "object", "list", "file", + // used as internal variables, can collide with parameter names + "localVarPath", "localVarQueryParams", "localVarCollectionQueryParams", + "localVarHeaderParams", "localVarCookieParams", "localVarFormParams", "localVarPostBody", + "localVarAccepts", "localVarAccept", "localVarContentTypes", + "localVarContentType", "localVarAuthNames", "localReturnType", + "ApiClient", "ApiException", "ApiResponse", "Configuration", "StringUtil", + + // language reserved words + "_", "abstract", "continue", "for", "new", "switch", "assert", + "default", "if", "package", "synchronized", "boolean", "do", "goto", "private", + "this", "break", "double", "implements", "protected", "throw", "byte", "else", + "import", "public", "throws", "case", "enum", "instanceof", "return", "transient", + "catch", "extends", "int", "short", "try", "char", "final", "interface", "static", + "void", "class", "finally", "long", "strictfp", "volatile", "const", "float", + "native", "super", "while", "null", "offsetdatetime", "localdate", "localtime" +]); + +const toJavaName = (name: string) => { + // Check if the name is a reserved word. Reserved words that overlap with TypeScript will already be escaped + // with a leading _ by parseOpenapi, so we remove this to test + const unescapedName = _camelCase(name.startsWith('_') ? name.slice(1) : name); + if (JAVA_KEYWORDS.has(unescapedName)) { + // Special case for "class" + if (unescapedName === "class") { + return "propertyClass"; + } + return `_${unescapedName}`; + } + return unescapedName; +}; + /** * Clean up any generated code that already exists */ @@ -321,25 +356,31 @@ const toTypeScriptType = (property: parseOpenapi.Model): string => { case "reference": return toTypescriptPrimitive(property); case "array": - return `Array<${property.link ? toTypeScriptType(property.link) : property.type}>`; + return `Array<${property.link && property.link.export !== "enum" ? toTypeScriptType(property.link) : property.type}>`; case "dictionary": - return `{ [key: string]: ${property.link ? toTypeScriptType(property.link) : property.type}; }`; + return `{ [key: string]: ${property.link && property.link.export !== "enum" ? toTypeScriptType(property.link) : property.type}; }`; default: return property.type; } }; const toJavaPrimitive = (property: parseOpenapi.Model): string => { - if (property.type === "string" && ["date", "date-time"].includes(property.format ?? '')) { - return "Date"; - } else if (property.type === "binary") { + if (property.type === "string" && property.format === "date") { + return "LocalDate"; + } else if (property.type === "string" && property.format === "date-time") { + return "OffsetDateTime"; + } else if (property.type === "string" && (property.format as any) === "uuid") { + return "UUID"; + } else if (property.type === "string" && (property.format as any) === "uri") { + return "URI"; + } else if (property.type === "binary" || (property.type === "string" && ["byte", "binary"].includes(property.format as any))) { return "byte[]"; } else if (property.type === "number") { switch(property.format) { case "int32": return "Integer"; case "int64": - return "BigInteger"; + return "Long"; case "float": return "Float"; case "double": @@ -356,6 +397,8 @@ const toJavaPrimitive = (property: parseOpenapi.Model): string => { return "Boolean"; } else if (property.type === "string") { return "String"; + } else if (property.type === "any") { + return "Object"; } return property.type; }; @@ -366,10 +409,14 @@ const toJavaType = (property: parseOpenapi.Model): string => { case "reference": return toJavaPrimitive(property); case "array": - return `${property.uniqueItems ? 'Set' : 'List'}<${property.link ? toTypeScriptType(property.link) : property.type}>`; + return `${property.uniqueItems ? 'Set' : 'List'}<${property.link && property.link.export !== "enum" ? toJavaType(property.link) : property.type}>`; case "dictionary": - return `Map`; + return `Map`; default: + // "any" has export = interface + if (PRIMITIVE_TYPES.has(property.type)) { + return toJavaPrimitive(property); + } return property.type; } }; @@ -411,9 +458,9 @@ const toPythonType = (property: parseOpenapi.Model): string => { case "reference": return toPythonPrimitive(property); case "array": - return `List[${property.link ? toPythonType(property.link) : property.type}]`; + return `List[${property.link && property.link.export !== "enum" ? toPythonType(property.link) : property.type}]`; case "dictionary": - return `Dict[str, ${property.link ? toPythonType(property.link) : property.type}]`; + return `Dict[str, ${property.link && property.link.export !== "enum" ? toPythonType(property.link) : property.type}]`; default: // "any" has export = interface if (PRIMITIVE_TYPES.has(property.type)) { @@ -432,7 +479,7 @@ const mutateModelWithAdditionalTypes = (model: parseOpenapi.Model) => { (model as any).typescriptName = model.name; (model as any).typescriptType = toTypeScriptType(model); - (model as any).javaName = model.name; + (model as any).javaName = toJavaName(model.name); (model as any).javaType = toJavaType(model); (model as any).pythonName = toPythonName('property', model.name); (model as any).pythonType = toPythonType(model); @@ -452,6 +499,7 @@ const mutateWithOpenapiSchemaProperties = (spec: OpenAPIV3.Document, model: pars (model as any).deprecated = !!schema.deprecated; (model as any).openapiType = schema.type; (model as any).isNotSchema = !!schema.not; + (model as any).isEnum = !!schema.enum && schema.enum.length > 0; // Copy any schema vendor extensions (model as any).vendorExtensions = {}; @@ -506,6 +554,8 @@ const mutateWithOpenapiSchemaProperties = (spec: OpenAPIV3.Document, model: pars const ensureModelLinks = (spec: OpenAPIV3.Document, data: parseOpenapi.ParsedSpecification) => { const modelsByName = Object.fromEntries(data.models.map((m) => [m.name, m])); const visited = new Set(); + + // Ensure set for all models data.models.forEach((model) => { const schema = resolveIfRef(spec, spec?.components?.schemas?.[model.name]); if (schema) { @@ -516,6 +566,27 @@ const ensureModelLinks = (spec: OpenAPIV3.Document, data: parseOpenapi.ParsedSpe _ensureModelLinks(spec, modelsByName, model, schema, visited) } }); + + // Ensure set for all parameters + data.services.forEach((service) => { + service.operations.forEach((op) => { + const specOp = (spec as any)?.paths?.[op.path]?.[op.method.toLowerCase()] as OpenAPIV3.OperationObject | undefined; + + const specParametersByName = Object.fromEntries((specOp?.parameters ?? []).map((p) => { + const param = resolveIfRef(spec, p); + return [param.name, param]; + })); + + op.parameters.forEach((parameter) => { + const specParameter = specParametersByName[parameter.prop]; + const specParameterSchema = resolveIfRef(spec, specParameter?.schema); + + if (specParameterSchema) { + _ensureModelLinks(spec, modelsByName, parameter, specParameterSchema, visited); + } + }); + }); + }); }; const _ensureModelLinks = (spec: OpenAPIV3.Document, modelsByName: {[name: string]: parseOpenapi.Model}, model: parseOpenapi.Model, schema: OpenAPIV3.SchemaObject, visited: Set) => { diff --git a/packages/type-safe-api/scripts/type-safe-api/generators/java/templates/client/apis/apis.ejs b/packages/type-safe-api/scripts/type-safe-api/generators/java/templates/client/apis/apis.ejs new file mode 100644 index 000000000..0ddd2edf3 --- /dev/null +++ b/packages/type-safe-api/scripts/type-safe-api/generators/java/templates/client/apis/apis.ejs @@ -0,0 +1,343 @@ +<%_ services.forEach((service) => { _%> +###TSAPI_WRITE_FILE### +{ + "dir": "<%- metadata.srcDir %>/api", + "name": "<%- service.className %>", + "ext": ".java", + "overwrite": true +} +###/TSAPI_WRITE_FILE###<%- include('../../header.partial.ejs', { info }) %> + + +package <%- metadata.packageName %>.api; + +import <%- metadata.packageName %>.ApiCallback; +import <%- metadata.packageName %>.ApiClient; +import <%- metadata.packageName %>.ApiException; +import <%- metadata.packageName %>.ApiResponse; +import <%- metadata.packageName %>.Configuration; +import <%- metadata.packageName %>.Pair; +import <%- metadata.packageName %>.ProgressRequestBody; +import <%- metadata.packageName %>.ProgressResponseBody; + +import com.google.gson.reflect.TypeToken; + +import java.io.IOException; + + +import java.math.BigDecimal; +import java.io.File; +<%_ service.modelImports.forEach((modelImport) => { _%> +import <%- metadata.packageName %>.model.<%- modelImport %>; +<%_ }); _%> + +import java.lang.reflect.Type; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import javax.ws.rs.core.GenericType; + +public class <%- service.className %> { + private ApiClient localVarApiClient; + private int localHostIndex; + private String localCustomBaseUrl; + + public <%- service.className %>() { + this(Configuration.getDefaultApiClient()); + } + + public <%- service.className %>(ApiClient apiClient) { + this.localVarApiClient = apiClient; + } + + public ApiClient getApiClient() { + return localVarApiClient; + } + + public void setApiClient(ApiClient apiClient) { + this.localVarApiClient = apiClient; + } + + public int getHostIndex() { + return localHostIndex; + } + + public void setHostIndex(int hostIndex) { + this.localHostIndex = hostIndex; + } + + public String getCustomBaseUrl() { + return localCustomBaseUrl; + } + + public void setCustomBaseUrl(String customBaseUrl) { + this.localCustomBaseUrl = customBaseUrl; + } + + <%_ service.operations.forEach((operation) => { _%> + <%_ const result = operation.results[0]; _%> + <%_ const requiredParameters = operation.parameters.filter(p => p.isRequired); _%> + <%_ const optionalParameters = operation.parameters.filter(p => !p.isRequired); _%> + <%_ + // In API methods, the File type is used for binary types + const javaType = (parameter) => parameter.javaType === "byte[]" ? "File" : parameter.javaType; + _%> + private okhttp3.Call <%- operation.name %>Call(<% operation.parameters.forEach((parameter) => { _%><%- javaType(parameter) %> <%- parameter.javaName %>, <% }); %>final ApiCallback _callback) throws ApiException { + String basePath = null; + // Operation Servers + String[] localBasePaths = new String[] { }; + + // Determine Base Path to Use + if (localCustomBaseUrl != null){ + basePath = localCustomBaseUrl; + } else if ( localBasePaths.length > 0 ) { + basePath = localBasePaths[localHostIndex]; + } else { + basePath = null; + } + + Object localVarPostBody = <% if(operation.parametersBody) { %><%- operation.parametersBody.javaName %><% } else { %>null<% } %>; + + // create path and map variables + String localVarPath = "<%- operation.path %>"<% operation.parameters.filter(p => p.in === "path").forEach((parameter) => { %> + .replace("{" + "<%- parameter.prop %>" + "}", localVarApiClient.escapeString(<% if (parameter.export === "array" && parameter.collectionFormat) { %>localVarApiClient.collectionPathParameterToString("<%- parameter.collectionFormat %>", <%- parameter.javaName %>)<% } else { %><%- parameter.javaName %>.toString()<% } %>))<% }); %>; + + List localVarQueryParams = new ArrayList(); + List localVarCollectionQueryParams = new ArrayList(); + Map localVarHeaderParams = new HashMap(); + Map localVarCookieParams = new HashMap(); + Map localVarFormParams = new HashMap(); + + <%_ operation.parameters.filter(p => p.in === "query").forEach((parameter) => { _%> + if (<%- parameter.javaName %> != null) { + <% if (parameter.export === "array" && parameter.collectionFormat) { %>localVarCollectionQueryParams.addAll(localVarApiClient.parameterToPairs("<%- parameter.collectionFormat %>", <% } else { %>localVarQueryParams.addAll(localVarApiClient.parameterToPair(<% } %>"<%- parameter.prop %>", <%- parameter.javaName %>)); + } + + <%_ }); _%> + <%_ operation.parameters.filter(p => p.in === "header").forEach((parameter) => { _%> + if (<%- parameter.javaName %> != null) { + localVarHeaderParams.put("<%- parameter.prop %>", localVarApiClient.parameterToString(<%- parameter.javaName %>)); + } + + <%_ }); _%> + <%_ operation.parameters.filter(p => p.in === "cookie").forEach((parameter) => { _%> + if (<%- parameter.javaName %> != null) { + localVarCookieParams.put("<%- parameter.prop %>", localVarApiClient.parameterToString(<%- parameter.javaName %>)); + } + + <%_ }); _%> + final String[] localVarAccepts = { + <%_ ((result || {}).mediaTypes || []).forEach((mediaType, i) => { _%> + "<%- mediaType %>"<% if (i < result.mediaTypes.length - 1) { %>,<% } %> + <%_ }); _%> + }; + final String localVarAccept = localVarApiClient.selectHeaderAccept(localVarAccepts); + if (localVarAccept != null) { + localVarHeaderParams.put("Accept", localVarAccept); + } + + final String[] localVarContentTypes = { + <%_ ((operation.parametersBody || {}).mediaTypes || []).forEach((mediaType, i) => { _%> + "<%- mediaType %>"<% if (i < operation.parametersBody.mediaTypes.length - 1) { %>,<% } %> + <%_ }); _%> + }; + final String localVarContentType = localVarApiClient.selectHeaderContentType(localVarContentTypes); + if (localVarContentType != null) { + localVarHeaderParams.put("Content-Type", localVarContentType); + } + + String[] localVarAuthNames = new String[] { }; + return localVarApiClient.buildCall(basePath, localVarPath, "<%- operation.method %>", localVarQueryParams, localVarCollectionQueryParams, localVarPostBody, localVarHeaderParams, localVarCookieParams, localVarFormParams, localVarAuthNames, _callback); + } + + <% if (operation.isDeprecated) { %>@Deprecated<% } %> + @SuppressWarnings("rawtypes") + private okhttp3.Call <%- operation.name %>ValidateBeforeCall(<% operation.parameters.forEach((parameter) => { _%><%- javaType(parameter) %> <%- parameter.javaName %>, <% }); %>final ApiCallback _callback) throws ApiException { + <%_ requiredParameters.forEach((parameter) => { _%> + // verify the required parameter '<%- parameter.javaName %>' is set + if (<%- parameter.javaName %> == null) { + throw new ApiException("Missing the required parameter '<%- parameter.javaName %>' when calling <%- operation.name %>(Async)"); + } + + <%_ }); _%> + return <%- operation.name %>Call(<% operation.parameters.forEach((parameter) => { _%><%- parameter.javaName %>, <% }); %>_callback); + + } + + <%_ const returnType = result && result.type !== 'void' ? result.javaType : 'Void'; _%> + private ApiResponse<<%- returnType %>> <%- operation.name %>WithHttpInfo(<% operation.parameters.forEach((parameter, i) => { _%><%- javaType(parameter) %> <%- parameter.javaName %><% if (i < operation.parameters.length - 1) { %>, <% } %><% }); %>) throws ApiException { + okhttp3.Call localVarCall = <%- operation.name %>ValidateBeforeCall(<% operation.parameters.forEach((parameter) => { _%><%- parameter.javaName %>, <% }); %>null); + <%_ if (returnType === 'Void') { _%> + return localVarApiClient.execute(localVarCall); + <%_ } else { _%> + Type localVarReturnType = new TypeToken<<%- returnType %>>(){}.getType(); + return localVarApiClient.execute(localVarCall, localVarReturnType); + <%_ } _%> + } + + + private okhttp3.Call <%- operation.name %>Async(<% operation.parameters.forEach((parameter) => { _%><%- javaType(parameter) %> <%- parameter.javaName %>, <% }); %>final ApiCallback<<%- returnType %>> _callback) throws ApiException { + + okhttp3.Call localVarCall = <%- operation.name %>ValidateBeforeCall(<% operation.parameters.forEach((parameter) => { _%><%- parameter.javaName %>, <% }); %>_callback); + <%_ if (returnType !== 'Void') { _%> + Type localVarReturnType = new TypeToken<<%- returnType %>>(){}.getType(); + localVarApiClient.executeAsync(localVarCall, localVarReturnType, _callback); + <%_ } else { _%> + localVarApiClient.executeAsync(localVarCall, _callback); + <%_ } _%> + return localVarCall; + } + + public class API<%- operation.name %>Request { + <%_ requiredParameters.forEach((parameter) => { _%> + private final <%- javaType(parameter) %> <%- parameter.javaName %>; + <%_ }); _%> + <%_ optionalParameters.forEach((parameter) => { _%> + private <%- javaType(parameter) %> <%- parameter.javaName %>; + <%_ }); _%> + + private API<%- operation.name %>Request(<% requiredParameters.forEach((parameter, i) => { %><%- javaType(parameter) %> <%- parameter.javaName %><% if (i < requiredParameters.length - 1) { %>, <% } %><% }); %>) { + <%_ requiredParameters.forEach((parameter) => { _%> + this.<%- parameter.javaName %> = <%- parameter.javaName %>; + <%_ }); _%> + } + + <%_ optionalParameters.forEach((parameter) => { _%> + /** + * Set <%- parameter.javaName %> + * @param <%- parameter.javaName %> <%- parameter.description || '' %> (optional) + * @return API<%- operation.name %>Request + */ + public API<%- operation.name %>Request <%- parameter.javaName %>(<%- javaType(parameter) %> <%- parameter.javaName %>) { + this.<%- parameter.javaName %> = <%- parameter.javaName %>; + return this; + } + + <%_ }); _%> + /** + * Build call for <%- operation.name %> + * @param _callback ApiCallback API callback + * @return Call to execute + * @throws ApiException If fail to serialize the request body object + <%_ if (operation.responses.length > 0) { _%> + * @http.response.details + + + <%_ operation.responses.forEach((response) => { _%> + + <%_ }); _%> +
Status Code Description Response Headers
<%- response.code %> <%- response.description || '' %> -
+ <%_ } _%> + <%_ if (operation.isDeprecated) { _%> + * @deprecated + <%_ } _%> + */ + <%_ if (operation.isDeprecated) { _%> + @Deprecated + <%_ } _%> + public okhttp3.Call buildCall(final ApiCallback _callback) throws ApiException { + return <%- operation.name %>Call(<% operation.parameters.forEach((parameter) => { %><%- parameter.javaName %>, <% }); %>_callback); + } + + /** + * Execute <%- operation.name %> request<% if (returnType !== 'Void') { %> + * @return <%- returnType %><% } %> + * @throws ApiException If fail to call the API, e.g. server error or cannot deserialize the response body + <%_ if (operation.responses.length > 0) { _%> + * @http.response.details + + + <%_ operation.responses.forEach((response) => { _%> + + <%_ }); _%> +
Status Code Description Response Headers
<%- response.code %> <%- response.description || '' %> -
+ <%_ } _%> + <%_ if (operation.isDeprecated) { _%> + * @deprecated + <%_ } _%> + */ + <%_ if (operation.isDeprecated) { _%> + @Deprecated + <%_ } _%> + public <%- returnType === 'Void' ? 'void' : returnType %> execute() throws ApiException { + <% if (returnType !== 'Void') { %>ApiResponse<<%- returnType %>> localVarResp = <% } %><%- operation.name %>WithHttpInfo(<% operation.parameters.forEach((parameter, i) => { %><%- parameter.javaName %><% if (i < operation.parameters.length - 1) { %>, <% } %><% }); %>);<% if (returnType !== 'Void') { %> + return localVarResp.getData();<% } %> + } + + /** + * Execute <%- operation.name %> request with HTTP info returned + * @return ApiResponse<<%- returnType %>> + * @throws ApiException If fail to call the API, e.g. server error or cannot deserialize the response body + <%_ if (operation.responses.length > 0) { _%> + * @http.response.details + + + <%_ operation.responses.forEach((response) => { _%> + + <%_ }); _%> +
Status Code Description Response Headers
<%- response.code %> <%- response.description || '' %> -
+ <%_ } _%> + <%_ if (operation.isDeprecated) { _%> + * @deprecated + <%_ } _%> + */ + <%_ if (operation.isDeprecated) { _%> + @Deprecated + <%_ } _%> + public ApiResponse<<%- returnType %>> executeWithHttpInfo() throws ApiException { + return <%- operation.name %>WithHttpInfo(<% operation.parameters.forEach((parameter, i) => { %><%- parameter.javaName %><% if (i < operation.parameters.length - 1) { %>, <% } %><% }); %>); + } + + /** + * Execute <%- operation.name %> request (asynchronously) + * @param _callback The callback to be executed when the API call finishes + * @return The request call + * @throws ApiException If fail to process the API call, e.g. serializing the request body object + <%_ if (operation.responses.length > 0) { _%> + * @http.response.details + + + <%_ operation.responses.forEach((response) => { _%> + + <%_ }); _%> +
Status Code Description Response Headers
<%- response.code %> <%- response.description || '' %> -
+ <%_ } _%> + <%_ if (operation.isDeprecated) { _%> + * @deprecated + <%_ } _%> + */ + <%_ if (operation.isDeprecated) { _%> + @Deprecated + <%_ } _%> + public okhttp3.Call executeAsync(final ApiCallback<<%- returnType %>> _callback) throws ApiException { + return <%- operation.name %>Async(<% operation.parameters.forEach((parameter) => { _%><%- parameter.javaName %>, <% }); %>_callback); + } + } + + /** + * <%- operation.summary || '' %> + * <%- operation.description || '' %><% requiredParameters.forEach((parameter) => { %> + * @param <%- parameter.javaName %> <%- parameter.description || '' %> (required)<% }); %> + * @return API<%- operation.name %>Request + <%_ if (operation.responses.length > 0) { _%> + * @http.response.details + + + <%_ operation.responses.forEach((response) => { _%> + + <%_ }); _%> +
Status Code Description Response Headers
<%- response.code %> <%- response.description || '' %> -
+ <%_ } _%> + <%_ if (operation.isDeprecated) { _%> + * @deprecated + <%_ } _%> + */ + <% if (operation.isDeprecated) { %>@Deprecated<% } %> + public API<%- operation.name %>Request <%- operation.name %>(<% requiredParameters.forEach((parameter, i) => { %><%- javaType(parameter) %> <%- parameter.javaName %><% if (i < requiredParameters.length - 1) { %>, <% } %><% }); %>) { + return new API<%- operation.name %>Request(<% requiredParameters.forEach((parameter, i) => { %><%- parameter.javaName %><% if (i < requiredParameters.length - 1) { %>, <% } %><% }); %>); + } + <%_ }); _%> +} + +<%_ }); _%> \ No newline at end of file diff --git a/packages/type-safe-api/scripts/type-safe-api/generators/java/templates/client/auth/apiKeyAuth.ejs b/packages/type-safe-api/scripts/type-safe-api/generators/java/templates/client/auth/apiKeyAuth.ejs new file mode 100644 index 000000000..59dc5617c --- /dev/null +++ b/packages/type-safe-api/scripts/type-safe-api/generators/java/templates/client/auth/apiKeyAuth.ejs @@ -0,0 +1,76 @@ +###TSAPI_WRITE_FILE### +{ + "dir": "<%- metadata.srcDir %>/auth", + "name": "ApiKeyAuth", + "ext": ".java", + "overwrite": true +} +###/TSAPI_WRITE_FILE###<%- include('../../header.partial.ejs', { info }) %> + + +package <%- metadata.packageName %>.auth; + +import <%- metadata.packageName %>.ApiException; +import <%- metadata.packageName %>.Pair; + +import java.net.URI; +import java.util.Map; +import java.util.List; + +public class ApiKeyAuth implements Authentication { + private final String location; + private final String paramName; + + private String apiKey; + private String apiKeyPrefix; + + public ApiKeyAuth(String location, String paramName) { + this.location = location; + this.paramName = paramName; + } + + public String getLocation() { + return location; + } + + public String getParamName() { + return paramName; + } + + public String getApiKey() { + return apiKey; + } + + public void setApiKey(String apiKey) { + this.apiKey = apiKey; + } + + public String getApiKeyPrefix() { + return apiKeyPrefix; + } + + public void setApiKeyPrefix(String apiKeyPrefix) { + this.apiKeyPrefix = apiKeyPrefix; + } + + @Override + public void applyToParams(List queryParams, Map headerParams, Map cookieParams, + String payload, String method, URI uri) throws ApiException { + if (apiKey == null) { + return; + } + String value; + if (apiKeyPrefix != null) { + value = apiKeyPrefix + " " + apiKey; + } else { + value = apiKey; + } + if ("query".equals(location)) { + queryParams.add(new Pair(paramName, value)); + } else if ("header".equals(location)) { + headerParams.put(paramName, value); + } else if ("cookie".equals(location)) { + cookieParams.put(paramName, value); + } + } +} diff --git a/packages/type-safe-api/scripts/type-safe-api/generators/java/templates/client/auth/authentication.ejs b/packages/type-safe-api/scripts/type-safe-api/generators/java/templates/client/auth/authentication.ejs new file mode 100644 index 000000000..1e7154d43 --- /dev/null +++ b/packages/type-safe-api/scripts/type-safe-api/generators/java/templates/client/auth/authentication.ejs @@ -0,0 +1,33 @@ +###TSAPI_WRITE_FILE### +{ + "dir": "<%- metadata.srcDir %>/auth", + "name": "Authentication", + "ext": ".java", + "overwrite": true +} +###/TSAPI_WRITE_FILE###<%- include('../../header.partial.ejs', { info }) %> + + +package <%- metadata.packageName %>.auth; + +import <%- metadata.packageName %>.Pair; +import <%- metadata.packageName %>.ApiException; + +import java.net.URI; +import java.util.Map; +import java.util.List; + +public interface Authentication { + /** + * Apply authentication settings to header and query params. + * + * @param queryParams List of query parameters + * @param headerParams Map of header parameters + * @param cookieParams Map of cookie parameters + * @param payload HTTP request body + * @param method HTTP method + * @param uri URI + * @throws ApiException if failed to update the parameters + */ + void applyToParams(List queryParams, Map headerParams, Map cookieParams, String payload, String method, URI uri) throws ApiException; +} diff --git a/packages/type-safe-api/scripts/type-safe-api/generators/java/templates/client/auth/httpBasicAuth.ejs b/packages/type-safe-api/scripts/type-safe-api/generators/java/templates/client/auth/httpBasicAuth.ejs new file mode 100644 index 000000000..15e5a6ce9 --- /dev/null +++ b/packages/type-safe-api/scripts/type-safe-api/generators/java/templates/client/auth/httpBasicAuth.ejs @@ -0,0 +1,54 @@ +###TSAPI_WRITE_FILE### +{ + "dir": "<%- metadata.srcDir %>/auth", + "name": "HttpBasicAuth", + "ext": ".java", + "overwrite": true +} +###/TSAPI_WRITE_FILE###<%- include('../../header.partial.ejs', { info }) %> + + +package <%- metadata.packageName %>.auth; + +import <%- metadata.packageName %>.Pair; +import <%- metadata.packageName %>.ApiException; + +import okhttp3.Credentials; + +import java.net.URI; +import java.util.Map; +import java.util.List; + +import java.io.UnsupportedEncodingException; + +public class HttpBasicAuth implements Authentication { + private String username; + private String password; + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + @Override + public void applyToParams(List queryParams, Map headerParams, Map cookieParams, + String payload, String method, URI uri) throws ApiException { + if (username == null && password == null) { + return; + } + headerParams.put("Authorization", Credentials.basic( + username == null ? "" : username, + password == null ? "" : password)); + } +} diff --git a/packages/type-safe-api/scripts/type-safe-api/generators/java/templates/client/auth/httpBearerAuth.ejs b/packages/type-safe-api/scripts/type-safe-api/generators/java/templates/client/auth/httpBearerAuth.ejs new file mode 100644 index 000000000..8996a3295 --- /dev/null +++ b/packages/type-safe-api/scripts/type-safe-api/generators/java/templates/client/auth/httpBearerAuth.ejs @@ -0,0 +1,59 @@ +###TSAPI_WRITE_FILE### +{ + "dir": "<%- metadata.srcDir %>/auth", + "name": "HttpBearerAuth", + "ext": ".java", + "overwrite": true +} +###/TSAPI_WRITE_FILE###<%- include('../../header.partial.ejs', { info }) %> + + +package <%- metadata.packageName %>.auth; + +import <%- metadata.packageName %>.ApiException; +import <%- metadata.packageName %>.Pair; + +import java.net.URI; +import java.util.Map; +import java.util.List; + +public class HttpBearerAuth implements Authentication { + private final String scheme; + private String bearerToken; + + public HttpBearerAuth(String scheme) { + this.scheme = scheme; + } + + /** + * Gets the token, which together with the scheme, will be sent as the value of the Authorization header. + * + * @return The bearer token + */ + public String getBearerToken() { + return bearerToken; + } + + /** + * Sets the token, which together with the scheme, will be sent as the value of the Authorization header. + * + * @param bearerToken The bearer token to send in the Authorization header + */ + public void setBearerToken(String bearerToken) { + this.bearerToken = bearerToken; + } + + @Override + public void applyToParams(List queryParams, Map headerParams, Map cookieParams, + String payload, String method, URI uri) throws ApiException { + if (bearerToken == null) { + return; + } + + headerParams.put("Authorization", (scheme != null ? upperCaseBearer(scheme) + " " : "") + bearerToken); + } + + private static String upperCaseBearer(String scheme) { + return ("bearer".equalsIgnoreCase(scheme)) ? "Bearer" : scheme; + } +} diff --git a/packages/type-safe-api/scripts/type-safe-api/generators/java/templates/client/generic/apiCallback.ejs b/packages/type-safe-api/scripts/type-safe-api/generators/java/templates/client/generic/apiCallback.ejs new file mode 100644 index 000000000..6f3c12cbb --- /dev/null +++ b/packages/type-safe-api/scripts/type-safe-api/generators/java/templates/client/generic/apiCallback.ejs @@ -0,0 +1,59 @@ +###TSAPI_WRITE_FILE### +{ + "dir": "<%- metadata.srcDir %>", + "name": "ApiCallback", + "ext": ".java", + "overwrite": true +} +###/TSAPI_WRITE_FILE###<%- include('../../header.partial.ejs', { info }) %> + + +package <%- metadata.packageName %>; + +import java.io.IOException; + +import java.util.Map; +import java.util.List; + +/** + * Callback for asynchronous API call. + * + * @param The return type + */ +public interface ApiCallback { + /** + * This is called when the API call fails. + * + * @param e The exception causing the failure + * @param statusCode Status code of the response if available, otherwise it would be 0 + * @param responseHeaders Headers of the response if available, otherwise it would be null + */ + void onFailure(ApiException e, int statusCode, Map> responseHeaders); + + /** + * This is called when the API call succeeded. + * + * @param result The result deserialized from response + * @param statusCode Status code of the response + * @param responseHeaders Headers of the response + */ + void onSuccess(T result, int statusCode, Map> responseHeaders); + + /** + * This is called when the API upload processing. + * + * @param bytesWritten bytes Written + * @param contentLength content length of request body + * @param done write end + */ + void onUploadProgress(long bytesWritten, long contentLength, boolean done); + + /** + * This is called when the API download processing. + * + * @param bytesRead bytes Read + * @param contentLength content length of the response + * @param done Read end + */ + void onDownloadProgress(long bytesRead, long contentLength, boolean done); +} diff --git a/packages/type-safe-api/scripts/type-safe-api/generators/java/templates/client/generic/apiClient.ejs b/packages/type-safe-api/scripts/type-safe-api/generators/java/templates/client/generic/apiClient.ejs new file mode 100644 index 000000000..b72fc2f6f --- /dev/null +++ b/packages/type-safe-api/scripts/type-safe-api/generators/java/templates/client/generic/apiClient.ejs @@ -0,0 +1,1543 @@ +###TSAPI_WRITE_FILE### +{ + "dir": "<%- metadata.srcDir %>", + "name": "ApiClient", + "ext": ".java", + "overwrite": true +} +###/TSAPI_WRITE_FILE###<%- include('../../header.partial.ejs', { info }) %> + + +package <%- metadata.packageName %>; + +import okhttp3.*; +import okhttp3.internal.http.HttpMethod; +import okhttp3.internal.tls.OkHostnameVerifier; +import okhttp3.logging.HttpLoggingInterceptor; +import okhttp3.logging.HttpLoggingInterceptor.Level; +import okio.Buffer; +import okio.BufferedSink; +import okio.Okio; + +import javax.net.ssl.*; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.UnsupportedEncodingException; +import java.lang.reflect.Type; +import java.net.URI; +import java.net.URLConnection; +import java.net.URLEncoder; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.security.GeneralSecurityException; +import java.security.KeyStore; +import java.security.SecureRandom; +import java.security.cert.Certificate; +import java.security.cert.CertificateException; +import java.security.cert.CertificateFactory; +import java.security.cert.X509Certificate; +import java.text.DateFormat; +import java.time.LocalDate; +import java.time.OffsetDateTime; +import java.time.format.DateTimeFormatter; +import java.util.*; +import java.util.Map.Entry; +import java.util.concurrent.TimeUnit; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import <%- metadata.packageName %>.auth.Authentication; +import <%- metadata.packageName %>.auth.HttpBasicAuth; +import <%- metadata.packageName %>.auth.HttpBearerAuth; +import <%- metadata.packageName %>.auth.ApiKeyAuth; + +/** + *

ApiClient class.

+ */ +public class ApiClient { + + private String basePath = "http://localhost"; + protected List servers = new ArrayList(Arrays.asList( + new ServerConfiguration( + "", + "No description provided", + new HashMap() + ) + )); + protected Integer serverIndex = 0; + protected Map serverVariables = null; + private boolean debugging = false; + private Map defaultHeaderMap = new HashMap(); + private Map defaultCookieMap = new HashMap(); + private String tempFolderPath = null; + + private Map authentications; + + private DateFormat dateFormat; + private DateFormat datetimeFormat; + private boolean lenientDatetimeFormat; + private int dateLength; + + private InputStream sslCaCert; + private boolean verifyingSsl; + private KeyManager[] keyManagers; + + private OkHttpClient httpClient; + private JSON json; + + private HttpLoggingInterceptor loggingInterceptor; + + /** + * Basic constructor for ApiClient + */ + public ApiClient() { + init(); + initHttpClient(); + + // Setup authentications (key: authentication name, value: authentication). + // Prevent the authentications from being modified. + authentications = Collections.unmodifiableMap(authentications); + } + + /** + * Basic constructor with custom OkHttpClient + * + * @param client a {@link okhttp3.OkHttpClient} object + */ + public ApiClient(OkHttpClient client) { + init(); + + httpClient = client; + + // Setup authentications (key: authentication name, value: authentication). + // Prevent the authentications from being modified. + authentications = Collections.unmodifiableMap(authentications); + } + + private void initHttpClient() { + initHttpClient(Collections.emptyList()); + } + + private void initHttpClient(List interceptors) { + OkHttpClient.Builder builder = new OkHttpClient.Builder(); + builder.addNetworkInterceptor(getProgressInterceptor()); + for (Interceptor interceptor: interceptors) { + builder.addInterceptor(interceptor); + } + + httpClient = builder.build(); + } + + private void init() { + verifyingSsl = true; + + json = new JSON(); + + // Set default User-Agent. + setUserAgent("OpenAPI-Generator/0.0.0/java"); + + authentications = new HashMap(); + } + + /** + * Get base path + * + * @return Base path + */ + public String getBasePath() { + return basePath; + } + + /** + * Set base path + * + * @param basePath Base path of the URL (e.g http://localhost + * @return An instance of OkHttpClient + */ + public ApiClient setBasePath(String basePath) { + this.basePath = basePath; + return this; + } + + public List getServers() { + return servers; + } + + public ApiClient setServers(List servers) { + this.servers = servers; + return this; + } + + public Integer getServerIndex() { + return serverIndex; + } + + public ApiClient setServerIndex(Integer serverIndex) { + this.serverIndex = serverIndex; + return this; + } + + public Map getServerVariables() { + return serverVariables; + } + + public ApiClient setServerVariables(Map serverVariables) { + this.serverVariables = serverVariables; + return this; + } + + /** + * Get HTTP client + * + * @return An instance of OkHttpClient + */ + public OkHttpClient getHttpClient() { + return httpClient; + } + + /** + * Set HTTP client, which must never be null. + * + * @param newHttpClient An instance of OkHttpClient + * @return Api Client + * @throws java.lang.NullPointerException when newHttpClient is null + */ + public ApiClient setHttpClient(OkHttpClient newHttpClient) { + this.httpClient = Objects.requireNonNull(newHttpClient, "HttpClient must not be null!"); + return this; + } + + /** + * Get JSON + * + * @return JSON object + */ + public JSON getJSON() { + return json; + } + + /** + * Set JSON + * + * @param json JSON object + * @return Api client + */ + public ApiClient setJSON(JSON json) { + this.json = json; + return this; + } + + /** + * True if isVerifyingSsl flag is on + * + * @return True if isVerifySsl flag is on + */ + public boolean isVerifyingSsl() { + return verifyingSsl; + } + + /** + * Configure whether to verify certificate and hostname when making https requests. + * Default to true. + * NOTE: Do NOT set to false in production code, otherwise you would face multiple types of cryptographic attacks. + * + * @param verifyingSsl True to verify TLS/SSL connection + * @return ApiClient + */ + public ApiClient setVerifyingSsl(boolean verifyingSsl) { + this.verifyingSsl = verifyingSsl; + applySslSettings(); + return this; + } + + /** + * Get SSL CA cert. + * + * @return Input stream to the SSL CA cert + */ + public InputStream getSslCaCert() { + return sslCaCert; + } + + /** + * Configure the CA certificate to be trusted when making https requests. + * Use null to reset to default. + * + * @param sslCaCert input stream for SSL CA cert + * @return ApiClient + */ + public ApiClient setSslCaCert(InputStream sslCaCert) { + this.sslCaCert = sslCaCert; + applySslSettings(); + return this; + } + + /** + *

Getter for the field keyManagers.

+ * + * @return an array of {@link javax.net.ssl.KeyManager} objects + */ + public KeyManager[] getKeyManagers() { + return keyManagers; + } + + /** + * Configure client keys to use for authorization in an SSL session. + * Use null to reset to default. + * + * @param managers The KeyManagers to use + * @return ApiClient + */ + public ApiClient setKeyManagers(KeyManager[] managers) { + this.keyManagers = managers; + applySslSettings(); + return this; + } + + /** + *

Getter for the field dateFormat.

+ * + * @return a {@link java.text.DateFormat} object + */ + public DateFormat getDateFormat() { + return dateFormat; + } + + /** + *

Setter for the field dateFormat.

+ * + * @param dateFormat a {@link java.text.DateFormat} object + * @return a {@link <%- metadata.packageName %>.ApiClient} object + */ + public ApiClient setDateFormat(DateFormat dateFormat) { + JSON.setDateFormat(dateFormat); + return this; + } + + /** + *

Set SqlDateFormat.

+ * + * @param dateFormat a {@link java.text.DateFormat} object + * @return a {@link <%- metadata.packageName %>.ApiClient} object + */ + public ApiClient setSqlDateFormat(DateFormat dateFormat) { + JSON.setSqlDateFormat(dateFormat); + return this; + } + + /** + *

Set OffsetDateTimeFormat.

+ * + * @param dateFormat a {@link java.time.format.DateTimeFormatter} object + * @return a {@link <%- metadata.packageName %>.ApiClient} object + */ + public ApiClient setOffsetDateTimeFormat(DateTimeFormatter dateFormat) { + JSON.setOffsetDateTimeFormat(dateFormat); + return this; + } + + /** + *

Set LocalDateFormat.

+ * + * @param dateFormat a {@link java.time.format.DateTimeFormatter} object + * @return a {@link <%- metadata.packageName %>.ApiClient} object + */ + public ApiClient setLocalDateFormat(DateTimeFormatter dateFormat) { + JSON.setLocalDateFormat(dateFormat); + return this; + } + + /** + *

Set LenientOnJson.

+ * + * @param lenientOnJson a boolean + * @return a {@link <%- metadata.packageName %>.ApiClient} object + */ + public ApiClient setLenientOnJson(boolean lenientOnJson) { + JSON.setLenientOnJson(lenientOnJson); + return this; + } + + /** + * Get authentications (key: authentication name, value: authentication). + * + * @return Map of authentication objects + */ + public Map getAuthentications() { + return authentications; + } + + /** + * Get authentication for the given name. + * + * @param authName The authentication name + * @return The authentication, null if not found + */ + public Authentication getAuthentication(String authName) { + return authentications.get(authName); + } + + + /** + * Helper method to set username for the first HTTP basic authentication. + * + * @param username Username + */ + public void setUsername(String username) { + for (Authentication auth : authentications.values()) { + if (auth instanceof HttpBasicAuth) { + ((HttpBasicAuth) auth).setUsername(username); + return; + } + } + throw new RuntimeException("No HTTP basic authentication configured!"); + } + + /** + * Helper method to set password for the first HTTP basic authentication. + * + * @param password Password + */ + public void setPassword(String password) { + for (Authentication auth : authentications.values()) { + if (auth instanceof HttpBasicAuth) { + ((HttpBasicAuth) auth).setPassword(password); + return; + } + } + throw new RuntimeException("No HTTP basic authentication configured!"); + } + + /** + * Helper method to set API key value for the first API key authentication. + * + * @param apiKey API key + */ + public void setApiKey(String apiKey) { + for (Authentication auth : authentications.values()) { + if (auth instanceof ApiKeyAuth) { + ((ApiKeyAuth) auth).setApiKey(apiKey); + return; + } + } + throw new RuntimeException("No API key authentication configured!"); + } + + /** + * Helper method to set API key prefix for the first API key authentication. + * + * @param apiKeyPrefix API key prefix + */ + public void setApiKeyPrefix(String apiKeyPrefix) { + for (Authentication auth : authentications.values()) { + if (auth instanceof ApiKeyAuth) { + ((ApiKeyAuth) auth).setApiKeyPrefix(apiKeyPrefix); + return; + } + } + throw new RuntimeException("No API key authentication configured!"); + } + + /** + * Helper method to set access token for the first OAuth2 authentication. + * + * @param accessToken Access token + */ + public void setAccessToken(String accessToken) { + throw new RuntimeException("No OAuth2 authentication configured!"); + } + + /** + * Helper method to set credentials for AWSV4 Signature + * + * @param accessKey Access Key + * @param secretKey Secret Key + * @param region Region + * @param service Service to access to + */ + public void setAWS4Configuration(String accessKey, String secretKey, String region, String service) { + throw new RuntimeException("No AWS4 authentication configured!"); + } + + /** + * Set the User-Agent header's value (by adding to the default header map). + * + * @param userAgent HTTP request's user agent + * @return ApiClient + */ + public ApiClient setUserAgent(String userAgent) { + addDefaultHeader("User-Agent", userAgent); + return this; + } + + /** + * Add a default header. + * + * @param key The header's key + * @param value The header's value + * @return ApiClient + */ + public ApiClient addDefaultHeader(String key, String value) { + defaultHeaderMap.put(key, value); + return this; + } + + /** + * Add a default cookie. + * + * @param key The cookie's key + * @param value The cookie's value + * @return ApiClient + */ + public ApiClient addDefaultCookie(String key, String value) { + defaultCookieMap.put(key, value); + return this; + } + + /** + * Check that whether debugging is enabled for this API client. + * + * @return True if debugging is enabled, false otherwise. + */ + public boolean isDebugging() { + return debugging; + } + + /** + * Enable/disable debugging for this API client. + * + * @param debugging To enable (true) or disable (false) debugging + * @return ApiClient + */ + public ApiClient setDebugging(boolean debugging) { + if (debugging != this.debugging) { + if (debugging) { + loggingInterceptor = new HttpLoggingInterceptor(); + loggingInterceptor.setLevel(Level.BODY); + httpClient = httpClient.newBuilder().addInterceptor(loggingInterceptor).build(); + } else { + final OkHttpClient.Builder builder = httpClient.newBuilder(); + builder.interceptors().remove(loggingInterceptor); + httpClient = builder.build(); + loggingInterceptor = null; + } + } + this.debugging = debugging; + return this; + } + + /** + * The path of temporary folder used to store downloaded files from endpoints + * with file response. The default value is null, i.e. using + * the system's default temporary folder. + * + * @see createTempFile + * @return Temporary folder path + */ + public String getTempFolderPath() { + return tempFolderPath; + } + + /** + * Set the temporary folder path (for downloading files) + * + * @param tempFolderPath Temporary folder path + * @return ApiClient + */ + public ApiClient setTempFolderPath(String tempFolderPath) { + this.tempFolderPath = tempFolderPath; + return this; + } + + /** + * Get connection timeout (in milliseconds). + * + * @return Timeout in milliseconds + */ + public int getConnectTimeout() { + return httpClient.connectTimeoutMillis(); + } + + /** + * Sets the connect timeout (in milliseconds). + * A value of 0 means no timeout, otherwise values must be between 1 and + * {@link java.lang.Integer#MAX_VALUE}. + * + * @param connectionTimeout connection timeout in milliseconds + * @return Api client + */ + public ApiClient setConnectTimeout(int connectionTimeout) { + httpClient = httpClient.newBuilder().connectTimeout(connectionTimeout, TimeUnit.MILLISECONDS).build(); + return this; + } + + /** + * Get read timeout (in milliseconds). + * + * @return Timeout in milliseconds + */ + public int getReadTimeout() { + return httpClient.readTimeoutMillis(); + } + + /** + * Sets the read timeout (in milliseconds). + * A value of 0 means no timeout, otherwise values must be between 1 and + * {@link java.lang.Integer#MAX_VALUE}. + * + * @param readTimeout read timeout in milliseconds + * @return Api client + */ + public ApiClient setReadTimeout(int readTimeout) { + httpClient = httpClient.newBuilder().readTimeout(readTimeout, TimeUnit.MILLISECONDS).build(); + return this; + } + + /** + * Get write timeout (in milliseconds). + * + * @return Timeout in milliseconds + */ + public int getWriteTimeout() { + return httpClient.writeTimeoutMillis(); + } + + /** + * Sets the write timeout (in milliseconds). + * A value of 0 means no timeout, otherwise values must be between 1 and + * {@link java.lang.Integer#MAX_VALUE}. + * + * @param writeTimeout connection timeout in milliseconds + * @return Api client + */ + public ApiClient setWriteTimeout(int writeTimeout) { + httpClient = httpClient.newBuilder().writeTimeout(writeTimeout, TimeUnit.MILLISECONDS).build(); + return this; + } + + + /** + * Format the given parameter object into string. + * + * @param param Parameter + * @return String representation of the parameter + */ + public String parameterToString(Object param) { + if (param == null) { + return ""; + } else if (param instanceof Date || param instanceof OffsetDateTime || param instanceof LocalDate) { + //Serialize to json string and remove the " enclosing characters + String jsonStr = JSON.serialize(param); + return jsonStr.substring(1, jsonStr.length() - 1); + } else if (param instanceof Collection) { + StringBuilder b = new StringBuilder(); + for (Object o : (Collection) param) { + if (b.length() > 0) { + b.append(","); + } + b.append(o); + } + return b.toString(); + } else { + return String.valueOf(param); + } + } + + /** + * Formats the specified query parameter to a list containing a single {@code Pair} object. + * + * Note that {@code value} must not be a collection. + * + * @param name The name of the parameter. + * @param value The value of the parameter. + * @return A list containing a single {@code Pair} object. + */ + public List parameterToPair(String name, Object value) { + List params = new ArrayList(); + + // preconditions + if (name == null || name.isEmpty() || value == null || value instanceof Collection) { + return params; + } + + params.add(new Pair(name, parameterToString(value))); + return params; + } + + /** + * Formats the specified collection query parameters to a list of {@code Pair} objects. + * + * Note that the values of each of the returned Pair objects are percent-encoded. + * + * @param collectionFormat The collection format of the parameter. + * @param name The name of the parameter. + * @param value The value of the parameter. + * @return A list of {@code Pair} objects. + */ + public List parameterToPairs(String collectionFormat, String name, Collection value) { + List params = new ArrayList(); + + // preconditions + if (name == null || name.isEmpty() || value == null || value.isEmpty()) { + return params; + } + + // create the params based on the collection format + if ("multi".equals(collectionFormat)) { + for (Object item : value) { + params.add(new Pair(name, escapeString(parameterToString(item)))); + } + return params; + } + + // collectionFormat is assumed to be "csv" by default + String delimiter = ","; + + // escape all delimiters except commas, which are URI reserved + // characters + if ("ssv".equals(collectionFormat)) { + delimiter = escapeString(" "); + } else if ("tsv".equals(collectionFormat)) { + delimiter = escapeString("\t"); + } else if ("pipes".equals(collectionFormat)) { + delimiter = escapeString("|"); + } + + StringBuilder sb = new StringBuilder(); + for (Object item : value) { + sb.append(delimiter); + sb.append(escapeString(parameterToString(item))); + } + + params.add(new Pair(name, sb.substring(delimiter.length()))); + + return params; + } + + /** + * Formats the specified collection path parameter to a string value. + * + * @param collectionFormat The collection format of the parameter. + * @param value The value of the parameter. + * @return String representation of the parameter + */ + public String collectionPathParameterToString(String collectionFormat, Collection value) { + // create the value based on the collection format + if ("multi".equals(collectionFormat)) { + // not valid for path params + return parameterToString(value); + } + + // collectionFormat is assumed to be "csv" by default + String delimiter = ","; + + if ("ssv".equals(collectionFormat)) { + delimiter = " "; + } else if ("tsv".equals(collectionFormat)) { + delimiter = "\t"; + } else if ("pipes".equals(collectionFormat)) { + delimiter = "|"; + } + + StringBuilder sb = new StringBuilder() ; + for (Object item : value) { + sb.append(delimiter); + sb.append(parameterToString(item)); + } + + return sb.substring(delimiter.length()); + } + + /** + * Sanitize filename by removing path. + * e.g. ../../sun.gif becomes sun.gif + * + * @param filename The filename to be sanitized + * @return The sanitized filename + */ + public String sanitizeFilename(String filename) { + return filename.replaceAll(".*[/\\\\]", ""); + } + + /** + * Check if the given MIME is a JSON MIME. + * JSON MIME examples: + * application/json + * application/json; charset=UTF8 + * APPLICATION/JSON + * application/vnd.company+json + * "* / *" is also default to JSON + * @param mime MIME (Multipurpose Internet Mail Extensions) + * @return True if the given MIME is JSON, false otherwise. + */ + public boolean isJsonMime(String mime) { + String jsonMime = "(?i)^(application/json|[^;/ \t]+/[^;/ \t]+[+]json)[ \t]*(;.*)?$"; + return mime != null && (mime.matches(jsonMime) || mime.equals("*/*")); + } + + /** + * Select the Accept header's value from the given accepts array: + * if JSON exists in the given array, use it; + * otherwise use all of them (joining into a string) + * + * @param accepts The accepts array to select from + * @return The Accept header to use. If the given array is empty, + * null will be returned (not to set the Accept header explicitly). + */ + public String selectHeaderAccept(String[] accepts) { + if (accepts.length == 0) { + return null; + } + for (String accept : accepts) { + if (isJsonMime(accept)) { + return accept; + } + } + return StringUtil.join(accepts, ","); + } + + /** + * Select the Content-Type header's value from the given array: + * if JSON exists in the given array, use it; + * otherwise use the first one of the array. + * + * @param contentTypes The Content-Type array to select from + * @return The Content-Type header to use. If the given array is empty, + * returns null. If it matches "any", JSON will be used. + */ + public String selectHeaderContentType(String[] contentTypes) { + if (contentTypes.length == 0) { + return null; + } + + if (contentTypes[0].equals("*/*")) { + return "application/json"; + } + + for (String contentType : contentTypes) { + if (isJsonMime(contentType)) { + return contentType; + } + } + + return contentTypes[0]; + } + + /** + * Escape the given string to be used as URL query value. + * + * @param str String to be escaped + * @return Escaped string + */ + public String escapeString(String str) { + try { + return URLEncoder.encode(str, "utf8").replaceAll("\\+", "%20"); + } catch (UnsupportedEncodingException e) { + return str; + } + } + + /** + * Deserialize response body to Java object, according to the return type and + * the Content-Type response header. + * + * @param Type + * @param response HTTP response + * @param returnType The type of the Java object + * @return The deserialized Java object + * @throws <%- metadata.packageName %>.ApiException If fail to deserialize response body, i.e. cannot read response body + * or the Content-Type of the response is not supported. + */ + @SuppressWarnings("unchecked") + public T deserialize(Response response, Type returnType) throws ApiException { + if (response == null || returnType == null) { + return null; + } + + if ("byte[]".equals(returnType.toString())) { + // Handle binary response (byte array). + try { + return (T) response.body().bytes(); + } catch (IOException e) { + throw new ApiException(e); + } + } else if (returnType.equals(File.class)) { + // Handle file downloading. + return (T) downloadFileFromResponse(response); + } + + String respBody; + try { + if (response.body() != null) + respBody = response.body().string(); + else + respBody = null; + } catch (IOException e) { + throw new ApiException(e); + } + + if (respBody == null || "".equals(respBody)) { + return null; + } + + String contentType = response.headers().get("Content-Type"); + if (contentType == null) { + // ensuring a default content type + contentType = "application/json"; + } + if (isJsonMime(contentType)) { + return JSON.deserialize(respBody, returnType); + } else if (returnType.equals(String.class)) { + // Expecting string, return the raw response body. + return (T) respBody; + } else { + throw new ApiException( + "Content type \"" + contentType + "\" is not supported for type: " + returnType, + response.code(), + response.headers().toMultimap(), + respBody); + } + } + + /** + * Serialize the given Java object into request body according to the object's + * class and the request Content-Type. + * + * @param obj The Java object + * @param contentType The request Content-Type + * @return The serialized request body + * @throws <%- metadata.packageName %>.ApiException If fail to serialize the given object + */ + public RequestBody serialize(Object obj, String contentType) throws ApiException { + if (obj instanceof byte[]) { + // Binary (byte array) body parameter support. + return RequestBody.create((byte[]) obj, MediaType.parse(contentType)); + } else if (obj instanceof File) { + // File body parameter support. + return RequestBody.create((File) obj, MediaType.parse(contentType)); + } else if ("text/plain".equals(contentType) && obj instanceof String) { + return RequestBody.create((String) obj, MediaType.parse(contentType)); + } else if (isJsonMime(contentType)) { + String content; + if (obj != null) { + content = JSON.serialize(obj); + } else { + content = null; + } + return RequestBody.create(content, MediaType.parse(contentType)); + } else if (obj instanceof String) { + return RequestBody.create((String) obj, MediaType.parse(contentType)); + } else { + throw new ApiException("Content type \"" + contentType + "\" is not supported"); + } + } + + /** + * Download file from the given response. + * + * @param response An instance of the Response object + * @throws <%- metadata.packageName %>.ApiException If fail to read file content from response and write to disk + * @return Downloaded file + */ + public File downloadFileFromResponse(Response response) throws ApiException { + try { + File file = prepareDownloadFile(response); + BufferedSink sink = Okio.buffer(Okio.sink(file)); + sink.writeAll(response.body().source()); + sink.close(); + return file; + } catch (IOException e) { + throw new ApiException(e); + } + } + + /** + * Prepare file for download + * + * @param response An instance of the Response object + * @return Prepared file for the download + * @throws java.io.IOException If fail to prepare file for download + */ + public File prepareDownloadFile(Response response) throws IOException { + String filename = null; + String contentDisposition = response.header("Content-Disposition"); + if (contentDisposition != null && !"".equals(contentDisposition)) { + // Get filename from the Content-Disposition header. + Pattern pattern = Pattern.compile("filename=['\"]?([^'\"\\s]+)['\"]?"); + Matcher matcher = pattern.matcher(contentDisposition); + if (matcher.find()) { + filename = sanitizeFilename(matcher.group(1)); + } + } + + String prefix = null; + String suffix = null; + if (filename == null) { + prefix = "download-"; + suffix = ""; + } else { + int pos = filename.lastIndexOf("."); + if (pos == -1) { + prefix = filename + "-"; + } else { + prefix = filename.substring(0, pos) + "-"; + suffix = filename.substring(pos); + } + // Files.createTempFile requires the prefix to be at least three characters long + if (prefix.length() < 3) + prefix = "download-"; + } + + if (tempFolderPath == null) + return Files.createTempFile(prefix, suffix).toFile(); + else + return Files.createTempFile(Paths.get(tempFolderPath), prefix, suffix).toFile(); + } + + /** + * {@link #execute(Call, Type)} + * + * @param Type + * @param call An instance of the Call object + * @return ApiResponse<T> + * @throws <%- metadata.packageName %>.ApiException If fail to execute the call + */ + public ApiResponse execute(Call call) throws ApiException { + return execute(call, null); + } + + /** + * Execute HTTP call and deserialize the HTTP response body into the given return type. + * + * @param returnType The return type used to deserialize HTTP response body + * @param The return type corresponding to (same with) returnType + * @param call Call + * @return ApiResponse object containing response status, headers and + * data, which is a Java object deserialized from response body and would be null + * when returnType is null. + * @throws <%- metadata.packageName %>.ApiException If fail to execute the call + */ + public ApiResponse execute(Call call, Type returnType) throws ApiException { + try { + Response response = call.execute(); + T data = handleResponse(response, returnType); + return new ApiResponse(response.code(), response.headers().toMultimap(), data); + } catch (IOException e) { + throw new ApiException(e); + } + } + + /** + * {@link #executeAsync(Call, Type, ApiCallback)} + * + * @param Type + * @param call An instance of the Call object + * @param callback ApiCallback<T> + */ + public void executeAsync(Call call, ApiCallback callback) { + executeAsync(call, null, callback); + } + + /** + * Execute HTTP call asynchronously. + * + * @param Type + * @param call The callback to be executed when the API call finishes + * @param returnType Return type + * @param callback ApiCallback + * @see #execute(Call, Type) + */ + @SuppressWarnings("unchecked") + public void executeAsync(Call call, final Type returnType, final ApiCallback callback) { + call.enqueue(new Callback() { + @Override + public void onFailure(Call call, IOException e) { + callback.onFailure(new ApiException(e), 0, null); + } + + @Override + public void onResponse(Call call, Response response) throws IOException { + T result; + try { + result = (T) handleResponse(response, returnType); + } catch (ApiException e) { + callback.onFailure(e, response.code(), response.headers().toMultimap()); + return; + } catch (Exception e) { + callback.onFailure(new ApiException(e), response.code(), response.headers().toMultimap()); + return; + } + callback.onSuccess(result, response.code(), response.headers().toMultimap()); + } + }); + } + + /** + * Handle the given response, return the deserialized object when the response is successful. + * + * @param Type + * @param response Response + * @param returnType Return type + * @return Type + * @throws <%- metadata.packageName %>.ApiException If the response has an unsuccessful status code or + * fail to deserialize the response body + */ + public T handleResponse(Response response, Type returnType) throws ApiException { + if (response.isSuccessful()) { + if (returnType == null || response.code() == 204) { + // returning null if the returnType is not defined, + // or the status code is 204 (No Content) + if (response.body() != null) { + try { + response.body().close(); + } catch (Exception e) { + throw new ApiException(response.message(), e, response.code(), response.headers().toMultimap()); + } + } + return null; + } else { + return deserialize(response, returnType); + } + } else { + String respBody = null; + if (response.body() != null) { + try { + respBody = response.body().string(); + } catch (IOException e) { + throw new ApiException(response.message(), e, response.code(), response.headers().toMultimap()); + } + } + throw new ApiException(response.message(), response.code(), response.headers().toMultimap(), respBody); + } + } + + /** + * Build HTTP call with the given options. + * + * @param baseUrl The base URL + * @param path The sub-path of the HTTP URL + * @param method The request method, one of "GET", "HEAD", "OPTIONS", "POST", "PUT", "PATCH" and "DELETE" + * @param queryParams The query parameters + * @param collectionQueryParams The collection query parameters + * @param body The request body object + * @param headerParams The header parameters + * @param cookieParams The cookie parameters + * @param formParams The form parameters + * @param authNames The authentications to apply + * @param callback Callback for upload/download progress + * @return The HTTP call + * @throws <%- metadata.packageName %>.ApiException If fail to serialize the request body object + */ + public Call buildCall(String baseUrl, String path, String method, List queryParams, List collectionQueryParams, Object body, Map headerParams, Map cookieParams, Map formParams, String[] authNames, ApiCallback callback) throws ApiException { + Request request = buildRequest(baseUrl, path, method, queryParams, collectionQueryParams, body, headerParams, cookieParams, formParams, authNames, callback); + + return httpClient.newCall(request); + } + + /** + * Build an HTTP request with the given options. + * + * @param baseUrl The base URL + * @param path The sub-path of the HTTP URL + * @param method The request method, one of "GET", "HEAD", "OPTIONS", "POST", "PUT", "PATCH" and "DELETE" + * @param queryParams The query parameters + * @param collectionQueryParams The collection query parameters + * @param body The request body object + * @param headerParams The header parameters + * @param cookieParams The cookie parameters + * @param formParams The form parameters + * @param authNames The authentications to apply + * @param callback Callback for upload/download progress + * @return The HTTP request + * @throws <%- metadata.packageName %>.ApiException If fail to serialize the request body object + */ + public Request buildRequest(String baseUrl, String path, String method, List queryParams, List collectionQueryParams, Object body, Map headerParams, Map cookieParams, Map formParams, String[] authNames, ApiCallback callback) throws ApiException { + // aggregate queryParams (non-collection) and collectionQueryParams into allQueryParams + List allQueryParams = new ArrayList(queryParams); + allQueryParams.addAll(collectionQueryParams); + + final String url = buildUrl(baseUrl, path, queryParams, collectionQueryParams); + + // prepare HTTP request body + RequestBody reqBody; + String contentType = headerParams.get("Content-Type"); + + if (!HttpMethod.permitsRequestBody(method)) { + reqBody = null; + } else if ("application/x-www-form-urlencoded".equals(contentType)) { + reqBody = buildRequestBodyFormEncoding(formParams); + } else if ("multipart/form-data".equals(contentType)) { + reqBody = buildRequestBodyMultipart(formParams); + } else if (body == null) { + if ("DELETE".equals(method)) { + // allow calling DELETE without sending a request body + reqBody = null; + } else { + // use an empty request body (for POST, PUT and PATCH) + reqBody = RequestBody.create("", contentType == null ? null : MediaType.parse(contentType)); + } + } else { + reqBody = serialize(body, contentType); + } + + // update parameters with authentication settings + updateParamsForAuth(authNames, allQueryParams, headerParams, cookieParams, requestBodyToString(reqBody), method, URI.create(url)); + + final Request.Builder reqBuilder = new Request.Builder().url(url); + processHeaderParams(headerParams, reqBuilder); + processCookieParams(cookieParams, reqBuilder); + + // Associate callback with request (if not null) so interceptor can + // access it when creating ProgressResponseBody + reqBuilder.tag(callback); + + Request request = null; + + if (callback != null && reqBody != null) { + ProgressRequestBody progressRequestBody = new ProgressRequestBody(reqBody, callback); + request = reqBuilder.method(method, progressRequestBody).build(); + } else { + request = reqBuilder.method(method, reqBody).build(); + } + + return request; + } + + /** + * Build full URL by concatenating base path, the given sub path and query parameters. + * + * @param baseUrl The base URL + * @param path The sub path + * @param queryParams The query parameters + * @param collectionQueryParams The collection query parameters + * @return The full URL + */ + public String buildUrl(String baseUrl, String path, List queryParams, List collectionQueryParams) { + final StringBuilder url = new StringBuilder(); + if (baseUrl != null) { + url.append(baseUrl).append(path); + } else { + String baseURL; + if (serverIndex != null) { + if (serverIndex < 0 || serverIndex >= servers.size()) { + throw new ArrayIndexOutOfBoundsException(String.format( + "Invalid index %d when selecting the host settings. Must be less than %d", serverIndex, servers.size() + )); + } + baseURL = servers.get(serverIndex).URL(serverVariables); + } else { + baseURL = basePath; + } + url.append(baseURL).append(path); + } + + if (queryParams != null && !queryParams.isEmpty()) { + // support (constant) query string in `path`, e.g. "/posts?draft=1" + String prefix = path.contains("?") ? "&" : "?"; + for (Pair param : queryParams) { + if (param.getValue() != null) { + if (prefix != null) { + url.append(prefix); + prefix = null; + } else { + url.append("&"); + } + String value = parameterToString(param.getValue()); + url.append(escapeString(param.getName())).append("=").append(escapeString(value)); + } + } + } + + if (collectionQueryParams != null && !collectionQueryParams.isEmpty()) { + String prefix = url.toString().contains("?") ? "&" : "?"; + for (Pair param : collectionQueryParams) { + if (param.getValue() != null) { + if (prefix != null) { + url.append(prefix); + prefix = null; + } else { + url.append("&"); + } + String value = parameterToString(param.getValue()); + // collection query parameter value already escaped as part of parameterToPairs + url.append(escapeString(param.getName())).append("=").append(value); + } + } + } + + return url.toString(); + } + + /** + * Set header parameters to the request builder, including default headers. + * + * @param headerParams Header parameters in the form of Map + * @param reqBuilder Request.Builder + */ + public void processHeaderParams(Map headerParams, Request.Builder reqBuilder) { + for (Entry param : headerParams.entrySet()) { + reqBuilder.header(param.getKey(), parameterToString(param.getValue())); + } + for (Entry header : defaultHeaderMap.entrySet()) { + if (!headerParams.containsKey(header.getKey())) { + reqBuilder.header(header.getKey(), parameterToString(header.getValue())); + } + } + } + + /** + * Set cookie parameters to the request builder, including default cookies. + * + * @param cookieParams Cookie parameters in the form of Map + * @param reqBuilder Request.Builder + */ + public void processCookieParams(Map cookieParams, Request.Builder reqBuilder) { + for (Entry param : cookieParams.entrySet()) { + reqBuilder.addHeader("Cookie", String.format("%s=%s", param.getKey(), param.getValue())); + } + for (Entry param : defaultCookieMap.entrySet()) { + if (!cookieParams.containsKey(param.getKey())) { + reqBuilder.addHeader("Cookie", String.format("%s=%s", param.getKey(), param.getValue())); + } + } + } + + /** + * Update query and header parameters based on authentication settings. + * + * @param authNames The authentications to apply + * @param queryParams List of query parameters + * @param headerParams Map of header parameters + * @param cookieParams Map of cookie parameters + * @param payload HTTP request body + * @param method HTTP method + * @param uri URI + * @throws <%- metadata.packageName %>.ApiException If fails to update the parameters + */ + public void updateParamsForAuth(String[] authNames, List queryParams, Map headerParams, + Map cookieParams, String payload, String method, URI uri) throws ApiException { + for (String authName : authNames) { + Authentication auth = authentications.get(authName); + if (auth == null) { + throw new RuntimeException("Authentication undefined: " + authName); + } + auth.applyToParams(queryParams, headerParams, cookieParams, payload, method, uri); + } + } + + /** + * Build a form-encoding request body with the given form parameters. + * + * @param formParams Form parameters in the form of Map + * @return RequestBody + */ + public RequestBody buildRequestBodyFormEncoding(Map formParams) { + okhttp3.FormBody.Builder formBuilder = new okhttp3.FormBody.Builder(); + for (Entry param : formParams.entrySet()) { + formBuilder.add(param.getKey(), parameterToString(param.getValue())); + } + return formBuilder.build(); + } + + /** + * Build a multipart (file uploading) request body with the given form parameters, + * which could contain text fields and file fields. + * + * @param formParams Form parameters in the form of Map + * @return RequestBody + */ + public RequestBody buildRequestBodyMultipart(Map formParams) { + MultipartBody.Builder mpBuilder = new MultipartBody.Builder().setType(MultipartBody.FORM); + for (Entry param : formParams.entrySet()) { + if (param.getValue() instanceof File) { + File file = (File) param.getValue(); + addPartToMultiPartBuilder(mpBuilder, param.getKey(), file); + } else if (param.getValue() instanceof List) { + List list = (List) param.getValue(); + for (Object item: list) { + if (item instanceof File) { + addPartToMultiPartBuilder(mpBuilder, param.getKey(), (File) item); + } else { + addPartToMultiPartBuilder(mpBuilder, param.getKey(), param.getValue()); + } + } + } else { + addPartToMultiPartBuilder(mpBuilder, param.getKey(), param.getValue()); + } + } + return mpBuilder.build(); + } + + /** + * Guess Content-Type header from the given file (defaults to "application/octet-stream"). + * + * @param file The given file + * @return The guessed Content-Type + */ + public String guessContentTypeFromFile(File file) { + String contentType = URLConnection.guessContentTypeFromName(file.getName()); + if (contentType == null) { + return "application/octet-stream"; + } else { + return contentType; + } + } + + /** + * Add a Content-Disposition Header for the given key and file to the MultipartBody Builder. + * + * @param mpBuilder MultipartBody.Builder + * @param key The key of the Header element + * @param file The file to add to the Header + */ + private void addPartToMultiPartBuilder(MultipartBody.Builder mpBuilder, String key, File file) { + Headers partHeaders = Headers.of("Content-Disposition", "form-data; name=\"" + key + "\"; filename=\"" + file.getName() + "\""); + MediaType mediaType = MediaType.parse(guessContentTypeFromFile(file)); + mpBuilder.addPart(partHeaders, RequestBody.create(file, mediaType)); + } + + /** + * Add a Content-Disposition Header for the given key and complex object to the MultipartBody Builder. + * + * @param mpBuilder MultipartBody.Builder + * @param key The key of the Header element + * @param obj The complex object to add to the Header + */ + private void addPartToMultiPartBuilder(MultipartBody.Builder mpBuilder, String key, Object obj) { + RequestBody requestBody; + if (obj instanceof String) { + requestBody = RequestBody.create((String) obj, MediaType.parse("text/plain")); + } else { + String content; + if (obj != null) { + content = JSON.serialize(obj); + } else { + content = null; + } + requestBody = RequestBody.create(content, MediaType.parse("application/json")); + } + + Headers partHeaders = Headers.of("Content-Disposition", "form-data; name=\"" + key + "\""); + mpBuilder.addPart(partHeaders, requestBody); + } + + /** + * Get network interceptor to add it to the httpClient to track download progress for + * async requests. + */ + private Interceptor getProgressInterceptor() { + return new Interceptor() { + @Override + public Response intercept(Interceptor.Chain chain) throws IOException { + final Request request = chain.request(); + final Response originalResponse = chain.proceed(request); + if (request.tag() instanceof ApiCallback) { + final ApiCallback callback = (ApiCallback) request.tag(); + return originalResponse.newBuilder() + .body(new ProgressResponseBody(originalResponse.body(), callback)) + .build(); + } + return originalResponse; + } + }; + } + + /** + * Apply SSL related settings to httpClient according to the current values of + * verifyingSsl and sslCaCert. + */ + private void applySslSettings() { + try { + TrustManager[] trustManagers; + HostnameVerifier hostnameVerifier; + if (!verifyingSsl) { + trustManagers = new TrustManager[]{ + new X509TrustManager() { + @Override + public void checkClientTrusted(java.security.cert.X509Certificate[] chain, String authType) throws CertificateException { + } + + @Override + public void checkServerTrusted(java.security.cert.X509Certificate[] chain, String authType) throws CertificateException { + } + + @Override + public java.security.cert.X509Certificate[] getAcceptedIssuers() { + return new java.security.cert.X509Certificate[]{}; + } + } + }; + hostnameVerifier = new HostnameVerifier() { + @Override + public boolean verify(String hostname, SSLSession session) { + return true; + } + }; + } else { + TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); + + if (sslCaCert == null) { + trustManagerFactory.init((KeyStore) null); + } else { + char[] password = null; // Any password will work. + CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509"); + Collection certificates = certificateFactory.generateCertificates(sslCaCert); + if (certificates.isEmpty()) { + throw new IllegalArgumentException("expected non-empty set of trusted certificates"); + } + KeyStore caKeyStore = newEmptyKeyStore(password); + int index = 0; + for (Certificate certificate : certificates) { + String certificateAlias = "ca" + (index++); + caKeyStore.setCertificateEntry(certificateAlias, certificate); + } + trustManagerFactory.init(caKeyStore); + } + trustManagers = trustManagerFactory.getTrustManagers(); + hostnameVerifier = OkHostnameVerifier.INSTANCE; + } + + SSLContext sslContext = SSLContext.getInstance("TLS"); + sslContext.init(keyManagers, trustManagers, new SecureRandom()); + httpClient = httpClient.newBuilder() + .sslSocketFactory(sslContext.getSocketFactory(), (X509TrustManager) trustManagers[0]) + .hostnameVerifier(hostnameVerifier) + .build(); + } catch (GeneralSecurityException e) { + throw new RuntimeException(e); + } + } + + private KeyStore newEmptyKeyStore(char[] password) throws GeneralSecurityException { + try { + KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType()); + keyStore.load(null, password); + return keyStore; + } catch (IOException e) { + throw new AssertionError(e); + } + } + + /** + * Convert the HTTP request body to a string. + * + * @param requestBody The HTTP request object + * @return The string representation of the HTTP request body + * @throws <%- metadata.packageName %>.ApiException If fail to serialize the request body object into a string + */ + private String requestBodyToString(RequestBody requestBody) throws ApiException { + if (requestBody != null) { + try { + final Buffer buffer = new Buffer(); + requestBody.writeTo(buffer); + return buffer.readUtf8(); + } catch (final IOException e) { + throw new ApiException(e); + } + } + + // empty http request body + return ""; + } +} diff --git a/packages/type-safe-api/scripts/type-safe-api/generators/java/templates/client/generic/apiException.ejs b/packages/type-safe-api/scripts/type-safe-api/generators/java/templates/client/generic/apiException.ejs new file mode 100644 index 000000000..25559d978 --- /dev/null +++ b/packages/type-safe-api/scripts/type-safe-api/generators/java/templates/client/generic/apiException.ejs @@ -0,0 +1,162 @@ +###TSAPI_WRITE_FILE### +{ + "dir": "<%- metadata.srcDir %>", + "name": "ApiException", + "ext": ".java", + "overwrite": true +} +###/TSAPI_WRITE_FILE###<%- include('../../header.partial.ejs', { info }) %> + + +package <%- metadata.packageName %>; + +import java.util.Map; +import java.util.List; + +import javax.ws.rs.core.GenericType; + +/** + *

ApiException class.

+ */ +@SuppressWarnings("serial") +public class ApiException extends Exception { + private int code = 0; + private Map> responseHeaders = null; + private String responseBody = null; + + /** + *

Constructor for ApiException.

+ */ + public ApiException() {} + + /** + *

Constructor for ApiException.

+ * + * @param throwable a {@link java.lang.Throwable} object + */ + public ApiException(Throwable throwable) { + super(throwable); + } + + /** + *

Constructor for ApiException.

+ * + * @param message the error message + */ + public ApiException(String message) { + super(message); + } + + /** + *

Constructor for ApiException.

+ * + * @param message the error message + * @param throwable a {@link java.lang.Throwable} object + * @param code HTTP status code + * @param responseHeaders a {@link java.util.Map} of HTTP response headers + * @param responseBody the response body + */ + public ApiException(String message, Throwable throwable, int code, Map> responseHeaders, String responseBody) { + super(message, throwable); + this.code = code; + this.responseHeaders = responseHeaders; + this.responseBody = responseBody; + } + + /** + *

Constructor for ApiException.

+ * + * @param message the error message + * @param code HTTP status code + * @param responseHeaders a {@link java.util.Map} of HTTP response headers + * @param responseBody the response body + */ + public ApiException(String message, int code, Map> responseHeaders, String responseBody) { + this(message, (Throwable) null, code, responseHeaders, responseBody); + } + + /** + *

Constructor for ApiException.

+ * + * @param message the error message + * @param throwable a {@link java.lang.Throwable} object + * @param code HTTP status code + * @param responseHeaders a {@link java.util.Map} of HTTP response headers + */ + public ApiException(String message, Throwable throwable, int code, Map> responseHeaders) { + this(message, throwable, code, responseHeaders, null); + } + + /** + *

Constructor for ApiException.

+ * + * @param code HTTP status code + * @param responseHeaders a {@link java.util.Map} of HTTP response headers + * @param responseBody the response body + */ + public ApiException(int code, Map> responseHeaders, String responseBody) { + this("Response Code: " + code + " Response Body: " + responseBody, (Throwable) null, code, responseHeaders, responseBody); + } + + /** + *

Constructor for ApiException.

+ * + * @param code HTTP status code + * @param message a {@link java.lang.String} object + */ + public ApiException(int code, String message) { + super(message); + this.code = code; + } + + /** + *

Constructor for ApiException.

+ * + * @param code HTTP status code + * @param message the error message + * @param responseHeaders a {@link java.util.Map} of HTTP response headers + * @param responseBody the response body + */ + public ApiException(int code, String message, Map> responseHeaders, String responseBody) { + this(code, message); + this.responseHeaders = responseHeaders; + this.responseBody = responseBody; + } + + /** + * Get the HTTP status code. + * + * @return HTTP status code + */ + public int getCode() { + return code; + } + + /** + * Get the HTTP response headers. + * + * @return A map of list of string + */ + public Map> getResponseHeaders() { + return responseHeaders; + } + + /** + * Get the HTTP response body. + * + * @return Response body in the form of string + */ + public String getResponseBody() { + return responseBody; + } + + /** + * Get the exception message including HTTP response data. + * + * @return The exception message + */ + public String getMessage() { + return String.format("Message: %s%nHTTP response code: %s%nHTTP response body: %s%nHTTP response headers: %s", + super.getMessage(), this.getCode(), this.getResponseBody(), this.getResponseHeaders()); + } +} diff --git a/packages/type-safe-api/scripts/type-safe-api/generators/java/templates/client/generic/apiResponse.ejs b/packages/type-safe-api/scripts/type-safe-api/generators/java/templates/client/generic/apiResponse.ejs new file mode 100644 index 000000000..87bc15498 --- /dev/null +++ b/packages/type-safe-api/scripts/type-safe-api/generators/java/templates/client/generic/apiResponse.ejs @@ -0,0 +1,73 @@ +###TSAPI_WRITE_FILE### +{ + "dir": "<%- metadata.srcDir %>", + "name": "ApiResponse", + "ext": ".java", + "overwrite": true +} +###/TSAPI_WRITE_FILE###<%- include('../../header.partial.ejs', { info }) %> + + +package <%- metadata.packageName %>; + +import java.util.List; +import java.util.Map; + +/** + * API response returned by API call. + */ +public class ApiResponse { + final private int statusCode; + final private Map> headers; + final private T data; + + /** + *

Constructor for ApiResponse.

+ * + * @param statusCode The status code of HTTP response + * @param headers The headers of HTTP response + */ + public ApiResponse(int statusCode, Map> headers) { + this(statusCode, headers, null); + } + + /** + *

Constructor for ApiResponse.

+ * + * @param statusCode The status code of HTTP response + * @param headers The headers of HTTP response + * @param data The object deserialized from response bod + */ + public ApiResponse(int statusCode, Map> headers, T data) { + this.statusCode = statusCode; + this.headers = headers; + this.data = data; + } + + /** + *

Get the status code.

+ * + * @return the status code + */ + public int getStatusCode() { + return statusCode; + } + + /** + *

Get the headers.

+ * + * @return a {@link java.util.Map} of headers + */ + public Map> getHeaders() { + return headers; + } + + /** + *

Get the data.

+ * + * @return the data + */ + public T getData() { + return data; + } +} diff --git a/packages/type-safe-api/scripts/type-safe-api/generators/java/templates/client/generic/configuration.ejs b/packages/type-safe-api/scripts/type-safe-api/generators/java/templates/client/generic/configuration.ejs new file mode 100644 index 000000000..7bedd7ab5 --- /dev/null +++ b/packages/type-safe-api/scripts/type-safe-api/generators/java/templates/client/generic/configuration.ejs @@ -0,0 +1,35 @@ +###TSAPI_WRITE_FILE### +{ + "dir": "<%- metadata.srcDir %>", + "name": "Configuration", + "ext": ".java", + "overwrite": true +} +###/TSAPI_WRITE_FILE###<%- include('../../header.partial.ejs', { info }) %> + + +package <%- metadata.packageName %>; + +public class Configuration { + private static ApiClient defaultApiClient = new ApiClient(); + + /** + * Get the default API client, which would be used when creating API + * instances without providing an API client. + * + * @return Default API client + */ + public static ApiClient getDefaultApiClient() { + return defaultApiClient; + } + + /** + * Set the default API client, which would be used when creating API + * instances without providing an API client. + * + * @param apiClient API client + */ + public static void setDefaultApiClient(ApiClient apiClient) { + defaultApiClient = apiClient; + } +} diff --git a/packages/type-safe-api/scripts/type-safe-api/generators/java/templates/client/generic/gzipRequestInterceptor.ejs b/packages/type-safe-api/scripts/type-safe-api/generators/java/templates/client/generic/gzipRequestInterceptor.ejs new file mode 100644 index 000000000..d21889641 --- /dev/null +++ b/packages/type-safe-api/scripts/type-safe-api/generators/java/templates/client/generic/gzipRequestInterceptor.ejs @@ -0,0 +1,82 @@ +###TSAPI_WRITE_FILE### +{ + "dir": "<%- metadata.srcDir %>", + "name": "GzipRequestInterceptor", + "ext": ".java", + "overwrite": true +} +###/TSAPI_WRITE_FILE###<%- include('../../header.partial.ejs', { info }) %> + + +package <%- metadata.packageName %>; + +import okhttp3.*; +import okio.Buffer; +import okio.BufferedSink; +import okio.GzipSink; +import okio.Okio; + +import java.io.IOException; + +/** + * Encodes request bodies using gzip. + * + * Taken from https://github.com/square/okhttp/issues/350 + */ +class GzipRequestInterceptor implements Interceptor { + @Override + public Response intercept(Chain chain) throws IOException { + Request originalRequest = chain.request(); + if (originalRequest.body() == null || originalRequest.header("Content-Encoding") != null) { + return chain.proceed(originalRequest); + } + + Request compressedRequest = originalRequest.newBuilder() + .header("Content-Encoding", "gzip") + .method(originalRequest.method(), forceContentLength(gzip(originalRequest.body()))) + .build(); + return chain.proceed(compressedRequest); + } + + private RequestBody forceContentLength(final RequestBody requestBody) throws IOException { + final Buffer buffer = new Buffer(); + requestBody.writeTo(buffer); + return new RequestBody() { + @Override + public MediaType contentType() { + return requestBody.contentType(); + } + + @Override + public long contentLength() { + return buffer.size(); + } + + @Override + public void writeTo(BufferedSink sink) throws IOException { + sink.write(buffer.snapshot()); + } + }; + } + + private RequestBody gzip(final RequestBody body) { + return new RequestBody() { + @Override + public MediaType contentType() { + return body.contentType(); + } + + @Override + public long contentLength() { + return -1; // We don't know the compressed length in advance! + } + + @Override + public void writeTo(BufferedSink sink) throws IOException { + BufferedSink gzipSink = Okio.buffer(new GzipSink(sink)); + body.writeTo(gzipSink); + gzipSink.close(); + } + }; + } +} diff --git a/packages/type-safe-api/scripts/type-safe-api/generators/java/templates/client/generic/json.ejs b/packages/type-safe-api/scripts/type-safe-api/generators/java/templates/client/generic/json.ejs new file mode 100644 index 000000000..92de06e11 --- /dev/null +++ b/packages/type-safe-api/scripts/type-safe-api/generators/java/templates/client/generic/json.ejs @@ -0,0 +1,400 @@ +###TSAPI_WRITE_FILE### +{ + "dir": "<%- metadata.srcDir %>", + "name": "JSON", + "ext": ".java", + "overwrite": true +} +###/TSAPI_WRITE_FILE###<%- include('../../header.partial.ejs', { info }) %> + + +package <%- metadata.packageName %>; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.JsonParseException; +import com.google.gson.TypeAdapter; +import com.google.gson.internal.bind.util.ISO8601Utils; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonWriter; +import com.google.gson.JsonElement; +import io.gsonfire.GsonFireBuilder; +import io.gsonfire.TypeSelector; + +import okio.ByteString; + +import java.io.IOException; +import java.io.StringReader; +import java.lang.reflect.Type; +import java.text.DateFormat; +import java.text.ParseException; +import java.text.ParsePosition; +import java.time.LocalDate; +import java.time.OffsetDateTime; +import java.time.format.DateTimeFormatter; +import java.util.Date; +import java.util.Locale; +import java.util.Map; +import java.util.HashMap; + +/* + * A JSON utility class + * + * NOTE: in the future, this class may be converted to static, which may break + * backward-compatibility + */ +public class JSON { + private static Gson gson; + private static boolean isLenientOnJson = false; + private static DateTypeAdapter dateTypeAdapter = new DateTypeAdapter(); + private static SqlDateTypeAdapter sqlDateTypeAdapter = new SqlDateTypeAdapter(); + private static OffsetDateTimeTypeAdapter offsetDateTimeTypeAdapter = new OffsetDateTimeTypeAdapter(); + private static LocalDateTypeAdapter localDateTypeAdapter = new LocalDateTypeAdapter(); + private static ByteArrayAdapter byteArrayAdapter = new ByteArrayAdapter(); + + @SuppressWarnings("unchecked") + public static GsonBuilder createGson() { + GsonFireBuilder fireBuilder = new GsonFireBuilder() + ; + GsonBuilder builder = fireBuilder.createGsonBuilder(); + return builder; + } + + private static String getDiscriminatorValue(JsonElement readElement, String discriminatorField) { + JsonElement element = readElement.getAsJsonObject().get(discriminatorField); + if (null == element) { + throw new IllegalArgumentException("missing discriminator field: <" + discriminatorField + ">"); + } + return element.getAsString(); + } + + /** + * Returns the Java class that implements the OpenAPI schema for the specified discriminator value. + * + * @param classByDiscriminatorValue The map of discriminator values to Java classes. + * @param discriminatorValue The value of the OpenAPI discriminator in the input data. + * @return The Java class that implements the OpenAPI schema + */ + private static Class getClassByDiscriminator(Map classByDiscriminatorValue, String discriminatorValue) { + Class clazz = (Class) classByDiscriminatorValue.get(discriminatorValue); + if (null == clazz) { + throw new IllegalArgumentException("cannot determine model class of name: <" + discriminatorValue + ">"); + } + return clazz; + } + + { + GsonBuilder gsonBuilder = createGson(); + gsonBuilder.registerTypeAdapter(Date.class, dateTypeAdapter); + gsonBuilder.registerTypeAdapter(java.sql.Date.class, sqlDateTypeAdapter); + gsonBuilder.registerTypeAdapter(OffsetDateTime.class, offsetDateTimeTypeAdapter); + gsonBuilder.registerTypeAdapter(LocalDate.class, localDateTypeAdapter); + gsonBuilder.registerTypeAdapter(byte[].class, byteArrayAdapter); + <%_ models.filter(m => m.export !== "enum").forEach((model) => { _%> + gsonBuilder.registerTypeAdapterFactory(new <%- metadata.packageName %>.model.<%- model.name %>.CustomTypeAdapterFactory()); + <%_ }); _%> + gson = gsonBuilder.create(); + } + + /** + * Get Gson. + * + * @return Gson + */ + public static Gson getGson() { + return gson; + } + + /** + * Set Gson. + * + * @param gson Gson + */ + public static void setGson(Gson gson) { + JSON.gson = gson; + } + + public static void setLenientOnJson(boolean lenientOnJson) { + isLenientOnJson = lenientOnJson; + } + + /** + * Serialize the given Java object into JSON string. + * + * @param obj Object + * @return String representation of the JSON + */ + public static String serialize(Object obj) { + return gson.toJson(obj); + } + + /** + * Deserialize the given JSON string to Java object. + * + * @param Type + * @param body The JSON string + * @param returnType The type to deserialize into + * @return The deserialized Java object + */ + @SuppressWarnings("unchecked") + public static T deserialize(String body, Type returnType) { + try { + if (isLenientOnJson) { + JsonReader jsonReader = new JsonReader(new StringReader(body)); + // see https://google-gson.googlecode.com/svn/trunk/gson/docs/javadocs/com/google/gson/stream/JsonReader.html#setLenient(boolean) + jsonReader.setLenient(true); + return gson.fromJson(jsonReader, returnType); + } else { + return gson.fromJson(body, returnType); + } + } catch (JsonParseException e) { + // Fallback processing when failed to parse JSON form response body: + // return the response body string directly for the String return type; + if (returnType.equals(String.class)) { + return (T) body; + } else { + throw (e); + } + } + } + + /** + * Gson TypeAdapter for Byte Array type + */ + public static class ByteArrayAdapter extends TypeAdapter { + + @Override + public void write(JsonWriter out, byte[] value) throws IOException { + if (value == null) { + out.nullValue(); + } else { + out.value(ByteString.of(value).base64()); + } + } + + @Override + public byte[] read(JsonReader in) throws IOException { + switch (in.peek()) { + case NULL: + in.nextNull(); + return null; + default: + String bytesAsBase64 = in.nextString(); + ByteString byteString = ByteString.decodeBase64(bytesAsBase64); + return byteString.toByteArray(); + } + } + } + + /** + * Gson TypeAdapter for JSR310 OffsetDateTime type + */ + public static class OffsetDateTimeTypeAdapter extends TypeAdapter { + + private DateTimeFormatter formatter; + + public OffsetDateTimeTypeAdapter() { + this(DateTimeFormatter.ISO_OFFSET_DATE_TIME); + } + + public OffsetDateTimeTypeAdapter(DateTimeFormatter formatter) { + this.formatter = formatter; + } + + public void setFormat(DateTimeFormatter dateFormat) { + this.formatter = dateFormat; + } + + @Override + public void write(JsonWriter out, OffsetDateTime date) throws IOException { + if (date == null) { + out.nullValue(); + } else { + out.value(formatter.format(date)); + } + } + + @Override + public OffsetDateTime read(JsonReader in) throws IOException { + switch (in.peek()) { + case NULL: + in.nextNull(); + return null; + default: + String date = in.nextString(); + if (date.endsWith("+0000")) { + date = date.substring(0, date.length()-5) + "Z"; + } + return OffsetDateTime.parse(date, formatter); + } + } + } + + /** + * Gson TypeAdapter for JSR310 LocalDate type + */ + public static class LocalDateTypeAdapter extends TypeAdapter { + + private DateTimeFormatter formatter; + + public LocalDateTypeAdapter() { + this(DateTimeFormatter.ISO_LOCAL_DATE); + } + + public LocalDateTypeAdapter(DateTimeFormatter formatter) { + this.formatter = formatter; + } + + public void setFormat(DateTimeFormatter dateFormat) { + this.formatter = dateFormat; + } + + @Override + public void write(JsonWriter out, LocalDate date) throws IOException { + if (date == null) { + out.nullValue(); + } else { + out.value(formatter.format(date)); + } + } + + @Override + public LocalDate read(JsonReader in) throws IOException { + switch (in.peek()) { + case NULL: + in.nextNull(); + return null; + default: + String date = in.nextString(); + return LocalDate.parse(date, formatter); + } + } + } + + public static void setOffsetDateTimeFormat(DateTimeFormatter dateFormat) { + offsetDateTimeTypeAdapter.setFormat(dateFormat); + } + + public static void setLocalDateFormat(DateTimeFormatter dateFormat) { + localDateTypeAdapter.setFormat(dateFormat); + } + + /** + * Gson TypeAdapter for java.sql.Date type + * If the dateFormat is null, a simple "yyyy-MM-dd" format will be used + * (more efficient than SimpleDateFormat). + */ + public static class SqlDateTypeAdapter extends TypeAdapter { + + private DateFormat dateFormat; + + public SqlDateTypeAdapter() {} + + public SqlDateTypeAdapter(DateFormat dateFormat) { + this.dateFormat = dateFormat; + } + + public void setFormat(DateFormat dateFormat) { + this.dateFormat = dateFormat; + } + + @Override + public void write(JsonWriter out, java.sql.Date date) throws IOException { + if (date == null) { + out.nullValue(); + } else { + String value; + if (dateFormat != null) { + value = dateFormat.format(date); + } else { + value = date.toString(); + } + out.value(value); + } + } + + @Override + public java.sql.Date read(JsonReader in) throws IOException { + switch (in.peek()) { + case NULL: + in.nextNull(); + return null; + default: + String date = in.nextString(); + try { + if (dateFormat != null) { + return new java.sql.Date(dateFormat.parse(date).getTime()); + } + return new java.sql.Date(ISO8601Utils.parse(date, new ParsePosition(0)).getTime()); + } catch (ParseException e) { + throw new JsonParseException(e); + } + } + } + } + + /** + * Gson TypeAdapter for java.util.Date type + * If the dateFormat is null, ISO8601Utils will be used. + */ + public static class DateTypeAdapter extends TypeAdapter { + + private DateFormat dateFormat; + + public DateTypeAdapter() {} + + public DateTypeAdapter(DateFormat dateFormat) { + this.dateFormat = dateFormat; + } + + public void setFormat(DateFormat dateFormat) { + this.dateFormat = dateFormat; + } + + @Override + public void write(JsonWriter out, Date date) throws IOException { + if (date == null) { + out.nullValue(); + } else { + String value; + if (dateFormat != null) { + value = dateFormat.format(date); + } else { + value = ISO8601Utils.format(date, true); + } + out.value(value); + } + } + + @Override + public Date read(JsonReader in) throws IOException { + try { + switch (in.peek()) { + case NULL: + in.nextNull(); + return null; + default: + String date = in.nextString(); + try { + if (dateFormat != null) { + return dateFormat.parse(date); + } + return ISO8601Utils.parse(date, new ParsePosition(0)); + } catch (ParseException e) { + throw new JsonParseException(e); + } + } + } catch (IllegalArgumentException e) { + throw new JsonParseException(e); + } + } + } + + public static void setDateFormat(DateFormat dateFormat) { + dateTypeAdapter.setFormat(dateFormat); + } + + public static void setSqlDateFormat(DateFormat dateFormat) { + sqlDateTypeAdapter.setFormat(dateFormat); + } +} diff --git a/packages/type-safe-api/scripts/type-safe-api/generators/java/templates/client/generic/pair.ejs b/packages/type-safe-api/scripts/type-safe-api/generators/java/templates/client/generic/pair.ejs new file mode 100644 index 000000000..6a98e5a43 --- /dev/null +++ b/packages/type-safe-api/scripts/type-safe-api/generators/java/templates/client/generic/pair.ejs @@ -0,0 +1,53 @@ +###TSAPI_WRITE_FILE### +{ + "dir": "<%- metadata.srcDir %>", + "name": "Pair", + "ext": ".java", + "overwrite": true +} +###/TSAPI_WRITE_FILE###<%- include('../../header.partial.ejs', { info }) %> + + +package <%- metadata.packageName %>; + +public class Pair { + private String name = ""; + private String value = ""; + + public Pair (String name, String value) { + setName(name); + setValue(value); + } + + private void setName(String name) { + if (!isValidString(name)) { + return; + } + + this.name = name; + } + + private void setValue(String value) { + if (!isValidString(value)) { + return; + } + + this.value = value; + } + + public String getName() { + return this.name; + } + + public String getValue() { + return this.value; + } + + private boolean isValidString(String arg) { + if (arg == null) { + return false; + } + + return true; + } +} diff --git a/packages/type-safe-api/scripts/type-safe-api/generators/java/templates/client/generic/progressRequestBody.ejs b/packages/type-safe-api/scripts/type-safe-api/generators/java/templates/client/generic/progressRequestBody.ejs new file mode 100644 index 000000000..2d8b5b1e4 --- /dev/null +++ b/packages/type-safe-api/scripts/type-safe-api/generators/java/templates/client/generic/progressRequestBody.ejs @@ -0,0 +1,70 @@ +###TSAPI_WRITE_FILE### +{ + "dir": "<%- metadata.srcDir %>", + "name": "ProgressRequestBody", + "ext": ".java", + "overwrite": true +} +###/TSAPI_WRITE_FILE###<%- include('../../header.partial.ejs', { info }) %> + + +package <%- metadata.packageName %>; + +import okhttp3.MediaType; +import okhttp3.RequestBody; + +import java.io.IOException; + +import okio.Buffer; +import okio.BufferedSink; +import okio.ForwardingSink; +import okio.Okio; +import okio.Sink; + +public class ProgressRequestBody extends RequestBody { + + private final RequestBody requestBody; + + private final ApiCallback callback; + + public ProgressRequestBody(RequestBody requestBody, ApiCallback callback) { + this.requestBody = requestBody; + this.callback = callback; + } + + @Override + public MediaType contentType() { + return requestBody.contentType(); + } + + @Override + public long contentLength() throws IOException { + return requestBody.contentLength(); + } + + @Override + public void writeTo(BufferedSink sink) throws IOException { + BufferedSink bufferedSink = Okio.buffer(sink(sink)); + requestBody.writeTo(bufferedSink); + bufferedSink.flush(); + } + + private Sink sink(Sink sink) { + return new ForwardingSink(sink) { + + long bytesWritten = 0L; + long contentLength = 0L; + + @Override + public void write(Buffer source, long byteCount) throws IOException { + super.write(source, byteCount); + if (contentLength == 0) { + contentLength = contentLength(); + } + + bytesWritten += byteCount; + callback.onUploadProgress(bytesWritten, contentLength, bytesWritten == contentLength); + } + }; + } +} diff --git a/packages/type-safe-api/scripts/type-safe-api/generators/java/templates/client/generic/progressResponseBody.ejs b/packages/type-safe-api/scripts/type-safe-api/generators/java/templates/client/generic/progressResponseBody.ejs new file mode 100644 index 000000000..baa7e5df8 --- /dev/null +++ b/packages/type-safe-api/scripts/type-safe-api/generators/java/templates/client/generic/progressResponseBody.ejs @@ -0,0 +1,67 @@ +###TSAPI_WRITE_FILE### +{ + "dir": "<%- metadata.srcDir %>", + "name": "ProgressResponseBody", + "ext": ".java", + "overwrite": true +} +###/TSAPI_WRITE_FILE###<%- include('../../header.partial.ejs', { info }) %> + + +package <%- metadata.packageName %>; + +import okhttp3.MediaType; +import okhttp3.ResponseBody; + +import java.io.IOException; + +import okio.Buffer; +import okio.BufferedSource; +import okio.ForwardingSource; +import okio.Okio; +import okio.Source; + +public class ProgressResponseBody extends ResponseBody { + + private final ResponseBody responseBody; + private final ApiCallback callback; + private BufferedSource bufferedSource; + + public ProgressResponseBody(ResponseBody responseBody, ApiCallback callback) { + this.responseBody = responseBody; + this.callback = callback; + } + + @Override + public MediaType contentType() { + return responseBody.contentType(); + } + + @Override + public long contentLength() { + return responseBody.contentLength(); + } + + @Override + public BufferedSource source() { + if (bufferedSource == null) { + bufferedSource = Okio.buffer(source(responseBody.source())); + } + return bufferedSource; + } + + private Source source(Source source) { + return new ForwardingSource(source) { + long totalBytesRead = 0L; + + @Override + public long read(Buffer sink, long byteCount) throws IOException { + long bytesRead = super.read(sink, byteCount); + // read() returns the number of bytes read, or -1 if this source is exhausted. + totalBytesRead += bytesRead != -1 ? bytesRead : 0; + callback.onDownloadProgress(totalBytesRead, responseBody.contentLength(), bytesRead == -1); + return bytesRead; + } + }; + } +} diff --git a/packages/type-safe-api/scripts/type-safe-api/generators/java/templates/client/generic/serverConfiguration.ejs b/packages/type-safe-api/scripts/type-safe-api/generators/java/templates/client/generic/serverConfiguration.ejs new file mode 100644 index 000000000..4b9e37ee1 --- /dev/null +++ b/packages/type-safe-api/scripts/type-safe-api/generators/java/templates/client/generic/serverConfiguration.ejs @@ -0,0 +1,65 @@ +###TSAPI_WRITE_FILE### +{ + "dir": "<%- metadata.srcDir %>", + "name": "ServerConfiguration", + "ext": ".java", + "overwrite": true +} +###/TSAPI_WRITE_FILE###package <%- metadata.packageName %>; + +import java.util.Map; + +/** + * Representing a Server configuration. + */ +public class ServerConfiguration { + public String URL; + public String description; + public Map variables; + + /** + * @param URL A URL to the target host. + * @param description A description of the host designated by the URL. + * @param variables A map between a variable name and its value. The value is used for substitution in the server's URL template. + */ + public ServerConfiguration(String URL, String description, Map variables) { + this.URL = URL; + this.description = description; + this.variables = variables; + } + + /** + * Format URL template using given variables. + * + * @param variables A map between a variable name and its value. + * @return Formatted URL. + */ + public String URL(Map variables) { + String url = this.URL; + + // go through variables and replace placeholders + for (Map.Entry variable: this.variables.entrySet()) { + String name = variable.getKey(); + ServerVariable serverVariable = variable.getValue(); + String value = serverVariable.defaultValue; + + if (variables != null && variables.containsKey(name)) { + value = variables.get(name); + if (serverVariable.enumValues.size() > 0 && !serverVariable.enumValues.contains(value)) { + throw new IllegalArgumentException("The variable " + name + " in the server URL has invalid value " + value + "."); + } + } + url = url.replace("{" + name + "}", value); + } + return url; + } + + /** + * Format URL template using default server variables. + * + * @return Formatted URL. + */ + public String URL() { + return URL(null); + } +} diff --git a/packages/type-safe-api/scripts/type-safe-api/generators/java/templates/client/generic/serverVariable.ejs b/packages/type-safe-api/scripts/type-safe-api/generators/java/templates/client/generic/serverVariable.ejs new file mode 100644 index 000000000..b97ec48eb --- /dev/null +++ b/packages/type-safe-api/scripts/type-safe-api/generators/java/templates/client/generic/serverVariable.ejs @@ -0,0 +1,30 @@ +###TSAPI_WRITE_FILE### +{ + "dir": "<%- metadata.srcDir %>", + "name": "ServerVariable", + "ext": ".java", + "overwrite": true +} +###/TSAPI_WRITE_FILE###package <%- metadata.packageName %>; + +import java.util.HashSet; + +/** + * Representing a Server Variable for server URL template substitution. + */ +public class ServerVariable { + public String description; + public String defaultValue; + public HashSet enumValues = null; + + /** + * @param description A description for the server variable. + * @param defaultValue The default value to use for substitution. + * @param enumValues An enumeration of string values to be used if the substitution options are from a limited set. + */ + public ServerVariable(String description, String defaultValue, HashSet enumValues) { + this.description = description; + this.defaultValue = defaultValue; + this.enumValues = enumValues; + } +} diff --git a/packages/type-safe-api/scripts/type-safe-api/generators/java/templates/client/generic/stringUtil.ejs b/packages/type-safe-api/scripts/type-safe-api/generators/java/templates/client/generic/stringUtil.ejs new file mode 100644 index 000000000..a0776ae93 --- /dev/null +++ b/packages/type-safe-api/scripts/type-safe-api/generators/java/templates/client/generic/stringUtil.ejs @@ -0,0 +1,79 @@ +###TSAPI_WRITE_FILE### +{ + "dir": "<%- metadata.srcDir %>", + "name": "StringUtil", + "ext": ".java", + "overwrite": true +} +###/TSAPI_WRITE_FILE###<%- include('../../header.partial.ejs', { info }) %> + + +package <%- metadata.packageName %>; + +import java.util.Collection; +import java.util.Iterator; + +public class StringUtil { + /** + * Check if the given array contains the given value (with case-insensitive comparison). + * + * @param array The array + * @param value The value to search + * @return true if the array contains the value + */ + public static boolean containsIgnoreCase(String[] array, String value) { + for (String str : array) { + if (value == null && str == null) { + return true; + } + if (value != null && value.equalsIgnoreCase(str)) { + return true; + } + } + return false; + } + + /** + * Join an array of strings with the given separator. + *

+ * Note: This might be replaced by utility method from commons-lang or guava someday + * if one of those libraries is added as dependency. + *

+ * + * @param array The array of strings + * @param separator The separator + * @return the resulting string + */ + public static String join(String[] array, String separator) { + int len = array.length; + if (len == 0) { + return ""; + } + + StringBuilder out = new StringBuilder(); + out.append(array[0]); + for (int i = 1; i < len; i++) { + out.append(separator).append(array[i]); + } + return out.toString(); + } + + /** + * Join a list of strings with the given separator. + * + * @param list The list of strings + * @param separator The separator + * @return the resulting string + */ + public static String join(Collection list, String separator) { + Iterator iterator = list.iterator(); + StringBuilder out = new StringBuilder(); + if (iterator.hasNext()) { + out.append(iterator.next()); + } + while (iterator.hasNext()) { + out.append(separator).append(iterator.next()); + } + return out.toString(); + } +} diff --git a/packages/type-safe-api/scripts/type-safe-api/generators/java/templates/client/models/abstractOpenApiSchema.ejs b/packages/type-safe-api/scripts/type-safe-api/generators/java/templates/client/models/abstractOpenApiSchema.ejs new file mode 100644 index 000000000..6c6fd0138 --- /dev/null +++ b/packages/type-safe-api/scripts/type-safe-api/generators/java/templates/client/models/abstractOpenApiSchema.ejs @@ -0,0 +1,146 @@ +###TSAPI_WRITE_FILE### +{ + "dir": "<%- metadata.srcDir %>/model", + "name": "AbstractOpenApiSchema", + "ext": ".java", + "overwrite": true +} +###/TSAPI_WRITE_FILE###<%- include('../../header.partial.ejs', { info }) %> + + +package <%- metadata.packageName %>.model; + +import <%- metadata.packageName %>.ApiException; +import java.util.Objects; +import java.lang.reflect.Type; +import java.util.Map; +import javax.ws.rs.core.GenericType; + +//import com.fasterxml.jackson.annotation.JsonValue; + +/** + * Abstract class for oneOf,anyOf schemas defined in OpenAPI spec + */ +@lombok.AllArgsConstructor @lombok.experimental.SuperBuilder +public abstract class AbstractOpenApiSchema { + + // store the actual instance of the schema/object + private Object instance; + + // is nullable + private Boolean isNullable; + + // schema type (e.g. oneOf, anyOf) + private final String schemaType; + + public AbstractOpenApiSchema(String schemaType, Boolean isNullable) { + this.schemaType = schemaType; + this.isNullable = isNullable; + } + + /** + * Get the list of oneOf/anyOf composed schemas allowed to be stored in this object + * + * @return an instance of the actual schema/object + */ + public abstract Map getSchemas(); + + /** + * Get the actual instance + * + * @return an instance of the actual schema/object + */ + //@JsonValue + public Object getActualInstance() {return instance;} + + /** + * Set the actual instance + * + * @param instance the actual instance of the schema/object + */ + public void setActualInstance(Object instance) {this.instance = instance;} + + /** + * Get the instant recursively when the schemas defined in oneOf/anyof happen to be oneOf/anyOf schema as well + * + * @return an instance of the actual schema/object + */ + public Object getActualInstanceRecursively() { + return getActualInstanceRecursively(this); + } + + private Object getActualInstanceRecursively(AbstractOpenApiSchema object) { + if (object.getActualInstance() == null) { + return null; + } else if (object.getActualInstance() instanceof AbstractOpenApiSchema) { + return getActualInstanceRecursively((AbstractOpenApiSchema)object.getActualInstance()); + } else { + return object.getActualInstance(); + } + } + + /** + * Get the schema type (e.g. anyOf, oneOf) + * + * @return the schema type + */ + public String getSchemaType() { + return schemaType; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class ").append(getClass()).append(" {\n"); + sb.append(" instance: ").append(toIndentedString(instance)).append("\n"); + sb.append(" isNullable: ").append(toIndentedString(isNullable)).append("\n"); + sb.append(" schemaType: ").append(toIndentedString(schemaType)).append("\n"); + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } + + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + AbstractOpenApiSchema a = (AbstractOpenApiSchema) o; + return Objects.equals(this.instance, a.instance) && + Objects.equals(this.isNullable, a.isNullable) && + Objects.equals(this.schemaType, a.schemaType); + } + + @Override + public int hashCode() { + return Objects.hash(instance, isNullable, schemaType); + } + + /** + * Is nullable + * + * @return true if it's nullable + */ + public Boolean isNullable() { + if (Boolean.TRUE.equals(isNullable)) { + return Boolean.TRUE; + } else { + return Boolean.FALSE; + } + } + + + +} diff --git a/packages/type-safe-api/scripts/type-safe-api/generators/java/templates/client/models/models.ejs b/packages/type-safe-api/scripts/type-safe-api/generators/java/templates/client/models/models.ejs new file mode 100644 index 000000000..8c2830704 --- /dev/null +++ b/packages/type-safe-api/scripts/type-safe-api/generators/java/templates/client/models/models.ejs @@ -0,0 +1,647 @@ +<%_ const lowerFirst = (str) => str.charAt(0).toLowerCase() + str.slice(1); _%> +<%_ const modelsByName = Object.fromEntries(models.map(m => [m.name, m])); _%> +<%_ const modelNameToParentModel = Object.fromEntries(models + .filter(m => m.composedModels && m.composedModels.length > 0) + .flatMap(parent => parent.composedModels.map(child => [child.name, parent]))); _%> +<%_ /* Filter out models which are hoisted children of all-ofs, since for java we mix in child properties */ _%> +<%_ models.filter(m => !(m.isHoisted && modelNameToParentModel[m.name] && modelNameToParentModel[m.name].export === "all-of")).forEach((model) => { _%> +<%_ const getRecursiveAllOfChildren = (m) => m.export === "all-of" ? [...m.composedModels, ...m.composedModels.flatMap(c => getRecursiveAllOfChildren(c))] : []; _%> +<%_ const recursiveAllOfChildren = getRecursiveAllOfChildren(model); _%> +<%_ const properties = [ + // For all-of models, filter out the properties which are composed models, since we're mixing in child properties instead + ...model.properties.filter(p => model.export !== "all-of" || p.name), + ...recursiveAllOfChildren.flatMap(m => m.properties.filter(p => model.export !== "all-of" || p.name)), +]; _%> +<%_ const propertyTypes = new Set(properties.map(p => p.type)); _%> +<%_ const unique = (items) => { + const seen = new Set(); + const res = []; + items.forEach(item => { + if (!seen.has(item)) { + res.push(item); + seen.add(item); + } + }); + return res; +}; _%> +<%_ const uniqueImports = unique([ + ...model.uniqueImports, + ...recursiveAllOfChildren.flatMap(m => m.uniqueImports), + // Filter out imports that aren't referenced (for all-of where we want to import the child model's properties rather than the child models) +]).filter(uniqueImport => propertyTypes.has(uniqueImport)); _%> +###TSAPI_WRITE_FILE### +{ + "dir": "<%- metadata.srcDir %>/model", + "name": "<%- model.name %>", + "ext": ".java", + "overwrite": true +} +###/TSAPI_WRITE_FILE###<%- include('../../header.partial.ejs', { info }) %> + + +package <%- metadata.packageName %>.model; + +import java.util.Objects; +import java.util.Arrays; +<%_ uniqueImports.forEach((importName) => { _%> +import <%- metadata.packageName %>.model.<%- importName %>; +<%_ }); _%> +import com.google.gson.TypeAdapter; +import com.google.gson.annotations.JsonAdapter; +import com.google.gson.annotations.SerializedName; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonWriter; +import java.io.IOException; + +import javax.ws.rs.core.GenericType; + +import java.io.IOException; +import java.io.File; +import java.math.BigDecimal; +import java.net.URI; +import java.time.LocalDate; +import java.time.OffsetDateTime; +import java.util.UUID; +import java.lang.reflect.Type; +import java.util.logging.Level; +import java.util.logging.Logger; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.HashMap; +import java.util.Map; +import java.util.Map.Entry; +import java.util.List; +import java.util.Set; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.JsonArray; +import com.google.gson.JsonParseException; +import com.google.gson.TypeAdapter; +import com.google.gson.TypeAdapterFactory; +import com.google.gson.reflect.TypeToken; +import com.google.gson.JsonPrimitive; +import com.google.gson.annotations.JsonAdapter; +import com.google.gson.annotations.SerializedName; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonWriter; +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonDeserializer; +import com.google.gson.JsonSerializationContext; +import com.google.gson.JsonSerializer; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParseException; + +import <%- metadata.packageName %>.JSON; + +<%_ if (model.export === "enum") { _%> +/** + * Gets or Sets <%- model.name %> + */ +@JsonAdapter(<%- model.name %>.Adapter.class) +public enum <%- model.name %> { + <%_ model.enum.forEach((e, i) => { _%> + + <%- e.name %>("<%- e.value.replace(/["']/g, '') %>")<% if (i < model.enum.length - 1) { %>,<% } else { %>;<% } %> + <%_ }); _%> + + private String value; + + <%- model.name %>(String value) { + this.value = value; + } + + public String getValue() { + return value; + } + + @Override + public String toString() { + return String.valueOf(value); + } + + public static <%- model.name %> fromValue(String value) { + for (<%- model.name %> b : <%- model.name %>.values()) { + if (b.value.equals(value)) { + return b; + } + } + throw new IllegalArgumentException("Unexpected value '" + value + "'"); + } + + public static class Adapter extends TypeAdapter<<%- model.name %>> { + @Override + public void write(final JsonWriter jsonWriter, final <%- model.name %> enumeration) throws IOException { + jsonWriter.value(enumeration.getValue()); + } + + @Override + public <%- model.name %> read(final JsonReader jsonReader) throws IOException { + String value = jsonReader.nextString(); + return <%- model.name %>.fromValue(value); + } + } +} +<%_ } else if (model.export === "one-of" || model.export === "any-of") { _%> +@lombok.experimental.SuperBuilder +public class <%- model.name %> extends AbstractOpenApiSchema { + private static final Logger log = Logger.getLogger(<%- model.name %>.class.getName()); + + public static class CustomTypeAdapterFactory implements TypeAdapterFactory { + @SuppressWarnings("unchecked") + @Override + public TypeAdapter create(Gson gson, TypeToken type) { + if (!<%- model.name %>.class.isAssignableFrom(type.getRawType())) { + return null; // this class only serializes '<%- model.name %>' and its subtypes + } + final TypeAdapter elementAdapter = gson.getAdapter(JsonElement.class); + <%_ properties.forEach((composedType) => { _%> + final TypeAdapter<<%- composedType.javaType %>> adapter<%- composedType.javaType %> = gson.getDelegateAdapter(this, TypeToken.get(<%- composedType.javaType %>.class)); + <%_ }); _%> + + return (TypeAdapter) new TypeAdapter<<%- model.name %>>() { + @Override + public void write(JsonWriter out, <%- model.name %> value) throws IOException { + if (value == null || value.getActualInstance() == null) { + elementAdapter.write(out, null); + return; + } + + <%_ properties.forEach((composedType) => { _%> + // check if the actual instance is of the type `<%- composedType.javaType %>` + if (value.getActualInstance() instanceof <%- composedType.javaType %>) { + JsonObject obj = adapter<%- composedType.javaType %>.toJsonTree((<%- composedType.javaType %>)value.getActualInstance()).getAsJsonObject(); + elementAdapter.write(out, obj); + return; + } + + <%_ }); _%> + throw new IOException("Failed to serialize as the type doesn't match oneOf schemas: <%- properties.map((p) => p.javaType).join(', ') %>"); + } + + @Override + public <%- model.name %> read(JsonReader in) throws IOException { + Object deserialized = null; + JsonObject jsonObject = elementAdapter.read(in).getAsJsonObject(); + + <%_ if (model.export === "one-of") { _%> + int match = 0; + ArrayList errorMessages = new ArrayList<>(); + TypeAdapter actualAdapter = elementAdapter; + <%_ } _%> + + <%_ properties.forEach((composedType) => { _%> + // deserialize <%- composedType.javaType %> + try { + // validate the JSON object to see if any exception is thrown + <%- composedType.javaType %>.validateJsonObject(jsonObject); + <%_ if (model.export === "one-of") { _%> + actualAdapter = adapter<%- composedType.javaType %>; + match++; + <%_ } _%> + log.log(Level.FINER, "Input data matches schema '<%- composedType.javaType %>'"); + <%_ if (model.export === "any-of") { _%> + <%- model.name %> ret = new <%- model.name %>(); + ret.setActualInstance(adapter<%- composedType.javaType %>.fromJsonTree(jsonObject)); + return ret; + <%_ } _%> + } catch (Exception e) { + // deserialization failed, continue + <%_ if (model.export === "one-of") { _%> + errorMessages.add(String.format("Deserialization for <%- composedType.javaType %> failed with `%s`.", e.getMessage())); + <%_ } _%> + log.log(Level.FINER, "Input data does not match schema '<%- composedType.javaType %>'", e); + } + + <%_ }); _%> + <%_ if (model.export === "one-of") { _%> + if (match == 1) { + <%- model.name %> ret = new <%- model.name %>(); + ret.setActualInstance(actualAdapter.fromJsonTree(jsonObject)); + return ret; + } + <%_ } _%> + + <%_ if (model.export === "one-of") { _%> + throw new IOException(String.format("Failed deserialization for <%- model.name %>: %d classes match result, expected 1. Detailed failure message for oneOf schemas: %s. JSON: %s", match, errorMessages, jsonObject.toString())); + <%_ } else { _%> + throw new IOException(String.format("Failed deserialization for <%- model.name %>: no class matched. JSON: %s", jsonObject.toString())); + <%_ } _%> + } + }.nullSafe(); + } + } + + // store a list of schema names defined in <%- model.export === "one-of" ? "oneOf" : "anyOf" %> + public static final Map schemas = new HashMap(); + + public <%- model.name %>() { + super("<%- model.export === "one-of" ? "oneOf" : "anyOf" %>", <% if (model.isNullable) { %>Boolean.TRUE<% } else { %>Boolean.FALSE<% } %>); + } + + <%_ properties.forEach((composedType) => { _%> + public <%- model.name %>(<%- composedType.javaType %> o) { + super("<%- model.export === "one-of" ? "oneOf" : "anyOf" %>", <% if (model.isNullable) { %>Boolean.TRUE<% } else { %>Boolean.FALSE<% } %>); + setActualInstance(o); + } + + <%_ }); _%> + static { + <%_ properties.forEach((composedType) => { _%> + schemas.put("<%- composedType.javaType %>", new GenericType<<%- composedType.javaType %>>() { + }); + <%_ }); _%> + } + + @Override + public Map getSchemas() { + return <%- model.name %>.schemas; + } + + /** + * Set the instance that matches the <%- model.export === "one-of" ? "oneOf" : "anyOf" %> child schema, check + * the instance parameter is valid against the <%- model.export === "one-of" ? "oneOf" : "anyOf" %> child schemas: + * <%- properties.map((p) => p.javaType).join(', ') %> + * + * It could be an instance of the '<%- model.export === "one-of" ? "oneOf" : "anyOf" %>' schemas. + * The <%- model.export === "one-of" ? "oneOf" : "anyOf" %> child schemas may themselves be a composed schema (allOf, anyOf, oneOf). + */ + @Override + public void setActualInstance(Object instance) { + <%_ if (model.isNullable) { _%> + if (instance == null) { + super.setActualInstance(instance); + return; + } + + <%_ } _%> + <%_ properties.forEach((composedType) => { _%> + if (instance instanceof <%- composedType.javaType %>) { + super.setActualInstance(instance); + return; + } + + <%_ }); _%> + throw new RuntimeException("Invalid instance type. Must be <%- properties.map((p) => p.javaType).join(', ') %>"); + } + + /** + * Get the actual instance, which can be the following: + * <%- properties.map((p) => p.javaType).join(', ') %> + * + * @return The actual instance (<%- properties.map((p) => p.javaType).join(', ') %>) + */ + @Override + public Object getActualInstance() { + return super.getActualInstance(); + } + + <%_ properties.forEach((composedType) => { _%> + /** + * Get the actual instance of `<%- composedType.javaType %>`. If the actual instance is not `<%- composedType.javaType %>`, + * the ClassCastException will be thrown. + * + * @return The actual instance of `<%- composedType.javaType %>` + * @throws ClassCastException if the instance is not `<%- composedType.javaType %>` + */ + public <%- composedType.javaType %> get<%- composedType.javaType %>() throws ClassCastException { + return (<%- composedType.javaType %>)super.getActualInstance(); + } + + <%_ }); _%> + + /** + * Validates the JSON Object and throws an exception if issues found + * + * @param jsonObj JSON Object + * @throws IOException if the JSON Object is invalid with respect to <%- model.name %> + */ + public static void validateJsonObject(JsonObject jsonObj) throws IOException { + // validate <%- model.export === "one-of" ? "oneOf" : "anyOf" %> schemas one by one + int validCount = 0; + <%_ if (model.export === "one-of") { _%> + ArrayList errorMessages = new ArrayList<>(); + <%_ } _%> + <%_ properties.forEach((composedType) => { _%> + // validate the json string with <%- composedType.javaType %> + try { + <%- composedType.javaType %>.validateJsonObject(jsonObj); + <%_ if (model.export === "one-of") { _%> + validCount++; + <%_ } else { _%> + return; // return earlier as at least one schema is valid with respect to the Json object + //validCount++; + <%_ } _%> + } catch (Exception e) { + <%_ if (model.export === "one-of") { _%> + errorMessages.add(String.format("Deserialization for <%- composedType.javaType %> failed with `%s`.", e.getMessage())); + <%_ } _%> + // continue to the next one + } + <%_ }); _%> + <%_ if (model.export === "one-of") { _%> + if (validCount != 1) { + throw new IOException(String.format("The JSON string is invalid for <%- model.name %> with oneOf schemas: <%- properties.map((p) => p.javaType).join(', ') %>. %d class(es) match the result, expected 1. Detailed failure message for oneOf schemas: %s. JSON: %s", validCount, errorMessages, jsonObj.toString())); + } + <%_ } else { _%> + if (validCount == 0) { + throw new IOException(String.format("The JSON string is invalid for <%- model.name %> with anyOf schemas: <%- properties.map((p) => p.javaType).join(', ') %>. JSON: %s", jsonObj.toString())); + } + <%_ } _%> + } + + /** + * Create an instance of <%- model.name %> given an JSON string + * + * @param jsonString JSON string + * @return An instance of <%- model.name %> + * @throws IOException if the JSON string is invalid with respect to <%- model.name %> + */ + public static <%- model.name %> fromJson(String jsonString) throws IOException { + return JSON.getGson().fromJson(jsonString, <%- model.name %>.class); + } + + /** + * Convert an instance of <%- model.name %> to an JSON string + * + * @return JSON string + */ + public String toJson() { + return JSON.getGson().toJson(this); + } + +} +<%_ } else { _%> +/** + * <%- model.description || model.name %> + <%_ if (model.isDeprecated) { _%> + * @deprecated + <%_ } _%> + */ +@lombok.AllArgsConstructor @lombok.experimental.SuperBuilder +public class <%- model.name %> { + <%_ properties.forEach((property) => { _%> + public static final String SERIALIZED_NAME_<%- property.pythonName.toUpperCase() %> = "<%- property.name %>"; + @SerializedName(SERIALIZED_NAME_<%- property.pythonName.toUpperCase() %>) + <%_ if (property.export === "array" || property.export === "dictionary") { _%> + private <%- property.javaType %> <%- property.javaName %> = <% if (property.isRequired) { %>new <%- property.export === "array" ? 'ArrayList' : 'HashMap' %><>()<% } else { %>null<% } %>; + <%_ } else { _%> + private <%- property.javaType %> <%- property.javaName %><% if (property.type === "any") { %> = null<% } %>; + <%_ } _%> + + <%_ }); _%> + public <%- model.name %>() { + } + + <%_ properties.forEach((property) => { _%> + public <%- model.name %> <%- property.javaName %>(<%- property.javaType %> <%- property.javaName %>) { + + this.<%- property.javaName %> = <%- property.javaName %>; + return this; + } + <%_ if (property.export === "array") { _%> + + public <%- model.name %> <%- include('../../getterSetter.partial.ejs', { prefix: 'add', name: property.javaName }) %>Item(<%- property.link.javaType %> <%- property.javaName %>Item) { + <%_ if (!property.isRequired) { _%> + if (this.<%- property.javaName %> == null) { + this.<%- property.javaName %> = new ArrayList<>(); + } + <%_ } _%> + this.<%- property.javaName %>.add(<%- property.javaName %>Item); + return this; + } + <%_ } else if (property.export === "dictionary") { _%> + + public <%- model.name %> <%- include('../../getterSetter.partial.ejs', { prefix: 'put', name: property.javaName }) %>Item(String key, <%- property.link.javaType %> <%- property.javaName %>Item) { + <%_ if (!property.isRequired) { _%> + if (this.<%- property.javaName %> == null) { + this.<%- property.javaName %> = new HashMap<>(); + } + <%_ } _%> + this.<%- property.javaName %>.put(key, <%- property.javaName %>Item); + return this; + } + <%_ } _%> + + /** + * <% if (property.description) { %><%- property.description %><% } else { %>Get <%- property.javaName %><% } %> + * @return <%- property.javaName %> + **/ + @javax.annotation.<%- property.isNullable || !property.isRequired ? 'Nullable' : 'Nonnull' %> + public <%- property.javaType %> <%- include('../../getterSetter.partial.ejs', { prefix: 'get', name: property.javaName }) %>() { + return <%- property.javaName %>; + } + + + public void <%- include('../../getterSetter.partial.ejs', { prefix: 'set', name: property.javaName }) %>(<%- property.javaType %> <%- property.javaName %>) { + this.<%- property.javaName %> = <%- property.javaName %>; + } + + <%_ }); _%> + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + <%- model.name %> <%- lowerFirst(model.name) %> = (<%- model.name %>) o; + return <% properties.forEach((property, i) => { %>Objects.equals(this.<%- property.javaName %>, <%- lowerFirst(model.name) %>.<%- property.javaName %>)<% if (i < properties.length - 1) { %> &&<% } else { %>;<% } %> + <% }); %> + } + + @Override + public int hashCode() { + return Objects.hash(<%- properties.map((property) => property.javaName).join(', ') %>); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class <%- model.name %> {\n"); + <%_ properties.forEach((property) => { _%> + sb.append(" <%- property.javaName %>: ").append(toIndentedString(<%- property.javaName %>)).append("\n"); + <%_ }); _%> + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } + + + public static HashSet openapiFields; + public static HashSet openapiRequiredFields; + + static { + // a set of all properties/fields (JSON key names) + openapiFields = new HashSet(); + <%_ properties.forEach((property) => { _%> + openapiFields.add("<%- property.name %>"); + <%_ }); _%> + + // a set of required properties/fields (JSON key names) + openapiRequiredFields = new HashSet(); + <%_ properties.filter(p => p.isRequired).forEach((property) => { _%> + openapiRequiredFields.add("<%- property.name %>"); + <%_ }); _%> + } + + /** + * Validates the JSON Object and throws an exception if issues found + * + * @param jsonObj JSON Object + * @throws IOException if the JSON Object is invalid with respect to <%- model.name %> + */ + public static void validateJsonObject(JsonObject jsonObj) throws IOException { + if (jsonObj == null) { + if (!<%- model.name %>.openapiRequiredFields.isEmpty()) { // has required fields but JSON object is null + throw new IllegalArgumentException(String.format("The required field(s) %s in <%- model.name %> is not found in the empty JSON string", <%- model.name %>.openapiRequiredFields.toString())); + } + } + + Set> entries = jsonObj.entrySet(); + // check to see if the JSON string contains additional fields + for (Entry entry : entries) { + if (!<%- model.name %>.openapiFields.contains(entry.getKey())) { + throw new IllegalArgumentException(String.format("The field `%s` in the JSON string is not defined in the `<%- model.name %>` properties. JSON: %s", entry.getKey(), jsonObj.toString())); + } + } + <%_ if (properties.filter(p => p.isRequired).length > 0) { _%> + + // check to make sure all required properties/fields are present in the JSON string + for (String requiredField : <%- model.name %>.openapiRequiredFields) { + if (jsonObj.get(requiredField) == null) { + throw new IllegalArgumentException(String.format("The required field `%s` is not found in the JSON string: %s", requiredField, jsonObj.toString())); + } + } + <%_ } _%> + <%_ properties.forEach((property) => { _%> + <%_ if (property.export === "array") { _%> + <%_ if (!property.link.isPrimitive) { _%> + <%_ if (property.isRequired) { _%> + // ensure the json data is an array + if (!jsonObj.get("<%- property.name %>").isJsonArray()) { + throw new IllegalArgumentException(String.format("Expected the field `<%- property.name %>` to be an array in the JSON string but got `%s`", jsonObj.get("<%- property.name %>").toString())); + } + + JsonArray jsonArray<%- property.javaName %> = jsonObj.getAsJsonArray("<%- property.name %>"); + // validate the required field `<%- property.name %>` (array) + for (int i = 0; i < jsonArray<%- property.javaName %>.size(); i++) { + <%- property.link.javaType %>.validateJsonObject(jsonArray<%- property.javaName %>.get(i).getAsJsonObject()); + }; + <%_ } else { _%> + if (jsonObj.get("<%- property.name %>") != null && !jsonObj.get("<%- property.name %>").isJsonNull()) { + JsonArray jsonArray<%- property.javaName %> = jsonObj.getAsJsonArray("<%- property.name %>"); + if (jsonArray<%- property.javaName %> != null) { + // ensure the json data is an array + if (!jsonObj.get("<%- property.name %>").isJsonArray()) { + throw new IllegalArgumentException(String.format("Expected the field `<%- property.name %>` to be an array in the JSON string but got `%s`", jsonObj.get("<%- property.name %>").toString())); + } + + // validate the optional field `<%- property.name %>` (array) + for (int i = 0; i < jsonArray<%- property.javaName %>.size(); i++) { + <%- property.link.javaType %>.validateJsonObject(jsonArray<%- property.javaName %>.get(i).getAsJsonObject()); + }; + } + } + <%_ } _%> + <%_ } else { _%> + <%_ if (!property.isRequired) { _%> + // ensure the optional json data is an array if present + if (jsonObj.get("<%- property.name %>") != null && !jsonObj.get("<%- property.name %>").isJsonArray()) { + throw new IllegalArgumentException(String.format("Expected the field `<%- property.name %>` to be an array in the JSON string but got `%s`", jsonObj.get("<%- property.name %>").toString())); + } + <%_ } else { _%> + // ensure the required json array is present + if (jsonObj.get("<%- property.name %>") == null) { + throw new IllegalArgumentException("Expected the field `linkedContent` to be an array in the JSON string but got `null`"); + } else if (!jsonObj.get("<%- property.name %>").isJsonArray()) { + throw new IllegalArgumentException(String.format("Expected the field `<%- property.name %>` to be an array in the JSON string but got `%s`", jsonObj.get("<%- property.name %>").toString())); + } + <%_ } _%> + <%_ } _%> + <%_ } _%> + <%_ if (property.isPrimitive) { _%> + <%_ if (property.type === "string") { _%> + if (<% if (!property.isRequired) { %>(jsonObj.get("<%- property.name %>") != null && !jsonObj.get("<%- property.name %>").isJsonNull()) && <% } %>!jsonObj.get("<%- property.name %>").isJsonPrimitive()) { + throw new IllegalArgumentException(String.format("Expected the field `<%- property.name %>` to be a primitive type in the JSON string but got `%s`", jsonObj.get("<%- property.name %>").toString())); + } + <%_ } _%> + <%_ } else if (property.export !== "array" && property.export !== "dictionary") { _%> + <%_ if (property.isRequired) { _%> + // validate the required field `<%- property.name %>` + <%- property.javaType %>.validateJsonObject(jsonObj.getAsJsonObject("<%- property.name %>")); + <%_ } else { _%> + // validate the optional field `<%- property.name %>` + if (jsonObj.get("<%- property.name %>") != null && !jsonObj.get("<%- property.name %>").isJsonNull()) { + <%- property.javaType %>.validateJsonObject(jsonObj.getAsJsonObject("<%- property.name %>")); + } + <%_ } _%> + <%_ } _%> + <%_ }); _%> + } + + public static class CustomTypeAdapterFactory implements TypeAdapterFactory { + @SuppressWarnings("unchecked") + @Override + public TypeAdapter create(Gson gson, TypeToken type) { + if (!<%- model.name %>.class.isAssignableFrom(type.getRawType())) { + return null; // this class only serializes '<%- model.name %>' and its subtypes + } + final TypeAdapter elementAdapter = gson.getAdapter(JsonElement.class); + final TypeAdapter<<%- model.name %>> thisAdapter + = gson.getDelegateAdapter(this, TypeToken.get(<%- model.name %>.class)); + + return (TypeAdapter) new TypeAdapter<<%- model.name %>>() { + @Override + public void write(JsonWriter out, <%- model.name %> value) throws IOException { + JsonObject obj = thisAdapter.toJsonTree(value).getAsJsonObject(); + elementAdapter.write(out, obj); + } + + @Override + public <%- model.name %> read(JsonReader in) throws IOException { + JsonObject jsonObj = elementAdapter.read(in).getAsJsonObject(); + validateJsonObject(jsonObj); + return thisAdapter.fromJsonTree(jsonObj); + } + + }.nullSafe(); + } + } + + /** + * Create an instance of <%- model.name %> given an JSON string + * + * @param jsonString JSON string + * @return An instance of <%- model.name %> + * @throws IOException if the JSON string is invalid with respect to <%- model.name %> + */ + public static <%- model.name %> fromJson(String jsonString) throws IOException { + return JSON.getGson().fromJson(jsonString, <%- model.name %>.class); + } + + /** + * Convert an instance of <%- model.name %> to an JSON string + * + * @return JSON string + */ + public String toJson() { + return JSON.getGson().toJson(this); + } +} +<%_ } _%> +<%_ }); _%> \ No newline at end of file diff --git a/packages/type-safe-api/scripts/type-safe-api/generators/java/templates/getterSetter.partial.ejs b/packages/type-safe-api/scripts/type-safe-api/generators/java/templates/getterSetter.partial.ejs new file mode 100644 index 000000000..01cb35f01 --- /dev/null +++ b/packages/type-safe-api/scripts/type-safe-api/generators/java/templates/getterSetter.partial.ejs @@ -0,0 +1,14 @@ +<%_ +const getterSetterCapitalize = (str) => { + // De-escape reserved names as we'll add a prefix + if (str.startsWith('_')) { + str = str.slice(1); + } + if (str.length > 1 && str.charAt(0).toLowerCase() === str.charAt(0) && str.charAt(1).toUpperCase() === str.charAt(1)) { + return str; + } + return str.charAt(0).toUpperCase() + str.slice(1); +}; +const capitalized = getterSetterCapitalize(name); +_%> +<%- prefix %><%- capitalized -%> \ No newline at end of file diff --git a/packages/type-safe-api/scripts/type-safe-api/generators/java/templates/header.partial.ejs b/packages/type-safe-api/scripts/type-safe-api/generators/java/templates/header.partial.ejs new file mode 100644 index 000000000..2518c7298 --- /dev/null +++ b/packages/type-safe-api/scripts/type-safe-api/generators/java/templates/header.partial.ejs @@ -0,0 +1,10 @@ +/* + * <%- info.title %> + * <%- info.description || '' %> + * + * The version of the OpenAPI document: <%- info.version %> + * + * + * NOTE: This class is auto generated. + * Do not edit the class manually. + */ \ No newline at end of file diff --git a/packages/type-safe-api/scripts/type-safe-api/generators/java/templates/operationConfig.mustache b/packages/type-safe-api/scripts/type-safe-api/generators/java/templates/operationConfig.mustache deleted file mode 100644 index 852464e43..000000000 --- a/packages/type-safe-api/scripts/type-safe-api/generators/java/templates/operationConfig.mustache +++ /dev/null @@ -1,50 +0,0 @@ -{{#apiInfo}} -{{#apis.0}} -package {{package}}.operation_config; -{{/apis.0}} -{{/apiInfo}} - -{{#apiInfo}} -{{#apis.0}} -import {{modelPackage}}.*; -{{/apis.0}} -{{/apiInfo}} - -{{#apiInfo}} -{{#apis}} -{{#imports}}import {{import}}; -{{/imports}} -{{/apis}} -{{/apiInfo}} - -import java.util.HashMap; -import java.util.Map; - -// Generic type for object "keyed" by operation names -{{>generatedAnnotation}} -@lombok.Builder @lombok.Getter -public class OperationConfig { - {{#apiInfo}} - {{#apis}} - {{#operations}} - {{#operation}} - private T {{nickname}}; - {{/operation}} - {{/operations}} - {{/apis}} - {{/apiInfo}} - - public Map asMap() { - Map map = new HashMap<>(); - {{#apiInfo}} - {{#apis}} - {{#operations}} - {{#operation}} - map.put("{{nickname}}", this.{{nickname}}); - {{/operation}} - {{/operations}} - {{/apis}} - {{/apiInfo}} - return map; - } -} diff --git a/packages/type-safe-api/scripts/type-safe-api/generators/java/templates/operationLookup.mustache b/packages/type-safe-api/scripts/type-safe-api/generators/java/templates/operationLookup.mustache deleted file mode 100644 index f638a7925..000000000 --- a/packages/type-safe-api/scripts/type-safe-api/generators/java/templates/operationLookup.mustache +++ /dev/null @@ -1,58 +0,0 @@ -{{#apiInfo}} -{{#apis.0}} -package {{package}}.operation_config; -{{/apis.0}} -{{/apiInfo}} - -{{#apiInfo}} -{{#apis.0}} -import {{modelPackage}}.*; -{{/apis.0}} -{{/apiInfo}} - -{{#apiInfo}} -{{#apis}} -{{#imports}}import {{import}}; -{{/imports}} -{{/apis}} -{{/apiInfo}} - -import java.util.HashMap; -import java.util.Map; -import java.util.List; -import java.util.Arrays; - - -// Look up path and http method for a given operation name -{{>generatedAnnotation}} -public class OperationLookup { - @lombok.Builder @lombok.Getter - public static class OperationLookupEntry { - private String method; - private String path; - private List contentTypes; - } - - /** - * Returns the operation lookup information for the TypeSafeRestApi construct - */ - public static Map getOperationLookup() { - final Map config = new HashMap<>(); - - {{#apiInfo}} - {{#apis}} - {{#operations}} - {{#operation}} - config.put("{{nickname}}", OperationLookupEntry.builder() - .path("{{path}}") - .method("{{httpMethod}}") - .contentTypes(Arrays.asList({{^consumes}}"application/json"{{/consumes}}{{#consumes}}{{#mediaType}}"{{{.}}}"{{^-last}},{{/-last}}{{/mediaType}}{{/consumes}})) - .build()); - {{/operation}} - {{/operations}} - {{/apis}} - {{/apiInfo}} - - return config; - } -} diff --git a/packages/type-safe-api/scripts/type-safe-api/generators/java/templates/handlers.mustache b/packages/type-safe-api/scripts/type-safe-api/generators/java/templates/server/handlers.ejs similarity index 70% rename from packages/type-safe-api/scripts/type-safe-api/generators/java/templates/handlers.mustache rename to packages/type-safe-api/scripts/type-safe-api/generators/java/templates/server/handlers.ejs index 90eec5589..0ebb093c2 100644 --- a/packages/type-safe-api/scripts/type-safe-api/generators/java/templates/handlers.mustache +++ b/packages/type-safe-api/scripts/type-safe-api/generators/java/templates/server/handlers.ejs @@ -1,23 +1,15 @@ -###TSAPI_SPLIT_FILE### ###TSAPI_WRITE_FILE### { - "dir": "api/handlers", + "dir": "<%- metadata.srcDir %>/api/handlers", "name": "Handlers", "ext": ".java", "overwrite": true } -###/TSAPI_WRITE_FILE###{{#apiInfo}} -{{#apis.0}} -package {{package}}.handlers; -{{/apis.0}} -{{/apiInfo}} - -{{#apiInfo}} -{{#apis.0}} -import {{modelPackage}}.*; -import {{package}}.interceptors.ResponseHeadersInterceptor; -{{/apis.0}} -{{/apiInfo}} +###/TSAPI_WRITE_FILE### +package <%- metadata.packageName %>.api.handlers; + +import <%- metadata.packageName %>.model.*; +import <%- metadata.packageName %>.api.interceptors.ResponseHeadersInterceptor; import java.util.Arrays; import java.util.Optional; @@ -46,20 +38,8 @@ import com.amazonaws.services.lambda.runtime.RequestHandler; import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent; import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent; -{{#apiInfo}} -{{#apis}} -{{#imports}}import {{import}}; -{{/imports}} -{{/apis}} -{{/apiInfo}} - -{{#apiInfo}} -{{#apis.0}} -import {{invokerPackage}}.JSON; -{{/apis.0}} -{{/apiInfo}} +import <%- metadata.packageName %>.JSON; -{{>generatedAnnotation}} public class Handlers { static { @@ -431,16 +411,13 @@ public class Handlers { } ###TSAPI_WRITE_FILE### { - "dir": "api/handlers", + "dir": "<%- metadata.srcDir %>/api/handlers", "name": "Response", "ext": ".java", "overwrite": true } -###/TSAPI_WRITE_FILE###{{#apiInfo}} -{{#apis.0}} -package {{package}}.handlers; -{{/apis.0}} -{{/apiInfo}} +###/TSAPI_WRITE_FILE### +package <%- metadata.packageName %>.api.handlers; import java.util.Map; import java.util.List; @@ -468,16 +445,13 @@ public interface Response { } ###TSAPI_WRITE_FILE### { - "dir": "api/handlers", + "dir": "<%- metadata.srcDir %>/api/handlers", "name": "ApiResponse", "ext": ".java", "overwrite": true } -###/TSAPI_WRITE_FILE###{{#apiInfo}} -{{#apis.0}} -package {{package}}.handlers; -{{/apis.0}} -{{/apiInfo}} +###/TSAPI_WRITE_FILE### +package <%- metadata.packageName %>.api.handlers; import java.util.Map; import java.util.List; @@ -493,16 +467,13 @@ public class ApiResponse implements Response { } ###TSAPI_WRITE_FILE### { - "dir": "api/handlers", + "dir": "<%- metadata.srcDir %>/api/handlers", "name": "Interceptor", "ext": ".java", "overwrite": true } -###/TSAPI_WRITE_FILE###{{#apiInfo}} -{{#apis.0}} -package {{package}}.handlers; -{{/apis.0}} -{{/apiInfo}} +###/TSAPI_WRITE_FILE### +package <%- metadata.packageName %>.api.handlers; /** * Interceptors can perform generic operations on requests and/or responses, optionally delegating to the remainder @@ -517,16 +488,13 @@ public interface Interceptor { } ###TSAPI_WRITE_FILE### { - "dir": "api/handlers", + "dir": "<%- metadata.srcDir %>/api/handlers", "name": "Interceptors", "ext": ".java", "overwrite": true } -###/TSAPI_WRITE_FILE###{{#apiInfo}} -{{#apis.0}} -package {{package}}.handlers; -{{/apis.0}} -{{/apiInfo}} +###/TSAPI_WRITE_FILE### +package <%- metadata.packageName %>.api.handlers; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; @@ -544,16 +512,13 @@ public @interface Interceptors { } ###TSAPI_WRITE_FILE### { - "dir": "api/handlers", + "dir": "<%- metadata.srcDir %>/api/handlers", "name": "HandlerChain", "ext": ".java", "overwrite": true } -###/TSAPI_WRITE_FILE###{{#apiInfo}} -{{#apis.0}} -package {{package}}.handlers; -{{/apis.0}} -{{/apiInfo}} +###/TSAPI_WRITE_FILE### +package <%- metadata.packageName %>.api.handlers; /** * A handler chain represents a series of interceptors, which may or may not delegate to following interceptors. * The lambda handler is always the last method in the chain. @@ -566,16 +531,13 @@ public interface HandlerChain { } ###TSAPI_WRITE_FILE### { - "dir": "api/handlers", + "dir": "<%- metadata.srcDir %>/api/handlers", "name": "RequestInput", "ext": ".java", "overwrite": true } -###/TSAPI_WRITE_FILE###{{#apiInfo}} -{{#apis.0}} -package {{package}}.handlers; -{{/apis.0}} -{{/apiInfo}} +###/TSAPI_WRITE_FILE### +package <%- metadata.packageName %>.api.handlers; import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent; import com.amazonaws.services.lambda.runtime.Context; @@ -605,16 +567,13 @@ public interface RequestInput { } ###TSAPI_WRITE_FILE### { - "dir": "api/handlers", + "dir": "<%- metadata.srcDir %>/api/handlers", "name": "ChainedRequestInput", "ext": ".java", "overwrite": true } -###/TSAPI_WRITE_FILE###{{#apiInfo}} -{{#apis.0}} -package {{package}}.handlers; -{{/apis.0}} -{{/apiInfo}} +###/TSAPI_WRITE_FILE### +package <%- metadata.packageName %>.api.handlers; /** * Reqeust input with a handler chain @@ -627,16 +586,13 @@ public interface ChainedRequestInput extends RequestInput { } ###TSAPI_WRITE_FILE### { - "dir": "api/handlers", + "dir": "<%- metadata.srcDir %>/api/handlers", "name": "InterceptorWarmupChainedRequestInput", "ext": ".java", "overwrite": true } -###/TSAPI_WRITE_FILE###{{#apiInfo}} -{{#apis.0}} -package {{package}}.handlers; -{{/apis.0}} -{{/apiInfo}} +###/TSAPI_WRITE_FILE### +package <%- metadata.packageName %>.api.handlers; import com.amazonaws.services.lambda.runtime.ClientContext; import com.amazonaws.services.lambda.runtime.CognitoIdentity; @@ -762,16 +718,13 @@ public class InterceptorWarmupChainedRequestInput implements ChainedRequestIn } ###TSAPI_WRITE_FILE### { - "dir": "api/handlers", + "dir": "<%- metadata.srcDir %>/api/handlers", "name": "InterceptorWithWarmup", "ext": ".java", "overwrite": true } -###/TSAPI_WRITE_FILE###{{#apiInfo}} -{{#apis.0}} -package {{package}}.handlers; -{{/apis.0}} -{{/apiInfo}} +###/TSAPI_WRITE_FILE### +package <%- metadata.packageName %>.api.handlers; import org.crac.Resource; import org.crac.Core; @@ -804,69 +757,46 @@ public abstract class InterceptorWithWarmup implements Interceptor()); } } -{{#apiInfo}} -{{#apis}} -{{#operations}} -{{#operation}} +<%_ allOperations.forEach((operation) => { _%> ###TSAPI_WRITE_FILE### { - "dir": "api/handlers/{{operationIdSnakeCase}}", - "name": "{{operationIdCamelCase}}Response", + "dir": "<%- metadata.srcDir %>/api/handlers/<%- operation.operationIdSnakeCase %>", + "name": "<%- operation.operationIdPascalCase %>Response", "ext": ".java", "overwrite": true } -###/TSAPI_WRITE_FILE###{{#apiInfo}} -{{#apis.0}} -package {{package}}.handlers.{{operationIdSnakeCase}}; -{{/apis.0}} -{{/apiInfo}} +###/TSAPI_WRITE_FILE### +package <%- metadata.packageName %>.api.handlers.<%- operation.operationIdSnakeCase %>; -import {{package}}.handlers.Response; +import <%- metadata.packageName %>.api.handlers.Response; /** - * Response for the {{nickname}} operation + * Response for the <%- operation.name %> operation */ -public interface {{operationIdCamelCase}}Response extends Response {} -{{/operation}} -{{/operations}} -{{/apis}} -{{/apiInfo}} -{{#apiInfo}} -{{#apis}} -{{#operations}} -{{#operation}} -{{#responses}} +public interface <%- operation.operationIdPascalCase %>Response extends Response {} +<%_ }); _%> +<%_ allOperations.forEach((operation) => { _%> +<%_ operation.responses.forEach((response) => { _%> ###TSAPI_WRITE_FILE### { - "dir": "api/handlers/{{operationIdSnakeCase}}", - "name": "{{operationIdCamelCase}}{{code}}Response", + "dir": "<%- metadata.srcDir %>/api/handlers/<%- operation.operationIdSnakeCase %>", + "name": "<%- operation.operationIdPascalCase %><%- response.code %>Response", "ext": ".java", "overwrite": true } -###/TSAPI_WRITE_FILE###{{#apiInfo}} -{{#apis.0}} -package {{package}}.handlers.{{operationIdSnakeCase}}; -{{/apis.0}} -{{/apiInfo}} - -{{#apiInfo}} -{{#apis.0}} -import {{modelPackage}}.*; -{{/apis.0}} -{{/apiInfo}} -{{#apiInfo}} -{{#apis.0}} -import {{invokerPackage}}.JSON; -{{/apis.0}} -{{/apiInfo}} +###/TSAPI_WRITE_FILE### +package <%- metadata.packageName %>.api.handlers.<%- operation.operationIdSnakeCase %>; + +import <%- metadata.packageName %>.model.*; +import <%- metadata.packageName %>.JSON; import java.util.Map; import java.util.HashMap; import java.util.List; /** - * Response with status code {{code}} for the {{nickname}} operation + * Response with status code <%- response.code %> for the <%- operation.name %> operation */ -public class {{operationIdCamelCase}}{{code}}Response extends RuntimeException implements {{operationIdCamelCase}}Response { +public class <%- operation.operationIdPascalCase %><%- response.code %>Response extends RuntimeException implements <%- operation.operationIdPascalCase %>Response { static { // JSON has a static instance of Gson which is instantiated lazily the first time it is initialised. // Create an instance here if required to ensure that the static Gson instance is always available. @@ -875,21 +805,22 @@ public class {{operationIdCamelCase}}{{code}}Response extends RuntimeException i } } + <%_ const responseType = response.isPrimitive ? 'String' : response.javaType _%> private final String body; - {{#dataType}}private final {{#isPrimitiveType}}String{{/isPrimitiveType}}{{^isPrimitiveType}}{{.}}{{/isPrimitiveType}} typedBody;{{/dataType}} + <% if (response.type !== 'void') { %>private final <%- responseType %> typedBody;<% } %> private final Map headers; private final Map> multiValueHeaders; - private {{operationIdCamelCase}}{{code}}Response({{#dataType}}final {{#isPrimitiveType}}String{{/isPrimitiveType}}{{^isPrimitiveType}}{{.}}{{/isPrimitiveType}} body, {{/dataType}}final Map headers, final Map> multiValueHeaders) { - {{#dataType}}this.typedBody = body;{{/dataType}} - this.body = {{#dataType}}{{#isPrimitiveType}}body{{/isPrimitiveType}}{{^isPrimitiveType}}body.toJson(){{/isPrimitiveType}}{{/dataType}}{{^dataType}}""{{/dataType}}; + private <%- operation.operationIdPascalCase %><%- response.code %>Response(<% if (response.type !== 'void') { %>final <%- responseType %> body, <% } %>final Map headers, final Map> multiValueHeaders) { + <% if (response.type !== 'void') { %>this.typedBody = body;<% } %> + this.body = <% if (response.type !== 'void') { %><% if (response.isPrimitive) { %>body<% } else { %>body.toJson()<% } %><% } else { %>""<% } %>; this.headers = headers; this.multiValueHeaders = multiValueHeaders; } @Override public int getStatusCode() { - return {{code}}; + return <%- response.code %>; } @Override @@ -897,11 +828,11 @@ public class {{operationIdCamelCase}}{{code}}Response extends RuntimeException i return this.body; } - {{#dataType}} - public {{#isPrimitiveType}}String{{/isPrimitiveType}}{{^isPrimitiveType}}{{.}}{{/isPrimitiveType}} getTypedBody() { + <%_ if (response.type !== 'void') { _%> + public <%- responseType %> getTypedBody() { return this.typedBody; } - {{/dataType}} + <%_ } _%> @Override public Map getHeaders() { @@ -914,53 +845,40 @@ public class {{operationIdCamelCase}}{{code}}Response extends RuntimeException i } /** - * Create a {{operationIdCamelCase}}{{code}}Response with{{^dataType}}out{{/dataType}} a body + * Create a <%- operation.operationIdPascalCase %><%- response.code %>Response with<% if (response.type === 'void') { %>out<% } %> a body */ - public static {{operationIdCamelCase}}{{code}}Response of({{#dataType}}final {{#isPrimitiveType}}String{{/isPrimitiveType}}{{^isPrimitiveType}}{{.}}{{/isPrimitiveType}} body{{/dataType}}) { - return new {{operationIdCamelCase}}{{code}}Response({{#dataType}}body, {{/dataType}}new HashMap<>(), new HashMap<>()); + public static <%- operation.operationIdPascalCase %><%- response.code %>Response of(<% if (response.type !== 'void') { %>final <%- responseType %> body<% } %>) { + return new <%- operation.operationIdPascalCase %><%- response.code %>Response(<% if (response.type !== 'void') { %>body, <% } %>new HashMap<>(), new HashMap<>()); } /** - * Create a {{operationIdCamelCase}}{{code}}Response with{{^dataType}}out{{/dataType}} a body and headers + * Create a <%- operation.operationIdPascalCase %><%- response.code %>Response with<% if (response.type === 'void') { %>out<% } %> a body and headers */ - public static {{operationIdCamelCase}}{{code}}Response of({{#dataType}}final {{#isPrimitiveType}}String{{/isPrimitiveType}}{{^isPrimitiveType}}{{.}}{{/isPrimitiveType}} body, {{/dataType}}final Map headers) { - return new {{operationIdCamelCase}}{{code}}Response({{#dataType}}body, {{/dataType}}headers, new HashMap<>()); + public static <%- operation.operationIdPascalCase %><%- response.code %>Response of(<% if (response.type !== 'void') { %>final <%- responseType %> body, <% } %>final Map headers) { + return new <%- operation.operationIdPascalCase %><%- response.code %>Response(<% if (response.type !== 'void') { %>body, <% } %>headers, new HashMap<>()); } /** - * Create a {{operationIdCamelCase}}{{code}}Response with{{^dataType}}out{{/dataType}} a body, headers and multi-value headers + * Create a <%- operation.operationIdPascalCase %><%- response.code %>Response with<% if (response.type === 'void') { %>out<% } %> a body, headers and multi-value headers */ - public static {{operationIdCamelCase}}{{code}}Response of({{#dataType}}final {{#isPrimitiveType}}String{{/isPrimitiveType}}{{^isPrimitiveType}}{{.}}{{/isPrimitiveType}} body, {{/dataType}}final Map headers, final Map> multiValueHeaders) { - return new {{operationIdCamelCase}}{{code}}Response({{#dataType}}body, {{/dataType}}headers, multiValueHeaders); + public static <%- operation.operationIdPascalCase %><%- response.code %>Response of(<% if (response.type !== 'void') { %>final <%- responseType %> body, <% } %>final Map headers, final Map> multiValueHeaders) { + return new <%- operation.operationIdPascalCase %><%- response.code %>Response(<% if (response.type !== 'void') { %>body, <% } %>headers, multiValueHeaders); } } -{{/responses}} -{{/operation}} -{{/operations}} -{{/apis}} -{{/apiInfo}} -{{#apiInfo}} -{{#apis}} -{{#operations}} -{{#operation}} +<%_ }); _%> +<%_ }); _%> +<%_ allOperations.forEach((operation) => { _%> ###TSAPI_WRITE_FILE### { - "dir": "api/handlers/{{operationIdSnakeCase}}", - "name": "{{operationIdCamelCase}}RequestParameters", + "dir": "<%- metadata.srcDir %>/api/handlers/<%- operation.operationIdSnakeCase %>", + "name": "<%- operation.operationIdPascalCase %>RequestParameters", "ext": ".java", "overwrite": true } -###/TSAPI_WRITE_FILE###{{#apiInfo}} -{{#apis.0}} -package {{package}}.handlers.{{operationIdSnakeCase}}; -{{/apis.0}} -{{/apiInfo}} - -{{#apiInfo}} -{{#apis.0}} -import {{package}}.handlers.Handlers; -{{/apis.0}} -{{/apiInfo}} +###/TSAPI_WRITE_FILE### +package <%- metadata.packageName %>.api.handlers.<%- operation.operationIdSnakeCase %>; + +import <%- metadata.packageName %>.api.handlers.Handlers; import java.util.Optional; import java.util.Map; import java.util.List; @@ -969,21 +887,22 @@ import java.util.HashMap; import java.time.OffsetDateTime; import java.math.BigDecimal; import java.math.BigInteger; +import java.util.stream.Collectors; import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent; +import <%- metadata.packageName %>.model.*; + /** - * Query, path and header parameters for the {{operationIdCamelCase}} operation + * Query, path and header parameters for the <%- operation.operationIdPascalCase %> operation */ @lombok.Builder @lombok.AllArgsConstructor -public class {{operationIdCamelCase}}RequestParameters { - {{#allParams}} - {{^isBodyParam}} - private final {{^required}}Optional<{{/required}}{{{dataType}}}{{^required}}>{{/required}} {{paramName}}; - {{/isBodyParam}} - {{/allParams}} - - public {{operationIdCamelCase}}RequestParameters(final APIGatewayProxyRequestEvent event) { +public class <%- operation.operationIdPascalCase %>RequestParameters { + <%_ operation.parameters.filter(p => p.in !== "body").forEach((parameter) => { _%> + private final <% if (!parameter.isRequired) { %>Optional<<% } %><%- parameter.javaType %><% if (!parameter.isRequired) { %>><% } %> <%- parameter.javaName %>; + <%_ }); _%> + + public <%- operation.operationIdPascalCase %>RequestParameters(final APIGatewayProxyRequestEvent event) { Map rawStringParameters = new HashMap<>(); Handlers.putAllFromNullableMap(event.getPathParameters(), rawStringParameters); Handlers.putAllFromNullableMap(event.getQueryStringParameters(), rawStringParameters); @@ -995,52 +914,31 @@ public class {{operationIdCamelCase}}RequestParameters { Handlers.putAllFromNullableMap(event.getMultiValueHeaders(), rawStringArrayParameters); Map> decodedStringArrayParameters = Handlers.decodeRequestArrayParameters(rawStringArrayParameters); - {{#allParams}} - {{^isBodyParam}} - this.{{paramName}} = {{^required}}Optional.ofNullable({{/required}}Handlers.coerce{{#isArray}}{{#items}}{{{dataType}}}Array{{/items}}{{/isArray}}{{^isArray}}{{{dataType}}}{{/isArray}}Parameter("{{baseName}}", {{required}}, decodedString{{#isArray}}Array{{/isArray}}Parameters){{^required}}){{/required}}; - {{/isBodyParam}} - {{/allParams}} + <%_ operation.parameters.filter(p => p.in !== "body").forEach((parameter) => { _%> + this.<%- parameter.javaName %> = <% if (!parameter.isRequired) { %>Optional.ofNullable(<% } %><% if (parameter.isEnum) { %><%- parameter.javaType %>.fromValue(<% } %>Handlers.coerce<% if (parameter.export === "array") { %><%- parameter.link.javaType %>Array<% } else { %><%- parameter.isEnum ? 'String' : parameter.javaType %><% } %>Parameter("<%- parameter.prop %>", <%- !!parameter.isRequired %>, decodedString<% if (parameter.export === "array") { %>Array<% } %>Parameters)<% if (parameter.export === "array" && parameter.link.isEnum) { %>.stream().map(<%- parameter.link.name %>::fromValue).collect(Collectors.toList())<% } %><% if (parameter.isEnum) { %>)<% } %><% if (!parameter.isRequired) { %>)<% } %>; + <%_ }); _%> } - {{#allParams}} - {{^isBodyParam}} - public {{^required}}Optional<{{/required}}{{{dataType}}}{{^required}}>{{/required}} {{#schema}}{{getter}}{{/schema}}() { - return this.{{paramName}}; + <%_ operation.parameters.filter(p => p.in !== "body").forEach((parameter) => { _%> + public <% if (!parameter.isRequired) { %>Optional<<% } %><%- parameter.javaType %><% if (!parameter.isRequired) { %>><% } %> <%- include('../getterSetter.partial.ejs', { prefix: 'get', name: parameter.javaName }) %>() { + return this.<%- parameter.javaName %>; } - {{/isBodyParam}} - {{/allParams}} + <%_ }); _%> } -{{/operation}} -{{/operations}} -{{/apis}} -{{/apiInfo}} -{{#apiInfo}} -{{#apis}} -{{#operations}} -{{#operation}} +<%_ }); _%> +<%_ allOperations.forEach((operation) => { _%> ###TSAPI_WRITE_FILE### { - "dir": "api/handlers/{{operationIdSnakeCase}}", - "name": "{{operationIdCamelCase}}Input", + "dir": "<%- metadata.srcDir %>/api/handlers/<%- operation.operationIdSnakeCase %>", + "name": "<%- operation.operationIdPascalCase %>Input", "ext": ".java", "overwrite": true } -###/TSAPI_WRITE_FILE###{{#apiInfo}} -{{#apis.0}} -package {{package}}.handlers.{{operationIdSnakeCase}}; -{{/apis.0}} -{{/apiInfo}} - -{{#apiInfo}} -{{#apis.0}} -import {{modelPackage}}.*; -{{/apis.0}} -{{/apiInfo}} -{{#apiInfo}} -{{#apis.0}} -import {{invokerPackage}}.JSON; -{{/apis.0}} -{{/apiInfo}} +###/TSAPI_WRITE_FILE### +package <%- metadata.packageName %>.api.handlers.<%- operation.operationIdSnakeCase %>; + +import <%- metadata.packageName %>.model.*; +import <%- metadata.packageName %>.JSON; import java.util.List; import java.util.ArrayList; import java.util.Optional; @@ -1050,11 +948,11 @@ import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent; import java.io.IOException; /** - * Input for the {{nickname}} operation + * Input for the <%- operation.name %> operation */ @lombok.Builder @lombok.AllArgsConstructor -public class {{operationIdCamelCase}}Input { +public class <%- operation.operationIdPascalCase %>Input { static { // JSON has a static instance of Gson which is instantiated lazily the first time it is initialised. // Create an instance here if required to ensure that the static Gson instance is always available. @@ -1063,68 +961,50 @@ public class {{operationIdCamelCase}}Input { } } - private final {{operationIdCamelCase}}RequestParameters requestParameters; - {{#bodyParam}} - private final {{#isModel}}{{dataType}}{{/isModel}}{{^isModel}}String{{/isModel}} body; - {{/bodyParam}} + private final <%- operation.operationIdPascalCase %>RequestParameters requestParameters; + <%_ if (operation.parametersBody) { _%> + private final <%- operation.parametersBody.isPrimitive ? 'String' : operation.parametersBody.javaType %> body; + <%_ } _%> - public {{operationIdCamelCase}}Input(final APIGatewayProxyRequestEvent event) { - this.requestParameters = new {{operationIdCamelCase}}RequestParameters(event); - {{#bodyParam}} - {{#isModel}} + public <%- operation.operationIdPascalCase %>Input(final APIGatewayProxyRequestEvent event) { + this.requestParameters = new <%- operation.operationIdPascalCase %>RequestParameters(event); + <%_ if (operation.parametersBody) { _%> + <%_ if (!operation.parametersBody.isPrimitive) { _%> try { - this.body = {{dataType}}.fromJson(event.getBody()); + this.body = <%- operation.parametersBody.javaType %>.fromJson(event.getBody()); } catch (IOException e) { throw new RuntimeException(e); }; - {{/isModel}} - {{^isModel}} + <%_ } else { _%> this.body = event.getBody(); - {{/isModel}} - {{/bodyParam}} + <%_ } _%> + <%_ } _%> } - public {{operationIdCamelCase}}RequestParameters getRequestParameters() { + public <%- operation.operationIdPascalCase %>RequestParameters getRequestParameters() { return this.requestParameters; } - {{#bodyParam}} - public {{#isModel}}{{dataType}}{{/isModel}}{{^isModel}}String{{/isModel}} getBody() { + <%_ if (operation.parametersBody) { _%> + public <%- operation.parametersBody.isPrimitive ? 'String' : operation.parametersBody.javaType %> getBody() { return this.body; } - {{/bodyParam}} + <%_ } _%> } -{{/operation}} -{{/operations}} -{{/apis}} -{{/apiInfo}} -{{#apiInfo}} -{{#apis}} -{{#operations}} -{{#operation}} +<%_ }); _%> +<%_ allOperations.forEach((operation) => { _%> ###TSAPI_WRITE_FILE### { - "dir": "api/handlers/{{operationIdSnakeCase}}", - "name": "{{operationIdCamelCase}}RequestInput", + "dir": "<%- metadata.srcDir %>/api/handlers/<%- operation.operationIdSnakeCase %>", + "name": "<%- operation.operationIdPascalCase %>RequestInput", "ext": ".java", "overwrite": true } -###/TSAPI_WRITE_FILE###{{#apiInfo}} -{{#apis.0}} -package {{package}}.handlers.{{operationIdSnakeCase}}; -{{/apis.0}} -{{/apiInfo}} - -{{#apiInfo}} -{{#apis.0}} -import {{modelPackage}}.*; -{{/apis.0}} -{{/apiInfo}} -{{#apiInfo}} -{{#apis.0}} -import {{package}}.handlers.RequestInput; -{{/apis.0}} -{{/apiInfo}} +###/TSAPI_WRITE_FILE### +package <%- metadata.packageName %>.api.handlers.<%- operation.operationIdSnakeCase %>; + +import <%- metadata.packageName %>.model.*; +import <%- metadata.packageName %>.api.handlers.RequestInput; import java.util.List; import java.util.Optional; import java.util.Map; @@ -1134,20 +1014,20 @@ import java.io.IOException; import com.amazonaws.services.lambda.runtime.Context; /** - * Full request input for the {{nickname}} operation, including the raw API Gateway event + * Full request input for the <%- operation.name %> operation, including the raw API Gateway event */ @lombok.Builder @lombok.AllArgsConstructor -public class {{operationIdCamelCase}}RequestInput implements RequestInput<{{operationIdCamelCase}}Input> { +public class <%- operation.operationIdPascalCase %>RequestInput implements RequestInput<<%- operation.operationIdPascalCase %>Input> { private final APIGatewayProxyRequestEvent event; private final Context context; private final Map interceptorContext; - private final {{operationIdCamelCase}}Input input; + private final <%- operation.operationIdPascalCase %>Input input; /** * Returns the typed request input, with path, query and body parameters */ - public {{operationIdCamelCase}}Input getInput() { + public <%- operation.operationIdPascalCase %>Input getInput() { return this.input; } @@ -1172,40 +1052,23 @@ public class {{operationIdCamelCase}}RequestInput implements RequestInput<{{oper return this.interceptorContext; } } -{{/operation}} -{{/operations}} -{{/apis}} -{{/apiInfo}} -{{#apiInfo}} -{{#apis}} -{{#operations}} -{{#operation}} +<%_ }); _%> +<%_ allOperations.forEach((operation) => { _%> ###TSAPI_WRITE_FILE### { - "dir": "api/handlers/{{operationIdSnakeCase}}", - "name": "{{operationIdCamelCase}}", + "dir": "<%- metadata.srcDir %>/api/handlers/<%- operation.operationIdSnakeCase %>", + "name": "<%- operation.operationIdPascalCase %>", "ext": ".java", "overwrite": true } -###/TSAPI_WRITE_FILE###{{#apiInfo}} -{{#apis.0}} -package {{package}}.handlers.{{operationIdSnakeCase}}; -{{/apis.0}} -{{/apiInfo}} - -{{#apiInfo}} -{{#apis.0}} -import {{modelPackage}}.*; -{{/apis.0}} -{{/apiInfo}} -{{#apiInfo}} -{{#apis.0}} -import {{invokerPackage}}.JSON; -import {{package}}.handlers.Interceptor; -import {{package}}.handlers.Handlers; -import {{package}}.handlers.*; -{{/apis.0}} -{{/apiInfo}} +###/TSAPI_WRITE_FILE### +package <%- metadata.packageName %>.api.handlers.<%- operation.operationIdSnakeCase %>; + +import <%- metadata.packageName %>.model.*; +import <%- metadata.packageName %>.JSON; +import <%- metadata.packageName %>.api.handlers.Interceptor; +import <%- metadata.packageName %>.api.handlers.Handlers; +import <%- metadata.packageName %>.api.handlers.*; import java.util.List; import java.util.ArrayList; @@ -1223,50 +1086,50 @@ import org.crac.Resource; /** - * Lambda handler wrapper for the {{nickname}} operation + * Lambda handler wrapper for the <%- operation.name %> operation */ -public abstract class {{operationIdCamelCase}} implements RequestHandler, Resource { +public abstract class <%- operation.operationIdPascalCase %> implements RequestHandler, Resource { { Core.getGlobalContext().register(this); } /** - * Handle the request for the {{nickname}} operation + * Handle the request for the <%- operation.name %> operation */ - public abstract {{operationIdCamelCase}}Response handle(final {{operationIdCamelCase}}RequestInput request); + public abstract <%- operation.operationIdPascalCase %>Response handle(final <%- operation.operationIdPascalCase %>RequestInput request); /** * Interceptors that the handler class has been decorated with */ - private List> annotationInterceptors = Handlers.getAnnotationInterceptors({{operationIdCamelCase}}.class); + private ListInput>> annotationInterceptors = Handlers.getAnnotationInterceptors(<%- operation.operationIdPascalCase %>.class); /** * For more complex interceptors that require instantiation with parameters, you may override this method to * return a list of instantiated interceptors. For simple interceptors with no need for constructor arguments, * prefer the @Interceptors annotation. */ - public List> getInterceptors() { + public ListInput>> getInterceptors() { return Collections.emptyList(); } - private List> getHandlerInterceptors() { - List> interceptors = new ArrayList<>(); + private ListInput>> getHandlerInterceptors() { + ListInput>> interceptors = new ArrayList<>(); interceptors.addAll(annotationInterceptors); interceptors.addAll(this.getInterceptors()); return interceptors; } - private HandlerChain<{{operationIdCamelCase}}Input> buildChain(List> interceptors) { - return Handlers.buildHandlerChain(interceptors, new HandlerChain<{{operationIdCamelCase}}Input>() { + private HandlerChain<<%- operation.operationIdPascalCase %>Input> buildChain(ListInput>> interceptors) { + return Handlers.buildHandlerChain(interceptors, new HandlerChain<<%- operation.operationIdPascalCase %>Input>() { @Override - public Response next(ChainedRequestInput<{{operationIdCamelCase}}Input> input) { - return handle(new {{operationIdCamelCase}}RequestInput(input.getEvent(), input.getContext(), input.getInterceptorContext(), input.getInput())); + public Response next(ChainedRequestInput<<%- operation.operationIdPascalCase %>Input> input) { + return handle(new <%- operation.operationIdPascalCase %>RequestInput(input.getEvent(), input.getContext(), input.getInterceptorContext(), input.getInput())); } }); } - private ChainedRequestInput<{{operationIdCamelCase}}Input> buildChainedRequestInput(final APIGatewayProxyRequestEvent event, final Context context, final {{operationIdCamelCase}}Input input, final Map interceptorContext) { - return new ChainedRequestInput<{{operationIdCamelCase}}Input>() { + private ChainedRequestInput<<%- operation.operationIdPascalCase %>Input> buildChainedRequestInput(final APIGatewayProxyRequestEvent event, final Context context, final <%- operation.operationIdPascalCase %>Input input, final Map interceptorContext) { + return new ChainedRequestInput<<%- operation.operationIdPascalCase %>Input>() { @Override public HandlerChain getChain() { // The chain's next method ignores the chain given as input, and is pre-built to follow the remaining @@ -1285,7 +1148,7 @@ public abstract class {{operationIdCamelCase}} implements RequestHandlerInput getInput() { return input; } @@ -1309,7 +1172,7 @@ public abstract class {{operationIdCamelCase}} implements RequestHandlerInput(new APIGatewayProxyRequestEvent() .withBody("{}") .withPathParameters(new HashMap<>()) .withQueryStringParameters(new HashMap<>()) @@ -1343,30 +1206,30 @@ public abstract class {{operationIdCamelCase}} implements RequestHandler getErrorResponseHeaders(final int statusCode) { Map headers = new HashMap<>(); - {{#responses}} - {{^is2xx}} - if (statusCode == {{code}} && "{{dataType}}".endsWith("ResponseContent")) { - headers.put("x-amzn-errortype", "{{dataType}}".substring(0, "{{dataType}}".length() - "ResponseContent".length())); + <%_ operation.responses.forEach((response) => { _%> + <%_ if (response.code < 200 || response.code >= 300) { _%> + if (statusCode == <%- response.code %> && "<%- response.javaType %>".endsWith("ResponseContent")) { + headers.put("x-amzn-errortype", "<%- response.javaType %>".substring(0, "<%- response.javaType %>".length() - "ResponseContent".length())); } - {{/is2xx}} - {{/responses}} + <%_ } _%> + <%_ }); _%> return headers; } - public APIGatewayProxyResponseEvent handleRequestWithAdditionalInterceptors(final APIGatewayProxyRequestEvent event, final Context context, final List> additionalInterceptors) { + public APIGatewayProxyResponseEvent handleRequestWithAdditionalInterceptors(final APIGatewayProxyRequestEvent event, final Context context, final ListInput>> additionalInterceptors) { final Map interceptorContext = new HashMap<>(); - interceptorContext.put("operationId", "{{nickname}}"); + interceptorContext.put("operationId", "<%- operation.name %>"); - List> interceptors = new ArrayList<>(); + ListInput>> interceptors = new ArrayList<>(); interceptors.addAll(additionalInterceptors); interceptors.addAll(this.getHandlerInterceptors()); final HandlerChain chain = this.buildChain(interceptors); - {{operationIdCamelCase}}Input input; + <%- operation.operationIdPascalCase %>Input input; try { - input = new {{operationIdCamelCase}}Input(event); + input = new <%- operation.operationIdPascalCase %>Input(event); } catch (RuntimeException e) { Map headers = new HashMap<>(); headers.putAll(Handlers.extractResponseHeadersFromInterceptors(interceptors)); @@ -1390,39 +1253,23 @@ public abstract class {{operationIdCamelCase}} implements RequestHandler ###TSAPI_WRITE_FILE### { - "dir": "api/handlers", + "dir": "<%- metadata.srcDir %>/api/handlers", "name": "HandlerRouter", "ext": ".java", "overwrite": true } -###/TSAPI_WRITE_FILE###{{#apiInfo}} -{{#apis.0}} -package {{package}}.handlers; -{{/apis.0}} -{{/apiInfo}} - -{{#apiInfo}} -{{#apis}} -{{#operations}} -{{#operation}} -import {{package}}.handlers.{{operationIdSnakeCase}}.*; -{{/operation}} -{{/operations}} -{{/apis}} -{{/apiInfo}} - -{{#apiInfo}} -{{#apis.0}} -import {{package}}.handlers.Handlers; -import {{package}}.handlers.*; -{{/apis.0}} -{{/apiInfo}} +###/TSAPI_WRITE_FILE### +package <%- metadata.packageName %>.api.handlers; + +<%_ allOperations.forEach((operation) => { _%> +import <%- metadata.packageName %>.api.handlers.<%- operation.operationIdSnakeCase %>.*; +<%_ }); _%> + +import <%- metadata.packageName %>.api.handlers.Handlers; +import <%- metadata.packageName %>.api.handlers.*; import com.amazonaws.services.lambda.runtime.Context; import com.amazonaws.services.lambda.runtime.RequestHandler; @@ -1437,49 +1284,25 @@ import java.util.Collections; public abstract class HandlerRouter implements RequestHandler { - {{#apiInfo}} - {{#apis}} - {{#operations}} - {{#operation}} - private static final String {{nickname}}MethodAndPath = Handlers.concatMethodAndPath("{{httpMethod}}", "{{path}}"); - {{/operation}} - {{/operations}} - {{/apis}} - {{/apiInfo}} - - {{#apiInfo}} - {{#apis}} - {{#operations}} - {{#operation}} - private final {{operationIdCamelCase}} constructed{{operationIdCamelCase}}; - {{/operation}} - {{/operations}} - {{/apis}} - {{/apiInfo}} - - {{#apiInfo}} - {{#apis}} - {{#operations}} - {{#operation}} + <%_ allOperations.forEach((operation) => { _%> + private static final String <%- operation.name %>MethodAndPath = Handlers.concatMethodAndPath("<%- operation.method %>", "<%- operation.path %>"); + <%_ }); _%> + + <%_ allOperations.forEach((operation) => { _%> + private final <%- operation.operationIdPascalCase %> constructed<%- operation.operationIdPascalCase %>; + <%_ }); _%> + + <%_ allOperations.forEach((operation) => { _%> /** - * This method must return your implementation of the {{operationIdCamelCase}} operation + * This method must return your implementation of the <%- operation.operationIdPascalCase %> operation */ - public abstract {{operationIdCamelCase}} {{nickname}}(); - {{/operation}} - {{/operations}} - {{/apis}} - {{/apiInfo}} + public abstract <%- operation.operationIdPascalCase %> <%- operation.name %>(); + <%_ }); _%> private static enum Route { - {{#apiInfo}} - {{#apis}} - {{#operations}} - {{#operation}} - {{nickname}}Route, - {{/operation}} - {{/operations}} - {{/apis}} - {{/apiInfo}} + <%_ allOperations.forEach((operation) => { _%> + <%- operation.name %>Route, + <%_ }); _%> } /** @@ -1488,27 +1311,15 @@ public abstract class HandlerRouter implements RequestHandler routes = new HashMap<>(); public HandlerRouter() { - {{#apiInfo}} - {{#apis}} - {{#operations}} - {{#operation}} - this.routes.put({{nickname}}MethodAndPath, Route.{{nickname}}Route); - {{/operation}} - {{/operations}} - {{/apis}} - {{/apiInfo}} + <%_ allOperations.forEach((operation) => { _%> + this.routes.put(<%- operation.name %>MethodAndPath, Route.<%- operation.name %>Route); + <%_ }); _%> // Handlers are all constructed in the router's constructor such that lambda behaviour remains consistent; // ie resources created in the constructor remain in memory between invocations. // https://docs.aws.amazon.com/lambda/latest/dg/java-handler.html - {{#apiInfo}} - {{#apis}} - {{#operations}} - {{#operation}} - this.constructed{{operationIdCamelCase}} = this.{{nickname}}(); - {{/operation}} - {{/operations}} - {{/apis}} - {{/apiInfo}} + <%_ allOperations.forEach((operation) => { _%> + this.constructed<%- operation.operationIdPascalCase %> = this.<%- operation.name %>(); + <%_ }); _%> } /** @@ -1528,18 +1339,12 @@ public abstract class HandlerRouter implements RequestHandler> {{nickname}}Interceptors = Handlers.getAnnotationInterceptors(this.getClass()); - {{nickname}}Interceptors.addAll(this.getInterceptors()); - return this.constructed{{operationIdCamelCase}}.handleRequestWithAdditionalInterceptors(event, context, {{nickname}}Interceptors); - {{/operation}} - {{/operations}} - {{/apis}} - {{/apiInfo}} + <%_ allOperations.forEach((operation) => { _%> + case <%- operation.name %>Route: + ListInput>> <%- operation.name %>Interceptors = Handlers.getAnnotationInterceptors(this.getClass()); + <%- operation.name %>Interceptors.addAll(this.getInterceptors()); + return this.constructed<%- operation.operationIdPascalCase %>.handleRequestWithAdditionalInterceptors(event, context, <%- operation.name %>Interceptors); + <%_ }); _%> default: throw new RuntimeException(String.format("No registered handler for method {} and path {}", method, path)); } diff --git a/packages/type-safe-api/scripts/type-safe-api/generators/java/templates/interceptors.mustache b/packages/type-safe-api/scripts/type-safe-api/generators/java/templates/server/interceptors.ejs similarity index 77% rename from packages/type-safe-api/scripts/type-safe-api/generators/java/templates/interceptors.mustache rename to packages/type-safe-api/scripts/type-safe-api/generators/java/templates/server/interceptors.ejs index cc07be105..4fdb12988 100644 --- a/packages/type-safe-api/scripts/type-safe-api/generators/java/templates/interceptors.mustache +++ b/packages/type-safe-api/scripts/type-safe-api/generators/java/templates/server/interceptors.ejs @@ -1,18 +1,17 @@ -###TSAPI_SPLIT_FILE### ###TSAPI_WRITE_FILE### { - "dir": "api/interceptors", + "dir": "<%- metadata.srcDir %>/api/interceptors", "name": "TryCatchInterceptor", "ext": ".java", "overwrite": true } -###/TSAPI_WRITE_FILE###{{#apiInfo}}{{#apis.0}}package {{package}}.interceptors;{{/apis.0}}{{/apiInfo}} +###/TSAPI_WRITE_FILE###package <%- metadata.packageName %>.api.interceptors; -import {{#apiInfo}}{{#apis.0}}{{package}}{{/apis.0}}{{/apiInfo}}.handlers.ApiResponse; -import {{#apiInfo}}{{#apis.0}}{{package}}{{/apis.0}}{{/apiInfo}}.handlers.ChainedRequestInput; -import {{#apiInfo}}{{#apis.0}}{{package}}{{/apis.0}}{{/apiInfo}}.handlers.Response; -import {{#apiInfo}}{{#apis.0}}{{package}}{{/apis.0}}{{/apiInfo}}.handlers.Interceptor; -import {{#apiInfo}}{{#apis.0}}{{package}}{{/apis.0}}{{/apiInfo}}.handlers.InterceptorWithWarmup; +import <%- metadata.packageName %>.api.handlers.ApiResponse; +import <%- metadata.packageName %>.api.handlers.ChainedRequestInput; +import <%- metadata.packageName %>.api.handlers.Response; +import <%- metadata.packageName %>.api.handlers.Interceptor; +import <%- metadata.packageName %>.api.handlers.InterceptorWithWarmup; import org.apache.logging.log4j.Logger; /** @@ -57,17 +56,17 @@ public class TryCatchInterceptor extends InterceptorWithWarmup { } ###TSAPI_WRITE_FILE### { - "dir": "api/interceptors", + "dir": "<%- metadata.srcDir %>/api/interceptors", "name": "ResponseHeadersInterceptor", "ext": ".java", "overwrite": true } -###/TSAPI_WRITE_FILE###{{#apiInfo}}{{#apis.0}}package {{package}}.interceptors;{{/apis.0}}{{/apiInfo}} +###/TSAPI_WRITE_FILE###package <%- metadata.packageName %>.api.interceptors; -import {{#apiInfo}}{{#apis.0}}{{package}}{{/apis.0}}{{/apiInfo}}.handlers.ChainedRequestInput; -import {{#apiInfo}}{{#apis.0}}{{package}}{{/apis.0}}{{/apiInfo}}.handlers.Response; -import {{#apiInfo}}{{#apis.0}}{{package}}{{/apis.0}}{{/apiInfo}}.handlers.Interceptor; -import {{#apiInfo}}{{#apis.0}}{{package}}{{/apis.0}}{{/apiInfo}}.handlers.InterceptorWithWarmup; +import <%- metadata.packageName %>.api.handlers.ChainedRequestInput; +import <%- metadata.packageName %>.api.handlers.Response; +import <%- metadata.packageName %>.api.handlers.Interceptor; +import <%- metadata.packageName %>.api.handlers.InterceptorWithWarmup; import java.util.Map; import java.util.HashMap; @@ -101,18 +100,18 @@ public class ResponseHeadersInterceptor extends InterceptorWithWarmup/api/interceptors/powertools", "name": "LoggingInterceptor", "ext": ".java", "overwrite": true } -###/TSAPI_WRITE_FILE###{{#apiInfo}}{{#apis.0}}package {{package}}.interceptors.powertools;{{/apis.0}}{{/apiInfo}} +###/TSAPI_WRITE_FILE###package <%- metadata.packageName %>.api.interceptors.powertools; -import {{#apiInfo}}{{#apis.0}}{{package}}{{/apis.0}}{{/apiInfo}}.handlers.ChainedRequestInput; -import {{#apiInfo}}{{#apis.0}}{{package}}{{/apis.0}}{{/apiInfo}}.handlers.RequestInput; -import {{#apiInfo}}{{#apis.0}}{{package}}{{/apis.0}}{{/apiInfo}}.handlers.Response; -import {{#apiInfo}}{{#apis.0}}{{package}}{{/apis.0}}{{/apiInfo}}.handlers.Interceptor; -import {{#apiInfo}}{{#apis.0}}{{package}}{{/apis.0}}{{/apiInfo}}.handlers.InterceptorWithWarmup; +import <%- metadata.packageName %>.api.handlers.ChainedRequestInput; +import <%- metadata.packageName %>.api.handlers.RequestInput; +import <%- metadata.packageName %>.api.handlers.Response; +import <%- metadata.packageName %>.api.handlers.Interceptor; +import <%- metadata.packageName %>.api.handlers.InterceptorWithWarmup; import com.amazonaws.services.lambda.runtime.Context; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -186,17 +185,17 @@ public class LoggingInterceptor extends InterceptorWithWarmup { } ###TSAPI_WRITE_FILE### { - "dir": "api/interceptors/powertools", + "dir": "<%- metadata.srcDir %>/api/interceptors/powertools", "name": "TracingInterceptor", "ext": ".java", "overwrite": true } -###/TSAPI_WRITE_FILE###{{#apiInfo}}{{#apis.0}}package {{package}}.interceptors.powertools;{{/apis.0}}{{/apiInfo}} +###/TSAPI_WRITE_FILE###package <%- metadata.packageName %>.api.interceptors.powertools; -import {{#apiInfo}}{{#apis.0}}{{package}}{{/apis.0}}{{/apiInfo}}.handlers.ChainedRequestInput; -import {{#apiInfo}}{{#apis.0}}{{package}}{{/apis.0}}{{/apiInfo}}.handlers.Response; -import {{#apiInfo}}{{#apis.0}}{{package}}{{/apis.0}}{{/apiInfo}}.handlers.Interceptor; -import {{#apiInfo}}{{#apis.0}}{{package}}{{/apis.0}}{{/apiInfo}}.handlers.InterceptorWithWarmup; +import <%- metadata.packageName %>.api.handlers.ChainedRequestInput; +import <%- metadata.packageName %>.api.handlers.Response; +import <%- metadata.packageName %>.api.handlers.Interceptor; +import <%- metadata.packageName %>.api.handlers.InterceptorWithWarmup; import com.amazonaws.xray.AWSXRay; import com.amazonaws.xray.AWSXRayRecorderBuilder; import com.amazonaws.xray.entities.Subsegment; @@ -290,18 +289,18 @@ public class TracingInterceptor extends InterceptorWithWarmup { } ###TSAPI_WRITE_FILE### { - "dir": "api/interceptors/powertools", + "dir": "<%- metadata.srcDir %>/api/interceptors/powertools", "name": "MetricsInterceptor", "ext": ".java", "overwrite": true } -###/TSAPI_WRITE_FILE###{{#apiInfo}}{{#apis.0}}package {{package}}.interceptors.powertools;{{/apis.0}}{{/apiInfo}} +###/TSAPI_WRITE_FILE###package <%- metadata.packageName %>.api.interceptors.powertools; -import {{#apiInfo}}{{#apis.0}}{{package}}{{/apis.0}}{{/apiInfo}}.handlers.ChainedRequestInput; -import {{#apiInfo}}{{#apis.0}}{{package}}{{/apis.0}}{{/apiInfo}}.handlers.RequestInput; -import {{#apiInfo}}{{#apis.0}}{{package}}{{/apis.0}}{{/apiInfo}}.handlers.Response; -import {{#apiInfo}}{{#apis.0}}{{package}}{{/apis.0}}{{/apiInfo}}.handlers.Interceptor; -import {{#apiInfo}}{{#apis.0}}{{package}}{{/apis.0}}{{/apiInfo}}.handlers.InterceptorWithWarmup; +import <%- metadata.packageName %>.api.handlers.ChainedRequestInput; +import <%- metadata.packageName %>.api.handlers.RequestInput; +import <%- metadata.packageName %>.api.handlers.Response; +import <%- metadata.packageName %>.api.handlers.Interceptor; +import <%- metadata.packageName %>.api.handlers.InterceptorWithWarmup; import software.amazon.cloudwatchlogs.emf.logger.MetricsLogger; import software.amazon.cloudwatchlogs.emf.model.DimensionSet; import software.amazon.lambda.powertools.core.internal.LambdaHandlerProcessor; @@ -351,17 +350,17 @@ public class MetricsInterceptor extends InterceptorWithWarmup { } ###TSAPI_WRITE_FILE### { - "dir": "api/interceptors", + "dir": "<%- metadata.srcDir %>/api/interceptors", "name": "DefaultInterceptors", "ext": ".java", "overwrite": true } -###/TSAPI_WRITE_FILE###{{#apiInfo}}{{#apis.0}}package {{package}}.interceptors;{{/apis.0}}{{/apiInfo}} +###/TSAPI_WRITE_FILE###package <%- metadata.packageName %>.api.interceptors; -import {{#apiInfo}}{{#apis.0}}{{package}}{{/apis.0}}{{/apiInfo}}.interceptors.powertools.LoggingInterceptor; -import {{#apiInfo}}{{#apis.0}}{{package}}{{/apis.0}}{{/apiInfo}}.interceptors.powertools.MetricsInterceptor; -import {{#apiInfo}}{{#apis.0}}{{package}}{{/apis.0}}{{/apiInfo}}.interceptors.powertools.TracingInterceptor; -import {{#apiInfo}}{{#apis.0}}{{package}}{{/apis.0}}{{/apiInfo}}.handlers.Interceptor; +import <%- metadata.packageName %>.api.interceptors.powertools.LoggingInterceptor; +import <%- metadata.packageName %>.api.interceptors.powertools.MetricsInterceptor; +import <%- metadata.packageName %>.api.interceptors.powertools.TracingInterceptor; +import <%- metadata.packageName %>.api.handlers.Interceptor; import java.util.Arrays; import java.util.List; diff --git a/packages/type-safe-api/scripts/type-safe-api/generators/java/templates/server/operationConfig.ejs b/packages/type-safe-api/scripts/type-safe-api/generators/java/templates/server/operationConfig.ejs new file mode 100644 index 000000000..4ec3762dd --- /dev/null +++ b/packages/type-safe-api/scripts/type-safe-api/generators/java/templates/server/operationConfig.ejs @@ -0,0 +1,30 @@ + +###TSAPI_WRITE_FILE### +{ + "dir": "<%- metadata.srcDir %>/api/operation_config", + "name": "OperationConfig", + "ext": ".java", + "overwrite": true +} +###/TSAPI_WRITE_FILE###package <%- metadata.packageName %>.api.operation_config; + +import <%- metadata.packageName %>.model.*; + +import java.util.HashMap; +import java.util.Map; + +// Generic type for object "keyed" by operation names +@lombok.Builder @lombok.Getter +public class OperationConfig { + <%_ allOperations.forEach((operation) => { _%> + private T <%- operation.name %>; + <%_ }); _%> + + public Map asMap() { + Map map = new HashMap<>(); + <%_ allOperations.forEach((operation) => { _%> + map.put("<%- operation.name %>", this.<%- operation.name %>); + <%_ }); _%> + return map; + } +} diff --git a/packages/type-safe-api/scripts/type-safe-api/generators/java/templates/server/operationLookup.ejs b/packages/type-safe-api/scripts/type-safe-api/generators/java/templates/server/operationLookup.ejs new file mode 100644 index 000000000..af306fe70 --- /dev/null +++ b/packages/type-safe-api/scripts/type-safe-api/generators/java/templates/server/operationLookup.ejs @@ -0,0 +1,43 @@ +###TSAPI_WRITE_FILE### +{ + "dir": "<%- metadata.srcDir %>/api/operation_config", + "name": "OperationLookup", + "ext": ".java", + "overwrite": true +} +###/TSAPI_WRITE_FILE###package <%- metadata.packageName %>.api.operation_config; + +import <%- metadata.packageName %>.model.*; + +import java.util.HashMap; +import java.util.Map; +import java.util.List; +import java.util.Arrays; + + +// Look up path and http method for a given operation name +public class OperationLookup { + @lombok.Builder @lombok.Getter + public static class OperationLookupEntry { + private String method; + private String path; + private List contentTypes; + } + + /** + * Returns the operation lookup information for the TypeSafeRestApi construct + */ + public static Map getOperationLookup() { + final Map config = new HashMap<>(); + + <%_ allOperations.forEach((operation) => { _%> + config.put("<%- operation.name %>", OperationLookupEntry.builder() + .path("<%- operation.path %>") + .method("<%- operation.method %>") + .contentTypes(Arrays.asList(<%- operation.parametersBody ? operation.parametersBody.mediaTypes.map(m => `"${m}"`).join(',') : '"application/json"' %>)) + .build()); + <%_ }); _%> + + return config; + } +} diff --git a/packages/type-safe-api/scripts/type-safe-api/generators/java/templates/operations.mustache b/packages/type-safe-api/scripts/type-safe-api/generators/java/templates/server/operations.ejs similarity index 53% rename from packages/type-safe-api/scripts/type-safe-api/generators/java/templates/operations.mustache rename to packages/type-safe-api/scripts/type-safe-api/generators/java/templates/server/operations.ejs index e3b908a14..f8498fe59 100644 --- a/packages/type-safe-api/scripts/type-safe-api/generators/java/templates/operations.mustache +++ b/packages/type-safe-api/scripts/type-safe-api/generators/java/templates/server/operations.ejs @@ -1,10 +1,12 @@ -{{#apiInfo}} -{{#apis.0}} -package {{package}}.operation_config; -{{/apis.0}} -{{/apiInfo}} +###TSAPI_WRITE_FILE### +{ + "dir": "<%- metadata.srcDir %>/api/operation_config", + "name": "Operations", + "ext": ".java", + "overwrite": true +} +###/TSAPI_WRITE_FILE###package <%- metadata.packageName %>.api.operation_config; -{{>generatedAnnotation}} public class Operations { /** * Returns an OperationConfig Builder with all values populated with the given value. @@ -13,15 +15,9 @@ public class Operations { */ public static OperationConfig.OperationConfigBuilder all(final T value) { return OperationConfig.builder() - {{#apiInfo}} - {{#apis}} - {{#operations}} - {{#operation}} - .{{nickname}}(value) - {{/operation}} - {{/operations}} - {{/apis}} - {{/apiInfo}} + <%_ allOperations.forEach((operation) => { _%> + .<%- operation.name %>(value) + <%_ }); _%> ; } } diff --git a/packages/type-safe-api/scripts/type-safe-api/generators/python/templates/types/pydanticType.partial.ejs b/packages/type-safe-api/scripts/type-safe-api/generators/python/templates/types/pydanticType.partial.ejs index 3855e3d91..5b67b6a04 100644 --- a/packages/type-safe-api/scripts/type-safe-api/generators/python/templates/types/pydanticType.partial.ejs +++ b/packages/type-safe-api/scripts/type-safe-api/generators/python/templates/types/pydanticType.partial.ejs @@ -60,12 +60,12 @@ const toPythonPydanticType = (property) => { case "reference": return toPythonPydanticPrimitive(property); case "array": { - const type = `List[${property.link ? toPythonPydanticType(property.link) : property.type}]`; + const type = `List[${property.link && property.link.export !== "enum" ? toPythonPydanticType(property.link) : property.type}]`; const constraints = getPythonPydanticConstraints(property); return constraints ? renderPydanticConstraintsType(type, constraints) : type; } case "dictionary": - return `Dict[str, ${property.link ? toPythonPydanticType(property.link) : property.type}]`; + return `Dict[str, ${property.link && property.link.export !== "enum" ? toPythonPydanticType(property.link) : property.type}]`; default: // "any" has export = interface if (property.isPrimitive) { diff --git a/packages/type-safe-api/src/project/codegen/runtime/generated-java-async-runtime-project.ts b/packages/type-safe-api/src/project/codegen/runtime/generated-java-async-runtime-project.ts index 72e98e07f..046eb514f 100644 --- a/packages/type-safe-api/src/project/codegen/runtime/generated-java-async-runtime-project.ts +++ b/packages/type-safe-api/src/project/codegen/runtime/generated-java-async-runtime-project.ts @@ -5,7 +5,8 @@ import { GeneratedJavaRuntimeBaseProject, GeneratedJavaRuntimeBaseProjectOptions, } from "./generated-java-runtime-base-project"; -import { GenerationOptions, OtherGenerators } from "../components/utils"; +import { Language } from "../../languages"; +import { CodegenOptions } from "../components/utils"; /** * Configuration for the generated java runtime project @@ -21,32 +22,25 @@ export class GeneratedJavaAsyncRuntimeProject extends GeneratedJavaRuntimeBasePr super(options); } - protected buildOpenApiGeneratorOptions(): GenerationOptions { + protected buildCodegenOptions(): CodegenOptions { return { - generator: "java", specPath: this.options.specPath, - generatorDirectory: OtherGenerators.JAVA_ASYNC_RUNTIME, - additionalProperties: { - useSingleRequestParameter: "true", + templateDirs: [ + // TODO: when implemented, swap to OtherGenerators.JAVA_ASYNC_RUNTIME and "java/templates/client/models" + Language.JAVA, + ], + metadata: { groupId: this.pom.groupId, artifactId: this.pom.artifactId, artifactVersion: this.pom.version, - invokerPackage: this.packageName, - apiPackage: `${this.packageName}.api`, - modelPackage: `${this.packageName}.model`, - hideGenerationTimestamp: "true", - additionalModelTypeAnnotations: [ - "@lombok.AllArgsConstructor", - // Regular lombok builder is not used since an abstract base schema class is also annotated - "@lombok.experimental.SuperBuilder", - ].join("\\ "), + packageName: this.packageName, + srcDir: path.join( + "src", + "main", + "java", + ...this.packageName.split(".") + ), }, - srcDir: path.join("src", "main", "java", ...this.packageName.split(".")), - normalizers: { - KEEP_ONLY_FIRST_TAG_IN_OPERATION: true, - }, - // Do not generate map/list types. Generator will use built in HashMap, ArrayList instead - generateAliasAsModel: false, }; } } diff --git a/packages/type-safe-api/src/project/codegen/runtime/generated-java-runtime-base-project.ts b/packages/type-safe-api/src/project/codegen/runtime/generated-java-runtime-base-project.ts index 3f263dba2..fa86f5768 100644 --- a/packages/type-safe-api/src/project/codegen/runtime/generated-java-runtime-base-project.ts +++ b/packages/type-safe-api/src/project/codegen/runtime/generated-java-runtime-base-project.ts @@ -6,14 +6,11 @@ import { CodeGenerationSourceOptions, GeneratedJavaRuntimeOptions, } from "../../types"; -import { OpenApiGeneratorIgnoreFile } from "../components/open-api-generator-ignore-file"; -import { OpenApiToolsJsonFile } from "../components/open-api-tools-json-file"; import { TypeSafeApiCommandEnvironment } from "../components/type-safe-api-command-environment"; import { - buildCleanOpenApiGeneratedCodeCommand, - buildInvokeOpenApiGeneratorCommandArgs, + buildCodegenCommandArgs, buildTypeSafeApiExecCommand, - GenerationOptions, + CodegenOptions, TypeSafeApiScript, } from "../components/utils"; @@ -59,26 +56,6 @@ const TEST_DEPENDENCIES: string[] = [ * Java project containing types generated using OpenAPI Generator CLI */ export abstract class GeneratedJavaRuntimeBaseProject extends JavaProject { - /** - * Patterns that are excluded from code generation - */ - public static openApiIgnorePatterns: string[] = [ - "pom.xml", - "gradle", - "gradle/**/*", - "gradlew", - "gradle.properties", - "gradlew.bat", - "build.gradle", - "settings.gradle", - "build.sbt", - ".travis.yml", - "git_push.sh", - "src/test", - "src/test/**/*", - "src/main/AndroidManifest.xml", - ]; - /** * The package name, for use in imports */ @@ -89,8 +66,6 @@ export abstract class GeneratedJavaRuntimeBaseProject extends JavaProject { */ protected readonly options: GeneratedJavaRuntimeBaseProjectOptions; - protected readonly openapiGeneratorIgnore: OpenApiGeneratorIgnoreFile; - constructor(options: GeneratedJavaRuntimeBaseProjectOptions) { super({ ...(options as any), @@ -100,17 +75,6 @@ export abstract class GeneratedJavaRuntimeBaseProject extends JavaProject { TypeSafeApiCommandEnvironment.ensure(this); this.options = options; - // Ignore files that we will control via projen - this.openapiGeneratorIgnore = new OpenApiGeneratorIgnoreFile(this); - this.openapiGeneratorIgnore.addPatterns( - ...GeneratedJavaRuntimeBaseProject.openApiIgnorePatterns - ); - - // Add OpenAPI Generator cli configuration - OpenApiToolsJsonFile.ensure(this).addOpenApiGeneratorCliConfig( - options.openApiGeneratorCliConfig - ); - // Add dependencies DEPENDENCIES.forEach((dep) => this.addDependency(dep)); TEST_DEPENDENCIES.forEach((dep) => this.addTestDependency(dep)); @@ -126,10 +90,9 @@ export abstract class GeneratedJavaRuntimeBaseProject extends JavaProject { // Generate the java code const generateTask = this.addTask("generate"); - generateTask.exec(buildCleanOpenApiGeneratedCodeCommand()); generateTask.exec( buildTypeSafeApiExecCommand( - TypeSafeApiScript.GENERATE, + TypeSafeApiScript.GENERATE_NEXT, this.buildGenerateCommandArgs() ) ); @@ -138,23 +101,14 @@ export abstract class GeneratedJavaRuntimeBaseProject extends JavaProject { if (!options.commitGeneratedCode) { // Ignore all the generated code - this.gitignore.addPatterns( - "src", - "docs", - "api", - "README.md", - ".openapi-generator" - ); - } else { - this.gitignore.addPatterns(".openapi-generator"); + this.gitignore.addPatterns("src", "docs", "api", "README.md"); } + this.gitignore.addPatterns(".openapi-generator", ".tsapi-manifest"); } public buildGenerateCommandArgs = () => { - return buildInvokeOpenApiGeneratorCommandArgs( - this.buildOpenApiGeneratorOptions() - ); + return buildCodegenCommandArgs(this.buildCodegenOptions()); }; - protected abstract buildOpenApiGeneratorOptions(): GenerationOptions; + protected abstract buildCodegenOptions(): CodegenOptions; } diff --git a/packages/type-safe-api/src/project/codegen/runtime/generated-java-runtime-project.ts b/packages/type-safe-api/src/project/codegen/runtime/generated-java-runtime-project.ts index 6c6655b18..ff79c5636 100644 --- a/packages/type-safe-api/src/project/codegen/runtime/generated-java-runtime-project.ts +++ b/packages/type-safe-api/src/project/codegen/runtime/generated-java-runtime-project.ts @@ -6,7 +6,7 @@ import { GeneratedJavaRuntimeBaseProjectOptions, } from "./generated-java-runtime-base-project"; import { Language } from "../../languages"; -import { GenerationOptions } from "../components/utils"; +import { CodegenOptions } from "../components/utils"; /** * Configuration for the generated java runtime project @@ -22,32 +22,25 @@ export class GeneratedJavaRuntimeProject extends GeneratedJavaRuntimeBaseProject super(options); } - protected buildOpenApiGeneratorOptions(): GenerationOptions { + protected buildCodegenOptions(): CodegenOptions { return { - generator: "java", specPath: this.options.specPath, - generatorDirectory: Language.JAVA, - additionalProperties: { - useSingleRequestParameter: "true", + templateDirs: [ + // TODO: when implemented, swap to OtherGenerators.JAVA_ASYNC_RUNTIME and "java/templates/client/models" + Language.JAVA, + ], + metadata: { groupId: this.pom.groupId, artifactId: this.pom.artifactId, artifactVersion: this.pom.version, - invokerPackage: this.packageName, - apiPackage: `${this.packageName}.api`, - modelPackage: `${this.packageName}.model`, - hideGenerationTimestamp: "true", - additionalModelTypeAnnotations: [ - "@lombok.AllArgsConstructor", - // Regular lombok builder is not used since an abstract base schema class is also annotated - "@lombok.experimental.SuperBuilder", - ].join("\\ "), + packageName: this.packageName, + srcDir: path.join( + "src", + "main", + "java", + ...this.packageName.split(".") + ), }, - srcDir: path.join("src", "main", "java", ...this.packageName.split(".")), - normalizers: { - KEEP_ONLY_FIRST_TAG_IN_OPERATION: true, - }, - // Do not generate map/list types. Generator will use built in HashMap, ArrayList instead - generateAliasAsModel: false, }; } } diff --git a/packages/type-safe-api/test/project/__snapshots__/type-safe-api-project.test.ts.snap b/packages/type-safe-api/test/project/__snapshots__/type-safe-api-project.test.ts.snap index d97bd9951..c4a3e8d7c 100644 --- a/packages/type-safe-api/test/project/__snapshots__/type-safe-api-project.test.ts.snap +++ b/packages/type-safe-api/test/project/__snapshots__/type-safe-api-project.test.ts.snap @@ -1,24 +1,6 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`Type Safe Api Project Unit Tests Custom OpenAPI Generator CLI Configuration 1`] = `undefined`; - -exports[`Type Safe Api Project Unit Tests Custom OpenAPI Generator CLI Configuration 2`] = ` -{ - "$schema": "node_modules/@openapitools/openapi-generator-cli/config.schema.json", - "//": "~~ Generated by projen. To modify, edit .projenrc.js and run "npx projen".", - "generator-cli": { - "repository": { - "downloadUrl": "https://my.custom.maven.repo/maven2/\${groupId}/\${artifactId}/\${versionName}/\${artifactId}-\${versionName}.jar", - }, - "storageDir": "~/.my-storage-dir", - "useDocker": true, - "version": "6.2.0", - }, - "spaces": 2, -} -`; - -exports[`Type Safe Api Project Unit Tests Custom OpenAPI Generator CLI Configuration 3`] = ` +exports[`Type Safe Api Project Unit Tests Custom OpenAPI Generator CLI Configuration 1`] = ` { "$schema": "node_modules/@openapitools/openapi-generator-cli/config.schema.json", "//": "~~ Generated by projen. To modify, edit .projenrc.js and run "npx projen".", @@ -1126,8 +1108,6 @@ Each runtime project includes types from your API model, as well as type-safe cl /.gitattributes linguist-generated /.gitignore linguist-generated -/.openapi-generator-ignore linguist-generated -/.pdk/dynamic-files/openapitools.json linguist-generated /.projen/** linguist-generated /.projen/deps.json linguist-generated /.projen/files.json linguist-generated @@ -1145,41 +1125,13 @@ node_modules/ .settings target dist/java -!/.openapi-generator-ignore -!/.pdk/dynamic-files/openapitools.json -/openapitools.json src docs api README.md .openapi-generator +.tsapi-manifest ", - "generated/runtime/java/.openapi-generator-ignore": "# ~~ Generated by projen. To modify, edit .projenrc.js and run "npx projen". -.gitignore -pom.xml -gradle -gradle/**/* -gradlew -gradle.properties -gradlew.bat -build.gradle -settings.gradle -build.sbt -.travis.yml -git_push.sh -src/test -src/test/**/* -src/main/AndroidManifest.xml -", - "generated/runtime/java/.pdk/dynamic-files/openapitools.json": { - "$schema": "node_modules/@openapitools/openapi-generator-cli/config.schema.json", - "//": "~~ Generated by projen. To modify, edit .projenrc.js and run "npx projen".", - "generator-cli": { - "storageDir": "~/.open-api-generator-cli", - "version": "6.3.0", - }, - "spaces": 2, - }, "generated/runtime/java/.projen/deps.json": { "//": "~~ Generated by projen. To modify, edit .projenrc.js and run "npx projen".", "dependencies": [ @@ -1389,8 +1341,6 @@ src/main/AndroidManifest.xml "files": [ ".gitattributes", ".gitignore", - ".openapi-generator-ignore", - ".pdk/dynamic-files/openapitools.json", ".projen/deps.json", ".projen/files.json", ".projen/tasks.json", @@ -1433,14 +1383,6 @@ src/main/AndroidManifest.xml }, ], }, - "create-openapitools.json": { - "name": "create-openapitools.json", - "steps": [ - { - "exec": "cp -f .pdk/dynamic-files/openapitools.json openapitools.json", - }, - ], - }, "default": { "description": "Synthesize project files", "name": "default", @@ -1455,13 +1397,7 @@ src/main/AndroidManifest.xml "name": "generate", "steps": [ { - "spawn": "create-openapitools.json", - }, - { - "exec": "npx --yes -p @aws/pdk@$AWS_PDK_VERSION type-safe-api.clean-openapi-generated-code --code-path .", - }, - { - "exec": "npx --yes -p @aws/pdk@$AWS_PDK_VERSION type-safe-api.generate --generator java --spec-path ../../../model/.api.json --output-path . --generator-dir java --src-dir src/main/java/com/generated/api/openapijavajavaruntime/runtime --tst-dir test --additional-properties "useSingleRequestParameter=true,groupId=com.generated.api,artifactId=openapi-java-java-runtime,artifactVersion=0.0.0,invokerPackage=com.generated.api.openapijavajavaruntime.runtime,apiPackage=com.generated.api.openapijavajavaruntime.runtime.api,modelPackage=com.generated.api.openapijavajavaruntime.runtime.model,hideGenerationTimestamp=true,additionalModelTypeAnnotations=@lombok.AllArgsConstructor\\ @lombok.experimental.SuperBuilder" --openapi-normalizer "KEEP_ONLY_FIRST_TAG_IN_OPERATION=true"", + "exec": "npx --yes -p @aws/pdk@$AWS_PDK_VERSION type-safe-api generate --specPath ../../../model/.api.json --outputPath . --templateDirs "java" --metadata '{"groupId":"com.generated.api","artifactId":"openapi-java-java-runtime","artifactVersion":"0.0.0","packageName":"com.generated.api.openapijavajavaruntime.runtime","srcDir":"src/main/java/com/generated/api/openapijavajavaruntime/runtime"}'", }, ], }, @@ -6425,8 +6361,6 @@ Each runtime project includes types from your API model, as well as type-safe cl /.gitattributes linguist-generated /.gitignore linguist-generated -/.openapi-generator-ignore linguist-generated -/.pdk/dynamic-files/openapitools.json linguist-generated /.projen/** linguist-generated /.projen/deps.json linguist-generated /.projen/files.json linguist-generated @@ -6446,43 +6380,15 @@ node_modules/ .settings target dist/java -!/.openapi-generator-ignore -!/.pdk/dynamic-files/openapitools.json -/openapitools.json src docs api README.md .openapi-generator +.tsapi-manifest !/project.json !/LICENSE ", - "packages/api/generated/runtime/java/.openapi-generator-ignore": "# ~~ Generated by projen. To modify, edit .projenrc.js and run "npx projen". -.gitignore -pom.xml -gradle -gradle/**/* -gradlew -gradle.properties -gradlew.bat -build.gradle -settings.gradle -build.sbt -.travis.yml -git_push.sh -src/test -src/test/**/* -src/main/AndroidManifest.xml -", - "packages/api/generated/runtime/java/.pdk/dynamic-files/openapitools.json": { - "$schema": "node_modules/@openapitools/openapi-generator-cli/config.schema.json", - "//": "~~ Generated by projen. To modify, edit .projenrc.js and run "npx projen".", - "generator-cli": { - "storageDir": "~/.open-api-generator-cli", - "version": "6.3.0", - }, - "spaces": 2, - }, "packages/api/generated/runtime/java/.projen/deps.json": { "//": "~~ Generated by projen. To modify, edit .projenrc.js and run "npx projen".", "dependencies": [ @@ -6692,8 +6598,6 @@ src/main/AndroidManifest.xml "files": [ ".gitattributes", ".gitignore", - ".openapi-generator-ignore", - ".pdk/dynamic-files/openapitools.json", ".projen/deps.json", ".projen/files.json", ".projen/tasks.json", @@ -6738,14 +6642,6 @@ src/main/AndroidManifest.xml }, ], }, - "create-openapitools.json": { - "name": "create-openapitools.json", - "steps": [ - { - "exec": "cp -f .pdk/dynamic-files/openapitools.json openapitools.json", - }, - ], - }, "default": { "description": "Synthesize project files", "name": "default", @@ -6754,13 +6650,7 @@ src/main/AndroidManifest.xml "name": "generate", "steps": [ { - "spawn": "create-openapitools.json", - }, - { - "exec": "npx --yes -p @aws/pdk@$AWS_PDK_VERSION type-safe-api.clean-openapi-generated-code --code-path .", - }, - { - "exec": "npx --yes -p @aws/pdk@$AWS_PDK_VERSION type-safe-api.generate --generator java --spec-path ../../../model/.api.json --output-path . --generator-dir java --src-dir src/main/java/com/generated/api/openapijavajavaruntime/runtime --tst-dir test --additional-properties "useSingleRequestParameter=true,groupId=com.generated.api,artifactId=openapi-java-java-runtime,artifactVersion=0.0.0,invokerPackage=com.generated.api.openapijavajavaruntime.runtime,apiPackage=com.generated.api.openapijavajavaruntime.runtime.api,modelPackage=com.generated.api.openapijavajavaruntime.runtime.model,hideGenerationTimestamp=true,additionalModelTypeAnnotations=@lombok.AllArgsConstructor\\ @lombok.experimental.SuperBuilder" --openapi-normalizer "KEEP_ONLY_FIRST_TAG_IN_OPERATION=true"", + "exec": "npx --yes -p @aws/pdk@$AWS_PDK_VERSION type-safe-api generate --specPath ../../../model/.api.json --outputPath . --templateDirs "java" --metadata '{"groupId":"com.generated.api","artifactId":"openapi-java-java-runtime","artifactVersion":"0.0.0","packageName":"com.generated.api.openapijavajavaruntime.runtime","srcDir":"src/main/java/com/generated/api/openapijavajavaruntime/runtime"}'", }, ], }, @@ -7236,13 +7126,6 @@ src/main/AndroidManifest.xml "cwd": "packages/api/generated/runtime/java", }, }, - "create-openapitools.json": { - "executor": "nx:run-commands", - "options": { - "command": "npx projen create-openapitools.json", - "cwd": "packages/api/generated/runtime/java", - }, - }, "default": { "executor": "nx:run-commands", "options": { @@ -10274,8 +10157,6 @@ Each runtime project includes types from your API model, as well as type-safe cl /.gitattributes linguist-generated /.gitignore linguist-generated -/.openapi-generator-ignore linguist-generated -/.pdk/dynamic-files/openapitools.json linguist-generated /.projen/** linguist-generated /.projen/deps.json linguist-generated /.projen/files.json linguist-generated @@ -10293,41 +10174,13 @@ node_modules/ .settings target dist/java -!/.openapi-generator-ignore -!/.pdk/dynamic-files/openapitools.json -/openapitools.json src docs api README.md .openapi-generator +.tsapi-manifest ", - "generated/runtime/java/.openapi-generator-ignore": "# ~~ Generated by projen. To modify, edit .projenrc.js and run "npx projen". -.gitignore -pom.xml -gradle -gradle/**/* -gradlew -gradle.properties -gradlew.bat -build.gradle -settings.gradle -build.sbt -.travis.yml -git_push.sh -src/test -src/test/**/* -src/main/AndroidManifest.xml -", - "generated/runtime/java/.pdk/dynamic-files/openapitools.json": { - "$schema": "node_modules/@openapitools/openapi-generator-cli/config.schema.json", - "//": "~~ Generated by projen. To modify, edit .projenrc.js and run "npx projen".", - "generator-cli": { - "storageDir": "~/.open-api-generator-cli", - "version": "6.3.0", - }, - "spaces": 2, - }, "generated/runtime/java/.projen/deps.json": { "//": "~~ Generated by projen. To modify, edit .projenrc.js and run "npx projen".", "dependencies": [ @@ -10537,8 +10390,6 @@ src/main/AndroidManifest.xml "files": [ ".gitattributes", ".gitignore", - ".openapi-generator-ignore", - ".pdk/dynamic-files/openapitools.json", ".projen/deps.json", ".projen/files.json", ".projen/tasks.json", @@ -10581,14 +10432,6 @@ src/main/AndroidManifest.xml }, ], }, - "create-openapitools.json": { - "name": "create-openapitools.json", - "steps": [ - { - "exec": "cp -f .pdk/dynamic-files/openapitools.json openapitools.json", - }, - ], - }, "default": { "description": "Synthesize project files", "name": "default", @@ -10603,13 +10446,7 @@ src/main/AndroidManifest.xml "name": "generate", "steps": [ { - "spawn": "create-openapitools.json", - }, - { - "exec": "npx --yes -p @aws/pdk@$AWS_PDK_VERSION type-safe-api.clean-openapi-generated-code --code-path .", - }, - { - "exec": "npx --yes -p @aws/pdk@$AWS_PDK_VERSION type-safe-api.generate --generator java --spec-path ../../../model/.api.json --output-path . --generator-dir java --src-dir src/main/java/com/generated/api/openapipythonjavaruntime/runtime --tst-dir test --additional-properties "useSingleRequestParameter=true,groupId=com.generated.api,artifactId=openapi-python-java-runtime,artifactVersion=0.0.0,invokerPackage=com.generated.api.openapipythonjavaruntime.runtime,apiPackage=com.generated.api.openapipythonjavaruntime.runtime.api,modelPackage=com.generated.api.openapipythonjavaruntime.runtime.model,hideGenerationTimestamp=true,additionalModelTypeAnnotations=@lombok.AllArgsConstructor\\ @lombok.experimental.SuperBuilder" --openapi-normalizer "KEEP_ONLY_FIRST_TAG_IN_OPERATION=true"", + "exec": "npx --yes -p @aws/pdk@$AWS_PDK_VERSION type-safe-api generate --specPath ../../../model/.api.json --outputPath . --templateDirs "java" --metadata '{"groupId":"com.generated.api","artifactId":"openapi-python-java-runtime","artifactVersion":"0.0.0","packageName":"com.generated.api.openapipythonjavaruntime.runtime","srcDir":"src/main/java/com/generated/api/openapipythonjavaruntime/runtime"}'", }, ], }, @@ -15506,8 +15343,6 @@ Each runtime project includes types from your API model, as well as type-safe cl /.gitattributes linguist-generated /.gitignore linguist-generated -/.openapi-generator-ignore linguist-generated -/.pdk/dynamic-files/openapitools.json linguist-generated /.projen/** linguist-generated /.projen/deps.json linguist-generated /.projen/files.json linguist-generated @@ -15527,43 +15362,15 @@ node_modules/ .settings target dist/java -!/.openapi-generator-ignore -!/.pdk/dynamic-files/openapitools.json -/openapitools.json src docs api README.md .openapi-generator +.tsapi-manifest !/project.json !/LICENSE ", - "packages/api/generated/runtime/java/.openapi-generator-ignore": "# ~~ Generated by projen. To modify, edit .projenrc.js and run "npx projen". -.gitignore -pom.xml -gradle -gradle/**/* -gradlew -gradle.properties -gradlew.bat -build.gradle -settings.gradle -build.sbt -.travis.yml -git_push.sh -src/test -src/test/**/* -src/main/AndroidManifest.xml -", - "packages/api/generated/runtime/java/.pdk/dynamic-files/openapitools.json": { - "$schema": "node_modules/@openapitools/openapi-generator-cli/config.schema.json", - "//": "~~ Generated by projen. To modify, edit .projenrc.js and run "npx projen".", - "generator-cli": { - "storageDir": "~/.open-api-generator-cli", - "version": "6.3.0", - }, - "spaces": 2, - }, "packages/api/generated/runtime/java/.projen/deps.json": { "//": "~~ Generated by projen. To modify, edit .projenrc.js and run "npx projen".", "dependencies": [ @@ -15773,8 +15580,6 @@ src/main/AndroidManifest.xml "files": [ ".gitattributes", ".gitignore", - ".openapi-generator-ignore", - ".pdk/dynamic-files/openapitools.json", ".projen/deps.json", ".projen/files.json", ".projen/tasks.json", @@ -15819,14 +15624,6 @@ src/main/AndroidManifest.xml }, ], }, - "create-openapitools.json": { - "name": "create-openapitools.json", - "steps": [ - { - "exec": "cp -f .pdk/dynamic-files/openapitools.json openapitools.json", - }, - ], - }, "default": { "description": "Synthesize project files", "name": "default", @@ -15835,13 +15632,7 @@ src/main/AndroidManifest.xml "name": "generate", "steps": [ { - "spawn": "create-openapitools.json", - }, - { - "exec": "npx --yes -p @aws/pdk@$AWS_PDK_VERSION type-safe-api.clean-openapi-generated-code --code-path .", - }, - { - "exec": "npx --yes -p @aws/pdk@$AWS_PDK_VERSION type-safe-api.generate --generator java --spec-path ../../../model/.api.json --output-path . --generator-dir java --src-dir src/main/java/com/generated/api/openapipythonjavaruntime/runtime --tst-dir test --additional-properties "useSingleRequestParameter=true,groupId=com.generated.api,artifactId=openapi-python-java-runtime,artifactVersion=0.0.0,invokerPackage=com.generated.api.openapipythonjavaruntime.runtime,apiPackage=com.generated.api.openapipythonjavaruntime.runtime.api,modelPackage=com.generated.api.openapipythonjavaruntime.runtime.model,hideGenerationTimestamp=true,additionalModelTypeAnnotations=@lombok.AllArgsConstructor\\ @lombok.experimental.SuperBuilder" --openapi-normalizer "KEEP_ONLY_FIRST_TAG_IN_OPERATION=true"", + "exec": "npx --yes -p @aws/pdk@$AWS_PDK_VERSION type-safe-api generate --specPath ../../../model/.api.json --outputPath . --templateDirs "java" --metadata '{"groupId":"com.generated.api","artifactId":"openapi-python-java-runtime","artifactVersion":"0.0.0","packageName":"com.generated.api.openapipythonjavaruntime.runtime","srcDir":"src/main/java/com/generated/api/openapipythonjavaruntime/runtime"}'", }, ], }, @@ -16317,13 +16108,6 @@ src/main/AndroidManifest.xml "cwd": "packages/api/generated/runtime/java", }, }, - "create-openapitools.json": { - "executor": "nx:run-commands", - "options": { - "command": "npx projen create-openapitools.json", - "cwd": "packages/api/generated/runtime/java", - }, - }, "default": { "executor": "nx:run-commands", "options": { @@ -19650,8 +19434,6 @@ Each runtime project includes types from your API model, as well as type-safe cl /.gitattributes linguist-generated /.gitignore linguist-generated -/.openapi-generator-ignore linguist-generated -/.pdk/dynamic-files/openapitools.json linguist-generated /.projen/** linguist-generated /.projen/deps.json linguist-generated /.projen/files.json linguist-generated @@ -19669,41 +19451,13 @@ node_modules/ .settings target dist/java -!/.openapi-generator-ignore -!/.pdk/dynamic-files/openapitools.json -/openapitools.json src docs api README.md .openapi-generator +.tsapi-manifest ", - "generated/runtime/java/.openapi-generator-ignore": "# ~~ Generated by projen. To modify, edit .projenrc.js and run "npx projen". -.gitignore -pom.xml -gradle -gradle/**/* -gradlew -gradle.properties -gradlew.bat -build.gradle -settings.gradle -build.sbt -.travis.yml -git_push.sh -src/test -src/test/**/* -src/main/AndroidManifest.xml -", - "generated/runtime/java/.pdk/dynamic-files/openapitools.json": { - "$schema": "node_modules/@openapitools/openapi-generator-cli/config.schema.json", - "//": "~~ Generated by projen. To modify, edit .projenrc.js and run "npx projen".", - "generator-cli": { - "storageDir": "~/.open-api-generator-cli", - "version": "6.3.0", - }, - "spaces": 2, - }, "generated/runtime/java/.projen/deps.json": { "//": "~~ Generated by projen. To modify, edit .projenrc.js and run "npx projen".", "dependencies": [ @@ -19913,8 +19667,6 @@ src/main/AndroidManifest.xml "files": [ ".gitattributes", ".gitignore", - ".openapi-generator-ignore", - ".pdk/dynamic-files/openapitools.json", ".projen/deps.json", ".projen/files.json", ".projen/tasks.json", @@ -19957,14 +19709,6 @@ src/main/AndroidManifest.xml }, ], }, - "create-openapitools.json": { - "name": "create-openapitools.json", - "steps": [ - { - "exec": "cp -f .pdk/dynamic-files/openapitools.json openapitools.json", - }, - ], - }, "default": { "description": "Synthesize project files", "name": "default", @@ -19979,13 +19723,7 @@ src/main/AndroidManifest.xml "name": "generate", "steps": [ { - "spawn": "create-openapitools.json", - }, - { - "exec": "npx --yes -p @aws/pdk@$AWS_PDK_VERSION type-safe-api.clean-openapi-generated-code --code-path .", - }, - { - "exec": "npx --yes -p @aws/pdk@$AWS_PDK_VERSION type-safe-api.generate --generator java --spec-path ../../../model/.api.json --output-path . --generator-dir java --src-dir src/main/java/com/generated/api/openapitypescriptjavaruntime/runtime --tst-dir test --additional-properties "useSingleRequestParameter=true,groupId=com.generated.api,artifactId=openapi-typescript-java-runtime,artifactVersion=0.0.0,invokerPackage=com.generated.api.openapitypescriptjavaruntime.runtime,apiPackage=com.generated.api.openapitypescriptjavaruntime.runtime.api,modelPackage=com.generated.api.openapitypescriptjavaruntime.runtime.model,hideGenerationTimestamp=true,additionalModelTypeAnnotations=@lombok.AllArgsConstructor\\ @lombok.experimental.SuperBuilder" --openapi-normalizer "KEEP_ONLY_FIRST_TAG_IN_OPERATION=true"", + "exec": "npx --yes -p @aws/pdk@$AWS_PDK_VERSION type-safe-api generate --specPath ../../../model/.api.json --outputPath . --templateDirs "java" --metadata '{"groupId":"com.generated.api","artifactId":"openapi-typescript-java-runtime","artifactVersion":"0.0.0","packageName":"com.generated.api.openapitypescriptjavaruntime.runtime","srcDir":"src/main/java/com/generated/api/openapitypescriptjavaruntime/runtime"}'", }, ], }, @@ -24940,8 +24678,6 @@ Each runtime project includes types from your API model, as well as type-safe cl /.gitattributes linguist-generated /.gitignore linguist-generated -/.openapi-generator-ignore linguist-generated -/.pdk/dynamic-files/openapitools.json linguist-generated /.projen/** linguist-generated /.projen/deps.json linguist-generated /.projen/files.json linguist-generated @@ -24961,43 +24697,15 @@ node_modules/ .settings target dist/java -!/.openapi-generator-ignore -!/.pdk/dynamic-files/openapitools.json -/openapitools.json src docs api README.md .openapi-generator +.tsapi-manifest !/project.json !/LICENSE ", - "packages/api/generated/runtime/java/.openapi-generator-ignore": "# ~~ Generated by projen. To modify, edit .projenrc.js and run "npx projen". -.gitignore -pom.xml -gradle -gradle/**/* -gradlew -gradle.properties -gradlew.bat -build.gradle -settings.gradle -build.sbt -.travis.yml -git_push.sh -src/test -src/test/**/* -src/main/AndroidManifest.xml -", - "packages/api/generated/runtime/java/.pdk/dynamic-files/openapitools.json": { - "$schema": "node_modules/@openapitools/openapi-generator-cli/config.schema.json", - "//": "~~ Generated by projen. To modify, edit .projenrc.js and run "npx projen".", - "generator-cli": { - "storageDir": "~/.open-api-generator-cli", - "version": "6.3.0", - }, - "spaces": 2, - }, "packages/api/generated/runtime/java/.projen/deps.json": { "//": "~~ Generated by projen. To modify, edit .projenrc.js and run "npx projen".", "dependencies": [ @@ -25207,8 +24915,6 @@ src/main/AndroidManifest.xml "files": [ ".gitattributes", ".gitignore", - ".openapi-generator-ignore", - ".pdk/dynamic-files/openapitools.json", ".projen/deps.json", ".projen/files.json", ".projen/tasks.json", @@ -25253,14 +24959,6 @@ src/main/AndroidManifest.xml }, ], }, - "create-openapitools.json": { - "name": "create-openapitools.json", - "steps": [ - { - "exec": "cp -f .pdk/dynamic-files/openapitools.json openapitools.json", - }, - ], - }, "default": { "description": "Synthesize project files", "name": "default", @@ -25269,13 +24967,7 @@ src/main/AndroidManifest.xml "name": "generate", "steps": [ { - "spawn": "create-openapitools.json", - }, - { - "exec": "npx --yes -p @aws/pdk@$AWS_PDK_VERSION type-safe-api.clean-openapi-generated-code --code-path .", - }, - { - "exec": "npx --yes -p @aws/pdk@$AWS_PDK_VERSION type-safe-api.generate --generator java --spec-path ../../../model/.api.json --output-path . --generator-dir java --src-dir src/main/java/com/generated/api/openapitypescriptjavaruntime/runtime --tst-dir test --additional-properties "useSingleRequestParameter=true,groupId=com.generated.api,artifactId=openapi-typescript-java-runtime,artifactVersion=0.0.0,invokerPackage=com.generated.api.openapitypescriptjavaruntime.runtime,apiPackage=com.generated.api.openapitypescriptjavaruntime.runtime.api,modelPackage=com.generated.api.openapitypescriptjavaruntime.runtime.model,hideGenerationTimestamp=true,additionalModelTypeAnnotations=@lombok.AllArgsConstructor\\ @lombok.experimental.SuperBuilder" --openapi-normalizer "KEEP_ONLY_FIRST_TAG_IN_OPERATION=true"", + "exec": "npx --yes -p @aws/pdk@$AWS_PDK_VERSION type-safe-api generate --specPath ../../../model/.api.json --outputPath . --templateDirs "java" --metadata '{"groupId":"com.generated.api","artifactId":"openapi-typescript-java-runtime","artifactVersion":"0.0.0","packageName":"com.generated.api.openapitypescriptjavaruntime.runtime","srcDir":"src/main/java/com/generated/api/openapitypescriptjavaruntime/runtime"}'", }, ], }, @@ -25751,13 +25443,6 @@ src/main/AndroidManifest.xml "cwd": "packages/api/generated/runtime/java", }, }, - "create-openapitools.json": { - "executor": "nx:run-commands", - "options": { - "command": "npx projen create-openapitools.json", - "cwd": "packages/api/generated/runtime/java", - }, - }, "default": { "executor": "nx:run-commands", "options": { @@ -28608,8 +28293,6 @@ Each runtime project includes types from your API model, as well as type-safe cl /.gitattributes linguist-generated /.gitignore linguist-generated -/.openapi-generator-ignore linguist-generated -/.pdk/dynamic-files/openapitools.json linguist-generated /.projen/** linguist-generated /.projen/deps.json linguist-generated /.projen/files.json linguist-generated @@ -28627,41 +28310,13 @@ node_modules/ .settings target dist/java -!/.openapi-generator-ignore -!/.pdk/dynamic-files/openapitools.json -/openapitools.json src docs api README.md .openapi-generator +.tsapi-manifest ", - "generated/runtime/java/.openapi-generator-ignore": "# ~~ Generated by projen. To modify, edit .projenrc.js and run "npx projen". -.gitignore -pom.xml -gradle -gradle/**/* -gradlew -gradle.properties -gradlew.bat -build.gradle -settings.gradle -build.sbt -.travis.yml -git_push.sh -src/test -src/test/**/* -src/main/AndroidManifest.xml -", - "generated/runtime/java/.pdk/dynamic-files/openapitools.json": { - "$schema": "node_modules/@openapitools/openapi-generator-cli/config.schema.json", - "//": "~~ Generated by projen. To modify, edit .projenrc.js and run "npx projen".", - "generator-cli": { - "storageDir": "~/.open-api-generator-cli", - "version": "6.3.0", - }, - "spaces": 2, - }, "generated/runtime/java/.projen/deps.json": { "//": "~~ Generated by projen. To modify, edit .projenrc.js and run "npx projen".", "dependencies": [ @@ -28871,8 +28526,6 @@ src/main/AndroidManifest.xml "files": [ ".gitattributes", ".gitignore", - ".openapi-generator-ignore", - ".pdk/dynamic-files/openapitools.json", ".projen/deps.json", ".projen/files.json", ".projen/tasks.json", @@ -28915,14 +28568,6 @@ src/main/AndroidManifest.xml }, ], }, - "create-openapitools.json": { - "name": "create-openapitools.json", - "steps": [ - { - "exec": "cp -f .pdk/dynamic-files/openapitools.json openapitools.json", - }, - ], - }, "default": { "description": "Synthesize project files", "name": "default", @@ -28937,13 +28582,7 @@ src/main/AndroidManifest.xml "name": "generate", "steps": [ { - "spawn": "create-openapitools.json", - }, - { - "exec": "npx --yes -p @aws/pdk@$AWS_PDK_VERSION type-safe-api.clean-openapi-generated-code --code-path .", - }, - { - "exec": "npx --yes -p @aws/pdk@$AWS_PDK_VERSION type-safe-api.generate --generator java --spec-path ../../../model/.api.json --output-path . --generator-dir java --src-dir src/main/java/com/generated/api/smithyhandlersjavaruntime/runtime --tst-dir test --additional-properties "useSingleRequestParameter=true,groupId=com.generated.api,artifactId=smithy-handlers-java-runtime,artifactVersion=0.0.0,invokerPackage=com.generated.api.smithyhandlersjavaruntime.runtime,apiPackage=com.generated.api.smithyhandlersjavaruntime.runtime.api,modelPackage=com.generated.api.smithyhandlersjavaruntime.runtime.model,hideGenerationTimestamp=true,additionalModelTypeAnnotations=@lombok.AllArgsConstructor\\ @lombok.experimental.SuperBuilder" --openapi-normalizer "KEEP_ONLY_FIRST_TAG_IN_OPERATION=true"", + "exec": "npx --yes -p @aws/pdk@$AWS_PDK_VERSION type-safe-api generate --specPath ../../../model/.api.json --outputPath . --templateDirs "java" --metadata '{"groupId":"com.generated.api","artifactId":"smithy-handlers-java-runtime","artifactVersion":"0.0.0","packageName":"com.generated.api.smithyhandlersjavaruntime.runtime","srcDir":"src/main/java/com/generated/api/smithyhandlersjavaruntime/runtime"}'", }, ], }, @@ -35880,8 +35519,6 @@ Each runtime project includes types from your API model, as well as type-safe cl /.gitattributes linguist-generated /.gitignore linguist-generated -/.openapi-generator-ignore linguist-generated -/.pdk/dynamic-files/openapitools.json linguist-generated /.projen/** linguist-generated /.projen/deps.json linguist-generated /.projen/files.json linguist-generated @@ -35899,41 +35536,13 @@ node_modules/ .settings target dist/java -!/.openapi-generator-ignore -!/.pdk/dynamic-files/openapitools.json -/openapitools.json src docs api README.md .openapi-generator +.tsapi-manifest ", - "generated/runtime/java/.openapi-generator-ignore": "# ~~ Generated by projen. To modify, edit .projenrc.js and run "npx projen". -.gitignore -pom.xml -gradle -gradle/**/* -gradlew -gradle.properties -gradlew.bat -build.gradle -settings.gradle -build.sbt -.travis.yml -git_push.sh -src/test -src/test/**/* -src/main/AndroidManifest.xml -", - "generated/runtime/java/.pdk/dynamic-files/openapitools.json": { - "$schema": "node_modules/@openapitools/openapi-generator-cli/config.schema.json", - "//": "~~ Generated by projen. To modify, edit .projenrc.js and run "npx projen".", - "generator-cli": { - "storageDir": "~/.open-api-generator-cli", - "version": "6.3.0", - }, - "spaces": 2, - }, "generated/runtime/java/.projen/deps.json": { "//": "~~ Generated by projen. To modify, edit .projenrc.js and run "npx projen".", "dependencies": [ @@ -36143,8 +35752,6 @@ src/main/AndroidManifest.xml "files": [ ".gitattributes", ".gitignore", - ".openapi-generator-ignore", - ".pdk/dynamic-files/openapitools.json", ".projen/deps.json", ".projen/files.json", ".projen/tasks.json", @@ -36187,14 +35794,6 @@ src/main/AndroidManifest.xml }, ], }, - "create-openapitools.json": { - "name": "create-openapitools.json", - "steps": [ - { - "exec": "cp -f .pdk/dynamic-files/openapitools.json openapitools.json", - }, - ], - }, "default": { "description": "Synthesize project files", "name": "default", @@ -36209,13 +35808,7 @@ src/main/AndroidManifest.xml "name": "generate", "steps": [ { - "spawn": "create-openapitools.json", - }, - { - "exec": "npx --yes -p @aws/pdk@$AWS_PDK_VERSION type-safe-api.clean-openapi-generated-code --code-path .", - }, - { - "exec": "npx --yes -p @aws/pdk@$AWS_PDK_VERSION type-safe-api.generate --generator java --spec-path ../../../model/.api.json --output-path . --generator-dir java --src-dir src/main/java/com/generated/api/smithyjavajavaruntime/runtime --tst-dir test --additional-properties "useSingleRequestParameter=true,groupId=com.generated.api,artifactId=smithy-java-java-runtime,artifactVersion=0.0.0,invokerPackage=com.generated.api.smithyjavajavaruntime.runtime,apiPackage=com.generated.api.smithyjavajavaruntime.runtime.api,modelPackage=com.generated.api.smithyjavajavaruntime.runtime.model,hideGenerationTimestamp=true,additionalModelTypeAnnotations=@lombok.AllArgsConstructor\\ @lombok.experimental.SuperBuilder" --openapi-normalizer "KEEP_ONLY_FIRST_TAG_IN_OPERATION=true"", + "exec": "npx --yes -p @aws/pdk@$AWS_PDK_VERSION type-safe-api generate --specPath ../../../model/.api.json --outputPath . --templateDirs "java" --metadata '{"groupId":"com.generated.api","artifactId":"smithy-java-java-runtime","artifactVersion":"0.0.0","packageName":"com.generated.api.smithyjavajavaruntime.runtime","srcDir":"src/main/java/com/generated/api/smithyjavajavaruntime/runtime"}'", }, ], }, @@ -41273,8 +40866,6 @@ Each runtime project includes types from your API model, as well as type-safe cl /.gitattributes linguist-generated /.gitignore linguist-generated -/.openapi-generator-ignore linguist-generated -/.pdk/dynamic-files/openapitools.json linguist-generated /.projen/** linguist-generated /.projen/deps.json linguist-generated /.projen/files.json linguist-generated @@ -41294,43 +40885,15 @@ node_modules/ .settings target dist/java -!/.openapi-generator-ignore -!/.pdk/dynamic-files/openapitools.json -/openapitools.json src docs api README.md .openapi-generator +.tsapi-manifest !/project.json !/LICENSE ", - "packages/api/generated/runtime/java/.openapi-generator-ignore": "# ~~ Generated by projen. To modify, edit .projenrc.js and run "npx projen". -.gitignore -pom.xml -gradle -gradle/**/* -gradlew -gradle.properties -gradlew.bat -build.gradle -settings.gradle -build.sbt -.travis.yml -git_push.sh -src/test -src/test/**/* -src/main/AndroidManifest.xml -", - "packages/api/generated/runtime/java/.pdk/dynamic-files/openapitools.json": { - "$schema": "node_modules/@openapitools/openapi-generator-cli/config.schema.json", - "//": "~~ Generated by projen. To modify, edit .projenrc.js and run "npx projen".", - "generator-cli": { - "storageDir": "~/.open-api-generator-cli", - "version": "6.3.0", - }, - "spaces": 2, - }, "packages/api/generated/runtime/java/.projen/deps.json": { "//": "~~ Generated by projen. To modify, edit .projenrc.js and run "npx projen".", "dependencies": [ @@ -41540,8 +41103,6 @@ src/main/AndroidManifest.xml "files": [ ".gitattributes", ".gitignore", - ".openapi-generator-ignore", - ".pdk/dynamic-files/openapitools.json", ".projen/deps.json", ".projen/files.json", ".projen/tasks.json", @@ -41586,14 +41147,6 @@ src/main/AndroidManifest.xml }, ], }, - "create-openapitools.json": { - "name": "create-openapitools.json", - "steps": [ - { - "exec": "cp -f .pdk/dynamic-files/openapitools.json openapitools.json", - }, - ], - }, "default": { "description": "Synthesize project files", "name": "default", @@ -41602,13 +41155,7 @@ src/main/AndroidManifest.xml "name": "generate", "steps": [ { - "spawn": "create-openapitools.json", - }, - { - "exec": "npx --yes -p @aws/pdk@$AWS_PDK_VERSION type-safe-api.clean-openapi-generated-code --code-path .", - }, - { - "exec": "npx --yes -p @aws/pdk@$AWS_PDK_VERSION type-safe-api.generate --generator java --spec-path ../../../model/.api.json --output-path . --generator-dir java --src-dir src/main/java/com/generated/api/smithyjavajavaruntime/runtime --tst-dir test --additional-properties "useSingleRequestParameter=true,groupId=com.generated.api,artifactId=smithy-java-java-runtime,artifactVersion=0.0.0,invokerPackage=com.generated.api.smithyjavajavaruntime.runtime,apiPackage=com.generated.api.smithyjavajavaruntime.runtime.api,modelPackage=com.generated.api.smithyjavajavaruntime.runtime.model,hideGenerationTimestamp=true,additionalModelTypeAnnotations=@lombok.AllArgsConstructor\\ @lombok.experimental.SuperBuilder" --openapi-normalizer "KEEP_ONLY_FIRST_TAG_IN_OPERATION=true"", + "exec": "npx --yes -p @aws/pdk@$AWS_PDK_VERSION type-safe-api generate --specPath ../../../model/.api.json --outputPath . --templateDirs "java" --metadata '{"groupId":"com.generated.api","artifactId":"smithy-java-java-runtime","artifactVersion":"0.0.0","packageName":"com.generated.api.smithyjavajavaruntime.runtime","srcDir":"src/main/java/com/generated/api/smithyjavajavaruntime/runtime"}'", }, ], }, @@ -42084,13 +41631,6 @@ src/main/AndroidManifest.xml "cwd": "packages/api/generated/runtime/java", }, }, - "create-openapitools.json": { - "executor": "nx:run-commands", - "options": { - "command": "npx projen create-openapitools.json", - "cwd": "packages/api/generated/runtime/java", - }, - }, "default": { "executor": "nx:run-commands", "options": { @@ -59679,8 +59219,6 @@ Each runtime project includes types from your API model, as well as type-safe cl /.gitattributes linguist-generated /.gitignore linguist-generated -/.openapi-generator-ignore linguist-generated -/.pdk/dynamic-files/openapitools.json linguist-generated /.projen/** linguist-generated /.projen/deps.json linguist-generated /.projen/files.json linguist-generated @@ -59698,41 +59236,13 @@ node_modules/ .settings target dist/java -!/.openapi-generator-ignore -!/.pdk/dynamic-files/openapitools.json -/openapitools.json src docs api README.md .openapi-generator +.tsapi-manifest ", - "generated/runtime/java/.openapi-generator-ignore": "# ~~ Generated by projen. To modify, edit .projenrc.js and run "npx projen". -.gitignore -pom.xml -gradle -gradle/**/* -gradlew -gradle.properties -gradlew.bat -build.gradle -settings.gradle -build.sbt -.travis.yml -git_push.sh -src/test -src/test/**/* -src/main/AndroidManifest.xml -", - "generated/runtime/java/.pdk/dynamic-files/openapitools.json": { - "$schema": "node_modules/@openapitools/openapi-generator-cli/config.schema.json", - "//": "~~ Generated by projen. To modify, edit .projenrc.js and run "npx projen".", - "generator-cli": { - "storageDir": "~/.open-api-generator-cli", - "version": "6.3.0", - }, - "spaces": 2, - }, "generated/runtime/java/.projen/deps.json": { "//": "~~ Generated by projen. To modify, edit .projenrc.js and run "npx projen".", "dependencies": [ @@ -59942,8 +59452,6 @@ src/main/AndroidManifest.xml "files": [ ".gitattributes", ".gitignore", - ".openapi-generator-ignore", - ".pdk/dynamic-files/openapitools.json", ".projen/deps.json", ".projen/files.json", ".projen/tasks.json", @@ -59986,14 +59494,6 @@ src/main/AndroidManifest.xml }, ], }, - "create-openapitools.json": { - "name": "create-openapitools.json", - "steps": [ - { - "exec": "cp -f .pdk/dynamic-files/openapitools.json openapitools.json", - }, - ], - }, "default": { "description": "Synthesize project files", "name": "default", @@ -60008,13 +59508,7 @@ src/main/AndroidManifest.xml "name": "generate", "steps": [ { - "spawn": "create-openapitools.json", - }, - { - "exec": "npx --yes -p @aws/pdk@$AWS_PDK_VERSION type-safe-api.clean-openapi-generated-code --code-path .", - }, - { - "exec": "npx --yes -p @aws/pdk@$AWS_PDK_VERSION type-safe-api.generate --generator java --spec-path ../../../model/.api.json --output-path . --generator-dir java --src-dir src/main/java/com/generated/api/smithypythonjavaruntime/runtime --tst-dir test --additional-properties "useSingleRequestParameter=true,groupId=com.generated.api,artifactId=smithy-python-java-runtime,artifactVersion=0.0.0,invokerPackage=com.generated.api.smithypythonjavaruntime.runtime,apiPackage=com.generated.api.smithypythonjavaruntime.runtime.api,modelPackage=com.generated.api.smithypythonjavaruntime.runtime.model,hideGenerationTimestamp=true,additionalModelTypeAnnotations=@lombok.AllArgsConstructor\\ @lombok.experimental.SuperBuilder" --openapi-normalizer "KEEP_ONLY_FIRST_TAG_IN_OPERATION=true"", + "exec": "npx --yes -p @aws/pdk@$AWS_PDK_VERSION type-safe-api generate --specPath ../../../model/.api.json --outputPath . --templateDirs "java" --metadata '{"groupId":"com.generated.api","artifactId":"smithy-python-java-runtime","artifactVersion":"0.0.0","packageName":"com.generated.api.smithypythonjavaruntime.runtime","srcDir":"src/main/java/com/generated/api/smithypythonjavaruntime/runtime"}'", }, ], }, @@ -65005,8 +64499,6 @@ Each runtime project includes types from your API model, as well as type-safe cl /.gitattributes linguist-generated /.gitignore linguist-generated -/.openapi-generator-ignore linguist-generated -/.pdk/dynamic-files/openapitools.json linguist-generated /.projen/** linguist-generated /.projen/deps.json linguist-generated /.projen/files.json linguist-generated @@ -65026,43 +64518,15 @@ node_modules/ .settings target dist/java -!/.openapi-generator-ignore -!/.pdk/dynamic-files/openapitools.json -/openapitools.json src docs api README.md .openapi-generator +.tsapi-manifest !/project.json !/LICENSE ", - "packages/api/generated/runtime/java/.openapi-generator-ignore": "# ~~ Generated by projen. To modify, edit .projenrc.js and run "npx projen". -.gitignore -pom.xml -gradle -gradle/**/* -gradlew -gradle.properties -gradlew.bat -build.gradle -settings.gradle -build.sbt -.travis.yml -git_push.sh -src/test -src/test/**/* -src/main/AndroidManifest.xml -", - "packages/api/generated/runtime/java/.pdk/dynamic-files/openapitools.json": { - "$schema": "node_modules/@openapitools/openapi-generator-cli/config.schema.json", - "//": "~~ Generated by projen. To modify, edit .projenrc.js and run "npx projen".", - "generator-cli": { - "storageDir": "~/.open-api-generator-cli", - "version": "6.3.0", - }, - "spaces": 2, - }, "packages/api/generated/runtime/java/.projen/deps.json": { "//": "~~ Generated by projen. To modify, edit .projenrc.js and run "npx projen".", "dependencies": [ @@ -65272,8 +64736,6 @@ src/main/AndroidManifest.xml "files": [ ".gitattributes", ".gitignore", - ".openapi-generator-ignore", - ".pdk/dynamic-files/openapitools.json", ".projen/deps.json", ".projen/files.json", ".projen/tasks.json", @@ -65318,14 +64780,6 @@ src/main/AndroidManifest.xml }, ], }, - "create-openapitools.json": { - "name": "create-openapitools.json", - "steps": [ - { - "exec": "cp -f .pdk/dynamic-files/openapitools.json openapitools.json", - }, - ], - }, "default": { "description": "Synthesize project files", "name": "default", @@ -65334,13 +64788,7 @@ src/main/AndroidManifest.xml "name": "generate", "steps": [ { - "spawn": "create-openapitools.json", - }, - { - "exec": "npx --yes -p @aws/pdk@$AWS_PDK_VERSION type-safe-api.clean-openapi-generated-code --code-path .", - }, - { - "exec": "npx --yes -p @aws/pdk@$AWS_PDK_VERSION type-safe-api.generate --generator java --spec-path ../../../model/.api.json --output-path . --generator-dir java --src-dir src/main/java/com/generated/api/smithypythonjavaruntime/runtime --tst-dir test --additional-properties "useSingleRequestParameter=true,groupId=com.generated.api,artifactId=smithy-python-java-runtime,artifactVersion=0.0.0,invokerPackage=com.generated.api.smithypythonjavaruntime.runtime,apiPackage=com.generated.api.smithypythonjavaruntime.runtime.api,modelPackage=com.generated.api.smithypythonjavaruntime.runtime.model,hideGenerationTimestamp=true,additionalModelTypeAnnotations=@lombok.AllArgsConstructor\\ @lombok.experimental.SuperBuilder" --openapi-normalizer "KEEP_ONLY_FIRST_TAG_IN_OPERATION=true"", + "exec": "npx --yes -p @aws/pdk@$AWS_PDK_VERSION type-safe-api generate --specPath ../../../model/.api.json --outputPath . --templateDirs "java" --metadata '{"groupId":"com.generated.api","artifactId":"smithy-python-java-runtime","artifactVersion":"0.0.0","packageName":"com.generated.api.smithypythonjavaruntime.runtime","srcDir":"src/main/java/com/generated/api/smithypythonjavaruntime/runtime"}'", }, ], }, @@ -65816,13 +65264,6 @@ src/main/AndroidManifest.xml "cwd": "packages/api/generated/runtime/java", }, }, - "create-openapitools.json": { - "executor": "nx:run-commands", - "options": { - "command": "npx projen create-openapitools.json", - "cwd": "packages/api/generated/runtime/java", - }, - }, "default": { "executor": "nx:run-commands", "options": { @@ -69243,8 +68684,6 @@ Each runtime project includes types from your API model, as well as type-safe cl /.gitattributes linguist-generated /.gitignore linguist-generated -/.openapi-generator-ignore linguist-generated -/.pdk/dynamic-files/openapitools.json linguist-generated /.projen/** linguist-generated /.projen/deps.json linguist-generated /.projen/files.json linguist-generated @@ -69262,41 +68701,13 @@ node_modules/ .settings target dist/java -!/.openapi-generator-ignore -!/.pdk/dynamic-files/openapitools.json -/openapitools.json src docs api README.md .openapi-generator +.tsapi-manifest ", - "generated/runtime/java/.openapi-generator-ignore": "# ~~ Generated by projen. To modify, edit .projenrc.js and run "npx projen". -.gitignore -pom.xml -gradle -gradle/**/* -gradlew -gradle.properties -gradlew.bat -build.gradle -settings.gradle -build.sbt -.travis.yml -git_push.sh -src/test -src/test/**/* -src/main/AndroidManifest.xml -", - "generated/runtime/java/.pdk/dynamic-files/openapitools.json": { - "$schema": "node_modules/@openapitools/openapi-generator-cli/config.schema.json", - "//": "~~ Generated by projen. To modify, edit .projenrc.js and run "npx projen".", - "generator-cli": { - "storageDir": "~/.open-api-generator-cli", - "version": "6.3.0", - }, - "spaces": 2, - }, "generated/runtime/java/.projen/deps.json": { "//": "~~ Generated by projen. To modify, edit .projenrc.js and run "npx projen".", "dependencies": [ @@ -69506,8 +68917,6 @@ src/main/AndroidManifest.xml "files": [ ".gitattributes", ".gitignore", - ".openapi-generator-ignore", - ".pdk/dynamic-files/openapitools.json", ".projen/deps.json", ".projen/files.json", ".projen/tasks.json", @@ -69550,14 +68959,6 @@ src/main/AndroidManifest.xml }, ], }, - "create-openapitools.json": { - "name": "create-openapitools.json", - "steps": [ - { - "exec": "cp -f .pdk/dynamic-files/openapitools.json openapitools.json", - }, - ], - }, "default": { "description": "Synthesize project files", "name": "default", @@ -69572,13 +68973,7 @@ src/main/AndroidManifest.xml "name": "generate", "steps": [ { - "spawn": "create-openapitools.json", - }, - { - "exec": "npx --yes -p @aws/pdk@$AWS_PDK_VERSION type-safe-api.clean-openapi-generated-code --code-path .", - }, - { - "exec": "npx --yes -p @aws/pdk@$AWS_PDK_VERSION type-safe-api.generate --generator java --spec-path ../../../model/.api.json --output-path . --generator-dir java --src-dir src/main/java/com/generated/api/smithytypescriptjavaruntime/runtime --tst-dir test --additional-properties "useSingleRequestParameter=true,groupId=com.generated.api,artifactId=smithy-typescript-java-runtime,artifactVersion=0.0.0,invokerPackage=com.generated.api.smithytypescriptjavaruntime.runtime,apiPackage=com.generated.api.smithytypescriptjavaruntime.runtime.api,modelPackage=com.generated.api.smithytypescriptjavaruntime.runtime.model,hideGenerationTimestamp=true,additionalModelTypeAnnotations=@lombok.AllArgsConstructor\\ @lombok.experimental.SuperBuilder" --openapi-normalizer "KEEP_ONLY_FIRST_TAG_IN_OPERATION=true"", + "exec": "npx --yes -p @aws/pdk@$AWS_PDK_VERSION type-safe-api generate --specPath ../../../model/.api.json --outputPath . --templateDirs "java" --metadata '{"groupId":"com.generated.api","artifactId":"smithy-typescript-java-runtime","artifactVersion":"0.0.0","packageName":"com.generated.api.smithytypescriptjavaruntime.runtime","srcDir":"src/main/java/com/generated/api/smithytypescriptjavaruntime/runtime"}'", }, ], }, @@ -74627,8 +74022,6 @@ Each runtime project includes types from your API model, as well as type-safe cl /.gitattributes linguist-generated /.gitignore linguist-generated -/.openapi-generator-ignore linguist-generated -/.pdk/dynamic-files/openapitools.json linguist-generated /.projen/** linguist-generated /.projen/deps.json linguist-generated /.projen/files.json linguist-generated @@ -74648,43 +74041,15 @@ node_modules/ .settings target dist/java -!/.openapi-generator-ignore -!/.pdk/dynamic-files/openapitools.json -/openapitools.json src docs api README.md .openapi-generator +.tsapi-manifest !/project.json !/LICENSE ", - "packages/api/generated/runtime/java/.openapi-generator-ignore": "# ~~ Generated by projen. To modify, edit .projenrc.js and run "npx projen". -.gitignore -pom.xml -gradle -gradle/**/* -gradlew -gradle.properties -gradlew.bat -build.gradle -settings.gradle -build.sbt -.travis.yml -git_push.sh -src/test -src/test/**/* -src/main/AndroidManifest.xml -", - "packages/api/generated/runtime/java/.pdk/dynamic-files/openapitools.json": { - "$schema": "node_modules/@openapitools/openapi-generator-cli/config.schema.json", - "//": "~~ Generated by projen. To modify, edit .projenrc.js and run "npx projen".", - "generator-cli": { - "storageDir": "~/.open-api-generator-cli", - "version": "6.3.0", - }, - "spaces": 2, - }, "packages/api/generated/runtime/java/.projen/deps.json": { "//": "~~ Generated by projen. To modify, edit .projenrc.js and run "npx projen".", "dependencies": [ @@ -74894,8 +74259,6 @@ src/main/AndroidManifest.xml "files": [ ".gitattributes", ".gitignore", - ".openapi-generator-ignore", - ".pdk/dynamic-files/openapitools.json", ".projen/deps.json", ".projen/files.json", ".projen/tasks.json", @@ -74940,14 +74303,6 @@ src/main/AndroidManifest.xml }, ], }, - "create-openapitools.json": { - "name": "create-openapitools.json", - "steps": [ - { - "exec": "cp -f .pdk/dynamic-files/openapitools.json openapitools.json", - }, - ], - }, "default": { "description": "Synthesize project files", "name": "default", @@ -74956,13 +74311,7 @@ src/main/AndroidManifest.xml "name": "generate", "steps": [ { - "spawn": "create-openapitools.json", - }, - { - "exec": "npx --yes -p @aws/pdk@$AWS_PDK_VERSION type-safe-api.clean-openapi-generated-code --code-path .", - }, - { - "exec": "npx --yes -p @aws/pdk@$AWS_PDK_VERSION type-safe-api.generate --generator java --spec-path ../../../model/.api.json --output-path . --generator-dir java --src-dir src/main/java/com/generated/api/smithytypescriptjavaruntime/runtime --tst-dir test --additional-properties "useSingleRequestParameter=true,groupId=com.generated.api,artifactId=smithy-typescript-java-runtime,artifactVersion=0.0.0,invokerPackage=com.generated.api.smithytypescriptjavaruntime.runtime,apiPackage=com.generated.api.smithytypescriptjavaruntime.runtime.api,modelPackage=com.generated.api.smithytypescriptjavaruntime.runtime.model,hideGenerationTimestamp=true,additionalModelTypeAnnotations=@lombok.AllArgsConstructor\\ @lombok.experimental.SuperBuilder" --openapi-normalizer "KEEP_ONLY_FIRST_TAG_IN_OPERATION=true"", + "exec": "npx --yes -p @aws/pdk@$AWS_PDK_VERSION type-safe-api generate --specPath ../../../model/.api.json --outputPath . --templateDirs "java" --metadata '{"groupId":"com.generated.api","artifactId":"smithy-typescript-java-runtime","artifactVersion":"0.0.0","packageName":"com.generated.api.smithytypescriptjavaruntime.runtime","srcDir":"src/main/java/com/generated/api/smithytypescriptjavaruntime/runtime"}'", }, ], }, @@ -75438,13 +74787,6 @@ src/main/AndroidManifest.xml "cwd": "packages/api/generated/runtime/java", }, }, - "create-openapitools.json": { - "executor": "nx:run-commands", - "options": { - "command": "npx projen create-openapitools.json", - "cwd": "packages/api/generated/runtime/java", - }, - }, "default": { "executor": "nx:run-commands", "options": { diff --git a/packages/type-safe-api/test/project/codegen/types/__snapshots__/generated-java-async-runtime-project.test.ts.snap b/packages/type-safe-api/test/project/codegen/types/__snapshots__/generated-java-async-runtime-project.test.ts.snap index 30fb81213..fa60252f3 100644 --- a/packages/type-safe-api/test/project/codegen/types/__snapshots__/generated-java-async-runtime-project.test.ts.snap +++ b/packages/type-safe-api/test/project/codegen/types/__snapshots__/generated-java-async-runtime-project.test.ts.snap @@ -7,8 +7,6 @@ exports[`Generated Java Async Runtime Unit Tests Synth 1`] = ` /.gitattributes linguist-generated /.github/workflows/pull-request-lint.yml linguist-generated /.gitignore linguist-generated -/.openapi-generator-ignore linguist-generated -/.pdk/dynamic-files/openapitools.json linguist-generated /.projen/** linguist-generated /.projen/deps.json linguist-generated /.projen/files.json linguist-generated @@ -56,42 +54,14 @@ node_modules/ .settings target dist/java -!/.openapi-generator-ignore -!/.pdk/dynamic-files/openapitools.json -/openapitools.json src docs api README.md .openapi-generator +.tsapi-manifest !/src/test/java/projenrc.java ", - ".openapi-generator-ignore": "# ~~ Generated by projen. To modify, edit src/test/java/projenrc.java and run "npx projen". -.gitignore -pom.xml -gradle -gradle/**/* -gradlew -gradle.properties -gradlew.bat -build.gradle -settings.gradle -build.sbt -.travis.yml -git_push.sh -src/test -src/test/**/* -src/main/AndroidManifest.xml -", - ".pdk/dynamic-files/openapitools.json": { - "$schema": "node_modules/@openapitools/openapi-generator-cli/config.schema.json", - "//": "~~ Generated by projen. To modify, edit src/test/java/projenrc.java and run "npx projen".", - "generator-cli": { - "storageDir": "~/.open-api-generator-cli", - "version": "6.3.0", - }, - "spaces": 2, - }, ".projen/deps.json": { "//": "~~ Generated by projen. To modify, edit src/test/java/projenrc.java and run "npx projen".", "dependencies": [ @@ -312,8 +282,6 @@ src/main/AndroidManifest.xml ".gitattributes", ".github/workflows/pull-request-lint.yml", ".gitignore", - ".openapi-generator-ignore", - ".pdk/dynamic-files/openapitools.json", ".projen/deps.json", ".projen/files.json", ".projen/tasks.json", @@ -391,14 +359,6 @@ src/main/AndroidManifest.xml }, ], }, - "create-openapitools.json": { - "name": "create-openapitools.json", - "steps": [ - { - "exec": "cp -f .pdk/dynamic-files/openapitools.json openapitools.json", - }, - ], - }, "default": { "description": "Synthesize project files", "name": "default", @@ -427,13 +387,7 @@ src/main/AndroidManifest.xml "name": "generate", "steps": [ { - "spawn": "create-openapitools.json", - }, - { - "exec": "npx --yes -p @aws/pdk@$AWS_PDK_VERSION type-safe-api.clean-openapi-generated-code --code-path .", - }, - { - "exec": "npx --yes -p @aws/pdk@$AWS_PDK_VERSION type-safe-api.generate --generator java --spec-path my-spec.json --output-path . --generator-dir java-async-runtime --src-dir src/main/java/test/test-java-runtime/runtime --tst-dir test --additional-properties "useSingleRequestParameter=true,groupId=test,artifactId=com.aws.pdk.test,artifactVersion=1.0.0,invokerPackage=test.test-java-runtime.runtime,apiPackage=test.test-java-runtime.runtime.api,modelPackage=test.test-java-runtime.runtime.model,hideGenerationTimestamp=true,additionalModelTypeAnnotations=@lombok.AllArgsConstructor\\ @lombok.experimental.SuperBuilder" --openapi-normalizer "KEEP_ONLY_FIRST_TAG_IN_OPERATION=true"", + "exec": "npx --yes -p @aws/pdk@$AWS_PDK_VERSION type-safe-api generate --specPath my-spec.json --outputPath . --templateDirs "java" --metadata '{"groupId":"test","artifactId":"com.aws.pdk.test","artifactVersion":"1.0.0","packageName":"test.test-java-runtime.runtime","srcDir":"src/main/java/test/test-java-runtime/runtime"}'", }, ], }, diff --git a/packages/type-safe-api/test/project/codegen/types/__snapshots__/generated-java-runtime-project.test.ts.snap b/packages/type-safe-api/test/project/codegen/types/__snapshots__/generated-java-runtime-project.test.ts.snap index 41bbd1bd0..7e9e03036 100644 --- a/packages/type-safe-api/test/project/codegen/types/__snapshots__/generated-java-runtime-project.test.ts.snap +++ b/packages/type-safe-api/test/project/codegen/types/__snapshots__/generated-java-runtime-project.test.ts.snap @@ -7,8 +7,6 @@ exports[`Generated Java Runtime Unit Tests Synth 1`] = ` /.gitattributes linguist-generated /.github/workflows/pull-request-lint.yml linguist-generated /.gitignore linguist-generated -/.openapi-generator-ignore linguist-generated -/.pdk/dynamic-files/openapitools.json linguist-generated /.projen/** linguist-generated /.projen/deps.json linguist-generated /.projen/files.json linguist-generated @@ -56,42 +54,14 @@ node_modules/ .settings target dist/java -!/.openapi-generator-ignore -!/.pdk/dynamic-files/openapitools.json -/openapitools.json src docs api README.md .openapi-generator +.tsapi-manifest !/src/test/java/projenrc.java ", - ".openapi-generator-ignore": "# ~~ Generated by projen. To modify, edit src/test/java/projenrc.java and run "npx projen". -.gitignore -pom.xml -gradle -gradle/**/* -gradlew -gradle.properties -gradlew.bat -build.gradle -settings.gradle -build.sbt -.travis.yml -git_push.sh -src/test -src/test/**/* -src/main/AndroidManifest.xml -", - ".pdk/dynamic-files/openapitools.json": { - "$schema": "node_modules/@openapitools/openapi-generator-cli/config.schema.json", - "//": "~~ Generated by projen. To modify, edit src/test/java/projenrc.java and run "npx projen".", - "generator-cli": { - "storageDir": "~/.open-api-generator-cli", - "version": "6.3.0", - }, - "spaces": 2, - }, ".projen/deps.json": { "//": "~~ Generated by projen. To modify, edit src/test/java/projenrc.java and run "npx projen".", "dependencies": [ @@ -312,8 +282,6 @@ src/main/AndroidManifest.xml ".gitattributes", ".github/workflows/pull-request-lint.yml", ".gitignore", - ".openapi-generator-ignore", - ".pdk/dynamic-files/openapitools.json", ".projen/deps.json", ".projen/files.json", ".projen/tasks.json", @@ -391,14 +359,6 @@ src/main/AndroidManifest.xml }, ], }, - "create-openapitools.json": { - "name": "create-openapitools.json", - "steps": [ - { - "exec": "cp -f .pdk/dynamic-files/openapitools.json openapitools.json", - }, - ], - }, "default": { "description": "Synthesize project files", "name": "default", @@ -427,13 +387,7 @@ src/main/AndroidManifest.xml "name": "generate", "steps": [ { - "spawn": "create-openapitools.json", - }, - { - "exec": "npx --yes -p @aws/pdk@$AWS_PDK_VERSION type-safe-api.clean-openapi-generated-code --code-path .", - }, - { - "exec": "npx --yes -p @aws/pdk@$AWS_PDK_VERSION type-safe-api.generate --generator java --spec-path my-spec.json --output-path . --generator-dir java --src-dir src/main/java/test/test-java-runtime/runtime --tst-dir test --additional-properties "useSingleRequestParameter=true,groupId=test,artifactId=com.aws.pdk.test,artifactVersion=1.0.0,invokerPackage=test.test-java-runtime.runtime,apiPackage=test.test-java-runtime.runtime.api,modelPackage=test.test-java-runtime.runtime.model,hideGenerationTimestamp=true,additionalModelTypeAnnotations=@lombok.AllArgsConstructor\\ @lombok.experimental.SuperBuilder" --openapi-normalizer "KEEP_ONLY_FIRST_TAG_IN_OPERATION=true"", + "exec": "npx --yes -p @aws/pdk@$AWS_PDK_VERSION type-safe-api generate --specPath my-spec.json --outputPath . --templateDirs "java" --metadata '{"groupId":"test","artifactId":"com.aws.pdk.test","artifactVersion":"1.0.0","packageName":"test.test-java-runtime.runtime","srcDir":"src/main/java/test/test-java-runtime/runtime"}'", }, ], }, diff --git a/packages/type-safe-api/test/project/type-safe-api-project.test.ts b/packages/type-safe-api/test/project/type-safe-api-project.test.ts index beaf75009..b9577a430 100644 --- a/packages/type-safe-api/test/project/type-safe-api-project.test.ts +++ b/packages/type-safe-api/test/project/type-safe-api-project.test.ts @@ -6,8 +6,6 @@ import { NodePackageManager } from "projen/lib/javascript"; import { synthProject, synthSmithyProject } from "./snapshot-utils"; import { DocumentationFormat, - GeneratedJavaInfrastructureOptions, - GeneratedJavaRuntimeOptions, Language, Library, ModelLanguage, @@ -382,19 +380,9 @@ describe("Type Safe Api Project Unit Tests", () => { ), infrastructure: { language: Language.JAVA, - options: { - java: { - openApiGeneratorCliConfig, - } satisfies Partial as any, - }, }, runtime: { languages: [Language.JAVA], - options: { - java: { - openApiGeneratorCliConfig, - } satisfies Partial as any, - }, }, model: { language: ModelLanguage.SMITHY, @@ -427,22 +415,6 @@ describe("Type Safe Api Project Unit Tests", () => { const snapshot = synthSmithyProject(project); - expect( - snapshot[ - `${path.relative( - project.outdir, - project.infrastructure.java!.outdir - )}/.pdk/dynamic-files/openapitools.json` - ] - ).toMatchSnapshot(); - expect( - snapshot[ - `${path.relative( - project.outdir, - project.runtime.java!.outdir - )}/.pdk/dynamic-files/openapitools.json` - ] - ).toMatchSnapshot(); expect( snapshot[ `${path.relative( diff --git a/packages/type-safe-api/test/resources/specs/edge-cases.yaml b/packages/type-safe-api/test/resources/specs/edge-cases.yaml index e5bb865c1..49ce1f1db 100644 --- a/packages/type-safe-api/test/resources/specs/edge-cases.yaml +++ b/packages/type-safe-api/test/resources/specs/edge-cases.yaml @@ -78,6 +78,10 @@ paths: items: type: number format: double + - in: query + name: my-enum-request-param + schema: + $ref: "#/components/schemas/MyEnum" responses: 200: description: ok diff --git a/packages/type-safe-api/test/scripts/generators/__snapshots__/docs.test.ts.snap b/packages/type-safe-api/test/scripts/generators/__snapshots__/docs.test.ts.snap index 906f54fda..2c7160dcf 100644 --- a/packages/type-safe-api/test/scripts/generators/__snapshots__/docs.test.ts.snap +++ b/packages/type-safe-api/test/scripts/generators/__snapshots__/docs.test.ts.snap @@ -5359,17 +5359,17 @@ Models/TestResponseMessagesInner.md", # **anyRequestResponse** -> any anyRequestResponse(body) +> Object anyRequestResponse(body) ### Parameters |Name | Type | Description | Notes | |------------- | ------------- | ------------- | -------------| -| **body** | **any**| | [optional] | +| **body** | **Object**| | [optional] | ### Return type -**any** +**Object** ### HTTP request headers @@ -5457,13 +5457,13 @@ This endpoint does not need any parameters. |Name | Type | Description | Notes | |------------- | ------------- | ------------- | -------------| | **param1** | **String**| This is parameter 1 | | -| **param2** | **List**| This is parameter 2 | | +| **param2** | **List**| This is parameter 2 | | | **param3** | **BigDecimal**| | | | **pathParam** | **String**| | | | **x-header-param** | **String**| This is a header parameter | | | **body** | [**TestRequest**](../Models/TestRequest.md)| | | | **param4** | **String**| | [optional] | -| **x-multi-value-header-param** | **List**| | [optional] | +| **x-multi-value-header-param** | **List**| | [optional] | ### Return type diff --git a/packages/type-safe-api/test/scripts/generators/__snapshots__/java.test.ts.snap b/packages/type-safe-api/test/scripts/generators/__snapshots__/java.test.ts.snap index 366cf9f75..1887efba3 100644 --- a/packages/type-safe-api/test/scripts/generators/__snapshots__/java.test.ts.snap +++ b/packages/type-safe-api/test/scripts/generators/__snapshots__/java.test.ts.snap @@ -2,136 +2,7 @@ exports[`Java Client Code Generation Script Unit Tests Generates With allof-model.yaml 1`] = ` { - ".github/workflows/maven.yml": "# This workflow will build a Java project with Maven, and cache/restore any dependencies to improve the workflow execution time -# For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-maven -# -# This file is auto-generated by OpenAPI Generator (https://openapi-generator.tech) - -name: Java CI with Maven - -on: - push: - branches: [ main, master ] - pull_request: - branches: [ main, master ] - -jobs: - build: - name: Build My API - runs-on: ubuntu-latest - strategy: - matrix: - java: [ '8' ] - steps: - - uses: actions/checkout@v2 - - name: Set up JDK - uses: actions/setup-java@v2 - with: - java-version: \${{ matrix.java }} - distribution: 'temurin' - cache: maven - - name: Build with Maven - run: mvn -B package --no-transfer-progress --file pom.xml -", - ".gitignore": "*.class - -# Mobile Tools for Java (J2ME) -.mtj.tmp/ - -# Package Files # -*.jar -*.war -*.ear - -# exclude jar for gradle wrapper -!gradle/wrapper/*.jar - -# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml -hs_err_pid* - -# build files -**/target -target -.gradle -build -", - ".openapi-generator-ignore": "# OpenAPI Generator Ignore -# Generated by openapi-generator https://github.com/openapitools/openapi-generator - -# Use this file to prevent files from being overwritten by the generator. -# The patterns follow closely to .gitignore or .dockerignore. - -# As an example, the C# client generator defines ApiClient.cs. -# You can make changes and tell OpenAPI Generator to ignore just this file by uncommenting the following line: -#ApiClient.cs - -# You can match any string of characters against a directory, file or extension with a single asterisk (*): -#foo/*/qux -# The above matches foo/bar/qux and foo/baz/qux, but not foo/bar/baz/qux - -# You can recursively match patterns against a directory, file or extension with a double asterisk (**): -#foo/**/qux -# This matches foo/bar/qux, foo/baz/qux, and foo/bar/baz/qux - -# You can also negate patterns with an exclamation (!). -# For example, you can ignore all files in a docs folder with the file extension .md: -#docs/*.md -# Then explicitly reverse the ignore rule for a single file: -#!docs/README.md -", - ".openapi-generator/FILES": ".github/workflows/maven.yml -.gitignore -.openapi-generator-ignore -.travis.yml -README.md -api/openapi.yaml -build.gradle -build.sbt -docs/DefaultApi.md -docs/Template.md -docs/TemplateBase.md -docs/TemplateBody.md -git_push.sh -gradle.properties -gradle/wrapper/gradle-wrapper.jar -gradle/wrapper/gradle-wrapper.properties -gradlew -gradlew.bat -pom.xml -settings.gradle -src/main/AndroidManifest.xml -src/main/java/test/test/runtime/ApiCallback.java -src/main/java/test/test/runtime/ApiClient.java -src/main/java/test/test/runtime/ApiException.java -src/main/java/test/test/runtime/ApiResponse.java -src/main/java/test/test/runtime/Configuration.java -src/main/java/test/test/runtime/GzipRequestInterceptor.java -src/main/java/test/test/runtime/JSON.java -src/main/java/test/test/runtime/Pair.java -src/main/java/test/test/runtime/ProgressRequestBody.java -src/main/java/test/test/runtime/ProgressResponseBody.java -src/main/java/test/test/runtime/ServerConfiguration.java -src/main/java/test/test/runtime/ServerVariable.java -src/main/java/test/test/runtime/StringUtil.java -src/main/java/test/test/runtime/__handlers.java -src/main/java/test/test/runtime/__interceptors.java -src/main/java/test/test/runtime/api/DefaultApi.java -src/main/java/test/test/runtime/api/operation_config/OperationConfig.java -src/main/java/test/test/runtime/api/operation_config/OperationLookup.java -src/main/java/test/test/runtime/api/operation_config/Operations.java -src/main/java/test/test/runtime/auth/ApiKeyAuth.java -src/main/java/test/test/runtime/auth/Authentication.java -src/main/java/test/test/runtime/auth/HttpBasicAuth.java -src/main/java/test/test/runtime/auth/HttpBearerAuth.java -src/main/java/test/test/runtime/model/AbstractOpenApiSchema.java -src/main/java/test/test/runtime/model/Template.java -src/main/java/test/test/runtime/model/TemplateBase.java -src/main/java/test/test/runtime/model/TemplateBody.java -src/test/java/test/test/runtime/api/DefaultApiTest.java -src/test/java/test/test/runtime/model/TemplateBaseTest.java -src/test/java/test/test/runtime/model/TemplateBodyTest.java -src/test/java/test/test/runtime/model/TemplateTest.java -src/main/java/test/test/runtime/api/handlers/Handlers.java + ".tsapi-manifest": "src/main/java/test/test/runtime/api/handlers/Handlers.java src/main/java/test/test/runtime/api/handlers/Response.java src/main/java/test/test/runtime/api/handlers/ApiResponse.java src/main/java/test/test/runtime/api/handlers/Interceptor.java @@ -153,357 +24,40 @@ src/main/java/test/test/runtime/api/interceptors/ResponseHeadersInterceptor.java src/main/java/test/test/runtime/api/interceptors/powertools/LoggingInterceptor.java src/main/java/test/test/runtime/api/interceptors/powertools/TracingInterceptor.java src/main/java/test/test/runtime/api/interceptors/powertools/MetricsInterceptor.java -src/main/java/test/test/runtime/api/interceptors/DefaultInterceptors.java", - ".openapi-generator/VERSION": "6.3.0", - ".pdk/dynamic-files/openapitools.json": "{ - "$schema": "node_modules/@openapitools/openapi-generator-cli/config.schema.json", - "spaces": 2, - "generator-cli": { - "version": "6.3.0", - "storageDir": "~/.open-api-generator-cli" - }, - "//": "~~ Generated by projen. To modify, edit src/test/java/projenrc.java and run \\"npx projen\\"." -} -", - "README.md": "# com.aws.pdk.test - -My API -- API version: 1.0.0 - -See https://github.com/aws/aws-pdk/issues/841 - - -*Automatically generated by the [OpenAPI Generator](https://openapi-generator.tech)* - - -## Requirements - -Building the API client library requires: -1. Java 1.8+ -2. Maven (3.8.3+)/Gradle (7.2+) - -## Installation - -To install the API client library to your local Maven repository, simply execute: - -\`\`\`shell -mvn clean install -\`\`\` - -To deploy it to a remote Maven repository instead, configure the settings of the repository and execute: - -\`\`\`shell -mvn clean deploy -\`\`\` - -Refer to the [OSSRH Guide](http://central.sonatype.org/pages/ossrh-guide.html) for more information. - -### Maven users - -Add this dependency to your project's POM: - -\`\`\`xml - - test - com.aws.pdk.test - 1.0.0 - compile - -\`\`\` - -### Gradle users - -Add this dependency to your project's build file: - -\`\`\`groovy - repositories { - mavenCentral() // Needed if the 'com.aws.pdk.test' jar has been published to maven central. - mavenLocal() // Needed if the 'com.aws.pdk.test' jar has been published to the local maven repo. - } - - dependencies { - implementation "test:com.aws.pdk.test:1.0.0" - } -\`\`\` - -### Others - -At first generate the JAR by executing: - -\`\`\`shell -mvn clean package -\`\`\` - -Then manually install the following JARs: - -* \`target/com.aws.pdk.test-1.0.0.jar\` -* \`target/lib/*.jar\` - -## Getting Started - -Please follow the [installation](#installation) instruction and execute the following Java code: - -\`\`\`java - -// Import classes: -import test.test.runtime.ApiClient; -import test.test.runtime.ApiException; -import test.test.runtime.Configuration; -import test.test.runtime.models.*; -import test.test.runtime.api.DefaultApi; - -public class Example { - public static void main(String[] args) { - ApiClient defaultClient = Configuration.getDefaultApiClient(); - defaultClient.setBasePath("http://localhost"); - - DefaultApi apiInstance = new DefaultApi(defaultClient); - try { - Template result = apiInstance.sayHello() - .execute(); - System.out.println(result); - } catch (ApiException e) { - System.err.println("Exception when calling DefaultApi#sayHello"); - System.err.println("Status code: " + e.getCode()); - System.err.println("Reason: " + e.getResponseBody()); - System.err.println("Response headers: " + e.getResponseHeaders()); - e.printStackTrace(); - } - } -} - -\`\`\` - -## Documentation for API Endpoints - -All URIs are relative to *http://localhost* - -Class | Method | HTTP request | Description ------------- | ------------- | ------------- | ------------- -*DefaultApi* | [**sayHello**](docs/DefaultApi.md#sayHello) | **GET** /hello | - - -## Documentation for Models - - - [Template](docs/Template.md) - - [TemplateBase](docs/TemplateBase.md) - - [TemplateBody](docs/TemplateBody.md) - - -## Documentation for Authorization - -All endpoints do not require authorization. -Authentication schemes defined for the API: - -## Recommendation - -It's recommended to create an instance of \`ApiClient\` per thread in a multithreaded environment to avoid any potential issues. - -## Author - - - -", - "api/openapi.yaml": "openapi: 3.0.3 -info: - description: See https://github.com/aws/aws-pdk/issues/841 - title: My API - version: 1.0.0 -servers: -- url: / -paths: - /hello: - get: - operationId: sayHello - responses: - "200": - content: - application/json: - schema: - $ref: '#/components/schemas/Template' - description: Successful response - x-accepts: application/json -components: - schemas: - Template: - allOf: - - $ref: '#/components/schemas/TemplateBase' - - $ref: '#/components/schemas/TemplateBody' - - description: | - Represents a complete template in the system. - title: Template - TemplateBase: - additionalProperties: false - description: | - Represents the base properties of a template. - properties: - id: - description: The unique identifier for a template. - format: uuid - maxLength: 36 - title: TemplateID - type: string - required: - - id - title: TemplateBase - type: object - TemplateBody: - additionalProperties: false - description: | - Represents the body of a template. - properties: - parent_id: - description: The unique identifier for a template. - format: uuid - maxLength: 36 - title: TemplateID - type: string - boolean: - description: A boolean value. - type: boolean - title: TemplateBody - type: object - TemplateID: - description: The unique identifier for a template. - format: uuid - maxLength: 36 - title: TemplateID - type: string - -", - "docs/DefaultApi.md": "# DefaultApi - -All URIs are relative to *http://localhost* - -| Method | HTTP request | Description | -|------------- | ------------- | -------------| -| [**sayHello**](DefaultApi.md#sayHello) | **GET** /hello | | - - - -# **sayHello** -> Template sayHello().execute(); - - - -### Example -\`\`\`java -// Import classes: -import test.test.runtime.ApiClient; -import test.test.runtime.ApiException; -import test.test.runtime.Configuration; -import test.test.runtime.models.*; -import test.test.runtime.api.DefaultApi; - -public class Example { - public static void main(String[] args) { - ApiClient defaultClient = Configuration.getDefaultApiClient(); - defaultClient.setBasePath("http://localhost"); - - DefaultApi apiInstance = new DefaultApi(defaultClient); - try { - Template result = apiInstance.sayHello() - .execute(); - System.out.println(result); - } catch (ApiException e) { - System.err.println("Exception when calling DefaultApi#sayHello"); - System.err.println("Status code: " + e.getCode()); - System.err.println("Reason: " + e.getResponseBody()); - System.err.println("Response headers: " + e.getResponseHeaders()); - e.printStackTrace(); - } - } -} -\`\`\` - -### Parameters -This endpoint does not need any parameter. - -### Return type - -[**Template**](Template.md) - -### Authorization - -No authorization required - -### HTTP request headers - - - **Content-Type**: Not defined - - **Accept**: application/json - -### HTTP response details -| Status code | Description | Response headers | -|-------------|-------------|------------------| -| **200** | Successful response | - | - -", - "docs/Template.md": " - -# Template - - -## Properties - -| Name | Type | Description | Notes | -|------------ | ------------- | ------------- | -------------| -|**id** | **UUID** | The unique identifier for a template. | | -|**parentId** | **UUID** | The unique identifier for a template. | [optional] | -|**_boolean** | **Boolean** | A boolean value. | [optional] | - - - -", - "docs/TemplateBase.md": " - -# TemplateBase - -Represents the base properties of a template. - -## Properties - -| Name | Type | Description | Notes | -|------------ | ------------- | ------------- | -------------| -|**id** | **UUID** | The unique identifier for a template. | | - - - -", - "docs/TemplateBody.md": " - -# TemplateBody - -Represents the body of a template. - -## Properties - -| Name | Type | Description | Notes | -|------------ | ------------- | ------------- | -------------| -|**parentId** | **UUID** | The unique identifier for a template. | [optional] | -|**_boolean** | **Boolean** | A boolean value. | [optional] | - - - -", - "openapitools.json": "{ - "$schema": "node_modules/@openapitools/openapi-generator-cli/config.schema.json", - "spaces": 2, - "generator-cli": { - "version": "6.3.0", - "storageDir": "~/.open-api-generator-cli" - }, - "//": "~~ Generated by projen. To modify, edit src/test/java/projenrc.java and run \\"npx projen\\"." -} -", +src/main/java/test/test/runtime/api/interceptors/DefaultInterceptors.java +src/main/java/test/test/runtime/api/operation_config/OperationConfig.java +src/main/java/test/test/runtime/api/operation_config/OperationLookup.java +src/main/java/test/test/runtime/api/operation_config/Operations.java +src/main/java/test/test/runtime/api/DefaultApi.java +src/main/java/test/test/runtime/auth/ApiKeyAuth.java +src/main/java/test/test/runtime/auth/Authentication.java +src/main/java/test/test/runtime/auth/HttpBasicAuth.java +src/main/java/test/test/runtime/auth/HttpBearerAuth.java +src/main/java/test/test/runtime/ApiCallback.java +src/main/java/test/test/runtime/ApiClient.java +src/main/java/test/test/runtime/ApiException.java +src/main/java/test/test/runtime/ApiResponse.java +src/main/java/test/test/runtime/Configuration.java +src/main/java/test/test/runtime/GzipRequestInterceptor.java +src/main/java/test/test/runtime/JSON.java +src/main/java/test/test/runtime/Pair.java +src/main/java/test/test/runtime/ProgressRequestBody.java +src/main/java/test/test/runtime/ProgressResponseBody.java +src/main/java/test/test/runtime/ServerConfiguration.java +src/main/java/test/test/runtime/ServerVariable.java +src/main/java/test/test/runtime/StringUtil.java +src/main/java/test/test/runtime/model/AbstractOpenApiSchema.java +src/main/java/test/test/runtime/model/Template.java +src/main/java/test/test/runtime/model/TemplateBase.java +src/main/java/test/test/runtime/model/TemplateBody.java", "src/main/java/test/test/runtime/ApiCallback.java": "/* * My API * See https://github.com/aws/aws-pdk/issues/841 * * The version of the OpenAPI document: 1.0.0 - * * - * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). - * https://openapi-generator.tech + * + * NOTE: This class is auto generated. * Do not edit the class manually. */ @@ -563,10 +117,9 @@ public interface ApiCallback { * See https://github.com/aws/aws-pdk/issues/841 * * The version of the OpenAPI document: 1.0.0 - * * - * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). - * https://openapi-generator.tech + * + * NOTE: This class is auto generated. * Do not edit the class manually. */ @@ -698,7 +251,7 @@ public class ApiClient { json = new JSON(); // Set default User-Agent. - setUserAgent("OpenAPI-Generator/1.0.0/java"); + setUserAgent("OpenAPI-Generator/0.0.0/java"); authentications = new HashMap(); } @@ -1950,10 +1503,10 @@ public class ApiClient { /** * Add a Content-Disposition Header for the given key and file to the MultipartBody Builder. * - * @param mpBuilder MultipartBody.Builder + * @param mpBuilder MultipartBody.Builder * @param key The key of the Header element * @param file The file to add to the Header - */ + */ private void addPartToMultiPartBuilder(MultipartBody.Builder mpBuilder, String key, File file) { Headers partHeaders = Headers.of("Content-Disposition", "form-data; name=\\"" + key + "\\"; filename=\\"" + file.getName() + "\\""); MediaType mediaType = MediaType.parse(guessContentTypeFromFile(file)); @@ -2110,10 +1663,9 @@ public class ApiClient { * See https://github.com/aws/aws-pdk/issues/841 * * The version of the OpenAPI document: 1.0.0 - * * - * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). - * https://openapi-generator.tech + * + * NOTE: This class is auto generated. * Do not edit the class manually. */ @@ -2129,12 +1681,11 @@ import javax.ws.rs.core.GenericType; *

ApiException class.

*/ @SuppressWarnings("serial") -@javax.annotation.Generated(value = "org.openapitools.codegen.languages.JavaClientCodegen") public class ApiException extends Exception { private int code = 0; private Map> responseHeaders = null; private String responseBody = null; - + /** *

Constructor for ApiException.

*/ @@ -2277,10 +1828,9 @@ public class ApiException extends Exception { * See https://github.com/aws/aws-pdk/issues/841 * * The version of the OpenAPI document: 1.0.0 - * * - * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). - * https://openapi-generator.tech + * + * NOTE: This class is auto generated. * Do not edit the class manually. */ @@ -2333,7 +1883,7 @@ public class ApiResponse { /** *

Get the headers.

* - * @return a {@link java.util.Map} of headers + * @return a {@link java.util.Map} of headers */ public Map> getHeaders() { return headers; @@ -2354,17 +1904,15 @@ public class ApiResponse { * See https://github.com/aws/aws-pdk/issues/841 * * The version of the OpenAPI document: 1.0.0 - * * - * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). - * https://openapi-generator.tech + * + * NOTE: This class is auto generated. * Do not edit the class manually. */ package test.test.runtime; -@javax.annotation.Generated(value = "org.openapitools.codegen.languages.JavaClientCodegen") public class Configuration { private static ApiClient defaultApiClient = new ApiClient(); @@ -2394,10 +1942,9 @@ public class Configuration { * See https://github.com/aws/aws-pdk/issues/841 * * The version of the OpenAPI document: 1.0.0 - * * - * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). - * https://openapi-generator.tech + * + * NOTE: This class is auto generated. * Do not edit the class manually. */ @@ -2480,10 +2027,9 @@ class GzipRequestInterceptor implements Interceptor { * See https://github.com/aws/aws-pdk/issues/841 * * The version of the OpenAPI document: 1.0.0 - * * - * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). - * https://openapi-generator.tech + * + * NOTE: This class is auto generated. * Do not edit the class manually. */ @@ -2884,17 +2430,15 @@ public class JSON { * See https://github.com/aws/aws-pdk/issues/841 * * The version of the OpenAPI document: 1.0.0 - * * - * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). - * https://openapi-generator.tech + * + * NOTE: This class is auto generated. * Do not edit the class manually. */ package test.test.runtime; -@javax.annotation.Generated(value = "org.openapitools.codegen.languages.JavaClientCodegen") public class Pair { private String name = ""; private String value = ""; @@ -2942,10 +2486,9 @@ public class Pair { * See https://github.com/aws/aws-pdk/issues/841 * * The version of the OpenAPI document: 1.0.0 - * * - * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). - * https://openapi-generator.tech + * + * NOTE: This class is auto generated. * Do not edit the class manually. */ @@ -3016,10 +2559,9 @@ public class ProgressRequestBody extends RequestBody { * See https://github.com/aws/aws-pdk/issues/841 * * The version of the OpenAPI document: 1.0.0 - * * - * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). - * https://openapi-generator.tech + * + * NOTE: This class is auto generated. * Do not edit the class manually. */ @@ -3170,10 +2712,9 @@ public class ServerVariable { * See https://github.com/aws/aws-pdk/issues/841 * * The version of the OpenAPI document: 1.0.0 - * * - * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). - * https://openapi-generator.tech + * + * NOTE: This class is auto generated. * Do not edit the class manually. */ @@ -3183,7 +2724,6 @@ package test.test.runtime; import java.util.Collection; import java.util.Iterator; -@javax.annotation.Generated(value = "org.openapitools.codegen.languages.JavaClientCodegen") public class StringUtil { /** * Check if the given array contains the given value (with case-insensitive comparison). @@ -3254,10 +2794,9 @@ public class StringUtil { * See https://github.com/aws/aws-pdk/issues/841 * * The version of the OpenAPI document: 1.0.0 - * * - * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). - * https://openapi-generator.tech + * + * NOTE: This class is auto generated. * Do not edit the class manually. */ @@ -3278,6 +2817,8 @@ import com.google.gson.reflect.TypeToken; import java.io.IOException; +import java.math.BigDecimal; +import java.io.File; import test.test.runtime.model.Template; import java.lang.reflect.Type; @@ -3368,19 +2909,20 @@ public class DefaultApi { return localVarApiClient.buildCall(basePath, localVarPath, "GET", localVarQueryParams, localVarCollectionQueryParams, localVarPostBody, localVarHeaderParams, localVarCookieParams, localVarFormParams, localVarAuthNames, _callback); } + @SuppressWarnings("rawtypes") private okhttp3.Call sayHelloValidateBeforeCall(final ApiCallback _callback) throws ApiException { return sayHelloCall(_callback); } - private ApiResponse