Skip to content

Commit 49d9684

Browse files
authored
Merge branch 'master' into openApi-3.1-ComposedSchema-fix
2 parents 09202fa + 616350b commit 49d9684

File tree

10 files changed

+291
-8
lines changed

10 files changed

+291
-8
lines changed

modules/swagger-core/src/main/java/io/swagger/v3/core/jackson/ModelResolver.java

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -309,10 +309,20 @@ public Schema resolve(AnnotatedType annotatedType, ModelConverterContext context
309309
isPrimitive = true;
310310
}
311311
if (model == null) {
312-
PrimitiveType primitiveType = PrimitiveType.fromType(type);
313-
if (primitiveType != null) {
314-
model = PrimitiveType.fromType(type).createProperty();
315-
isPrimitive = true;
312+
if (resolvedSchemaAnnotation != null && StringUtils.isEmpty(resolvedSchemaAnnotation.type())) {
313+
PrimitiveType primitiveType = PrimitiveType.fromTypeAndFormat(type, resolvedSchemaAnnotation.format());
314+
if (primitiveType != null) {
315+
model = primitiveType.createProperty();
316+
isPrimitive = true;
317+
}
318+
}
319+
320+
if (model == null) {
321+
PrimitiveType primitiveType = PrimitiveType.fromType(type);
322+
if (primitiveType != null) {
323+
model = primitiveType.createProperty();
324+
isPrimitive = true;
325+
}
316326
}
317327
}
318328

@@ -629,7 +639,8 @@ public Schema resolve(AnnotatedType annotatedType, ModelConverterContext context
629639
propType = ((AnnotatedMethod)member).getParameterType(0);
630640
}
631641

632-
}
642+
}
643+
633644
String propSchemaName = null;
634645
io.swagger.v3.oas.annotations.media.Schema ctxSchema = AnnotationsUtils.getSchemaAnnotation(annotations);
635646
if (AnnotationsUtils.hasSchemaAnnotation(ctxSchema)) {

modules/swagger-core/src/main/java/io/swagger/v3/core/util/AnnotationsUtils.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1384,7 +1384,10 @@ public static Optional<Header> getHeader(io.swagger.v3.oas.annotations.headers.H
13841384
if (resolvedSchema.schema != null) {
13851385
headerObject.setSchema(resolvedSchema.schema);
13861386
}
1387-
resolvedSchema.referencedSchemas.forEach(components::addSchemas);
1387+
if (resolvedSchema.referencedSchemas != null && components != null) {
1388+
resolvedSchema.referencedSchemas.forEach(components::addSchemas);
1389+
}
1390+
13881391
}
13891392
}
13901393
if (hasArrayAnnotation(header.array())){

modules/swagger-core/src/main/java/io/swagger/v3/core/util/PrimitiveType.java

Lines changed: 51 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
import org.apache.commons.lang3.StringUtils;
1616

1717
import java.lang.reflect.Type;
18+
import java.util.ArrayList;
19+
import java.util.Collection;
1820
import java.util.Collections;
1921
import java.util.HashMap;
2022
import java.util.Map;
@@ -41,13 +43,23 @@ public Schema createProperty() {
4143
},
4244
BYTE(Byte.class, "byte") {
4345
@Override
44-
public ByteArraySchema createProperty() {
46+
public Schema createProperty() {
47+
if (
48+
(System.getProperty(Schema.BINARY_STRING_CONVERSION_PROPERTY) != null && System.getProperty(Schema.BINARY_STRING_CONVERSION_PROPERTY).equals(Schema.BynaryStringConversion.BINARY_STRING_CONVERSION_STRING_SCHEMA.toString())) ||
49+
(System.getenv(Schema.BINARY_STRING_CONVERSION_PROPERTY) != null && System.getenv(Schema.BINARY_STRING_CONVERSION_PROPERTY).equals(Schema.BynaryStringConversion.BINARY_STRING_CONVERSION_STRING_SCHEMA.toString()))) {
50+
return new StringSchema().format("byte");
51+
}
4552
return new ByteArraySchema();
4653
}
4754
},
4855
BINARY(Byte.class, "binary") {
4956
@Override
50-
public BinarySchema createProperty() {
57+
public Schema createProperty() {
58+
if (
59+
(System.getProperty(Schema.BINARY_STRING_CONVERSION_PROPERTY) != null && System.getProperty(Schema.BINARY_STRING_CONVERSION_PROPERTY).equals(Schema.BynaryStringConversion.BINARY_STRING_CONVERSION_STRING_SCHEMA.toString())) ||
60+
(System.getenv(Schema.BINARY_STRING_CONVERSION_PROPERTY) != null && System.getenv(Schema.BINARY_STRING_CONVERSION_PROPERTY).equals(Schema.BynaryStringConversion.BINARY_STRING_CONVERSION_STRING_SCHEMA.toString()))) {
61+
return new StringSchema().format("binary");
62+
}
5163
return new BinarySchema();
5264
}
5365
},
@@ -149,6 +161,7 @@ public Schema createProperty() {
149161
};
150162

151163
private static final Map<Class<?>, PrimitiveType> KEY_CLASSES;
164+
private static final Map<Class<?>, Collection<PrimitiveType>> MULTI_KEY_CLASSES;
152165
private static final Map<Class<?>, PrimitiveType> BASE_CLASSES;
153166
/**
154167
* Adds support of a small number of "well-known" types, specifically for
@@ -244,6 +257,11 @@ public Schema createProperty() {
244257
addKeys(keyClasses, OBJECT, Object.class);
245258
KEY_CLASSES = Collections.unmodifiableMap(keyClasses);
246259

260+
final Map<Class<?>, Collection<PrimitiveType>> multiKeyClasses = new HashMap<>();
261+
addMultiKeys(multiKeyClasses, BYTE, byte[].class);
262+
addMultiKeys(multiKeyClasses, BINARY, byte[].class);
263+
MULTI_KEY_CLASSES = Collections.unmodifiableMap(multiKeyClasses);
264+
247265
final Map<Class<?>, PrimitiveType> baseClasses = new HashMap<>();
248266
addKeys(baseClasses, DATE_TIME, java.util.Date.class, java.util.Calendar.class);
249267
BASE_CLASSES = Collections.unmodifiableMap(baseClasses);
@@ -343,6 +361,20 @@ public static Set<String> nonSystemTypePackages() {
343361
return nonSystemTypePackages;
344362
}
345363

364+
public static PrimitiveType fromTypeAndFormat(Type type, String format) {
365+
final Class<?> raw = TypeFactory.defaultInstance().constructType(type).getRawClass();
366+
final Collection<PrimitiveType> keys = MULTI_KEY_CLASSES.get(raw);
367+
if (keys == null || keys.isEmpty() || StringUtils.isBlank(format)) {
368+
return fromType(type);
369+
} else {
370+
return keys
371+
.stream()
372+
.filter(t -> t.getCommonName().equalsIgnoreCase(format))
373+
.findAny()
374+
.orElse(null);
375+
}
376+
}
377+
346378
public static PrimitiveType fromType(Type type) {
347379
final Class<?> raw = TypeFactory.defaultInstance().constructType(type).getRawClass();
348380
final PrimitiveType key = KEY_CLASSES.get(raw);
@@ -351,6 +383,14 @@ public static PrimitiveType fromType(Type type) {
351383
return key;
352384
}
353385
}
386+
387+
final Collection<PrimitiveType> keys = MULTI_KEY_CLASSES.get(raw);
388+
if (keys != null && !keys.isEmpty()) {
389+
final PrimitiveType first = keys.iterator().next();
390+
if (!customExcludedClasses.contains(raw.getName())) {
391+
return first;
392+
}
393+
}
354394

355395
final PrimitiveType custom = customClasses.get(raw.getName());
356396
if (custom != null) {
@@ -424,6 +464,15 @@ private static <K> void addKeys(Map<K, PrimitiveType> map, PrimitiveType type, K
424464
}
425465
}
426466

467+
private static <K> void addMultiKeys(Map<K, Collection<PrimitiveType>> map, PrimitiveType type, K... keys) {
468+
for (K key : keys) {
469+
if (!map.containsKey(key)) {
470+
map.put(key, new ArrayList<>());
471+
}
472+
map.get(key).add(type);
473+
}
474+
}
475+
427476
private static class DateStub {
428477
private DateStub() {
429478
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
package io.swagger.v3.jaxrs2;
2+
3+
import java.io.IOException;
4+
5+
import io.swagger.v3.oas.models.media.Schema;
6+
import org.testng.annotations.AfterTest;
7+
import org.testng.annotations.BeforeTest;
8+
import org.testng.annotations.Test;
9+
10+
import io.swagger.v3.jaxrs2.annotations.AbstractAnnotationTest;
11+
import io.swagger.v3.jaxrs2.resources.BinaryParameterResource;
12+
13+
public class BinaryParameterResourceTest extends AbstractAnnotationTest {
14+
15+
@Test(description = "check binary model serialization with base64", singleThreaded = true) // tests issue #2466
16+
public void shouldSerializeBinaryParameterBase64() throws IOException {
17+
try {
18+
System.setProperty(Schema.BINARY_STRING_CONVERSION_PROPERTY, Schema.BynaryStringConversion.BINARY_STRING_CONVERSION_BASE64.toString());
19+
compareAsYaml(BinaryParameterResource.class, getOpenAPIAsString("BinaryParameterResource.yaml"));
20+
} finally {
21+
System.clearProperty(Schema.BINARY_STRING_CONVERSION_PROPERTY);
22+
}
23+
}
24+
25+
@BeforeTest
26+
public void before() {
27+
System.clearProperty(Schema.BINARY_STRING_CONVERSION_PROPERTY);
28+
}
29+
30+
@AfterTest
31+
public void after() {
32+
System.clearProperty(Schema.BINARY_STRING_CONVERSION_PROPERTY);
33+
}
34+
35+
36+
@Test(description = "check binary model serialization with StringSchema", singleThreaded = true) // tests issue #2466
37+
public void shouldSerializeBinaryParameterStringSchema() throws IOException {
38+
try {
39+
System.setProperty(Schema.BINARY_STRING_CONVERSION_PROPERTY, Schema.BynaryStringConversion.BINARY_STRING_CONVERSION_STRING_SCHEMA.toString());
40+
compareAsYaml(BinaryParameterResource.class, getOpenAPIAsString("BinaryParameterResource.yaml"));
41+
} finally {
42+
System.clearProperty(Schema.BINARY_STRING_CONVERSION_PROPERTY);
43+
}
44+
}
45+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
package io.swagger.v3.jaxrs2.resources;
2+
3+
import javax.ws.rs.Consumes;
4+
import javax.ws.rs.POST;
5+
import javax.ws.rs.Path;
6+
import javax.ws.rs.core.Context;
7+
import javax.ws.rs.core.MediaType;
8+
import javax.ws.rs.core.Response;
9+
import javax.ws.rs.core.UriInfo;
10+
11+
import io.swagger.v3.jaxrs2.resources.model.Item;
12+
import io.swagger.v3.oas.annotations.Operation;
13+
import io.swagger.v3.oas.annotations.Parameter;
14+
import io.swagger.v3.oas.annotations.headers.Header;
15+
import io.swagger.v3.oas.annotations.media.Content;
16+
import io.swagger.v3.oas.annotations.media.Schema;
17+
import io.swagger.v3.oas.annotations.responses.ApiResponse;
18+
19+
@Path("/")
20+
public class BinaryParameterResource {
21+
@Consumes({ MediaType.APPLICATION_JSON })
22+
@Path("/binary")
23+
@POST
24+
@Operation(
25+
summary = "Create new item",
26+
description = "Post operation with entity in a body",
27+
responses = {
28+
@ApiResponse(
29+
content = @Content(
30+
schema = @Schema(implementation = Item.class),
31+
mediaType = MediaType.APPLICATION_JSON
32+
),
33+
headers = @Header(name = "Location"),
34+
responseCode = "201"
35+
)
36+
}
37+
)
38+
public Response createItem(@Context final UriInfo uriInfo, @Parameter(required = true) final Item item) {
39+
return Response
40+
.created(uriInfo.getBaseUriBuilder().path(item.getName()).build())
41+
.entity(item).build();
42+
}
43+
44+
}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
package io.swagger.v3.jaxrs2.resources.model;
2+
3+
import io.swagger.v3.oas.annotations.media.Schema;
4+
5+
public class Item {
6+
private String name;
7+
private String value;
8+
@Schema(example = "Ynl0ZQ==")
9+
private byte[] bytes;
10+
@Schema(format = "binary", example = "YmluYXJ5")
11+
private byte[] binary;
12+
13+
private byte[] byteNoAnnotation;
14+
15+
public Item() {
16+
}
17+
18+
public void setBinary(byte[] binary) {
19+
this.binary = binary;
20+
}
21+
22+
public byte[] getBinary() {
23+
return binary;
24+
}
25+
26+
public void setName(String name) {
27+
this.name = name;
28+
}
29+
30+
public String getName() {
31+
return name;
32+
}
33+
34+
public void setValue(String value) {
35+
this.value = value;
36+
}
37+
38+
public String getValue() {
39+
return value;
40+
}
41+
42+
public void setBytes(byte[] bytes) {
43+
this.bytes = bytes;
44+
}
45+
46+
public byte[] getBytes() {
47+
return bytes;
48+
}
49+
50+
public void setByteNoAnnotation(byte[] byteNoAnnotation) {
51+
this.byteNoAnnotation = byteNoAnnotation;
52+
}
53+
54+
public byte[] getByteNoAnnotation() {
55+
return byteNoAnnotation;
56+
}
57+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
openapi: 3.0.1
2+
paths:
3+
/binary:
4+
post:
5+
summary: Create new item
6+
description: Post operation with entity in a body
7+
operationId: createItem
8+
requestBody:
9+
content:
10+
application/json:
11+
schema:
12+
$ref: '#/components/schemas/Item'
13+
required: true
14+
responses:
15+
"201":
16+
headers:
17+
Location:
18+
style: simple
19+
content:
20+
application/json:
21+
schema:
22+
$ref: '#/components/schemas/Item'
23+
components:
24+
schemas:
25+
Item:
26+
type: object
27+
properties:
28+
name:
29+
type: string
30+
value:
31+
type: string
32+
bytes:
33+
type: string
34+
format: byte
35+
example: Ynl0ZQ==
36+
binary:
37+
type: string
38+
format: binary
39+
example: YmluYXJ5
40+
byteNoAnnotation:
41+
type: string
42+
format: byte

modules/swagger-models/src/main/java/io/swagger/v3/oas/models/media/BinarySchema.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package io.swagger.v3.oas.models.media;
22

3+
import java.util.Base64;
34
import java.util.List;
45
import java.util.Objects;
56

@@ -36,6 +37,13 @@ protected byte[] cast(Object value) {
3637
try {
3738
if (value instanceof byte[]) {
3839
return (byte[]) value;
40+
} else if (value instanceof String) {
41+
if (
42+
(System.getProperty(BINARY_STRING_CONVERSION_PROPERTY) != null && System.getProperty(BINARY_STRING_CONVERSION_PROPERTY).equals(BynaryStringConversion.BINARY_STRING_CONVERSION_BASE64.toString())) ||
43+
(System.getenv(BINARY_STRING_CONVERSION_PROPERTY) != null && System.getenv(BINARY_STRING_CONVERSION_PROPERTY).equals(BynaryStringConversion.BINARY_STRING_CONVERSION_BASE64.toString()))) {
44+
return Base64.getDecoder().decode((String) value);
45+
}
46+
return value.toString().getBytes();
3947
} else {
4048
return value.toString().getBytes();
4149
}

modules/swagger-models/src/main/java/io/swagger/v3/oas/models/media/ByteArraySchema.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package io.swagger.v3.oas.models.media;
22

3+
import java.util.Base64;
34
import java.util.List;
45
import java.util.Objects;
56

@@ -36,6 +37,13 @@ protected byte[] cast(Object value) {
3637
try {
3738
if (value instanceof byte[]) {
3839
return (byte[]) value;
40+
} else if (value instanceof String) {
41+
if (
42+
(System.getProperty(BINARY_STRING_CONVERSION_PROPERTY) != null && System.getProperty(BINARY_STRING_CONVERSION_PROPERTY).equals(BynaryStringConversion.BINARY_STRING_CONVERSION_BASE64.toString())) ||
43+
(System.getenv(BINARY_STRING_CONVERSION_PROPERTY) != null && System.getenv(BINARY_STRING_CONVERSION_PROPERTY).equals(BynaryStringConversion.BINARY_STRING_CONVERSION_BASE64.toString()))) {
44+
return Base64.getDecoder().decode((String) value);
45+
}
46+
return value.toString().getBytes();
3947
} else {
4048
return value.toString().getBytes();
4149
}

0 commit comments

Comments
 (0)