From 965a94eae275520bc7d7477f067d1e2ec9075095 Mon Sep 17 00:00:00 2001 From: Nathan Force Date: Wed, 11 Jan 2023 10:27:55 -0500 Subject: [PATCH 1/2] generate resources with smaller footprint by using client --- .../codegen/CodegenOperation.java | 9 + .../TypeScriptAxiosClientCodegen.java | 1 + .../resources/typescript-axios/api.mustache | 68 +++---- .../typescript-axios/client.mustache | 173 ++++++++++++++++++ 4 files changed, 218 insertions(+), 33 deletions(-) create mode 100644 modules/openapi-generator/src/main/resources/typescript-axios/client.mustache diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/CodegenOperation.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/CodegenOperation.java index f18293eaf20d..6d7daf244663 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/CodegenOperation.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/CodegenOperation.java @@ -87,6 +87,15 @@ public boolean getHasQueryParams() { return nonempty(queryParams); } + /** + * Check if there's body, form, query, or path params + * + * @return true if any exist, false otherwise + */ + public boolean getHasInputParams() { + return getHasFormParams() || getHasBodyParam() || getHasQueryParams() || getHasPathParams(); + } + /** * Check if there's at least one header parameter * diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TypeScriptAxiosClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TypeScriptAxiosClientCodegen.java index 5899f5403c51..b1d5d0a66961 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TypeScriptAxiosClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TypeScriptAxiosClientCodegen.java @@ -47,6 +47,7 @@ public TypeScriptAxiosClientCodegen() { modelTemplateFiles.put("model.mustache", ".ts"); apiTemplateFiles.put("api.mustache", ".ts"); + supportingFiles.add(new SupportingFile("client.mustache", "", "client.ts")); modelPackage = "models"; apiPackage = "resources"; diff --git a/modules/openapi-generator/src/main/resources/typescript-axios/api.mustache b/modules/openapi-generator/src/main/resources/typescript-axios/api.mustache index 79152ba0435b..93f308158910 100644 --- a/modules/openapi-generator/src/main/resources/typescript-axios/api.mustache +++ b/modules/openapi-generator/src/main/resources/typescript-axios/api.mustache @@ -1,7 +1,8 @@ {{>licenseInfo}} -import axios, { AxiosRequestConfig, Method } from 'axios'; +import type { AxiosRequestConfig } from 'axios'; + +import { mamba } from '../index'; -import { axios as axiosClient, MambaApiError } from '../axios.config'; {{#hasFormParams}} import { stringify } from 'qs'; {{/hasFormParams}} @@ -28,39 +29,40 @@ export class {{classname}}Resource { {{#allParams}}{{^isCookieParam}}{{^isHeaderParam}}* @param {{paramName}} {{description}} {{/isHeaderParam}}{{/isCookieParam}}{{/allParams}} */ - public {{nickname}}({{#allParams}}{{^isQueryParam}}{{^isCookieParam}}{{^isHeaderParam}}{{^isBodyParam}}{{paramName}}{{/isBodyParam}}{{#isBodyParam}}body{{/isBodyParam}}{{^required}}?{{/required}}: {{{dataType}}}, {{/isHeaderParam}}{{/isCookieParam}}{{/isQueryParam}}{{/allParams}}{{#hasQueryParams}}query?: { {{#queryParams}}{{#lambda.snakecase_param}}{{paramName}}{{/lambda.snakecase_param}}{{^required}}?{{/required}}: {{{dataType}}}{{^-last}},{{/-last}} {{/queryParams}}}, {{/hasQueryParams}}axiosConfig?: AxiosRequestConfig): Promise<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}{}{{/returnType}}> { - const apiRoute = '{{path}}'; - const reqPath = apiRoute{{#pathParams}} - .replace('{' + '{{baseName}}}', String({{paramName}}{{#isDateTime}}.toISOString(){{/isDateTime}})){{/pathParams}}; -{{#hasFormParams}} - let reqFormParams = { -{{#formParams}} - {{baseName}}: {{paramName}}{{^-last}},{{/-last}} -{{/formParams}} - }; -{{/hasFormParams}} - const reqConfig = { - ...axiosConfig, - method: '{{httpMethod}}' as Method, - url: reqPath{{#hasQueryParams}}, - params: query{{/hasQueryParams}}{{#bodyParam}}, - data: body{{/bodyParam}}{{#hasFormParams}}, - headers: { 'Content-Type': 'application/x-www-form-urlencoded'}, - data: stringify(reqFormParams), -{{/hasFormParams}} + public {{nickname}}({{#allParams}}{{^isQueryParam}}{{^isCookieParam}}{{^isHeaderParam}}{{^isBodyParam}}{{paramName}}{{/isBodyParam}}{{#isBodyParam}}body{{/isBodyParam}}{{^required}}?{{/required}}: {{{dataType}}}, {{/isHeaderParam}}{{/isCookieParam}}{{/isQueryParam}}{{/allParams}}{{#hasQueryParams}}query?: { {{#queryParams}}{{#lambda.snakecase_param}}{{paramName}}{{/lambda.snakecase_param}}{{^required}}?{{/required}}: {{{dataType}}}{{^-last}},{{/-last}} {{/queryParams}}}, {{/hasQueryParams}}axiosConfig?: AxiosRequestConfig): Promise<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}void{{/returnType}}> { + const url = '{{httpMethod}} {{path}}' as const; + + {{#hasInputParams}} + const input = { + {{#pathParams}} + {{#lambda.snakecase_param}}{{paramName}}{{/lambda.snakecase_param}}: {{paramName}}, + {{/pathParams}} + + {{#hasQueryParams}} + query, + {{/hasQueryParams}} + + {{#hasBodyParam}} + data: body, + {{/hasBodyParam}} + + {{#hasFormParams}} + form: { + {{#formParams}} + {{#lambda.snakecase_param}}{{baseName}}{{/lambda.snakecase_param}}: {{paramName}}{{^-last}},{{/-last}} + {{/formParams}} + } + {{/hasFormParams}} }; - return axiosClient.request(reqConfig) - .then(res => { - return res.data; - }) - .catch(error => { - if (axios.isAxiosError(error)) { - throw new MambaApiError({ apiRoute, error }); - } else { - throw error; - } - }); + + return mamba.request(url, input, axiosConfig); + {{/hasInputParams}} + + {{^hasInputParams}} + return mamba.request(url, axiosConfig); + {{/hasInputParams}} + } {{/operation}} diff --git a/modules/openapi-generator/src/main/resources/typescript-axios/client.mustache b/modules/openapi-generator/src/main/resources/typescript-axios/client.mustache new file mode 100644 index 000000000000..5a93f7804995 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/typescript-axios/client.mustache @@ -0,0 +1,173 @@ +/* tslint:disable */ +/* eslint-disable */ +{{>licenseInfo}} +import axios, { AxiosRequestConfig, Method, AxiosInstance, AxiosError } from 'axios'; +import qs from 'qs'; +import { HasRequiredKeys } from 'type-fest'; + + +{{#models}} +{{#model}}import { {{classname}} } from './models/{{classname}}';{{/model}} +{{/models}} + +type RequestInputOutput = { +{{#apiInfo}} +{{#apis}} +{{#operations}} +{{#operation}} +{{^hasInputParams}} + '{{#lambda.uppercase}}{{httpMethod}}{{/lambda.uppercase}} {{{path}}}': [ + input: never, response: {{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}void{{/returnType}} + ] +{{/hasInputParams}} +{{#hasInputParams}} + '{{#lambda.uppercase}}{{httpMethod}}{{/lambda.uppercase}} {{{path}}}': [ + input: { + {{#pathParams}} + {{#lambda.snakecase_param}}{{paramName}}{{/lambda.snakecase_param}}{{^required}}?{{/required}}: {{{dataType}}}, + {{/pathParams}} + + {{#hasQueryParams}} + query?: { {{#queryParams}}{{#lambda.snakecase_param}}{{paramName}}{{/lambda.snakecase_param}}{{^required}}?{{/required}}: {{{dataType}}}, {{/queryParams}}}, + {{/hasQueryParams}} + + {{#hasBodyParam}} + {{#bodyParam}} + data{{^required}}?{{/required}}: {{{dataType}}}{{/bodyParam}}, + {{/hasBodyParam}} + + {{#hasFormParams}} + form: { {{#formParams}}{{#lambda.snakecase_param}}{{paramName}}{{/lambda.snakecase_param}}{{^required}}?{{/required}}: {{{dataType}}}, {{/formParams}}}, + {{/hasFormParams}} + }, + response: {{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}void{{/returnType}} + ] +{{/hasInputParams}} +{{/operation}} +{{/operations}} +{{/apis}} +{{/apiInfo}} +} + +export class MambaApiError extends AxiosError< + TransformedData, + RawData +> { + public apiRoute: string; + public name = 'MambaApiError'; + + constructor({ + apiRoute, + error, + }: { + apiRoute: string; + error: AxiosError; + }) { + super( + error.message, + error.code, + error.config, + error.request, + error.response + ); + + this.apiRoute = apiRoute; + } +} + +interface MambaApiClientOptions { + baseUrl?: string; + headers?: Headers; + onError?: (error: any) => void; +} + +export class MambaApiClient { + private _axios: AxiosInstance; + + constructor(options?: MambaApiClientOptions) { + this._axios = axios.create({ + baseURL: options?.baseUrl ?? undefined, + withCredentials: true, + paramsSerializer: (params: any) => + qs.stringify(params, { indices: false }), + }); + } + + configure(options: MambaApiClientOptions) { + if (options.baseUrl) { + this._axios.defaults.baseURL = options.baseUrl; + } + + if (options.headers) { + const headers = Object.fromEntries(options.headers.entries()); + this._axios.defaults.headers.common = headers; + } + + const { onError } = options; + if (onError) { + this._axios.interceptors.response.use( + (response) => response, + (error) => { + onError(error); + + return Promise.reject(error); + } + ); + } + } + + async request

( + path: P, + ...args: RequestInputOutput[P][0] extends never + ? [config?: AxiosRequestConfig] + : HasRequiredKeys extends true + ? [input: RequestInputOutput[P][0], config?: AxiosRequestConfig] + : [input?: RequestInputOutput[P][0], config?: AxiosRequestConfig] + ): Promise { + if (!this._axios.defaults.baseURL) { + throw new Error( + 'No baseUrl set for MambaApiClient. Call configure() before making requests.' + ); + } + + let [method, url] = path.split(' '); + + const [input = {}, extraConfig = {}] = args; + // @ts-expect-error + const { data, query, form, ...pathParams } = input; + + if (pathParams) { + url = url.replace(/{(\w+)}/g, (match, key: string) => { + // @ts-expect-error + return pathParams[key] || match; + }); + } + + const config: AxiosRequestConfig = { + ...extraConfig, + method: method as Method, + url, + params: query, + data, + } + + if (form) { + config.headers = { 'Content-Type': 'application/x-www-form-urlencoded'}; + } + + return this._axios + .request(config) + .then((res) => { + return res.data; + }) + .catch((error) => { + if (axios.isAxiosError(error)) { + throw new MambaApiError({ apiRoute: url, error }); + } else { + throw error; + } + }); + } + +} + From f29fc4a7fb0278ee1cb2ae5ea5e380be63fea914 Mon Sep 17 00:00:00 2001 From: Nathan Force Date: Wed, 11 Jan 2023 10:39:31 -0500 Subject: [PATCH 2/2] merge headers --- .../src/main/resources/typescript-axios/client.mustache | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/modules/openapi-generator/src/main/resources/typescript-axios/client.mustache b/modules/openapi-generator/src/main/resources/typescript-axios/client.mustache index 5a93f7804995..fc5e4fe303a7 100644 --- a/modules/openapi-generator/src/main/resources/typescript-axios/client.mustache +++ b/modules/openapi-generator/src/main/resources/typescript-axios/client.mustache @@ -152,7 +152,10 @@ export class MambaApiClient { } if (form) { - config.headers = { 'Content-Type': 'application/x-www-form-urlencoded'}; + config.headers = { + ...config.headers, + 'Content-Type': 'application/x-www-form-urlencoded', + }; } return this._axios