From e92d340b84690cce6851fa4a32530ac29911d75a Mon Sep 17 00:00:00 2001 From: dvacca-onfido <134616519+dvacca-onfido@users.noreply.github.com> Date: Thu, 20 Jun 2024 13:52:29 +0200 Subject: [PATCH] [CAT-1241][Node] Add FileTransfer object to allow in-memory only file upload/download (#91) --- .../update-specs-and-client-libraries.yaml | 3 +- README.md | 43 +- generators/typescript-axios/config.yaml | 5 + .../typescript-axios/templates/SHA256SUM | 2 + .../typescript-axios/templates/api.mustache | 60 +++ .../templates/apiInner.mustache | 457 ++++++++++++++++++ .../templates/file-transfer.mustache | 20 + .../typescript-axios/templates/index.mustache | 3 +- shell/run-prettier.sh | 5 +- 9 files changed, 573 insertions(+), 25 deletions(-) create mode 100644 generators/typescript-axios/templates/api.mustache create mode 100644 generators/typescript-axios/templates/apiInner.mustache create mode 100644 generators/typescript-axios/templates/file-transfer.mustache diff --git a/.github/workflows/update-specs-and-client-libraries.yaml b/.github/workflows/update-specs-and-client-libraries.yaml index 6f2fd050..4686fe1c 100644 --- a/.github/workflows/update-specs-and-client-libraries.yaml +++ b/.github/workflows/update-specs-and-client-libraries.yaml @@ -174,6 +174,7 @@ jobs: - generator: typescript-axios git_repo_id: onfido-node preCommit: | + sed -iE 's/\([ <{]\)File\([>},]\)/\1FileTransfer\2/g' api.ts npx prettier --write package.json npm install version: ${{ needs.generate_specs_and_libraries.outputs.typescript_axios_version }} @@ -214,7 +215,7 @@ jobs: - name: Integrate generated code (${{ matrix.version }}) if: ${{ matrix.update }} run: | - rsync -r --delete-after --exclude='/.git*' --exclude='/CHANGELOG*' \ + rsync -r --delete-after --exclude='/.git*' --exclude='/CHANGELOG*' --exclude='/.release.json' \ --exclude='/.openapi-generator-ignore' --exclude='/.openapi-generator/FILES' \ --exclude-from=generators/${{ matrix.generator }}/exclusions.txt \ generated/artifacts/${{ matrix.generator }}/ . diff --git a/README.md b/README.md index 5b3c9364..0dabd5cc 100644 --- a/README.md +++ b/README.md @@ -95,13 +95,14 @@ To update the OpenAPI generator version, bump the version in both [update-specs- SHA256SUM for template ... changed, diff reported below. To overwrite template, run: ... ``` + This happens when templates we're overriding have been updated. The script automatically fixes checksums for one generator at each run, but the templates need to be carefully reviewed and updated by following the procedure below: 1. Check which files have changed, by running `git diff generators/**/templates/SHA256SUM` 2. Compare each file with the one that has been freshly generated, e.g. if _libraries/okhttp-gson/ApiClient.mustache_ checksum has been denoted as modified for _java/okhttp-gson_ generator: - ```sh - diff generators/java/okhttp-gson/templates/libraries/okhttp-gson/ApiClient.mustache generated/templates/java/okhttp-gson/libraries/okhttp-gson/ApiClient.mustache - ``` + ```sh + diff generators/java/okhttp-gson/templates/libraries/okhttp-gson/ApiClient.mustache generated/templates/java/okhttp-gson/libraries/okhttp-gson/ApiClient.mustache + ``` 3. Add all changes from the new version except the ones noted by mustache comments (i.e. `{{! }}`) 4. Commit changes to both templates and SHA256SUM files @@ -111,23 +112,23 @@ The changes to README.md should be carefully reviewed by comparing `generated/te Repository is open to external contributions. At this end please: -1. [Fork]() repository +1. [Fork](https://github.com/onfido/onfido-openapi-spec/fork) repository 2. Create your feature branch (`git checkout -b my-new-feature`) -4. Make your changes, see below sections for project setup and testing. -4. To update one (or more) client libraries, clone them in the parent folder so that all the _onfido-*_ repositories lie at the same level. Then run the script `./shell/generate.sh` in the _onfido-openapi-spec_ folder and `./shell/sync-lib.sh` in each of the client libraries' folder, as in the examples below: +3. Make your changes, see below sections for project setup and testing. +4. To update one (or more) client libraries, clone them in the parent folder so that all the _onfido-\*_ repositories lie at the same level. Then run the script `./shell/generate.sh` in the _onfido-openapi-spec_ folder and `./shell/sync-lib.sh` in each of the client libraries' folder, as in the examples below: - ```sh - ../onfido-openapi-spec/shell/sync-lib.sh java java/okhttp-gson - ../onfido-openapi-spec/shell/sync-lib.sh php - ``` + ```sh + ../onfido-openapi-spec/shell/sync-lib.sh java java/okhttp-gson + ../onfido-openapi-spec/shell/sync-lib.sh php + ``` 5. To verify changes to the OpenAPI definition with external tools, run: - ```sh - ./shell/refresh-openapi-spec-for-documentation.py \ - generated/artifacts/openapi/openapi.json \ - generated/artifacts/openapi-documentation/openapi.json - ``` + ```sh + ./shell/refresh-openapi-spec-for-documentation.py \ + generated/artifacts/openapi/openapi.json \ + generated/artifacts/openapi-documentation/openapi.json + ``` 6. Before committing your changes, run the script `./shell/run-prettier.sh` 7. Commit your changes (`git commit -am 'Add some feature'`) @@ -142,12 +143,12 @@ Described below is the procedure on how to deliver new client libraries: 2. Manually trigger an [update-specs-and-client-libraries](https://github.com/onfido/onfido-openapi-spec/actions/workflows/update-specs-and-client-libraries.yaml) by clicking over the _Run workflow_ button 3. Select the type of change: - | Type | Description | - |-------------|-----------------------------------------------------------------------------------------------------------------------| - | _No change_ | no change expected to client library code | - | _Patch_ | bug fix not causing any change to client library interface | - | _Minor_ | backward compatible change to client library interface (e.g. new endpoint, new optional parameters) | - | _Major_ | non-backward compatible change to client library interface (e.g. remove or change endpoint, new mandatory parameters) | + | Type | Description | + | ----------- | --------------------------------------------------------------------------------------------------------------------- | + | _No change_ | no change expected to client library code | + | _Patch_ | bug fix not causing any change to client library interface | + | _Minor_ | backward compatible change to client library interface (e.g. new endpoint, new optional parameters) | + | _Major_ | non-backward compatible change to client library interface (e.g. remove or change endpoint, new mandatory parameters) | 4. Select the libraries to update 5. A new PR will be created for each library (or overridden if already present) diff --git a/generators/typescript-axios/config.yaml b/generators/typescript-axios/config.yaml index d868d13e..4d062529 100644 --- a/generators/typescript-axios/config.yaml +++ b/generators/typescript-axios/config.yaml @@ -5,8 +5,13 @@ enumUnknownDefaultCase: true supportsES6: true withNodeImports: true httpUserAgent: onfido-node/${CLIENT_LIBRARY_VERSION} +# Type mapping doesn't work with this generator +# typeMappings: +# File: FileTransfer files: LICENSE.mustache: destinationFilename: LICENSE webhook-event-verifier.mustache: destinationFilename: webhook-event-verifier.ts + file-transfer.mustache: + destinationFilename: file-transfer.ts diff --git a/generators/typescript-axios/templates/SHA256SUM b/generators/typescript-axios/templates/SHA256SUM index 5ddd8a09..aa7bea21 100644 --- a/generators/typescript-axios/templates/SHA256SUM +++ b/generators/typescript-axios/templates/SHA256SUM @@ -1,5 +1,7 @@ e13ee7c0cad9a79bab00e8b2a7942bc5a78f63115ece3dfd71a6472bdb02dd22 ./README.mustache +3a077701e62c4d0942657c78cf0116cbe58436e34d723d81aaed7ed0d56d2ed1 ./api.mustache +0f4ea3b4db29a3bbada4c5ad5e4b08fa598305ba09aa29d237edee1634f8805b ./apiInner.mustache d8d68213e1a9a9983f89f688aaf31360f69d34a871cab4fc94f59a40279b13d9 ./configuration.mustache 5030fcd1954b4d981f2d06fce4900fe29c97c7b74ee66f2eb5f45e81b9252a94 ./index.mustache 28f5e210e99474200c8e5f13e4f2374c1556406eb71c15e808d81913000cd549 ./package.mustache diff --git a/generators/typescript-axios/templates/api.mustache b/generators/typescript-axios/templates/api.mustache new file mode 100644 index 00000000..9b77ad78 --- /dev/null +++ b/generators/typescript-axios/templates/api.mustache @@ -0,0 +1,60 @@ +/* tslint:disable */ +/* eslint-disable */ +{{>licenseInfo}} + +{{^withSeparateModelsAndApi}} +import type { Configuration } from './configuration'; +import type { AxiosPromise, AxiosInstance, RawAxiosRequestConfig } from 'axios'; +import globalAxios from 'axios'; +{{#withNodeImports}} +// URLSearchParams not necessarily used +// @ts-ignore +import { URL, URLSearchParams } from 'url'; +{{#multipartFormData}} +import FormData from 'form-data' +{{/multipartFormData}} +{{/withNodeImports}} +// Some imports not used depending on template conditions +// @ts-ignore +import { DUMMY_BASE_URL, assertParamExists, setApiKeyToObject, setBasicAuthToObject, setBearerAuthToObject, setOAuthToObject, setSearchParams, serializeDataIfNeeded, toPathString, createRequestFunction } from './common'; +import type { RequestArgs } from './base'; +// @ts-ignore +import { BASE_PATH, COLLECTION_FORMATS, BaseAPI, RequiredError, operationServerMap } from './base'; + +{{! ONFIDO: Added lines of code - BEGIN }} +import { FileTransfer } from './file-transfer'; +import { AxiosHeaders } from 'axios'; + +globalAxios.interceptors.response.use(async (response) => { + if (response.headers instanceof AxiosHeaders && response.headers['content-type']) { + if ( ! response.headers['content-type'].toString().includes('application/json') ) { + const contentDisposition = response.headers['content-disposition']; + var filename = ""; + + if (contentDisposition && contentDisposition != "") { + const matcher = contentDisposition.match(/filename=['\"]?([^'\"\s]+)['\"]?/); + + if (matcher != null) { + filename = matcher[1].replace(/.*[/\\\\]/g, ""); + } + } + + response.data = new FileTransfer(response.data, filename); + } + } + + return response; + }); + +{{! ONFIDO: Added lines of code - END }} + +{{#models}} +{{#model}}{{#isEnum}}{{>modelEnum}}{{/isEnum}}{{#oneOf}}{{#-first}}{{>modelOneOf}}{{/-first}}{{/oneOf}}{{^isEnum}}{{^oneOf}}{{>modelGeneric}}{{/oneOf}}{{/isEnum}}{{/model}} +{{/models}} +{{#apiInfo}}{{#apis}} +{{>apiInner}} +{{/apis}}{{/apiInfo}} +{{/withSeparateModelsAndApi}}{{#withSeparateModelsAndApi}} +{{#apiInfo}}{{#apis}}{{#operations}}export * from './{{tsApiPackage}}/{{classFilename}}'; +{{/operations}}{{/apis}}{{/apiInfo}} +{{/withSeparateModelsAndApi}} diff --git a/generators/typescript-axios/templates/apiInner.mustache b/generators/typescript-axios/templates/apiInner.mustache new file mode 100644 index 00000000..132a95db --- /dev/null +++ b/generators/typescript-axios/templates/apiInner.mustache @@ -0,0 +1,457 @@ +{{#withSeparateModelsAndApi}} +/* tslint:disable */ +/* eslint-disable */ +{{>licenseInfo}} + +import type { Configuration } from '{{apiRelativeToRoot}}configuration'; +import type { AxiosPromise, AxiosInstance, RawAxiosRequestConfig } from 'axios'; +import globalAxios from 'axios'; +{{#withNodeImports}} +// URLSearchParams not necessarily used +// @ts-ignore +import { URL, URLSearchParams } from 'url'; +{{#multipartFormData}} +import FormData from 'form-data' +{{/multipartFormData}} +{{/withNodeImports}} +// Some imports not used depending on template conditions +// @ts-ignore +import { DUMMY_BASE_URL, assertParamExists, setApiKeyToObject, setBasicAuthToObject, setBearerAuthToObject, setOAuthToObject, setSearchParams, serializeDataIfNeeded, toPathString, createRequestFunction } from '{{apiRelativeToRoot}}common'; +// @ts-ignore +import { BASE_PATH, COLLECTION_FORMATS, type RequestArgs, BaseAPI, RequiredError, operationServerMap } from '{{apiRelativeToRoot}}base'; +{{#imports}} +// @ts-ignore +import type { {{classname}} } from '{{apiRelativeToRoot}}{{tsModelPackage}}'; +{{/imports}} +{{/withSeparateModelsAndApi}} +{{^withSeparateModelsAndApi}} +{{/withSeparateModelsAndApi}} +{{#operations}} +/** + * {{classname}} - axios parameter creator{{#description}} + * {{&description}}{{/description}} + * @export + */ +export const {{classname}}AxiosParamCreator = function (configuration?: Configuration) { + return { + {{#operation}} + /** + * {{¬es}} + {{#summary}} + * @summary {{&summary}} + {{/summary}} + {{#allParams}} + * @param {{=<% %>=}}{<%#isEnum%><%&datatypeWithEnum%><%/isEnum%><%^isEnum%><%&dataType%><%#isNullable%> | null<%/isNullable%><%/isEnum%>}<%={{ }}=%> {{^required}}[{{/required}}{{paramName}}{{^required}}]{{/required}} {{description}} + {{/allParams}} + * @param {*} [options] Override http request option.{{#isDeprecated}} + * @deprecated{{/isDeprecated}} + * @throws {RequiredError} + */ + {{nickname}}: async ({{#allParams}}{{paramName}}{{^required}}?{{/required}}: {{#isEnum}}{{{datatypeWithEnum}}}{{/isEnum}}{{^isEnum}}{{{dataType}}}{{#isNullable}} | null{{/isNullable}}{{/isEnum}}, {{/allParams}}options: RawAxiosRequestConfig = {}): Promise => { + {{#allParams}} + {{#required}} + // verify required parameter '{{paramName}}' is not null or undefined + assertParamExists('{{nickname}}', '{{paramName}}', {{paramName}}) + {{/required}} + {{/allParams}} + const localVarPath = `{{{path}}}`{{#pathParams}} + .replace(`{${"{{baseName}}"}}`, encodeURIComponent(String({{paramName}}))){{/pathParams}}; + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { method: '{{httpMethod}}', ...baseOptions, ...options}; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any;{{#vendorExtensions}}{{#hasFormParams}} + const localVarFormParams = new {{^multipartFormData}}URLSearchParams(){{/multipartFormData}}{{#multipartFormData}}((configuration && configuration.formDataCtor) || FormData)(){{/multipartFormData}};{{/hasFormParams}}{{/vendorExtensions}} + + {{#authMethods}} + // authentication {{name}} required + {{#isApiKey}} + {{#isKeyInHeader}} + await setApiKeyToObject(localVarHeaderParameter, "{{keyParamName}}", configuration) + {{/isKeyInHeader}} + {{#isKeyInQuery}} + await setApiKeyToObject(localVarQueryParameter, "{{keyParamName}}", configuration) + {{/isKeyInQuery}} + {{/isApiKey}} + {{#isBasicBasic}} + // http basic authentication required + setBasicAuthToObject(localVarRequestOptions, configuration) + {{/isBasicBasic}} + {{#isBasicBearer}} + // http bearer authentication required + await setBearerAuthToObject(localVarHeaderParameter, configuration) + {{/isBasicBearer}} + {{#isOAuth}} + // oauth required + await setOAuthToObject(localVarHeaderParameter, "{{name}}", [{{#scopes}}"{{{scope}}}"{{^-last}}, {{/-last}}{{/scopes}}], configuration) + {{/isOAuth}} + + {{/authMethods}} + {{#queryParams}} + {{#isArray}} + if ({{paramName}}) { + {{#isCollectionFormatMulti}} + {{#uniqueItems}} + localVarQueryParameter['{{baseName}}'] = Array.from({{paramName}}); + {{/uniqueItems}} + {{^uniqueItems}} + localVarQueryParameter['{{baseName}}'] = {{paramName}}; + {{/uniqueItems}} + {{/isCollectionFormatMulti}} + {{^isCollectionFormatMulti}} + {{#uniqueItems}} + localVarQueryParameter['{{baseName}}'] = Array.from({{paramName}}).join(COLLECTION_FORMATS.{{collectionFormat}}); + {{/uniqueItems}} + {{^uniqueItems}} + localVarQueryParameter['{{baseName}}'] = {{paramName}}.join(COLLECTION_FORMATS.{{collectionFormat}}); + {{/uniqueItems}} + {{/isCollectionFormatMulti}} + } + {{/isArray}} + {{^isArray}} + if ({{paramName}} !== undefined) { + {{#isDateTime}} + localVarQueryParameter['{{baseName}}'] = ({{paramName}} as any instanceof Date) ? + ({{paramName}} as any).toISOString() : + {{paramName}}; + {{/isDateTime}} + {{^isDateTime}} + {{#isDate}} + localVarQueryParameter['{{baseName}}'] = ({{paramName}} as any instanceof Date) ? + ({{paramName}} as any).toISOString().substring(0,10) : + {{paramName}}; + {{/isDate}} + {{^isDate}} + {{#isExplode}} + {{#isPrimitiveType}} + localVarQueryParameter['{{baseName}}'] = {{paramName}}; + {{/isPrimitiveType}} + {{^isPrimitiveType}} + {{^isEnumRef}} + {{^isEnum}} + for (const [key, value] of Object.entries({{paramName}})) { + localVarQueryParameter[key] = value; + } + {{/isEnum}} + {{/isEnumRef}} + {{#isEnum}} + localVarQueryParameter['{{baseName}}'] = {{paramName}}; + {{/isEnum}} + {{#isEnumRef}} + localVarQueryParameter['{{baseName}}'] = {{paramName}}; + {{/isEnumRef}} + {{/isPrimitiveType}} + {{/isExplode}} + {{^isExplode}} + localVarQueryParameter['{{baseName}}'] = {{paramName}}; + {{/isExplode}} + {{/isDate}} + {{/isDateTime}} + } + {{/isArray}} + + {{/queryParams}} + {{#headerParams}} + {{#isArray}} + if ({{paramName}}) { + {{#uniqueItems}} + let mapped = Array.from({{paramName}}).map(value => ("{{{dataType}}}" !== "Set") ? JSON.stringify(value) : (value || "")); + {{/uniqueItems}} + {{^uniqueItems}} + let mapped = {{paramName}}.map(value => ("{{{dataType}}}" !== "Array") ? JSON.stringify(value) : (value || "")); + {{/uniqueItems}} + localVarHeaderParameter['{{baseName}}'] = mapped.join(COLLECTION_FORMATS["{{collectionFormat}}"]); + } + {{/isArray}} + {{^isArray}} + {{! `val == null` covers for both `null` and `undefined`}} + if ({{paramName}} != null) { + {{#isString}} + localVarHeaderParameter['{{baseName}}'] = String({{paramName}}); + {{/isString}} + {{^isString}} + {{! isString is falsy also for $ref that defines a string or enum type}} + localVarHeaderParameter['{{baseName}}'] = typeof {{paramName}} === 'string' + ? {{paramName}} + : JSON.stringify({{paramName}}); + {{/isString}} + } + {{/isArray}} + + {{/headerParams}} + {{#vendorExtensions}} + {{#formParams}} + {{#isArray}} + if ({{paramName}}) { + {{#isCollectionFormatMulti}} + {{#contentType}} + localVarFormParams.append('{{baseName}}', new Blob([JSON.stringify({{paramName}})], { type: "{{contentType}}", })); + {{/contentType}} + {{^contentType}} + {{paramName}}.forEach((element) => { + localVarFormParams.{{#multipartFormData}}append{{/multipartFormData}}{{^multipartFormData}}set{{/multipartFormData}}('{{baseName}}', element as any); + }) + {{/contentType}} + {{/isCollectionFormatMulti}} + {{^isCollectionFormatMulti}} + localVarFormParams.{{#multipartFormData}}append{{/multipartFormData}}{{^multipartFormData}}set{{/multipartFormData}}('{{baseName}}', {{paramName}}.join(COLLECTION_FORMATS.{{collectionFormat}})); + {{/isCollectionFormatMulti}} + }{{/isArray}} + {{^isArray}} + if ({{paramName}} !== undefined) { {{^multipartFormData}} {{! ONFIDO: Add a case specific for File - BEGIN }} + localVarFormParams.set('{{baseName}}', {{paramName}} as any);{{/multipartFormData}}{{#multipartFormData}}{{#isPrimitiveType}}{{^isBoolean}}{{^isFile}} + localVarFormParams.append('{{baseName}}', {{paramName}} as any);{{/isFile}}{{/isBoolean}}{{/isPrimitiveType}}{{#isPrimitiveType}}{{#isBoolean}} + localVarFormParams.append('{{baseName}}', String({{paramName}}) as any);{{/isBoolean}}{{/isPrimitiveType}}{{#isPrimitiveType}}{{#isFile}} + localVarFormParams.append('{{baseName}}', {{paramName}}.buffer as any, {{paramName}}.filename);{{/isFile}}{{/isPrimitiveType}}{{^isPrimitiveType}}{{#isEnumRef}} + localVarFormParams.append('{{baseName}}', {{paramName}} as any);{{/isEnumRef}}{{^isEnumRef}} + localVarFormParams.append('{{baseName}}', new Blob([JSON.stringify({{paramName}})], { type: "application/json", }));{{/isEnumRef}}{{/isPrimitiveType}}{{/multipartFormData}} + }{{/isArray}}{{! ONFIDO: Add a case specific for File - END }} + {{/formParams}}{{/vendorExtensions}} + {{#vendorExtensions}}{{#hasFormParams}}{{^multipartFormData}} + localVarHeaderParameter['Content-Type'] = 'application/x-www-form-urlencoded';{{/multipartFormData}}{{#multipartFormData}} + localVarHeaderParameter['Content-Type'] = 'multipart/form-data';{{/multipartFormData}} + {{/hasFormParams}}{{/vendorExtensions}} + {{#bodyParam}} + {{^consumes}} + localVarHeaderParameter['Content-Type'] = 'application/json'; + {{/consumes}} + {{#consumes.0}} + localVarHeaderParameter['Content-Type'] = '{{{mediaType}}}'; + {{/consumes.0}} + + {{/bodyParam}} + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions,{{#hasFormParams}}{{#multipartFormData}} ...(localVarFormParams as any).getHeaders?.(),{{/multipartFormData}}{{/hasFormParams}} ...options.headers}; + {{#hasFormParams}} + localVarRequestOptions.data = localVarFormParams{{#vendorExtensions}}{{^multipartFormData}}.toString(){{/multipartFormData}}{{/vendorExtensions}}; + {{/hasFormParams}} + {{#bodyParam}} + localVarRequestOptions.data = serializeDataIfNeeded({{paramName}}, localVarRequestOptions, configuration) + {{/bodyParam}} + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + {{/operation}} + } +}; + +/** + * {{classname}} - functional programming interface{{#description}} + * {{{.}}}{{/description}} + * @export + */ +export const {{classname}}Fp = function(configuration?: Configuration) { + const localVarAxiosParamCreator = {{classname}}AxiosParamCreator(configuration) + return { + {{#operation}} + /** + * {{¬es}} + {{#summary}} + * @summary {{&summary}} + {{/summary}} + {{#allParams}} + * @param {{=<% %>=}}{<%#isEnum%><%&datatypeWithEnum%><%/isEnum%><%^isEnum%><%&dataType%><%#isNullable%> | null<%/isNullable%><%/isEnum%>}<%={{ }}=%> {{^required}}[{{/required}}{{paramName}}{{^required}}]{{/required}} {{description}} + {{/allParams}} + * @param {*} [options] Override http request option.{{#isDeprecated}} + * @deprecated{{/isDeprecated}} + * @throws {RequiredError} + */ + async {{nickname}}({{#allParams}}{{paramName}}{{^required}}?{{/required}}: {{#isEnum}}{{{datatypeWithEnum}}}{{/isEnum}}{{^isEnum}}{{{dataType}}}{{#isNullable}} | null{{/isNullable}}{{/isEnum}}, {{/allParams}}options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<{{{returnType}}}{{^returnType}}void{{/returnType}}>> { + const localVarAxiosArgs = await localVarAxiosParamCreator.{{nickname}}({{#allParams}}{{paramName}}, {{/allParams}}options); + const localVarOperationServerIndex = configuration?.serverIndex ?? 0; + const localVarOperationServerBasePath = operationServerMap['{{classname}}.{{nickname}}']?.[localVarOperationServerIndex]?.url; + return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath); + }, + {{/operation}} + } +}; + +/** + * {{classname}} - factory interface{{#description}} + * {{&description}}{{/description}} + * @export + */ +export const {{classname}}Factory = function (configuration?: Configuration, basePath?: string, axios?: AxiosInstance) { + const localVarFp = {{classname}}Fp(configuration) + return { + {{#operation}} + /** + * {{¬es}} + {{#summary}} + * @summary {{&summary}} + {{/summary}} + {{#useSingleRequestParameter}} + {{#allParams.0}} + * @param {{=<% %>=}}{<%& classname %><%& operationIdCamelCase %>Request}<%={{ }}=%> requestParameters Request parameters. + {{/allParams.0}} + {{/useSingleRequestParameter}} + {{^useSingleRequestParameter}} + {{#allParams}} + * @param {{=<% %>=}}{<%#isEnum%><%&datatypeWithEnum%><%/isEnum%><%^isEnum%><%&dataType%><%#isNullable%> | null<%/isNullable%><%/isEnum%>}<%={{ }}=%> {{^required}}[{{/required}}{{paramName}}{{^required}}]{{/required}} {{description}} + {{/allParams}} + {{/useSingleRequestParameter}} + * @param {*} [options] Override http request option.{{#isDeprecated}} + * @deprecated{{/isDeprecated}} + * @throws {RequiredError} + */ + {{#useSingleRequestParameter}} + {{nickname}}({{#allParams.0}}requestParameters: {{classname}}{{operationIdCamelCase}}Request{{^hasRequiredParams}} = {}{{/hasRequiredParams}}, {{/allParams.0}}options?: RawAxiosRequestConfig): AxiosPromise<{{{returnType}}}{{^returnType}}void{{/returnType}}> { + return localVarFp.{{nickname}}({{#allParams.0}}{{#allParams}}requestParameters.{{paramName}}, {{/allParams}}{{/allParams.0}}options).then((request) => request(axios, basePath)); + }, + {{/useSingleRequestParameter}} + {{^useSingleRequestParameter}} + {{nickname}}({{#allParams}}{{paramName}}{{^required}}?{{/required}}: {{#isEnum}}{{{datatypeWithEnum}}}{{/isEnum}}{{^isEnum}}{{{dataType}}}{{#isNullable}} | null{{/isNullable}}{{/isEnum}}, {{/allParams}}options?: any): AxiosPromise<{{{returnType}}}{{^returnType}}void{{/returnType}}> { + return localVarFp.{{nickname}}({{#allParams}}{{paramName}}, {{/allParams}}options).then((request) => request(axios, basePath)); + }, + {{/useSingleRequestParameter}} + {{/operation}} + }; +}; + +{{#withInterfaces}} +/** + * {{classname}} - interface{{#description}} + * {{&description}}{{/description}} + * @export + * @interface {{classname}} + */ +export interface {{classname}}Interface { +{{#operation}} + /** + * {{¬es}} + {{#summary}} + * @summary {{&summary}} + {{/summary}} + {{#allParams}} + * @param {{=<% %>=}}{<%#isEnum%><%&datatypeWithEnum%><%/isEnum%><%^isEnum%><%&dataType%><%#isNullable%> | null<%/isNullable%><%/isEnum%>}<%={{ }}=%> {{^required}}[{{/required}}{{paramName}}{{^required}}]{{/required}} {{description}} + {{/allParams}} + * @param {*} [options] Override http request option.{{#isDeprecated}} + * @deprecated{{/isDeprecated}} + * @throws {RequiredError} + * @memberof {{classname}}Interface + */ + {{nickname}}({{#allParams}}{{paramName}}{{^required}}?{{/required}}: {{#isEnum}}{{{datatypeWithEnum}}}{{/isEnum}}{{^isEnum}}{{{dataType}}}{{#isNullable}} | null{{/isNullable}}{{/isEnum}}, {{/allParams}}options?: RawAxiosRequestConfig): AxiosPromise<{{{returnType}}}{{^returnType}}void{{/returnType}}>; + +{{/operation}} +} + +{{/withInterfaces}} +{{#useSingleRequestParameter}} +{{#operation}} +{{#allParams.0}} +/** + * Request parameters for {{nickname}} operation in {{classname}}. + * @export + * @interface {{classname}}{{operationIdCamelCase}}Request + */ +export interface {{classname}}{{operationIdCamelCase}}Request { + {{#allParams}} + /** + * {{description}} + * @type {{=<% %>=}}{<%&dataType%>}<%={{ }}=%> + * @memberof {{classname}}{{operationIdCamelCase}} + */ + readonly {{paramName}}{{^required}}?{{/required}}: {{#isEnum}}{{{datatypeWithEnum}}}{{/isEnum}}{{^isEnum}}{{{dataType}}}{{#isNullable}} | null{{/isNullable}}{{/isEnum}} + {{^-last}} + + {{/-last}} + {{/allParams}} +} + +{{/allParams.0}} +{{/operation}} +{{/useSingleRequestParameter}} +/** + * {{classname}} - object-oriented interface{{#description}} + * {{{.}}}{{/description}} + * @export + * @class {{classname}} + * @extends {BaseAPI} + */ +{{#withInterfaces}} +export class {{classname}} extends BaseAPI implements {{classname}}Interface { +{{/withInterfaces}} +{{^withInterfaces}} +export class {{classname}} extends BaseAPI { +{{/withInterfaces}} + {{#operation}} + /** + * {{¬es}} + {{#summary}} + * @summary {{&summary}} + {{/summary}} + {{#useSingleRequestParameter}} + {{#allParams.0}} + * @param {{=<% %>=}}{<%& classname %><%& operationIdCamelCase %>Request}<%={{ }}=%> requestParameters Request parameters. + {{/allParams.0}} + {{/useSingleRequestParameter}} + {{^useSingleRequestParameter}} + {{#allParams}} + * @param {{=<% %>=}}{<%#isEnum%><%&datatypeWithEnum%><%/isEnum%><%^isEnum%><%&dataType%><%#isNullable%> | null<%/isNullable%><%/isEnum%>}<%={{ }}=%> {{^required}}[{{/required}}{{paramName}}{{^required}}]{{/required}} {{description}} + {{/allParams}} + {{/useSingleRequestParameter}} + * @param {*} [options] Override http request option.{{#isDeprecated}} + * @deprecated{{/isDeprecated}} + * @throws {RequiredError} + * @memberof {{classname}} + */ + {{#useSingleRequestParameter}} + public {{nickname}}({{#allParams.0}}requestParameters: {{classname}}{{operationIdCamelCase}}Request{{^hasRequiredParams}} = {}{{/hasRequiredParams}}, {{/allParams.0}}options?: RawAxiosRequestConfig) { + return {{classname}}Fp(this.configuration).{{nickname}}({{#allParams.0}}{{#allParams}}requestParameters.{{paramName}}, {{/allParams}}{{/allParams.0}}options).then((request) => request(this.axios, this.basePath)); + } + {{/useSingleRequestParameter}} + {{^useSingleRequestParameter}} + public {{nickname}}({{#allParams}}{{paramName}}{{^required}}?{{/required}}: {{#isEnum}}{{{datatypeWithEnum}}}{{/isEnum}}{{^isEnum}}{{{dataType}}}{{#isNullable}} | null{{/isNullable}}{{/isEnum}}, {{/allParams}}options?: RawAxiosRequestConfig) { + return {{classname}}Fp(this.configuration).{{nickname}}({{#allParams}}{{paramName}}, {{/allParams}}options).then((request) => request(this.axios, this.basePath)); + } + {{/useSingleRequestParameter}} + {{^-last}} + + {{/-last}} + {{/operation}} +} +{{/operations}} + +{{#operations}} +{{#operation}} +{{#allParams}} +{{#isEnum}} +{{#stringEnums}} +/** + * @export + * @enum {string} + */ +export enum {{operationIdCamelCase}}{{enumName}} { +{{#allowableValues}} + {{#enumVars}} + {{{name}}} = {{{value}}}{{^-last}},{{/-last}} + {{/enumVars}} +{{/allowableValues}} +} +{{/stringEnums}} +{{^stringEnums}} +/** + * @export + */ +export const {{operationIdCamelCase}}{{enumName}} = { +{{#allowableValues}} + {{#enumVars}} + {{{name}}}: {{{value}}}{{^-last}},{{/-last}} + {{/enumVars}} +{{/allowableValues}} +} as const; +export type {{operationIdCamelCase}}{{enumName}} = typeof {{operationIdCamelCase}}{{enumName}}[keyof typeof {{operationIdCamelCase}}{{enumName}}]; +{{/stringEnums}} +{{/isEnum}} +{{/allParams}} +{{/operation}} +{{/operations}} diff --git a/generators/typescript-axios/templates/file-transfer.mustache b/generators/typescript-axios/templates/file-transfer.mustache new file mode 100644 index 00000000..e2076d8e --- /dev/null +++ b/generators/typescript-axios/templates/file-transfer.mustache @@ -0,0 +1,20 @@ +import { readFileSync, PathLike } from "fs"; + +export class FileTransfer { + public readonly buffer: Buffer; + public readonly filename: String; + + constructor(buffer: String, filename: String); + constructor(buffer: Buffer, filename: String); + constructor(inputFile: PathLike); + + constructor(data?: String | Buffer | PathLike, filename?: String) { + if (filename == null) { + this.buffer = readFileSync(data as PathLike); + this.filename = (data as PathLike).toString(); + } else { + this.buffer = data as Buffer; + this.filename = filename; + } + } +} diff --git a/generators/typescript-axios/templates/index.mustache b/generators/typescript-axios/templates/index.mustache index 33eac20d..fe92120a 100644 --- a/generators/typescript-axios/templates/index.mustache +++ b/generators/typescript-axios/templates/index.mustache @@ -5,5 +5,6 @@ export * from "./api"; export * from "./configuration"; {{#withSeparateModelsAndApi}}export * from "./{{tsModelPackage}}";{{/withSeparateModelsAndApi}} -{{! Additional import for webhook event verifier }} +{{! Additional exports below }} export * from "./webhook-event-verifier"; +export * from "./file-transfer"; diff --git a/shell/run-prettier.sh b/shell/run-prettier.sh index 617bf701..8d36e3c7 100755 --- a/shell/run-prettier.sh +++ b/shell/run-prettier.sh @@ -11,8 +11,9 @@ else fi npm install prettier -npx prettier $option openapi.yaml paths/*.yaml schemas/*/*.yaml responses/*.yaml \ +npx prettier $option *.md openapi.yaml paths/*.yaml schemas/*/*.yaml responses/*.yaml \ $(git ls-files generators/\*\*/config.yaml) .github/workflows/*.yaml npx prettier $option --parser markdown $(git ls-files generators/\*\*/templates/README.mustache) \ generators/common/templates/README_footer.mustache -npx prettier $option --parser typescript generators/typescript-axios/templates/webhook-event-verifier.mustache +npx prettier $option --parser typescript \ + generators/typescript-axios/templates/{webhook-event-verifier,file-transfer}.mustache \ No newline at end of file