Skip to content
This repository was archived by the owner on Dec 25, 2024. It is now read-only.

Commit fbd7844

Browse files
authored
Java, adds output class instantiation for Map and List use cases (#285)
* Adds test of array instantiation with an output class * Fixes format test error * Adds method checking to see if there is a list output class * Adds test of type object schema that has a map output type
1 parent a152f5a commit fbd7844

File tree

8 files changed

+292
-62
lines changed

8 files changed

+292
-62
lines changed

samples/client/petstore/java/src/main/java/org/openapijsonschematools/schemas/Schema.java

+27-29
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ private static Object castToAllowedTypes(Object arg, List<Object> pathToItem, Pa
3636
Object fixedVal = castToAllowedTypes(val, newPathToItem, pathToType);
3737
argFixed.put(key, fixedVal);
3838
}
39-
return new FrozenMap(argFixed);
39+
return new FrozenMap<>(argFixed);
4040
} else if (arg instanceof Boolean) {
4141
pathToType.put(pathToItem, Boolean.class);
4242
return arg;
@@ -63,7 +63,7 @@ private static Object castToAllowedTypes(Object arg, List<Object> pathToItem, Pa
6363
argFixed.add(fixedVal);
6464
i += 1;
6565
}
66-
return new FrozenList(argFixed);
66+
return new FrozenList<>(argFixed);
6767
} else if (arg instanceof ZonedDateTime) {
6868
pathToType.put(pathToItem, String.class);
6969
return arg.toString();
@@ -104,7 +104,7 @@ private static PathToSchemasMap getPathToSchemas(Class<Schema> cls, Object arg,
104104
return pathToSchemasMap;
105105
}
106106

107-
private static LinkedHashMap<String, Object> getProperties(Object arg, List<Object> pathToItem, PathToSchemasMap pathToSchemas) {
107+
private static FrozenMap<String, Object> getProperties(Object arg, List<Object> pathToItem, PathToSchemasMap pathToSchemas) {
108108
LinkedHashMap<String, Object> properties = new LinkedHashMap<>();
109109
Map<String, Object> castArg = (Map<String, Object>) arg;
110110
for(Map.Entry<String, Object> entry: castArg.entrySet()) {
@@ -116,7 +116,7 @@ private static LinkedHashMap<String, Object> getProperties(Object arg, List<Obje
116116
Object castValue = getNewInstance(propertyClass, value, propertyPathToItem, pathToSchemas);
117117
properties.put(propertyName, castValue);
118118
}
119-
return new FrozenMap(properties);
119+
return new FrozenMap<>(properties);
120120
}
121121

122122
private static FrozenList<Object> getItems(Object arg, List<Object> pathToItem, PathToSchemasMap pathToSchemas) {
@@ -131,37 +131,35 @@ private static FrozenList<Object> getItems(Object arg, List<Object> pathToItem,
131131
items.add(castItem);
132132
i += 1;
133133
}
134-
return new FrozenList(items);
135-
}
136-
137-
private static Map<Class<?>, Class<?>> getTypeToOutputClass(Class<?> cls) {
138-
try {
139-
// This must be implemented in Schemas that are generics as a static method
140-
Method method = cls.getMethod("typeToOutputClass");
141-
Map<Class<?>, Class<?>> typeToOutputClass = (Map<Class<?>, Class<?>>) method.invoke(null);
142-
return typeToOutputClass;
143-
} catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException e) {
144-
return null;
145-
}
134+
return new FrozenList<>(items);
146135
}
147136

148137
private static Object getNewInstance(Class<Schema> cls, Object arg, List<Object> pathToItem, PathToSchemasMap pathToSchemas) {
149-
Object usedArg;
138+
if (!(arg instanceof Map || arg instanceof List)) {
139+
// str, int, float, boolean, null, FileIO, bytes
140+
return arg;
141+
}
150142
if (arg instanceof Map) {
151-
usedArg = getProperties(arg, pathToItem, pathToSchemas);
143+
FrozenMap<String, Object> usedArg = getProperties(arg, pathToItem, pathToSchemas);
144+
try {
145+
Method method = cls.getMethod("getMapOutputInstance", FrozenMap.class);
146+
return method.invoke(null, usedArg);
147+
} catch (NoSuchMethodException e) {
148+
return usedArg;
149+
} catch (InvocationTargetException | IllegalAccessException e) {
150+
throw new RuntimeException(e);
151+
}
152152
} else if (arg instanceof List) {
153-
usedArg = getItems(arg, pathToItem, pathToSchemas);
154-
} else {
155-
// str, int, float, boolean, null, FileIO, bytes
156-
return arg;
157-
}
158-
Class<?> argType = arg.getClass();
159-
Map<Class<?>, Class<?>> typeToOutputClass = getTypeToOutputClass(cls);
160-
if (typeToOutputClass == null) {
161-
return usedArg;
153+
FrozenList<Object> usedArg = getItems(arg, pathToItem, pathToSchemas);
154+
try {
155+
Method method = cls.getMethod("getListOutputInstance", FrozenList.class);
156+
return method.invoke(null, usedArg);
157+
} catch (NoSuchMethodException e) {
158+
return usedArg;
159+
} catch (InvocationTargetException | IllegalAccessException e) {
160+
throw new RuntimeException(e);
161+
}
162162
}
163-
Class<?> outputClass = typeToOutputClass.get(argType);
164-
// TODO add class instantiation here
165163
return null;
166164
}
167165

samples/client/petstore/java/src/main/java/org/openapijsonschematools/schemas/validators/FormatValidator.java

+7-2
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,13 @@ private Void validateNumericFormat(Number arg, String format, ValidationMetadata
2525
// there is a json schema test where 1.0 validates as an integer
2626
BigInteger intArg;
2727
if (arg instanceof Float || arg instanceof Double) {
28-
Double doubleArg = (Double) arg;
29-
if (Math.floor((Double) arg) != doubleArg) {
28+
Double doubleArg;
29+
if (arg instanceof Float) {
30+
doubleArg = arg.doubleValue();
31+
} else {
32+
doubleArg = (Double) arg;
33+
}
34+
if (Math.floor(doubleArg) != doubleArg) {
3035
throw new RuntimeException(
3136
"Invalid non-integer value " + arg + " for format " + format + " at " + validationMetadata.pathToItem()
3237
);

samples/client/petstore/java/src/test/java/org/openapijsonschematools/schemas/ArrayTypeSchemaTest.java

+54
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,35 @@ public static FrozenList<Object> validate(List<Object> arg, SchemaConfiguration
2424
}
2525
}
2626

27+
class ArrayWithOutputClsSchemaList extends FrozenList<String> {
28+
ArrayWithOutputClsSchemaList(FrozenList<? extends String> m) {
29+
super(m);
30+
}
31+
32+
public static ArrayWithOutputClsSchemaList of(List<Object> arg, SchemaConfiguration configuration) {
33+
return ArrayWithOutputClsSchema.validate(arg, configuration);
34+
}
35+
}
36+
37+
record ArrayWithOutputClsSchema(LinkedHashSet<Class<?>> type, Class<?> items) implements Schema {
38+
public static ArrayWithOutputClsSchema withDefaults() {
39+
LinkedHashSet<Class<?>> type = new LinkedHashSet<>();
40+
// can't use ImmutableList because it does not allow null values in entries
41+
// can't use Collections.unmodifiableList because Collections.UnmodifiableList is not public + extensible
42+
type.add(FrozenList.class);
43+
Class<?> items = StringSchema.class;
44+
return new ArrayWithOutputClsSchema(type, items);
45+
}
46+
47+
public static ArrayWithOutputClsSchemaList getListOutputInstance(FrozenList<? extends String> arg) {
48+
return new ArrayWithOutputClsSchemaList(arg);
49+
}
50+
51+
public static ArrayWithOutputClsSchemaList validate(List<Object> arg, SchemaConfiguration configuration) {
52+
return Schema.validate(ArrayWithOutputClsSchema.class, arg, configuration);
53+
}
54+
}
55+
2756
public class ArrayTypeSchemaTest {
2857
static final SchemaConfiguration configuration = new SchemaConfiguration(JsonSchemaKeywordFlags.ofNone());
2958

@@ -58,4 +87,29 @@ public void testValidateArrayWithItemsSchema() {
5887
finalInList, configuration
5988
));
6089
}
90+
91+
@Test
92+
public void testValidateArrayWithOutputClsSchema() {
93+
// map with only item works
94+
List<Object> inList = new ArrayList<>();
95+
inList.add("abc");
96+
ArrayWithOutputClsSchemaList validatedValue = ArrayWithOutputClsSchema.validate(inList, configuration);
97+
List<Object> outList = new ArrayList<>();
98+
outList.add("abc");
99+
Assert.assertEquals(validatedValue, outList);
100+
101+
// map with no items works
102+
inList = new ArrayList<>();
103+
validatedValue = ArrayWithOutputClsSchema.validate(inList, configuration);
104+
outList = new ArrayList<>();
105+
Assert.assertEquals(validatedValue, outList);
106+
107+
// invalid prop type fails
108+
inList = new ArrayList<>();
109+
inList.add(1);
110+
List<Object> finalInList = inList;
111+
Assert.assertThrows(RuntimeException.class, () -> ArrayWithOutputClsSchema.validate(
112+
finalInList, configuration
113+
));
114+
}
61115
}

samples/client/petstore/java/src/test/java/org/openapijsonschematools/schemas/ObjectTypeSchemaTest.java

+58
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,35 @@ public static FrozenMap<String, Object> validate(Map<String, Object> arg, Schema
5757
}
5858
}
5959

60+
class ObjectWithOutputTypeSchemaMap extends FrozenMap<String, Object> {
61+
ObjectWithOutputTypeSchemaMap(FrozenMap<? extends String, ?> m) {
62+
super(m);
63+
}
64+
65+
public static ObjectWithOutputTypeSchemaMap of(Map<String, Object> arg, SchemaConfiguration configuration) {
66+
return ObjectWithOutputTypeSchema.validate(arg, configuration);
67+
}
68+
}
69+
70+
71+
record ObjectWithOutputTypeSchema(LinkedHashSet<Class<?>> type, LinkedHashMap<String, Class<?>> properties) implements Schema {
72+
public static ObjectWithOutputTypeSchema withDefaults() {
73+
LinkedHashSet<Class<?>> type = new LinkedHashSet<>();
74+
type.add(FrozenMap.class);
75+
LinkedHashMap<String, Class<?>> properties = new LinkedHashMap<>();
76+
properties.put("someString", StringSchema.class);
77+
return new ObjectWithOutputTypeSchema(type, properties);
78+
}
79+
80+
public static ObjectWithOutputTypeSchemaMap getMapOutputInstance(FrozenMap<? extends String, ?> arg) {
81+
return new ObjectWithOutputTypeSchemaMap(arg);
82+
}
83+
84+
public static ObjectWithOutputTypeSchemaMap validate(Map<String, Object> arg, SchemaConfiguration configuration) {
85+
return Schema.validate(ObjectWithOutputTypeSchema.class, arg, configuration);
86+
}
87+
}
88+
6089
public class ObjectTypeSchemaTest {
6190
static final SchemaConfiguration configuration = new SchemaConfiguration(JsonSchemaKeywordFlags.ofNone());
6291

@@ -161,4 +190,33 @@ public void testValidateObjectWithPropsAndAddpropsSchema() {
161190
invalidAddpropMap, configuration
162191
));
163192
}
193+
194+
@Test
195+
public void testValidateObjectWithOutputTypeSchema() {
196+
// map with only property works
197+
Map<String, Object> inMap = new LinkedHashMap<>();
198+
inMap.put("someString", "abc");
199+
ObjectWithOutputTypeSchemaMap validatedValue = ObjectWithOutputTypeSchema.validate(inMap, configuration);
200+
LinkedHashMap<String, String> outMap = new LinkedHashMap<>();
201+
outMap.put("someString", "abc");
202+
Assert.assertEquals(validatedValue, outMap);
203+
204+
// map with additional unvalidated property works
205+
inMap = new LinkedHashMap<>();
206+
inMap.put("someString", "abc");
207+
inMap.put("someOtherString", "def");
208+
validatedValue = ObjectWithOutputTypeSchema.validate(inMap, configuration);
209+
outMap = new LinkedHashMap<>();
210+
outMap.put("someString", "abc");
211+
outMap.put("someOtherString", "def");
212+
Assert.assertEquals(validatedValue, outMap);
213+
214+
// invalid prop type fails
215+
inMap = new LinkedHashMap<>();
216+
inMap.put("someString", 1);
217+
Map<String, Object> finalInMap = inMap;
218+
Assert.assertThrows(RuntimeException.class, () -> ObjectWithOutputTypeSchema.validate(
219+
finalInMap, configuration
220+
));
221+
}
164222
}

