Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -73,3 +73,4 @@ documentation/rebuildpdf.bat
.ai-commons/skills/manage-zephyr-test-cases/references/.env
.claude/CLAUDE.md
.github/copilot-instructions.md
.claude/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@
@Retention(RUNTIME)
@Documentation("Mark a connector as having a fixed schema. " +
"Annotation's value must match the name of a DiscoverSchema or a DiscoverSchemaExtended annotation. " +
"The related action will return the fixed schema for the specified flows.")
"The related action will return the fixed schema for the specified flows. " +
"Use watch() to declare parameter paths whose changes should trigger a schema refresh at design time.")
public @interface FixedSchema {

String value() default "";
Expand All @@ -42,4 +43,19 @@
*/
String[] flows() default {};

/**
* The parameter paths to watch for changes at design time. When any of the listed parameters
* changes, the associated {@code DiscoverSchema} or {@code DiscoverSchemaExtended} service
* will be re-invoked to refresh the fixed schema dynamically.
* <p>
* Uses the same relative-path syntax as {@code @Updatable.parameters()}: for example,
* {@code "configuration/someParam"} refers to a child parameter of the component configuration root,
* and {@code "../sibling"} refers to a sibling parameter.
* <p>
* Requires {@link #value()} to be set.
*
* @return the parameter paths to watch. Default to none.
*/
String[] watch() default {};

}
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ public class ComponentSchemaEnricher implements ComponentMetadataEnricher {

public static final String FIXED_SCHEMA_FLOWS_META_PREFIX = "tcomp::ui::schema::flows::fixed";

public static final String FIXED_SCHEMA_WATCH_META_PREFIX = "tcomp::ui::schema::fixed::watch";

private static final Set<Class<? extends Annotation>> SUPPORTED_ANNOTATIONS =
new HashSet<>(Arrays.asList(PartitionMapper.class, Emitter.class, Processor.class, DriverRunner.class));

Expand Down Expand Up @@ -84,6 +86,9 @@ public Map<String, String> onComponent(final Type type, final Annotation[] annot
} else {
metadata.put(FIXED_SCHEMA_FLOWS_META_PREFIX, Branches.DEFAULT_BRANCH);
}
if (fixedSchema.watch().length > 0) {
metadata.put(FIXED_SCHEMA_WATCH_META_PREFIX, String.join(",", fixedSchema.watch()));
}

return metadata;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,18 @@ void fixedSchemaMetadataNotPresent() {
assertEquals(emptyMap(), enricher.onComponent(MyProcessor.class, MyProcessor.class.getAnnotations()));
}

@Test
void fixedSchemaMetadataWithWatchPresent() {
assertEquals(new HashMap<String, String>() {

{
put("tcomp::ui::schema::fixed", "discover");
put("tcomp::ui::schema::flows::fixed", "__default__");
put("tcomp::ui::schema::fixed::watch", "configuration/param1,configuration/param2");
}
}, enricher.onComponent(EmitterWithWatch.class, EmitterWithWatch.class.getAnnotations()));
}

@Test
void dbMappingMetadataPresent() {
assertEquals(new HashMap<String, String>() {
Expand Down Expand Up @@ -103,4 +115,10 @@ private static class MyProcessor {
private static class MyEmitter {

}
}

@Emitter
@FixedSchema(value = "discover", watch = { "configuration/param1", "configuration/param2" })
private static class EmitterWithWatch {

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,14 @@ public Stream<String> validate(final AnnotationFinder finder, final List<Class<?
.map(e -> String.format("Empty @FixedSchema annotation's value in class %s.",
e.getKey().getSimpleName()))
.toList());
// search for watch() declared without value()
errors.addAll(finder.findAnnotatedClasses(FixedSchema.class)
.stream()
.filter(c -> c.getAnnotation(FixedSchema.class).watch().length > 0
&& c.getAnnotation(FixedSchema.class).value().isEmpty())
.map(c -> String.format("@FixedSchema.watch() requires value() to be set in class %s.",
c.getSimpleName()))
.toList());
// search for missing methods
final List<String> methods = Stream
.concat(finder.findAnnotatedMethods(DiscoverSchema.class)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,11 @@
package org.talend.sdk.component.tools.validator;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;

import java.io.Serializable;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;

import org.apache.xbean.finder.AnnotationFinder;
Expand Down Expand Up @@ -53,6 +55,20 @@ void validateFixedSchema() {
assertEquals(2, errors.count());
}

@Test
void validateFixedSchemaWatchWithoutValue() {
final FixedSchemaValidator validator = new FixedSchemaValidator();
final AnnotationFinder finder =
new AnnotationFinder(new ClassesArchive(MySourceWatchNoValue.class, FixedService.class));
final Stream<String> errors =
validator.validate(finder, Arrays.asList(MySourceWatchNoValue.class, FixedService.class));
final List<String> errorList = errors.toList();
assertEquals(2, errorList.size());
assertTrue(errorList.stream()
.anyMatch(e -> e.contains("@FixedSchema.watch() requires value() to be set")
&& e.contains("MySourceWatchNoValue")));
}

@Emitter(family = "test", name = "mysource0")
@FixedSchema("discover")
static class MySourceOk implements Serializable {
Expand Down Expand Up @@ -113,4 +129,14 @@ public Schema discover(DS ds, String branch) {
return null;
}
}

@Emitter(family = "test", name = "mysourcewatchnovalue")
@FixedSchema(watch = { "configuration/param" })
static class MySourceWatchNoValue implements Serializable {

@Producer
public Record next() {
return null;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
@Icon(value = Icon.IconType.CUSTOM, custom = "icon")
@Emitter(name = "Input")
@Documentation("Fixed schema sample input connector.")
@FixedSchema("fixedschemadse")
@FixedSchema(value = "fixedschemadse", watch = { "configuration/aBoolean" })
public class FixedSchemaInput implements Serializable {

private final Config config;
Expand Down
Loading