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 @@ public Stream<String> validate(final AnnotationFinder finder, final List<Class<?
// 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 @@ public Stream<String> validate(final AnnotationFinder finder, final List<Class<?
datasetDiscover, //
discover, //
discoverProcessor, //
dynamicDependencyErrors, //
updatesErrors, //
enumProposable, //
proposableWithoutDynamic) //
Expand Down Expand Up @@ -206,6 +213,37 @@ private Stream<String> findDiscoverSchemaExtendedErrors(final AnnotationFinder f
.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();

return Stream.of(returnType, optionParameter)
.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 +332,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;
}
}
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(4, 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