Skip to content

Commit 242291f

Browse files
committed
[typescript-fetch] number, string, and Date now considered in oneOf models. Issue #21259
1 parent 5eb083e commit 242291f

File tree

4 files changed

+216
-2
lines changed

4 files changed

+216
-2
lines changed

modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TypeScriptFetchClientCodegen.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -799,6 +799,11 @@ private ExtendedCodegenModel processCodeGenModel(ExtendedCodegenModel cm) {
799799
.filter(Objects::nonNull)
800800
.collect(Collectors.toCollection(TreeSet::new));
801801

802+
cm.oneOfPrimitives = oneOfsList.stream()
803+
.filter(CodegenProperty::getIsPrimitiveType)
804+
.filter(Objects::nonNull)
805+
.collect(Collectors.toCollection(HashSet::new));
806+
802807
if (!cm.oneOf.isEmpty()) {
803808
// For oneOfs only import $refs within the oneOf
804809
cm.imports = cm.imports.stream()
@@ -1484,6 +1489,8 @@ public class ExtendedCodegenModel extends CodegenModel {
14841489
public Set<String> oneOfModels = new TreeSet<>();
14851490
@Getter @Setter
14861491
public Set<String> oneOfArrays = new TreeSet<>();
1492+
@Getter @Setter
1493+
public Set<CodegenProperty> oneOfPrimitives = new HashSet<>();
14871494

14881495
public boolean isEntity; // Is a model containing an "id" property marked as isUniqueId
14891496
public String returnPassthrough;

modules/openapi-generator/src/main/resources/typescript-fetch/modelOneOf.mustache

Lines changed: 114 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,64 @@ export function {{classname}}FromJSONTyped(json: any, ignoreDiscriminator: boole
5555
}
5656
{{/-last}}
5757
{{/oneOfArrays}}
58-
58+
{{#oneOfPrimitives}}
59+
{{#isArray}}
60+
{{#items}}
61+
{{#isDateType}}
62+
if (Array.isArray(json)) {
63+
if (json.every(item => !(isNaN(new Date(json).getTime()))) {
64+
return json.map(value => new Date(json);
65+
}
66+
}
67+
{{/isDateType}}
68+
{{#isDateTimeType}}
69+
if (Array.isArray(json)) {
70+
if (json.every(item => !(isNaN(new Date(json).getTime()))) {
71+
return json.map(value => new Date(json);
72+
}
73+
}
74+
{{/isDateTimeType}}
75+
{{#isNumeric}}
76+
if (Array.isArray(json)) {
77+
if (json.every(item => typeof item === 'number'{{#isEnum}} && ({{#allowableValues}}{{#values}}item === {{.}}{{^-last}} || {{/-last}}{{/values}}{{/allowableValues}}){{/isEnum}})) {
78+
return json;
79+
}
80+
}
81+
{{/isNumeric}}
82+
{{#isString}}
83+
if (Array.isArray(json)) {
84+
if (json.every(item => typeof item === 'string'{{#isEnum}} && ({{#allowableValues}}{{#values}}item === '{{.}}'{{^-last}} || {{/-last}}{{/values}}{{/allowableValues}}){{/isEnum}})) {
85+
return json;
86+
}
87+
}
88+
{{/isString}}
89+
{{/items}}
90+
{{/isArray}}
91+
{{^isArray}}
92+
{{#isDateType}}
93+
if(!(isNaN(new Date(json).getTime()))) {
94+
return {{^required}}json == null ? undefined : {{/required}}({{#required}}{{#isNullable}}json == null ? null : {{/isNullable}}{{/required}}new Date(json));
95+
}
96+
{{/isDateType}}
97+
{{^isDateType}}
98+
{{#isDateTimeType}}
99+
if(!(isNaN(new Date(json).getTime()))) {
100+
return {{^required}}json == null ? undefined : {{/required}}({{#required}}{{#isNullable}}json == null ? null : {{/isNullable}}{{/required}}new Date(json));
101+
}
102+
{{/isDateTimeType}}
103+
{{/isDateType}}
104+
{{#isNumeric}}
105+
if(typeof json === 'number'{{#isEnum}} && ({{#allowableValues}}{{#values}}json === {{.}}{{^-last}} || {{/-last}}{{/values}}{{/allowableValues}}){{/isEnum}}) {
106+
return json;
107+
}
108+
{{/isNumeric}}
109+
{{#isString}}
110+
if(typeof json === 'string'{{#isEnum}} && ({{#allowableValues}}{{#values}}json === '{{.}}'{{^-last}} || {{/-last}}{{/values}}{{/allowableValues}}){{/isEnum}}) {
111+
return json;
112+
}
113+
{{/isString}}
114+
{{/isArray}}
115+
{{/oneOfPrimitives}}
59116
return {} as any;
60117
{{/discriminator}}
61118
}
@@ -103,7 +160,62 @@ export function {{classname}}ToJSONTyped(value?: {{classname}} | null, ignoreDis
103160
}
104161
{{/-last}}
105162
{{/oneOfArrays}}
106-
163+
{{#oneOfPrimitives}}
164+
{{#isArray}}
165+
{{#items}}
166+
{{#isDateType}}
167+
if (Array.isArray(value)) {
168+
if (value.every(item => item instanceof Date) {
169+
return value.map(value => value.toISOString().substring(0,10)));
170+
}
171+
}
172+
{{/isDateType}}
173+
{{#isDateTimeType}}
174+
if (Array.isArray(value)) {
175+
if (value.every(item => item instanceof Date) {
176+
return value.map(value => value.toISOString();
177+
}
178+
}
179+
{{/isDateTimeType}}
180+
{{#isNumeric}}
181+
if (Array.isArray(value)) {
182+
if (value.every(item => typeof item === 'number'{{#isEnum}} && ({{#allowableValues}}{{#values}}item === {{.}}{{^-last}} || {{/-last}}{{/values}}{{/allowableValues}}){{/isEnum}}) {
183+
return value;
184+
}
185+
}
186+
{{/isNumeric}}
187+
{{#isString}}
188+
if (Array.isArray(value)) {
189+
if (value.every(item => typeof item === 'string'{{#isEnum}} && ({{#allowableValues}}{{#values}}item === '{{.}}'{{^-last}} || {{/-last}}{{/values}}{{/allowableValues}}){{/isEnum}}) {
190+
return value;
191+
}
192+
}
193+
{{/isString}}
194+
{{/items}}
195+
{{/isArray}}
196+
{{^isArray}}
197+
{{#isDateType}}
198+
if(value instanceof Date) {
199+
return ((value{{#isNullable}} as any{{/isNullable}}){{^required}}{{#isNullable}}?{{/isNullable}}{{/required}}.toISOString().substring(0,10));
200+
}
201+
{{/isDateType}}
202+
{{#isDateTimeType}}
203+
if(value instanceof Date) {
204+
return {{^required}}{{#isNullable}}value === null ? null : {{/isNullable}}{{^isNullable}}value == null ? undefined : {{/isNullable}}{{/required}}((value{{#isNullable}} as any{{/isNullable}}){{^required}}{{#isNullable}}?{{/isNullable}}{{/required}}.toISOString());
205+
}
206+
{{/isDateTimeType}}
207+
{{#isNumeric}}
208+
if(typeof value === 'number'{{#isEnum}} && ({{#allowableValues}}{{#values}}value === {{.}}{{^-last}} || {{/-last}}{{/values}}{{/allowableValues}}){{/isEnum}}) {
209+
return value;
210+
}
211+
{{/isNumeric}}
212+
{{#isString}}
213+
if(typeof value === 'string'{{#isEnum}} && ({{#allowableValues}}{{#values}}value === '{{.}}'{{^-last}} || {{/-last}}{{/values}}{{/allowableValues}}){{/isEnum}}) {
214+
return value;
215+
}
216+
{{/isString}}
217+
{{/isArray}}
218+
{{/oneOfPrimitives}}
107219
return {};
108220
{{/discriminator}}
109221
}

modules/openapi-generator/src/test/java/org/openapitools/codegen/typescript/fetch/TypeScriptFetchClientCodegenTest.java

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -372,6 +372,39 @@ public void givenObjectHasAdditionalPropertiesWhenGenerateThenIndexSignatureNotU
372372
TestUtils.assertFileContains(exampleApiPath, "new Blob([JSON.stringify(ResponseOfStringToJSON");
373373
}
374374

375+
@Test(description = "Issue #21295")
376+
public void givenSchemaIsOneOfAndComposedSchemasArePrimitiveThenReturnStatementsAreCorrect() throws Exception {
377+
File output = Files.createTempDirectory("test").toFile().getCanonicalFile();
378+
output.deleteOnExit();
379+
String outputPath = output.getAbsolutePath();
380+
381+
382+
TypeScriptFetchClientCodegen clientCodegen = new TypeScriptFetchClientCodegen();
383+
clientCodegen.setOutputDir(outputPath);
384+
385+
DefaultGenerator defaultGenerator = new DefaultGenerator();
386+
defaultGenerator.opts(
387+
new ClientOptInput().openAPI(TestUtils.parseSpec("src/test/resources/bugs/issue_21259.yaml"))
388+
.config(clientCodegen)
389+
).generate();
390+
391+
Path exampleModelPath = Paths.get(outputPath + "/models/MyCustomSpeed.ts");
392+
//FromJSON
393+
TestUtils.assertFileContains(exampleModelPath, "typeof json === 'number'");
394+
TestUtils.assertFileContains(exampleModelPath, "typeof json === 'string'");
395+
TestUtils.assertFileContains(exampleModelPath, "json === 'fixed-value-a' || json === 'fixed-value-b' || json === 'fixed-value-c'");
396+
TestUtils.assertFileContains(exampleModelPath, "isNaN(new Date(json).getTime())");
397+
TestUtils.assertFileContains(exampleModelPath, "json.every(item => typeof item === 'number'");
398+
// TestUtils.assertFileContains(exampleModelPath, "json.every(item => typeof item === 'string' && (item === 'oneof-array-enum-a' || item oneof-array-enum-b || item === oneof-array-enum-c)");
399+
//ToJSON
400+
TestUtils.assertFileContains(exampleModelPath, "typeof value === 'number'");
401+
TestUtils.assertFileContains(exampleModelPath, "typeof value === 'string'");
402+
TestUtils.assertFileContains(exampleModelPath, "value === 'fixed-value-a' || value === 'fixed-value-b' || value === 'fixed-value-c'");
403+
TestUtils.assertFileContains(exampleModelPath, "value instanceof Date");
404+
TestUtils.assertFileContains(exampleModelPath, "value.every(item => typeof item === 'number'");
405+
// TestUtils.assertFileContains(exampleModelPath, "value.every(item => typeof item === 'string' && (item === 'oneof-array-enum-a' || item oneof-array-enum-b || item === oneof-array-enum-c)");
406+
}
407+
375408
private static File generate(Map<String, Object> properties) throws IOException {
376409
File output = Files.createTempDirectory("test").toFile();
377410
output.deleteOnExit();
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
#Modified from the original
2+
openapi: 3.0.1
3+
info:
4+
title: Minimal API for Bug Report
5+
version: v1
6+
paths:
7+
/test:
8+
post:
9+
summary: Test endpoint with MyCustomSpeed
10+
requestBody:
11+
required: true
12+
content:
13+
application/json:
14+
schema:
15+
$ref: '#/components/schemas/TestPayload'
16+
responses:
17+
'200':
18+
description: OK
19+
components:
20+
schemas:
21+
MyNumericValue:
22+
type: object
23+
properties:
24+
lmnop:
25+
type: number
26+
description: A numeric value (e.g., 0 to 1).
27+
MyCustomSpeed:
28+
oneOf:
29+
- $ref: '#/components/schemas/MyNumericValue'
30+
- type: string
31+
enum:
32+
- "fixed-value-a"
33+
- "fixed-value-b"
34+
- "fixed-value-c"
35+
- type: string
36+
format: date
37+
- type: string
38+
format: date-time
39+
- type: integer
40+
format: int64
41+
enum: [10, 20, 30]
42+
- type: array
43+
items:
44+
type: number
45+
- type: array
46+
items:
47+
- type: object
48+
- type: array
49+
items:
50+
- type: string
51+
enum:
52+
# It seems enums within arrays don't work. Leaving this here, though
53+
- "oneof-array-enum-a"
54+
- "oneof-array-enum-b"
55+
- "oneof-array-enum-c"
56+
- type:
57+
description: A value that can be a number or a specific string.
58+
TestPayload:
59+
type: object
60+
properties:
61+
speed_setting:
62+
$ref: '#/components/schemas/MyCustomSpeed'

0 commit comments

Comments
 (0)