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

feat(TCOMP-2739):Create an action validator for the @DynamicDependencies annotation #901

Merged
merged 9 commits into from
Jun 28, 2024
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -95,6 +98,9 @@
// parameters for @DiscoverSchemaExtended
final Stream<String> discoverProcessor = findDiscoverSchemaExtendedErrors(finder);

// parameters for @DynamicDependencies
final Stream<String> dynamicDependencyErrors = findDynamicDependenciesErrors(finder);

// returned type for @Update, for now limit it on objects and not primitives
final Stream<String> updatesErrors = this.findUpdatesErrors(finder);

Expand Down Expand Up @@ -130,6 +136,7 @@
datasetDiscover, //
discover, //
discoverProcessor, //
dynamicDependencyErrors, //
updatesErrors, //
enumProposable, //
proposableWithoutDynamic) //
Expand Down Expand Up @@ -206,6 +213,44 @@
.orElseGet(Stream::empty);
}

/**
* Checks method signature for @DynamicDependencies annotation.
* Valid signatures are:
* <ul>
* <li>public List<String> getDependencies(@Option("configuration") final TheDataset dataset)</li>
* </ul>
*
* @param finder
* @return Errors on @DynamicDependencies method
*/
private Stream<String> findDynamicDependenciesErrors(final AnnotationFinder finder) {

final Stream<String> optionParameter = finder
.findAnnotatedMethods(DynamicDependencies.class)
.stream()
.filter(m -> !hasOption(m))
undx marked this conversation as resolved.
Show resolved Hide resolved
.map(m -> m + " should have a parameter being an option (marked with @Option)")
undx marked this conversation as resolved.
Show resolved Hide resolved
.sorted();

final Stream<String> returnType = finder
.findAnnotatedMethods(DynamicDependencies.class)
.stream()
.filter(m -> !hasStringInList(m))
.map(m -> m + " should return List<String>")
.sorted();

final Stream<String> dataset = finder
.findAnnotatedMethods(DynamicDependencies.class)
.stream()
.filter(m -> !hasTypeParameter(m, DataSet.class))
.map(m -> m + " should have its Dataset parameter")
.sorted();
undx marked this conversation as resolved.
Show resolved Hide resolved

return Stream.of(returnType, optionParameter, dataset)
.reduce(Stream::concat)
.orElseGet(Stream::empty);
}

private Stream<String> findUpdatesErrors(final AnnotationFinder finder) {
final Map<String, Method> updates = finder
.findAnnotatedMethods(Update.class)
Expand Down Expand Up @@ -294,4 +339,20 @@
private boolean hasCorrectReturnType(final Method method) {
return Schema.class.isAssignableFrom(method.getReturnType());
}

private boolean hasListReturnType(final Method method) {

Check warning on line 343 in component-tools/src/main/java/org/talend/sdk/component/tools/validator/ActionValidator.java

View check run for this annotation

sonar-eks / Component Runtime Sonarqube Results

component-tools/src/main/java/org/talend/sdk/component/tools/validator/ActionValidator.java#L343

Remove this unused private "hasListReturnType" method.
undx marked this conversation as resolved.
Show resolved Hide resolved
return List.class.isAssignableFrom(method.getReturnType());
}

private boolean hasStringInList(final Method method) {
if (List.class.isAssignableFrom(method.getReturnType())) {
if (method.getGenericReturnType() instanceof ParameterizedType) {

Check warning on line 349 in component-tools/src/main/java/org/talend/sdk/component/tools/validator/ActionValidator.java

View check run for this annotation

sonar-eks / Component Runtime Sonarqube Results

component-tools/src/main/java/org/talend/sdk/component/tools/validator/ActionValidator.java#L349

Merge this if statement with the enclosing one.
Type[] actualTypeArguments = ((ParameterizedType) method.getGenericReturnType()).getActualTypeArguments();
if (actualTypeArguments.length > 0) {
return "java.lang.String".equals(actualTypeArguments[0].getTypeName());
}
}
}
return false;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -179,7 +180,7 @@ public static Validators build(final Configuration configuration, final Validato

public static Stream<Class<? extends Annotation>> getActionsStream() {
return of(AsyncValidation.class, DynamicValues.class, HealthCheck.class, DiscoverSchema.class,
Suggestions.class, Update.class);
Suggestions.class, Update.class, DynamicDependencies.class);
}

public static Stream<ParameterMeta> flatten(final Collection<ParameterMeta> options) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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<String> noerrors =
validator.validate(finder, Arrays.asList(ActionDynamicDependenciesOK.class));
assertEquals(0, noerrors.count());

finder = new AnnotationFinder(new ClassesArchive(ActionDynamicDependenciesKO.class));
final Stream<String> errors = validator.validate(finder, Arrays.asList(ActionDynamicDependenciesKO.class));
assertEquals(6, errors.count());
}

@Test
void validate() {
final ActionValidator validator = new ActionValidator(new FakeHelper());
Expand Down Expand Up @@ -263,4 +277,32 @@ public Record guessProcessorSchemaKo6(@Option FakeDataSet configuration, RecordB
return null;
}
}

@Service
static class ActionDynamicDependenciesOK {

@DynamicDependencies("test-all")
public List<String> getDynamicDependencies(@Option("configuration") final DataSet dataset) {
undx marked this conversation as resolved.
Show resolved Hide resolved
return null;
}
}

@Service
static class ActionDynamicDependenciesKO {

@DynamicDependencies("error: return List<String>")
public String getDynamicDependencies(@Option("configuration") final DataSet dataset) {
undx marked this conversation as resolved.
Show resolved Hide resolved
return null;
}

@DynamicDependencies("error-param:no Option, no Dataset")
public List<String> getDynamicDependencies2() {
return null;
}

@DynamicDependencies("error: List<T> T not String, param not dataset")
public List<Object> getDynamicDependencies3(@Option("configuration") final FakeDataStore dataset) {
return null;
}
}
}
Loading