feat(flow): allow grouping plugin properties

Closes #7199
This commit is contained in:
Loïc Mathieu
2025-06-03 10:33:23 +02:00
parent 712a3753f6
commit 9bd87d7492
6 changed files with 54 additions and 13 deletions

View File

@@ -356,6 +356,9 @@ public class JsonSchemaGenerator {
if (pluginPropertyAnnotation.internalStorageURI()) {
memberAttributes.put("$internalStorageURI", true);
}
if (!pluginPropertyAnnotation.group().isEmpty()) {
memberAttributes.put("$group", pluginPropertyAnnotation.group());
}
}
Schema schema = member.getAnnotationConsideringFieldAndGetter(Schema.class);
@@ -741,10 +744,13 @@ public class JsonSchemaGenerator {
this.build(builder, false);
// we don't return base properties unless specified with @PluginProperty
// we don't return base properties unless specified with @PluginProperty and hidden is false
builder
.forFields()
.withIgnoreCheck(fieldScope -> base != null && fieldScope.getAnnotation(PluginProperty.class) == null && fieldScope.getDeclaringType().getTypeName().equals(base.getName()));
.withIgnoreCheck(fieldScope -> base != null &&
(fieldScope.getAnnotation(PluginProperty.class) == null || fieldScope.getAnnotation(PluginProperty.class).hidden()) &&
fieldScope.getDeclaringType().getTypeName().equals(base.getName())
);
SchemaGeneratorConfig schemaGeneratorConfig = builder.build();

View File

@@ -4,6 +4,7 @@ import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonInclude;
import io.kestra.core.exceptions.IllegalVariableEvaluationException;
import io.kestra.core.models.annotations.Plugin;
import io.kestra.core.models.annotations.PluginProperty;
import io.kestra.core.models.executions.TaskRun;
import io.kestra.core.models.property.Property;
import io.kestra.core.models.tasks.retrys.AbstractRetry;
@@ -31,33 +32,44 @@ abstract public class Task implements TaskInterface {
protected String type;
@PluginProperty(hidden = true, group = PluginProperty.CORE_GROUP)
protected String version;
@PluginProperty(hidden = true, group = PluginProperty.CORE_GROUP)
private String description;
@Valid
@PluginProperty(hidden = true, group = PluginProperty.CORE_GROUP)
protected AbstractRetry retry;
@PluginProperty(hidden = true, group = PluginProperty.CORE_GROUP)
protected Property<Duration> timeout;
@Builder.Default
@PluginProperty(hidden = true, group = PluginProperty.CORE_GROUP)
protected Boolean disabled = false;
@Valid
@PluginProperty(hidden = true, group = PluginProperty.CORE_GROUP)
private WorkerGroup workerGroup;
@PluginProperty(hidden = true, group = PluginProperty.CORE_GROUP)
private Level logLevel;
@Builder.Default
@PluginProperty(hidden = true, group = PluginProperty.CORE_GROUP)
private boolean allowFailure = false;
@Builder.Default
@PluginProperty(hidden = true, group = PluginProperty.CORE_GROUP)
private boolean logToFile = false;
@Builder.Default
@PluginProperty(hidden = true, group = PluginProperty.CORE_GROUP)
private String runIf = "true";
@Builder.Default
@PluginProperty(hidden = true, group = PluginProperty.CORE_GROUP)
private boolean allowWarning = false;
public Optional<Task> findById(String id) {

View File

@@ -6,6 +6,7 @@ import io.kestra.core.exceptions.IllegalVariableEvaluationException;
import io.kestra.core.models.Plugin;
import io.kestra.core.models.PluginVersioning;
import io.kestra.core.models.WorkerJobLifecycle;
import io.kestra.core.models.annotations.PluginProperty;
import io.kestra.core.models.property.Property;
import io.kestra.core.runners.RunContext;
import io.kestra.plugin.core.runner.Process;
@@ -39,6 +40,7 @@ public abstract class TaskRunner<T extends TaskRunnerDetailResult> implements Pl
@Pattern(regexp="\\p{javaJavaIdentifierStart}\\p{javaJavaIdentifierPart}*(\\.\\p{javaJavaIdentifierStart}\\p{javaJavaIdentifierPart}*)*")
protected String type;
@PluginProperty(hidden = true, group = PluginProperty.CORE_GROUP)
protected String version;
@JsonIgnore

View File

@@ -34,11 +34,13 @@ abstract public class AbstractTrigger implements TriggerInterface {
protected String type;
@PluginProperty(hidden = true, group = PluginProperty.CORE_GROUP)
protected String version;
@PluginProperty(hidden = true, group = PluginProperty.CORE_GROUP)
private String description;
@PluginProperty
@PluginProperty(group = PluginProperty.CORE_GROUP)
@Schema(
title = "List of conditions in order to limit the flow trigger."
)
@@ -47,11 +49,14 @@ abstract public class AbstractTrigger implements TriggerInterface {
@NotNull
@Builder.Default
@PluginProperty(hidden = true, group = PluginProperty.CORE_GROUP)
private boolean disabled = false;
@Valid
@PluginProperty(hidden = true, group = PluginProperty.CORE_GROUP)
private WorkerGroup workerGroup;
@PluginProperty(hidden = true, group = PluginProperty.CORE_GROUP)
private Level logLevel;
@Schema(
@@ -60,15 +65,17 @@ abstract public class AbstractTrigger implements TriggerInterface {
)
@JsonSerialize(using = ListOrMapOfLabelSerializer.class)
@JsonDeserialize(using = ListOrMapOfLabelDeserializer.class)
@PluginProperty(hidden = true, group = PluginProperty.CORE_GROUP)
private List<@NoSystemLabelValidation Label> labels;
@PluginProperty
@PluginProperty(group = PluginProperty.CORE_GROUP)
@Schema(
title = "List of execution states after which a trigger should be stopped (a.k.a. disabled)."
)
private List<State.Type> stopAfter;
@Builder.Default
@PluginProperty(hidden = true, group = PluginProperty.CORE_GROUP)
private boolean logToFile = false;
/**

View File

@@ -97,16 +97,18 @@ class JsonSchemaGeneratorTest {
assertThat(items.containsKey("anyOf"), is(true));
assertThat(items.containsKey("oneOf"), is(false));
var bash = definitions.get(Log.class.getName());
assertThat((List<String>) bash.get("required"), not(contains("level")));
assertThat((String) ((Map<String, Map<String, Object>>) bash.get("properties")).get("level").get("markdownDescription"), containsString("Default value is : `INFO`"));
assertThat(((String) ((Map<String, Map<String, Object>>) bash.get("properties")).get("message").get("markdownDescription")).contains("can be a string"), is(true));
assertThat(((Map<String, Map<String, Object>>) bash.get("properties")).get("type").containsKey("pattern"), is(false));
assertThat((String) bash.get("markdownDescription"), containsString("##### Examples"));
assertThat((String) bash.get("markdownDescription"), containsString("level: DEBUG"));
var log = definitions.get(Log.class.getName());
assertThat((List<String>) log.get("required"), not(contains("level")));
assertThat((String) ((Map<String, Map<String, Object>>) log.get("properties")).get("level").get("markdownDescription"), containsString("Default value is : `INFO`"));
assertThat(((String) ((Map<String, Map<String, Object>>) log.get("properties")).get("message").get("markdownDescription")).contains("can be a string"), is(true));
assertThat(((Map<String, Map<String, Object>>) log.get("properties")).get("type").containsKey("pattern"), is(false));
assertThat(((Map<String, Map<String, Object>>) log.get("properties")).get("description").get("$group"), is(PluginProperty.CORE_GROUP));
assertThat(((Map<String, Map<String, Object>>) log.get("properties")).get("level").containsKey("$group"), is(false));
assertThat((String) log.get("markdownDescription"), containsString("##### Examples"));
assertThat((String) log.get("markdownDescription"), containsString("level: DEBUG"));
var bashType = definitions.get(Log.class.getName());
assertThat(bashType, is(notNullValue()));
var logType = definitions.get(Log.class.getName());
assertThat(logType, is(notNullValue()));
var requiredWithDefault = definitions.get("io.kestra.core.docs.JsonSchemaGeneratorTest-RequiredWithDefault");
assertThat(requiredWithDefault, is(notNullValue()));

View File

@@ -9,6 +9,8 @@ import static java.lang.annotation.RetentionPolicy.RUNTIME;
@Retention(RUNTIME)
@Target({ElementType.FIELD, ElementType.METHOD})
public @interface PluginProperty {
String CORE_GROUP = "core";
/**
* @return whether the property is renderer
*/
@@ -28,4 +30,14 @@ public @interface PluginProperty {
* @return whether the property is an internal storage URI
*/
boolean internalStorageURI() default false;
/**
* @return the group of the property (for the NoCode editor properties grouping).
*/
String group() default "";
/**
* @return true if this property needs to be hidden from the documentation.
*/
boolean hidden() default false;
}