Skip to content

Commit

Permalink
Improved schema building
Browse files Browse the repository at this point in the history
  • Loading branch information
altro3 committed May 9, 2023
1 parent 0274891 commit 48a4288
Show file tree
Hide file tree
Showing 35 changed files with 1,015 additions and 629 deletions.
1 change: 1 addition & 0 deletions config/checkstyle/custom-suppressions.xml
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,5 @@
<suppressions>
<suppress checks="." files="io[\\/]micronaut[\\/]openapi[\\/]swagger[\\/]" />
<suppress checks="FileLength" files=".*" />
<suppress checks="ParameterNumber" files=".*" />
</suppressions>

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
import io.micronaut.context.annotation.Requires;
import io.micronaut.context.env.Environment;
import io.micronaut.core.annotation.AnnotationValue;
import io.micronaut.core.util.ArrayUtils;
import io.micronaut.core.util.CollectionUtils;
import io.micronaut.core.util.StringUtils;
import io.micronaut.http.HttpMethod;
Expand Down Expand Up @@ -178,11 +179,13 @@ public int getOrder() {

private List<MediaType> mediaTypes(MethodElement element, Class<? extends Annotation> ann) {
String[] values = element.stringValues(ann);
if (values.length == 0) {
if (ArrayUtils.isEmpty(values)) {
return DEFAULT_MEDIA_TYPES;
} else {
return Arrays.stream(values).map(MediaType::of).distinct().collect(Collectors.toList());
}
return Arrays.stream(values)
.map(MediaType::of)
.distinct()
.collect(Collectors.toList());
}

@Override
Expand All @@ -201,18 +204,17 @@ protected List<UriMatchTemplate> uriMatchTemplates(MethodElement element, Visito
UriMatchTemplate matchTemplate = UriMatchTemplate.of(controllerValue);
// check if we have multiple uris
String[] uris = element.stringValues(HttpMethodMapping.class, "uris");
if (uris.length == 0) {
if (ArrayUtils.isEmpty(uris)) {
String methodValue = element.getValue(HttpMethodMapping.class, String.class).orElse("/");
methodValue = OpenApiApplicationVisitor.replacePlaceholders(methodValue, context);
return Collections.singletonList(matchTemplate.nest(methodValue));
} else {
List<UriMatchTemplate> matchTemplates = new ArrayList<>(uris.length);
for (String methodValue : uris) {
methodValue = OpenApiApplicationVisitor.replacePlaceholders(methodValue, context);
matchTemplates.add(matchTemplate.nest(methodValue));
}
return matchTemplates;
}
List<UriMatchTemplate> matchTemplates = new ArrayList<>(uris.length);
for (String methodValue : uris) {
methodValue = OpenApiApplicationVisitor.replacePlaceholders(methodValue, context);
matchTemplates.add(matchTemplate.nest(methodValue));
}
return matchTemplates;
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import io.swagger.v3.oas.models.Components;
import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.media.ArraySchema;
import io.swagger.v3.oas.models.media.ComposedSchema;
import io.swagger.v3.oas.models.media.Schema;

import static io.micronaut.openapi.visitor.Utils.resolveComponents;
Expand All @@ -33,6 +34,9 @@
*/
public final class SchemaUtils {

public static final Schema<?> EMPTY_SCHEMA = new Schema<>();
public static final Schema<?> EMPTY_SIMPLE_SCHEMA = new SimpleSchema();
public static final Schema<?> EMPTY_COMPOSED_SCHEMA = new ComposedSchema();
public static final String TYPE_OBJECT = "object";

private SchemaUtils() {
Expand Down
15 changes: 2 additions & 13 deletions openapi/src/main/java/io/micronaut/openapi/visitor/Utils.java
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,6 @@ public final class Utils {

private static PropertyPlaceholderResolver propertyPlaceholderResolver;
private static OpenAPI testReference;
private static OpenAPI testReferenceAfterPlaceholders;
private static String testFileName;
private static String testYamlReference;
private static String testJsonReference;
Expand Down Expand Up @@ -178,14 +177,11 @@ public static Components resolveComponents(OpenAPI openAPI) {
*
* @return The {@link OpenAPI} instance
*/
public static OpenAPI resolveOpenAPI(VisitorContext context) {
public static OpenAPI resolveOpenApi(VisitorContext context) {
OpenAPI openAPI = context.get(ATTR_OPENAPI, OpenAPI.class).orElse(null);
if (openAPI == null) {
openAPI = new OpenAPI();
context.put(ATTR_OPENAPI, openAPI);
if (isTestMode()) {
setTestReference(openAPI);
}
}
return openAPI;
}
Expand All @@ -194,6 +190,7 @@ public static OpenAPI resolveOpenAPI(VisitorContext context) {
* Return stacktrace for throwable and message.
*
* @param t throwable
*
* @return stacktrace
*/
public static String printStackTrace(Throwable t) {
Expand All @@ -216,14 +213,6 @@ public static void setTestReference(OpenAPI testReference) {
Utils.testReference = testReference;
}

public static OpenAPI getTestReferenceAfterPlaceholders() {
return testReferenceAfterPlaceholders;
}

public static void setTestReferenceAfterPlaceholders(OpenAPI testReferenceAfterPlaceholders) {
Utils.testReferenceAfterPlaceholders = testReferenceAfterPlaceholders;
}

public static String getTestYamlReference() {
return testYamlReference;
}
Expand Down
13 changes: 4 additions & 9 deletions openapi/src/test/groovy/io/micronaut/openapi/MyJaxbElement4.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
import javax.validation.constraints.Pattern;
import javax.validation.constraints.Size;

import io.swagger.v3.oas.annotations.Hidden;
import io.swagger.v3.oas.annotations.media.Schema;

public class MyJaxbElement4 {
Expand All @@ -18,11 +17,7 @@ public class MyJaxbElement4 {
* Discount data
*/
@Schema(oneOf = {DiscountSizeOpenApi.class, DiscountFixedOpenApi.class, MultiplierSizeOpenApi.class})
public Discount value;

@Hidden
public interface Discount {
}
public Object value;

/**
* Discout type
Expand All @@ -37,7 +32,7 @@ public enum DiscountTypeType {
/**
* Discount size
*/
public static class DiscountSizeOpenApi implements Discount {
public static class DiscountSizeOpenApi {

/**
* Value description
Expand All @@ -57,7 +52,7 @@ public static class DiscountSizeOpenApi implements Discount {
/**
* Discount fixed
*/
public static class DiscountFixedOpenApi implements Discount {
public static class DiscountFixedOpenApi {

/**
* Value description
Expand All @@ -76,7 +71,7 @@ public static class DiscountFixedOpenApi implements Discount {
/**
* Multiplier size
*/
public static class MultiplierSizeOpenApi implements Discount {
public static class MultiplierSizeOpenApi {

/**
* Value description
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -698,11 +698,6 @@ import io.swagger.v3.oas.annotations.security.SecurityScheme;
scopes = @OAuthScope(name = "write:pets", description = "modify pets in your account"))),
description = "ssssss"
)
@SecurityScheme(
name = "schemeWithRef",
type = SecuritySchemeType.DEFAULT,
ref = "#/components/securitySchemes/foo"
)
class Application {
}
Expand Down Expand Up @@ -775,24 +770,12 @@ class MyBean {}
oauth2.flows
oauth2.flows.implicit
oauth2.scheme == null

def withRef = openAPI.components.securitySchemes['schemeWithRef']
withRef.type == null
withRef.in == null
withRef.name == null
withRef.description == null
withRef.openIdConnectUrl == null
withRef.bearerFormat == null
withRef.flows == null
withRef.scheme == null
withRef.$ref == '#/components/securitySchemes/foo'
}

void "test disable openapi"() {

given: "An API definition"
Utils.testReference = null
Utils.testReferenceAfterPlaceholders = null
System.setProperty(OpenApiApplicationVisitor.MICRONAUT_OPENAPI_ENABLED, "false")

when:
Expand Down Expand Up @@ -851,7 +834,6 @@ class MyBean {}
''')
then: "the state is correct"
!Utils.testReference
!Utils.testReferenceAfterPlaceholders

cleanup:
System.clearProperty(OpenApiApplicationVisitor.MICRONAUT_OPENAPI_FIELD_VISIBILITY_LEVEL)
Expand All @@ -862,7 +844,6 @@ class MyBean {}

given: "An API definition"
Utils.testReference = null
Utils.testReferenceAfterPlaceholders = null
System.setProperty(OpenApiApplicationVisitor.MICRONAUT_CONFIG_FILE_LOCATIONS, "project:/src/test/resources/")
System.setProperty(Environment.ENVIRONMENTS_PROPERTY, "disabled-openapi")

Expand Down Expand Up @@ -918,7 +899,6 @@ class MyBean {}
''')
then: "the state is correct"
!Utils.testReference
!Utils.testReferenceAfterPlaceholders

cleanup:
System.clearProperty(OpenApiApplicationVisitor.MICRONAUT_OPENAPI_FIELD_VISIBILITY_LEVEL)
Expand All @@ -932,7 +912,6 @@ class MyBean {}

given: "An API definition"
Utils.testReference = null
Utils.testReferenceAfterPlaceholders = null
System.setProperty(OpenApiApplicationVisitor.MICRONAUT_OPENAPI_CONFIG_FILE, "openapi-disabled-openapi.properties")

when:
Expand Down Expand Up @@ -987,7 +966,6 @@ class MyBean {}
''')
then: "the state is correct"
!Utils.testReference
!Utils.testReferenceAfterPlaceholders

cleanup:
System.clearProperty(OpenApiApplicationVisitor.MICRONAUT_OPENAPI_CONFIG_FILE)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,49 +72,49 @@ class MyBean {}

OpenAPI openAPI = Utils.testReference
Operation operation = openAPI.paths?.get("/")?.get
def petSchema = openAPI.components.schemas['Pets'];

expect:
operation
operation.responses.size() == 1
openAPI.components.schemas['Pets'].description == 'Pets'
openAPI.components.schemas['Pets'].properties['pets'].nullable == false
openAPI.components.schemas['Pets'].properties['pets'].description == 'a list of Pets'
openAPI.components.schemas['Pets'].properties['pets'].minItems == 2
openAPI.components.schemas['Pets'].properties['pets'].items.$ref == '#/components/schemas/Pet'
openAPI.components.schemas['Pets'].properties['pets'].items.description == 'Pet'
openAPI.components.schemas['Pets'].properties['pets'].items.nullable == null

openAPI.components.schemas['Pets'].properties['ids'].nullable == false
openAPI.components.schemas['Pets'].properties['ids'].description == 'a list of Ids'
openAPI.components.schemas['Pets'].properties['ids'].minItems == 2
openAPI.components.schemas['Pets'].properties['ids'].items.format == 'int64'
openAPI.components.schemas['Pets'].properties['ids'].items.description == 'Yes'
openAPI.components.schemas['Pets'].properties['ids'].items.nullable == true

openAPI.components.schemas['Pets'].properties['primitiveIds'].nullable == false
openAPI.components.schemas['Pets'].properties['primitiveIds'].description == 'a list of primitive Ids'
openAPI.components.schemas['Pets'].properties['primitiveIds'].minItems == 2
openAPI.components.schemas['Pets'].properties['primitiveIds'].items.format == 'int64'
openAPI.components.schemas['Pets'].properties['primitiveIds'].items.description == 'Yes'
openAPI.components.schemas['Pets'].properties['primitiveIds'].items.nullable == true

openAPI.components.schemas['Pets'].properties['nestedPrimitiveIds'].description == 'a nested array of primitive Ids'
openAPI.components.schemas['Pets'].properties['nestedPrimitiveIds'].items.items.format == 'int64'

openAPI.components.schemas['Pets'].properties['nestedPetList'].description == 'a nested list of Pets'
openAPI.components.schemas['Pets'].properties['nestedPetList'].items.items.$ref == '#/components/schemas/Pet'

openAPI.components.schemas['Pets'].properties['nestedPetArray'].description == 'a nested array of Pets'
openAPI.components.schemas['Pets'].properties['nestedPetArray'].items.items.$ref == '#/components/schemas/Pet'

openAPI.components.schemas['Pets'].properties['nestedIdArray'].description == 'a nested array of Ids'
openAPI.components.schemas['Pets'].properties['nestedIdArray'].items.items.format == 'int64'

openAPI.components.schemas['Pets'].properties['idArrayList'].description == 'a list of nested Ids'
openAPI.components.schemas['Pets'].properties['idArrayList'].items.items.format == 'int64'

openAPI.components.schemas['Pets'].properties['idListArray'].description == 'an array of nested Ids'
openAPI.components.schemas['Pets'].properties['idListArray'].items.items.format == 'int64'
petSchema.description == 'Pets'
petSchema.properties['pets'].nullable == false
petSchema.properties['pets'].description == 'a list of Pets'
petSchema.properties['pets'].minItems == 2
petSchema.properties['pets'].items.$ref == '#/components/schemas/Pet'
petSchema.properties['pets'].items.nullable == null

petSchema.properties['ids'].nullable == false
petSchema.properties['ids'].description == 'a list of Ids'
petSchema.properties['ids'].minItems == 2
petSchema.properties['ids'].items.format == 'int64'
petSchema.properties['ids'].items.description == 'Yes'
petSchema.properties['ids'].items.nullable == true

petSchema.properties['primitiveIds'].nullable == false
petSchema.properties['primitiveIds'].description == 'a list of primitive Ids'
petSchema.properties['primitiveIds'].minItems == 2
petSchema.properties['primitiveIds'].items.format == 'int64'
petSchema.properties['primitiveIds'].items.description == 'Yes'
petSchema.properties['primitiveIds'].items.nullable == true

petSchema.properties['nestedPrimitiveIds'].description == 'a nested array of primitive Ids'
petSchema.properties['nestedPrimitiveIds'].items.items.format == 'int64'

petSchema.properties['nestedPetList'].description == 'a nested list of Pets'
petSchema.properties['nestedPetList'].items.items.$ref == '#/components/schemas/Pet'

petSchema.properties['nestedPetArray'].description == 'a nested array of Pets'
petSchema.properties['nestedPetArray'].items.items.$ref == '#/components/schemas/Pet'

petSchema.properties['nestedIdArray'].description == 'a nested array of Ids'
petSchema.properties['nestedIdArray'].items.items.format == 'int64'

petSchema.properties['idArrayList'].description == 'a list of nested Ids'
petSchema.properties['idArrayList'].items.items.format == 'int64'

petSchema.properties['idListArray'].description == 'an array of nested Ids'
petSchema.properties['idListArray'].items.items.format == 'int64'
}

void "test ArraySchema with arraySchema field in Controller ApiResponse"() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import io.swagger.v3.oas.models.Operation
import io.swagger.v3.oas.models.media.Schema
import spock.lang.Issue

import java.time.OffsetDateTime

class OpenApiBasicSchemaSpec extends AbstractOpenApiTypeElementSpec {

void "test @PositiveOrZero and @NegativeOrZero correctly results in minimum 0 and maximum 0"() {
Expand Down Expand Up @@ -1321,7 +1323,7 @@ class DemoData {
private URL url;
@Schema(defaultValue = "274191c9-c176-4b1c-8263-1b658cbdc7fc")
private UUID uuid;
@Schema(defaultValue = "Jan 12, 1952")
@Schema(defaultValue = "2007-12-03T10:15:30+01:00")
private Date date;
@Schema(defaultValue = "myDefault3")
private MySubObject mySubObject;
Expand Down Expand Up @@ -1428,13 +1430,14 @@ public class MyBean {}
schema.properties.uuid.type == 'string'
schema.properties.uuid.format == 'uuid'

schema.properties.date.default == 'Jan 12, 1952'
// TODO: need to add support custom format for DateTime
schema.properties.date.default == OffsetDateTime.parse('2007-12-03T10:15:30+01:00')
schema.properties.date.type == 'string'
schema.properties.date.format == 'date-time'

schema.properties.mySubObject.default == 'myDefault3'
schema.properties.mySubObject.type == null
schema.properties.mySubObject.format == null
schema.properties.mySubObject.allOf.get(1).default == 'myDefault3'
schema.properties.mySubObject.allOf.get(1).type == null
schema.properties.mySubObject.allOf.get(1).format == null
}

@Issue("https://github.com/micronaut-projects/micronaut-openapi/issues/947")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1001,7 +1001,7 @@ class MyController {
class MyBean {}
''')
when:
OpenAPI api = Utils.testReferenceAfterPlaceholders
OpenAPI api = Utils.testReference

then:
api.paths.size() == 2
Expand Down Expand Up @@ -1078,7 +1078,6 @@ class MyBean {}

then:
openAPI.components.schemas.size() == 1
openAPI.components.schemas['TestPojo'].name == 'TestPojo'
openAPI.components.schemas['TestPojo'].type == 'object'
openAPI.components.schemas['TestPojo'].properties.size() == 1
openAPI.components.schemas['TestPojo'].properties['testString'].type == 'string'
Expand Down
Loading

0 comments on commit 48a4288

Please sign in to comment.