From 63df8e3e462da711c84bea3f1d173c9f18a85855 Mon Sep 17 00:00:00 2001 From: YannC <37600690+Skraye@users.noreply.github.com> Date: Tue, 14 Oct 2025 10:23:31 +0200 Subject: [PATCH] Fix: openapi tweaks (#11970) * fix: added some on @ApiResponse annotation + added nullable annotation for TaskRun class * fix: review changes --- .../java/io/kestra/core/app/AppBlockInterface.java | 4 +++- .../io/kestra/core/app/AppPluginInterface.java | 4 +++- .../kestra/core/models/conditions/Condition.java | 4 +++- .../kestra/core/models/dashboards/DataFilter.java | 4 +++- .../core/models/dashboards/DataFilterKPI.java | 4 +++- .../core/models/dashboards/charts/Chart.java | 4 +++- .../io/kestra/core/models/executions/TaskRun.java | 7 ++++--- .../io/kestra/core/models/flows/AbstractFlow.java | 8 -------- .../io/kestra/core/models/flows/PluginDefault.java | 4 ---- .../io/kestra/core/models/tasks/TaskInterface.java | 4 +++- .../kestra/core/models/tasks/logs/LogExporter.java | 4 +++- .../core/models/tasks/runners/TaskRunner.java | 3 ++- .../core/models/triggers/TriggerInterface.java | 3 ++- .../io/kestra/core/plugins/AdditionalPlugin.java | 4 +++- .../java/io/kestra/core/utils/RegexPatterns.java | 5 +++++ .../chart/mardown/sources/MarkdownSource.java | 4 +++- webserver/openapi.properties | 3 ++- .../controllers/api/ExecutionController.java | 3 +-- .../webserver/controllers/api/FlowController.java | 14 ++++---------- 19 files changed, 50 insertions(+), 40 deletions(-) create mode 100644 core/src/main/java/io/kestra/core/utils/RegexPatterns.java diff --git a/core/src/main/java/io/kestra/core/app/AppBlockInterface.java b/core/src/main/java/io/kestra/core/app/AppBlockInterface.java index df07e40ac5..0b0d45afc3 100644 --- a/core/src/main/java/io/kestra/core/app/AppBlockInterface.java +++ b/core/src/main/java/io/kestra/core/app/AppBlockInterface.java @@ -7,6 +7,8 @@ import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.Pattern; +import static io.kestra.core.utils.RegexPatterns.JAVA_IDENTIFIER_REGEX; + /** * Top-level marker interface for Kestra's plugin of type App. */ @@ -18,6 +20,6 @@ public interface AppBlockInterface extends io.kestra.core.models.Plugin { ) @NotNull @NotBlank - @Pattern(regexp = "^[A-Za-z_$][A-Za-z0-9_$]*(\\.[A-Za-z_$][A-Za-z0-9_$]*)*$") + @Pattern(regexp = JAVA_IDENTIFIER_REGEX) String getType(); } diff --git a/core/src/main/java/io/kestra/core/app/AppPluginInterface.java b/core/src/main/java/io/kestra/core/app/AppPluginInterface.java index 4b9de3df80..a11cb2cb2d 100644 --- a/core/src/main/java/io/kestra/core/app/AppPluginInterface.java +++ b/core/src/main/java/io/kestra/core/app/AppPluginInterface.java @@ -7,6 +7,8 @@ import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.Pattern; +import static io.kestra.core.utils.RegexPatterns.JAVA_IDENTIFIER_REGEX; + /** * Top-level marker interface for Kestra's plugin of type App. */ @@ -18,6 +20,6 @@ public interface AppPluginInterface extends io.kestra.core.models.Plugin { ) @NotNull @NotBlank - @Pattern(regexp = "^[A-Za-z_$][A-Za-z0-9_$]*(\\.[A-Za-z_$][A-Za-z0-9_$]*)*$") + @Pattern(regexp = JAVA_IDENTIFIER_REGEX) String getType(); } diff --git a/core/src/main/java/io/kestra/core/models/conditions/Condition.java b/core/src/main/java/io/kestra/core/models/conditions/Condition.java index 7a0f5723f9..1d6c2fbf70 100644 --- a/core/src/main/java/io/kestra/core/models/conditions/Condition.java +++ b/core/src/main/java/io/kestra/core/models/conditions/Condition.java @@ -12,6 +12,8 @@ import lombok.experimental.SuperBuilder; import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.Pattern; +import static io.kestra.core.utils.RegexPatterns.JAVA_IDENTIFIER_REGEX; + @io.kestra.core.models.annotations.Plugin @SuperBuilder @Getter @@ -20,6 +22,6 @@ import jakarta.validation.constraints.Pattern; @JsonInclude(JsonInclude.Include.NON_DEFAULT) public abstract class Condition implements Plugin, Rethrow.PredicateChecked { @NotNull - @Pattern(regexp = "^[A-Za-z_$][A-Za-z0-9_$]*(\\.[A-Za-z_$][A-Za-z0-9_$]*)*$") + @Pattern(regexp = JAVA_IDENTIFIER_REGEX) protected String type; } diff --git a/core/src/main/java/io/kestra/core/models/dashboards/DataFilter.java b/core/src/main/java/io/kestra/core/models/dashboards/DataFilter.java index d5786d2068..de04dff6df 100644 --- a/core/src/main/java/io/kestra/core/models/dashboards/DataFilter.java +++ b/core/src/main/java/io/kestra/core/models/dashboards/DataFilter.java @@ -20,6 +20,8 @@ import java.util.List; import java.util.Map; import java.util.Set; +import static io.kestra.core.utils.RegexPatterns.JAVA_IDENTIFIER_REGEX; + @SuperBuilder(toBuilder = true) @Getter @NoArgsConstructor @@ -28,7 +30,7 @@ import java.util.Set; public abstract class DataFilter, C extends ColumnDescriptor> implements io.kestra.core.models.Plugin, IData { @NotNull @NotBlank - @Pattern(regexp = "^[A-Za-z_$][A-Za-z0-9_$]*(\\.[A-Za-z_$][A-Za-z0-9_$]*)*$") + @Pattern(regexp = JAVA_IDENTIFIER_REGEX) private String type; private Map columns; diff --git a/core/src/main/java/io/kestra/core/models/dashboards/DataFilterKPI.java b/core/src/main/java/io/kestra/core/models/dashboards/DataFilterKPI.java index 2a324bc419..29db943c36 100644 --- a/core/src/main/java/io/kestra/core/models/dashboards/DataFilterKPI.java +++ b/core/src/main/java/io/kestra/core/models/dashboards/DataFilterKPI.java @@ -19,6 +19,8 @@ import java.util.Collections; import java.util.List; import java.util.Set; +import static io.kestra.core.utils.RegexPatterns.JAVA_IDENTIFIER_REGEX; + @SuperBuilder(toBuilder = true) @Getter @NoArgsConstructor @@ -27,7 +29,7 @@ import java.util.Set; public abstract class DataFilterKPI, C extends ColumnDescriptor> implements io.kestra.core.models.Plugin, IData { @NotNull @NotBlank - @Pattern(regexp = "^[A-Za-z_$][A-Za-z0-9_$]*(\\.[A-Za-z_$][A-Za-z0-9_$]*)*$") + @Pattern(regexp = JAVA_IDENTIFIER_REGEX) private String type; private C columns; diff --git a/core/src/main/java/io/kestra/core/models/dashboards/charts/Chart.java b/core/src/main/java/io/kestra/core/models/dashboards/charts/Chart.java index a341e5976e..a1de95d806 100644 --- a/core/src/main/java/io/kestra/core/models/dashboards/charts/Chart.java +++ b/core/src/main/java/io/kestra/core/models/dashboards/charts/Chart.java @@ -12,6 +12,8 @@ import lombok.Getter; import lombok.NoArgsConstructor; import lombok.experimental.SuperBuilder; +import static io.kestra.core.utils.RegexPatterns.JAVA_IDENTIFIER_REGEX; + @SuperBuilder(toBuilder = true) @Getter @NoArgsConstructor @@ -26,7 +28,7 @@ public abstract class Chart

implements io.kestra.core.mod @NotNull @NotBlank - @Pattern(regexp = "^[A-Za-z_$][A-Za-z0-9_$]*(\\.[A-Za-z_$][A-Za-z0-9_$]*)*$") + @Pattern(regexp = JAVA_IDENTIFIER_REGEX) protected String type; @Valid diff --git a/core/src/main/java/io/kestra/core/models/executions/TaskRun.java b/core/src/main/java/io/kestra/core/models/executions/TaskRun.java index 5f7e685f4d..f7510d8c78 100644 --- a/core/src/main/java/io/kestra/core/models/executions/TaskRun.java +++ b/core/src/main/java/io/kestra/core/models/executions/TaskRun.java @@ -52,6 +52,7 @@ public class TaskRun implements TenantInterface { @With @JsonInclude(JsonInclude.Include.ALWAYS) + @Nullable Variables outputs; @NotNull @@ -64,7 +65,6 @@ public class TaskRun implements TenantInterface { Boolean dynamic; // Set it to true to force execution even if the execution is killed - @Nullable @With Boolean forceExecution; @@ -217,7 +217,7 @@ public class TaskRun implements TenantInterface { public boolean isSame(TaskRun taskRun) { return this.getId().equals(taskRun.getId()) && ((this.getValue() == null && taskRun.getValue() == null) || (this.getValue() != null && this.getValue().equals(taskRun.getValue()))) && - ((this.getIteration() == null && taskRun.getIteration() == null) || (this.getIteration() != null && this.getIteration().equals(taskRun.getIteration()))) ; + ((this.getIteration() == null && taskRun.getIteration() == null) || (this.getIteration() != null && this.getIteration().equals(taskRun.getIteration()))); } public String toString(boolean pretty) { @@ -249,7 +249,7 @@ public class TaskRun implements TenantInterface { * This method is used when the retry is apply on a task * but the retry type is NEW_EXECUTION * - * @param retry Contains the retry configuration + * @param retry Contains the retry configuration * @param execution Contains the attempt number and original creation date * @return The next retry date, null if maxAttempt || maxDuration is reached */ @@ -270,6 +270,7 @@ public class TaskRun implements TenantInterface { /** * This method is used when the Retry definition comes from the flow + * * @param retry The retry configuration * @return The next retry date, null if maxAttempt || maxDuration is reached */ diff --git a/core/src/main/java/io/kestra/core/models/flows/AbstractFlow.java b/core/src/main/java/io/kestra/core/models/flows/AbstractFlow.java index 9803fffcea..fac2050706 100644 --- a/core/src/main/java/io/kestra/core/models/flows/AbstractFlow.java +++ b/core/src/main/java/io/kestra/core/models/flows/AbstractFlow.java @@ -77,14 +77,6 @@ public abstract class AbstractFlow implements FlowInterface { Map variables; - @Schema( - oneOf = { - String.class, // Corresponds to 'type: string' in OpenAPI - Map.class // Corresponds to 'type: object' in OpenAPI - } - ) - interface StringOrMapValue {} - @Valid private WorkerGroup workerGroup; diff --git a/core/src/main/java/io/kestra/core/models/flows/PluginDefault.java b/core/src/main/java/io/kestra/core/models/flows/PluginDefault.java index 1ca9c8348a..69b39990c9 100644 --- a/core/src/main/java/io/kestra/core/models/flows/PluginDefault.java +++ b/core/src/main/java/io/kestra/core/models/flows/PluginDefault.java @@ -24,10 +24,6 @@ public class PluginDefault { @Schema( type = "object", - oneOf = { - Map.class, - String.class - }, additionalProperties = Schema.AdditionalPropertiesValue.FALSE ) private final Map values; diff --git a/core/src/main/java/io/kestra/core/models/tasks/TaskInterface.java b/core/src/main/java/io/kestra/core/models/tasks/TaskInterface.java index d4e2c3f170..4a32b23543 100644 --- a/core/src/main/java/io/kestra/core/models/tasks/TaskInterface.java +++ b/core/src/main/java/io/kestra/core/models/tasks/TaskInterface.java @@ -8,6 +8,8 @@ import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.Pattern; +import static io.kestra.core.utils.RegexPatterns.JAVA_IDENTIFIER_REGEX; + @JsonInclude(JsonInclude.Include.NON_DEFAULT) public interface TaskInterface extends Plugin, PluginVersioning { @NotNull @@ -17,7 +19,7 @@ public interface TaskInterface extends Plugin, PluginVersioning { @NotNull @NotBlank - @Pattern(regexp = "^[A-Za-z_$][A-Za-z0-9_$]*(\\.[A-Za-z_$][A-Za-z0-9_$]*)*$") + @Pattern(regexp = JAVA_IDENTIFIER_REGEX) @Schema(title = "The class name of this task.") String getType(); } diff --git a/core/src/main/java/io/kestra/core/models/tasks/logs/LogExporter.java b/core/src/main/java/io/kestra/core/models/tasks/logs/LogExporter.java index 1d939b73dd..fde5ae0225 100644 --- a/core/src/main/java/io/kestra/core/models/tasks/logs/LogExporter.java +++ b/core/src/main/java/io/kestra/core/models/tasks/logs/LogExporter.java @@ -11,6 +11,8 @@ import lombok.NoArgsConstructor; import lombok.experimental.SuperBuilder; import reactor.core.publisher.Flux; +import static io.kestra.core.utils.RegexPatterns.JAVA_IDENTIFIER_REGEX; + @Plugin @SuperBuilder(toBuilder = true) @Getter @@ -22,7 +24,7 @@ public abstract class LogExporter implements io.kestra.core.m protected String id; @NotBlank - @Pattern(regexp = "^[A-Za-z_$][A-Za-z0-9_$]*(\\.[A-Za-z_$][A-Za-z0-9_$]*)*$") + @Pattern(regexp = JAVA_IDENTIFIER_REGEX) protected String type; public abstract T sendLogs(RunContext runContext, Flux logRecords) throws Exception; diff --git a/core/src/main/java/io/kestra/core/models/tasks/runners/TaskRunner.java b/core/src/main/java/io/kestra/core/models/tasks/runners/TaskRunner.java index d86f219b08..6624165760 100644 --- a/core/src/main/java/io/kestra/core/models/tasks/runners/TaskRunner.java +++ b/core/src/main/java/io/kestra/core/models/tasks/runners/TaskRunner.java @@ -25,6 +25,7 @@ import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; import static io.kestra.core.utils.WindowsUtils.windowsToUnixPath; +import static io.kestra.core.utils.RegexPatterns.JAVA_IDENTIFIER_REGEX; /** * Base class for all task runners. @@ -36,7 +37,7 @@ import static io.kestra.core.utils.WindowsUtils.windowsToUnixPath; @JsonInclude(JsonInclude.Include.NON_DEFAULT) public abstract class TaskRunner implements Plugin, PluginVersioning, WorkerJobLifecycle { @NotBlank - @Pattern(regexp = "^[A-Za-z_$][A-Za-z0-9_$]*(\\.[A-Za-z_$][A-Za-z0-9_$]*)*$") + @Pattern(regexp = JAVA_IDENTIFIER_REGEX) protected String type; @PluginProperty(hidden = true, group = PluginProperty.CORE_GROUP) diff --git a/core/src/main/java/io/kestra/core/models/triggers/TriggerInterface.java b/core/src/main/java/io/kestra/core/models/triggers/TriggerInterface.java index 22db73e82f..cb3c28abaa 100644 --- a/core/src/main/java/io/kestra/core/models/triggers/TriggerInterface.java +++ b/core/src/main/java/io/kestra/core/models/triggers/TriggerInterface.java @@ -7,6 +7,7 @@ import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.Pattern; +import static io.kestra.core.utils.RegexPatterns.JAVA_IDENTIFIER_REGEX; public interface TriggerInterface extends Plugin, PluginVersioning { @NotNull @@ -17,7 +18,7 @@ public interface TriggerInterface extends Plugin, PluginVersioning { @NotNull @NotBlank - @Pattern(regexp = "^[A-Za-z_$][A-Za-z0-9_$]*(\\.[A-Za-z_$][A-Za-z0-9_$]*)*$") + @Pattern(regexp = JAVA_IDENTIFIER_REGEX) @Schema(title = "The class name for this current trigger.") String getType(); diff --git a/core/src/main/java/io/kestra/core/plugins/AdditionalPlugin.java b/core/src/main/java/io/kestra/core/plugins/AdditionalPlugin.java index b1621b36f3..0736b53add 100644 --- a/core/src/main/java/io/kestra/core/plugins/AdditionalPlugin.java +++ b/core/src/main/java/io/kestra/core/plugins/AdditionalPlugin.java @@ -8,6 +8,8 @@ import lombok.Getter; import lombok.NoArgsConstructor; import lombok.experimental.SuperBuilder; +import static io.kestra.core.utils.RegexPatterns.JAVA_IDENTIFIER_REGEX; + @io.kestra.core.models.annotations.Plugin @SuperBuilder(toBuilder = true) @Getter @@ -15,6 +17,6 @@ import lombok.experimental.SuperBuilder; public abstract class AdditionalPlugin implements Plugin { @NotNull @NotBlank - @Pattern(regexp = "^[A-Za-z_$][A-Za-z0-9_$]*(\\.[A-Za-z_$][A-Za-z0-9_$]*)*$") + @Pattern(regexp = JAVA_IDENTIFIER_REGEX) protected String type; } diff --git a/core/src/main/java/io/kestra/core/utils/RegexPatterns.java b/core/src/main/java/io/kestra/core/utils/RegexPatterns.java new file mode 100644 index 0000000000..669b82d840 --- /dev/null +++ b/core/src/main/java/io/kestra/core/utils/RegexPatterns.java @@ -0,0 +1,5 @@ +package io.kestra.core.utils; + +public class RegexPatterns { + public static final String JAVA_IDENTIFIER_REGEX = "^[A-Za-z_$][A-Za-z0-9_$]*(\\.[A-Za-z_$][A-Za-z0-9_$]*)*$"; +} diff --git a/core/src/main/java/io/kestra/plugin/core/dashboard/chart/mardown/sources/MarkdownSource.java b/core/src/main/java/io/kestra/plugin/core/dashboard/chart/mardown/sources/MarkdownSource.java index 7e48f4048f..6f540b7034 100644 --- a/core/src/main/java/io/kestra/plugin/core/dashboard/chart/mardown/sources/MarkdownSource.java +++ b/core/src/main/java/io/kestra/plugin/core/dashboard/chart/mardown/sources/MarkdownSource.java @@ -7,6 +7,8 @@ import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.Pattern; import lombok.Getter; +import static io.kestra.core.utils.RegexPatterns.JAVA_IDENTIFIER_REGEX; + @Getter @JsonTypeInfo( use = JsonTypeInfo.Id.NAME, @@ -20,6 +22,6 @@ import lombok.Getter; public class MarkdownSource { @NotNull @NotBlank - @Pattern(regexp = "^[A-Za-z_$][A-Za-z0-9_$]*(\\.[A-Za-z_$][A-Za-z0-9_$]*)*$") + @Pattern(regexp = JAVA_IDENTIFIER_REGEX) private String type; } diff --git a/webserver/openapi.properties b/webserver/openapi.properties index 928cc2e7a7..7a73b3302a 100644 --- a/webserver/openapi.properties +++ b/webserver/openapi.properties @@ -1,2 +1,3 @@ # Keep the name static -micronaut.openapi.filename=kestra \ No newline at end of file +micronaut.openapi.filename=kestra +micronaut.openapi.constructor-arguments-as-required=false diff --git a/webserver/src/main/java/io/kestra/webserver/controllers/api/ExecutionController.java b/webserver/src/main/java/io/kestra/webserver/controllers/api/ExecutionController.java index 47ee5ecb79..6427cd6056 100644 --- a/webserver/src/main/java/io/kestra/webserver/controllers/api/ExecutionController.java +++ b/webserver/src/main/java/io/kestra/webserver/controllers/api/ExecutionController.java @@ -44,12 +44,10 @@ import io.kestra.webserver.responses.BulkResponse; import io.kestra.webserver.responses.PagedResults; import io.kestra.webserver.services.ExecutionDependenciesStreamingService; import io.kestra.webserver.services.ExecutionStreamingService; -import io.kestra.core.runners.SecureVariableRendererFactory; import io.kestra.webserver.utils.PageableUtils; import io.kestra.webserver.utils.RequestUtils; import io.kestra.webserver.utils.filepreview.FileRender; import io.kestra.webserver.utils.filepreview.FileRenderBuilder; -import io.micronaut.context.ApplicationContext; import io.micronaut.context.annotation.Value; import io.micronaut.context.event.ApplicationEventPublisher; import io.micronaut.core.annotation.Introspected; @@ -680,6 +678,7 @@ public class ExecutionController { @Post(uri = "/{namespace}/{id}", consumes = MediaType.MULTIPART_FORM_DATA) @Operation(tags = {"Executions"}, summary = "Create a new execution for a flow") @ApiResponse(responseCode = "409", description = "if the flow is disabled") + @ApiResponse(responseCode = "200", description = "On execution created", content = {@Content(schema = @Schema(implementation = ExecutionResponse.class))}) @SingleResult public Publisher createExecution( @Parameter(description = "The flow namespace") @PathVariable String namespace, diff --git a/webserver/src/main/java/io/kestra/webserver/controllers/api/FlowController.java b/webserver/src/main/java/io/kestra/webserver/controllers/api/FlowController.java index 03facb9111..d649867c9b 100644 --- a/webserver/src/main/java/io/kestra/webserver/controllers/api/FlowController.java +++ b/webserver/src/main/java/io/kestra/webserver/controllers/api/FlowController.java @@ -1,20 +1,13 @@ package io.kestra.webserver.controllers.api; import com.fasterxml.jackson.core.JsonProcessingException; -import io.kestra.core.docs.JsonSchemaGenerator; import io.kestra.core.exceptions.FlowProcessingException; import io.kestra.core.exceptions.IllegalVariableEvaluationException; import io.kestra.core.exceptions.InternalException; -import io.kestra.core.models.QueryFilter; import io.kestra.core.models.HasSource; +import io.kestra.core.models.QueryFilter; import io.kestra.core.models.SearchResult; -import io.kestra.core.models.flows.Flow; -import io.kestra.core.models.flows.FlowId; -import io.kestra.core.models.flows.FlowInterface; -import io.kestra.core.models.flows.FlowScope; -import io.kestra.core.models.flows.FlowWithException; -import io.kestra.core.models.flows.FlowWithSource; -import io.kestra.core.models.flows.GenericFlow; +import io.kestra.core.models.flows.*; import io.kestra.core.models.hierarchies.FlowGraph; import io.kestra.core.models.tasks.Task; import io.kestra.core.models.topologies.FlowTopology; @@ -24,7 +17,6 @@ import io.kestra.core.models.validations.ManualConstraintViolation; import io.kestra.core.models.validations.ModelValidator; import io.kestra.core.models.validations.ValidateConstraintViolation; import io.kestra.core.repositories.FlowRepositoryInterface; -import io.kestra.core.repositories.FlowTopologyRepositoryInterface; import io.kestra.core.serializers.JacksonMapper; import io.kestra.core.serializers.YamlParser; import io.kestra.core.services.FlowService; @@ -54,6 +46,7 @@ import io.micronaut.validation.Validated; import io.swagger.v3.oas.annotations.Hidden; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.media.Content; import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.parameters.RequestBody; import io.swagger.v3.oas.annotations.responses.ApiResponse; @@ -445,6 +438,7 @@ public class FlowController { @Put(uri = "{namespace}/{id}", consumes = MediaType.APPLICATION_YAML) @ExecuteOn(TaskExecutors.IO) @Operation(tags = {"Flows"}, summary = "Update a flow") + @ApiResponse(responseCode = "200", description = "On success", content = {@Content(schema = @Schema(implementation = FlowWithSource.class))}) public HttpResponse updateFlow( @Parameter(description = "The flow namespace") @PathVariable String namespace, @Parameter(description = "The flow id") @PathVariable String id,