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..68ea8b7eebc34 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 @@ -42,4 +42,13 @@ */ String[] flows() default {}; + /** + * Parameter paths that trigger a schema refresh in Studio when their value changes. + * Uses the same dot-notation as {@code @Updatable#parameters()}, relative to the component root. + * Example: {@code watch = {"configuration.dataSet.connection.url"}} + * + * @return the parameter paths to watch. Default to none (no dynamic refresh). + */ + 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..5e5de732b3953 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,38 @@ void fixedSchemaMetadataNotPresent() { assertEquals(emptyMap(), enricher.onComponent(MyProcessor.class, MyProcessor.class.getAnnotations())); } + @Test + void fixedSchemaWatchSinglePath() { + assertEquals(new HashMap() { + + { + put("tcomp::ui::schema::fixed", "discover"); + put("tcomp::ui::schema::flows::fixed", "__default__"); + put("tcomp::ui::schema::fixed::watch", "configuration.dataSet.connection.url"); + } + }, enricher.onComponent(EmitterWithWatch.class, EmitterWithWatch.class.getAnnotations())); + } + + @Test + void fixedSchemaWatchMultiplePaths() { + assertEquals(new HashMap() { + + { + put("tcomp::ui::schema::fixed", "discover"); + put("tcomp::ui::schema::flows::fixed", "__default__"); + put("tcomp::ui::schema::fixed::watch", "configuration.dataSet.url,configuration.dataSet.table"); + } + }, enricher.onComponent(EmitterWithMultiWatch.class, EmitterWithMultiWatch.class.getAnnotations())); + } + + @Test + void fixedSchemaWatchEmptyDoesNotEmitKey() { + final java.util.Map result = + enricher.onComponent(MyEmitter.class, MyEmitter.class.getAnnotations()); + org.junit.jupiter.api.Assertions.assertFalse(result.containsKey("tcomp::ui::schema::fixed::watch"), + "watch key must be absent when watch() is empty"); + } + @Test void dbMappingMetadataPresent() { assertEquals(new HashMap() { @@ -103,4 +135,16 @@ private static class MyProcessor { private static class MyEmitter { } + + @Emitter + @FixedSchema(value = "discover", watch = { "configuration.dataSet.connection.url" }) + private static class EmitterWithWatch { + + } + + @Emitter + @FixedSchema(value = "discover", watch = { "configuration.dataSet.url", "configuration.dataSet.table" }) + private static class EmitterWithMultiWatch { + + } } diff --git a/documentation/src/main/antora/modules/ROOT/pages/studio-schema.adoc b/documentation/src/main/antora/modules/ROOT/pages/studio-schema.adoc index 5f25589e3468d..b159ed62d2414 100644 --- a/documentation/src/main/antora/modules/ROOT/pages/studio-schema.adoc +++ b/documentation/src/main/antora/modules/ROOT/pages/studio-schema.adoc @@ -268,6 +268,16 @@ In some cases, the schema will always use the same scheme. Having a fixed schema will prevent Studio to display the Guess schema button and the reject UI part. The annotation's value should point to a `@DiscoverSchema` or `@DiscoverSchemaExtended` action. +The `@FixedSchema` annotation accepts the following attributes: + +[cols="1,1,3",options="header"] +|=== +| Attribute | Default | Description +| `value` | `""` | Name of the `@DiscoverSchema` / `@DiscoverSchemaExtended` action that returns the fixed schema. +| `flows` | `{}` (main flow) | The connector flows to which the fixed schema applies. For Input components this is always the main flow. For Output components you can list `main`, `reject`, or any custom flow name. Studio's main flow key is `__default__`. +| `watch` | `{}` (none) | Parameter paths (dot-notation, relative to the component root) whose value change triggers an automatic schema refresh in Studio. Uses the same path syntax as `@Updatable#parameters()`. Example: `watch = {"configuration.dataSet.connection.url"}`. +|=== + Example with an emitter: [source,java]