Skip to content

Commit

Permalink
SED-3219 support collections in keyword inputs (#41)
Browse files Browse the repository at this point in the history
* SED-3219: support collections in keyword inputs

* SED-3219 improving KW input schema for arrays

---------

Co-authored-by: David Stephan <[email protected]>
  • Loading branch information
iegorov777 and david-stephan authored Jul 1, 2024
1 parent e9642ff commit 91305f6
Show file tree
Hide file tree
Showing 6 changed files with 76 additions and 45 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -27,27 +27,14 @@
import java.math.BigInteger;
import java.util.Collection;

import static step.handlers.javahandler.JsonInputConverter.resolveGenericTypeForCollection;
import static step.handlers.javahandler.JsonInputConverter.resolveGenericTypeForArrayAndCollection;

public class JsonInputConverter {

public static final String ARRAY_VALUE_SEPARATOR = ";";

public static void addValueToJsonBuilder(String value, JsonObjectBuilder builder, Type type, String jsonName) throws IllegalArgumentException {
Class<?> clazz;

try {
if (type instanceof Class) {
clazz = (Class<?>) type;
} else if (type instanceof ParameterizedType) {
// we expect the parameterized collection here
clazz = (Class<?>) ((ParameterizedType) type).getRawType();
} else {
throw new IllegalArgumentException("Unsupported type " + type + " found for field " + jsonName);
}
} catch (Exception ex) {
throw new IllegalArgumentException("Unsupported type " + type + " found for field " + jsonName);
}
Class<?> clazz = resolveClass(type, jsonName);

if(String.class.isAssignableFrom(clazz)){
builder.add(jsonName, value);
Expand All @@ -66,15 +53,7 @@ public static void addValueToJsonBuilder(String value, JsonObjectBuilder builder
} else if(clazz.isArray() || Collection.class.isAssignableFrom(clazz)){
JsonArrayBuilder arrayBuilder = Json.createArrayBuilder();

Class<?> arrayValueType;
if (clazz.isArray()) {
arrayValueType = clazz.getComponentType();
} else if (Collection.class.isAssignableFrom(clazz)) {
// we need to check the generic parameter type for collection
arrayValueType = resolveGenericTypeForCollection(type, jsonName);
} else {
throw new IllegalArgumentException("Unsupported type found for array field " + jsonName + ": " + type);
}
Class<?> arrayValueType = resolveGenericTypeForArrayAndCollection(clazz, type, jsonName);

for (String arrayValue : value.split(ARRAY_VALUE_SEPARATOR)) {
if(String.class.isAssignableFrom(arrayValueType)){
Expand Down Expand Up @@ -107,6 +86,23 @@ public static void addValueToJsonBuilder(String value, JsonObjectBuilder builder
}
}

public static Class resolveClass(Type type, String jsonName) {
Class<?> clazz;
try {
if (type instanceof Class) {
clazz = (Class<?>) type;
} else if (type instanceof ParameterizedType) {
// we expect the parameterized collection here
clazz = (Class<?>) ((ParameterizedType) type).getRawType();
} else {
throw new IllegalArgumentException("Unsupported type " + type + " found for field " + jsonName);
}
} catch (Exception ex) {
throw new IllegalArgumentException("Unsupported type " + type + " found for field " + jsonName);
}
return clazz;
}

public static String resolveJsonPropertyType(Class<?> type) {
if (String.class.isAssignableFrom(type)) {
return "string";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,11 @@
import java.io.StringReader;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.lang.reflect.Type;
import java.util.*;

import static step.handlers.javahandler.JsonInputConverter.resolveGenericTypeForArrayAndCollection;

public class KeywordJsonSchemaCreator {

private final JsonProvider jsonProvider = JsonProvider.provider();
Expand Down Expand Up @@ -97,6 +100,15 @@ private JsonObject readJsonSchemaFromInputAnnotations(Method method) throws Json
String propertyType = JsonInputConverter.resolveJsonPropertyType(type);
propertyParamsBuilder.add("type", propertyType);

if (propertyType.equals("array")) {
//add items type
Type parameterizedType = p.getParameterizedType();
Class<?> aClass = resolveGenericTypeForArrayAndCollection(JsonInputConverter.resolveClass(parameterizedType, parameterName), parameterizedType, parameterName);
String arrayElementType = JsonInputConverter.resolveJsonPropertyType(aClass);
JsonObject arrayType = jsonProvider.createObjectBuilder().add("type", arrayElementType).build();
propertyParamsBuilder.add("items", arrayType);
}

if (inputAnnotation.defaultValue() != null && !inputAnnotation.defaultValue().isEmpty()) {
try {
JsonSchemaCreator.addDefaultValue(inputAnnotation.defaultValue(), propertyParamsBuilder, p.getParameterizedType(), parameterName);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,8 @@ public void MyKeywordWithInputNestedFieldAnnotation(@Input(name = "stringField",
@Keyword
public void MyKeywordWithInputArrays(@Input(name = "stringArray", defaultValue = "a;b;c", required = true) String[] stringArray,
@Input(name = "integerArray", defaultValue = "1;2;3") Integer[] integerArray,
@Input(name = "stringList", defaultValue = "c;d;e") List<String> stringList) {
@Input(name = "stringList", defaultValue = "c;d;e") List<String> stringList,
@Input(name = "booleanList", defaultValue = "true;false;true") ArrayList<Boolean> booleanList) {
output.add("test", "test");
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,31 @@
"properties": {
"stringArray": {
"type": "array",
"items": {
"type": "string"
},
"default": ["a", "b", "c"]
},
"integerArray": {
"type": "array",
"items": {
"type": "number"
},
"default": [1, 2, 3]
},
"stringList": {
"type": "array",
"items": {
"type": "string"
},
"default": ["c", "d", "e"]
},
"booleanList": {
"type": "array",
"items": {
"type": "boolean"
},
"default": [true, false, true]
}
},
"required": [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,15 +59,7 @@ public static Object getValueFromJsonInput(JsonObject input, String name, String
} else if (valueType.isArray() || Collection.class.isAssignableFrom(valueType)) {
JsonArray jsonArray = (validInputValue) ? input.getJsonArray(name) :
convertStringToJsonArrayBuilder(name, defaultValue, valueType, type).build();
Class<?> arrayValueType;
if (valueType.isArray()) {
arrayValueType = valueType.getComponentType();
} else if (Collection.class.isAssignableFrom(valueType)) {
// we need to check the generic parameter type for collection
arrayValueType = resolveGenericTypeForCollection(type, name);
} else {
throw new IllegalArgumentException("Unsupported type found for array input " + name + ": " + type);
}
Class<?> arrayValueType = resolveGenericTypeForArrayAndCollection(valueType, type, name);;
Object[] arrayValue = null;
if (String.class.isAssignableFrom(arrayValueType)) {
arrayValue = new String[jsonArray.size()];
Expand Down Expand Up @@ -106,7 +98,11 @@ public static Object getValueFromJsonInput(JsonObject input, String name, String
}
}
if (Collection.class.isAssignableFrom(valueType)) {
value = valueType.getConstructor().newInstance();
if (valueType.isInterface()) {
value = new ArrayList<>();
} else {
value = valueType.getConstructor().newInstance();
}
((Collection) value).addAll(Arrays.asList(arrayValue));
} else {
value = arrayValue;
Expand Down Expand Up @@ -176,15 +172,7 @@ private static Optional<Object> getSimpleValueFrom(JsonObject input, String name
private static JsonArrayBuilder convertStringToJsonArrayBuilder(String jsonName, String value, Class<?> clazz, Type type) {
JsonArrayBuilder arrayBuilder = Json.createArrayBuilder();

Class<?> arrayValueType;
if (clazz.isArray()) {
arrayValueType = clazz.getComponentType();
} else if (Collection.class.isAssignableFrom(clazz)) {
// we need to check the generic parameter type for collection
arrayValueType = resolveGenericTypeForCollection(type, jsonName);
} else {
throw new IllegalArgumentException("Unsupported type found for array field " + jsonName + ": " + type);
}
Class<?> arrayValueType = resolveGenericTypeForArrayAndCollection(clazz, type, jsonName);;

for (String arrayValue : value.split(ARRAY_VALUE_SEPARATOR)) {
if(String.class.isAssignableFrom(arrayValueType)){
Expand All @@ -208,6 +196,19 @@ private static JsonArrayBuilder convertStringToJsonArrayBuilder(String jsonName,
return arrayBuilder;
}

public static Class<?> resolveGenericTypeForArrayAndCollection(Class<?> clazz, Type type, String jsonName) {
Class<?> arrayValueType;
if (clazz.isArray()) {
arrayValueType = clazz.getComponentType();
} else if (Collection.class.isAssignableFrom(clazz)) {
// we need to check the generic parameter type for collection
arrayValueType = resolveGenericTypeForCollection(type, jsonName);
} else {
throw new IllegalArgumentException("Unsupported type found for array field " + jsonName + ": " + type);
}
return arrayValueType;
}

public static Class<?> resolveGenericTypeForCollection(Type type, String jsonName) {
Class<?> arrayValueType;
if (!(type instanceof ParameterizedType)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,8 @@ public void MyKeywordWithInputNested(@Input(name = "stringField", defaultValue =
@Keyword
public void MyKeywordWithInputArrays(@Input(name = "stringArray", defaultValue = "a;b;c", required = true) String[] stringArray,
@Input(name = "integerArray", defaultValue = "1;2;3") Integer[] integerArray,
@Input(name = "stringList", defaultValue = "1;2;3") ArrayList<String> stringList) {
@Input(name = "stringList", defaultValue = "1;2;3") ArrayList<String> stringList,
@Input(name = "booleanList", defaultValue = "true;false;true") ArrayList<Boolean> booleanList) {
// fill output to check execution result in tests
if (stringArray != null) {
output.add("stringArrayOut", Arrays.stream(stringArray).reduce((s, s2) -> s + "+" + s2).orElse(""));
Expand All @@ -63,6 +64,10 @@ public void MyKeywordWithInputNested(@Input(name = "stringField", defaultValue =
if (stringList != null) {
output.add("stringListOut", stringList.stream().reduce((s, s2) -> s + "+" + s2).orElse(""));
}

if (booleanList != null) {
output.add("booleanListOut", booleanList.stream().map(Object::toString).reduce((s, s2) -> s + "+" + s2).orElse(""));
}
}

@Keyword
Expand Down

0 comments on commit 91305f6

Please sign in to comment.