diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TypeScriptFetchClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TypeScriptFetchClientCodegen.java index ab15af74c3c..0d3668e6703 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TypeScriptFetchClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TypeScriptFetchClientCodegen.java @@ -782,16 +782,30 @@ public class TypeScriptFetchClientCodegen extends AbstractTypeScriptClientCodege } } } + + List oneOfsList = Optional.ofNullable(cm.getComposedSchemas()) + .map(CodegenComposedSchemas::getOneOf) + .orElse(Collections.emptyList()); + + cm.oneOfModels = oneOfsList.stream() + .filter(CodegenProperty::getIsModel) + .map(CodegenProperty::getBaseType) + .filter(Objects::nonNull) + .collect(Collectors.toCollection(TreeSet::new)); + + cm.oneOfArrays = oneOfsList.stream() + .filter(CodegenProperty::getIsArray) + .map(CodegenProperty::getComplexType) + .filter(Objects::nonNull) + .collect(Collectors.toCollection(TreeSet::new)); + if (!cm.oneOf.isEmpty()) { // For oneOfs only import $refs within the oneOf - TreeSet oneOfRefs = new TreeSet<>(); - for (String im : cm.imports) { - if (cm.oneOf.contains(im)) { - oneOfRefs.add(im); - } - } - cm.imports = oneOfRefs; + cm.imports = cm.imports.stream() + .filter(im -> cm.oneOfModels.contains(im) || cm.oneOfArrays.contains(im)) + .collect(Collectors.toCollection(TreeSet::new)); } + return cm; } @@ -1474,6 +1488,12 @@ public class TypeScriptFetchClientCodegen extends AbstractTypeScriptClientCodege public class ExtendedCodegenModel extends CodegenModel { @Getter @Setter public Set modelImports = new TreeSet(); + + @Getter @Setter + public Set oneOfModels = new TreeSet<>(); + @Getter @Setter + public Set oneOfArrays = new TreeSet<>(); + public boolean isEntity; // Is a model containing an "id" property marked as isUniqueId public String returnPassthrough; public boolean hasReturnPassthroughVoid; @@ -1570,6 +1590,7 @@ public class TypeScriptFetchClientCodegen extends AbstractTypeScriptClientCodege this.setItems(cm.getItems()); this.setAdditionalProperties(cm.getAdditionalProperties()); this.setIsModel(cm.getIsModel()); + this.setComposedSchemas(cm.getComposedSchemas()); } @Override diff --git a/modules/openapi-generator/src/main/resources/typescript-fetch/modelOneOf.mustache b/modules/openapi-generator/src/main/resources/typescript-fetch/modelOneOf.mustache index b516da670e1..721f0fc7b76 100644 --- a/modules/openapi-generator/src/main/resources/typescript-fetch/modelOneOf.mustache +++ b/modules/openapi-generator/src/main/resources/typescript-fetch/modelOneOf.mustache @@ -1,5 +1,5 @@ {{#hasImports}} -{{#oneOf}} +{{#imports}} import type { {{{.}}} } from './{{.}}{{importFileExtension}}'; import { instanceOf{{{.}}}, @@ -7,7 +7,7 @@ import { {{{.}}}FromJSONTyped, {{{.}}}ToJSON, } from './{{.}}{{importFileExtension}}'; -{{/oneOf}} +{{/imports}} {{/hasImports}} {{>modelOneOfInterfaces}} @@ -31,11 +31,30 @@ export function {{classname}}FromJSONTyped(json: any, ignoreDiscriminator: boole } {{/discriminator}} {{^discriminator}} - {{#oneOf}} + {{#oneOfModels}} + {{#-first}} + if (typeof json !== 'object') { + return json; + } + {{/-first}} if (instanceOf{{{.}}}(json)) { return {{{.}}}FromJSONTyped(json, true); } - {{/oneOf}} + {{/oneOfModels}} + {{#oneOfArrays}} + {{#-first}} + if (Array.isArray(json)) { + if (json.every(item => typeof item === 'object')) { + {{/-first}} + if (json.every(item => instanceOf{{{.}}}(item))) { + return json.map(value => {{{.}}}FromJSONTyped(value, true)); + } + {{#-last}} + } + return json; + } + {{/-last}} + {{/oneOfArrays}} return {} as any; {{/discriminator}} @@ -59,13 +78,31 @@ export function {{classname}}ToJSONTyped(value?: {{classname}} | null, ignoreDis return json; } {{/discriminator}} - {{^discriminator}} - {{#oneOf}} + {{#oneOfModels}} + {{#-first}} + if (typeof value !== 'object') { + return value; + } + {{/-first}} if (instanceOf{{{.}}}(value)) { return {{{.}}}ToJSON(value as {{{.}}}); } - {{/oneOf}} + {{/oneOfModels}} + {{#oneOfArrays}} + {{#-first}} + if (Array.isArray(value)) { + if (value.every(item => typeof item === 'object')) { + {{/-first}} + if (value.every(item => instanceOf{{{.}}}(item))) { + return value.map(value => {{{.}}}ToJSON(value as {{{.}}})); + } + {{#-last}} + } + return value; + } + {{/-last}} + {{/oneOfArrays}} return {}; {{/discriminator}} diff --git a/modules/openapi-generator/src/test/resources/3_0/typescript-fetch/oneOf.yaml b/modules/openapi-generator/src/test/resources/3_0/typescript-fetch/oneOf.yaml index 244139a28d3..ba0dd51d792 100644 --- a/modules/openapi-generator/src/test/resources/3_0/typescript-fetch/oneOf.yaml +++ b/modules/openapi-generator/src/test/resources/3_0/typescript-fetch/oneOf.yaml @@ -15,12 +15,34 @@ paths: application/json: schema: $ref: '#/components/schemas/TestResponse' + /test-array: + get: + operationId: testArray + responses: + 200: + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/TestArrayResponse' components: schemas: + TestArrayResponse: + oneOf: + - type: array + items: + $ref: "#/components/schemas/TestA" + - type: array + items: + $ref: "#/components/schemas/TestB" + - type: array + items: + type: string TestResponse: oneOf: - $ref: "#/components/schemas/TestA" - $ref: "#/components/schemas/TestB" + - type: string TestA: type: object properties: @@ -34,4 +56,4 @@ components: bar: type: string required: - - bar + - bar \ No newline at end of file diff --git a/samples/client/petstore/typescript-fetch/builds/oneOf/.openapi-generator/FILES b/samples/client/petstore/typescript-fetch/builds/oneOf/.openapi-generator/FILES index 1e29c70aeda..6bb15a3adfc 100644 --- a/samples/client/petstore/typescript-fetch/builds/oneOf/.openapi-generator/FILES +++ b/samples/client/petstore/typescript-fetch/builds/oneOf/.openapi-generator/FILES @@ -2,6 +2,7 @@ apis/DefaultApi.ts apis/index.ts index.ts models/TestA.ts +models/TestArrayResponse.ts models/TestB.ts models/TestResponse.ts models/index.ts diff --git a/samples/client/petstore/typescript-fetch/builds/oneOf/apis/DefaultApi.ts b/samples/client/petstore/typescript-fetch/builds/oneOf/apis/DefaultApi.ts index 3383adf93df..f7e1936ab80 100644 --- a/samples/client/petstore/typescript-fetch/builds/oneOf/apis/DefaultApi.ts +++ b/samples/client/petstore/typescript-fetch/builds/oneOf/apis/DefaultApi.ts @@ -15,9 +15,12 @@ import * as runtime from '../runtime'; import type { + TestArrayResponse, TestResponse, } from '../models/index'; import { + TestArrayResponseFromJSON, + TestArrayResponseToJSON, TestResponseFromJSON, TestResponseToJSON, } from '../models/index'; @@ -51,4 +54,28 @@ export class DefaultApi extends runtime.BaseAPI { return await response.value(); } + /** + */ + async testArrayRaw(initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise> { + const queryParameters: any = {}; + + const headerParameters: runtime.HTTPHeaders = {}; + + const response = await this.request({ + path: `/test-array`, + method: 'GET', + headers: headerParameters, + query: queryParameters, + }, initOverrides); + + return new runtime.JSONApiResponse(response, (jsonValue) => TestArrayResponseFromJSON(jsonValue)); + } + + /** + */ + async testArray(initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise { + const response = await this.testArrayRaw(initOverrides); + return await response.value(); + } + } diff --git a/samples/client/petstore/typescript-fetch/builds/oneOf/models/TestArrayResponse.ts b/samples/client/petstore/typescript-fetch/builds/oneOf/models/TestArrayResponse.ts new file mode 100644 index 00000000000..0848d99781c --- /dev/null +++ b/samples/client/petstore/typescript-fetch/builds/oneOf/models/TestArrayResponse.ts @@ -0,0 +1,82 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * testing oneOf without discriminator + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * 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 + * Do not edit the class manually. + */ + +import type { TestA } from './TestA'; +import { + instanceOfTestA, + TestAFromJSON, + TestAFromJSONTyped, + TestAToJSON, +} from './TestA'; +import type { TestB } from './TestB'; +import { + instanceOfTestB, + TestBFromJSON, + TestBFromJSONTyped, + TestBToJSON, +} from './TestB'; + +/** + * @type TestArrayResponse + * + * @export + */ +export type TestArrayResponse = Array | Array | Array; + +export function TestArrayResponseFromJSON(json: any): TestArrayResponse { + return TestArrayResponseFromJSONTyped(json, false); +} + +export function TestArrayResponseFromJSONTyped(json: any, ignoreDiscriminator: boolean): TestArrayResponse { + if (json == null) { + return json; + } + if (Array.isArray(json)) { + if (json.every(item => typeof item === 'object')) { + if (json.every(item => instanceOfTestA(item))) { + return json.map(value => TestAFromJSONTyped(value, true)); + } + if (json.every(item => instanceOfTestB(item))) { + return json.map(value => TestBFromJSONTyped(value, true)); + } + } + return json; + } + + return {} as any; +} + +export function TestArrayResponseToJSON(json: any): any { + return TestArrayResponseToJSONTyped(json, false); +} + +export function TestArrayResponseToJSONTyped(value?: TestArrayResponse | null, ignoreDiscriminator: boolean = false): any { + if (value == null) { + return value; + } + if (Array.isArray(value)) { + if (value.every(item => typeof item === 'object')) { + if (value.every(item => instanceOfTestA(item))) { + return value.map(value => TestAToJSON(value as TestA)); + } + if (value.every(item => instanceOfTestB(item))) { + return value.map(value => TestBToJSON(value as TestB)); + } + } + return value; + } + + return {}; +} + diff --git a/samples/client/petstore/typescript-fetch/builds/oneOf/models/TestResponse.ts b/samples/client/petstore/typescript-fetch/builds/oneOf/models/TestResponse.ts index e228a3e9f73..09d1da13156 100644 --- a/samples/client/petstore/typescript-fetch/builds/oneOf/models/TestResponse.ts +++ b/samples/client/petstore/typescript-fetch/builds/oneOf/models/TestResponse.ts @@ -32,7 +32,7 @@ import { * * @export */ -export type TestResponse = TestA | TestB; +export type TestResponse = TestA | TestB | string; export function TestResponseFromJSON(json: any): TestResponse { return TestResponseFromJSONTyped(json, false); @@ -42,6 +42,9 @@ export function TestResponseFromJSONTyped(json: any, ignoreDiscriminator: boolea if (json == null) { return json; } + if (typeof json !== 'object') { + return json; + } if (instanceOfTestA(json)) { return TestAFromJSONTyped(json, true); } @@ -60,7 +63,9 @@ export function TestResponseToJSONTyped(value?: TestResponse | null, ignoreDiscr if (value == null) { return value; } - + if (typeof value !== 'object') { + return value; + } if (instanceOfTestA(value)) { return TestAToJSON(value as TestA); } diff --git a/samples/client/petstore/typescript-fetch/builds/oneOf/models/index.ts b/samples/client/petstore/typescript-fetch/builds/oneOf/models/index.ts index 8da6963476f..7ba8efbbf0e 100644 --- a/samples/client/petstore/typescript-fetch/builds/oneOf/models/index.ts +++ b/samples/client/petstore/typescript-fetch/builds/oneOf/models/index.ts @@ -1,5 +1,6 @@ /* tslint:disable */ /* eslint-disable */ export * from './TestA'; +export * from './TestArrayResponse'; export * from './TestB'; export * from './TestResponse';