Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(TCOMP-2260): validation tests #698

Open
wants to merge 19 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 17 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,8 @@ public List<org.apache.johnzon.jsonschema.spi.ValidationExtension> createDefault
.filter(v -> !org.apache.johnzon.jsonschema.spi.builtin.MaximumValidation.class.isInstance(v))
.filter(v -> !org.apache.johnzon.jsonschema.spi.builtin.RequiredValidation.class.isInstance(v))
.filter(v -> !org.apache.johnzon.jsonschema.spi.builtin.PatternValidation.class.isInstance(v))
.collect(Collectors.toList());
.collect(Collectors.toList())
;
yyin-talend marked this conversation as resolved.
Show resolved Hide resolved
validations.add(new TypeValidation());
validations.add(new EnumValidationWithDefaultValue());
validations.add(new MinimumValidation());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,18 +33,7 @@
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.AbstractMap;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.*;
yyin-talend marked this conversation as resolved.
Show resolved Hide resolved
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.function.BiFunction;
Expand Down Expand Up @@ -208,19 +197,29 @@ public Function<Map<String, String>, Object[]> parameterFactory(final Executable

return config -> {
final Map<String, String> notNullConfig = ofNullable(config).orElseGet(Collections::emptyMap);
final PayloadValidator visitor = new PayloadValidator();
if (!visitor.skip) {
visitor.globalPayload = new PayloadMapper((a, b) -> {
}).visitAndMap(metas, notNullConfig);
final PayloadMapper payloadMapper = new PayloadMapper(visitor);
payloadMapper.setGlobalPayload(visitor.globalPayload);
payloadMapper.visitAndMap(metas, notNullConfig);
visitor.throwIfFailed();
}
checkPayload(metas, notNullConfig);
return factories.stream().map(f -> f.apply(notNullConfig)).toArray(Object[]::new);
};
}

public static void checkPayload(final List<ParameterMeta> metas, final Map<String, String> notNullConfig) {
JsonObject globalPayload = new PayloadMapper((a, b) -> {
}).visitAndMap(metas, notNullConfig);
checkWithPayload(metas, notNullConfig, globalPayload);
}

public static void checkWithPayload(final List<ParameterMeta> metas, final Map<String, String> notNullConfig,
final JsonObject payload) {
final PayloadValidator visitor = new PayloadValidator();
if (!visitor.skip) {
visitor.globalPayload = payload;
final PayloadMapper payloadMapper = new PayloadMapper(visitor);
payloadMapper.setGlobalPayload(payload);
payloadMapper.visitAndMap(metas, notNullConfig);
visitor.throwIfFailed();
}
}

