From 5f86ff177bca11bd252242a72b6ba200d91765c8 Mon Sep 17 00:00:00 2001 From: Sean Pallister Date: Sat, 3 Nov 2018 16:43:02 -0700 Subject: [PATCH 1/2] Add hasFileOperations property to mustache model, and responseToFile helper to TS-Fetch API template. --- .../codegen/DefaultGenerator.java | 23 ++++++++++ .../resources/typescript-fetch/api.mustache | 42 ++++++++++++++++++- 2 files changed, 64 insertions(+), 1 deletion(-) diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultGenerator.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultGenerator.java index a18fffa35823..8d6bf2e485ee 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultGenerator.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultGenerator.java @@ -801,6 +801,29 @@ private Map buildSupportFileBundle(List allOperations, L bundle.put("hasServers", true); } + if (allOperations != null) { + // Scan all API Group Operations to determine if there is at least one Operation within this API that + // returns a File Response, in which case the API hasFileOperations, and we may use this property in + // the mustache templates if useful... + for (HashMap apiGroup : (List>) (Object) allOperations) { + HashMap apiGroupOperationsContainer = + (HashMap) apiGroup.get("operations"); + List apiGroupOperations = + (List) (Object) apiGroupOperationsContainer.get("operation"); + for (CodegenOperation apiGroupOperation : apiGroupOperations) { + if (apiGroupOperation.isResponseFile) { + bundle.put("hasFileOperations", true); + break; + } + } + // If we've already found at least one API Group containing an operation that must return + // file responses, we're done... + if (bundle.containsKey("hasFileOperations")) { + break; + } + } + } + if (openAPI.getExternalDocs() != null) { bundle.put("externalDocs", openAPI.getExternalDocs()); } diff --git a/modules/openapi-generator/src/main/resources/typescript-fetch/api.mustache b/modules/openapi-generator/src/main/resources/typescript-fetch/api.mustache index f6da696178dd..e5b03de1e121 100644 --- a/modules/openapi-generator/src/main/resources/typescript-fetch/api.mustache +++ b/modules/openapi-generator/src/main/resources/typescript-fetch/api.mustache @@ -67,6 +67,41 @@ export class RequiredError extends Error { } } +{{#hasFileOperations}} +const responseToFile = (fileResponse: Response): Promise => { + let filename = "download"; // Default filename of 'download' + + const contentTypeHeader = fileResponse.headers.get("Content-Type"); + const lastModifiedHeader = fileResponse.headers.get("Last-Modified"); + const contentDispositionHeader = fileResponse.headers.get("Content-Disposition"); + + const type = contentTypeHeader || ""; + const lastModified = lastModifiedHeader ? Date.parse(lastModifiedHeader) : Date.now(); + if (contentDispositionHeader) { + const filenameRegex = /filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/; + const filenameMatches = filenameRegex.exec(contentDispositionHeader); + if (filenameMatches && filenameMatches[1]) { + filename = filenameMatches[1].replace(/['"]/g, ""); + } + } + + return fileResponse.blob().then(blob => { + // If the File constructor is available and this is not Edge (which has an unimplemented constructor), use it... + // (https://developer.mozilla.org/en-US/docs/Web/API/File/File) + if (typeof File === "function" && !/Edge/.test(navigator.userAgent)) { + return new File([blob], filename, { type, lastModified }); + } + // Otherwise, extend the Blob with the additional properties and return it as a File... + else { + const file = blob as any; + file.name = filename; + file.lastModified = lastModified; + return file as File; + } + }); +}; + +{{/hasFileOperations}} {{#models}} {{#model}}{{#isEnum}}{{>modelEnum}}{{/isEnum}}{{^isEnum}}{{>modelGeneric}}{{/isEnum}}{{/model}} {{/models}} @@ -264,12 +299,17 @@ export const {{classname}}Fp = function(configuration?: Configuration) { * @param {*} [options] Override http request option. * @throws {RequiredError} */ - {{nickname}}({{#allParams}}{{paramName}}{{^required}}?{{/required}}: {{{dataType}}}, {{/allParams}}options?: any): (fetch?: FetchAPI, basePath?: string) => Promise<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}Response{{/returnType}}> { + {{nickname}}({{#allParams}}{{paramName}}{{^required}}?{{/required}}: {{{dataType}}}, {{/allParams}}options?: any): (fetch?: FetchAPI, basePath?: string) => Promise<{{#returnType}}{{^isResponseFile}}{{{returnType}}}{{/isResponseFile}}{{#isResponseFile}}File{{/isResponseFile}}{{/returnType}}{{^returnType}}Response{{/returnType}}> { const localVarFetchArgs = {{classname}}FetchParamCreator(configuration).{{nickname}}({{#allParams}}{{paramName}}, {{/allParams}}options); return (fetch: FetchAPI = portableFetch, basePath: string = BASE_PATH) => { return fetch(basePath + localVarFetchArgs.url, localVarFetchArgs.options).then((response) => { if (response.status >= 200 && response.status < 300) { + {{^isResponseFile}} return response{{#returnType}}.json(){{/returnType}}; + {{/isResponseFile}} + {{#isResponseFile}} + return responseToFile(response); + {{/isResponseFile}} } else { throw response; } From 9c92fe85fef5c860796ba4bb24a32df2b3b975b2 Mon Sep 17 00:00:00 2001 From: Sean Pallister Date: Sat, 3 Nov 2018 16:53:22 -0700 Subject: [PATCH 2/2] Update TypeScript-fetch Petstore sample. --- .../typescript-fetch/.openapi-generator/VERSION | 2 +- .../client/petstore-security-test/typescript-fetch/api.ts | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/samples/client/petstore-security-test/typescript-fetch/.openapi-generator/VERSION b/samples/client/petstore-security-test/typescript-fetch/.openapi-generator/VERSION index 0f58aa041419..e24c1f857e01 100644 --- a/samples/client/petstore-security-test/typescript-fetch/.openapi-generator/VERSION +++ b/samples/client/petstore-security-test/typescript-fetch/.openapi-generator/VERSION @@ -1 +1 @@ -3.1.2-SNAPSHOT \ No newline at end of file +3.3.3-SNAPSHOT \ No newline at end of file diff --git a/samples/client/petstore-security-test/typescript-fetch/api.ts b/samples/client/petstore-security-test/typescript-fetch/api.ts index f5b4defe68b7..28a5d6c8b538 100644 --- a/samples/client/petstore-security-test/typescript-fetch/api.ts +++ b/samples/client/petstore-security-test/typescript-fetch/api.ts @@ -100,7 +100,7 @@ export interface Return { export const FakeApiFetchParamCreator = function (configuration?: Configuration) { return { /** - * + * To test code injection *_/ ' \" =end -- \\r\\n \\n \\r * @summary To test code injection *_/ ' \" =end -- \\r\\n \\n \\r * @param {UNKNOWN_BASE_TYPE} [UNKNOWN_BASE_TYPE] * @param {*} [options] Override http request option. @@ -141,7 +141,7 @@ export const FakeApiFetchParamCreator = function (configuration?: Configuration) export const FakeApiFp = function(configuration?: Configuration) { return { /** - * + * To test code injection *_/ ' \" =end -- \\r\\n \\n \\r * @summary To test code injection *_/ ' \" =end -- \\r\\n \\n \\r * @param {UNKNOWN_BASE_TYPE} [UNKNOWN_BASE_TYPE] * @param {*} [options] Override http request option. @@ -169,7 +169,7 @@ export const FakeApiFp = function(configuration?: Configuration) { export const FakeApiFactory = function (configuration?: Configuration, fetch?: FetchAPI, basePath?: string) { return { /** - * + * To test code injection *_/ ' \" =end -- \\r\\n \\n \\r * @summary To test code injection *_/ ' \" =end -- \\r\\n \\n \\r * @param {UNKNOWN_BASE_TYPE} [UNKNOWN_BASE_TYPE] * @param {*} [options] Override http request option. @@ -189,7 +189,7 @@ export const FakeApiFactory = function (configuration?: Configuration, fetch?: F */ export class FakeApi extends BaseAPI { /** - * + * To test code injection *_/ ' \" =end -- \\r\\n \\n \\r * @summary To test code injection *_/ ' \" =end -- \\r\\n \\n \\r * @param {UNKNOWN_BASE_TYPE} [UNKNOWN_BASE_TYPE] * @param {*} [options] Override http request option.