From 1a08056895e19ea9dd15fc2b20ab98c1ff2e5d48 Mon Sep 17 00:00:00 2001 From: undx Date: Mon, 12 Dec 2022 13:28:56 +0100 Subject: [PATCH 1/7] fix(TPRUN-4949): reproduce issue w/ UT --- .../manager/reflect/ReflectionService.java | 3 + .../manager/ReflectionServiceTest.java | 191 ++++++++++++++---- .../ConditionParameterEnricherTest.java | 171 ++++++++++++++++ .../reflect/visibility/PayloadMapperTest.java | 18 ++ .../visibility/VisibilityServiceTest.java | 94 +++++++++ .../runtime/manager/test/MethodsHolder.java | 99 +++++++++ 6 files changed, 533 insertions(+), 43 deletions(-) diff --git a/component-runtime-manager/src/main/java/org/talend/sdk/component/runtime/manager/reflect/ReflectionService.java b/component-runtime-manager/src/main/java/org/talend/sdk/component/runtime/manager/reflect/ReflectionService.java index a81f48e22bdf7..bd8d1a815368f 100644 --- a/component-runtime-manager/src/main/java/org/talend/sdk/component/runtime/manager/reflect/ReflectionService.java +++ b/component-runtime-manager/src/main/java/org/talend/sdk/component/runtime/manager/reflect/ReflectionService.java @@ -905,9 +905,12 @@ private static class PayloadValidator implements PayloadMapper.OnParameter { @Override public void onParameter(final ParameterMeta meta, final JsonValue value) { + // TODO remove logging via stderr if (!VISIBILITY_SERVICE.build(meta).isVisible(globalPayload)) { + System.err.println("[onParameter] NOT VISIBLE " + meta.getPath() + "\t\t value: " + value); return; } + System.err.println("[onParameter] VISIBLE " + meta.getPath() + "\t\t value: " + value); if (Boolean.parseBoolean(meta.getMetadata().get("tcomp::validation::required")) && value == JsonValue.NULL) { diff --git a/component-runtime-manager/src/test/java/org/talend/sdk/component/runtime/manager/ReflectionServiceTest.java b/component-runtime-manager/src/test/java/org/talend/sdk/component/runtime/manager/ReflectionServiceTest.java index d63605c1b3a7a..363b1dcdb0116 100644 --- a/component-runtime-manager/src/test/java/org/talend/sdk/component/runtime/manager/ReflectionServiceTest.java +++ b/component-runtime-manager/src/test/java/org/talend/sdk/component/runtime/manager/ReflectionServiceTest.java @@ -29,6 +29,7 @@ import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; import java.io.Serializable; import java.lang.reflect.Constructor; @@ -798,40 +799,18 @@ void nestedRequiredActiveIfWithWrongPattern() throws NoSuchMethodException { @Test void nestedRequiredActiveIf_Rest() throws NoSuchMethodException { - final ParameterModelService service = new ParameterModelService(new PropertyEditorRegistry()); - final List metas = service - .buildParameterMetas(MethodsHolder.class.getMethod("visibility", MethodsHolder.RestDatastore.class), - "def", new BaseParameterEnricher.Context(new LocalConfigurationService(emptyList(), "test"))); - final Object[] params = reflectionService - .parameterFactory(MethodsHolder.class.getMethod("visibility", MethodsHolder.RestDatastore.class), - emptyMap(), metas) - .apply(new HashMap() { - - { - put("value.apiDesc.loadAPI", "false"); - } - }); - + final Map payload = new HashMap(); + payload.put("value.apiDesc.loadAPI", "false"); + final Object[] params = buildObjectParams("visibility", payload, MethodsHolder.RestDatastore.class); assertTrue(MethodsHolder.RestDatastore.class.isInstance(params[0])); } @Test void nestedRequiredActiveIfTrue_Rest() throws NoSuchMethodException { - final ParameterModelService service = new ParameterModelService(new PropertyEditorRegistry()); - final List metas = service - .buildParameterMetas(MethodsHolder.class.getMethod("visibility", MethodsHolder.RestDatastore.class), - "def", new BaseParameterEnricher.Context(new LocalConfigurationService(emptyList(), "test"))); - final Object[] params = reflectionService - .parameterFactory(MethodsHolder.class.getMethod("visibility", MethodsHolder.RestDatastore.class), - emptyMap(), metas) - .apply(new HashMap() { - - { - put("value.apiDesc.loadAPI", "true"); - put("value.complexConfiguration.url", "https://talend.com"); - } - }); - + final Map payload = new HashMap(); + payload.put("value.apiDesc.loadAPI", "true"); + payload.put("value.complexConfiguration.url", "https://talend.com"); + final Object[] params = buildObjectParams("visibility", payload, MethodsHolder.RestDatastore.class); assertTrue(MethodsHolder.RestDatastore.class.isInstance(params[0])); final MethodsHolder.RestDatastore value = MethodsHolder.RestDatastore.class.cast(params[0]); assertTrue(value.getApiDesc().isLoadAPI()); @@ -840,22 +819,148 @@ void nestedRequiredActiveIfTrue_Rest() throws NoSuchMethodException { @Test void nestedRequiredActiveIfWrong_Rest() throws NoSuchMethodException { - final ParameterModelService service = new ParameterModelService(new PropertyEditorRegistry()); - final List metas = service - .buildParameterMetas(MethodsHolder.class.getMethod("visibility", MethodsHolder.RestDatastore.class), - "def", new BaseParameterEnricher.Context(new LocalConfigurationService(emptyList(), "test"))); + final Map payload = new HashMap(); + payload.put("value.apiDesc.loadAPI", "true"); + payload.put("value.complexConfiguration.url", ""); assertThrows(IllegalArgumentException.class, - () -> reflectionService - .parameterFactory( - MethodsHolder.class.getMethod("visibility", MethodsHolder.RestDatastore.class), - emptyMap(), metas) - .apply(new HashMap() { + () -> buildObjectParams("visibility", payload, MethodsHolder.RestDatastore.class)); + } - { - put("value.apiDesc.loadAPI", "true"); - put("value.complexConfiguration.url", " "); - } - })); + @Test + void simpleRequiredActiveIfFiltersOk() throws NoSuchMethodException { + final Map payload = new HashMap(); + payload.put("configuration.logicalOpType", "ALL"); + payload.put("configuration.logicalOpValue", "ALL"); + final Object[] params = buildObjectParams("visibility", payload, MethodsHolder.FilterConfiguration.class); + assertTrue(MethodsHolder.FilterConfiguration.class.isInstance(params[0])); + final MethodsHolder.FilterConfiguration value = MethodsHolder.FilterConfiguration.class.cast(params[0]); + assertEquals("ALL", value.getLogicalOpType()); + assertEquals("ALL", value.getLogicalOpValue()); + } + + @Test + void simpleRequiredActiveIfFiltersKo() throws NoSuchMethodException { + final String expected = "- Property 'configuration.logicalOpValue' is required."; + final Map payload = new HashMap(); + payload.put("configuration.logicalOpType", "ALL"); + try { + buildObjectParams("visibility", payload, MethodsHolder.FilterConfiguration.class); + } catch (IllegalArgumentException e) { + assertEquals(expected, e.getMessage()); + } + } + + @Test + void simpleNestedRequiredActiveIfFiltersOk() throws NoSuchMethodException { + final Map payload = new HashMap(); + payload.put("configuration.logicalOpType", "AL"); + payload.put("configuration.filters[0].columnName", "col1"); + payload.put("configuration.filters[0].operator", "IS_VALID"); + payload.put("configuration.filters[0].value", "VALID"); + final Object[] params = buildObjectParams("visibility", payload, MethodsHolder.FilterConfiguration.class); + assertTrue(MethodsHolder.FilterConfiguration.class.isInstance(params[0])); + final MethodsHolder.FilterConfiguration value = MethodsHolder.FilterConfiguration.class.cast(params[0]); + assertEquals("VALID", value.getFilters().get(0).getValue()); + } + + @Test + void simpleNestedRequiredActiveIfFiltersKo() throws NoSuchMethodException { + final String expected = "- Property 'configuration.filters[${index}].value' is required."; + final Map payload = new HashMap(); + payload.put("configuration.logicalOpType", "AL"); + payload.put("configuration.filters[0].columnName", "col1"); + payload.put("configuration.filters[0].operator", "IS_VALID"); + try { + buildObjectParams("visibility", payload, MethodsHolder.FilterConfiguration.class); + fail("Cannot reach here!"); + } catch (IllegalArgumentException e) { + assertEquals(expected, e.getMessage()); + } + } + + @Test + void nestedRequiredActiveIfFiltersOk() throws NoSuchMethodException { + final Map payload = new HashMap(); + payload.put("configuration.logicalOpType", "AL"); + // visibility (false) should not throw Property 'configuration.filters[${index}].value' is required. + payload.put("configuration.filters[0].columnName", "col0"); + payload.put("configuration.filters[0].operator", "IS_NULL"); + payload.put("configuration.filters[1].columnName", "col1"); + payload.put("configuration.filters[1].operator", "IS_EMPTY"); + payload.put("configuration.filters[2].columnName", "col2"); + payload.put("configuration.filters[2].operator", "IS_NOT_NULL"); + payload.put("configuration.filters[3].columnName", "col3"); + payload.put("configuration.filters[3].operator", "IS_NOT_EMPTY"); + final Object[] params = buildObjectParams("visibility", payload, MethodsHolder.FilterConfiguration.class); + assertTrue(MethodsHolder.FilterConfiguration.class.isInstance(params[0])); + final MethodsHolder.FilterConfiguration value = MethodsHolder.FilterConfiguration.class.cast(params[0]); + assertEquals("AL", value.getLogicalOpType()); + assertEquals("col0", value.getFilters().get(0).getColumnName()); + } + + @Test + void nestedRequiredActiveIfFiltersKo() throws NoSuchMethodException { + final String expected = "- Property 'configuration.filters[${index}].value' is required."; + final Map payload = new HashMap(); + payload.put("configuration.logicalOpType", "AL"); + payload.put("configuration.filters[0].columnName", "col0"); + payload.put("configuration.filters[0].operator", "IS_NULL"); + payload.put("configuration.filters[0].value", ""); + payload.put("configuration.filters[1].columnName", "col1"); + payload.put("configuration.filters[1].operator", "IS_EMPTY"); + // FIXME this array element SHOULD BE VISIBLE and its value required + payload.put("configuration.filters[2].columnName", "col2"); + payload.put("configuration.filters[2].operator", "IS_VALID"); + try { + buildObjectParams("visibility", payload, MethodsHolder.FilterConfiguration.class); + fail("configuration.filters[2].operator=IS_VALID should be visible!"); + } catch (IllegalArgumentException e) { + assertEquals(expected, e.getMessage()); + } + } + + @Test + void configWithActiveIfEnumOK() throws NoSuchMethodException { + final Map payload = new HashMap(); + payload.put("configuration.bool1", "true"); + payload.put("configuration.bool2", "false"); + payload.put("configuration.bool3", "true"); + payload.put("configuration.enumRequired", "ONE"); + payload.put("configuration.enumIf", "ONE"); + payload.put("configuration.enumIfs", "ONE"); + final Object[] params = buildObjectParams("visibility", payload, MethodsHolder.ConfigWithActiveIfEnum.class); + assertTrue(MethodsHolder.ConfigWithActiveIfEnum.class.isInstance(params[0])); + final MethodsHolder.ConfigWithActiveIfEnum value = MethodsHolder.ConfigWithActiveIfEnum.class.cast(params[0]); + assertTrue(value.isBool1()); + } + + @Test + void configWithActiveIfEnumKoAll() throws NoSuchMethodException { + final String expected = + "- Property 'configuration.enumIf' is required.\n- Property 'configuration.enumIfs' is required.\n- Property 'configuration.enumRequired' is required."; + final Map payload = new HashMap(); + payload.put("configuration.bool1", "true"); + payload.put("configuration.bool2", "false"); + payload.put("configuration.bool3", "true"); + try { + buildObjectParams("visibility", payload, MethodsHolder.ConfigWithActiveIfEnum.class); + fail("Cannot reach here!"); + } catch (IllegalArgumentException e) { + assertEquals(expected, e.getMessage()); + } + } + + private Object[] buildObjectParams(final String method, final Map payload, final Class... args) + throws NoSuchMethodException { + return buildObjectParams(MethodsHolder.class.getMethod(method, args), payload); + } + + private Object[] buildObjectParams(final Method factory, final Map payload) + throws NoSuchMethodException { + final ParameterModelService service = new ParameterModelService(new PropertyEditorRegistry()); + final List metas = service.buildParameterMetas(factory, "def", + new BaseParameterEnricher.Context(new LocalConfigurationService(emptyList(), "test"))); + return reflectionService.parameterFactory(factory, emptyMap(), metas).apply(payload); } private Function, Object[]> getComponentFactory(final Class param, diff --git a/component-runtime-manager/src/test/java/org/talend/sdk/component/runtime/manager/reflect/parameterenricher/ConditionParameterEnricherTest.java b/component-runtime-manager/src/test/java/org/talend/sdk/component/runtime/manager/reflect/parameterenricher/ConditionParameterEnricherTest.java index ca932cc6d4cf1..5277e59fba1a9 100644 --- a/component-runtime-manager/src/test/java/org/talend/sdk/component/runtime/manager/reflect/parameterenricher/ConditionParameterEnricherTest.java +++ b/component-runtime-manager/src/test/java/org/talend/sdk/component/runtime/manager/reflect/parameterenricher/ConditionParameterEnricherTest.java @@ -317,4 +317,175 @@ public Class annotationType() { } })); } + + @Test + void activeIfsOnSameTarget() { + assertEquals(new HashMap() { + + { + put("tcomp::condition::ifs::operator", "AND"); + put("tcomp::condition::if::target::0", "filter.operator"); + put("tcomp::condition::if::value::0", "IS_NULL"); + put("tcomp::condition::if::negate::0", "true"); + put("tcomp::condition::if::evaluationStrategy::0", "DEFAULT"); + put("tcomp::condition::if::target::1", "filter.operator"); + put("tcomp::condition::if::value::1", "IS_NOT_NULL"); + put("tcomp::condition::if::negate::1", "true"); + put("tcomp::condition::if::evaluationStrategy::1", "DEFAULT"); + put("tcomp::condition::if::target::2", "filter.operator"); + put("tcomp::condition::if::value::2", "IS_EMPTY"); + put("tcomp::condition::if::negate::2", "true"); + put("tcomp::condition::if::evaluationStrategy::2", "DEFAULT"); + put("tcomp::condition::if::target::3", "filter.operator"); + put("tcomp::condition::if::value::3", "IS_NOT_EMPTY"); + put("tcomp::condition::if::negate::3", "true"); + put("tcomp::condition::if::evaluationStrategy::3", "DEFAULT"); + } + }, new ConditionParameterEnricher().onParameterAnnotation("testParam", String.class, new ActiveIfs() { + + @Override + public Operator operator() { + return Operator.AND; + } + + @Override + public Class annotationType() { + return ActiveIfs.class; + } + + @Override + public ActiveIf[] value() { + return new ActiveIf[] { + new ActiveIf() { + + @Override + public EvaluationStrategyOption[] evaluationStrategyOptions() { + return new EvaluationStrategyOption[0]; + } + + @Override + public String target() { + return "filter.operator"; + } + + @Override + public boolean negate() { + return true; + } + + @Override + public EvaluationStrategy evaluationStrategy() { + return EvaluationStrategy.DEFAULT; + } + + @Override + public Class annotationType() { + return ActiveIf.class; + } + + @Override + public String[] value() { + return new String[] { "IS_NULL" }; + } + }, + new ActiveIf() { + + @Override + public EvaluationStrategyOption[] evaluationStrategyOptions() { + return new EvaluationStrategyOption[0]; + } + + @Override + public String target() { + return "filter.operator"; + } + + @Override + public boolean negate() { + return true; + } + + @Override + public EvaluationStrategy evaluationStrategy() { + return EvaluationStrategy.DEFAULT; + } + + @Override + public Class annotationType() { + return ActiveIf.class; + } + + @Override + public String[] value() { + return new String[] { "IS_NOT_NULL" }; + } + }, + new ActiveIf() { + + @Override + public EvaluationStrategyOption[] evaluationStrategyOptions() { + return new EvaluationStrategyOption[0]; + } + + @Override + public String target() { + return "filter.operator"; + } + + @Override + public boolean negate() { + return true; + } + + @Override + public EvaluationStrategy evaluationStrategy() { + return EvaluationStrategy.DEFAULT; + } + + @Override + public Class annotationType() { + return ActiveIf.class; + } + + @Override + public String[] value() { + return new String[] { "IS_EMPTY" }; + } + }, + new ActiveIf() { + + @Override + public EvaluationStrategyOption[] evaluationStrategyOptions() { + return new EvaluationStrategyOption[0]; + } + + @Override + public String target() { + return "filter.operator"; + } + + @Override + public boolean negate() { + return true; + } + + @Override + public EvaluationStrategy evaluationStrategy() { + return EvaluationStrategy.DEFAULT; + } + + @Override + public Class annotationType() { + return ActiveIf.class; + } + + @Override + public String[] value() { + return new String[] { "IS_NOT_EMPTY" }; + } + } + }; + } + })); + } } diff --git a/component-runtime-manager/src/test/java/org/talend/sdk/component/runtime/manager/reflect/visibility/PayloadMapperTest.java b/component-runtime-manager/src/test/java/org/talend/sdk/component/runtime/manager/reflect/visibility/PayloadMapperTest.java index f2dd53f0b9654..77eddae02211f 100644 --- a/component-runtime-manager/src/test/java/org/talend/sdk/component/runtime/manager/reflect/visibility/PayloadMapperTest.java +++ b/component-runtime-manager/src/test/java/org/talend/sdk/component/runtime/manager/reflect/visibility/PayloadMapperTest.java @@ -94,6 +94,24 @@ void complex() throws NoSuchMethodException { + "\"other\":{\"value\":\"done\"},\"simple\":\"rs\",\"strings\":[\"rs1\"]}}", value.toString()); } + @Test + void filters() throws NoSuchMethodException { + final List params = service + .buildParameterMetas( + MethodsHolder.class.getMethod("visibility", MethodsHolder.FilterConfiguration.class), "def", + new BaseParameterEnricher.Context(new LocalConfigurationService(emptyList(), "test"))); + final Map payload = new TreeMap<>(); + payload.put("configuration.logicalOpType", "ALL"); + payload.put("configuration.filters[0].columnName", "col0"); + payload.put("configuration.filters[0].operator", "IS_NULL"); + payload.put("configuration.filters[0].value", ""); + final JsonValue value = extractorFactory.visitAndMap(params, payload); + System.out.println(value.toString()); + assertEquals( + "{\"configuration\":{\"filters\":[{\"columnName\":\"col0\",\"operator\":\"IS_NULL\",\"value\":\"\"}],\"logicalOpType\":\"ALL\"}}", + value.toString()); + } + public static class OtherObject { @Option diff --git a/component-runtime-manager/src/test/java/org/talend/sdk/component/runtime/manager/reflect/visibility/VisibilityServiceTest.java b/component-runtime-manager/src/test/java/org/talend/sdk/component/runtime/manager/reflect/visibility/VisibilityServiceTest.java index c1c75db78c9f6..4d1a10cc3a528 100644 --- a/component-runtime-manager/src/test/java/org/talend/sdk/component/runtime/manager/reflect/visibility/VisibilityServiceTest.java +++ b/component-runtime-manager/src/test/java/org/talend/sdk/component/runtime/manager/reflect/visibility/VisibilityServiceTest.java @@ -44,6 +44,7 @@ void one() { emptyList(), metadata, false)); assertTrue(conditionGroup.isVisible(Json.createObjectBuilder().add("the_target", "a").build())); assertTrue(conditionGroup.isVisible(Json.createObjectBuilder().add("the_target", "c").build())); + assertFalse(conditionGroup.isVisible(Json.createObjectBuilder().add("the_target", "d").build())); assertFalse(conditionGroup.isVisible(Json.createObjectBuilder().build())); } @@ -69,4 +70,97 @@ void two() { assertFalse(conditionGroup .isVisible(Json.createObjectBuilder().add("the_target1", "d").add("the_target2", "aa").build())); } + + @Test + void activeIfs() { + final Map metadata = new HashMap<>(); + metadata.put("tcomp::condition::ifs::operator", "AND"); + metadata.put("tcomp::condition::if::target::0", "operator"); + metadata.put("tcomp::condition::if::value::0", "IS_NULL"); + metadata.put("tcomp::condition::if::negate::0", "true"); + metadata.put("tcomp::condition::if::evaluationStrategy::0", "DEFAULT"); + metadata.put("tcomp::condition::if::target::1", "operator"); + metadata.put("tcomp::condition::if::value::1", "IS_NOT_NULL"); + metadata.put("tcomp::condition::if::negate::1", "true"); + metadata.put("tcomp::condition::if::evaluationStrategy::1", "DEFAULT"); + metadata.put("tcomp::condition::if::target::2", "operator"); + metadata.put("tcomp::condition::if::value::2", "IS_EMPTY"); + metadata.put("tcomp::condition::if::negate::2", "true"); + metadata.put("tcomp::condition::if::evaluationStrategy::2", "DEFAULT"); + metadata.put("tcomp::condition::if::target::3", "operator"); + metadata.put("tcomp::condition::if::value::3", "IS_NOT_EMPTY"); + metadata.put("tcomp::condition::if::negate::3", "true"); + metadata.put("tcomp::condition::if::evaluationStrategy::3", "DEFAULT"); + final VisibilityService.ConditionGroup conditionGroup = service + .build(new ParameterMeta(null, String.class, ParameterMeta.Type.STRING, "filter", "filter", null, + emptyList(), emptyList(), metadata, false)); + assertTrue(conditionGroup.isVisible(Json.createObjectBuilder().add("operator", "IS_NUMERIC").build())); + assertTrue(conditionGroup.isVisible(Json.createObjectBuilder().add("operator", "VALID").build())); + assertFalse(conditionGroup.isVisible(Json.createObjectBuilder().add("operator", "IS_NULL").build())); + assertFalse(conditionGroup.isVisible(Json.createObjectBuilder().add("operator", "IS_NOT_NULL").build())); + assertFalse(conditionGroup.isVisible(Json.createObjectBuilder().add("operator", "IS_EMPTY").build())); + assertFalse(conditionGroup.isVisible(Json.createObjectBuilder().add("operator", "IS_NOT_EMPTY").build())); + } + + @Test + void activeIfsWithArray() { + final Map metadata = new HashMap<>(); + metadata.put("tcomp::condition::ifs::operator", "AND"); + metadata.put("tcomp::condition::if::target::0", "arry[${index}].operator"); + metadata.put("tcomp::condition::if::value::0", "IS_NULL"); + metadata.put("tcomp::condition::if::negate::0", "true"); + metadata.put("tcomp::condition::if::evaluationStrategy::0", "DEFAULT"); + metadata.put("tcomp::condition::if::target::1", "arry[${index}].operator"); + metadata.put("tcomp::condition::if::value::1", "IS_NOT_NULL"); + metadata.put("tcomp::condition::if::negate::1", "true"); + metadata.put("tcomp::condition::if::evaluationStrategy::1", "DEFAULT"); + metadata.put("tcomp::condition::if::target::2", "arry[${index}].operator"); + metadata.put("tcomp::condition::if::value::2", "IS_EMPTY"); + metadata.put("tcomp::condition::if::negate::2", "true"); + metadata.put("tcomp::condition::if::evaluationStrategy::2", "DEFAULT"); + metadata.put("tcomp::condition::if::target::3", "arry[${index}].operator"); + metadata.put("tcomp::condition::if::value::3", "IS_NOT_EMPTY"); + metadata.put("tcomp::condition::if::negate::3", "true"); + metadata.put("tcomp::condition::if::evaluationStrategy::3", "DEFAULT"); + final VisibilityService.ConditionGroup conditionGroup = service + .build(new ParameterMeta(null, String.class, ParameterMeta.Type.STRING, "filter", "filter", null, + emptyList(), emptyList(), metadata, false)); + assertFalse(conditionGroup.isVisible(Json.createObjectBuilder() + .add("arry", + Json.createArrayBuilder() + .add(Json.createObjectBuilder().add("operator", "IS_NULL").build()) + .add(Json.createObjectBuilder().add("operator", "IS_NOT_NULL").build()) + .add(Json.createObjectBuilder().add("operator", "IS_EMPTY").build()) + .add(Json.createObjectBuilder().add("operator", "IS_NOT_EMPTY").build())) + .build())); + assertTrue(conditionGroup.isVisible(Json.createObjectBuilder() + .add("arry", Json.createArrayBuilder() + .add(Json.createObjectBuilder().add("operator", "IS_VALID").build())) + .build())); + } + + @Test + void activeIfWithArray() { + final Map metadata = new HashMap<>(); + metadata.put("tcomp::condition::if::target::0", "arry[${index}].operator"); + metadata.put("tcomp::condition::if::value::0", "IS_NULL,IS_NOT_NULL,IS_EMPTY,IS_NOT_EMPTY"); + metadata.put("tcomp::condition::if::negate::0", "true"); + metadata.put("tcomp::condition::if::evaluationStrategy::0", "DEFAULT"); + final VisibilityService.ConditionGroup conditionGroup = service + .build(new ParameterMeta(null, String.class, ParameterMeta.Type.STRING, "filter", "filter", null, + emptyList(), emptyList(), metadata, false)); + assertFalse(conditionGroup.isVisible(Json.createObjectBuilder() + .add("arry", + Json.createArrayBuilder() + .add(Json.createObjectBuilder().add("operator", "IS_NULL").build()) + .add(Json.createObjectBuilder().add("operator", "IS_NOT_NULL").build()) + .add(Json.createObjectBuilder().add("operator", "IS_EMPTY").build()) + .add(Json.createObjectBuilder().add("operator", "IS_NOT_EMPTY").build())) + .build())); + assertTrue(conditionGroup.isVisible(Json.createObjectBuilder() + .add("arry", Json.createArrayBuilder() + .add(Json.createObjectBuilder().add("operator", "IS_VALID").build())) + .build())); + } + } diff --git a/component-runtime-manager/src/test/java/org/talend/sdk/component/runtime/manager/test/MethodsHolder.java b/component-runtime-manager/src/test/java/org/talend/sdk/component/runtime/manager/test/MethodsHolder.java index 0ab304611955e..1e69ce3870457 100644 --- a/component-runtime-manager/src/test/java/org/talend/sdk/component/runtime/manager/test/MethodsHolder.java +++ b/component-runtime-manager/src/test/java/org/talend/sdk/component/runtime/manager/test/MethodsHolder.java @@ -15,20 +15,27 @@ */ package org.talend.sdk.component.runtime.manager.test; +import static org.talend.sdk.component.api.configuration.condition.ActiveIfs.Operator.AND; + import java.time.ZonedDateTime; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import java.util.Map; import org.talend.sdk.component.api.configuration.Option; import org.talend.sdk.component.api.configuration.action.Proposable; import org.talend.sdk.component.api.configuration.condition.ActiveIf; +import org.talend.sdk.component.api.configuration.condition.ActiveIfs; import org.talend.sdk.component.api.configuration.constraint.Max; import org.talend.sdk.component.api.configuration.constraint.Min; import org.talend.sdk.component.api.configuration.constraint.Pattern; import org.talend.sdk.component.api.configuration.constraint.Required; import org.talend.sdk.component.api.configuration.type.DataSet; +import org.talend.sdk.component.api.configuration.ui.OptionsOrder; +import org.talend.sdk.component.api.meta.Documentation; +import lombok.Data; import lombok.Getter; public class MethodsHolder { @@ -81,6 +88,14 @@ public void visibility(final RestDatastore value) { // no-op } + public void visibility(@Option("configuration") final FilterConfiguration filters) { + // no-op + } + + public void visibility(@Option("configuration") final ConfigWithActiveIfEnum config) { + // no-op + } + @Getter public static class Array { @@ -169,4 +184,88 @@ public static class ComplexConfiguration { private String url = ""; } } + + @Data + public static class FilterConfiguration { + + @Option + @Required + @Documentation("How to combine filters") + private String logicalOpType; + + @Option + @Required + @ActiveIf(target = "logicalOpType", value = "ALL") + @Documentation("How to combine filters") + private String logicalOpValue; + + @Option + @Documentation("The list of filters to apply") + private List filters = new ArrayList<>(Arrays.asList(new Criteria())); + + @Data + @OptionsOrder({ "columnName", "function", "operator", "value" }) + @Documentation("An unitary filter.") + public static class Criteria { + + @Option + @Required + @Documentation("The input field path to use for this criteria") + private String columnName; + + @Option + @Documentation("The operator") + private String operator; + + @Option + @Required + @ActiveIfs(operator = AND, value = { + @ActiveIf(negate = true, target = "operator", value = "IS_NULL"), + @ActiveIf(negate = true, target = "operator", value = "IS_NOT_NULL"), + @ActiveIf(negate = true, target = "operator", value = "IS_EMPTY"), + @ActiveIf(negate = true, target = "operator", value = "IS_NOT_EMPTY") }) + @Documentation("The value to compare to") + private String value; + } + } + + @Data + public static class ConfigWithActiveIfEnum { + + @Option + @Required + private boolean bool1; + + @Option + @Required + private boolean bool2; + + @Option + @Required + private boolean bool3; + + @Option + @Required + private ActiveIfEnum enumRequired; + + @Option + @Required + @ActiveIf(target = "bool1", value = "true") + private ActiveIfEnum enumIf; + + @Option + @Required + @ActiveIfs({ + @ActiveIf(target = "bool1", value = "true"), + @ActiveIf(target = "bool2", value = "false"), + @ActiveIf(target = "bool3", value = "true") + }) + private ActiveIfEnum enumIfs; + + enum ActiveIfEnum { + ONE, + TWO, + THREE + } + } } From 9ee8042f71641db6cac18da31c487bbb0763c405 Mon Sep 17 00:00:00 2001 From: undx Date: Wed, 14 Dec 2022 11:35:43 +0100 Subject: [PATCH 2/7] fix(TPRUN-4949): start of JsonPointer fix JsonPointer cannot access to pointer like `ary[${index}].x` This tries to workaround this limitation. - pending bug see UT --- .../reflect/visibility/VisibilityService.java | 25 +++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/component-runtime-manager/src/main/java/org/talend/sdk/component/runtime/manager/reflect/visibility/VisibilityService.java b/component-runtime-manager/src/main/java/org/talend/sdk/component/runtime/manager/reflect/visibility/VisibilityService.java index 8f7c67c1555c8..c2628e88b0e0f 100644 --- a/component-runtime-manager/src/main/java/org/talend/sdk/component/runtime/manager/reflect/visibility/VisibilityService.java +++ b/component-runtime-manager/src/main/java/org/talend/sdk/component/runtime/manager/reflect/visibility/VisibilityService.java @@ -29,6 +29,7 @@ import java.util.stream.IntStream; import java.util.stream.Stream; +import javax.json.JsonArrayBuilder; import javax.json.JsonNumber; import javax.json.JsonObject; import javax.json.JsonPointer; @@ -40,6 +41,7 @@ import lombok.NoArgsConstructor; import lombok.RequiredArgsConstructor; +import lombok.ToString; @RequiredArgsConstructor public class VisibilityService { @@ -63,7 +65,8 @@ public ConditionGroup build(final ParameterMeta param) { final String negateKey = "tcomp::condition::if::negate" + index; final String evaluationStrategyKey = "tcomp::condition::if::evaluationStrategy" + index; final String absoluteTargetPath = pathResolver.resolveProperty(param.getPath(), meta.getValue()); - return new Condition(toPointer(absoluteTargetPath), + return new Condition('/' + absoluteTargetPath.replace('.', '/'), + toPointer(absoluteTargetPath), Boolean.parseBoolean(param.getMetadata().getOrDefault(negateKey, "false")), param.getMetadata().getOrDefault(evaluationStrategyKey, "DEFAULT").toUpperCase(ROOT), param.getMetadata().getOrDefault(valueKey, "true").split(",")); @@ -119,6 +122,7 @@ private String doResolveProperty(final String propPath, final String paramRef) { } @RequiredArgsConstructor(access = PRIVATE) + @ToString public static class Condition { private static final Function TO_STRING = v -> v == null ? null : String.valueOf(v); @@ -126,6 +130,8 @@ public static class Condition { private static final Function TO_LOWERCASE = v -> v == null ? null : String.valueOf(v).toLowerCase(ROOT); + private final String path; + private final JsonPointer pointer; private final boolean negation; @@ -142,6 +148,9 @@ private boolean evaluate(final String expected, final JsonObject payload) { final Object actual = extractValue(payload); switch (evaluationStrategy) { case "DEFAULT": + if (Collection.class.isInstance(actual)) { + return Collection.class.cast(actual).contains(expected); + } return expected.equals(TO_STRING.apply(actual)); case "LENGTH": if (actual == null) { @@ -209,10 +218,22 @@ private boolean evaluate(final String expected, final JsonObject payload) { private Object extractValue(final JsonObject payload) { if (!pointer.containsValue(payload)) { + if (path.contains("[${index}]")) { + final JsonPointer ptr = JsonProvider.provider() + .createPointer(path.substring(0, path.indexOf("[${index}]"))); + final JsonPointer subptr = JsonProvider.provider() + .createPointer(path.substring(path.indexOf("]") + 1)); + final JsonArrayBuilder builder = JsonProvider.provider().createArrayBuilder(); + ptr.getValue(payload) + .asJsonArray() + .stream() + .forEach(j -> builder.add(subptr.getValue(j.asJsonObject()))); + // TODO comment to activate initial behavior / remove comments to activate start of fix + //return ofNullable(builder.build()).map(this::mapValue).orElse(null); + } return null; } return ofNullable(pointer.getValue(payload)).map(this::mapValue).orElse(null); - } private Object mapValue(final JsonValue value) { From d6b16fd7d7afaae7dfb4408893cf09d898ac22e5 Mon Sep 17 00:00:00 2001 From: yyin-talend Date: Thu, 5 Jan 2023 14:47:57 +0800 Subject: [PATCH 3/7] array validation --- .../manager/reflect/ReflectionService.java | 4 +- .../reflect/visibility/VisibilityService.java | 67 +++++++++++++++---- .../manager/ReflectionServiceTest.java | 2 + 3 files changed, 60 insertions(+), 13 deletions(-) diff --git a/component-runtime-manager/src/main/java/org/talend/sdk/component/runtime/manager/reflect/ReflectionService.java b/component-runtime-manager/src/main/java/org/talend/sdk/component/runtime/manager/reflect/ReflectionService.java index bd8d1a815368f..7ce15f0683d81 100644 --- a/component-runtime-manager/src/main/java/org/talend/sdk/component/runtime/manager/reflect/ReflectionService.java +++ b/component-runtime-manager/src/main/java/org/talend/sdk/component/runtime/manager/reflect/ReflectionService.java @@ -890,7 +890,7 @@ public interface Messages { } @RequiredArgsConstructor - private static class PayloadValidator implements PayloadMapper.OnParameter { + protected static class PayloadValidator implements PayloadMapper.OnParameter { private static final VisibilityService VISIBILITY_SERVICE = new VisibilityService(JsonProvider.provider()); @@ -903,6 +903,8 @@ private static class PayloadValidator implements PayloadMapper.OnParameter { JsonObject globalPayload; + public String currentParamPath; + @Override public void onParameter(final ParameterMeta meta, final JsonValue value) { // TODO remove logging via stderr diff --git a/component-runtime-manager/src/main/java/org/talend/sdk/component/runtime/manager/reflect/visibility/VisibilityService.java b/component-runtime-manager/src/main/java/org/talend/sdk/component/runtime/manager/reflect/visibility/VisibilityService.java index c2628e88b0e0f..9958cfc003d9b 100644 --- a/component-runtime-manager/src/main/java/org/talend/sdk/component/runtime/manager/reflect/visibility/VisibilityService.java +++ b/component-runtime-manager/src/main/java/org/talend/sdk/component/runtime/manager/reflect/visibility/VisibilityService.java @@ -23,8 +23,7 @@ import static lombok.AccessLevel.PRIVATE; import java.lang.reflect.Array; -import java.util.Collection; -import java.util.Map; +import java.util.*; import java.util.function.Function; import java.util.stream.IntStream; import java.util.stream.Stream; @@ -53,7 +52,8 @@ public class VisibilityService { public ConditionGroup build(final ParameterMeta param) { final boolean and = "AND".equalsIgnoreCase(param.getMetadata().getOrDefault("tcomp::condition::ifs::operator", "AND")); - return new ConditionGroup(param + Map conditions = new HashMap<>(); + ConditionGroup group = new ConditionGroup(param .getMetadata() .entrySet() .stream() @@ -65,13 +65,32 @@ public ConditionGroup build(final ParameterMeta param) { final String negateKey = "tcomp::condition::if::negate" + index; final String evaluationStrategyKey = "tcomp::condition::if::evaluationStrategy" + index; final String absoluteTargetPath = pathResolver.resolveProperty(param.getPath(), meta.getValue()); - return new Condition('/' + absoluteTargetPath.replace('.', '/'), - toPointer(absoluteTargetPath), - Boolean.parseBoolean(param.getMetadata().getOrDefault(negateKey, "false")), - param.getMetadata().getOrDefault(evaluationStrategyKey, "DEFAULT").toUpperCase(ROOT), - param.getMetadata().getOrDefault(valueKey, "true").split(",")); + Condition condition = conditions.get(absoluteTargetPath); + if (condition == null) { + condition = new Condition('/' + absoluteTargetPath.replace('.', '/'), + toPointer(absoluteTargetPath), + Boolean.parseBoolean(param.getMetadata().getOrDefault(negateKey, "false")), + param.getMetadata().getOrDefault(evaluationStrategyKey, "DEFAULT").toUpperCase(ROOT), + param.getMetadata().getOrDefault(valueKey, "true").split(",")); + conditions.put(absoluteTargetPath, condition); + } else { + List collection = Arrays.stream(condition.values).collect(toList()); + collection.add(String.valueOf(param.getMetadata().getOrDefault(valueKey, "true"))); + condition = new Condition('/' + absoluteTargetPath.replace('.', '/'), + toPointer(absoluteTargetPath), + Boolean.parseBoolean(param.getMetadata().getOrDefault(negateKey, "false")), + param.getMetadata().getOrDefault(evaluationStrategyKey, "DEFAULT").toUpperCase(ROOT), + collection.toArray(new String[0])); + conditions.replace(absoluteTargetPath, condition); + } + return condition; }) .collect(toList()), and ? stream -> stream.allMatch(i -> i) : stream -> stream.anyMatch(i -> i)); + if (and) { + group.conditions.clear(); + group.conditions.addAll(conditions.values()); + } + return group; } private JsonPointer toPointer(final String absoluteTargetPath) { @@ -140,16 +159,26 @@ public static class Condition { private final String[] values; + //used for array position + private int index; + boolean evaluateCondition(final JsonObject payload) { + System.err.println("[Condition]--negation--"+negation+"--values--" ); + for (String s :values) { + System.err.print(s +","); + } return negation != Stream.of(values).anyMatch(val -> evaluate(val, payload)); } private boolean evaluate(final String expected, final JsonObject payload) { final Object actual = extractValue(payload); + System.err.println("[Condition]--extract--" + actual.toString()); switch (evaluationStrategy) { case "DEFAULT": if (Collection.class.isInstance(actual)) { - return Collection.class.cast(actual).contains(expected); + //if values >= actual, return true, it actual contains any element out of values, return false; + return Stream.of(Collection.class.cast(actual)).filter(c -> !Arrays.stream(values).collect(toList()).contains(c)).count() == 0; + //return Collection.class.cast(actual).contains(expected); } return expected.equals(TO_STRING.apply(actual)); case "LENGTH": @@ -227,9 +256,13 @@ private Object extractValue(final JsonObject payload) { ptr.getValue(payload) .asJsonArray() .stream() - .forEach(j -> builder.add(subptr.getValue(j.asJsonObject()))); + .forEach(j -> { + JsonValue value = subptr.getValue(j.asJsonObject()); +// System.err.println(value.toString()); + builder.add(value); + }); // TODO comment to activate initial behavior / remove comments to activate start of fix - //return ofNullable(builder.build()).map(this::mapValue).orElse(null); + return ofNullable(builder.build()).map(this::mapValue).orElse(null); } return null; } @@ -239,7 +272,16 @@ private Object extractValue(final JsonObject payload) { private Object mapValue(final JsonValue value) { switch (value.getValueType()) { case ARRAY: - return value.asJsonArray().stream().map(this::mapValue).collect(toList()); +// List list = new ArrayList(); +// value.asJsonArray().forEach(each -> { +// String str = each.toString().substring(1,each.toString().length()-1); +// if (!Stream.of(values).anyMatch(s->s.equals(str))){//.collect(toList()).contains(str)) { +// list.add(each.toString()); +// } +// }); +// return list; +// + return value.asJsonArray().stream().map(this::mapValue).collect(toList()); case STRING: return JsonString.class.cast(value).getString(); case TRUE: @@ -265,6 +307,7 @@ public static class ConditionGroup { private final Function, Boolean> aggregator; public boolean isVisible(final JsonObject payload) { + System.err.println("[ConditionGroup::isVisible]: conditions size :" +conditions.size()); return conditions .stream() .allMatch(group -> aggregator.apply(conditions.stream().map(c -> c.evaluateCondition(payload)))); diff --git a/component-runtime-manager/src/test/java/org/talend/sdk/component/runtime/manager/ReflectionServiceTest.java b/component-runtime-manager/src/test/java/org/talend/sdk/component/runtime/manager/ReflectionServiceTest.java index 363b1dcdb0116..953722669c02f 100644 --- a/component-runtime-manager/src/test/java/org/talend/sdk/component/runtime/manager/ReflectionServiceTest.java +++ b/component-runtime-manager/src/test/java/org/talend/sdk/component/runtime/manager/ReflectionServiceTest.java @@ -908,9 +908,11 @@ void nestedRequiredActiveIfFiltersKo() throws NoSuchMethodException { payload.put("configuration.filters[0].value", ""); payload.put("configuration.filters[1].columnName", "col1"); payload.put("configuration.filters[1].operator", "IS_EMPTY"); +// payload.put("configuration.filters[1].value", ""); // FIXME this array element SHOULD BE VISIBLE and its value required payload.put("configuration.filters[2].columnName", "col2"); payload.put("configuration.filters[2].operator", "IS_VALID"); +// payload.put("configuration.filters[2].value", ""); try { buildObjectParams("visibility", payload, MethodsHolder.FilterConfiguration.class); fail("configuration.filters[2].operator=IS_VALID should be visible!"); From e90ed0adcb184799d34ec15a1b796c0e38ff1d63 Mon Sep 17 00:00:00 2001 From: yyin-talend Date: Mon, 27 Mar 2023 15:05:38 +0800 Subject: [PATCH 4/7] fixed --- .../manager/reflect/ReflectionService.java | 5 - .../reflect/visibility/VisibilityService.java | 205 ++++++++---------- .../manager/ReflectionServiceTest.java | 7 +- .../visibility/VisibilityServiceTest.java | 65 +++++- 4 files changed, 157 insertions(+), 125 deletions(-) diff --git a/component-runtime-manager/src/main/java/org/talend/sdk/component/runtime/manager/reflect/ReflectionService.java b/component-runtime-manager/src/main/java/org/talend/sdk/component/runtime/manager/reflect/ReflectionService.java index 7ce15f0683d81..28ed7a2422906 100644 --- a/component-runtime-manager/src/main/java/org/talend/sdk/component/runtime/manager/reflect/ReflectionService.java +++ b/component-runtime-manager/src/main/java/org/talend/sdk/component/runtime/manager/reflect/ReflectionService.java @@ -903,16 +903,11 @@ protected static class PayloadValidator implements PayloadMapper.OnParameter { JsonObject globalPayload; - public String currentParamPath; - @Override public void onParameter(final ParameterMeta meta, final JsonValue value) { - // TODO remove logging via stderr if (!VISIBILITY_SERVICE.build(meta).isVisible(globalPayload)) { - System.err.println("[onParameter] NOT VISIBLE " + meta.getPath() + "\t\t value: " + value); return; } - System.err.println("[onParameter] VISIBLE " + meta.getPath() + "\t\t value: " + value); if (Boolean.parseBoolean(meta.getMetadata().get("tcomp::validation::required")) && value == JsonValue.NULL) { diff --git a/component-runtime-manager/src/main/java/org/talend/sdk/component/runtime/manager/reflect/visibility/VisibilityService.java b/component-runtime-manager/src/main/java/org/talend/sdk/component/runtime/manager/reflect/visibility/VisibilityService.java index 9958cfc003d9b..aa890698dfeb2 100644 --- a/component-runtime-manager/src/main/java/org/talend/sdk/component/runtime/manager/reflect/visibility/VisibilityService.java +++ b/component-runtime-manager/src/main/java/org/talend/sdk/component/runtime/manager/reflect/visibility/VisibilityService.java @@ -1,12 +1,12 @@ /** * Copyright (C) 2006-2022 Talend Inc. - www.talend.com - * + *

* Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + *

* http://www.apache.org/licenses/LICENSE-2.0 - * + *

* Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -15,32 +15,23 @@ */ package org.talend.sdk.component.runtime.manager.reflect.visibility; -import static java.util.Locale.ROOT; -import static java.util.Optional.ofNullable; -import static java.util.stream.Collectors.joining; -import static java.util.stream.Collectors.toList; -import static java.util.stream.Collectors.toMap; -import static lombok.AccessLevel.PRIVATE; +import lombok.NoArgsConstructor; +import lombok.RequiredArgsConstructor; +import lombok.ToString; +import org.talend.sdk.component.runtime.manager.ParameterMeta; +import javax.json.*; +import javax.json.spi.JsonProvider; import java.lang.reflect.Array; import java.util.*; import java.util.function.Function; import java.util.stream.IntStream; import java.util.stream.Stream; -import javax.json.JsonArrayBuilder; -import javax.json.JsonNumber; -import javax.json.JsonObject; -import javax.json.JsonPointer; -import javax.json.JsonString; -import javax.json.JsonValue; -import javax.json.spi.JsonProvider; - -import org.talend.sdk.component.runtime.manager.ParameterMeta; - -import lombok.NoArgsConstructor; -import lombok.RequiredArgsConstructor; -import lombok.ToString; +import static java.util.Locale.ROOT; +import static java.util.Optional.ofNullable; +import static java.util.stream.Collectors.*; +import static lombok.AccessLevel.PRIVATE; @RequiredArgsConstructor public class VisibilityService { @@ -159,89 +150,85 @@ public static class Condition { private final String[] values; - //used for array position - private int index; - boolean evaluateCondition(final JsonObject payload) { - System.err.println("[Condition]--negation--"+negation+"--values--" ); - for (String s :values) { - System.err.print(s +","); - } return negation != Stream.of(values).anyMatch(val -> evaluate(val, payload)); } private boolean evaluate(final String expected, final JsonObject payload) { final Object actual = extractValue(payload); - System.err.println("[Condition]--extract--" + actual.toString()); switch (evaluationStrategy) { - case "DEFAULT": - if (Collection.class.isInstance(actual)) { - //if values >= actual, return true, it actual contains any element out of values, return false; - return Stream.of(Collection.class.cast(actual)).filter(c -> !Arrays.stream(values).collect(toList()).contains(c)).count() == 0; - //return Collection.class.cast(actual).contains(expected); - } - return expected.equals(TO_STRING.apply(actual)); - case "LENGTH": - if (actual == null) { - return "0".equals(expected); - } - final int expectedSize = Integer.parseInt(expected); - if (Collection.class.isInstance(actual)) { - return expectedSize == Collection.class.cast(actual).size(); - } - if (actual.getClass().isArray()) { - return expectedSize == Array.getLength(actual); - } - if (String.class.isInstance(actual)) { - return expectedSize == String.class.cast(actual).length(); - } - return false; - default: - Function preprocessor = TO_STRING; - if (evaluationStrategy.startsWith("CONTAINS")) { - final int start = evaluationStrategy.indexOf('('); - if (start >= 0) { - final int end = evaluationStrategy.indexOf(')', start); - if (end >= 0) { - final Map configuration = Stream - .of(evaluationStrategy.substring(start + 1, end).split(",")) - .map(String::trim) - .filter(it -> !it.isEmpty()) - .map(it -> { - final int sep = it.indexOf('='); - if (sep > 0) { - return new String[] { it.substring(0, sep), it.substring(sep + 1) }; - } - return new String[] { "value", it }; - }) - .collect(toMap(a -> a[0], a -> a[1])); - if (Boolean.parseBoolean(configuration.getOrDefault("lowercase", "false"))) { - preprocessor = TO_LOWERCASE; + case "DEFAULT": + if (Collection.class.isInstance(actual)) { + //if values >= actual, return true, it actual contains any element out of values, return false; + for (Object s : Collection.class.cast(actual)) { + if (!Arrays.stream(values).collect(toList()).contains(s)) { + return false; } } + return true; } + return expected.equals(TO_STRING.apply(actual)); + case "LENGTH": if (actual == null) { - return false; - } - if (CharSequence.class.isInstance(actual)) { - return ofNullable(preprocessor.apply(TO_STRING.apply(actual))) - .map(it -> it.contains(expected)) - .orElse(false); + return "0".equals(expected); } + final int expectedSize = Integer.parseInt(expected); if (Collection.class.isInstance(actual)) { - final Collection collection = Collection.class.cast(actual); - return collection.stream().map(preprocessor).anyMatch(it -> it.contains(expected)); + return expectedSize == Collection.class.cast(actual).size(); } if (actual.getClass().isArray()) { - return IntStream - .range(0, Array.getLength(actual)) - .mapToObj(i -> Array.get(actual, i)) - .map(preprocessor) - .anyMatch(it -> it.contains(expected)); + return expectedSize == Array.getLength(actual); + } + if (String.class.isInstance(actual)) { + return expectedSize == String.class.cast(actual).length(); } return false; - } - throw new IllegalArgumentException("Not supported operation '" + evaluationStrategy + "'"); + default: + Function preprocessor = TO_STRING; + if (evaluationStrategy.startsWith("CONTAINS")) { + final int start = evaluationStrategy.indexOf('('); + if (start >= 0) { + final int end = evaluationStrategy.indexOf(')', start); + if (end >= 0) { + final Map configuration = Stream + .of(evaluationStrategy.substring(start + 1, end).split(",")) + .map(String::trim) + .filter(it -> !it.isEmpty()) + .map(it -> { + final int sep = it.indexOf('='); + if (sep > 0) { + return new String[]{it.substring(0, sep), it.substring(sep + 1)}; + } + return new String[]{"value", it}; + }) + .collect(toMap(a -> a[0], a -> a[1])); + if (Boolean.parseBoolean(configuration.getOrDefault("lowercase", "false"))) { + preprocessor = TO_LOWERCASE; + } + } + } + if (actual == null) { + return false; + } + if (CharSequence.class.isInstance(actual)) { + return ofNullable(preprocessor.apply(TO_STRING.apply(actual))) + .map(it -> it.contains(expected)) + .orElse(false); + } + if (Collection.class.isInstance(actual)) { + final Collection collection = Collection.class.cast(actual); + return collection.stream().map(preprocessor).anyMatch(it -> it.contains(expected)); + } + if (actual.getClass().isArray()) { + return IntStream + .range(0, Array.getLength(actual)) + .mapToObj(i -> Array.get(actual, i)) + .map(preprocessor) + .anyMatch(it -> it.contains(expected)); + } + return false; + } + throw new IllegalArgumentException("Not supported operation '" + evaluationStrategy + "'"); } } @@ -258,10 +245,8 @@ private Object extractValue(final JsonObject payload) { .stream() .forEach(j -> { JsonValue value = subptr.getValue(j.asJsonObject()); -// System.err.println(value.toString()); - builder.add(value); + builder.add(value); }); - // TODO comment to activate initial behavior / remove comments to activate start of fix return ofNullable(builder.build()).map(this::mapValue).orElse(null); } return null; @@ -271,30 +256,21 @@ private Object extractValue(final JsonObject payload) { private Object mapValue(final JsonValue value) { switch (value.getValueType()) { - case ARRAY: -// List list = new ArrayList(); -// value.asJsonArray().forEach(each -> { -// String str = each.toString().substring(1,each.toString().length()-1); -// if (!Stream.of(values).anyMatch(s->s.equals(str))){//.collect(toList()).contains(str)) { -// list.add(each.toString()); -// } -// }); -// return list; -// + case ARRAY: return value.asJsonArray().stream().map(this::mapValue).collect(toList()); - case STRING: - return JsonString.class.cast(value).getString(); - case TRUE: - return true; - case FALSE: - return false; - case NUMBER: - return JsonNumber.class.cast(value).doubleValue(); - case NULL: - return null; - case OBJECT: - default: - return value; + case STRING: + return JsonString.class.cast(value).getString(); + case TRUE: + return true; + case FALSE: + return false; + case NUMBER: + return JsonNumber.class.cast(value).doubleValue(); + case NULL: + return null; + case OBJECT: + default: + return value; } } } @@ -307,7 +283,6 @@ public static class ConditionGroup { private final Function, Boolean> aggregator; public boolean isVisible(final JsonObject payload) { - System.err.println("[ConditionGroup::isVisible]: conditions size :" +conditions.size()); return conditions .stream() .allMatch(group -> aggregator.apply(conditions.stream().map(c -> c.evaluateCondition(payload)))); diff --git a/component-runtime-manager/src/test/java/org/talend/sdk/component/runtime/manager/ReflectionServiceTest.java b/component-runtime-manager/src/test/java/org/talend/sdk/component/runtime/manager/ReflectionServiceTest.java index 953722669c02f..a168a659095bb 100644 --- a/component-runtime-manager/src/test/java/org/talend/sdk/component/runtime/manager/ReflectionServiceTest.java +++ b/component-runtime-manager/src/test/java/org/talend/sdk/component/runtime/manager/ReflectionServiceTest.java @@ -905,14 +905,13 @@ void nestedRequiredActiveIfFiltersKo() throws NoSuchMethodException { payload.put("configuration.logicalOpType", "AL"); payload.put("configuration.filters[0].columnName", "col0"); payload.put("configuration.filters[0].operator", "IS_NULL"); - payload.put("configuration.filters[0].value", ""); + payload.put("configuration.filters[0].value", "V"); payload.put("configuration.filters[1].columnName", "col1"); payload.put("configuration.filters[1].operator", "IS_EMPTY"); -// payload.put("configuration.filters[1].value", ""); - // FIXME this array element SHOULD BE VISIBLE and its value required + payload.put("configuration.filters[1].value", "V"); + // this array element SHOULD BE VISIBLE and its value required payload.put("configuration.filters[2].columnName", "col2"); payload.put("configuration.filters[2].operator", "IS_VALID"); -// payload.put("configuration.filters[2].value", ""); try { buildObjectParams("visibility", payload, MethodsHolder.FilterConfiguration.class); fail("configuration.filters[2].operator=IS_VALID should be visible!"); diff --git a/component-runtime-manager/src/test/java/org/talend/sdk/component/runtime/manager/reflect/visibility/VisibilityServiceTest.java b/component-runtime-manager/src/test/java/org/talend/sdk/component/runtime/manager/reflect/visibility/VisibilityServiceTest.java index 4d1a10cc3a528..9f2287eb48665 100644 --- a/component-runtime-manager/src/test/java/org/talend/sdk/component/runtime/manager/reflect/visibility/VisibilityServiceTest.java +++ b/component-runtime-manager/src/test/java/org/talend/sdk/component/runtime/manager/reflect/visibility/VisibilityServiceTest.java @@ -42,10 +42,10 @@ void one() { final VisibilityService.ConditionGroup conditionGroup = service .build(new ParameterMeta(null, String.class, ParameterMeta.Type.STRING, "foo", "foo", null, emptyList(), emptyList(), metadata, false)); + assertFalse(conditionGroup.isVisible(Json.createObjectBuilder().build())); assertTrue(conditionGroup.isVisible(Json.createObjectBuilder().add("the_target", "a").build())); assertTrue(conditionGroup.isVisible(Json.createObjectBuilder().add("the_target", "c").build())); assertFalse(conditionGroup.isVisible(Json.createObjectBuilder().add("the_target", "d").build())); - assertFalse(conditionGroup.isVisible(Json.createObjectBuilder().build())); } @Test @@ -139,6 +139,46 @@ void activeIfsWithArray() { .build())); } + @Test + void activeIfsWithArray_false() { + final Map metadata = new HashMap<>(); + metadata.put("tcomp::condition::ifs::operator", "AND"); + metadata.put("tcomp::condition::if::target::0", "arry[${index}].operator"); + metadata.put("tcomp::condition::if::value::0", "IS_NULL"); + metadata.put("tcomp::condition::if::negate::0", "false"); + metadata.put("tcomp::condition::if::evaluationStrategy::0", "DEFAULT"); + metadata.put("tcomp::condition::if::target::1", "arry[${index}].operator"); + metadata.put("tcomp::condition::if::value::1", "IS_NOT_NULL"); + metadata.put("tcomp::condition::if::negate::1", "false"); + metadata.put("tcomp::condition::if::evaluationStrategy::1", "DEFAULT"); + metadata.put("tcomp::condition::if::target::2", "arry[${index}].operator"); + metadata.put("tcomp::condition::if::value::2", "IS_EMPTY"); + metadata.put("tcomp::condition::if::negate::2", "false"); + metadata.put("tcomp::condition::if::evaluationStrategy::2", "DEFAULT"); + metadata.put("tcomp::condition::if::target::3", "arry[${index}].operator"); + metadata.put("tcomp::condition::if::value::3", "IS_NOT_EMPTY"); + metadata.put("tcomp::condition::if::negate::3", "false"); + metadata.put("tcomp::condition::if::evaluationStrategy::3", "DEFAULT"); + final VisibilityService.ConditionGroup conditionGroup = service + .build(new ParameterMeta(null, String.class, ParameterMeta.Type.STRING, "filter", "filter", null, + emptyList(), emptyList(), metadata, false)); + assertTrue(conditionGroup.isVisible(Json.createObjectBuilder() + .add("arry", + Json.createArrayBuilder() + .add(Json.createObjectBuilder().add("operator", "IS_NULL").build()) + .add(Json.createObjectBuilder().add("operator", "IS_NOT_EMPTY").build())) + .build())); + assertTrue(conditionGroup.isVisible(Json.createObjectBuilder() + .add("arry", + Json.createArrayBuilder() + .add(Json.createObjectBuilder().add("operator", "IS_NOT_NULL").build()) + .add(Json.createObjectBuilder().add("operator", "IS_EMPTY").build())) + .build())); + assertFalse(conditionGroup.isVisible(Json.createObjectBuilder() + .add("arry", Json.createArrayBuilder() + .add(Json.createObjectBuilder().add("operator", "IS_VALID").build())) + .build())); + } @Test void activeIfWithArray() { final Map metadata = new HashMap<>(); @@ -163,4 +203,27 @@ void activeIfWithArray() { .build())); } + @Test + void activeIfWithArray_false() { + final Map metadata = new HashMap<>(); + metadata.put("tcomp::condition::if::target::0", "arry[${index}].operator"); + metadata.put("tcomp::condition::if::value::0", "IS_NULL,IS_NOT_NULL,IS_EMPTY,IS_NOT_EMPTY"); + metadata.put("tcomp::condition::if::negate::0", "false"); + metadata.put("tcomp::condition::if::evaluationStrategy::0", "DEFAULT"); + final VisibilityService.ConditionGroup conditionGroup = service + .build(new ParameterMeta(null, String.class, ParameterMeta.Type.STRING, "filter", "filter", null, + emptyList(), emptyList(), metadata, false)); + assertTrue(conditionGroup.isVisible(Json.createObjectBuilder() + .add("arry", + Json.createArrayBuilder() + .add(Json.createObjectBuilder().add("operator", "IS_NULL").build()) + .add(Json.createObjectBuilder().add("operator", "IS_NOT_NULL").build()) + .add(Json.createObjectBuilder().add("operator", "IS_EMPTY").build()) + .add(Json.createObjectBuilder().add("operator", "IS_NOT_EMPTY").build())) + .build())); + assertFalse(conditionGroup.isVisible(Json.createObjectBuilder() + .add("arry", Json.createArrayBuilder() + .add(Json.createObjectBuilder().add("operator", "IS_VALID").build())) + .build())); + } } From 171c99bf1bd432b4ce60b9f87d895bb1013581e0 Mon Sep 17 00:00:00 2001 From: yyin-talend Date: Wed, 12 Apr 2023 14:33:38 +0800 Subject: [PATCH 5/7] fix(TCOMP-2337): fix imports --- .../manager/reflect/visibility/VisibilityService.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/component-runtime-manager/src/main/java/org/talend/sdk/component/runtime/manager/reflect/visibility/VisibilityService.java b/component-runtime-manager/src/main/java/org/talend/sdk/component/runtime/manager/reflect/visibility/VisibilityService.java index 7875f47ced983..8973cafe8fffa 100644 --- a/component-runtime-manager/src/main/java/org/talend/sdk/component/runtime/manager/reflect/visibility/VisibilityService.java +++ b/component-runtime-manager/src/main/java/org/talend/sdk/component/runtime/manager/reflect/visibility/VisibilityService.java @@ -25,9 +25,12 @@ import javax.json.*; import javax.json.spi.JsonProvider; import java.lang.reflect.Array; +import java.util.Arrays; import java.util.Collection; +import java.util.HashMap; import java.util.Map; import java.util.function.Function; +import java.util.List; import java.util.stream.IntStream; import java.util.stream.Stream; @@ -36,12 +39,12 @@ import javax.json.JsonPointer; import javax.json.JsonString; import javax.json.JsonValue; -import javax.json.spi.JsonProvider; import org.talend.sdk.component.runtime.manager.ParameterMeta; import lombok.NoArgsConstructor; import lombok.RequiredArgsConstructor; +import lombok.ToString; @RequiredArgsConstructor public class VisibilityService { From 6ffbf834cb25a11e3b02264d8c33f5dc067ffab6 Mon Sep 17 00:00:00 2001 From: yyin-talend Date: Wed, 12 Apr 2023 14:43:29 +0800 Subject: [PATCH 6/7] fix{TCOMP-2337): fix --- .../manager/reflect/visibility/VisibilityService.java | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/component-runtime-manager/src/main/java/org/talend/sdk/component/runtime/manager/reflect/visibility/VisibilityService.java b/component-runtime-manager/src/main/java/org/talend/sdk/component/runtime/manager/reflect/visibility/VisibilityService.java index 8973cafe8fffa..11efd540d3271 100644 --- a/component-runtime-manager/src/main/java/org/talend/sdk/component/runtime/manager/reflect/visibility/VisibilityService.java +++ b/component-runtime-manager/src/main/java/org/talend/sdk/component/runtime/manager/reflect/visibility/VisibilityService.java @@ -22,24 +22,23 @@ import static java.util.stream.Collectors.toMap; import static lombok.AccessLevel.PRIVATE; -import javax.json.*; -import javax.json.spi.JsonProvider; import java.lang.reflect.Array; import java.util.Arrays; import java.util.Collection; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.function.Function; -import java.util.List; import java.util.stream.IntStream; import java.util.stream.Stream; +import javax.json.JsonArrayBuilder; import javax.json.JsonNumber; import javax.json.JsonObject; import javax.json.JsonPointer; import javax.json.JsonString; import javax.json.JsonValue; - +import javax.json.spi.JsonProvider; import org.talend.sdk.component.runtime.manager.ParameterMeta; import lombok.NoArgsConstructor; From 5e2d1d7da358fcd3ee5ba81fccf2ad9a05acc798 Mon Sep 17 00:00:00 2001 From: yyin-talend Date: Wed, 12 Apr 2023 14:50:03 +0800 Subject: [PATCH 7/7] fix(TCOMP-2337):fix order --- .../runtime/manager/reflect/visibility/VisibilityService.java | 1 + 1 file changed, 1 insertion(+) diff --git a/component-runtime-manager/src/main/java/org/talend/sdk/component/runtime/manager/reflect/visibility/VisibilityService.java b/component-runtime-manager/src/main/java/org/talend/sdk/component/runtime/manager/reflect/visibility/VisibilityService.java index 11efd540d3271..0adbea7232837 100644 --- a/component-runtime-manager/src/main/java/org/talend/sdk/component/runtime/manager/reflect/visibility/VisibilityService.java +++ b/component-runtime-manager/src/main/java/org/talend/sdk/component/runtime/manager/reflect/visibility/VisibilityService.java @@ -39,6 +39,7 @@ import javax.json.JsonString; import javax.json.JsonValue; import javax.json.spi.JsonProvider; + import org.talend.sdk.component.runtime.manager.ParameterMeta; import lombok.NoArgsConstructor;