src/main/resources/java/src/main/java/org/openapitools/schemas/Schema.hbs

+27-29
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ public interface Schema extends SchemaValidator {
3636
Object fixedVal = castToAllowedTypes(val, newPathToItem, pathToType);
3737
argFixed.put(key, fixedVal);
3838
}
39-
return new FrozenMap(argFixed);
39+
return new FrozenMap<>(argFixed);
4040
} else if (arg instanceof Boolean) {
4141
pathToType.put(pathToItem, Boolean.class);
4242
return arg;
@@ -63,7 +63,7 @@ public interface Schema extends SchemaValidator {
6363
argFixed.add(fixedVal);
6464
i += 1;
6565
}
66-
return new FrozenList(argFixed);
66+
return new FrozenList<>(argFixed);
6767
} else if (arg instanceof ZonedDateTime) {
6868
pathToType.put(pathToItem, String.class);
6969
return arg.toString();
@@ -104,7 +104,7 @@ public interface Schema extends SchemaValidator {
104104
return pathToSchemasMap;
105105
}
106106

107-
private static LinkedHashMap<String, Object> getProperties(Object arg, List<Object> pathToItem, PathToSchemasMap pathToSchemas) {
107+
private static FrozenMap<String, Object> getProperties(Object arg, List<Object> pathToItem, PathToSchemasMap pathToSchemas) {
108108
LinkedHashMap<String, Object> properties = new LinkedHashMap<>();
109109
Map<String, Object> castArg = (Map<String, Object>) arg;
110110
for(Map.Entry<String, Object> entry: castArg.entrySet()) {
@@ -116,7 +116,7 @@ public interface Schema extends SchemaValidator {
116116
Object castValue = getNewInstance(propertyClass, value, propertyPathToItem, pathToSchemas);
117117
properties.put(propertyName, castValue);
118118
}
119-
return new FrozenMap(properties);
119+
return new FrozenMap<>(properties);
120120
}
121121

122122
private static FrozenList<Object> getItems(Object arg, List<Object> pathToItem, PathToSchemasMap pathToSchemas) {
@@ -131,37 +131,35 @@ public interface Schema extends SchemaValidator {
131131
items.add(castItem);
132132
i += 1;
133133
}
134-
return new FrozenList(items);
135-
}
136-
137-
private static Map<Class<?>, Class<?>> getTypeToOutputClass(Class<?> cls) {
138-
try {
139-
// This must be implemented in Schemas that are generics as a static method
140-
Method method = cls.getMethod("typeToOutputClass");
141-
Map<Class<?>, Class<?>> typeToOutputClass = (Map<Class<?>, Class<?>>) method.invoke(null);
142-
return typeToOutputClass;
143-
} catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException e) {
144-
return null;
145-
}
134+
return new FrozenList<>(items);
146135
}
147136

148137
private static Object getNewInstance(Class<Schema> cls, Object arg, List<Object> pathToItem, PathToSchemasMap pathToSchemas) {
149-
Object usedArg;
138+
if (!(arg instanceof Map || arg instanceof List)) {
139+
// str, int, float, boolean, null, FileIO, bytes
140+
return arg;
141+
}
150142
if (arg instanceof Map) {
151-
usedArg = getProperties(arg, pathToItem, pathToSchemas);
143+
FrozenMap<String, Object> usedArg = getProperties(arg, pathToItem, pathToSchemas);
144+
try {
145+
Method method = cls.getMethod("getMapOutputInstance", FrozenMap.class);
146+
return method.invoke(null, usedArg);
147+
} catch (NoSuchMethodException e) {
148+
return usedArg;
149+
} catch (InvocationTargetException | IllegalAccessException e) {
150+
throw new RuntimeException(e);
151+
}
152152
} else if (arg instanceof List) {
153-
usedArg = getItems(arg, pathToItem, pathToSchemas);
154-
} else {
155-
// str, int, float, boolean, null, FileIO, bytes
156-
return arg;
157-
}
158-
Class<?> argType = arg.getClass();
159-
Map<Class<?>, Class<?>> typeToOutputClass = getTypeToOutputClass(cls);
160-
if (typeToOutputClass == null) {
161-
return usedArg;
153+
FrozenList<Object> usedArg = getItems(arg, pathToItem, pathToSchemas);
154+
try {
155+
Method method = cls.getMethod("getListOutputInstance", FrozenList.class);
156+
return method.invoke(null, usedArg);
157+
} catch (NoSuchMethodException e) {
158+
return usedArg;
159+
} catch (InvocationTargetException | IllegalAccessException e) {
160+
throw new RuntimeException(e);
161+
}
162162
}
163-
Class<?> outputClass = typeToOutputClass.get(argType);
164-
// TODO add class instantiation here
165163
return null;
166164
}
167165

src/main/resources/java/src/main/java/org/openapitools/schemas/validators/FormatValidator.hbs

+7-2
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,13 @@ public class FormatValidator implements KeywordValidator {
2525
// there is a json schema test where 1.0 validates as an integer
2626
BigInteger intArg;
2727
if (arg instanceof Float || arg instanceof Double) {
28-
Double doubleArg = (Double) arg;
29-
if (Math.floor((Double) arg) != doubleArg) {
28+
Double doubleArg;
29+
if (arg instanceof Float) {
30+
doubleArg = arg.doubleValue();
31+
} else {
32+
doubleArg = (Double) arg;
33+
}
34+
if (Math.floor(doubleArg) != doubleArg) {
3035
throw new RuntimeException(
3136
"Invalid non-integer value " + arg + " for format " + format + " at " + validationMetadata.pathToItem()
3237
);

0 commit comments

Comments
 (0)