Skip to content

Commit 71a7a82

Browse files
authored
Nullable subschemas (#13850)
* Add failing example of nullable subschema * Do not generate new subschemas when nullable * Generate client * Update go example schema/test
1 parent 028b38d commit 71a7a82

File tree

16 files changed

+715
-9
lines changed

16 files changed

+715
-9
lines changed
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
generatorName: typescript-fetch
2+
outputDir: samples/client/petstore/typescript-fetch/builds/allOf-nullable
3+
inputSpec: modules/openapi-generator/src/test/resources/3_0/allOf-nullable.yaml
4+
templateDir: modules/openapi-generator/src/main/resources/typescript-fetch

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

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -196,11 +196,16 @@ private boolean isModelNeeded(Schema schema, Set<Schema> visitedSchemas) {
196196
// allOf, anyOf, oneOf
197197
ComposedSchema m = (ComposedSchema) schema;
198198

199-
if (m.getAllOf() != null && m.getAllOf().size() == 1 && m.getReadOnly() != null && m.getReadOnly()) {
200-
// Check if this composed schema only contains an allOf and a readOnly.
199+
boolean isSingleAllOf = m.getAllOf() != null && m.getAllOf().size() == 1;
200+
boolean isReadOnly = m.getReadOnly() != null && m.getReadOnly();
201+
boolean isNullable = m.getNullable() != null && m.getNullable();
202+
203+
if (isSingleAllOf && (isReadOnly || isNullable)) {
204+
// Check if this composed schema only contains an allOf and a readOnly or nullable.
201205
ComposedSchema c = new ComposedSchema();
202206
c.setAllOf(m.getAllOf());
203-
c.setReadOnly(true);
207+
c.setReadOnly(m.getReadOnly());
208+
c.setNullable(m.getNullable());
204209
if (m.equals(c)) {
205210
return isModelNeeded(m.getAllOf().get(0), visitedSchemas);
206211
}

modules/openapi-generator/src/test/java/org/openapitools/codegen/go/GoClientCodegenTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,7 @@ public void testNullableComposition() throws IOException {
160160
List<File> files = generator.opts(configurator.toClientOptInput()).generate();
161161
files.forEach(File::deleteOnExit);
162162

163-
TestUtils.assertFileContains(Paths.get(output + "/model_example.go"), "Child NullableExampleChild");
163+
TestUtils.assertFileContains(Paths.get(output + "/model_example.go"), "Child NullableChild");
164164
}
165165

166166
@Test

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

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -470,4 +470,14 @@ public void testNestedReadonlySchemas() {
470470
final Map<String, Schema> schemaBefore = openAPI.getComponents().getSchemas();
471471
Assert.assertEquals(schemaBefore.keySet(), Sets.newHashSet("club", "owner"));
472472
}
473+
474+
@Test(description = "Don't generate new schemas for nullable references")
475+
public void testNestedNullableSchemas() {
476+
final OpenAPI openAPI = TestUtils.parseFlattenSpec("src/test/resources/3_0/allOf-nullable.yaml");
477+
final DefaultCodegen codegen = new TypeScriptFetchClientCodegen();
478+
codegen.processOpts();
479+
codegen.setOpenAPI(openAPI);
480+
final Map<String, Schema> schemaBefore = openAPI.getComponents().getSchemas();
481+
Assert.assertEquals(schemaBefore.keySet(), Sets.newHashSet("club", "owner"));
482+
}
473483
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
openapi: 3.0.1
2+
info:
3+
version: 1.0.0
4+
title: Example
5+
license:
6+
name: MIT
7+
servers:
8+
- url: http://api.example.xyz/v1
9+
paths:
10+
/person/display/{personId}:
11+
get:
12+
parameters:
13+
- name: personId
14+
in: path
15+
required: true
16+
description: The id of the person to retrieve
17+
schema:
18+
type: string
19+
operationId: list
20+
responses:
21+
'200':
22+
description: OK
23+
content:
24+
application/json:
25+
schema:
26+
$ref: "#/components/schemas/club"
27+
components:
28+
schemas:
29+
club:
30+
properties:
31+
owner:
32+
allOf:
33+
- $ref: '#/components/schemas/owner'
34+
nullable: true
35+
36+
owner:
37+
properties:
38+
name:
39+
type: string
40+
maxLength: 255
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# OpenAPI Generator Ignore
2+
# Generated by openapi-generator https://github.com/openapitools/openapi-generator
3+
4+
# Use this file to prevent files from being overwritten by the generator.
5+
# The patterns follow closely to .gitignore or .dockerignore.
6+
7+
# As an example, the C# client generator defines ApiClient.cs.
8+
# You can make changes and tell OpenAPI Generator to ignore just this file by uncommenting the following line:
9+
#ApiClient.cs
10+
11+
# You can match any string of characters against a directory, file or extension with a single asterisk (*):
12+
#foo/*/qux
13+
# The above matches foo/bar/qux and foo/baz/qux, but not foo/bar/baz/qux
14+
15+
# You can recursively match patterns against a directory, file or extension with a double asterisk (**):
16+
#foo/**/qux
17+
# This matches foo/bar/qux, foo/baz/qux, and foo/bar/baz/qux
18+
19+
# You can also negate patterns with an exclamation (!).
20+
# For example, you can ignore all files in a docs folder with the file extension .md:
21+
#docs/*.md
22+
# Then explicitly reverse the ignore rule for a single file:
23+
#!docs/README.md
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
apis/DefaultApi.ts
2+
apis/index.ts
3+
index.ts
4+
models/Club.ts
5+
models/Owner.ts
6+
models/index.ts
7+
runtime.ts
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
6.2.1-SNAPSHOT
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
/* tslint:disable */
2+
/* eslint-disable */
3+
/**
4+
* Example
5+
* No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator)
6+
*
7+
* The version of the OpenAPI document: 1.0.0
8+
*
9+
*
10+
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
11+
* https://openapi-generator.tech
12+
* Do not edit the class manually.
13+
*/
14+
15+
16+
import * as runtime from '../runtime';
17+
import type {
18+
Club,
19+
} from '../models';
20+
import {
21+
ClubFromJSON,
22+
ClubToJSON,
23+
} from '../models';
24+
25+
export interface ListRequest {
26+
personId: string;
27+
}
28+
29+
/**
30+
*
31+
*/
32+
export class DefaultApi extends runtime.BaseAPI {
33+
34+
/**
35+
*/
36+
async listRaw(requestParameters: ListRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<runtime.ApiResponse<Club>> {
37+
if (requestParameters.personId === null || requestParameters.personId === undefined) {
38+
throw new runtime.RequiredError('personId','Required parameter requestParameters.personId was null or undefined when calling list.');
39+
}
40+
41+
const queryParameters: any = {};
42+
43+
const headerParameters: runtime.HTTPHeaders = {};
44+
45+
const response = await this.request({
46+
path: `/person/display/{personId}`.replace(`{${"personId"}}`, encodeURIComponent(String(requestParameters.personId))),
47+
method: 'GET',
48+
headers: headerParameters,
49+
query: queryParameters,
50+
}, initOverrides);
51+
52+
return new runtime.JSONApiResponse(response, (jsonValue) => ClubFromJSON(jsonValue));
53+
}
54+
55+
/**
56+
*/
57+
async list(requestParameters: ListRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<Club> {
58+
const response = await this.listRaw(requestParameters, initOverrides);
59+
return await response.value();
60+
}
61+
62+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
/* tslint:disable */
2+
/* eslint-disable */
3+
export * from './DefaultApi';

0 commit comments

Comments
 (0)