Skip to content

Commit 992afd5

Browse files
authored
Fix ModelUtils.getUnusedSchema() (#253)
Fix #252 `ModelUtils.getUnusedSchema()` consider Schemas referenced in other Schemas. Implemented for: * array * object * maps * ComposedSchema - oneOf - anyOf - allOf * not
1 parent 8de5c62 commit 992afd5

File tree

3 files changed

+283
-4
lines changed

3 files changed

+283
-4
lines changed

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

Lines changed: 57 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,7 @@ public static List<String> getSchemasUsedOnlyInFormParam(OpenAPI openAPI) {
159159
*/
160160
private static void visitOpenAPI(OpenAPI openAPI, OpenAPISchemaVisitor visitor) {
161161
Map<String, PathItem> paths = openAPI.getPaths();
162+
List<String> visitedSchemas = new ArrayList<>();
162163

163164
if (paths != null) {
164165
for (PathItem path : paths.values()) {
@@ -170,7 +171,7 @@ private static void visitOpenAPI(OpenAPI openAPI, OpenAPISchemaVisitor visitor)
170171
for (Parameter p : operation.getParameters()) {
171172
Parameter parameter = getReferencedParameter(openAPI, p);
172173
if (parameter.getSchema() != null) {
173-
visitor.visit(parameter.getSchema(), null);
174+
visitSchema(openAPI, parameter.getSchema(), null, visitedSchemas, visitor);
174175
}
175176
}
176177
}
@@ -180,7 +181,7 @@ private static void visitOpenAPI(OpenAPI openAPI, OpenAPISchemaVisitor visitor)
180181
if (requestBody != null && requestBody.getContent() != null) {
181182
for (Entry<String, MediaType> e : requestBody.getContent().entrySet()) {
182183
if (e.getValue().getSchema() != null) {
183-
visitor.visit(e.getValue().getSchema(), e.getKey());
184+
visitSchema(openAPI, e.getValue().getSchema(), e.getKey(), visitedSchemas, visitor);
184185
}
185186
}
186187
}
@@ -192,7 +193,7 @@ private static void visitOpenAPI(OpenAPI openAPI, OpenAPISchemaVisitor visitor)
192193
if (apiResponse != null && apiResponse.getContent() != null) {
193194
for (Entry<String, MediaType> e : apiResponse.getContent().entrySet()) {
194195
if (e.getValue().getSchema() != null) {
195-
visitor.visit(e.getValue().getSchema(), e.getKey());
196+
visitSchema(openAPI, e.getValue().getSchema(), e.getKey(), visitedSchemas, visitor);
196197
}
197198
}
198199
}
@@ -204,6 +205,59 @@ private static void visitOpenAPI(OpenAPI openAPI, OpenAPISchemaVisitor visitor)
204205
}
205206
}
206207

208+
private static void visitSchema(OpenAPI openAPI, Schema schema, String mimeType, List<String> visitedSchemas, OpenAPISchemaVisitor visitor) {
209+
visitor.visit(schema, mimeType);
210+
if(schema.get$ref() != null) {
211+
String ref = getSimpleRef(schema.get$ref());
212+
if(!visitedSchemas.contains(ref)) {
213+
visitedSchemas.add(ref);
214+
Schema referencedSchema = getSchemas(openAPI).get(ref);
215+
if(referencedSchema != null) {
216+
visitSchema(openAPI, referencedSchema, mimeType, visitedSchemas, visitor);
217+
}
218+
}
219+
}
220+
if(schema instanceof ComposedSchema) {
221+
List<Schema> oneOf = ((ComposedSchema) schema).getOneOf();
222+
if(oneOf != null) {
223+
for (Schema s : oneOf) {
224+
visitSchema(openAPI, s, mimeType, visitedSchemas, visitor);
225+
}
226+
}
227+
List<Schema> allOf = ((ComposedSchema) schema).getAllOf();
228+
if(allOf != null) {
229+
for (Schema s : allOf) {
230+
visitSchema(openAPI, s, mimeType, visitedSchemas, visitor);
231+
}
232+
}
233+
List<Schema> anyOf = ((ComposedSchema) schema).getAnyOf();
234+
if(anyOf != null) {
235+
for (Schema s : anyOf) {
236+
visitSchema(openAPI, s, mimeType, visitedSchemas, visitor);
237+
}
238+
}
239+
} else if(schema instanceof ArraySchema) {
240+
Schema itemsSchema = ((ArraySchema) schema).getItems();
241+
if(itemsSchema != null) {
242+
visitSchema(openAPI, itemsSchema, mimeType, visitedSchemas, visitor);
243+
}
244+
} else if(isMapSchema(schema)) {
245+
Object additionalProperties = schema.getAdditionalProperties();
246+
if(additionalProperties instanceof Schema) {
247+
visitSchema(openAPI, (Schema) additionalProperties, mimeType, visitedSchemas, visitor);
248+
}
249+
}
250+
if(schema.getNot() != null) {
251+
visitSchema(openAPI, schema.getNot(), mimeType, visitedSchemas, visitor);
252+
}
253+
Map<String, Schema> properties = schema.getProperties();
254+
if(properties != null) {
255+
for (Schema property : properties.values()) {
256+
visitSchema(openAPI, property, mimeType, visitedSchemas, visitor);
257+
}
258+
}
259+
}
260+
207261
@FunctionalInterface
208262
private static interface OpenAPISchemaVisitor {
209263

modules/openapi-generator/src/test/java/org/openapitools/codegen/utils/ModelUtilsTest.java

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ public class ModelUtilsTest {
4040
public void testGetAllUsedSchemas() {
4141
final OpenAPI openAPI = new OpenAPIParser().readLocation("src/test/resources/3_0/unusedSchemas.yaml", null, new ParseOptions()).getOpenAPI();
4242
List<String> allUsedSchemas = ModelUtils.getAllUsedSchemas(openAPI);
43-
Assert.assertEquals(allUsedSchemas.size(), 12);
43+
Assert.assertEquals(allUsedSchemas.size(), 28);
4444

4545
Assert.assertTrue(allUsedSchemas.contains("SomeObjShared"), "contains 'SomeObjShared'");
4646
Assert.assertTrue(allUsedSchemas.contains("SomeObj1"), "contains 'UnusedObj1'");
@@ -54,6 +54,22 @@ public void testGetAllUsedSchemas() {
5454
Assert.assertTrue(allUsedSchemas.contains("SomeObj10A"), "contains 'SomeObj10A'");
5555
Assert.assertTrue(allUsedSchemas.contains("SomeObj10B"), "contains 'SomeObj10B'");
5656
Assert.assertTrue(allUsedSchemas.contains("SomeObj11"), "contains 'SomeObj11'");
57+
Assert.assertTrue(allUsedSchemas.contains("SomeArrayObj12"), "contains 'SomeArrayObj12'");
58+
Assert.assertTrue(allUsedSchemas.contains("ArrayItem12"), "contains 'ArrayItem12'");
59+
Assert.assertTrue(allUsedSchemas.contains("SomeArrayObj13"), "contains 'SomeArrayObj13'");
60+
Assert.assertTrue(allUsedSchemas.contains("ArrayItem13"), "contains 'ArrayItem13'");
61+
Assert.assertTrue(allUsedSchemas.contains("SomeObj14"), "contains 'SomeObj14'");
62+
Assert.assertTrue(allUsedSchemas.contains("PropertyObj14"), "contains 'PropertyObj14'");
63+
Assert.assertTrue(allUsedSchemas.contains("SomeObj15"), "contains 'SomeObj15'");
64+
Assert.assertTrue(allUsedSchemas.contains("SomeMapObj16"), "contains 'SomeMapObj16'");
65+
Assert.assertTrue(allUsedSchemas.contains("MapItem16"), "contains 'MapItem16'");
66+
Assert.assertTrue(allUsedSchemas.contains("SomeObj17"), "contains 'SomeObj17'");
67+
Assert.assertTrue(allUsedSchemas.contains("SomeObj18"), "contains 'SomeObj18'");
68+
Assert.assertTrue(allUsedSchemas.contains("Common18"), "contains 'Common18'");
69+
Assert.assertTrue(allUsedSchemas.contains("Obj19ByAge"), "contains 'Obj19ByAge'");
70+
Assert.assertTrue(allUsedSchemas.contains("Obj19ByType"), "contains 'Obj19ByType'");
71+
Assert.assertTrue(allUsedSchemas.contains("SomeObj20"), "contains 'SomeObj20'");
72+
Assert.assertTrue(allUsedSchemas.contains("OtherObj20"), "contains 'OtherObj20'");
5773
}
5874

5975
@Test

modules/openapi-generator/src/test/resources/3_0/unusedSchemas.yaml

Lines changed: 209 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,104 @@ paths:
135135
200:
136136
description: Successful Operation
137137
content: {}
138+
/some/p12:
139+
post:
140+
operationId: p12
141+
requestBody:
142+
content:
143+
application/json:
144+
schema:
145+
$ref: '#/components/schemas/SomeArrayObj12'
146+
responses:
147+
200:
148+
description: Successful Operation
149+
content: {}
150+
/some/p13:
151+
get:
152+
operationId: p13
153+
responses:
154+
200:
155+
description: Successful Operation
156+
content:
157+
application/json:
158+
schema:
159+
$ref: '#/components/schemas/SomeArrayObj13'
160+
/some/p14:
161+
get:
162+
operationId: p14
163+
responses:
164+
200:
165+
description: Successful Operation
166+
content:
167+
application/json:
168+
schema:
169+
$ref: '#/components/schemas/SomeObj14'
170+
/some/p15:
171+
get:
172+
operationId: p15
173+
responses:
174+
'200':
175+
description: OK
176+
content:
177+
text/plain:
178+
schema:
179+
$ref: '#/components/schemas/SomeObj15'
180+
/some/p16:
181+
get:
182+
operationId: p16
183+
responses:
184+
'200':
185+
description: OK
186+
content:
187+
text/plain:
188+
schema:
189+
$ref: '#/components/schemas/SomeMapObj16'
190+
/some/p17:
191+
get:
192+
operationId: p17
193+
responses:
194+
'200':
195+
description: OK
196+
content:
197+
text/plain:
198+
schema:
199+
oneOf:
200+
- type: string
201+
- $ref: '#/components/schemas/SomeObj17'
202+
/some/p18:
203+
get:
204+
operationId: p18
205+
requestBody:
206+
content:
207+
application/json:
208+
schema:
209+
$ref: '#/components/schemas/SomeObj18'
210+
responses:
211+
200:
212+
description: Successful Operation
213+
content: {}
214+
/some/p19:
215+
patch:
216+
requestBody:
217+
content:
218+
application/json:
219+
schema:
220+
anyOf:
221+
- $ref: '#/components/schemas/Obj19ByAge'
222+
- $ref: '#/components/schemas/Obj19ByType'
223+
responses:
224+
'200':
225+
description: Updated
226+
/some/p20:
227+
get:
228+
operationId: op20
229+
responses:
230+
'200':
231+
description: OK
232+
content:
233+
text/plain:
234+
schema:
235+
$ref: '#/components/schemas/SomeObj20'
138236
components:
139237
schemas:
140238
UnusedObj1:
@@ -254,6 +352,117 @@ components:
254352
- v1
255353
- v2
256354
default: v1
355+
SomeArrayObj12:
356+
type: array
357+
items:
358+
$ref: "#/components/schemas/ArrayItem12"
359+
ArrayItem12:
360+
type: object
361+
properties:
362+
prop1:
363+
type: string
364+
prop2:
365+
type: integer
366+
format: int32
367+
SomeArrayObj13:
368+
type: array
369+
items:
370+
type: array
371+
items:
372+
$ref: "#/components/schemas/ArrayItem13"
373+
ArrayItem13:
374+
type: object
375+
properties:
376+
prop1:
377+
type: string
378+
prop2:
379+
type: integer
380+
format: int64
381+
SomeObj14:
382+
type: object
383+
properties:
384+
obj14:
385+
$ref: "#/components/schemas/PropertyObj14"
386+
PropertyObj14:
387+
type: object
388+
properties:
389+
prop1:
390+
type: string
391+
SomeObj15:
392+
type: object
393+
properties:
394+
message:
395+
type: string
396+
details:
397+
type: array
398+
items:
399+
$ref: '#/components/schemas/SomeObj15'
400+
SomeMapObj16:
401+
type: array
402+
items:
403+
$ref: "#/components/schemas/MapItem16"
404+
MapItem16:
405+
type: object
406+
properties:
407+
prop1:
408+
type: integer
409+
format: int32
410+
prop2:
411+
type: integer
412+
format: int64
413+
SomeObj17:
414+
type: object
415+
properties:
416+
id:
417+
type: String
418+
message:
419+
type: String
420+
SomeObj18:
421+
allOf:
422+
- $ref: '#/components/schemas/Common18'
423+
- type: object
424+
properties:
425+
firstName:
426+
type: String
427+
lastName:
428+
type: String
429+
Common18:
430+
type: object
431+
properties:
432+
id:
433+
type: String
434+
active:
435+
type: boolean
436+
Obj19ByAge:
437+
type: object
438+
properties:
439+
age:
440+
type: integer
441+
firstName:
442+
type: String
443+
lastName:
444+
type: String
445+
required:
446+
- age
447+
Obj19ByType:
448+
type: object
449+
properties:
450+
sometype:
451+
type: string
452+
enum: [A, B, C]
453+
enabled:
454+
type: boolean
455+
required:
456+
- sometype
457+
SomeObj20:
458+
type: object
459+
properties:
460+
other:
461+
not:
462+
$ref: '#/components/schemas/OtherObj20'
463+
OtherObj20:
464+
type: string
465+
enum: [A, B, C]
257466
SomeObjShared:
258467
type: object
259468
properties:

0 commit comments

Comments
 (0)