public Function<Supplier<Object>, Object> createContextualSupplier(final ClassLoader loader) {
return supplier -> {
final Thread thread = Thread.currentThread();
Expand Down Expand Up @@ -319,7 +318,7 @@ private Object createList(final ClassLoader loader, final Function<Supplier<Obje
final String configName = String.format("%s[%d]", name, paramIdx);
if (!config.containsKey(configName)) {
if (config.keySet().stream().anyMatch(k -> k.startsWith(configName + "."))) { // object
// mapping
// mapping
if (paramIdx == 0) {
args = findArgsName(itemClass);
}
Expand Down Expand Up @@ -887,6 +886,8 @@ public interface Messages {
String uniqueItems(String property);

String pattern(String property, String pattern);

String enumValues(String property, String enums, String val);
}

@RequiredArgsConstructor
Expand All @@ -908,7 +909,6 @@ public void onParameter(final ParameterMeta meta, final JsonValue value) {
if (!VISIBILITY_SERVICE.build(meta).isVisible(globalPayload)) {
return;
}

if (Boolean.parseBoolean(meta.getMetadata().get("tcomp::validation::required"))
&& value == JsonValue.NULL) {
errors.add(MESSAGES.required(meta.getPath()));
Expand Down Expand Up @@ -996,6 +996,20 @@ public void onParameter(final ParameterMeta meta, final JsonValue value) {
}
}
}
{
final String enumValues = metadata.get("tcomp::validation::enumValues");
if (enumValues != null) {// value = null or not in enumValues: add error
if (value == null) {
errors.add(MESSAGES.enumValues(meta.getPath(), enumValues, null));
} else {
final String val = JsonValue.class.cast(value).toString().replace("\"", "");
String[] enums = enumValues.substring(1, enumValues.length() - 1).split(",");
if (Arrays.stream(enums).noneMatch(s -> s.trim().equalsIgnoreCase(val))) {
errors.add(MESSAGES.enumValues(meta.getPath(), enumValues, val));
}
}
}
}
}

private void throwIfFailed() {
Expand All @@ -1007,7 +1021,7 @@ private void throwIfFailed() {

/**
* Helper function for creating an instance from a configuration map.
*
*
* @param clazz Class of the wanted instance.
* @param <T> Type managed
* @return function that generate the wanted instance when calling
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ private void onProperty(final String contextualPrefix, final Collection<Paramete
break;
}
case BOOLEAN:
final String boolValue = config.get(newPath);
final String boolValue = getValue(config, newPath, definition);
if (boolValue == null || boolValue.isEmpty()) {
parameterVisitor.onParameter(definition, JsonValue.NULL);
} else {
Expand All @@ -98,7 +98,7 @@ private void onProperty(final String contextualPrefix, final Collection<Paramete
.ifPresent(v -> json.add(name, Boolean.parseBoolean(v)));
break;
case NUMBER:
final String numberValue = config.get(newPath);
final String numberValue = getValue(config, newPath, definition);
if (numberValue == null || numberValue.isEmpty()) {
parameterVisitor.onParameter(definition, JsonValue.NULL);
} else {
Expand All @@ -114,7 +114,7 @@ private void onProperty(final String contextualPrefix, final Collection<Paramete
break;
case ENUM:
case STRING: {
final String value = config.get(newPath);
final String value = getValue(config, newPath, definition);
parameterVisitor.onParameter(definition, value == null ? JsonValue.NULL : jsonp.createValue(value));
ofNullable(value).ifPresent(v -> json.add(name, v));
break;
Expand All @@ -123,6 +123,11 @@ private void onProperty(final String contextualPrefix, final Collection<Paramete
}
}

private static String getValue(final Map<String, String> config, final String newPath,
final ParameterMeta definition) {
return config.get(newPath) == null ? config.get(definition.getPath()) : config.get(newPath);
}

private void onObject(final Collection<ParameterMeta> definitions, final ParameterMeta meta,
final Map<String, String> config, final JsonObjectBuilder json, final String name,
final String currentPath) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,4 @@ org.talend.sdk.component.runtime.manager.reflect.ReflectionService$Messages.minI
org.talend.sdk.component.runtime.manager.reflect.ReflectionService$Messages.maxItems = Length of property ''{0}'' should be < {1}, got {2}.
org.talend.sdk.component.runtime.manager.reflect.ReflectionService$Messages.uniqueItems = ''{0}'' has duplicated items.
org.talend.sdk.component.runtime.manager.reflect.ReflectionService$Messages.pattern = ''{0}'' does not match ''{1}''.
org.talend.sdk.component.runtime.manager.reflect.ReflectionService$Messages.enumValues = Invalid value for Property ''{0}'' expected: ''{1}'', got {2}.
Original file line number Diff line number Diff line change
Expand Up @@ -48,4 +48,7 @@ public class ConfigTypeNode {

private Collection<ActionReference> actions;

public void visibility(final SimplePropertyDefinition spd) {
// no-op
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,18 @@
*/
package org.talend.sdk.component.server.service;

import static java.util.Collections.emptyList;
import static java.util.Optional.ofNullable;
import static java.util.function.Function.identity;
import static java.util.stream.Collectors.toSet;
import static org.talend.sdk.component.server.lang.CustomCollectors.toLinkedMap;

import java.util.AbstractMap;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
Expand All @@ -33,13 +36,21 @@

import javax.enterprise.context.ApplicationScoped;
import javax.inject.Inject;
import javax.json.JsonException;
import javax.json.JsonObject;
import javax.json.JsonPointer;
import javax.json.JsonString;
import javax.json.JsonValue;
import javax.json.bind.Jsonb;
import javax.json.spi.JsonProvider;

import org.talend.sdk.component.runtime.internationalization.ParameterBundle;
import org.talend.sdk.component.runtime.manager.ParameterMeta;
import org.talend.sdk.component.runtime.manager.reflect.ReflectionService;
import org.talend.sdk.component.runtime.manager.reflect.parameterenricher.ValidationParameterEnricher;
import org.talend.sdk.component.runtime.manager.util.DefaultValueInspector;
import org.talend.sdk.component.server.configuration.ComponentServerConfiguration;
import org.talend.sdk.component.server.front.model.ConfigTypeNode;
import org.talend.sdk.component.server.front.model.PropertyValidation;
import org.talend.sdk.component.server.front.model.SimplePropertyDefinition;
import org.talend.sdk.component.server.service.qualifier.ComponentServer;
Expand Down Expand Up @@ -67,6 +78,85 @@ public Stream<SimplePropertyDefinition> buildProperties(final List<ParameterMeta
return buildProperties(meta, loader, locale, rootInstance, null);
}

public void validate(final ConfigTypeNode configNode, final JsonObject payload) {
ReflectionService.checkWithPayload(buildParameterMetas(configNode.getProperties()),
extractConfig(configNode, payload), payload);
}

private JsonPointer toPointer(final String absoluteTargetPath) {
return JsonProvider.provider().createPointer('/' + absoluteTargetPath.replace('.', '/'));
}

// Map<full path, value> from JsonObject(payload).
private Map<String, String> extractConfig(final ConfigTypeNode configNode, final JsonObject payload) {
Map<String, String> payloadConfig = new HashMap<>();
for (SimplePropertyDefinition propertyDefinition : configNode.getProperties()) {
try {
JsonValue value = toPointer(propertyDefinition.getPath()).getValue(payload);
if (value != null && !JsonObject.class.isInstance(value)) {
payloadConfig.put(propertyDefinition.getPath(), getValue(value));
}
} catch (JsonException e) {
continue;
}
}

return payloadConfig;
}

private String getValue(final JsonValue value) {
switch (value.getValueType()) {
case TRUE:
case FALSE:
case NUMBER:
return String.valueOf(value);
case STRING:
return JsonString.class.cast(value).getString();
default:
return null;
}
}

private List<ParameterMeta> buildParameterMetas(final List<SimplePropertyDefinition> properties) {
List<ParameterMeta> parameterMetaList = new ArrayList<>();
for (SimplePropertyDefinition propertyDefinition : properties) {
final String path = sanitizePropertyName(propertyDefinition.getPath());
final String name = sanitizePropertyName(propertyDefinition.getName());
final ParameterMeta.Type type = findType(propertyDefinition.getType());
// translate PropertyValidation
final Map<String, String> sanitizedMetadata = ofNullable(getParamMetadata(propertyDefinition.getMetadata()))
.orElse(new LinkedHashMap<>());
if (propertyDefinition.getValidation() != null) {
sanitizedMetadata.putAll(propertyValidationService.mapMeta(propertyDefinition.getValidation()));
}

parameterMetaList.add(new ParameterMeta(null, null, type, path, name, null,
emptyList(), null, sanitizedMetadata, false));
}
return parameterMetaList;
}

private static LinkedHashMap<String, String> getParamMetadata(final Map<String, String> p) {
return ofNullable(p)
.map(m -> m
.entrySet()
.stream()
.filter(e -> !e.getKey().startsWith(ValidationParameterEnricher.META_PREFIX))
.collect(toLinkedMap(e -> e.getKey().replace("condition::", "tcomp::condition::"),
Map.Entry::getValue)))
.orElse(null);
}

private static LinkedHashMap<String, String> getSanitizedMetadata(final Map<String, String> p) {
return ofNullable(p)
.map(m -> m
.entrySet()
.stream()
.filter(e -> !e.getKey().startsWith(ValidationParameterEnricher.META_PREFIX))
.collect(toLinkedMap(e -> e.getKey().replace("tcomp::", ""), Map.Entry::getValue)))
.orElse(null);
}

private Stream<SimplePropertyDefinition> buildProperties(final List<ParameterMeta> meta, final ClassLoader loader,
final Locale locale, final DefaultValueInspector.Instance rootInstance, final ParameterMeta parent) {
return meta.stream().flatMap(p -> {
Expand All @@ -81,13 +171,7 @@ private Stream<SimplePropertyDefinition> buildProperties(final List<ParameterMet
}
validation.setEnumValues(p.getProposals());
}
final Map<String, String> sanitizedMetadata = ofNullable(p.getMetadata())
.map(m -> m
.entrySet()
.stream()
.filter(e -> !e.getKey().startsWith(ValidationParameterEnricher.META_PREFIX))
.collect(toLinkedMap(e -> e.getKey().replace("tcomp::", ""), Map.Entry::getValue)))
.orElse(null);
final Map<String, String> sanitizedMetadata = getSanitizedMetadata(p.getMetadata());
final Map<String, String> metadata;
if (parent != null) {
metadata = sanitizedMetadata;
Expand Down Expand Up @@ -182,4 +266,21 @@ private String toDefault(final DefaultValueInspector.Instance instance, final Pa
private String sanitizePropertyName(final String path) {
return path.replace("${index}", "");
}

private ParameterMeta.Type findType(final String type) {
if (ParameterMeta.Type.STRING.name().equals(type)) {
return ParameterMeta.Type.STRING;
} else if (ParameterMeta.Type.BOOLEAN.name().equals(type)) {
return ParameterMeta.Type.BOOLEAN;
} else if (ParameterMeta.Type.NUMBER.name().equals(type)) {
return ParameterMeta.Type.NUMBER;
} else if (ParameterMeta.Type.ENUM.name().equals(type)) {
return ParameterMeta.Type.ENUM;
} else if (ParameterMeta.Type.ARRAY.name().equals(type)) {
return ParameterMeta.Type.ARRAY;
} else if (ParameterMeta.Type.OBJECT.name().equals(type)) {
return ParameterMeta.Type.OBJECT;
}
return ParameterMeta.Type.OBJECT;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import static java.util.stream.Collectors.toList;

import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.function.BiFunction;
import java.util.function.Function;
Expand All @@ -35,6 +36,8 @@ public class PropertyValidationService {

private Function<Map<String, String>, PropertyValidation> propertyValidationCreator;

private Function<PropertyValidation, Map<String, String>> propertyMetaCreator;

@PostConstruct
private void initMapper() {
// precompute the mapping of validations to centralize the convention - note: can be moved to impl for setters
Expand Down Expand Up @@ -75,9 +78,36 @@ private void initMapper() {
}
return validation;
};

}

public PropertyValidation map(final Map<String, String> meta) {
return propertyValidationCreator.apply(meta);
}

public Map<String, String> mapMeta(final PropertyValidation propertyValidation) {
final Map<String, String> metaMap = new HashMap<>();

Stream.of(PropertyValidation.class.getDeclaredFields()).filter(field -> {
try {
field.setAccessible(true);
return field.get(propertyValidation) != null;
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
})
.forEach(field -> {
field.setAccessible(true);
try {
Object value = field.get(propertyValidation);
if (value != null) {
metaMap.put(ValidationParameterEnricher.META_PREFIX + field.getName(),
String.valueOf(value));// f.get(instance).toString());
}
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
});
return metaMap;
}
}
Loading