diff --git a/component-tools/src/main/java/org/talend/sdk/component/tools/validator/ActionValidator.java b/component-tools/src/main/java/org/talend/sdk/component/tools/validator/ActionValidator.java index f84e9541a31bf..12b6b8084918d 100644 --- a/component-tools/src/main/java/org/talend/sdk/component/tools/validator/ActionValidator.java +++ b/component-tools/src/main/java/org/talend/sdk/component/tools/validator/ActionValidator.java @@ -22,6 +22,8 @@ import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.lang.reflect.Parameter; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; import java.util.Arrays; import java.util.List; import java.util.Map; @@ -40,6 +42,7 @@ import org.talend.sdk.component.api.service.ActionType; import org.talend.sdk.component.api.service.Service; import org.talend.sdk.component.api.service.completion.DynamicValues; +import org.talend.sdk.component.api.service.dependency.DynamicDependencies; import org.talend.sdk.component.api.service.discovery.DiscoverDataset; import org.talend.sdk.component.api.service.healthcheck.HealthCheck; import org.talend.sdk.component.api.service.schema.DiscoverSchema; @@ -95,6 +98,9 @@ public Stream validate(final AnnotationFinder finder, final List discoverProcessor = findDiscoverSchemaExtendedErrors(finder); + // parameters for @DynamicDependencies + final Stream dynamicDependencyErrors = findDynamicDependenciesErrors(finder); + // returned type for @Update, for now limit it on objects and not primitives final Stream updatesErrors = this.findUpdatesErrors(finder); @@ -130,6 +136,7 @@ public Stream validate(final AnnotationFinder finder, final List findDiscoverSchemaExtendedErrors(final AnnotationFinder f .orElseGet(Stream::empty); } + /** + * Checks method signature for @DynamicDependencies annotation. + * Valid signatures are: + *
    + *
  • public List getDependencies(@Option("configuration") final TheDataset dataset)
  • + *
+ * + * @param finder + * @return Errors on @DynamicDependencies method + */ + private Stream findDynamicDependenciesErrors(final AnnotationFinder finder) { + + final Stream optionParameter = finder + .findAnnotatedMethods(DynamicDependencies.class) + .stream() + .filter(m -> !hasOption(m) || !hasDatasetParameter(m)) + .map(m -> m + " should have a Dataset parameter marked with @Option") + .sorted(); + + final Stream returnType = finder + .findAnnotatedMethods(DynamicDependencies.class) + .stream() + .filter(m -> !hasStringInList(m)) + .map(m -> m + " should return List") + .sorted(); + + return Stream.of(returnType, optionParameter) + .reduce(Stream::concat) + .orElseGet(Stream::empty); + } + private Stream findUpdatesErrors(final AnnotationFinder finder) { final Map updates = finder .findAnnotatedMethods(Update.class) @@ -284,6 +322,12 @@ private boolean hasSchemaCorrectNaming(final Method method) { .count() == 1; } + private boolean hasDatasetParameter(final Method method) { + return Arrays.stream(method.getParameters()) + .filter(p -> p.getType().isAnnotationPresent(DataSet.class)) + .count() == 1; + } + private boolean hasBranchCorrectNaming(final Method method) { return Arrays.stream(method.getParameters()) .filter(p -> String.class.isAssignableFrom(p.getType())) @@ -294,4 +338,15 @@ private boolean hasBranchCorrectNaming(final Method method) { private boolean hasCorrectReturnType(final Method method) { return Schema.class.isAssignableFrom(method.getReturnType()); } + + private boolean hasStringInList(final Method method) { + if (List.class.isAssignableFrom(method.getReturnType()) + && method.getGenericReturnType() instanceof ParameterizedType) { + Type[] actualTypeArguments = ((ParameterizedType) method.getGenericReturnType()).getActualTypeArguments(); + if (actualTypeArguments.length > 0) { + return "java.lang.String".equals(actualTypeArguments[0].getTypeName()); + } + } + return false; + } } diff --git a/component-tools/src/main/java/org/talend/sdk/component/tools/validator/Validators.java b/component-tools/src/main/java/org/talend/sdk/component/tools/validator/Validators.java index 8355938a75d31..a0f0ff7d234fe 100644 --- a/component-tools/src/main/java/org/talend/sdk/component/tools/validator/Validators.java +++ b/component-tools/src/main/java/org/talend/sdk/component/tools/validator/Validators.java @@ -33,6 +33,7 @@ import org.talend.sdk.component.api.service.asyncvalidation.AsyncValidation; import org.talend.sdk.component.api.service.completion.DynamicValues; import org.talend.sdk.component.api.service.completion.Suggestions; +import org.talend.sdk.component.api.service.dependency.DynamicDependencies; import org.talend.sdk.component.api.service.healthcheck.HealthCheck; import org.talend.sdk.component.api.service.schema.DiscoverSchema; import org.talend.sdk.component.api.service.update.Update; @@ -179,7 +180,7 @@ public static Validators build(final Configuration configuration, final Validato public static Stream> getActionsStream() { return of(AsyncValidation.class, DynamicValues.class, HealthCheck.class, DiscoverSchema.class, - Suggestions.class, Update.class); + Suggestions.class, Update.class, DynamicDependencies.class); } public static Stream flatten(final Collection options) { diff --git a/component-tools/src/test/java/org/talend/sdk/component/tools/validator/ActionValidatorTest.java b/component-tools/src/test/java/org/talend/sdk/component/tools/validator/ActionValidatorTest.java index 993c438a8e59b..e774991ad9f8d 100644 --- a/component-tools/src/test/java/org/talend/sdk/component/tools/validator/ActionValidatorTest.java +++ b/component-tools/src/test/java/org/talend/sdk/component/tools/validator/ActionValidatorTest.java @@ -36,6 +36,7 @@ import org.talend.sdk.component.api.service.Service; import org.talend.sdk.component.api.service.completion.DynamicValues; import org.talend.sdk.component.api.service.completion.Values; +import org.talend.sdk.component.api.service.dependency.DynamicDependencies; import org.talend.sdk.component.api.service.discovery.DiscoverDataset; import org.talend.sdk.component.api.service.discovery.DiscoverDatasetResult; import org.talend.sdk.component.api.service.discovery.DiscoverDatasetResult.DatasetDescription; @@ -73,6 +74,19 @@ void validateDiscoverProcessorSchema() { assertEquals(13, errors.count()); } + @Test + void validateDynamicDependencies() { + final ActionValidator validator = new ActionValidator(new FakeHelper()); + AnnotationFinder finder = new AnnotationFinder(new ClassesArchive(ActionDynamicDependenciesOK.class)); + final Stream noerrors = + validator.validate(finder, Arrays.asList(ActionDynamicDependenciesOK.class)); + assertEquals(0, noerrors.count()); + + finder = new AnnotationFinder(new ClassesArchive(ActionDynamicDependenciesKO.class)); + final Stream errors = validator.validate(finder, Arrays.asList(ActionDynamicDependenciesKO.class)); + assertEquals(10, errors.count()); + } + @Test void validate() { final ActionValidator validator = new ActionValidator(new FakeHelper()); @@ -263,4 +277,48 @@ public Record guessProcessorSchemaKo6(@Option FakeDataSet configuration, RecordB return null; } } + + @Service + static class ActionDynamicDependenciesOK { + + @DynamicDependencies("test-all") + public List getDynamicDependencies(@Option("configuration") final FakeDataSet dataset) { + return null; + } + } + + @Service + static class ActionDynamicDependenciesKO { + + @DynamicDependencies("error: return List") + public String getDynamicDependencies(@Option("configuration") final FakeDataSet dataset) { + return null; + } + + @DynamicDependencies("error-param:no Option, no Dataset") + public List getDynamicDependencies2() { + return null; + } + + @DynamicDependencies("error: param not dataset") + public String getDynamicDependencies3(@Option("configuration") final FakeDataStore dataset) { + return null; + } + + @DynamicDependencies("error: param not option") + public List getDynamicDependencies4(final FakeDataStore dataset) { + return null; + } + + @DynamicDependencies("error: List T not String") + public List getDynamicDependencies5(@Option("configuration") final FakeDataSet dataset) { + return null; + } + + @DynamicDependencies("error: 2 params") + public List getDynamicDependencies6(@Option("configuration") final FakeDataSet dataset, + @Option("configuration") final FakeDataSet dataset2) { + return null; + } + } } \ No newline at end of file