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.")

@thboileau thboileau Jul 28, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like that we need to update the documentation of the framework (cf "documentation" root directory of this repository)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 20da2fb3eb0 — added a "Watching parameters for schema refresh" subsection to documentation/src/main/antora/modules/ROOT/pages/studio-schema.adoc documenting the watch() attribute, with a code example mirroring the sample-features/fixed-schema usage.

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 @@ -329,6 +329,28 @@ public class UiServices {
----


=== Watching parameters for schema refresh

NOTE: Available since 1.95.x release

By default, a `@FixedSchema` is resolved once, when the component instance is created in the designer.
Use `watch()` to declare parameter paths whose changes should trigger a schema refresh at design time —
this is useful when a parameter changes the shape of the fixed schema (for instance, an option that adds
or removes a column).

`watch()` uses the same relative-path syntax as `@Updatable.parameters()` (`.` for self, `../sibling` for
a sibling parameter, `child/path` for a child parameter) and requires `value()` to be set. Each time a
watched parameter changes, the `@DiscoverSchema` or `@DiscoverSchemaExtended` action referenced by `value()`
is re-invoked to refresh the fixed schema.

[source,java]
----
@FixedSchema(value = "fixedschemadse", watch = { "configuration/aBoolean" })
@Emitter(name = "Input")
public class MyEmitter implements Serializable {
...
----

ifeval::["{backend}" == "html5"]
[role="relatedlinks"]
== Related articles
Expand Down
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