diff --git a/modules/swagger-core/src/main/java/io/swagger/v3/core/util/ModelDeserializer.java b/modules/swagger-core/src/main/java/io/swagger/v3/core/util/ModelDeserializer.java index f549663bba..a5bbfc941b 100644 --- a/modules/swagger-core/src/main/java/io/swagger/v3/core/util/ModelDeserializer.java +++ b/modules/swagger-core/src/main/java/io/swagger/v3/core/util/ModelDeserializer.java @@ -12,7 +12,9 @@ import io.swagger.v3.oas.models.media.BooleanSchema; import io.swagger.v3.oas.models.media.ComposedSchema; import io.swagger.v3.oas.models.media.DateSchema; +import io.swagger.v3.oas.models.media.DateTimeLocalSchema; import io.swagger.v3.oas.models.media.DateTimeSchema; +import io.swagger.v3.oas.models.media.DurationSchema; import io.swagger.v3.oas.models.media.EmailSchema; import io.swagger.v3.oas.models.media.IntegerSchema; import io.swagger.v3.oas.models.media.JsonSchema; @@ -22,6 +24,8 @@ import io.swagger.v3.oas.models.media.PasswordSchema; import io.swagger.v3.oas.models.media.Schema; import io.swagger.v3.oas.models.media.StringSchema; +import io.swagger.v3.oas.models.media.TimeLocalSchema; +import io.swagger.v3.oas.models.media.TimeSchema; import io.swagger.v3.oas.models.media.UUIDSchema; import org.apache.commons.lang3.StringUtils; @@ -46,6 +50,10 @@ public class ModelDeserializer extends JsonDeserializer { private static final String FORMAT = "format"; private static final String DATE_FORMAT = "date"; private static final String DATE_TIME_FORMAT = "date-time"; + private static final String TIME_FORMAT = "time"; + private static final String DURATION_FORMAT = "duration"; + private static final String DATE_TIME_LOCAL_FORMAT = "date-time-local"; + private static final String TIME_LOCAL_FORMAT = "time-local"; private static final String EMAIL_FORMAT = "email"; private static final String PASSWORD_FORMAT = "password"; private static final String UUID_FORMAT = "uuid"; @@ -196,7 +204,15 @@ private Schema deserializeSchemaWithType(JsonNode node, JsonNode typeNode) { schema = Json.mapper().convertValue(node, DateSchema.class); } else if (DATE_TIME_FORMAT.equals(format)) { schema = Json.mapper().convertValue(node, DateTimeSchema.class); - } else if (EMAIL_FORMAT.equals(format)) { + } else if (TIME_FORMAT.equals(format)) { + schema = Json.mapper().convertValue(node, TimeSchema.class); + } else if (DURATION_FORMAT.equals(format)) { + schema = Json.mapper().convertValue(node, DurationSchema.class); + } else if (DATE_TIME_LOCAL_FORMAT.equals(format)) { + schema = Json.mapper().convertValue(node, DateTimeLocalSchema.class); + } else if (TIME_LOCAL_FORMAT.equals(format)) { + schema = Json.mapper().convertValue(node, TimeLocalSchema.class); + }else if (EMAIL_FORMAT.equals(format)) { schema = Json.mapper().convertValue(node, EmailSchema.class); } else if (PASSWORD_FORMAT.equals(format)) { schema = Json.mapper().convertValue(node, PasswordSchema.class); diff --git a/modules/swagger-core/src/main/java/io/swagger/v3/core/util/PrimitiveType.java b/modules/swagger-core/src/main/java/io/swagger/v3/core/util/PrimitiveType.java index 6d99fb7a2c..d3164f9420 100644 --- a/modules/swagger-core/src/main/java/io/swagger/v3/core/util/PrimitiveType.java +++ b/modules/swagger-core/src/main/java/io/swagger/v3/core/util/PrimitiveType.java @@ -5,13 +5,17 @@ import io.swagger.v3.oas.models.media.BooleanSchema; import io.swagger.v3.oas.models.media.ByteArraySchema; import io.swagger.v3.oas.models.media.DateSchema; +import io.swagger.v3.oas.models.media.DateTimeLocalSchema; import io.swagger.v3.oas.models.media.DateTimeSchema; +import io.swagger.v3.oas.models.media.DurationSchema; import io.swagger.v3.oas.models.media.FileSchema; import io.swagger.v3.oas.models.media.IntegerSchema; import io.swagger.v3.oas.models.media.JsonSchema; import io.swagger.v3.oas.models.media.NumberSchema; import io.swagger.v3.oas.models.media.Schema; import io.swagger.v3.oas.models.media.StringSchema; +import io.swagger.v3.oas.models.media.TimeLocalSchema; +import io.swagger.v3.oas.models.media.TimeSchema; import io.swagger.v3.oas.models.media.UUIDSchema; import org.apache.commons.lang3.StringUtils; @@ -221,6 +225,46 @@ public Schema createProperty31() { return new JsonSchema().typesItem("string").format("partial-time"); } }, + DATE_TIME_LOCAL(java.time.LocalDateTime.class, "date-time-local") { + @Override + public Schema createProperty() { + return new DateTimeLocalSchema(); + } + @Override + public Schema createProperty31() { + return new JsonSchema().typesItem("string").format("date-time-local"); + } + }, + TIME(java.time.OffsetTime.class, "time") { + @Override + public Schema createProperty() { + return new TimeSchema(); + } + @Override + public Schema createProperty31() { + return new JsonSchema().typesItem("string").format("time"); + } + }, + TIME_LOCAL(java.time.LocalTime.class, "time-local") { + @Override + public Schema createProperty() { + return new TimeLocalSchema(); + } + @Override + public Schema createProperty31() { + return new JsonSchema().typesItem("string").format("time-local"); + } + }, + DURATION(java.time.Duration.class, "duration") { + @Override + public Schema createProperty() { + return new DurationSchema(); + } + @Override + public Schema createProperty31() { + return new JsonSchema().typesItem("string").format("duration"); + } + }, FILE(java.io.File.class, "file") { @Override public FileSchema createProperty() { @@ -315,6 +359,10 @@ public Schema createProperty31() { dms.put("string_uuid", "uuid"); dms.put("string_date", "date"); dms.put("string_date-time", "date-time"); + dms.put("string_date-time-local", "date-time-local"); + dms.put("string_time", "time"); + dms.put("string_time-local", "time-local"); + dms.put("string_duration", "duration"); dms.put("string_partial-time", "partial-time"); dms.put("string_password", "password"); dms.put("boolean_", "boolean"); @@ -361,6 +409,9 @@ public Schema createProperty31() { "org.joda.time.ReadableDateTime", "org.joda.time.DateTime", "java.time.Instant"); + addKeys(externalClasses, TIME, "java.time.OffsetTime"); + addKeys(externalClasses, DURATION, "java.time.Duration"); + addKeys(externalClasses, TIME_LOCAL, "java.time.LocalTime"); EXTERNAL_CLASSES = Collections.unmodifiableMap(externalClasses); final Map names = new TreeMap<>(String.CASE_INSENSITIVE_ORDER); @@ -581,10 +632,28 @@ 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 * + *

{@code "partial-time"} is not itself a registered OpenAPI Formats Registry value + * (it is borrowed from RFC 3339 grammar). {@code java.time.LocalTime} already defaults to + * the registry-compliant {@code "time-local"}; this method overrides that default for + * callers who specifically need the {@code "partial-time"} format instead, since it is a + * different mapping, not a strict replacement for it. + * * @since 2.0.6 + * @deprecated Prefer the default {@code "time-local"} mapping for {@code java.time.LocalTime}. */ + @Deprecated public static void enablePartialTime() { customClasses().put("org.joda.time.LocalTime", PrimitiveType.PARTIAL_TIME); customClasses().put("java.time.LocalTime", PrimitiveType.PARTIAL_TIME); } + + /** + * Opts in to the OpenAPI Formats Registry mapping for {@code java.time.LocalDateTime}: + * maps it to format {@code "date-time-local"} instead of the default {@code "date-time"}. + * + * @since 2.3.0 + */ + public static void enableJava8Formats() { + customClasses().put("java.time.LocalDateTime", PrimitiveType.DATE_TIME_LOCAL); + } } diff --git a/modules/swagger-core/src/test/java/io/swagger/v3/core/resolving/Java8DateFormatsTest.java b/modules/swagger-core/src/test/java/io/swagger/v3/core/resolving/Java8DateFormatsTest.java new file mode 100644 index 0000000000..ae8f351be9 --- /dev/null +++ b/modules/swagger-core/src/test/java/io/swagger/v3/core/resolving/Java8DateFormatsTest.java @@ -0,0 +1,194 @@ +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.TestObjectJava8Dates; +import io.swagger.v3.core.resolving.resources.TestObject2992; +import io.swagger.v3.core.util.PrimitiveType; +import io.swagger.v3.oas.models.media.DurationSchema; +import io.swagger.v3.oas.models.media.Schema; +import io.swagger.v3.oas.models.media.TimeLocalSchema; +import io.swagger.v3.oas.models.media.TimeSchema; +import org.testng.annotations.Test; + +import java.time.Duration; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.OffsetTime; +import java.util.Map; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertNotNull; +import static org.testng.Assert.assertTrue; + +/** + * Verifies Java 8 date/time type → OpenAPI format mappings (issue #5172). + * + * Default behaviour (fixed; previous complex-object expansion was always incorrect): + * OffsetTime → "time" + * Duration → "duration" + * LocalTime → "time-local" + * + * Default behaviour (unchanged for compatibility): + * LocalDateTime → "date-time" + * + * Opt-in via PrimitiveType.enableJava8Formats(): + * LocalDateTime → "date-time-local" + */ +public class Java8DateFormatsTest extends SwaggerTestBase { + + @Test + public void testDefaultFormats() throws Exception { + final ModelResolver modelResolver = new ModelResolver(mapper()); + final ModelConverterContextImpl context = new ModelConverterContextImpl(modelResolver); + + context.resolve(new AnnotatedType(TestObjectJava8Dates.class)); + + SerializationMatchers.assertEqualsToYaml(context.getDefinedModels(), "TestObjectJava8Dates:\n" + + " type: object\n" + + " properties:\n" + + " localDateTime:\n" + + " type: string\n" + + " format: date-time\n" + + " offsetDateTime:\n" + + " type: string\n" + + " format: date-time\n" + + " zonedDateTime:\n" + + " type: string\n" + + " format: date-time\n" + + " instant:\n" + + " type: string\n" + + " format: date-time\n" + + " localDate:\n" + + " type: string\n" + + " format: date\n" + + " offsetTime:\n" + + " type: string\n" + + " format: time\n" + + " duration:\n" + + " type: string\n" + + " format: duration"); + } + + @Test + public void testEnableJava8Formats() throws Exception { + // Save current state so other tests are not affected by the static customClasses map + final Map custom = PrimitiveType.customClasses(); + final PrimitiveType prevLocalDateTime = custom.get("java.time.LocalDateTime"); + final PrimitiveType prevLocalTime = custom.get("java.time.LocalTime"); + + PrimitiveType.enableJava8Formats(); + try { + final ModelResolver modelResolver = new ModelResolver(mapper()); + final ModelConverterContextImpl context = new ModelConverterContextImpl(modelResolver); + + context.resolve(new AnnotatedType(TestObject2992.class)); + + // LocalDateTime → "date-time-local", LocalTime → "time-local" after opt-in + SerializationMatchers.assertEqualsToYaml(context.getDefinedModels(), "TestObject2992:\n" + + " type: object\n" + + " properties:\n" + + " name:\n" + + " type: string\n" + + " a:\n" + + " type: string\n" + + " format: time-local\n" + + " b:\n" + + " type: string\n" + + " format: time-local\n" + + " c:\n" + + " type: string\n" + + " format: time-local\n" + + " d:\n" + + " type: string\n" + + " format: date-time-local\n" + + " e:\n" + + " type: string\n" + + " format: date-time-local\n" + + " f:\n" + + " type: string\n" + + " format: date-time-local"); + } finally { + // Restore previous state so subsequent tests are not affected + if (prevLocalDateTime == null) custom.remove("java.time.LocalDateTime"); + else custom.put("java.time.LocalDateTime", prevLocalDateTime); + if (prevLocalTime == null) custom.remove("java.time.LocalTime"); + else custom.put("java.time.LocalTime", prevLocalTime); + } + } + + @Test + public void testDefaultLocalTime() { + // Isolate from any prior enablePartialTime()/enableJava8Formats() call left in the + // shared static customClasses map by other tests (e.g. Ticket2992Test), so this checks + // the true PrimitiveType default, regardless of test execution order. + final String key = "java.time.LocalTime"; + final Map custom = PrimitiveType.customClasses(); + final PrimitiveType previous = custom.remove(key); + try { + final Schema schema = PrimitiveType.createProperty(LocalTime.class); + assertNotNull(schema); + assertEquals(schema.getClass(), TimeLocalSchema.class); + assertEquals(schema.getFormat(), "time-local"); + } finally { + if (previous == null) custom.remove(key); + else custom.put(key, previous); + } + } + + @Test + public void testDefaultOffsetTime() { + final Schema schema = PrimitiveType.createProperty(OffsetTime.class); + assertNotNull(schema); + assertEquals(schema.getClass(), TimeSchema.class); + assertEquals(schema.getFormat(), "time"); + } + + @Test + public void testDefaultDuration() { + final Schema schema = PrimitiveType.createProperty(Duration.class); + assertNotNull(schema); + assertEquals(schema.getClass(), DurationSchema.class); + assertEquals(schema.getFormat(), "duration"); + } + + @Test + public void testDefaultFormats31() { + assertSchema31(PrimitiveType.createProperty(OffsetTime.class, true), "time"); + assertSchema31(PrimitiveType.createProperty(Duration.class, true), "duration"); + + // LocalTime: same guard as testDefaultLocalTime — isolate from customClasses + final String key = "java.time.LocalTime"; + final Map custom = PrimitiveType.customClasses(); + final PrimitiveType previous = custom.remove(key); + try { + assertSchema31(PrimitiveType.createProperty(LocalTime.class, true), "time-local"); + } finally { + if (previous == null) custom.remove(key); + else custom.put(key, previous); + } + } + + @Test + public void testEnableJava8Formats31() { + final Map custom = PrimitiveType.customClasses(); + final PrimitiveType previous = custom.get("java.time.LocalDateTime"); + + PrimitiveType.enableJava8Formats(); + try { + assertSchema31(PrimitiveType.createProperty(LocalDateTime.class, true), "date-time-local"); + } finally { + if (previous == null) custom.remove("java.time.LocalDateTime"); + else custom.put("java.time.LocalDateTime", previous); + } + } + + private static void assertSchema31(Schema schema, String format) { + assertNotNull(schema); + assertNotNull(schema.getTypes()); + assertTrue(schema.getTypes().contains("string")); + assertEquals(schema.getFormat(), format); + } +} diff --git a/modules/swagger-core/src/test/java/io/swagger/v3/core/resolving/Ticket2992Test.java b/modules/swagger-core/src/test/java/io/swagger/v3/core/resolving/Ticket2992Test.java index 75eebf5df4..a38e93c413 100644 --- a/modules/swagger-core/src/test/java/io/swagger/v3/core/resolving/Ticket2992Test.java +++ b/modules/swagger-core/src/test/java/io/swagger/v3/core/resolving/Ticket2992Test.java @@ -9,6 +9,8 @@ import io.swagger.v3.oas.models.media.Schema; import org.testng.annotations.Test; +import java.util.Map; + public class Ticket2992Test extends SwaggerTestBase { @Test @@ -21,48 +23,6 @@ public void testLocalTime() throws Exception { 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" + @@ -70,13 +30,13 @@ public void testLocalTime() throws Exception { " type: string\n" + " a:\n" + " type: string\n" + - " format: partial-time\n" + + " format: time-local\n" + " b:\n" + " type: string\n" + - " format: partial-time\n" + + " format: time-local\n" + " c:\n" + " type: string\n" + - " format: partial-time\n" + + " format: time-local\n" + " d:\n" + " type: string\n" + " format: date-time\n" + @@ -86,6 +46,46 @@ public void testLocalTime() throws Exception { " f:\n" + " type: string\n" + " format: date-time"); + + // Save current state so other tests are not affected by the static customClasses map + final Map custom = PrimitiveType.customClasses(); + final PrimitiveType previous = custom.get("java.time.LocalTime"); + + PrimitiveType.enablePartialTime(); + try { + 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"); + } finally { + // Restore previous state so subsequent tests are not affected + if (previous == null) custom.remove("java.time.LocalTime"); + else custom.put("java.time.LocalTime", previous); + } } } diff --git a/modules/swagger-core/src/test/java/io/swagger/v3/core/resolving/resources/TestObjectJava8Dates.java b/modules/swagger-core/src/test/java/io/swagger/v3/core/resolving/resources/TestObjectJava8Dates.java new file mode 100644 index 0000000000..660daf821a --- /dev/null +++ b/modules/swagger-core/src/test/java/io/swagger/v3/core/resolving/resources/TestObjectJava8Dates.java @@ -0,0 +1,45 @@ +package io.swagger.v3.core.resolving.resources; + +import java.time.Duration; +import java.time.Instant; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.OffsetDateTime; +import java.time.OffsetTime; +import java.time.ZonedDateTime; + +// LocalTime default is verified separately in Java8DateFormatsTest#testDefaultLocalTime, +// as an isolated PrimitiveType-level check that clears any prior enablePartialTime()/ +// enableJava8Formats() state before asserting — not here, to avoid coupling this +// integration fixture to shared mutable state in PrimitiveType.customClasses(). +public class TestObjectJava8Dates { + + private LocalDateTime localDateTime; + private OffsetDateTime offsetDateTime; + private ZonedDateTime zonedDateTime; + private Instant instant; + private LocalDate localDate; + private OffsetTime offsetTime; + private Duration duration; + + public LocalDateTime getLocalDateTime() { return localDateTime; } + public void setLocalDateTime(LocalDateTime localDateTime) { this.localDateTime = localDateTime; } + + public OffsetDateTime getOffsetDateTime() { return offsetDateTime; } + public void setOffsetDateTime(OffsetDateTime offsetDateTime) { this.offsetDateTime = offsetDateTime; } + + public ZonedDateTime getZonedDateTime() { return zonedDateTime; } + public void setZonedDateTime(ZonedDateTime zonedDateTime) { this.zonedDateTime = zonedDateTime; } + + public Instant getInstant() { return instant; } + public void setInstant(Instant instant) { this.instant = instant; } + + public LocalDate getLocalDate() { return localDate; } + public void setLocalDate(LocalDate localDate) { this.localDate = localDate; } + + public OffsetTime getOffsetTime() { return offsetTime; } + public void setOffsetTime(OffsetTime offsetTime) { this.offsetTime = offsetTime; } + + public Duration getDuration() { return duration; } + public void setDuration(Duration duration) { this.duration = duration; } +} diff --git a/modules/swagger-core/src/test/java/io/swagger/v3/core/serialization/properties/PropertySerializationTest.java b/modules/swagger-core/src/test/java/io/swagger/v3/core/serialization/properties/PropertySerializationTest.java index 3cd5ea4022..5d362f2102 100644 --- a/modules/swagger-core/src/test/java/io/swagger/v3/core/serialization/properties/PropertySerializationTest.java +++ b/modules/swagger-core/src/test/java/io/swagger/v3/core/serialization/properties/PropertySerializationTest.java @@ -6,13 +6,17 @@ import io.swagger.v3.oas.models.media.ArraySchema; import io.swagger.v3.oas.models.media.BooleanSchema; import io.swagger.v3.oas.models.media.DateSchema; +import io.swagger.v3.oas.models.media.DateTimeLocalSchema; import io.swagger.v3.oas.models.media.DateTimeSchema; +import io.swagger.v3.oas.models.media.DurationSchema; import io.swagger.v3.oas.models.media.IntegerSchema; import io.swagger.v3.oas.models.media.MapSchema; import io.swagger.v3.oas.models.media.NumberSchema; import io.swagger.v3.oas.models.media.ObjectSchema; import io.swagger.v3.oas.models.media.Schema; import io.swagger.v3.oas.models.media.StringSchema; +import io.swagger.v3.oas.models.media.TimeLocalSchema; +import io.swagger.v3.oas.models.media.TimeSchema; import org.testng.annotations.Test; import java.io.IOException; @@ -81,6 +85,74 @@ public void deserializeDateTimeProperty() throws IOException { JsonAssert.assertJsonEquals(m, m.writeValueAsString(p), json); } + @Test(description = "it should serialize a TimeSchema") + public void serializeTimeSchema() throws IOException { + final TimeSchema p = new TimeSchema(); + final String json = "{\"type\":\"string\",\"format\":\"time\"}"; + JsonAssert.assertJsonEquals(m, m.writeValueAsString(p), json); + } + + @Test(description = "it should deserialize a TimeSchema") + public void deserializeTimeSchema() throws IOException { + final String json = "{\"type\":\"string\",\"format\":\"time\"}"; + final Schema p = m.readValue(json, Schema.class); + assertEquals(p.getType(), "string"); + assertEquals(p.getFormat(), "time"); + assertEquals(p.getClass(), TimeSchema.class); + JsonAssert.assertJsonEquals(m, m.writeValueAsString(p), json); + } + + @Test(description = "it should serialize a DurationSchema") + public void serializeDurationSchema() throws IOException { + final DurationSchema p = new DurationSchema(); + final String json = "{\"type\":\"string\",\"format\":\"duration\"}"; + JsonAssert.assertJsonEquals(m, m.writeValueAsString(p), json); + } + + @Test(description = "it should deserialize a DurationSchema") + public void deserializeDurationSchema() throws IOException { + final String json = "{\"type\":\"string\",\"format\":\"duration\"}"; + final Schema p = m.readValue(json, Schema.class); + assertEquals(p.getType(), "string"); + assertEquals(p.getFormat(), "duration"); + assertEquals(p.getClass(), DurationSchema.class); + JsonAssert.assertJsonEquals(m, m.writeValueAsString(p), json); + } + + @Test(description = "it should serialize a DateTimeLocalSchema") + public void serializeDateTimeLocalSchema() throws IOException { + final DateTimeLocalSchema p = new DateTimeLocalSchema(); + final String json = "{\"type\":\"string\",\"format\":\"date-time-local\"}"; + JsonAssert.assertJsonEquals(m, m.writeValueAsString(p), json); + } + + @Test(description = "it should deserialize a DateTimeLocalSchema") + public void deserializeDateTimeLocalSchema() throws IOException { + final String json = "{\"type\":\"string\",\"format\":\"date-time-local\"}"; + final Schema p = m.readValue(json, Schema.class); + assertEquals(p.getType(), "string"); + assertEquals(p.getFormat(), "date-time-local"); + assertEquals(p.getClass(), DateTimeLocalSchema.class); + JsonAssert.assertJsonEquals(m, m.writeValueAsString(p), json); + } + + @Test(description = "it should serialize a TimeLocalSchema") + public void serializeTimeLocalSchema() throws IOException { + final TimeLocalSchema p = new TimeLocalSchema(); + final String json = "{\"type\":\"string\",\"format\":\"time-local\"}"; + JsonAssert.assertJsonEquals(m, m.writeValueAsString(p), json); + } + + @Test(description = "it should deserialize a TimeLocalSchema") + public void deserializeTimeLocalSchema() throws IOException { + final String json = "{\"type\":\"string\",\"format\":\"time-local\"}"; + final Schema p = m.readValue(json, Schema.class); + assertEquals(p.getType(), "string"); + assertEquals(p.getFormat(), "time-local"); + assertEquals(p.getClass(), TimeLocalSchema.class); + JsonAssert.assertJsonEquals(m, m.writeValueAsString(p), json); + } + @Test(description = "it should serialize a DoubleProperty") public void serializeDoubleProperty() throws IOException { final NumberSchema p = new NumberSchema() diff --git a/modules/swagger-models/src/main/java/io/swagger/v3/oas/models/media/DateTimeLocalSchema.java b/modules/swagger-models/src/main/java/io/swagger/v3/oas/models/media/DateTimeLocalSchema.java new file mode 100644 index 0000000000..6983360152 --- /dev/null +++ b/modules/swagger-models/src/main/java/io/swagger/v3/oas/models/media/DateTimeLocalSchema.java @@ -0,0 +1,66 @@ +package io.swagger.v3.oas.models.media; + +import java.time.LocalDateTime; +import java.util.Objects; + +/** + * DateTimeLocalSchema + */ +public class DateTimeLocalSchema extends Schema { + + public DateTimeLocalSchema() { + super("string", "date-time-local"); + } + + @Override + public DateTimeLocalSchema type(String type) { + super.setType(type); + return this; + } + + @Override + public DateTimeLocalSchema format(String format) { + super.setFormat(format); + return this; + } + + @Override + protected LocalDateTime cast(Object value) { + if (value != null) { + try { + if (value instanceof LocalDateTime) { + return (LocalDateTime) value; + } else if (value instanceof String) { + return LocalDateTime.parse((String) value); + } + } catch (Exception e) { + } + } + return null; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + return super.equals(o); + } + + @Override + public int hashCode() { + return Objects.hash(super.hashCode()); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class DateTimeLocalSchema {\n"); + sb.append(" ").append(toIndentedString(super.toString())).append("\n"); + sb.append("}"); + return sb.toString(); + } +} diff --git a/modules/swagger-models/src/main/java/io/swagger/v3/oas/models/media/DurationSchema.java b/modules/swagger-models/src/main/java/io/swagger/v3/oas/models/media/DurationSchema.java new file mode 100644 index 0000000000..3c031568bb --- /dev/null +++ b/modules/swagger-models/src/main/java/io/swagger/v3/oas/models/media/DurationSchema.java @@ -0,0 +1,66 @@ +package io.swagger.v3.oas.models.media; + +import java.time.Duration; +import java.util.Objects; + +/** + * DurationSchema + */ +public class DurationSchema extends Schema { + + public DurationSchema() { + super("string", "duration"); + } + + @Override + public DurationSchema type(String type) { + super.setType(type); + return this; + } + + @Override + public DurationSchema format(String format) { + super.setFormat(format); + return this; + } + + @Override + protected Duration cast(Object value) { + if (value != null) { + try { + if (value instanceof Duration) { + return (Duration) value; + } else if (value instanceof String) { + return Duration.parse((String) value); + } + } catch (Exception e) { + } + } + return null; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + return super.equals(o); + } + + @Override + public int hashCode() { + return Objects.hash(super.hashCode()); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class DurationSchema {\n"); + sb.append(" ").append(toIndentedString(super.toString())).append("\n"); + sb.append("}"); + return sb.toString(); + } +} diff --git a/modules/swagger-models/src/main/java/io/swagger/v3/oas/models/media/TimeLocalSchema.java b/modules/swagger-models/src/main/java/io/swagger/v3/oas/models/media/TimeLocalSchema.java new file mode 100644 index 0000000000..08a9738d4d --- /dev/null +++ b/modules/swagger-models/src/main/java/io/swagger/v3/oas/models/media/TimeLocalSchema.java @@ -0,0 +1,66 @@ +package io.swagger.v3.oas.models.media; + +import java.time.LocalTime; +import java.util.Objects; + +/** + * TimeLocalSchema + */ +public class TimeLocalSchema extends Schema { + + public TimeLocalSchema() { + super("string", "time-local"); + } + + @Override + public TimeLocalSchema type(String type) { + super.setType(type); + return this; + } + + @Override + public TimeLocalSchema format(String format) { + super.setFormat(format); + return this; + } + + @Override + protected LocalTime cast(Object value) { + if (value != null) { + try { + if (value instanceof LocalTime) { + return (LocalTime) value; + } else if (value instanceof String) { + return LocalTime.parse((String) value); + } + } catch (Exception e) { + } + } + return null; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + return super.equals(o); + } + + @Override + public int hashCode() { + return Objects.hash(super.hashCode()); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class TimeLocalSchema {\n"); + sb.append(" ").append(toIndentedString(super.toString())).append("\n"); + sb.append("}"); + return sb.toString(); + } +} diff --git a/modules/swagger-models/src/main/java/io/swagger/v3/oas/models/media/TimeSchema.java b/modules/swagger-models/src/main/java/io/swagger/v3/oas/models/media/TimeSchema.java new file mode 100644 index 0000000000..c14a5251ed --- /dev/null +++ b/modules/swagger-models/src/main/java/io/swagger/v3/oas/models/media/TimeSchema.java @@ -0,0 +1,66 @@ +package io.swagger.v3.oas.models.media; + +import java.time.OffsetTime; +import java.util.Objects; + +/** + * TimeSchema + */ +public class TimeSchema extends Schema { + + public TimeSchema() { + super("string", "time"); + } + + @Override + public TimeSchema type(String type) { + super.setType(type); + return this; + } + + @Override + public TimeSchema format(String format) { + super.setFormat(format); + return this; + } + + @Override + protected OffsetTime cast(Object value) { + if (value != null) { + try { + if (value instanceof OffsetTime) { + return (OffsetTime) value; + } else if (value instanceof String) { + return OffsetTime.parse((String) value); + } + } catch (Exception e) { + } + } + return null; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + return super.equals(o); + } + + @Override + public int hashCode() { + return Objects.hash(super.hashCode()); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class TimeSchema {\n"); + sb.append(" ").append(toIndentedString(super.toString())).append("\n"); + sb.append("}"); + return sb.toString(); + } +}