Skip to content
This repository was archived by the owner on Nov 24, 2022. It is now read-only.

Commit 4bef0f7

Browse files
authored
Merge pull request swagger-api#1162 from scottr-ad/master
issue-1161 - ResolverFully does not resolve $ref in ComposedSchema with properties
2 parents 9a040f6 + 5534793 commit 4bef0f7

4 files changed

Lines changed: 188 additions & 187 deletions

File tree

modules/swagger-parser-v3/src/main/java/io/swagger/v3/parser/util/ResolverFully.java

Lines changed: 103 additions & 187 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,19 @@
11
package io.swagger.v3.parser.util;
22

3+
import static io.swagger.v3.parser.util.RefUtils.computeDefinitionName;
4+
import static io.swagger.v3.parser.util.RefUtils.computeRefFormat;
5+
import static io.swagger.v3.parser.util.RefUtils.isAnExternalRefFormat;
6+
7+
import java.util.ArrayList;
8+
import java.util.HashMap;
9+
import java.util.HashSet;
10+
import java.util.IdentityHashMap;
11+
import java.util.LinkedHashMap;
12+
import java.util.List;
13+
import java.util.Map;
14+
import java.util.Set;
15+
import java.util.stream.Collectors;
16+
317
import io.swagger.v3.oas.models.Components;
418
import io.swagger.v3.oas.models.OpenAPI;
519
import io.swagger.v3.oas.models.Operation;
@@ -22,19 +36,6 @@
2236
import io.swagger.v3.parser.models.RefFormat;
2337
import org.slf4j.Logger;
2438
import org.slf4j.LoggerFactory;
25-
import java.util.ArrayList;
26-
import java.util.HashMap;
27-
import java.util.HashSet;
28-
import java.util.IdentityHashMap;
29-
import java.util.LinkedHashMap;
30-
import java.util.List;
31-
import java.util.Map;
32-
import java.util.Set;
33-
import java.util.stream.Collectors;
34-
35-
import static io.swagger.v3.parser.util.RefUtils.computeDefinitionName;
36-
import static io.swagger.v3.parser.util.RefUtils.computeRefFormat;
37-
import static io.swagger.v3.parser.util.RefUtils.isAnExternalRefFormat;
3839

