Skip to content

Commit

Permalink
Merge pull request #930 from altro3/fix_byte_type
Browse files Browse the repository at this point in the history
Fixed wrong byte type detection. Again remove generation schema for class java.lang.Object
  • Loading branch information
graemerocher authored Feb 13, 2023
2 parents add3031 + 419adad commit 85c96fa
Show file tree
Hide file tree
Showing 6 changed files with 181 additions and 26 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -905,6 +905,8 @@ protected Schema resolveSchema(OpenAPI openAPI, @Nullable Element definingElemen
schema = new StringSchema().format("partial-time");
} else if (type.isAssignable(Number.class)) {
schema = PrimitiveType.NUMBER.createProperty();
} else if (type.getName().equals(Object.class.getName())) {
schema = PrimitiveType.OBJECT.createProperty();
} else {
schema = getSchemaDefinition(openAPI, context, type, typeArgs, definingElement, mediaTypes);
}
Expand Down Expand Up @@ -1022,7 +1024,7 @@ protected void processSchemaProperty(VisitorContext context, TypedElement elemen
String elType = schemaAnnotationValue.stringValue("type").orElse(null);
String elFormat = schemaAnnotationValue.stringValue("format").orElse(null);
if (elType == null && elementType != null) {
Pair<String, String> typeAndFormat = ConvertUtils.getTypeAndFormatByClass(elementType.getName());
Pair<String, String> typeAndFormat = ConvertUtils.getTypeAndFormatByClass(elementType.getName(), elementType.isArray());
elType = typeAndFormat.getFirst();
if (elFormat == null) {
elFormat = typeAndFormat.getSecond();
Expand Down Expand Up @@ -1583,7 +1585,7 @@ private void setSchemaDocumentation(Element element, Schema schemaToBind) {
protected Schema bindSchemaAnnotationValue(VisitorContext context, Element element, Schema schemaToBind, AnnotationValue<io.swagger.v3.oas.annotations.media.Schema> schemaAnn) {

ClassElement classElement = ((TypedElement) element).getType();
Pair<String, String> typeAndFormat = classElement.isIterable() ? Pair.of("array", null) : ConvertUtils.getTypeAndFormatByClass(classElement.getName());
Pair<String, String> typeAndFormat = classElement.isIterable() ? Pair.of("array", null) : ConvertUtils.getTypeAndFormatByClass(classElement.getName(), classElement.isArray());

JsonNode schemaJson = toJson(schemaAnn.getValues(), context);
return doBindSchemaAnnotationValue(context, element, schemaToBind, schemaJson,
Expand All @@ -1604,7 +1606,8 @@ private Schema doBindSchemaAnnotationValue(VisitorContext context, Element eleme
}

if (elType == null && element != null) {
Pair<String, String> typeAndFormat = ConvertUtils.getTypeAndFormatByClass(((TypedElement) element).getType().getName());
ClassElement typeEl = ((TypedElement) element).getType();
Pair<String, String> typeAndFormat = ConvertUtils.getTypeAndFormatByClass(typeEl.getName(), typeEl.isArray());
elType = typeAndFormat.getFirst();
if (elFormat == null) {
elFormat = typeAndFormat.getSecond();
Expand Down Expand Up @@ -1814,7 +1817,9 @@ private Schema getSchemaDefinition(
schemas.put(schemaName, schema);

EnumElement enumEl = (EnumElement) type;
schema.setType(checkEnumJsonValueType(enumEl, schema.getType()));
Pair<String, String> typeAndFormat = checkEnumJsonValueType(enumEl, schema.getType(), schema.getFormat());
schema.setType(typeAndFormat.getFirst());
schema.setFormat(typeAndFormat.getSecond());
if (CollectionUtils.isEmpty(schema.getEnum())) {
schema.setEnum(getEnumValues(enumEl, schema.getType(), schema.getFormat(), context));
}
Expand Down Expand Up @@ -1894,6 +1899,11 @@ private Schema processSuperTypes(Schema schema,
List<MediaType> mediaTypes,
Map<String, Schema> schemas,
VisitorContext context) {

if (type.getName().equals(Object.class.getName())) {
return null;
}

ClassElement classElement = ((TypedElement) type).getType();
List<ClassElement> superTypes = new ArrayList<>();
Collection<ClassElement> parentInterfaces = classElement.getInterfaces();
Expand Down Expand Up @@ -2012,8 +2022,9 @@ protected Schema readSchema(AnnotationValue<io.swagger.v3.oas.annotations.media.

String elType = (String) values.get("type");
String elFormat = (String) values.get("format");
if (elType == null && type != null) {
Pair<String, String> typeAndFormat = ConvertUtils.getTypeAndFormatByClass(type.getName());
if (elType == null && type instanceof TypedElement) {
TypedElement typedType = (TypedElement) type;
Pair<String, String> typeAndFormat = ConvertUtils.getTypeAndFormatByClass(typedType.getName(), typedType.isArray());
elType = typeAndFormat.getFirst();
if (elFormat == null) {
elFormat = typeAndFormat.getSecond();
Expand Down Expand Up @@ -2076,7 +2087,8 @@ protected Schema readSchema(AnnotationValue<io.swagger.v3.oas.annotations.media.
}
if (type instanceof EnumElement) {
EnumElement enumEl = (EnumElement) type;
schema.setType(checkEnumJsonValueType(enumEl, elType != null ? elType : PrimitiveType.STRING.getCommonName()));
Pair<String, String> typeAndFormat = checkEnumJsonValueType(enumEl, elType != null ? elType : PrimitiveType.STRING.getCommonName(), null);
schema.setType(typeAndFormat.getFirst());
if (CollectionUtils.isEmpty(schema.getEnum())) {
schema.setEnum(getEnumValues((EnumElement) type, schema.getType(), elFormat, context));
}
Expand All @@ -2089,22 +2101,19 @@ protected Schema readSchema(AnnotationValue<io.swagger.v3.oas.annotations.media.
}

@NonNull
private String checkEnumJsonValueType(@NonNull EnumElement type, @Nullable String schemaType) {
private Pair<String, String> checkEnumJsonValueType(@NonNull EnumElement type, @Nullable String schemaType, @Nullable String schemaFormat) {
if (schemaType != null && !schemaType.equals(PrimitiveType.STRING.getCommonName())) {
return schemaType;
return Pair.of(schemaType, schemaFormat);
}
PrimitiveType jsonValueType = null;
Pair<String, String> result = null;
// check JsonValue method
for (MethodElement method : type.getEnclosedElements(ElementQuery.ALL_METHODS)) {
if (method.isAnnotationPresent(JsonValue.class)) {
jsonValueType = PrimitiveType.fromName(method.getReturnType().getName());
result = ConvertUtils.getTypeAndFormatByClass(method.getReturnType().getName(), method.getReturnType().isArray());
break;
}
}
if (jsonValueType != null) {
schemaType = jsonValueType.getCommonName();
}
return schemaType != null ? schemaType : PrimitiveType.STRING.getCommonName();
return result != null ? result : Pair.of(PrimitiveType.STRING.getCommonName(), schemaFormat);
}

private List<Object> getEnumValues(EnumElement type, String schemaType, String schemaFormat, VisitorContext context) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -191,10 +191,11 @@ public static SecurityRequirement mapToSecurityRequirement(AnnotationValue<io.sw
* Detect openapi type nd format by java class name.
*
* @param className java class name
* @param isArray is it array
*
* @return pair with openapi type and format
*/
public static Pair<String, String> getTypeAndFormatByClass(String className) {
public static Pair<String, String> getTypeAndFormatByClass(String className, boolean isArray) {
if (className == null) {
return Pair.of("object", null);
}
Expand Down Expand Up @@ -222,9 +223,13 @@ public static Pair<String, String> getTypeAndFormatByClass(String className) {
} else if (Double.class.getName().equals(className)
|| double.class.getName().equals(className)) {
return Pair.of("number", "double");
} else if (isArray && (Byte.class.getName().equals(className)
|| byte.class.getName().equals(className))) {
return Pair.of("string", "byte");
// swagger doesn't support type byte
} else if (Byte.class.getName().equals(className)
|| byte.class.getName().equals(className)) {
return Pair.of("string", "byte");
return Pair.of("integer", "int32");
} else if (BigDecimal.class.getName().equals(className)) {
return Pair.of("number", null);
} else if (URI.class.getName().equals(className)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -534,4 +534,145 @@ class MyBean {}
schema.enum.get(0) == 'AWS'
schema.enum.get(1) == 'AZURE'
}

void "test build OpenAPI enum Schema with byte type values"() {

when:
buildBeanDefinition('test.MyBean', '''
package test;
import java.util.List;
import io.micronaut.http.annotation.Controller;
import io.micronaut.http.annotation.Post;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonValue;
@Controller
class OpenApiController {
@Post
public void postRaw(DictionaryRequest request) {
}
}
class DictionaryRequest {
protected Type1 type1;
protected Type2 type2;
public Type1 getType1() {
return type1;
}
public void setType1(Type1 type1) {
this.type1 = type1;
}
public Type2 getType2() {
return type2;
}
public void setType2(Type2 type2) {
this.type2 = type2;
}
}
enum Type1 {
@JsonProperty("1")
_1(((byte) 1)),
@JsonProperty("2")
_2(((byte) 2));
private final byte value;
Type1(byte value) {
this.value = value;
}
@JsonValue
public byte value() {
return value;
}
@JsonCreator
public static Type1 fromValue(byte v) {
for (Type1 c : values()) {
if (c.value == v) {
return c;
}
}
throw new IllegalArgumentException(String.valueOf(v));
}
}
enum Type2 {
@JsonProperty("111")
_1("111"),
@JsonProperty("222")
_2("222");
private final byte[] value;
Type2(String value) {
this.value = value.getBytes();
}
@JsonValue
public byte[] value() {
return value;
}
@JsonCreator
public static Type2 fromValue(byte[] v) {
for (Type2 c : values()) {
if (c.value == v) {
return c;
}
}
throw new IllegalArgumentException(String.valueOf(v));
}
}
@jakarta.inject.Singleton
class MyBean {}
''')
then: "the state is correct"
Utils.testReferenceAfterPlaceholders != null

when: "The OpenAPI is retrieved"
OpenAPI openAPI = Utils.testReferenceAfterPlaceholders

then: "the state is correct"
openAPI.components
openAPI.components.schemas
openAPI.components.schemas.size() == 3

when:
Schema requestSchema = openAPI.components.schemas['DictionaryRequest']
Schema type1Schema = openAPI.components.schemas['Type1']
Schema type2Schema = openAPI.components.schemas['Type2']

then: "the components are valid"
requestSchema
type1Schema
type1Schema.enum
type1Schema.enum.size() == 2
type1Schema.type == 'integer'
type1Schema.format == 'int32'
type1Schema.enum.contains(1)
type1Schema.enum.contains(2)

type2Schema
type2Schema.enum
type2Schema.enum.size() == 2
type2Schema.type == 'string'
type2Schema.format == 'byte'
type2Schema.enum.contains("111")
type2Schema.enum.contains("222")
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ class MyBean {}
loginPathItem.post.requestBody.content['application/x-www-form-urlencoded'].schema['$ref'] == '#/components/schemas/UsernamePasswordCredentials'
loginPathItem.post.requestBody.content['application/json'].schema
loginPathItem.post.requestBody.content['application/json'].schema['$ref'] == '#/components/schemas/UsernamePasswordCredentials'
loginPathItem.post.responses['200'].content['application/json'].schema['$ref'] == '#/components/schemas/Object'
loginPathItem.post.responses['200'].content['application/json'].schema.type == 'object'
openAPI.components.schemas['UsernamePasswordCredentials']
openAPI.components.schemas['UsernamePasswordCredentials'].required.size() == 2
openAPI.components.schemas['UsernamePasswordCredentials'].properties['username']
Expand Down Expand Up @@ -202,7 +202,7 @@ class MyBean {}
loginPathItem.post.requestBody.content['application/x-www-form-urlencoded'].schema['$ref'] == '#/components/schemas/UsernamePasswordCredentials'
loginPathItem.post.requestBody.content['application/json'].schema
loginPathItem.post.requestBody.content['application/json'].schema['$ref'] == '#/components/schemas/UsernamePasswordCredentials'
loginPathItem.post.responses['200'].content['application/json'].schema['$ref'] == '#/components/schemas/Object'
loginPathItem.post.responses['200'].content['application/json'].schema.type == 'object'

openAPI.components.schemas['UsernamePasswordCredentials']
openAPI.components.schemas['UsernamePasswordCredentials'].required.size() == 2
Expand Down Expand Up @@ -315,17 +315,17 @@ class MyBean {}
loginPathItem.post.requestBody.content['application/x-www-form-urlencoded'].schema['$ref'] == '#/components/schemas/UsernamePasswordCredentials'
loginPathItem.post.requestBody.content['application/json'].schema
loginPathItem.post.requestBody.content['application/json'].schema['$ref'] == '#/components/schemas/UsernamePasswordCredentials'
loginPathItem.post.responses['200'].content['application/json'].schema['$ref'] == '#/components/schemas/Object'
loginPathItem.post.responses['200'].content['application/json'].schema.type == 'object'

logoutPathItem.post.operationId == 'index'
logoutPathItem.post.tags[0] == "Tag 5"
logoutPathItem.post.security[0]["req 3"]
logoutPathItem.post.responses['200'].content['application/json'].schema['$ref'] == '#/components/schemas/Object'
logoutPathItem.post.responses['200'].content['application/json'].schema.type == 'object'

logoutPathItem.get.operationId == 'indexGet'
logoutPathItem.get.tags[0] == "Tag 5"
logoutPathItem.get.security[0]["req 3"]
logoutPathItem.get.responses['200'].content['application/json'].schema['$ref'] == '#/components/schemas/Object'
logoutPathItem.get.responses['200'].content['application/json'].schema.type == 'object'

openAPI.components.schemas['UsernamePasswordCredentials']
openAPI.components.schemas['UsernamePasswordCredentials'].required.size() == 2
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -313,7 +313,7 @@ class Tag {
private String name;
private String description;
public Tag(String name, String description) {
Tag(String name, String description) {
this.name = name;
this.description = description;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -200,17 +200,17 @@ class MyBean {}
loginPathItem.post.requestBody.content['application/x-www-form-urlencoded'].schema['$ref'] == '#/components/schemas/UsernamePasswordCredentials'
loginPathItem.post.requestBody.content['application/json'].schema
loginPathItem.post.requestBody.content['application/json'].schema['$ref'] == '#/components/schemas/UsernamePasswordCredentials'
loginPathItem.post.responses['200'].content['application/json'].schema['$ref'] == '#/components/schemas/Object'
loginPathItem.post.responses['200'].content['application/json'].schema.type == 'object'

logoutPathItem.post.operationId == 'index'
logoutPathItem.post.tags[0] == "Tag 5"
logoutPathItem.post.security[0]["req 3"]
logoutPathItem.post.responses['200'].content['application/json'].schema['$ref'] == '#/components/schemas/Object'
loginPathItem.post.responses['200'].content['application/json'].schema.type == 'object'

logoutPathItem.get.operationId == 'indexGet'
logoutPathItem.get.tags[0] == "Tag 5"
logoutPathItem.get.security[0]["req 3"]
logoutPathItem.get.responses['200'].content['application/json'].schema['$ref'] == '#/components/schemas/Object'
loginPathItem.post.responses['200'].content['application/json'].schema.type == 'object'

openAPI.components.schemas['UsernamePasswordCredentials']
openAPI.components.schemas['UsernamePasswordCredentials'].required.size() == 2
Expand Down

0 comments on commit 85c96fa

Please sign in to comment.