Skip to content

Commit

Permalink
OAS 3.1 - properties and ref as siblings
Browse files Browse the repository at this point in the history
  • Loading branch information
frantuma committed Jun 23, 2023
1 parent 2676db0 commit b0ce133
Show file tree
Hide file tree
Showing 12 changed files with 280 additions and 39 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@
import java.util.concurrent.CopyOnWriteArrayList;

public class ModelConverters {
private static final ModelConverters SINGLETON = new ModelConverters();
private static ModelConverters SINGLETON = null;
private static ModelConverters SINGLETON31 = null;
static Logger LOGGER = LoggerFactory.getLogger(ModelConverters.class);
private final List<ModelConverter> converters;
private final Set<String> skippedPackages = new HashSet<>();
Expand All @@ -41,10 +42,42 @@ public ModelConverters(boolean openapi31) {
}
}

public static ModelConverters getInstance() {
public static ModelConverters getInstance(boolean openapi31) {
if (openapi31) {
if (SINGLETON31 == null) {
SINGLETON31 = new ModelConverters(openapi31);
init(SINGLETON31);
}
return SINGLETON31;
}
if (SINGLETON == null) {
SINGLETON = new ModelConverters(openapi31);
init(SINGLETON);
}
return SINGLETON;
}

private static void init(ModelConverters converter) {
converter.skippedPackages.add("java.lang");

ServiceLoader<ModelConverter> loader = ServiceLoader.load(ModelConverter.class);
Iterator<ModelConverter> itr = loader.iterator();
while (itr.hasNext()) {
ModelConverter ext = itr.next();
if (ext == null) {
LOGGER.error("failed to load extension {}", ext);
} else {
converter.addConverter(ext);
LOGGER.debug("adding ModelConverter: {}", ext);
}
}

}
public static ModelConverters getInstance() {
return getInstance(false);
}


public void addConverter(ModelConverter converter) {
converters.add(0, converter);
}
Expand Down Expand Up @@ -140,20 +173,4 @@ private boolean shouldProcess(Type type) {
}
return !skippedClasses.contains(className);
}

