diff --git a/.gitignore b/.gitignore
index ce463d6bc58d5..b6dbe45b555a1 100644
--- a/.gitignore
+++ b/.gitignore
@@ -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
diff --git a/component-api/src/main/java/org/talend/sdk/component/api/service/schema/FixedSchema.java b/component-api/src/main/java/org/talend/sdk/component/api/service/schema/FixedSchema.java
index f79d48508bd6b..76d30845aad3b 100644
--- a/component-api/src/main/java/org/talend/sdk/component/api/service/schema/FixedSchema.java
+++ b/component-api/src/main/java/org/talend/sdk/component/api/service/schema/FixedSchema.java
@@ -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 "";
@@ -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.
+ *
+ * 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.
+ *
+ * Requires {@link #value()} to be set.
+ *
+ * @return the parameter paths to watch. Default to none.
+ */
+ String[] watch() default {};
+
}
diff --git a/component-runtime-manager/src/main/java/org/talend/sdk/component/runtime/manager/extension/ComponentSchemaEnricher.java b/component-runtime-manager/src/main/java/org/talend/sdk/component/runtime/manager/extension/ComponentSchemaEnricher.java
index 3e3b92acb6a53..ebb35f021aab7 100644
--- a/component-runtime-manager/src/main/java/org/talend/sdk/component/runtime/manager/extension/ComponentSchemaEnricher.java
+++ b/component-runtime-manager/src/main/java/org/talend/sdk/component/runtime/manager/extension/ComponentSchemaEnricher.java
@@ -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> SUPPORTED_ANNOTATIONS =
new HashSet<>(Arrays.asList(PartitionMapper.class, Emitter.class, Processor.class, DriverRunner.class));
@@ -84,6 +86,9 @@ public Map 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;
}
diff --git a/component-runtime-manager/src/test/java/org/talend/sdk/component/runtime/manager/extension/ComponentSchemaEnricherTest.java b/component-runtime-manager/src/test/java/org/talend/sdk/component/runtime/manager/extension/ComponentSchemaEnricherTest.java
index 7518f6caacad1..ac6849bc69c8f 100644
--- a/component-runtime-manager/src/test/java/org/talend/sdk/component/runtime/manager/extension/ComponentSchemaEnricherTest.java
+++ b/component-runtime-manager/src/test/java/org/talend/sdk/component/runtime/manager/extension/ComponentSchemaEnricherTest.java
@@ -60,6 +60,18 @@ void fixedSchemaMetadataNotPresent() {
assertEquals(emptyMap(), enricher.onComponent(MyProcessor.class, MyProcessor.class.getAnnotations()));
}
+ @Test
+ void fixedSchemaMetadataWithWatchPresent() {
+ assertEquals(new HashMap() {
+
+ {
+ 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() {
@@ -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 {
+
+ }
+}
\ No newline at end of file
diff --git a/component-tools/src/main/java/org/talend/sdk/component/tools/validator/FixedSchemaValidator.java b/component-tools/src/main/java/org/talend/sdk/component/tools/validator/FixedSchemaValidator.java
index 3c8750e031343..1dd922fcf337b 100644
--- a/component-tools/src/main/java/org/talend/sdk/component/tools/validator/FixedSchemaValidator.java
+++ b/component-tools/src/main/java/org/talend/sdk/component/tools/validator/FixedSchemaValidator.java
@@ -43,6 +43,14 @@ public Stream validate(final AnnotationFinder finder, final List 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 methods = Stream
.concat(finder.findAnnotatedMethods(DiscoverSchema.class)
diff --git a/component-tools/src/test/java/org/talend/sdk/component/tools/validator/FixedSchemaValidatorTest.java b/component-tools/src/test/java/org/talend/sdk/component/tools/validator/FixedSchemaValidatorTest.java
index 1c39da34af2d6..9d265a209e038 100644
--- a/component-tools/src/test/java/org/talend/sdk/component/tools/validator/FixedSchemaValidatorTest.java
+++ b/component-tools/src/test/java/org/talend/sdk/component/tools/validator/FixedSchemaValidatorTest.java
@@ -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;
@@ -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 errors =
+ validator.validate(finder, Arrays.asList(MySourceWatchNoValue.class, FixedService.class));
+ final List 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 {
@@ -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;
+ }
+ }
}
diff --git a/sample-parent/sample-features/fixed-schema/src/main/java/org/talend/sdk/component/sample/feature/fixedschema/input/FixedSchemaInput.java b/sample-parent/sample-features/fixed-schema/src/main/java/org/talend/sdk/component/sample/feature/fixedschema/input/FixedSchemaInput.java
index 5b2e34c3b9b01..44f1266dc7d39 100644
--- a/sample-parent/sample-features/fixed-schema/src/main/java/org/talend/sdk/component/sample/feature/fixedschema/input/FixedSchemaInput.java
+++ b/sample-parent/sample-features/fixed-schema/src/main/java/org/talend/sdk/component/sample/feature/fixedschema/input/FixedSchemaInput.java
@@ -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;