Skip to content

Commit

Permalink
ref #2992 - add support for LocalTime and custom system and primitive…
Browse files Browse the repository at this point in the history
… types
  • Loading branch information
frantuma committed Oct 26, 2018
1 parent c18deed commit 2ecbdb5
Show file tree
Hide file tree
Showing 4 changed files with 261 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.concurrent.ConcurrentHashMap;

/**
* The <code>PrimitiveType</code> enumeration defines a mapping of limited set
Expand Down Expand Up @@ -126,6 +128,12 @@ public DateTimeSchema createProperty() {
return new DateTimeSchema();
}
},
PARTIAL_TIME(java.time.LocalTime.class, "partial-time") {
@Override
public Schema createProperty() {
return new StringSchema().format("partial-time");
}
},
FILE(java.io.File.class, "file") {
@Override
public FileSchema createProperty() {
Expand All @@ -146,6 +154,25 @@ public Schema createProperty() {
* Joda lib.
*/
private static final Map<String, PrimitiveType> EXTERNAL_CLASSES;

/**
* Adds support for custom mapping of classes to primitive types
*/
private static Map<String, PrimitiveType> customClasses = new ConcurrentHashMap<String, PrimitiveType>();

/**
* class qualified names prefixes to be considered as "system" types
*/
private static Set<String> systemPrefixes = new ConcurrentHashMap<String, Boolean>().newKeySet();
/**
* class qualified names NOT to be considered as "system" types
*/
private static Set<String> nonSystemTypes = new ConcurrentHashMap<String, Boolean>().newKeySet();
/**
* package names NOT to be considered as "system" types
*/
private static Set<String> nonSystemTypePackages = new ConcurrentHashMap<String, Boolean>().newKeySet();

/**
* Alternative names for primitive types that have to be supported for
* backward compatibility.
Expand All @@ -157,6 +184,10 @@ public Schema createProperty() {
public static final Map<String, String> datatypeMappings;

static {
systemPrefixes.add("java.");
systemPrefixes.add("javax.");
nonSystemTypes.add("java.time.LocalTime");

final Map<String, String> dms = new HashMap<>();
dms.put("integer_int32", "integer");
dms.put("integer_", "integer");
Expand All @@ -173,6 +204,7 @@ public Schema createProperty() {
dms.put("string_uuid", "uuid");
dms.put("string_date", "date");
dms.put("string_date-time", "date-time");
dms.put("string_partial-time", "partial-time");
dms.put("string_password", "password");
dms.put("boolean", "boolean");
dms.put("object_", "object");
Expand Down Expand Up @@ -231,16 +263,63 @@ private PrimitiveType(Class<?> keyClass, String commonName) {
this.commonName = commonName;
}

/**
* Adds support for custom mapping of classes to primitive types
*
* @return Map of custom classes to primitive type
* @since 2.0.6
*/
public static Map<String, PrimitiveType> customClasses() {
return customClasses;
}

/**
* class qualified names prefixes to be considered as "system" types
*
* @return Mutable set of class qualified names prefixes to be considered as "system" types
* @since 2.0.6
*/
public static Set<String> systemPrefixes() {
return systemPrefixes;
}

/**
* class qualified names NOT to be considered as "system" types
*
* @return Mutable set of class qualified names NOT to be considered as "system" types
* @since 2.0.6
*/
public static Set<String> nonSystemTypes() {
return nonSystemTypes;
}

/**
* package names NOT to be considered as "system" types
*
* @return Mutable set of package names NOT to be considered as "system" types
* @since 2.0.6
*/
public static Set<String> nonSystemTypePackages() {
return nonSystemTypePackages;
}