3940
public class ResolverFully {
4041
private static final Logger LOGGER = LoggerFactory.getLogger(ResolverFully.class);
@@ -325,14 +326,7 @@ public Schema resolveSchema(Schema schema) {
325326
Schema innerProperty = obj.getProperties().get(propertyName);
326327
// reference check
327328
if(schema != innerProperty) {
328-
if(resolvedProperties.get(propertyName) == null || resolvedProperties.get(propertyName) != innerProperty) {
329-
LOGGER.debug("avoiding infinite loop");
330-
Schema resolved = resolveSchema(innerProperty);
331-
updated.put(propertyName, resolved);
332-
resolvedProperties.put(propertyName, resolved);
333-
}else {
334-
updated.put(propertyName, resolvedProperties.get(propertyName));
335-
}
329+
updated.put(propertyName, resolveSchemaProperty(propertyName, innerProperty));
336330
}
337331
}
338332
obj.setProperties(updated);
@@ -341,191 +335,59 @@ public Schema resolveSchema(Schema schema) {
341335
}
342336

343337

344-
if(schema instanceof ComposedSchema) {
338+
Schema result = schema;
339+
340+
if (schema instanceof ComposedSchema) {
345341
ComposedSchema composedSchema = (ComposedSchema) schema;
346-
boolean adjacent = false;
347-
if (aggregateCombinators) {
348-
Schema model = SchemaTypeUtil.createSchema(composedSchema.getType(), composedSchema.getFormat());
349-
Set<String> requiredProperties = new HashSet<>();
350-
Set<Object> examples = new HashSet<>();
351342

352-
if ((composedSchema.getAllOf() != null && composedSchema.getAnyOf() != null && composedSchema.getOneOf() != null)||
353-
(composedSchema.getAllOf() != null && composedSchema.getAnyOf() != null) ||
354-
(composedSchema.getAllOf() != null && composedSchema.getOneOf() != null)||
355-
(composedSchema.getOneOf() != null && composedSchema.getAnyOf() != null)){
343+
boolean hasAllOf = composedSchema.getAllOf() != null;
344+
boolean hasAnyOf = composedSchema.getAnyOf() != null;
345+
boolean hasOneOf = composedSchema.getOneOf() != null;
346+
boolean adjacent = (hasAllOf && hasAnyOf) || (hasAllOf && hasOneOf) || (hasAnyOf && hasOneOf);
356347

357-
adjacent = true;
348+
if (aggregateCombinators && (hasAllOf || adjacent)) {
349+
Schema combinedModel = SchemaTypeUtil.createSchema(composedSchema.getType(), composedSchema.getFormat());
350+
Set<Object> examples = new HashSet<>();
358351

352+
if (hasAllOf) {
353+
aggregateSchemaCombinators(composedSchema, combinedModel, composedSchema.getAllOf(), examples);
359354
}
360-
361-
if (composedSchema.getAllOf() != null) {
362-
for (Schema innerModel : composedSchema.getAllOf()) {
363-
Schema resolved = resolveSchema(innerModel);
364-
Map<String, Schema> properties = resolved.getProperties();
365-
if (resolved.getProperties() != null) {
366-
for (String key : properties.keySet()) {
367-
Schema prop = (Schema) resolved.getProperties().get(key);
368-
if(resolvedProperties.get(key) == null || resolvedProperties.get(key) != prop) {
369-
LOGGER.debug("avoiding infinite loop");
370-
Schema resolvedProp = resolveSchema(prop);
371-
model.addProperties(key,resolvedProp );
372-
resolvedProperties.put(key, resolvedProp);
373-
}else {
374-
model.addProperties(key,resolvedProperties.get(key));
375-
}
376-
}
377-
if (resolved.getRequired() != null) {
378-
for (int i = 0; i < resolved.getRequired().size(); i++) {
379-
if (resolved.getRequired().get(i) != null) {
380-
requiredProperties.add(resolved.getRequired().get(i).toString());
381-
}
382-
}
383-
}
384-
}
385-
if (requiredProperties.size() > 0) {
386-
model.setRequired(new ArrayList<>(requiredProperties));
387-
}
388-
if (resolved.getExample() != null) {
389-
examples.add(resolved.getExample());
390-
}
391-
if (composedSchema.getExtensions() != null) {
392-
Map<String, Object> extensions = composedSchema.getExtensions();
393-
for (String key : extensions.keySet()) {
394-
model.addExtension(key, composedSchema.getExtensions().get(key));
395-
}
396-
}
397-
}
398-
399-
} if (composedSchema.getOneOf() != null) {
400-
if(adjacent == false) {
401-
Schema resolved;
402-
List<Schema> list = new ArrayList<>();
403-
for (Schema innerModel : composedSchema.getOneOf()) {
404-
resolved = resolveSchema(innerModel);
405-
list.add(resolved);
406-
}
407-
composedSchema.setOneOf(list);
408-
return composedSchema;
409-
}else {
410-
for (Schema innerModel : composedSchema.getOneOf()) {
411-
Schema resolved = resolveSchema(innerModel);
412-
Map<String, Schema> properties = resolved.getProperties();
413-
if (resolved.getProperties() != null) {
414-
for (String key : properties.keySet()) {
415-
Schema prop = (Schema) resolved.getProperties().get(key);
416-
if(resolvedProperties.get(key) == null || resolvedProperties.get(key) != prop) {
417-
LOGGER.debug("avoiding infinite loop");
418-
Schema resolvedProp = resolveSchema(prop);
419-
model.addProperties(key,resolvedProp );
420-
resolvedProperties.put(key, resolvedProp);
421-
}else {
422-
model.addProperties(key,resolvedProperties.get(key));
423-
}
424-
}
425-
if (resolved.getRequired() != null) {
426-
for (int i = 0; i < resolved.getRequired().size(); i++) {
427-
if (resolved.getRequired().get(i) != null) {
428-
requiredProperties.add(resolved.getRequired().get(i).toString());
429-
}
430-
}
431-
}
432-
}
433-
if (requiredProperties.size() > 0) {
434-
model.setRequired(new ArrayList<>(requiredProperties));
435-
}
436-
if (resolved.getExample() != null) {
437-
examples.add(resolved.getExample());
438-
}
439-
if (composedSchema.getExtensions() != null) {
440-
Map<String, Object> extensions = composedSchema.getExtensions();
441-
for (String key : extensions.keySet()) {
442-
model.addExtension(key, composedSchema.getExtensions().get(key));
443-
}
444-
}
445-
}
446-
}
447-
448-
} if (composedSchema.getAnyOf() != null) {
449-
if(adjacent == false) {
450-
Schema resolved;
451-
List<Schema> list = new ArrayList<>();
452-
for (Schema innerModel : composedSchema.getAnyOf()) {
453-
resolved = resolveSchema(innerModel);
454-
list.add(resolved);
455-
}
456-
composedSchema.setAnyOf(list);
457-
return composedSchema;
458-
}else {
459-
for (Schema innerModel : composedSchema.getAnyOf()) {
460-
Schema resolved = resolveSchema(innerModel);
461-
Map<String, Schema> properties = resolved.getProperties();
462-
if (resolved.getProperties() != null) {
463-
for (String key : properties.keySet()) {
464-
Schema prop = (Schema) resolved.getProperties().get(key);
465-
if(resolvedProperties.get(key) == null || resolvedProperties.get(key) != prop) {
466-
LOGGER.debug("avoiding infinite loop");
467-
Schema resolvedProp = resolveSchema(prop);
468-
model.addProperties(key,resolvedProp );
469-
resolvedProperties.put(key, resolvedProp);
470-
}else {
471-
model.addProperties(key,resolvedProperties.get(key));
472-
}
473-
}
474-
if (resolved.getRequired() != null) {
475-
for (int i = 0; i < resolved.getRequired().size(); i++) {
476-
if (resolved.getRequired().get(i) != null) {
477-
requiredProperties.add(resolved.getRequired().get(i).toString());
478-
}
479-
}
480-
}
481-
}
482-
if (requiredProperties.size() > 0) {
483-
model.setRequired(new ArrayList<>(requiredProperties));
484-
}
485-
if (resolved.getExample() != null) {
486-
examples.add(resolved.getExample());
487-
}
488-
if (composedSchema.getExtensions() != null) {
489-
Map<String, Object> extensions = composedSchema.getExtensions();
490-
for (String key : extensions.keySet()) {
491-
model.addExtension(key, composedSchema.getExtensions().get(key));
492-
}
493-
}
494-
}
495-
}
355+
if (hasOneOf) {
356+
aggregateSchemaCombinators(composedSchema, combinedModel, composedSchema.getOneOf(), examples);
357+
}
358+
if (hasAnyOf) {
359+
aggregateSchemaCombinators(composedSchema, combinedModel, composedSchema.getAnyOf(), examples);
496360
}
361+
497362
if (schema.getExample() != null) {
498-
model.setExample(schema.getExample());
363+
combinedModel.setExample(schema.getExample());
499364
} else if (!examples.isEmpty()) {
500-
model.setExample(examples);
365+
combinedModel.setExample(examples);
501366
}
502-
return model;
367+
368+
result = combinedModel;
369+
503370
} else {
504-
// User don't want to aggregate composed schema, we only solve refs
505-
if (composedSchema.getAllOf() != null)
371+
// User doesn't need or want to aggregate composed schema, we only solve refs
372+
if (hasAllOf) {
506373
composedSchema.allOf(composedSchema.getAllOf().stream().map(this::resolveSchema).collect(Collectors.toList()));
507-
if (composedSchema.getOneOf() != null)
374+
}
375+
if (hasOneOf) {
508376
composedSchema.oneOf(composedSchema.getOneOf().stream().map(this::resolveSchema).collect(Collectors.toList()));
509-
if (composedSchema.getAnyOf() != null)
377+
}
378+
if (hasAnyOf) {
510379
composedSchema.anyOf(composedSchema.getAnyOf().stream().map(this::resolveSchema).collect(Collectors.toList()));
511-
return composedSchema;
380+
}
512381
}
513382
}
514383

515-
if (schema.getProperties() != null) {
516-
Schema model = schema;
384+
if (result.getProperties() != null) {
385+
Schema model = result;
517386
Map<String, Schema> updated = new LinkedHashMap<>();
518387
Map<String, Schema> properties = model.getProperties();
519388
for (String propertyName : properties.keySet()) {
520389
Schema property = (Schema) model.getProperties().get(propertyName);
521-
if(resolvedProperties.get(propertyName) == null || resolvedProperties.get(propertyName) != property) {
522-
LOGGER.debug("avoiding infinite loop");
523-
Schema resolved = resolveSchema(property);
524-
updated.put(propertyName, resolved);
525-
resolvedProperties.put(propertyName, resolved);
526-
}else {
527-
updated.put(propertyName, resolvedProperties.get(propertyName));
528-
}
390+
updated.put(propertyName, resolveSchemaProperty(propertyName, property));
529391
}
530392

531393
for (String key : updated.keySet()) {
@@ -549,7 +411,7 @@ public Schema resolveSchema(Schema schema) {
549411
return model;
550412
}
551413

552-
return schema;
414+
return result;
553415
}
554416

555417
public Map<String,Example> resolveExample(Map<String,Example> examples){
@@ -571,4 +433,58 @@ public Map<String,Example> resolveExample(Map<String,Example> examples){
571433
return resolveExamples;
572434

573435
}
436+
437+
private void aggregateSchemaCombinators(ComposedSchema sourceSchema, Schema targetSchema,
438+
List<Schema> schemasToAggregate, Set<Object> examples) {
439+
440+
Set<String> requiredProperties = new HashSet<>();
441+
442+
for (Schema innerModel : schemasToAggregate) {
443+
Schema resolved = resolveSchema(innerModel);
444+
Map<String, Schema> properties = resolved.getProperties();
445+
if (resolved.getProperties() != null) {
446+
for (String key : properties.keySet()) {
447+
Schema prop = (Schema) resolved.getProperties().get(key);
448+
targetSchema.addProperties(key, resolveSchemaProperty(key, prop));
449+
}
450+
451+
if (resolved.getRequired() != null) {
452+
for (Object required : resolved.getRequired()) {
453+
if (required != null) {
454+
requiredProperties.add(required.toString());
455+
}
456+
}
457+
}
458+
}
459+
if (resolved.getExample() != null) {
460+
examples.add(resolved.getExample());
461+
}
462+
if (sourceSchema.getExtensions() != null) {
463+
Map<String, Object> extensions = sourceSchema.getExtensions();
464+
for (String key : extensions.keySet()) {
465+
targetSchema.addExtension(key, sourceSchema.getExtensions().get(key));
466+
}
467+
}
468+
}
469+
470+
if (requiredProperties.size() > 0) {
471+
List<String> required = new ArrayList<>();
472+
if (targetSchema.getRequired() != null) {
473+
required.addAll(targetSchema.getRequired());
474+
}
475+
required.addAll(requiredProperties);
476+
targetSchema.setRequired(required);
477+
}
478+
}
479+
480+
private Schema resolveSchemaProperty(String propertyName, Schema innerProperty) {
481+
if (resolvedProperties.get(propertyName) == null || resolvedProperties.get(propertyName) != innerProperty) {
482+
LOGGER.debug("avoiding infinite loop");
483+
Schema resolved = resolveSchema(innerProperty);
484+
resolvedProperties.put(propertyName, resolved);
485+
return resolved;
486+
} else {
487+
return resolvedProperties.get(propertyName);
488+
}
489+
}
574490
}

modules/swagger-parser-v3/src/test/java/io/swagger/v3/parser/test/OpenAPIResolverTest.java

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -490,6 +490,33 @@ public void testIssue85(@Injectable final List<AuthorizationValue> auths) {
490490
assertTrue(prop instanceof Schema);
491491
}
492492

493+
@Test
494+
public void testIssue1161(@Injectable final List<AuthorizationValue> auths) {
495+
String path = "/issue-1161/swagger.yaml";
496+
497+
ParseOptions options = new ParseOptions();
498+
options.setResolve(true);
499+
options.setResolveFully(true);
500+
501+
OpenAPI openAPI = new OpenAPIV3Parser().readLocation(path, auths, options).getOpenAPI();
502+
ResolverFully resolverUtil = new ResolverFully();
503+
resolverUtil.resolveFully(openAPI);
504+
505+
Schema petsSchema = openAPI.getComponents().getSchemas().get("Pets");
506+
Schema colouringsSchema = openAPI.getComponents().getSchemas().get("Colouring");
507+
508+
assertNotNull(petsSchema);
509+
assertNotNull(colouringsSchema);
510+
511+
assertTrue(petsSchema instanceof ComposedSchema);
512+
assertTrue(petsSchema.getProperties() != null);
513+
assertTrue(((ComposedSchema) petsSchema).getOneOf() != null);
514+
515+
Schema petsColouringProperty = (Schema) petsSchema.getProperties().get("colouring");
516+
assertTrue(petsColouringProperty.get$ref() == null);
517+
assertTrue(petsColouringProperty == colouringsSchema);
518+
}
519+
493520
@Test
494521
public void selfReferenceTest(@Injectable final List<AuthorizationValue> auths) {
495522
String yaml = "" +

0 commit comments

Comments
 (0)