static {
SINGLETON.skippedPackages.add("java.lang");

ServiceLoader<ModelConverter> loader = ServiceLoader.load(ModelConverter.class);
Iterator<ModelConverter> itr = loader.iterator();
while (itr.hasNext()) {
ModelConverter ext = itr.next();
if (ext == null) {
LOGGER.error("failed to load extension {}", ext);
} else {
SINGLETON.addConverter(ext);
LOGGER.debug("adding ModelConverter: {}", ext);
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
import io.swagger.v3.oas.models.media.ComposedSchema;
import io.swagger.v3.oas.models.media.Discriminator;
import io.swagger.v3.oas.models.media.IntegerSchema;
import io.swagger.v3.oas.models.media.JsonSchema;
import io.swagger.v3.oas.models.media.MapSchema;
import io.swagger.v3.oas.models.media.NumberSchema;
import io.swagger.v3.oas.models.media.ObjectSchema;
Expand Down Expand Up @@ -205,11 +206,15 @@ public Schema resolve(AnnotatedType annotatedType, ModelConverterContext context

name = decorateModelName(annotatedType, name);

// if we have a ref we don't consider anything else
// if we have a ref, for OAS 3.0 we don't consider anything else, while for OAS 3.1 we store the ref and add it later
String schemaRefFromAnnotation = null;
if (resolvedSchemaAnnotation != null &&
StringUtils.isNotEmpty(resolvedSchemaAnnotation.ref())) {
if (resolvedArrayAnnotation == null) {
return new Schema().$ref(resolvedSchemaAnnotation.ref()).name(name);
schemaRefFromAnnotation = resolvedSchemaAnnotation.ref();
if (!openapi31) {
return new JsonSchema().$ref(resolvedSchemaAnnotation.ref()).name(name);
}
} else {
ArraySchema schema = new ArraySchema();
resolveArraySchema(annotatedType, schema, resolvedArrayAnnotation);
Expand Down Expand Up @@ -336,7 +341,11 @@ public Schema resolve(AnnotatedType annotatedType, ModelConverterContext context
}

if ("Object".equals(name)) {
return new Schema();
Schema schema = new Schema();
if (schemaRefFromAnnotation != null) {
schema.raw$ref(schemaRefFromAnnotation);
}
return schema;
}

List<Class<?>> composedSchemaReferencedClasses = getComposedSchemaReferencedClasses(type.getRawClass(), annotatedType.getCtxAnnotations(), resolvedSchemaAnnotation);
Expand All @@ -362,6 +371,9 @@ public Schema resolve(AnnotatedType annotatedType, ModelConverterContext context
model = new Schema().$ref(Components.COMPONENTS_SCHEMAS_REF + name);
}
if (!isComposedSchema) {
if (schemaRefFromAnnotation != null && model != null) {
model.raw$ref(schemaRefFromAnnotation);
}
return model;
}
}
Expand Down Expand Up @@ -712,7 +724,13 @@ public Schema resolve(AnnotatedType annotatedType, ModelConverterContext context
property = new Schema().$ref(constructRef(pName));
}
} else if (property.get$ref() != null) {
property = new Schema().$ref(StringUtils.isNotEmpty(property.get$ref()) ? property.get$ref() : property.getName());
if (!openapi31) {
property = new Schema().$ref(StringUtils.isNotEmpty(property.get$ref()) ? property.get$ref() : property.getName());
} else {
if (StringUtils.isEmpty(property.get$ref())) {
property.$ref(property.getName());
}
}
}
}
property.setName(propName);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -795,12 +795,7 @@ public static Schema resolveSchemaFromType(Class<?> schemaImplementation, Compon
schemaObject = primitiveType.createProperty();
} else {
schemaObject = new Schema();
ResolvedSchema resolvedSchema;
if (openapi31) {
resolvedSchema = new ModelConverters(true).readAllAsResolvedSchema(new AnnotatedType().type(schemaImplementation).jsonViewAnnotation(jsonViewAnnotation));
} else {
resolvedSchema = ModelConverters.getInstance().readAllAsResolvedSchema(new AnnotatedType().type(schemaImplementation).jsonViewAnnotation(jsonViewAnnotation));
}
ResolvedSchema resolvedSchema = ModelConverters.getInstance(openapi31).readAllAsResolvedSchema(new AnnotatedType().type(schemaImplementation).jsonViewAnnotation(jsonViewAnnotation));
Map<String, Schema> schemaMap;
if (resolvedSchema != null) {
schemaMap = resolvedSchema.referencedSchemas;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,12 +66,7 @@ public static Parameter applyAnnotations(
.jsonViewAnnotation(jsonViewAnnotation)
.ctxAnnotations(reworkedAnnotations.toArray(new Annotation[reworkedAnnotations.size()]));

final ResolvedSchema resolvedSchema;
if (openapi31) {
resolvedSchema = new ModelConverters(true).resolveAsResolvedSchema(annotatedType);
} else {
resolvedSchema = ModelConverters.getInstance().resolveAsResolvedSchema(annotatedType);
}
final ResolvedSchema resolvedSchema = ModelConverters.getInstance(openapi31).resolveAsResolvedSchema(annotatedType);

if (resolvedSchema.schema != null) {
parameter.setSchema(resolvedSchema.schema);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -524,7 +524,7 @@ public T init() throws OpenApiConfigurationException {
if (objectMapperProcessor != null) {
ObjectMapper mapper = IntegrationObjectMapperFactory.createJson();
objectMapperProcessor.processJsonObjectMapper(mapper);
ModelConverters.getInstance().addConverter(new ModelResolver(mapper));
ModelConverters.getInstance(Boolean.TRUE.equals(openApiConfiguration.isOpenAPI31())).addConverter(new ModelResolver(mapper));

objectMapperProcessor.processOutputJsonObjectMapper(outputJsonMapper);
objectMapperProcessor.processOutputYamlObjectMapper(outputYamlMapper);
Expand All @@ -537,7 +537,7 @@ public T init() throws OpenApiConfigurationException {
try {
if (modelConverters != null && !modelConverters.isEmpty()) {
for (ModelConverter converter: modelConverters) {
ModelConverters.getInstance().addConverter(converter);
ModelConverters.getInstance(Boolean.TRUE.equals(openApiConfiguration.isOpenAPI31())).addConverter(converter);
}
}
} catch (Exception e) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ public class SwaggerConfiguration implements OpenAPIConfiguration {

private Boolean alwaysResolveAppPath;

private Boolean openAPI31;
private Boolean openAPI31 = false;

private Boolean convertToOpenAPI31;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ public Reader() {
paths = new Paths();
openApiTags = new LinkedHashSet<>();
components = new Components();
setConfiguration(new SwaggerConfiguration().openAPI(openAPI));

}

Expand Down Expand Up @@ -1120,7 +1121,7 @@ protected Operation parseMethod(
final Class<?> subResource = getSubResourceWithJaxRsSubresourceLocatorSpecs(method);
Schema returnTypeSchema = null;
if (!shouldIgnoreClass(returnType.getTypeName()) && !method.getGenericReturnType().equals(subResource)) {
ResolvedSchema resolvedSchema = ModelConverters.getInstance().resolveAsResolvedSchema(new AnnotatedType(returnType).resolveAsRef(true).jsonViewAnnotation(jsonViewAnnotation));
ResolvedSchema resolvedSchema = ModelConverters.getInstance(config.isOpenAPI31()).resolveAsResolvedSchema(new AnnotatedType(returnType).resolveAsRef(true).jsonViewAnnotation(jsonViewAnnotation));
if (resolvedSchema.schema != null) {
returnTypeSchema = resolvedSchema.schema;
Content content = new Content();
Expand Down Expand Up @@ -1231,7 +1232,7 @@ private boolean shouldIgnoreClass(String className) {
}
ignore = rawClassName.startsWith("javax.ws.rs.");
ignore = ignore || rawClassName.equalsIgnoreCase("void");
ignore = ignore || ModelConverters.getInstance().isRegisteredAsSkippedClass(rawClassName);
ignore = ignore || ModelConverters.getInstance(config.isOpenAPI31()).isRegisteredAsSkippedClass(rawClassName);
return ignore;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,10 @@
import io.swagger.v3.core.jackson.ModelResolver;
import io.swagger.v3.core.model.ApiDescription;
import io.swagger.v3.core.util.PrimitiveType;
import io.swagger.v3.core.util.Yaml;
import io.swagger.v3.jaxrs2.matchers.SerializationMatchers;
import io.swagger.v3.jaxrs2.petstore31.PetResource;
import io.swagger.v3.jaxrs2.petstore31.TagResource;
import io.swagger.v3.jaxrs2.resources.ResponseReturnTypeResource;
import io.swagger.v3.jaxrs2.resources.SchemaPropertiesResource;
import io.swagger.v3.jaxrs2.resources.SingleExampleResource;
Expand Down Expand Up @@ -3273,6 +3275,22 @@ public void testOas31Petstore() {
" description: Pet not found\n" +
"components:\n" +
" schemas:\n" +
" Bar:\n" +
" deprecated: true\n" +
" description: Bar\n" +
" properties:\n" +
" foo:\n" +
" type: string\n" +
" const: bar\n" +
" bar:\n" +
" type: integer\n" +
" format: int32\n" +
" exclusiveMaximum: 4\n" +
" foobar:\n" +
" type:\n" +
" - integer\n" +
" - string\n" +
" format: int32\n" +
" Category:\n" +
" properties:\n" +
" id:\n" +
Expand All @@ -3282,6 +3300,23 @@ public void testOas31Petstore() {
" type: string\n" +
" xml:\n" +
" name: Category\n" +
" Foo:\n" +
" deprecated: true\n" +
" description: Foo\n" +
" properties:\n" +
" foo:\n" +
" type: string\n" +
" const: foo\n" +
" bar:\n" +
" type: integer\n" +
" format: int32\n" +
" exclusiveMaximum: 2\n" +
" foobar:\n" +
" type:\n" +
" - integer\n" +
" - string\n" +
" - object\n" +
" format: int32\n" +
" IfSchema:\n" +
" deprecated: true\n" +
" description: if schema\n" +
Expand Down Expand Up @@ -3354,8 +3389,109 @@ public void testOas31Petstore() {
" format: int64\n" +
" name:\n" +
" type: string\n" +
" annotated:\n" +
" $ref: '#/components/schemas/Category'\n" +
" description: child description\n" +
" properties:\n" +
" bar:\n" +
" deprecated: true\n" +
" description: Bar\n" +
" properties:\n" +
" foo:\n" +
" type: string\n" +
" const: bar\n" +
" bar:\n" +
" type: integer\n" +
" format: int32\n" +
" exclusiveMaximum: 4\n" +
" foobar:\n" +
" type:\n" +
" - integer\n" +
" - string\n" +
" format: int32\n" +
" foo:\n" +
" deprecated: true\n" +
" description: Foo\n" +
" properties:\n" +
" foo:\n" +
" type: string\n" +
" const: foo\n" +
" bar:\n" +
" type: integer\n" +
" format: int32\n" +
" exclusiveMaximum: 2\n" +
" foobar:\n" +
" type:\n" +
" - integer\n" +
" - string\n" +
" - object\n" +
" format: int32\n" +
" xml:\n" +
" name: Tag\n";
SerializationMatchers.assertEqualsToYaml31(openAPI, yaml);
}

@Test
public void test31RefSiblings() {
SwaggerConfiguration config = new SwaggerConfiguration().openAPI31(true).openAPI(new OpenAPI());
Reader reader = new Reader(config);

OpenAPI openAPI = reader.read(TagResource.class);
String yaml = "openapi: 3.1.0\n" +
"paths:\n" +
" /tag/tag:\n" +
" get:\n" +
" operationId: getTag\n" +
" responses:\n" +
" default:\n" +
" description: default response\n" +
" content:\n" +
" '*/*':\n" +
" schema:\n" +
" $ref: '#/components/schemas/SimpleTag'\n" +
"components:\n" +
" schemas:\n" +
" Foo:\n" +
" deprecated: true\n" +
" description: Foo\n" +
" properties:\n" +
" foo:\n" +
" type: string\n" +
" const: foo\n" +
" bar:\n" +
" type: integer\n" +
" format: int32\n" +
" exclusiveMaximum: 2\n" +
" foobar:\n" +
" type:\n" +
" - integer\n" +
" - string\n" +
" - object\n" +
" format: int32\n" +
" SimpleTag:\n" +
" properties:\n" +
" annotated:\n" +
" $ref: '#/components/schemas/SimpleCategory'\n" +
" description: child description\n" +
" properties:\n" +
" foo:\n" +
" deprecated: true\n" +
" description: Foo\n" +
" properties:\n" +
" foo:\n" +
" type: string\n" +
" const: foo\n" +
" bar:\n" +
" type: integer\n" +
" format: int32\n" +
" exclusiveMaximum: 2\n" +
" foobar:\n" +
" type:\n" +
" - integer\n" +
" - string\n" +
" - object\n" +
" format: int32\n" +
" SimpleCategory: {}\n";
SerializationMatchers.assertEqualsToYaml31(openAPI, yaml);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
package io.swagger.v3.jaxrs2.petstore31;
public class SimpleCategory {}
Loading

0 comments on commit b0ce133

Please sign in to comment.