diff --git a/samples/client/petstore/java/.openapi-generator/FILES b/samples/client/petstore/java/.openapi-generator/FILES index 29549829b4e..853d8eed662 100644 --- a/samples/client/petstore/java/.openapi-generator/FILES +++ b/samples/client/petstore/java/.openapi-generator/FILES @@ -2,13 +2,18 @@ README.md pom.xml src/main/java/org/openapijsonschematools/configurations/JsonSchemaKeywordFlags.java src/main/java/org/openapijsonschematools/configurations/SchemaConfiguration.java +src/main/java/org/openapijsonschematools/schemas/AnyTypeSchema.java src/main/java/org/openapijsonschematools/schemas/CustomIsoparser.java src/main/java/org/openapijsonschematools/schemas/PathToSchemasMap.java +src/main/java/org/openapijsonschematools/schemas/PathToTypeMap.java +src/main/java/org/openapijsonschematools/schemas/Schema.java src/main/java/org/openapijsonschematools/schemas/SchemaValidator.java +src/main/java/org/openapijsonschematools/schemas/UnsetAnyTypeSchema.java src/main/java/org/openapijsonschematools/schemas/ValidationMetadata.java src/main/java/org/openapijsonschematools/schemas/validators/KeywordValidator.java src/main/java/org/openapijsonschematools/schemas/validators/TypeValidator.java src/test/java/org/openapijsonschematools/configurations/JsonSchemaKeywordFlagsTest.java +src/test/java/org/openapijsonschematools/schemas/AnyTypeSchemaTest.java src/test/java/org/openapijsonschematools/schemas/CustomIsoparserTest.java src/test/java/org/openapijsonschematools/schemas/SchemaValidatorTest.java src/test/java/org/openapijsonschematools/schemas/validators/TypeValidatorTest.java diff --git a/samples/client/petstore/java/src/main/java/org/openapijsonschematools/schemas/AnyTypeSchema.java b/samples/client/petstore/java/src/main/java/org/openapijsonschematools/schemas/AnyTypeSchema.java new file mode 100644 index 00000000000..500739c8cb4 --- /dev/null +++ b/samples/client/petstore/java/src/main/java/org/openapijsonschematools/schemas/AnyTypeSchema.java @@ -0,0 +1,54 @@ +package org.openapijsonschematools.schemas; + +import org.openapijsonschematools.configurations.SchemaConfiguration; + +import java.time.LocalDate; +import java.time.ZonedDateTime; +import java.util.List; +import java.util.Map; + +record AnyTypeSchema() implements Schema { + public static AnyTypeSchema withDefaults() { + return new AnyTypeSchema(); + } + + public static Void validate(Void arg, SchemaConfiguration configuration) { + return Schema.validate(AnyTypeSchema.class, arg, configuration); + } + + public static Boolean validate(Boolean arg, SchemaConfiguration configuration) { + return Schema.validate(AnyTypeSchema.class, arg, configuration); + } + + public static Integer validate(Integer arg, SchemaConfiguration configuration) { + return Schema.validate(AnyTypeSchema.class, arg, configuration); + } + + public static Float validate(Float arg, SchemaConfiguration configuration) { + return Schema.validate(AnyTypeSchema.class, arg, configuration); + } + + public static Double validate(Double arg, SchemaConfiguration configuration) { + return Schema.validate(AnyTypeSchema.class, arg, configuration); + } + + public static String validate(String arg, SchemaConfiguration configuration) { + return Schema.validate(AnyTypeSchema.class, arg, configuration); + } + + public static String validate(ZonedDateTime arg, SchemaConfiguration configuration) { + return Schema.validate(AnyTypeSchema.class, arg, configuration); + } + + public static String validate(LocalDate arg, SchemaConfiguration configuration) { + return Schema.validate(AnyTypeSchema.class, arg, configuration); + } + + public static T validate(T arg, SchemaConfiguration configuration) { + return Schema.validate(AnyTypeSchema.class, arg, configuration); + } + + public static U validate(U arg, SchemaConfiguration configuration) { + return Schema.validate(AnyTypeSchema.class, arg, configuration); + } +} diff --git a/samples/client/petstore/java/src/main/java/org/openapijsonschematools/schemas/PathToSchemasMap.java b/samples/client/petstore/java/src/main/java/org/openapijsonschematools/schemas/PathToSchemasMap.java index 915813b0f6f..9c9b9465998 100644 --- a/samples/client/petstore/java/src/main/java/org/openapijsonschematools/schemas/PathToSchemasMap.java +++ b/samples/client/petstore/java/src/main/java/org/openapijsonschematools/schemas/PathToSchemasMap.java @@ -1,8 +1,20 @@ package org.openapijsonschematools.schemas; -import java.util.HashMap; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; -public class PathToSchemasMap extends HashMap, Map, Void>> { +public class PathToSchemasMap extends LinkedHashMap, LinkedHashMap, Void>> { + + public void update(PathToSchemasMap other) { + for (Map.Entry, LinkedHashMap, Void>> entry: other.entrySet()) { + List pathToItem = entry.getKey(); + LinkedHashMap, Void> otherSchemas = entry.getValue(); + if (containsKey(pathToItem)) { + get(pathToItem).putAll(otherSchemas); + } else { + put(pathToItem, otherSchemas); + } + } + } } \ No newline at end of file diff --git a/samples/client/petstore/java/src/main/java/org/openapijsonschematools/schemas/PathToTypeMap.java b/samples/client/petstore/java/src/main/java/org/openapijsonschematools/schemas/PathToTypeMap.java new file mode 100644 index 00000000000..9aba997ddaa --- /dev/null +++ b/samples/client/petstore/java/src/main/java/org/openapijsonschematools/schemas/PathToTypeMap.java @@ -0,0 +1,8 @@ +package org.openapijsonschematools.schemas; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class PathToTypeMap extends HashMap, Class> { +} \ No newline at end of file diff --git a/samples/client/petstore/java/src/main/java/org/openapijsonschematools/schemas/Schema.java b/samples/client/petstore/java/src/main/java/org/openapijsonschematools/schemas/Schema.java new file mode 100644 index 00000000000..749e82074aa --- /dev/null +++ b/samples/client/petstore/java/src/main/java/org/openapijsonschematools/schemas/Schema.java @@ -0,0 +1,228 @@ +package org.openapijsonschematools.schemas; + +import org.openapijsonschematools.configurations.JsonSchemaKeywordFlags; +import org.openapijsonschematools.configurations.SchemaConfiguration; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.math.BigDecimal; +import java.time.LocalDate; +import java.time.ZonedDateTime; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Set; + +public interface Schema extends SchemaValidator { + private static Object castToAllowedTypes(Object arg, List pathToItem, PathToTypeMap pathToType) { + if (arg == null) { + pathToType.put(pathToItem, Void.class); + return null; + } else if (arg instanceof String) { + pathToType.put(pathToItem, String.class); + return arg; + } else if (arg instanceof Map) { + pathToType.put(pathToItem, Map.class); + LinkedHashMap argFixed = new LinkedHashMap<>(); + for (Map.Entry entry: ((Map) arg).entrySet()) { + String key = (String) entry.getKey(); + Object val = entry.getValue(); + List newPathToItem = new ArrayList<>(pathToItem); + newPathToItem.add(key); + Object fixedVal = castToAllowedTypes(val, newPathToItem, pathToType); + argFixed.put(key, fixedVal); + } + return argFixed; + } else if (arg instanceof Boolean) { + pathToType.put(pathToItem, Boolean.class); + return arg; + } else if (arg instanceof Integer) { + pathToType.put(pathToItem, Integer.class); + return arg; + } else if (arg instanceof Float) { + pathToType.put(pathToItem, Float.class); + return arg; + } else if (arg instanceof Double) { + pathToType.put(pathToItem, Double.class); + return arg; + } else if (arg instanceof BigDecimal) { + pathToType.put(pathToItem, BigDecimal.class); + return arg; + } else if (arg instanceof List) { + pathToType.put(pathToItem, List.class); + List argFixed = new ArrayList<>(); + int i =0; + for (Object item: ((List) arg).toArray()) { + List newPathToItem = new ArrayList<>(pathToItem); + newPathToItem.add(i); + Object fixedVal = castToAllowedTypes(item, newPathToItem, pathToType); + argFixed.add(fixedVal); + i += 1; + } + return argFixed; + } else if (arg instanceof ZonedDateTime) { + pathToType.put(pathToItem, String.class); + return arg.toString(); + } else if (arg instanceof LocalDate) { + pathToType.put(pathToItem, String.class); + return arg.toString(); + } else { + Class argClass = arg.getClass(); + throw new RuntimeException("Invalid type passed in got input="+arg+" type="+argClass); + } + } + + private static PathToSchemasMap getPathToSchemas(Class cls, Object arg, ValidationMetadata validationMetadata, PathToTypeMap pathToType) { + PathToSchemasMap pathToSchemasMap = new PathToSchemasMap(); + if (validationMetadata.validationRanEarlier(cls)) { + // todo add deeper validated schemas + } else { + PathToSchemasMap otherPathToSchemas = SchemaValidator.validate(cls, arg, validationMetadata); + pathToSchemasMap.update(otherPathToSchemas); + for (LinkedHashMap, Void> schemas: pathToSchemasMap.values()) { + Class firstSchema = schemas.entrySet().iterator().next().getKey(); + schemas.clear(); + schemas.put(firstSchema, null); + } + Set> missingPaths = new HashSet<>(pathToType.keySet()); + missingPaths.removeAll(pathToSchemasMap.keySet()); + if (!missingPaths.isEmpty()) { + LinkedHashMap, Void> unsetAnyTypeSchema = new LinkedHashMap<>(); + unsetAnyTypeSchema.put(UnsetAnyTypeSchema.class, null); + for (List pathToItem: missingPaths) { + pathToSchemasMap.put(pathToItem, unsetAnyTypeSchema); + } + } + } + return pathToSchemasMap; + } + + private static LinkedHashMap getProperties(Object arg, List pathToItem, PathToSchemasMap pathToSchemas) { + LinkedHashMap properties = new LinkedHashMap<>(); + Map castArg = (Map) arg; + for(Map.Entry entry: castArg.entrySet()) { + String propertyName = entry.getKey(); + List propertyPathToItem = new ArrayList<>(pathToItem); + propertyPathToItem.add(propertyName); + Class propertyClass = (Class) pathToSchemas.get(propertyPathToItem).entrySet().iterator().next().getKey(); + Object value = entry.getValue(); + Object castValue = getNewInstance(propertyClass, value, propertyPathToItem, pathToSchemas); + properties.put(propertyName, castValue); + } + return properties; + } + + private static List getItems(Object arg, List pathToItem, PathToSchemasMap pathToSchemas) { + ArrayList items = new ArrayList<>(); + List castItems = (List) arg; + int i = 0; + for (Object item: castItems) { + List itemPathToItem = new ArrayList<>(pathToItem); + itemPathToItem.add(i); + Class itemClass = (Class) pathToSchemas.get(itemPathToItem).entrySet().iterator().next().getKey(); + Object castItem = getNewInstance(itemClass, item, itemPathToItem, pathToSchemas); + items.add(castItem); + i += 1; + } + return items; + } + + private static Map, Class> getTypeToOutputClass(Class cls) { + try { + // This must be implemented in Schemas that are generics as a static method + Method method = cls.getMethod("typeToOutputClass"); + Map, Class> typeToOutputClass = (Map, Class>) method.invoke(null); + return typeToOutputClass; + } catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException e) { + return null; + } + } + + private static Object getNewInstance(Class cls, Object arg, List pathToItem, PathToSchemasMap pathToSchemas) { + Object usedArg; + if (arg instanceof Map) { + usedArg = getProperties(arg, pathToItem, pathToSchemas); + } else if (arg instanceof List) { + usedArg = getItems(arg, pathToItem, pathToSchemas); + } else { + // str, int, float, boolean, null, FileIO, bytes + return arg; + } + Class argType = arg.getClass(); + Map, Class> typeToOutputClass = getTypeToOutputClass(cls); + if (typeToOutputClass == null) { + return usedArg; + } + Class outputClass = typeToOutputClass.get(argType); + // TODO add class instantiation here + return null; + } + + static Void validate(Class cls, Void arg, SchemaConfiguration configuration) { + return (Void) validateObject(cls, arg, configuration); + } + + static Boolean validate(Class cls, Boolean arg, SchemaConfiguration configuration) { + return (Boolean) validateObject(cls, arg, configuration); + } + + static Integer validate(Class cls, Integer arg, SchemaConfiguration configuration) { + return (Integer) validateObject(cls, arg, configuration); + } + + static Float validate(Class cls, Float arg, SchemaConfiguration configuration) { + return (Float) validateObject(cls, arg, configuration); + } + + static Double validate(Class cls, Double arg, SchemaConfiguration configuration) { + return (Double) validateObject(cls, arg, configuration); + } + + static String validate(Class cls, String arg, SchemaConfiguration configuration) { + return (String) validateObject(cls, arg, configuration); + } + + static String validate(Class cls, ZonedDateTime arg, SchemaConfiguration configuration) { + return (String) validateObject(cls, arg, configuration); + } + + static String validate(Class cls, LocalDate arg, SchemaConfiguration configuration) { + return (String) validateObject(cls, arg, configuration); + } + + static T validate(Class cls, T arg, SchemaConfiguration configuration) { + return (T) validateObject(cls, arg, configuration); + } + + static U validate(Class cls, U arg, SchemaConfiguration configuration) { + return (U) validateObject(cls, arg, configuration); + } + + // todo add bytes and FileIO + + private static Object validateObject(Class cls, Object arg, SchemaConfiguration configuration) { + Class castCls = (Class) cls; + if (arg instanceof Map || arg instanceof List) { + // todo don't run validation if the instance is one of the class generic types + } + PathToTypeMap pathToType = new PathToTypeMap(); + List pathToItem = new ArrayList<>(); + pathToItem.add("args[0]"); + Object castArg = castToAllowedTypes(arg, pathToItem, pathToType); + SchemaConfiguration usedConfiguration = Objects.requireNonNullElseGet(configuration, () -> new SchemaConfiguration(JsonSchemaKeywordFlags.ofNone())); + PathToSchemasMap validatedPathToSchemas = new PathToSchemasMap(); + ValidationMetadata validationMetadata = new ValidationMetadata( + pathToItem, + usedConfiguration, + validatedPathToSchemas, + new LinkedHashSet<>() + ); + PathToSchemasMap pathToSchemasMap = getPathToSchemas(castCls, castArg, validationMetadata, pathToType); + return getNewInstance(castCls, castArg, validationMetadata.pathToItem(), pathToSchemasMap); + } + +} \ No newline at end of file diff --git a/samples/client/petstore/java/src/main/java/org/openapijsonschematools/schemas/SchemaValidator.java b/samples/client/petstore/java/src/main/java/org/openapijsonschematools/schemas/SchemaValidator.java index 8439ee4164f..38553c8afcb 100644 --- a/samples/client/petstore/java/src/main/java/org/openapijsonschematools/schemas/SchemaValidator.java +++ b/samples/client/petstore/java/src/main/java/org/openapijsonschematools/schemas/SchemaValidator.java @@ -7,30 +7,43 @@ import java.lang.reflect.RecordComponent; import java.util.HashMap; import java.util.LinkedHashSet; +import java.util.LinkedHashMap; +import java.util.List; import java.util.Map; public interface SchemaValidator { - static final HashMap keywordToValidator = new HashMap(){{ + HashMap keywordToValidator = new HashMap(){{ put("type", new TypeValidator()); }}; - static PathToSchemasMap _validate( - SchemaValidator schema, + + static PathToSchemasMap validate( + Class schemaCls, Object arg, ValidationMetadata validationMetadata - ) throws InvocationTargetException, IllegalAccessException { + ) { HashMap fieldsToValues = new HashMap<>(); + SchemaValidator schema; + try { + schema = (SchemaValidator) schemaCls.getMethod("withDefaults").invoke(null); + } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) { + throw new RuntimeException(e); + } LinkedHashSet disabledKeywords = validationMetadata.configuration().disabledKeywordFlags().getKeywords(); - Class schemaCls = (Class) schema.getClass(); RecordComponent[] recordComponents = schemaCls.getRecordComponents(); for (RecordComponent recordComponent : recordComponents) { String fieldName = recordComponent.getName(); if (disabledKeywords.contains(fieldName)) { continue; } - Object value = recordComponent.getAccessor().invoke(schema); - fieldsToValues.put(fieldName, value); + try { + Object value = recordComponent.getAccessor().invoke(schema); + fieldsToValues.put(fieldName, value); + } catch (IllegalAccessException | InvocationTargetException e) { + throw new RuntimeException(e); + } } PathToSchemasMap pathToSchemas = new PathToSchemasMap(); + Class castSchemaCls = (Class) schemaCls; for (Map.Entry entry: fieldsToValues.entrySet()) { String jsonKeyword = entry.getKey(); Object value = entry.getValue(); @@ -39,10 +52,27 @@ static PathToSchemasMap _validate( arg, value, null, - schemaCls, + castSchemaCls, validationMetadata ); + if (otherPathToSchemas == null) { + continue; + } + pathToSchemas.update(otherPathToSchemas); + } + Class baseClass; + if (arg == null) { + baseClass = Void.class; + } else { + baseClass = arg.getClass(); } + List pathToItem = validationMetadata.pathToItem(); + if (!pathToSchemas.containsKey(pathToItem)) { + pathToSchemas.put(validationMetadata.pathToItem(), new LinkedHashMap<>()); + } + pathToSchemas.get(pathToItem).put(baseClass, null); + pathToSchemas.get(pathToItem).put(schemaCls, null); + return pathToSchemas; } } diff --git a/samples/client/petstore/java/src/main/java/org/openapijsonschematools/schemas/UnsetAnyTypeSchema.java b/samples/client/petstore/java/src/main/java/org/openapijsonschematools/schemas/UnsetAnyTypeSchema.java new file mode 100644 index 00000000000..f5120344836 --- /dev/null +++ b/samples/client/petstore/java/src/main/java/org/openapijsonschematools/schemas/UnsetAnyTypeSchema.java @@ -0,0 +1,54 @@ +package org.openapijsonschematools.schemas; + +import org.openapijsonschematools.configurations.SchemaConfiguration; + +import java.time.LocalDate; +import java.time.ZonedDateTime; +import java.util.List; +import java.util.Map; + +record UnsetAnyTypeSchema() implements Schema { + static UnsetAnyTypeSchema withDefaults() { + return new UnsetAnyTypeSchema(); + } + + static Void validate(Void arg, SchemaConfiguration configuration) { + return Schema.validate(UnsetAnyTypeSchema.class, arg, configuration); + } + + static Boolean validate(Boolean arg, SchemaConfiguration configuration) { + return Schema.validate(UnsetAnyTypeSchema.class, arg, configuration); + } + + static Integer validate(Integer arg, SchemaConfiguration configuration) { + return Schema.validate(UnsetAnyTypeSchema.class, arg, configuration); + } + + static Float validate(Float arg, SchemaConfiguration configuration) { + return Schema.validate(UnsetAnyTypeSchema.class, arg, configuration); + } + + static Double validate(Double arg, SchemaConfiguration configuration) { + return Schema.validate(UnsetAnyTypeSchema.class, arg, configuration); + } + + static String validate(String arg, SchemaConfiguration configuration) { + return Schema.validate(UnsetAnyTypeSchema.class, arg, configuration); + } + + static String validate(ZonedDateTime arg, SchemaConfiguration configuration) { + return Schema.validate(UnsetAnyTypeSchema.class, arg, configuration); + } + + static String validate(LocalDate arg, SchemaConfiguration configuration) { + return Schema.validate(UnsetAnyTypeSchema.class, arg, configuration); + } + + static T validate(T arg, SchemaConfiguration configuration) { + return Schema.validate(UnsetAnyTypeSchema.class, arg, configuration); + } + + static U validate(U arg, SchemaConfiguration configuration) { + return Schema.validate(UnsetAnyTypeSchema.class, arg, configuration); + } +} diff --git a/samples/client/petstore/java/src/test/java/org/openapijsonschematools/schemas/AnyTypeSchemaTest.java b/samples/client/petstore/java/src/test/java/org/openapijsonschematools/schemas/AnyTypeSchemaTest.java new file mode 100644 index 00000000000..57333589db3 --- /dev/null +++ b/samples/client/petstore/java/src/test/java/org/openapijsonschematools/schemas/AnyTypeSchemaTest.java @@ -0,0 +1,87 @@ +package org.openapijsonschematools.schemas; + +import org.junit.Assert; +import org.junit.Test; +import org.openapijsonschematools.configurations.JsonSchemaKeywordFlags; +import org.openapijsonschematools.configurations.SchemaConfiguration; + +import java.time.LocalDate; +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.util.ArrayList; +import java.util.LinkedHashMap; + +public class AnyTypeSchemaTest { + static final SchemaConfiguration configuration = new SchemaConfiguration(JsonSchemaKeywordFlags.ofNone()); + + @Test + public void testValidateNull() { + Void validatedValue = AnyTypeSchema.validate((Void) null, configuration); + Assert.assertNull(validatedValue); + } + + @Test + public void testValidateBoolean() { + Boolean trueValue = AnyTypeSchema.validate(Boolean.TRUE, configuration); + Assert.assertEquals(trueValue, Boolean.TRUE); + + Boolean falseValue = AnyTypeSchema.validate(Boolean.FALSE, configuration); + Assert.assertEquals(falseValue, Boolean.FALSE); + } + + @Test + public void testValidateInt() { + Integer validatedValue = AnyTypeSchema.validate(1, configuration); + Assert.assertEquals(validatedValue, Integer.valueOf(1)); + } + + @Test + public void testValidateFloat() { + Float validatedValue = AnyTypeSchema.validate(3.14f, configuration); + Assert.assertEquals(validatedValue, Float.valueOf(3.14f)); + } + + @Test + public void testValidateDouble() { + Double validatedValue = AnyTypeSchema.validate(70.6458763d, configuration); + Assert.assertEquals(validatedValue, Double.valueOf(70.6458763d)); + } + + @Test + public void testValidateString() { + String validatedValue = AnyTypeSchema.validate("a", configuration); + Assert.assertEquals(validatedValue, "a"); + } + + @Test + public void testValidateZonedDateTime() { + String validatedValue = AnyTypeSchema.validate(ZonedDateTime.of(2017, 7, 21, 17, 32, 28, 0, ZoneId.of("Z")), configuration); + Assert.assertEquals(validatedValue, "2017-07-21T17:32:28Z"); + } + + @Test + public void testValidateLocalDate() { + String validatedValue = AnyTypeSchema.validate(LocalDate.of(2017, 7, 21), configuration); + Assert.assertEquals(validatedValue, "2017-07-21"); + } + + @Test + public void testValidateMap() { + LinkedHashMap inMap = new LinkedHashMap<>(); + inMap.put("today", LocalDate.of(2017, 7, 21)); + LinkedHashMap validatedValue = AnyTypeSchema.validate(inMap, configuration); + LinkedHashMap outMap = new LinkedHashMap<>(); + outMap.put("today", "2017-07-21"); + Assert.assertEquals(validatedValue, outMap); + } + + @Test + public void testValidateList() { + ArrayList inList = new ArrayList<>(); + inList.add(LocalDate.of(2017, 7, 21)); + ArrayList validatedValue = AnyTypeSchema.validate(inList, configuration); + ArrayList outList = new ArrayList<>(); + outList.add( "2017-07-21"); + Assert.assertEquals(validatedValue, outList); + } +} diff --git a/samples/client/petstore/java/src/test/java/org/openapijsonschematools/schemas/SchemaValidatorTest.java b/samples/client/petstore/java/src/test/java/org/openapijsonschematools/schemas/SchemaValidatorTest.java index 22b6678d00a..6e29f940ca4 100644 --- a/samples/client/petstore/java/src/test/java/org/openapijsonschematools/schemas/SchemaValidatorTest.java +++ b/samples/client/petstore/java/src/test/java/org/openapijsonschematools/schemas/SchemaValidatorTest.java @@ -1,48 +1,46 @@ package org.openapijsonschematools.schemas; +import org.junit.Assert; import org.junit.Test; import org.openapijsonschematools.configurations.JsonSchemaKeywordFlags; import org.openapijsonschematools.configurations.SchemaConfiguration; import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; +import java.util.LinkedHashMap; import java.util.LinkedHashSet; +import java.util.List; record SomeSchema(LinkedHashSet> type) implements SchemaValidator { - static SomeSchema withDefaults() { + public static SomeSchema withDefaults() { LinkedHashSet> type = new LinkedHashSet<>(); type.add(String.class); return new SomeSchema(type); } - - static PathToSchemasMap _validate( - Object arg, - ValidationMetadata validationMetadata - ) throws InstantiationException, IllegalAccessException, InvocationTargetException, NoSuchMethodException { - return SchemaValidator._validate( - SomeSchema.withDefaults(), - arg, - validationMetadata - ); - } - - } public class SchemaValidatorTest { - @Test - public void testValidateSucceeds() throws InstantiationException, IllegalAccessException, InvocationTargetException, NoSuchMethodException { + public void testValidateSucceeds() { + List pathToItem = new ArrayList<>(); + pathToItem.add("args[0"); ValidationMetadata validationMetadata = new ValidationMetadata( - new ArrayList<>(), + pathToItem, new SchemaConfiguration(JsonSchemaKeywordFlags.ofNone()), new PathToSchemasMap(), new LinkedHashSet<>() ); - SomeSchema._validate( + PathToSchemasMap pathToSchemas = SchemaValidator.validate( + SomeSchema.class, "hi", validationMetadata ); + PathToSchemasMap expectedPathToSchemas = new PathToSchemasMap(); + LinkedHashMap, Void> validatedClasses = new LinkedHashMap<>(); + validatedClasses.put(SomeSchema.class, null); + validatedClasses.put(String.class, null); + expectedPathToSchemas.put(pathToItem, validatedClasses); + Assert.assertEquals(pathToSchemas, expectedPathToSchemas); } } diff --git a/src/main/java/org/openapijsonschematools/codegen/generators/JavaClientGenerator.java b/src/main/java/org/openapijsonschematools/codegen/generators/JavaClientGenerator.java index c0352494b52..1c38d25ef51 100644 --- a/src/main/java/org/openapijsonschematools/codegen/generators/JavaClientGenerator.java +++ b/src/main/java/org/openapijsonschematools/codegen/generators/JavaClientGenerator.java @@ -261,26 +261,47 @@ protected String testPackagePath() { public void processOpts() { HashMap schemaDocs = new HashMap<>(); additionalProperties.put(CodegenConstants.PACKAGE_NAME, packageName); + supportingFiles.add(new SupportingFile( + "src/main/java/org/openapitools/schemas/AnyTypeSchema.hbs", + packagePath() + File.separatorChar + "schemas", + "AnyTypeSchema.java")); supportingFiles.add(new SupportingFile( "src/main/java/org/openapitools/schemas/CustomIsoparser.hbs", packagePath() + File.separatorChar + "schemas", "CustomIsoparser.java")); supportingFiles.add(new SupportingFile( - "src/test/java/org/openapitools/schemas/CustomIsoparserTest.hbs", - testPackagePath() + File.separatorChar + "schemas", - "CustomIsoparserTest.java")); + "src/main/java/org/openapitools/schemas/PathToSchemasMap.hbs", + packagePath() + File.separatorChar + "schemas", + "PathToSchemasMap.java")); supportingFiles.add(new SupportingFile( - "src/main/java/org/openapitools/schemas/ValidationMetadata.hbs", + "src/main/java/org/openapitools/schemas/PathToTypeMap.hbs", packagePath() + File.separatorChar + "schemas", - "ValidationMetadata.java")); + "PathToTypeMap.java")); supportingFiles.add(new SupportingFile( - "src/main/java/org/openapitools/schemas/PathToSchemasMap.hbs", + "src/main/java/org/openapitools/schemas/Schema.hbs", packagePath() + File.separatorChar + "schemas", - "PathToSchemasMap.java")); + "Schema.java")); supportingFiles.add(new SupportingFile( "src/main/java/org/openapitools/schemas/SchemaValidator.hbs", packagePath() + File.separatorChar + "schemas", "SchemaValidator.java")); + supportingFiles.add(new SupportingFile( + "src/main/java/org/openapitools/schemas/UnsetAnyTypeSchema.hbs", + packagePath() + File.separatorChar + "schemas", + "UnsetAnyTypeSchema.java")); + supportingFiles.add(new SupportingFile( + "src/main/java/org/openapitools/schemas/ValidationMetadata.hbs", + packagePath() + File.separatorChar + "schemas", + "ValidationMetadata.java")); + // tests + supportingFiles.add(new SupportingFile( + "src/test/java/org/openapitools/schemas/AnyTypeSchemaTest.hbs", + testPackagePath() + File.separatorChar + "schemas", + "AnyTypeSchemaTest.java")); + supportingFiles.add(new SupportingFile( + "src/test/java/org/openapitools/schemas/CustomIsoparserTest.hbs", + testPackagePath() + File.separatorChar + "schemas", + "CustomIsoparserTest.java")); supportingFiles.add(new SupportingFile( "src/test/java/org/openapitools/schemas/SchemaValidatorTest.hbs", testPackagePath() + File.separatorChar + "schemas", @@ -291,11 +312,11 @@ public void processOpts() { "src/main/java/org/openapitools/schemas/validators/KeywordValidator.hbs", packagePath() + File.separatorChar + "schemas" + File.separatorChar + "validators", "KeywordValidator.java")); - // type supportingFiles.add(new SupportingFile( "src/main/java/org/openapitools/schemas/validators/TypeValidator.hbs", packagePath() + File.separatorChar + "schemas" + File.separatorChar + "validators", "TypeValidator.java")); + // tests supportingFiles.add(new SupportingFile( "src/test/java/org/openapitools/schemas/validators/TypeValidatorTest.hbs", testPackagePath() + File.separatorChar + "schemas" + File.separatorChar + "validators", diff --git a/src/main/resources/java/src/main/java/org/openapitools/schemas/AnyTypeSchema.hbs b/src/main/resources/java/src/main/java/org/openapitools/schemas/AnyTypeSchema.hbs new file mode 100644 index 00000000000..e328a03b7fb --- /dev/null +++ b/src/main/resources/java/src/main/java/org/openapitools/schemas/AnyTypeSchema.hbs @@ -0,0 +1,54 @@ +package {{{packageName}}}.schemas; + +import {{{packageName}}}.configurations.SchemaConfiguration; + +import java.time.LocalDate; +import java.time.ZonedDateTime; +import java.util.List; +import java.util.Map; + +record AnyTypeSchema() implements Schema { + public static AnyTypeSchema withDefaults() { + return new AnyTypeSchema(); + } + + public static Void validate(Void arg, SchemaConfiguration configuration) { + return Schema.validate(AnyTypeSchema.class, arg, configuration); + } + + public static Boolean validate(Boolean arg, SchemaConfiguration configuration) { + return Schema.validate(AnyTypeSchema.class, arg, configuration); + } + + public static Integer validate(Integer arg, SchemaConfiguration configuration) { + return Schema.validate(AnyTypeSchema.class, arg, configuration); + } + + public static Float validate(Float arg, SchemaConfiguration configuration) { + return Schema.validate(AnyTypeSchema.class, arg, configuration); + } + + public static Double validate(Double arg, SchemaConfiguration configuration) { + return Schema.validate(AnyTypeSchema.class, arg, configuration); + } + + public static String validate(String arg, SchemaConfiguration configuration) { + return Schema.validate(AnyTypeSchema.class, arg, configuration); + } + + public static String validate(ZonedDateTime arg, SchemaConfiguration configuration) { + return Schema.validate(AnyTypeSchema.class, arg, configuration); + } + + public static String validate(LocalDate arg, SchemaConfiguration configuration) { + return Schema.validate(AnyTypeSchema.class, arg, configuration); + } + + public static T validate(T arg, SchemaConfiguration configuration) { + return Schema.validate(AnyTypeSchema.class, arg, configuration); + } + + public static U validate(U arg, SchemaConfiguration configuration) { + return Schema.validate(AnyTypeSchema.class, arg, configuration); + } +} diff --git a/src/main/resources/java/src/main/java/org/openapitools/schemas/PathToSchemasMap.hbs b/src/main/resources/java/src/main/java/org/openapitools/schemas/PathToSchemasMap.hbs index cc8526fb767..7aa27723b42 100644 --- a/src/main/resources/java/src/main/java/org/openapitools/schemas/PathToSchemasMap.hbs +++ b/src/main/resources/java/src/main/java/org/openapitools/schemas/PathToSchemasMap.hbs @@ -1,8 +1,20 @@ package {{{packageName}}}.schemas; -import java.util.HashMap; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; -public class PathToSchemasMap extends HashMap, Map, Void>> { +public class PathToSchemasMap extends LinkedHashMap, LinkedHashMap, Void>> { + + public void update(PathToSchemasMap other) { + for (Map.Entry, LinkedHashMap, Void>> entry: other.entrySet()) { + List pathToItem = entry.getKey(); + LinkedHashMap, Void> otherSchemas = entry.getValue(); + if (containsKey(pathToItem)) { + get(pathToItem).putAll(otherSchemas); + } else { + put(pathToItem, otherSchemas); + } + } + } } \ No newline at end of file diff --git a/src/main/resources/java/src/main/java/org/openapitools/schemas/PathToTypeMap.hbs b/src/main/resources/java/src/main/java/org/openapitools/schemas/PathToTypeMap.hbs new file mode 100644 index 00000000000..d16c7f5fe9c --- /dev/null +++ b/src/main/resources/java/src/main/java/org/openapitools/schemas/PathToTypeMap.hbs @@ -0,0 +1,8 @@ +package {{{packageName}}}.schemas; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class PathToTypeMap extends HashMap, Class> { +} \ No newline at end of file diff --git a/src/main/resources/java/src/main/java/org/openapitools/schemas/Schema.hbs b/src/main/resources/java/src/main/java/org/openapitools/schemas/Schema.hbs new file mode 100644 index 00000000000..f37ba5a2fb2 --- /dev/null +++ b/src/main/resources/java/src/main/java/org/openapitools/schemas/Schema.hbs @@ -0,0 +1,228 @@ +package {{{packageName}}}.schemas; + +import {{{packageName}}}.configurations.JsonSchemaKeywordFlags; +import {{{packageName}}}.configurations.SchemaConfiguration; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.math.BigDecimal; +import java.time.LocalDate; +import java.time.ZonedDateTime; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Set; + +public interface Schema extends SchemaValidator { + private static Object castToAllowedTypes(Object arg, List pathToItem, PathToTypeMap pathToType) { + if (arg == null) { + pathToType.put(pathToItem, Void.class); + return null; + } else if (arg instanceof String) { + pathToType.put(pathToItem, String.class); + return arg; + } else if (arg instanceof Map) { + pathToType.put(pathToItem, Map.class); + LinkedHashMap argFixed = new LinkedHashMap<>(); + for (Map.Entry entry: ((Map) arg).entrySet()) { + String key = (String) entry.getKey(); + Object val = entry.getValue(); + List newPathToItem = new ArrayList<>(pathToItem); + newPathToItem.add(key); + Object fixedVal = castToAllowedTypes(val, newPathToItem, pathToType); + argFixed.put(key, fixedVal); + } + return argFixed; + } else if (arg instanceof Boolean) { + pathToType.put(pathToItem, Boolean.class); + return arg; + } else if (arg instanceof Integer) { + pathToType.put(pathToItem, Integer.class); + return arg; + } else if (arg instanceof Float) { + pathToType.put(pathToItem, Float.class); + return arg; + } else if (arg instanceof Double) { + pathToType.put(pathToItem, Double.class); + return arg; + } else if (arg instanceof BigDecimal) { + pathToType.put(pathToItem, BigDecimal.class); + return arg; + } else if (arg instanceof List) { + pathToType.put(pathToItem, List.class); + List argFixed = new ArrayList<>(); + int i =0; + for (Object item: ((List) arg).toArray()) { + List newPathToItem = new ArrayList<>(pathToItem); + newPathToItem.add(i); + Object fixedVal = castToAllowedTypes(item, newPathToItem, pathToType); + argFixed.add(fixedVal); + i += 1; + } + return argFixed; + } else if (arg instanceof ZonedDateTime) { + pathToType.put(pathToItem, String.class); + return arg.toString(); + } else if (arg instanceof LocalDate) { + pathToType.put(pathToItem, String.class); + return arg.toString(); + } else { + Class argClass = arg.getClass(); + throw new RuntimeException("Invalid type passed in got input="+arg+" type="+argClass); + } + } + + private static PathToSchemasMap getPathToSchemas(Class cls, Object arg, ValidationMetadata validationMetadata, PathToTypeMap pathToType) { + PathToSchemasMap pathToSchemasMap = new PathToSchemasMap(); + if (validationMetadata.validationRanEarlier(cls)) { + // todo add deeper validated schemas + } else { + PathToSchemasMap otherPathToSchemas = SchemaValidator.validate(cls, arg, validationMetadata); + pathToSchemasMap.update(otherPathToSchemas); + for (LinkedHashMap, Void> schemas: pathToSchemasMap.values()) { + Class firstSchema = schemas.entrySet().iterator().next().getKey(); + schemas.clear(); + schemas.put(firstSchema, null); + } + Set> missingPaths = new HashSet<>(pathToType.keySet()); + missingPaths.removeAll(pathToSchemasMap.keySet()); + if (!missingPaths.isEmpty()) { + LinkedHashMap, Void> unsetAnyTypeSchema = new LinkedHashMap<>(); + unsetAnyTypeSchema.put(UnsetAnyTypeSchema.class, null); + for (List pathToItem: missingPaths) { + pathToSchemasMap.put(pathToItem, unsetAnyTypeSchema); + } + } + } + return pathToSchemasMap; + } + + private static LinkedHashMap getProperties(Object arg, List pathToItem, PathToSchemasMap pathToSchemas) { + LinkedHashMap properties = new LinkedHashMap<>(); + Map castArg = (Map) arg; + for(Map.Entry entry: castArg.entrySet()) { + String propertyName = entry.getKey(); + List propertyPathToItem = new ArrayList<>(pathToItem); + propertyPathToItem.add(propertyName); + Class propertyClass = (Class) pathToSchemas.get(propertyPathToItem).entrySet().iterator().next().getKey(); + Object value = entry.getValue(); + Object castValue = getNewInstance(propertyClass, value, propertyPathToItem, pathToSchemas); + properties.put(propertyName, castValue); + } + return properties; + } + + private static List getItems(Object arg, List pathToItem, PathToSchemasMap pathToSchemas) { + ArrayList items = new ArrayList<>(); + List castItems = (List) arg; + int i = 0; + for (Object item: castItems) { + List itemPathToItem = new ArrayList<>(pathToItem); + itemPathToItem.add(i); + Class itemClass = (Class) pathToSchemas.get(itemPathToItem).entrySet().iterator().next().getKey(); + Object castItem = getNewInstance(itemClass, item, itemPathToItem, pathToSchemas); + items.add(castItem); + i += 1; + } + return items; + } + + private static Map, Class> getTypeToOutputClass(Class cls) { + try { + // This must be implemented in Schemas that are generics as a static method + Method method = cls.getMethod("typeToOutputClass"); + Map, Class> typeToOutputClass = (Map, Class>) method.invoke(null); + return typeToOutputClass; + } catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException e) { + return null; + } + } + + private static Object getNewInstance(Class cls, Object arg, List pathToItem, PathToSchemasMap pathToSchemas) { + Object usedArg; + if (arg instanceof Map) { + usedArg = getProperties(arg, pathToItem, pathToSchemas); + } else if (arg instanceof List) { + usedArg = getItems(arg, pathToItem, pathToSchemas); + } else { + // str, int, float, boolean, null, FileIO, bytes + return arg; + } + Class argType = arg.getClass(); + Map, Class> typeToOutputClass = getTypeToOutputClass(cls); + if (typeToOutputClass == null) { + return usedArg; + } + Class outputClass = typeToOutputClass.get(argType); + // TODO add class instantiation here + return null; + } + + static Void validate(Class cls, Void arg, SchemaConfiguration configuration) { + return (Void) validateObject(cls, arg, configuration); + } + + static Boolean validate(Class cls, Boolean arg, SchemaConfiguration configuration) { + return (Boolean) validateObject(cls, arg, configuration); + } + + static Integer validate(Class cls, Integer arg, SchemaConfiguration configuration) { + return (Integer) validateObject(cls, arg, configuration); + } + + static Float validate(Class cls, Float arg, SchemaConfiguration configuration) { + return (Float) validateObject(cls, arg, configuration); + } + + static Double validate(Class cls, Double arg, SchemaConfiguration configuration) { + return (Double) validateObject(cls, arg, configuration); + } + + static String validate(Class cls, String arg, SchemaConfiguration configuration) { + return (String) validateObject(cls, arg, configuration); + } + + static String validate(Class cls, ZonedDateTime arg, SchemaConfiguration configuration) { + return (String) validateObject(cls, arg, configuration); + } + + static String validate(Class cls, LocalDate arg, SchemaConfiguration configuration) { + return (String) validateObject(cls, arg, configuration); + } + + static T validate(Class cls, T arg, SchemaConfiguration configuration) { + return (T) validateObject(cls, arg, configuration); + } + + static U validate(Class cls, U arg, SchemaConfiguration configuration) { + return (U) validateObject(cls, arg, configuration); + } + + // todo add bytes and FileIO + + private static Object validateObject(Class cls, Object arg, SchemaConfiguration configuration) { + Class castCls = (Class) cls; + if (arg instanceof Map || arg instanceof List) { + // todo don't run validation if the instance is one of the class generic types + } + PathToTypeMap pathToType = new PathToTypeMap(); + List pathToItem = new ArrayList<>(); + pathToItem.add("args[0]"); + Object castArg = castToAllowedTypes(arg, pathToItem, pathToType); + SchemaConfiguration usedConfiguration = Objects.requireNonNullElseGet(configuration, () -> new SchemaConfiguration(JsonSchemaKeywordFlags.ofNone())); + PathToSchemasMap validatedPathToSchemas = new PathToSchemasMap(); + ValidationMetadata validationMetadata = new ValidationMetadata( + pathToItem, + usedConfiguration, + validatedPathToSchemas, + new LinkedHashSet<>() + ); + PathToSchemasMap pathToSchemasMap = getPathToSchemas(castCls, castArg, validationMetadata, pathToType); + return getNewInstance(castCls, castArg, validationMetadata.pathToItem(), pathToSchemasMap); + } + +} \ No newline at end of file diff --git a/src/main/resources/java/src/main/java/org/openapitools/schemas/SchemaValidator.hbs b/src/main/resources/java/src/main/java/org/openapitools/schemas/SchemaValidator.hbs index 97d417734a1..0a8e7e3e484 100644 --- a/src/main/resources/java/src/main/java/org/openapitools/schemas/SchemaValidator.hbs +++ b/src/main/resources/java/src/main/java/org/openapitools/schemas/SchemaValidator.hbs @@ -1,36 +1,49 @@ -package org.openapijsonschematools.schemas; +package {{{packageName}}}.schemas; -import org.openapijsonschematools.schemas.validators.KeywordValidator; -import org.openapijsonschematools.schemas.validators.TypeValidator; +import {{{packageName}}}.schemas.validators.KeywordValidator; +import {{{packageName}}}.schemas.validators.TypeValidator; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.RecordComponent; import java.util.HashMap; import java.util.LinkedHashSet; +import java.util.LinkedHashMap; +import java.util.List; import java.util.Map; public interface SchemaValidator { - static final HashMap keywordToValidator = new HashMap()\{{ + HashMap keywordToValidator = new HashMap()\{{ put("type", new TypeValidator()); }}; - static PathToSchemasMap _validate( - SchemaValidator schema, + + static PathToSchemasMap validate( + Class schemaCls, Object arg, ValidationMetadata validationMetadata - ) throws InvocationTargetException, IllegalAccessException { + ) { HashMap fieldsToValues = new HashMap<>(); + SchemaValidator schema; + try { + schema = (SchemaValidator) schemaCls.getMethod("withDefaults").invoke(null); + } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) { + throw new RuntimeException(e); + } LinkedHashSet disabledKeywords = validationMetadata.configuration().disabledKeywordFlags().getKeywords(); - Class schemaCls = (Class) schema.getClass(); RecordComponent[] recordComponents = schemaCls.getRecordComponents(); for (RecordComponent recordComponent : recordComponents) { String fieldName = recordComponent.getName(); if (disabledKeywords.contains(fieldName)) { continue; } - Object value = recordComponent.getAccessor().invoke(schema); - fieldsToValues.put(fieldName, value); + try { + Object value = recordComponent.getAccessor().invoke(schema); + fieldsToValues.put(fieldName, value); + } catch (IllegalAccessException | InvocationTargetException e) { + throw new RuntimeException(e); + } } PathToSchemasMap pathToSchemas = new PathToSchemasMap(); + Class castSchemaCls = (Class) schemaCls; for (Map.Entry entry: fieldsToValues.entrySet()) { String jsonKeyword = entry.getKey(); Object value = entry.getValue(); @@ -39,10 +52,27 @@ public interface SchemaValidator { arg, value, null, - schemaCls, + castSchemaCls, validationMetadata ); + if (otherPathToSchemas == null) { + continue; + } + pathToSchemas.update(otherPathToSchemas); + } + Class baseClass; + if (arg == null) { + baseClass = Void.class; + } else { + baseClass = arg.getClass(); } + List pathToItem = validationMetadata.pathToItem(); + if (!pathToSchemas.containsKey(pathToItem)) { + pathToSchemas.put(validationMetadata.pathToItem(), new LinkedHashMap<>()); + } + pathToSchemas.get(pathToItem).put(baseClass, null); + pathToSchemas.get(pathToItem).put(schemaCls, null); + return pathToSchemas; } } diff --git a/src/main/resources/java/src/main/java/org/openapitools/schemas/UnsetAnyTypeSchema.hbs b/src/main/resources/java/src/main/java/org/openapitools/schemas/UnsetAnyTypeSchema.hbs new file mode 100644 index 00000000000..284c888645d --- /dev/null +++ b/src/main/resources/java/src/main/java/org/openapitools/schemas/UnsetAnyTypeSchema.hbs @@ -0,0 +1,54 @@ +package {{{packageName}}}.schemas; + +import {{{packageName}}}.configurations.SchemaConfiguration; + +import java.time.LocalDate; +import java.time.ZonedDateTime; +import java.util.List; +import java.util.Map; + +record UnsetAnyTypeSchema() implements Schema { + static UnsetAnyTypeSchema withDefaults() { + return new UnsetAnyTypeSchema(); + } + + static Void validate(Void arg, SchemaConfiguration configuration) { + return Schema.validate(UnsetAnyTypeSchema.class, arg, configuration); + } + + static Boolean validate(Boolean arg, SchemaConfiguration configuration) { + return Schema.validate(UnsetAnyTypeSchema.class, arg, configuration); + } + + static Integer validate(Integer arg, SchemaConfiguration configuration) { + return Schema.validate(UnsetAnyTypeSchema.class, arg, configuration); + } + + static Float validate(Float arg, SchemaConfiguration configuration) { + return Schema.validate(UnsetAnyTypeSchema.class, arg, configuration); + } + + static Double validate(Double arg, SchemaConfiguration configuration) { + return Schema.validate(UnsetAnyTypeSchema.class, arg, configuration); + } + + static String validate(String arg, SchemaConfiguration configuration) { + return Schema.validate(UnsetAnyTypeSchema.class, arg, configuration); + } + + static String validate(ZonedDateTime arg, SchemaConfiguration configuration) { + return Schema.validate(UnsetAnyTypeSchema.class, arg, configuration); + } + + static String validate(LocalDate arg, SchemaConfiguration configuration) { + return Schema.validate(UnsetAnyTypeSchema.class, arg, configuration); + } + + static T validate(T arg, SchemaConfiguration configuration) { + return Schema.validate(UnsetAnyTypeSchema.class, arg, configuration); + } + + static U validate(U arg, SchemaConfiguration configuration) { + return Schema.validate(UnsetAnyTypeSchema.class, arg, configuration); + } +} diff --git a/src/main/resources/java/src/test/java/org/openapitools/schemas/AnyTypeSchemaTest.hbs b/src/main/resources/java/src/test/java/org/openapitools/schemas/AnyTypeSchemaTest.hbs new file mode 100644 index 00000000000..43dfa422668 --- /dev/null +++ b/src/main/resources/java/src/test/java/org/openapitools/schemas/AnyTypeSchemaTest.hbs @@ -0,0 +1,87 @@ +package {{{packageName}}}.schemas; + +import org.junit.Assert; +import org.junit.Test; +import {{{packageName}}}.configurations.JsonSchemaKeywordFlags; +import {{{packageName}}}.configurations.SchemaConfiguration; + +import java.time.LocalDate; +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.util.ArrayList; +import java.util.LinkedHashMap; + +public class AnyTypeSchemaTest { + static final SchemaConfiguration configuration = new SchemaConfiguration(JsonSchemaKeywordFlags.ofNone()); + + @Test + public void testValidateNull() { + Void validatedValue = AnyTypeSchema.validate((Void) null, configuration); + Assert.assertNull(validatedValue); + } + + @Test + public void testValidateBoolean() { + Boolean trueValue = AnyTypeSchema.validate(Boolean.TRUE, configuration); + Assert.assertEquals(trueValue, Boolean.TRUE); + + Boolean falseValue = AnyTypeSchema.validate(Boolean.FALSE, configuration); + Assert.assertEquals(falseValue, Boolean.FALSE); + } + + @Test + public void testValidateInt() { + Integer validatedValue = AnyTypeSchema.validate(1, configuration); + Assert.assertEquals(validatedValue, Integer.valueOf(1)); + } + + @Test + public void testValidateFloat() { + Float validatedValue = AnyTypeSchema.validate(3.14f, configuration); + Assert.assertEquals(validatedValue, Float.valueOf(3.14f)); + } + + @Test + public void testValidateDouble() { + Double validatedValue = AnyTypeSchema.validate(70.6458763d, configuration); + Assert.assertEquals(validatedValue, Double.valueOf(70.6458763d)); + } + + @Test + public void testValidateString() { + String validatedValue = AnyTypeSchema.validate("a", configuration); + Assert.assertEquals(validatedValue, "a"); + } + + @Test + public void testValidateZonedDateTime() { + String validatedValue = AnyTypeSchema.validate(ZonedDateTime.of(2017, 7, 21, 17, 32, 28, 0, ZoneId.of("Z")), configuration); + Assert.assertEquals(validatedValue, "2017-07-21T17:32:28Z"); + } + + @Test + public void testValidateLocalDate() { + String validatedValue = AnyTypeSchema.validate(LocalDate.of(2017, 7, 21), configuration); + Assert.assertEquals(validatedValue, "2017-07-21"); + } + + @Test + public void testValidateMap() { + LinkedHashMap inMap = new LinkedHashMap<>(); + inMap.put("today", LocalDate.of(2017, 7, 21)); + LinkedHashMap validatedValue = AnyTypeSchema.validate(inMap, configuration); + LinkedHashMap outMap = new LinkedHashMap<>(); + outMap.put("today", "2017-07-21"); + Assert.assertEquals(validatedValue, outMap); + } + + @Test + public void testValidateList() { + ArrayList inList = new ArrayList<>(); + inList.add(LocalDate.of(2017, 7, 21)); + ArrayList validatedValue = AnyTypeSchema.validate(inList, configuration); + ArrayList outList = new ArrayList<>(); + outList.add( "2017-07-21"); + Assert.assertEquals(validatedValue, outList); + } +} diff --git a/src/main/resources/java/src/test/java/org/openapitools/schemas/SchemaValidatorTest.hbs b/src/main/resources/java/src/test/java/org/openapitools/schemas/SchemaValidatorTest.hbs index aa454c4545f..7df95828781 100644 --- a/src/main/resources/java/src/test/java/org/openapitools/schemas/SchemaValidatorTest.hbs +++ b/src/main/resources/java/src/test/java/org/openapitools/schemas/SchemaValidatorTest.hbs @@ -1,48 +1,46 @@ package {{{packageName}}}.schemas; +import org.junit.Assert; import org.junit.Test; import {{{packageName}}}.configurations.JsonSchemaKeywordFlags; import {{{packageName}}}.configurations.SchemaConfiguration; import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; +import java.util.LinkedHashMap; import java.util.LinkedHashSet; +import java.util.List; record SomeSchema(LinkedHashSet> type) implements SchemaValidator { - static SomeSchema withDefaults() { + public static SomeSchema withDefaults() { LinkedHashSet> type = new LinkedHashSet<>(); type.add(String.class); return new SomeSchema(type); } - - static PathToSchemasMap _validate( - Object arg, - ValidationMetadata validationMetadata - ) throws InstantiationException, IllegalAccessException, InvocationTargetException, NoSuchMethodException { - return SchemaValidator._validate( - SomeSchema.withDefaults(), - arg, - validationMetadata - ); - } - - } public class SchemaValidatorTest { - @Test - public void testValidateSucceeds() throws InstantiationException, IllegalAccessException, InvocationTargetException, NoSuchMethodException { + public void testValidateSucceeds() { + List pathToItem = new ArrayList<>(); + pathToItem.add("args[0"); ValidationMetadata validationMetadata = new ValidationMetadata( - new ArrayList<>(), + pathToItem, new SchemaConfiguration(JsonSchemaKeywordFlags.ofNone()), new PathToSchemasMap(), new LinkedHashSet<>() ); - SomeSchema._validate( + PathToSchemasMap pathToSchemas = SchemaValidator.validate( + SomeSchema.class, "hi", validationMetadata ); + PathToSchemasMap expectedPathToSchemas = new PathToSchemasMap(); + LinkedHashMap, Void> validatedClasses = new LinkedHashMap<>(); + validatedClasses.put(SomeSchema.class, null); + validatedClasses.put(String.class, null); + expectedPathToSchemas.put(pathToItem, validatedClasses); + Assert.assertEquals(pathToSchemas, expectedPathToSchemas); } }