public static PrimitiveType fromType(Type type) {
final Class<?> raw = TypeFactory.defaultInstance().constructType(type).getRawClass();
final PrimitiveType key = KEY_CLASSES.get(raw);
if (key != null) {
return key;
}

final PrimitiveType custom = customClasses.get(raw.getName());
if (custom != null) {
return custom;
}

final PrimitiveType external = EXTERNAL_CLASSES.get(raw.getName());
if (external != null) {
return external;
}

for (Map.Entry<Class<?>, PrimitiveType> entry : BASE_CLASSES.entrySet()) {
if (entry.getKey().isAssignableFrom(raw)) {
return entry.getValue();
Expand Down Expand Up @@ -302,4 +381,15 @@ private static class DateStub {
private DateStub() {
}
}

/**
* Convenience method to map LocalTime to string primitive with rfc3339 format partial-time.
* See https://xml2rfc.tools.ietf.org/public/rfc/html/rfc3339.html#anchor14
*
* @since 2.0.6
*/
public static void enablePartialTime() {
customClasses().put("org.joda.time.LocalTime", PrimitiveType.PARTIAL_TIME);
customClasses().put("java.time.LocalTime", PrimitiveType.PARTIAL_TIME);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -360,8 +360,13 @@ public static boolean isVoid(Type type) {
public static boolean isSystemType(JavaType type) {
// used while resolving container types to skip resolving system types; possibly extend by checking classloader
// and/or other packages
if (type.getRawClass().getName().startsWith("java")) {
return true;
for (String systemPrefix: PrimitiveType.systemPrefixes()) {
if (type.getRawClass().getName().startsWith(systemPrefix)) {
if ( !PrimitiveType.nonSystemTypes().contains(type.getRawClass().getName()) &&
!PrimitiveType.nonSystemTypePackages().contains(type.getRawClass().getPackage().getName())) {
return true;
}
}
}
if (type.isArrayType()) {
return true;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
package io.swagger.v3.core.resolving;

import io.swagger.v3.core.converter.AnnotatedType;
import io.swagger.v3.core.converter.ModelConverterContextImpl;
import io.swagger.v3.core.jackson.ModelResolver;
import io.swagger.v3.core.matchers.SerializationMatchers;
import io.swagger.v3.core.resolving.resources.TestObject2992;
import io.swagger.v3.core.util.PrimitiveType;
import io.swagger.v3.oas.models.media.Schema;
import org.testng.annotations.Test;

public class Ticket2992Test extends SwaggerTestBase {

@Test
public void testLocalTime() throws Exception {

final ModelResolver modelResolver = new ModelResolver(mapper());

ModelConverterContextImpl context = new ModelConverterContextImpl(modelResolver);

Schema model = context
.resolve(new AnnotatedType(TestObject2992.class));

SerializationMatchers.assertEqualsToYaml(context.getDefinedModels(), "LocalTime:\n" +
" type: object\n" +
" properties:\n" +
" hour:\n" +
" type: integer\n" +
" format: int32\n" +
" minute:\n" +
" type: integer\n" +
" format: int32\n" +
" second:\n" +
" type: integer\n" +
" format: int32\n" +
" nano:\n" +
" type: integer\n" +
" format: int32\n" +
"TestObject2992:\n" +
" type: object\n" +
" properties:\n" +
" name:\n" +
" type: string\n" +
" a:\n" +
" $ref: '#/components/schemas/LocalTime'\n" +
" b:\n" +
" $ref: '#/components/schemas/LocalTime'\n" +
" c:\n" +
" $ref: '#/components/schemas/LocalTime'\n" +
" d:\n" +
" type: string\n" +
" format: date-time\n" +
" e:\n" +
" type: string\n" +
" format: date-time\n" +
" f:\n" +
" type: string\n" +
" format: date-time");

PrimitiveType.enablePartialTime();
context = new ModelConverterContextImpl(modelResolver);

context
.resolve(new AnnotatedType(TestObject2992.class));

SerializationMatchers.assertEqualsToYaml(context.getDefinedModels(), "TestObject2992:\n" +
" type: object\n" +
" properties:\n" +
" name:\n" +
" type: string\n" +
" a:\n" +
" type: string\n" +
" format: partial-time\n" +
" b:\n" +
" type: string\n" +
" format: partial-time\n" +
" c:\n" +
" type: string\n" +
" format: partial-time\n" +
" d:\n" +
" type: string\n" +
" format: date-time\n" +
" e:\n" +
" type: string\n" +
" format: date-time\n" +
" f:\n" +
" type: string\n" +
" format: date-time");
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package io.swagger.v3.core.resolving.resources;

import java.time.LocalDateTime;
import java.time.LocalTime;

public class TestObject2992 {

private String name;
private LocalTime a;
private LocalTime b;
private LocalTime c;

private LocalDateTime d;
private LocalDateTime e;
private LocalDateTime f;


public LocalTime getA() {
return a;
}

public void setA(LocalTime a) {
this.a = a;
}

public LocalTime getB() {
return b;
}

public void setB(LocalTime b) {
this.b = b;
}

public LocalTime getC() {
return c;
}

public void setC(LocalTime c) {
this.c = c;
}

public LocalDateTime getD() {
return d;
}

public void setD(LocalDateTime d) {
this.d = d;
}

public LocalDateTime getE() {
return e;
}

public void setE(LocalDateTime e) {
this.e = e;
}

public LocalDateTime getF() {
return f;
}

public void setF(LocalDateTime f) {
this.f = f;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}
}

0 comments on commit 2ecbdb5

Please sign in to comment.