diff --git a/api/src/main/resources/schema/workflow.yaml b/api/src/main/resources/schema/workflow.yaml index b59e2f3a..73bda7ee 100644 --- a/api/src/main/resources/schema/workflow.yaml +++ b/api/src/main/resources/schema/workflow.yaml @@ -1,4 +1,4 @@ -$id: https://serverlessworkflow.io/schemas/1.0.0-alpha5/workflow.yaml +$id: https://serverlessworkflow.io/schemas/1.0.1/workflow.yaml $schema: https://json-schema.org/draft/2020-12/schema description: Serverless Workflow DSL - Workflow Schema. type: object @@ -226,638 +226,664 @@ $defs: oneOf: - title: CallAsyncAPI description: Defines the AsyncAPI call to perform. - $ref: '#/$defs/taskBase' type: object required: [ call, with ] unevaluatedProperties: false - properties: - call: - type: string - const: asyncapi - with: - type: object - title: AsyncApiArguments - description: The Async API call arguments. - properties: - document: - $ref: '#/$defs/externalResource' - title: AsyncAPIDocument - description: The document that defines the AsyncAPI operation to call. - channel: - type: string - title: With - description: The name of the channel on which to perform the operation. Used only in case the referenced document uses AsyncAPI v2.6.0. - operation: - type: string - title: AsyncAPIOperation - description: A reference to the AsyncAPI operation to call. - server: - $ref: '#/$defs/asyncApiServer' - title: AsyncAPIServer - description: An object used to configure to the server to call the specified AsyncAPI operation on. - protocol: - type: string - title: AsyncApiProtocol - description: The protocol to use to select the target server. - enum: [ amqp, amqp1, anypointmq, googlepubsub, http, ibmmq, jms, kafka, mercure, mqtt, mqtt5, nats, pulsar, redis, sns, solace, sqs, stomp, ws ] - message: - $ref: '#/$defs/asyncApiOutboundMessage' - title: AsyncApiMessage - description: An object used to configure the message to publish using the target operation. - subscription: - $ref: '#/$defs/asyncApiSubscription' - title: AsyncApiSubscription - description: An object used to configure the subscription to messages consumed using the target operation. - authentication: - $ref: '#/$defs/referenceableAuthenticationPolicy' - title: AsyncAPIAuthentication - description: The authentication policy, if any, to use when calling the AsyncAPI operation. - oneOf: - - required: [ document, operation, message ] - - required: [ document, operation, subscription ] - - required: [ document, channel, message ] - - required: [ document, channel, subscription ] - unevaluatedProperties: false + allOf: + - $ref: '#/$defs/taskBase' + - properties: + call: + type: string + const: asyncapi + with: + type: object + title: AsyncApiArguments + description: The Async API call arguments. + properties: + document: + $ref: '#/$defs/externalResource' + title: AsyncAPIDocument + description: The document that defines the AsyncAPI operation to call. + channel: + type: string + title: With + description: The name of the channel on which to perform the operation. Used only in case the referenced document uses AsyncAPI v2.6.0. + operation: + type: string + title: AsyncAPIOperation + description: A reference to the AsyncAPI operation to call. + server: + $ref: '#/$defs/asyncApiServer' + title: AsyncAPIServer + description: An object used to configure to the server to call the specified AsyncAPI operation on. + protocol: + type: string + title: AsyncApiProtocol + description: The protocol to use to select the target server. + enum: [ amqp, amqp1, anypointmq, googlepubsub, http, ibmmq, jms, kafka, mercure, mqtt, mqtt5, nats, pulsar, redis, sns, solace, sqs, stomp, ws ] + message: + $ref: '#/$defs/asyncApiOutboundMessage' + title: AsyncApiMessage + description: An object used to configure the message to publish using the target operation. + subscription: + $ref: '#/$defs/asyncApiSubscription' + title: AsyncApiSubscription + description: An object used to configure the subscription to messages consumed using the target operation. + authentication: + $ref: '#/$defs/referenceableAuthenticationPolicy' + title: AsyncAPIAuthentication + description: The authentication policy, if any, to use when calling the AsyncAPI operation. + oneOf: + - required: [ document, operation, message ] + - required: [ document, operation, subscription ] + - required: [ document, channel, message ] + - required: [ document, channel, subscription ] + unevaluatedProperties: false - title: CallGRPC description: Defines the GRPC call to perform. - $ref: '#/$defs/taskBase' type: object unevaluatedProperties: false required: [ call, with ] - properties: - call: - type: string - const: grpc - with: - type: object - title: GRPCArguments - description: The GRPC call arguments. - properties: - proto: - $ref: '#/$defs/externalResource' - title: WithGRPCProto - description: The proto resource that describes the GRPC service to call. - service: - type: object - title: WithGRPCService - unevaluatedProperties: false - properties: - name: - type: string - title: WithGRPCServiceName - description: The name of the GRPC service to call. - host: - type: string - title: WithGRPCServiceHost - description: The hostname of the GRPC service to call. - pattern: ^[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?$ - port: - type: integer - title: WithGRPCServicePost - description: The port number of the GRPC service to call. - minimum: 0 - maximum: 65535 - authentication: - $ref: '#/$defs/referenceableAuthenticationPolicy' - title: WithGRPCServiceAuthentication - description: The endpoint's authentication policy, if any. - required: [ name, host ] - method: - type: string - title: WithGRPCMethod - description: The name of the method to call on the defined GRPC service. - arguments: - type: object - title: WithGRPCArguments - description: The arguments, if any, to call the method with. - additionalProperties: true - required: [ proto, service, method ] - unevaluatedProperties: false + allOf: + - $ref: '#/$defs/taskBase' + - properties: + call: + type: string + const: grpc + with: + type: object + title: GRPCArguments + description: The GRPC call arguments. + properties: + proto: + $ref: '#/$defs/externalResource' + title: WithGRPCProto + description: The proto resource that describes the GRPC service to call. + service: + type: object + title: WithGRPCService + unevaluatedProperties: false + properties: + name: + type: string + title: WithGRPCServiceName + description: The name of the GRPC service to call. + host: + type: string + title: WithGRPCServiceHost + description: The hostname of the GRPC service to call. + pattern: ^[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?$ + port: + type: integer + title: WithGRPCServicePost + description: The port number of the GRPC service to call. + minimum: 0 + maximum: 65535 + authentication: + $ref: '#/$defs/referenceableAuthenticationPolicy' + title: WithGRPCServiceAuthentication + description: The endpoint's authentication policy, if any. + required: [ name, host ] + method: + type: string + title: WithGRPCMethod + description: The name of the method to call on the defined GRPC service. + arguments: + type: object + title: WithGRPCArguments + description: The arguments, if any, to call the method with. + additionalProperties: true + required: [ proto, service, method ] + unevaluatedProperties: false - title: CallHTTP description: Defines the HTTP call to perform. - $ref: '#/$defs/taskBase' type: object unevaluatedProperties: false required: [ call, with ] - properties: - call: - type: string - const: http - with: - type: object - title: HTTPArguments - description: The HTTP call arguments. - properties: - method: - type: string - title: HTTPMethod - description: The HTTP method of the HTTP request to perform. - endpoint: - title: HTTPEndpoint - description: The HTTP endpoint to send the request to. - $ref: '#/$defs/endpoint' - headers: - type: object - title: HTTPHeaders - description: A name/value mapping of the headers, if any, of the HTTP request to perform. - body: - title: HTTPBody - description: The body, if any, of the HTTP request to perform. - query: - type: object - title: HTTPQuery - description: A name/value mapping of the query parameters, if any, of the HTTP request to perform. - additionalProperties: true - output: - type: string - title: HTTPOutput - description: The http call output format. Defaults to 'content'. - enum: [ raw, content, response ] - redirect: - type: boolean - title: HttpRedirect - description: Specifies whether redirection status codes (`300–399`) should be treated as errors. - required: [ method, endpoint ] - unevaluatedProperties: false + allOf: + - $ref: '#/$defs/taskBase' + - properties: + call: + type: string + const: http + with: + type: object + title: HTTPArguments + description: The HTTP call arguments. + properties: + method: + type: string + title: HTTPMethod + description: The HTTP method of the HTTP request to perform. + endpoint: + title: HTTPEndpoint + description: The HTTP endpoint to send the request to. + $ref: '#/$defs/endpoint' + headers: + oneOf: + - type: object + additionalProperties: + type: string + - $ref: '#/$defs/runtimeExpression' + title: HTTPHeaders + description: A name/value mapping of the headers, if any, of the HTTP request to perform. + body: + title: HTTPBody + description: The body, if any, of the HTTP request to perform. + query: + oneOf: + - type: object + additionalProperties: + type: string + - $ref: '#/$defs/runtimeExpression' + title: HTTPQuery + description: A name/value mapping of the query parameters, if any, of the HTTP request to perform. + additionalProperties: true + output: + type: string + title: HTTPOutput + description: The http call output format. Defaults to 'content'. + enum: [ raw, content, response ] + redirect: + type: boolean + title: HttpRedirect + description: Specifies whether redirection status codes (`300–399`) should be treated as errors. + required: [ method, endpoint ] + unevaluatedProperties: false - title: CallOpenAPI description: Defines the OpenAPI call to perform. - $ref: '#/$defs/taskBase' type: object unevaluatedProperties: false required: [ call, with ] - properties: - call: - type: string - const: openapi - with: - type: object - title: OpenAPIArguments - description: The OpenAPI call arguments. - properties: - document: - $ref: '#/$defs/externalResource' - title: WithOpenAPIDocument - description: The document that defines the OpenAPI operation to call. - operationId: - type: string - title: WithOpenAPIOperation - description: The id of the OpenAPI operation to call. - parameters: - type: object - title: WithOpenAPIParameters - description: A name/value mapping of the parameters of the OpenAPI operation to call. - additionalProperties: true - authentication: - $ref: '#/$defs/referenceableAuthenticationPolicy' - title: WithOpenAPIAuthentication - description: The authentication policy, if any, to use when calling the OpenAPI operation. - output: - type: string - enum: [ raw, content, response ] - title: WithOpenAPIOutput - description: The http call output format. Defaults to 'content'. - redirect: - type: boolean - title: HttpRedirect - description: Specifies whether redirection status codes (`300–399`) should be treated as errors. - required: [ document, operationId ] - unevaluatedProperties: false + allOf: + - $ref: '#/$defs/taskBase' + - properties: + call: + type: string + const: openapi + with: + type: object + title: OpenAPIArguments + description: The OpenAPI call arguments. + properties: + document: + $ref: '#/$defs/externalResource' + title: WithOpenAPIDocument + description: The document that defines the OpenAPI operation to call. + operationId: + type: string + title: WithOpenAPIOperation + description: The id of the OpenAPI operation to call. + parameters: + type: object + title: WithOpenAPIParameters + description: A name/value mapping of the parameters of the OpenAPI operation to call. + additionalProperties: true + authentication: + $ref: '#/$defs/referenceableAuthenticationPolicy' + title: WithOpenAPIAuthentication + description: The authentication policy, if any, to use when calling the OpenAPI operation. + output: + type: string + enum: [ raw, content, response ] + title: WithOpenAPIOutput + description: The http call output format. Defaults to 'content'. + redirect: + type: boolean + title: HttpRedirect + description: Specifies whether redirection status codes (`300–399`) should be treated as errors. + required: [ document, operationId ] + unevaluatedProperties: false - title: CallFunction description: Defines the function call to perform. - $ref: '#/$defs/taskBase' type: object unevaluatedProperties: false required: [ call ] - properties: - call: - type: string - not: - enum: ["asyncapi", "grpc", "http", "openapi"] - description: The name of the function to call. - with: - type: object - title: FunctionArguments - description: A name/value mapping of the parameters, if any, to call the function with. - additionalProperties: true + allOf: + - $ref: '#/$defs/taskBase' + - properties: + call: + type: string + not: + enum: ["asyncapi", "grpc", "http", "openapi"] + description: The name of the function to call. + with: + type: object + title: FunctionArguments + description: A name/value mapping of the parameters, if any, to call the function with. + additionalProperties: true forkTask: type: object - $ref: '#/$defs/taskBase' title: ForkTask description: Allows workflows to execute multiple tasks concurrently and optionally race them against each other, with a single possible winner, which sets the task's output. unevaluatedProperties: false required: [ fork ] - properties: - fork: - type: object - title: ForkTaskConfiguration - description: The configuration of the branches to perform concurrently. - unevaluatedProperties: false - required: [ branches ] - properties: - branches: - $ref: '#/$defs/taskList' - title: ForkBranches - compete: - type: boolean - title: ForkCompete - description: Indicates whether or not the concurrent tasks are racing against each other, with a single possible winner, which sets the composite task's output. - default: false + allOf: + - $ref: '#/$defs/taskBase' + - properties: + fork: + type: object + title: ForkTaskConfiguration + description: The configuration of the branches to perform concurrently. + unevaluatedProperties: false + required: [ branches ] + properties: + branches: + $ref: '#/$defs/taskList' + title: ForkBranches + compete: + type: boolean + title: ForkCompete + description: Indicates whether or not the concurrent tasks are racing against each other, with a single possible winner, which sets the composite task's output. + default: false doTask: type: object - $ref: '#/$defs/taskBase' title: DoTask description: Allows to execute a list of tasks in sequence. unevaluatedProperties: false required: [ do ] - properties: - do: - $ref: '#/$defs/taskList' - title: DoTaskConfiguration - description: The configuration of the tasks to perform sequentially. + allOf: + - $ref: '#/$defs/taskBase' + - properties: + do: + $ref: '#/$defs/taskList' + title: DoTaskConfiguration + description: The configuration of the tasks to perform sequentially. emitTask: type: object - $ref: '#/$defs/taskBase' title: EmitTask description: Allows workflows to publish events to event brokers or messaging systems, facilitating communication and coordination between different components and services. required: [ emit ] unevaluatedProperties: false - properties: - emit: - type: object - title: EmitTaskConfiguration - description: The configuration of an event's emission. - unevaluatedProperties: false - properties: - event: - type: object - title: EmitEventDefinition - description: The definition of the event to emit. - properties: - with: - $ref: '#/$defs/eventProperties' - title: EmitEventWith - description: Defines the properties of event to emit. - required: [ source, type ] - additionalProperties: true - required: [ event ] + allOf: + - $ref: '#/$defs/taskBase' + - properties: + emit: + type: object + title: EmitTaskConfiguration + description: The configuration of an event's emission. + unevaluatedProperties: false + properties: + event: + type: object + title: EmitEventDefinition + description: The definition of the event to emit. + properties: + with: + $ref: '#/$defs/eventProperties' + title: EmitEventWith + description: Defines the properties of event to emit. + required: [ source, type ] + additionalProperties: true + required: [ event ] forTask: type: object - $ref: '#/$defs/taskBase' title: ForTask description: Allows workflows to iterate over a collection of items, executing a defined set of subtasks for each item in the collection. This task type is instrumental in handling scenarios such as batch processing, data transformation, and repetitive operations across datasets. required: [ for, do ] unevaluatedProperties: false - properties: - for: - type: object - title: ForTaskConfiguration - description: The definition of the loop that iterates over a range of values. - unevaluatedProperties: false - properties: - each: - type: string - title: ForEach - description: The name of the variable used to store the current item being enumerated. - default: item - in: - type: string - title: ForIn - description: A runtime expression used to get the collection to enumerate. - at: - type: string - title: ForAt - description: The name of the variable used to store the index of the current item being enumerated. - default: index - required: [ in ] - while: - type: string - title: While - description: A runtime expression that represents the condition, if any, that must be met for the iteration to continue. - do: - $ref: '#/$defs/taskList' - title: ForTaskDo + allOf: + - $ref: '#/$defs/taskBase' + - properties: + for: + type: object + title: ForTaskConfiguration + description: The definition of the loop that iterates over a range of values. + unevaluatedProperties: false + properties: + each: + type: string + title: ForEach + description: The name of the variable used to store the current item being enumerated. + default: item + in: + type: string + title: ForIn + description: A runtime expression used to get the collection to enumerate. + at: + type: string + title: ForAt + description: The name of the variable used to store the index of the current item being enumerated. + default: index + required: [ in ] + while: + type: string + title: While + description: A runtime expression that represents the condition, if any, that must be met for the iteration to continue. + do: + $ref: '#/$defs/taskList' + title: ForTaskDo listenTask: type: object - $ref: '#/$defs/taskBase' title: ListenTask description: Provides a mechanism for workflows to await and react to external events, enabling event-driven behavior within workflow systems. required: [ listen ] unevaluatedProperties: false - properties: - listen: - type: object - title: ListenTaskConfiguration - description: The configuration of the listener to use. - unevaluatedProperties: false - properties: - to: - $ref: '#/$defs/eventConsumptionStrategy' - title: ListenTo - description: Defines the event(s) to listen to. - read: - type: string - enum: [ data, envelope, raw ] - default: data - title: ListenAndReadAs - description: Specifies how events are read during the listen operation. - required: [ to ] - foreach: - $ref: '#/$defs/subscriptionIterator' - title: ListenIterator - description: Configures the iterator, if any, for processing consumed event(s). + allOf: + - $ref: '#/$defs/taskBase' + - properties: + listen: + type: object + title: ListenTaskConfiguration + description: The configuration of the listener to use. + unevaluatedProperties: false + properties: + to: + $ref: '#/$defs/eventConsumptionStrategy' + title: ListenTo + description: Defines the event(s) to listen to. + read: + type: string + enum: [ data, envelope, raw ] + default: data + title: ListenAndReadAs + description: Specifies how events are read during the listen operation. + required: [ to ] + foreach: + $ref: '#/$defs/subscriptionIterator' + title: ListenIterator + description: Configures the iterator, if any, for processing consumed event(s). raiseTask: type: object - $ref: '#/$defs/taskBase' title: RaiseTask description: Intentionally triggers and propagates errors. required: [ raise ] unevaluatedProperties: false - properties: - raise: - type: object - title: RaiseTaskConfiguration - description: The definition of the error to raise. - unevaluatedProperties: false - properties: - error: - title: RaiseTaskError - oneOf: - - $ref: '#/$defs/error' - title: RaiseErrorDefinition - description: Defines the error to raise. - - type: string - title: RaiseErrorReference - description: The name of the error to raise - required: [ error ] + allOf: + - $ref: '#/$defs/taskBase' + - properties: + raise: + type: object + title: RaiseTaskConfiguration + description: The definition of the error to raise. + unevaluatedProperties: false + properties: + error: + title: RaiseTaskError + oneOf: + - $ref: '#/$defs/error' + title: RaiseErrorDefinition + description: Defines the error to raise. + - type: string + title: RaiseErrorReference + description: The name of the error to raise + required: [ error ] runTask: type: object - $ref: '#/$defs/taskBase' title: RunTask description: Provides the capability to execute external containers, shell commands, scripts, or workflows. required: [ run ] unevaluatedProperties: false - properties: - run: - type: object - title: RunTaskConfiguration - description: The configuration of the process to execute. - unevaluatedProperties: false - properties: - await: - type: boolean - default: true - title: AwaitProcessCompletion - description: Whether to await the process completion before continuing. - return: - type: string - title: ProcessReturnType - description: Configures the output of the process. - enum: [ stdout, stderr, code, all, none ] - default: stdout - oneOf: - - title: RunContainer - description: Enables the execution of external processes encapsulated within a containerized environment. - properties: - container: - type: object - title: Container - description: The configuration of the container to run. - unevaluatedProperties: false - properties: - image: - type: string - title: ContainerImage - description: The name of the container image to run. - name: - type: string - title: ContainerName - description: A runtime expression, if any, used to give specific name to the container. - command: - type: string - title: ContainerCommand - description: The command, if any, to execute on the container. - ports: - type: object - title: ContainerPorts - description: The container's port mappings, if any. - volumes: - type: object - title: ContainerVolumes - description: The container's volume mappings, if any. - environment: - type: object - title: ContainerEnvironment - description: A key/value mapping of the environment variables, if any, to use when running the configured process. - lifetime: - $ref: '#/$defs/containerLifetime' - title: ContainerLifetime - description: An object, if any, used to configure the container's lifetime - required: [ image ] - required: [ container ] - - title: RunScript - description: Enables the execution of custom scripts or code within a workflow, empowering workflows to perform specialized logic, data processing, or integration tasks by executing user-defined scripts written in various programming languages. - properties: - script: - type: object - title: Script - description: The configuration of the script to run. - unevaluatedProperties: false - properties: - language: - type: string - title: ScriptLanguage - description: The language of the script to run. - arguments: - type: object - title: ScriptArguments - description: A key/value mapping of the arguments, if any, to use when running the configured script. - additionalProperties: true - environment: - type: object - title: ScriptEnvironment - description: A key/value mapping of the environment variables, if any, to use when running the configured script process. - additionalProperties: true - oneOf: - - title: InlineScript - type: object - description: The script's code. - properties: - code: - type: string - title: InlineScriptCode - required: [ code ] - - title: ExternalScript - type: object - description: The script's resource. - properties: - source: - $ref: '#/$defs/externalResource' - title: ExternalScriptResource - required: [ source ] - required: [ language ] - required: [ script ] - - title: RunShell - description: Enables the execution of shell commands within a workflow, enabling workflows to interact with the underlying operating system and perform system-level operations, such as file manipulation, environment configuration, or system administration tasks. - properties: - shell: - type: object - title: Shell - description: The configuration of the shell command to run. - unevaluatedProperties: false - properties: - command: - type: string - title: ShellCommand - description: The shell command to run. - arguments: - type: object - title: ShellArguments - description: A list of the arguments of the shell command to run. - additionalProperties: true - environment: - type: object - title: ShellEnvironment - description: A key/value mapping of the environment variables, if any, to use when running the configured process. - additionalProperties: true - required: [ command ] - required: [ shell ] - - title: RunWorkflow - description: Enables the invocation and execution of nested workflows within a parent workflow, facilitating modularization, reusability, and abstraction of complex logic or business processes by encapsulating them into standalone workflow units. - properties: - workflow: - type: object - title: SubflowConfiguration - description: The configuration of the workflow to run. - unevaluatedProperties: false - properties: - namespace: - type: string - title: SubflowNamespace - description: The namespace the workflow to run belongs to. - name: - type: string - title: SubflowName - description: The name of the workflow to run. - version: - type: string - default: latest - title: SubflowVersion - description: The version of the workflow to run. Defaults to latest. - input: - type: object - title: SubflowInput - description: The data, if any, to pass as input to the workflow to execute. The value should be validated against the target workflow's input schema, if specified. - additionalProperties: true - required: [ namespace, name, version ] - required: [ workflow ] + allOf: + - $ref: '#/$defs/taskBase' + - properties: + run: + type: object + title: RunTaskConfiguration + description: The configuration of the process to execute. + unevaluatedProperties: false + properties: + await: + type: boolean + default: true + title: AwaitProcessCompletion + description: Whether to await the process completion before continuing. + return: + type: string + title: ProcessReturnType + description: Configures the output of the process. + enum: [ stdout, stderr, code, all, none ] + default: stdout + oneOf: + - title: RunContainer + description: Enables the execution of external processes encapsulated within a containerized environment. + properties: + container: + type: object + title: Container + description: The configuration of the container to run. + unevaluatedProperties: false + properties: + image: + type: string + title: ContainerImage + description: The name of the container image to run. + name: + type: string + title: ContainerName + description: A runtime expression, if any, used to give specific name to the container. + command: + type: string + title: ContainerCommand + description: The command, if any, to execute on the container. + ports: + type: object + title: ContainerPorts + description: The container's port mappings, if any. + volumes: + type: object + title: ContainerVolumes + description: The container's volume mappings, if any. + environment: + type: object + title: ContainerEnvironment + description: A key/value mapping of the environment variables, if any, to use when running the configured process. + lifetime: + $ref: '#/$defs/containerLifetime' + title: ContainerLifetime + description: An object, if any, used to configure the container's lifetime + required: [ image ] + required: [ container ] + - title: RunScript + description: Enables the execution of custom scripts or code within a workflow, empowering workflows to perform specialized logic, data processing, or integration tasks by executing user-defined scripts written in various programming languages. + properties: + script: + type: object + title: Script + description: The configuration of the script to run. + unevaluatedProperties: false + properties: + language: + type: string + title: ScriptLanguage + description: The language of the script to run. + arguments: + type: object + title: ScriptArguments + description: A key/value mapping of the arguments, if any, to use when running the configured script. + additionalProperties: true + environment: + type: object + title: ScriptEnvironment + description: A key/value mapping of the environment variables, if any, to use when running the configured script process. + additionalProperties: true + oneOf: + - title: InlineScript + type: object + description: The script's code. + properties: + code: + type: string + title: InlineScriptCode + required: [ code ] + - title: ExternalScript + type: object + description: The script's resource. + properties: + source: + $ref: '#/$defs/externalResource' + title: ExternalScriptResource + required: [ source ] + required: [ language ] + required: [ script ] + - title: RunShell + description: Enables the execution of shell commands within a workflow, enabling workflows to interact with the underlying operating system and perform system-level operations, such as file manipulation, environment configuration, or system administration tasks. + properties: + shell: + type: object + title: Shell + description: The configuration of the shell command to run. + unevaluatedProperties: false + properties: + command: + type: string + title: ShellCommand + description: The shell command to run. + arguments: + type: object + title: ShellArguments + description: A list of the arguments of the shell command to run. + additionalProperties: true + environment: + type: object + title: ShellEnvironment + description: A key/value mapping of the environment variables, if any, to use when running the configured process. + additionalProperties: true + required: [ command ] + required: [ shell ] + - title: RunWorkflow + description: Enables the invocation and execution of nested workflows within a parent workflow, facilitating modularization, reusability, and abstraction of complex logic or business processes by encapsulating them into standalone workflow units. + properties: + workflow: + type: object + title: SubflowConfiguration + description: The configuration of the workflow to run. + unevaluatedProperties: false + properties: + namespace: + type: string + title: SubflowNamespace + description: The namespace the workflow to run belongs to. + name: + type: string + title: SubflowName + description: The name of the workflow to run. + version: + type: string + default: latest + title: SubflowVersion + description: The version of the workflow to run. Defaults to latest. + input: + type: object + title: SubflowInput + description: The data, if any, to pass as input to the workflow to execute. The value should be validated against the target workflow's input schema, if specified. + additionalProperties: true + required: [ namespace, name, version ] + required: [ workflow ] setTask: type: object - $ref: '#/$defs/taskBase' title: SetTask description: A task used to set data. required: [ set ] unevaluatedProperties: false - properties: - set: - type: object - title: SetTaskConfiguration - description: The data to set. - minProperties: 1 - additionalProperties: true + allOf: + - $ref: '#/$defs/taskBase' + - properties: + set: + oneOf: + - type: object + minProperties: 1 + additionalProperties: true + - type: string + title: SetTaskConfiguration + description: The data to set. switchTask: type: object - $ref: '#/$defs/taskBase' title: SwitchTask description: Enables conditional branching within workflows, allowing them to dynamically select different paths based on specified conditions or criteria. required: [ switch ] unevaluatedProperties: false - properties: - switch: - type: array - title: SwitchTaskConfiguration - description: The definition of the switch to use. - minItems: 1 - items: - type: object - title: SwitchItem - minProperties: 1 - maxProperties: 1 - additionalProperties: + allOf: + - $ref: '#/$defs/taskBase' + - properties: + switch: + type: array + title: SwitchTaskConfiguration + description: The definition of the switch to use. + minItems: 1 + items: type: object - title: SwitchCase - description: The definition of a case within a switch task, defining a condition and corresponding tasks to execute if the condition is met. - unevaluatedProperties: false - required: [ then ] - properties: - when: - type: string - title: SwitchCaseCondition - description: A runtime expression used to determine whether or not the case matches. - then: - $ref: '#/$defs/flowDirective' - title: SwitchCaseOutcome - description: The flow directive to execute when the case matches. + title: SwitchItem + minProperties: 1 + maxProperties: 1 + additionalProperties: + type: object + title: SwitchCase + description: The definition of a case within a switch task, defining a condition and corresponding tasks to execute if the condition is met. + unevaluatedProperties: false + required: [ then ] + properties: + when: + type: string + title: SwitchCaseCondition + description: A runtime expression used to determine whether or not the case matches. + then: + $ref: '#/$defs/flowDirective' + title: SwitchCaseOutcome + description: The flow directive to execute when the case matches. tryTask: type: object - $ref: '#/$defs/taskBase' title: TryTask description: Serves as a mechanism within workflows to handle errors gracefully, potentially retrying failed tasks before proceeding with alternate ones. required: [ try, catch ] unevaluatedProperties: false - properties: - try: - $ref: '#/$defs/taskList' - title: TryTaskConfiguration - description: The task(s) to perform. - catch: - type: object - title: TryTaskCatch - description: The object used to define the errors to catch. - unevaluatedProperties: false - properties: - errors: - type: object - title: CatchErrors - properties: - with: - $ref: '#/$defs/errorFilter' - description: static error filter - as: - type: string - title: CatchAs - description: The name of the runtime expression variable to save the error as. Defaults to 'error'. - when: - type: string - title: CatchWhen - description: A runtime expression used to determine whether to catch the filtered error. - exceptWhen: - type: string - title: CatchExceptWhen - description: A runtime expression used to determine whether not to catch the filtered error. - retry: - oneOf: - - $ref: '#/$defs/retryPolicy' - title: RetryPolicyDefinition - description: The retry policy to use, if any, when catching errors. - - type: string - title: RetryPolicyReference - description: The name of the retry policy to use, if any, when catching errors. - do: - $ref: '#/$defs/taskList' - title: TryTaskCatchDo - description: The definition of the task(s) to run when catching an error. + allOf: + - $ref: '#/$defs/taskBase' + - properties: + try: + $ref: '#/$defs/taskList' + title: TryTaskConfiguration + description: The task(s) to perform. + catch: + type: object + title: TryTaskCatch + description: The object used to define the errors to catch. + unevaluatedProperties: false + properties: + errors: + type: object + title: CatchErrors + properties: + with: + $ref: '#/$defs/errorFilter' + description: static error filter + as: + type: string + title: CatchAs + description: The name of the runtime expression variable to save the error as. Defaults to 'error'. + when: + type: string + title: CatchWhen + description: A runtime expression used to determine whether to catch the filtered error. + exceptWhen: + type: string + title: CatchExceptWhen + description: A runtime expression used to determine whether not to catch the filtered error. + retry: + oneOf: + - $ref: '#/$defs/retryPolicy' + title: RetryPolicyDefinition + description: The retry policy to use, if any, when catching errors. + - type: string + title: RetryPolicyReference + description: The name of the retry policy to use, if any, when catching errors. + do: + $ref: '#/$defs/taskList' + title: TryTaskCatchDo + description: The definition of the task(s) to run when catching an error. waitTask: type: object - $ref: '#/$defs/taskBase' title: WaitTask description: Allows workflows to pause or delay their execution for a specified period of time. required: [ wait ] unevaluatedProperties: false - properties: - wait: - $ref: '#/$defs/duration' - title: WaitTaskConfiguration - description: The amount of time to wait. + allOf: + - $ref: '#/$defs/taskBase' + - properties: + wait: + $ref: '#/$defs/duration' + title: WaitTaskConfiguration + description: The amount of time to wait. flowDirective: title: FlowDirective description: Represents different transition options for a workflow. @@ -1188,13 +1214,21 @@ $defs: title: ExpressionErrorInstance description: An expression based error instance. title: - type: string - title: ErrorTitle description: A short, human-readable summary of the error. + title: ErrorTitle + anyOf: + - $ref: '#/$defs/runtimeExpression' + title: ExpressionErrorTitle + - type: string + title: LiteralErrorTitle detail: - type: string title: ErrorDetails description: A human-readable explanation specific to this occurrence of the error. + anyOf: + - $ref: '#/$defs/runtimeExpression' + title: ExpressionErrorDetails + - type: string + title: LiteralErrorDetails required: [ type, status ] errorFilter: type: object diff --git a/custom-generator/src/main/java/io/serverlessworkflow/generator/AllAnyOneOfSchemaRule.java b/custom-generator/src/main/java/io/serverlessworkflow/generator/AllAnyOneOfSchemaRule.java index 622efcbb..28a611cb 100644 --- a/custom-generator/src/main/java/io/serverlessworkflow/generator/AllAnyOneOfSchemaRule.java +++ b/custom-generator/src/main/java/io/serverlessworkflow/generator/AllAnyOneOfSchemaRule.java @@ -52,6 +52,7 @@ class AllAnyOneOfSchemaRule extends SchemaRule { + private static final String ALL_OF = "allOf"; private RuleFactory ruleFactory; AllAnyOneOfSchemaRule(RuleFactory ruleFactory) { @@ -142,7 +143,7 @@ public JType apply( unionType("oneOf", nodeName, schemaNode, parent, generatableType, schema, oneOfTypes); unionType("anyOf", nodeName, schemaNode, parent, generatableType, schema, oneOfTypes); - unionType("allOf", nodeName, schemaNode, parent, generatableType, schema, allOfTypes); + allOfType(nodeName, schemaNode, parent, generatableType, schema, allOfTypes); Collections.sort(oneOfTypes); @@ -204,6 +205,100 @@ public JType apply( return javaType; } + private void allOfType( + String nodeName, + JsonNode schemaNode, + JsonNode parent, + JClassContainer generatableType, + Schema schema, + List allOfTypes) { + if (schemaNode.has(ALL_OF)) { + ArrayNode array = (ArrayNode) schemaNode.get(ALL_OF); + if (array.size() == 2) { + JsonNode refNode = null; + JsonNode propsNode = null; + int refNodePos = 0; + int propsNodePos = 0; + int pos = 0; + for (JsonNode node : array) { + if (node.isObject() && node.size() == 1) { + if (node.has(REF)) { + refNode = node; + refNodePos = pos++; + } else if (node.has("properties")) { + propsNode = node; + propsNodePos = pos++; + } else { + pos++; + break; + } + } + } + if (refNode != null && propsNode != null) { + allOfTypes.add( + new JTypeWrapper( + inheritanceNode( + nodeName, + schemaNode, + generatableType, + schema, + refNode, + refNodePos, + propsNode, + propsNodePos), + array)); + return; + } + } + unionType(ALL_OF, nodeName, schemaNode, parent, generatableType, schema, allOfTypes); + } + } + + private JType inheritanceNode( + String nodeName, + JsonNode schemaNode, + JClassContainer container, + Schema schema, + JsonNode refNode, + int refNodePos, + JsonNode propsNode, + int propsNodePos) { + try { + JDefinedClass javaType = + container._class( + ruleFactory + .getNameHelper() + .getUniqueClassName(nodeName, schemaNode, container.getPackage())); + javaType._extends( + (JClass) + refType( + refNode.get(REF).asText(), + nodeName, + refNode, + schemaNode, + container, + childSchema(schema, ALL_OF, refNodePos))); + ruleFactory + .getPropertiesRule() + .apply( + nodeName, + propsNode.get("properties"), + propsNode, + javaType, + childSchema(schema, ALL_OF, propsNodePos)); + return javaType; + } catch (JClassAlreadyExistsException e) { + throw new IllegalStateException(e); + } + } + + private Schema childSchema(Schema parentSchema, String prefix, int pos) { + String ref = parentSchema.getId().toString() + '/' + prefix + '/' + pos; + return ruleFactory + .getSchemaStore() + .create(URI.create(ref), ruleFactory.getGenerationConfig().getRefFragmentPathDelimiters()); + } + private JDefinedClass populateAllOf( Schema parentSchema, JDefinedClass definedClass, Collection allOfTypes) { return wrapAll(parentSchema, definedClass, Optional.empty(), allOfTypes, Optional.empty()); @@ -344,8 +439,10 @@ private void wrapIt( Optional valueField, JType unionType, JsonNode node) { - JFieldVar instanceField = getInstanceField(parentSchema, definedClass, unionType, node); - JMethod method = getSetterMethod(definedClass, instanceField, node); + String typeName = getTypeName(node, unionType, parentSchema); + JFieldVar instanceField = + getInstanceField(typeName, parentSchema, definedClass, unionType, node); + JMethod method = getSetterMethod(typeName, definedClass, instanceField, node); method .body() .assign( @@ -370,8 +467,8 @@ private JVar setupMethod( } private JMethod getSetterMethod( - JDefinedClass definedClass, JFieldVar instanceField, JsonNode node) { - String setterName = ruleFactory.getNameHelper().getSetterName(instanceField.name(), node); + String fieldName, JDefinedClass definedClass, JFieldVar instanceField, JsonNode node) { + String setterName = ruleFactory.getNameHelper().getSetterName(fieldName, node); JMethod fluentMethod = definedClass.method(JMod.PUBLIC, definedClass, setterName.replaceFirst("set", "with")); JBlock body = fluentMethod.body(); @@ -391,9 +488,10 @@ private void wrapStrings( if (pattern == null && iter.hasNext()) { pattern = ".*"; } + String typeName = getTypeName(first.getNode(), first.getType(), parentSchema); JFieldVar instanceField = - getInstanceField(parentSchema, definedClass, first.getType(), first.getNode()); - JMethod setterMethod = getSetterMethod(definedClass, instanceField, first.getNode()); + getInstanceField(typeName, parentSchema, definedClass, first.getType(), first.getNode()); + JMethod setterMethod = getSetterMethod(typeName, definedClass, instanceField, first.getNode()); JVar methodParam = setupMethod(definedClass, setterMethod, valueField, instanceField); JBlock body = setterMethod.body(); if (pattern != null) { @@ -403,7 +501,12 @@ private void wrapStrings( while (iter.hasNext()) { JTypeWrapper item = iter.next(); instanceField = - getInstanceField(parentSchema, definedClass, item.getType(), item.getNode()); + getInstanceField( + getTypeName(item.getNode(), item.getType(), parentSchema), + parentSchema, + definedClass, + item.getType(), + item.getNode()); pattern = pattern(item.getNode(), parentSchema); if (pattern == null) { pattern = ".*"; @@ -431,16 +534,16 @@ private void wrapStrings( } private JFieldVar getInstanceField( - Schema parentSchema, JDefinedClass definedClass, JType type, JsonNode node) { + String fieldName, + Schema parentSchema, + JDefinedClass definedClass, + JType type, + JsonNode node) { JFieldVar instanceField = definedClass.field( - JMod.PRIVATE, - type, - ruleFactory - .getNameHelper() - .getPropertyName(getTypeName(node, type, parentSchema), node)); + JMod.PRIVATE, type, ruleFactory.getNameHelper().getPropertyName(fieldName, node)); GeneratorUtils.getterMethod( - definedClass, instanceField, ruleFactory.getNameHelper(), instanceField.name()); + definedClass, instanceField, ruleFactory.getNameHelper(), fieldName); return instanceField; } @@ -486,13 +589,7 @@ private void unionType( int i = 0; for (JsonNode oneOf : array) { if (!ignoreNode(oneOf)) { - String ref = parentSchema.getId().toString() + '/' + prefix + '/' + i++; - Schema schema = - ruleFactory - .getSchemaStore() - .create( - URI.create(ref), - ruleFactory.getGenerationConfig().getRefFragmentPathDelimiters()); + Schema schema = childSchema(parentSchema, prefix, i++); types.add( new JTypeWrapper( schema.isGenerated() @@ -529,29 +626,39 @@ private Optional refType( String nodeName, JsonNode schemaNode, JsonNode parent, - JClassContainer generatableType, + JClassContainer container, Schema parentSchema) { - if (schemaNode.has(REF)) { - String ref = schemaNode.get(REF).asText(); - Schema schema = - ruleFactory - .getSchemaStore() - .create( - parentSchema, - ref, - ruleFactory.getGenerationConfig().getRefFragmentPathDelimiters()); - - return Optional.of( - schema.isGenerated() - ? schema.getJavaType() - : apply( - nameFromRef(ref, nodeName, schemaNode), - schema.getContent(), - parent, - generatableType, - schema)); - } - return Optional.empty(); + return schemaNode.has(REF) + ? Optional.of( + refType( + schemaNode.get(REF).asText(), + nodeName, + schemaNode, + parent, + container, + parentSchema)) + : Optional.empty(); + } + + private JType refType( + String ref, + String nodeName, + JsonNode schemaNode, + JsonNode parent, + JClassContainer container, + Schema parentSchema) { + Schema schema = + ruleFactory + .getSchemaStore() + .create( + parentSchema, + ref, + ruleFactory.getGenerationConfig().getRefFragmentPathDelimiters()); + + return schema.isGenerated() + ? schema.getJavaType() + : apply( + nameFromRef(ref, nodeName, schemaNode), schema.getContent(), parent, container, schema); } private JsonNode schemaRef(JsonNode schemaNode, Schema parentSchema) { diff --git a/impl/core/src/main/java/io/serverlessworkflow/impl/executors/RaiseExecutor.java b/impl/core/src/main/java/io/serverlessworkflow/impl/executors/RaiseExecutor.java index 6dd43c2b..7a2c4025 100644 --- a/impl/core/src/main/java/io/serverlessworkflow/impl/executors/RaiseExecutor.java +++ b/impl/core/src/main/java/io/serverlessworkflow/impl/executors/RaiseExecutor.java @@ -30,7 +30,6 @@ import io.serverlessworkflow.impl.WorkflowException; import io.serverlessworkflow.impl.WorkflowPosition; import io.serverlessworkflow.impl.WorkflowUtils; -import io.serverlessworkflow.impl.executors.RegularTaskExecutor.RegularTaskExecutorBuilder; import io.serverlessworkflow.impl.expressions.ExpressionFactory; import io.serverlessworkflow.impl.resources.ResourceLoader; import java.util.Map; @@ -66,9 +65,15 @@ protected RaiseExecutorBuilder( this.instanceFilter = getInstanceFunction(application.expressionFactory(), error.getInstance()); this.titleFilter = - WorkflowUtils.buildStringFilter(application.expressionFactory(), error.getTitle()); + WorkflowUtils.buildStringFilter( + application.expressionFactory(), + error.getTitle().getExpressionErrorTitle(), + error.getTitle().getLiteralErrorTitle()); this.detailFilter = - WorkflowUtils.buildStringFilter(application.expressionFactory(), error.getDetail()); + WorkflowUtils.buildStringFilter( + application.expressionFactory(), + error.getDetail().getExpressionErrorDetails(), + error.getTitle().getExpressionErrorTitle()); this.errorBuilder = (w, t) -> buildError(error, w, t); } diff --git a/impl/core/src/main/java/io/serverlessworkflow/impl/executors/SetExecutor.java b/impl/core/src/main/java/io/serverlessworkflow/impl/executors/SetExecutor.java index c5600891..f8373d39 100644 --- a/impl/core/src/main/java/io/serverlessworkflow/impl/executors/SetExecutor.java +++ b/impl/core/src/main/java/io/serverlessworkflow/impl/executors/SetExecutor.java @@ -16,25 +16,26 @@ package io.serverlessworkflow.impl.executors; import com.fasterxml.jackson.databind.JsonNode; +import io.serverlessworkflow.api.types.Set; import io.serverlessworkflow.api.types.SetTask; +import io.serverlessworkflow.api.types.SetTaskConfiguration; import io.serverlessworkflow.api.types.Workflow; import io.serverlessworkflow.impl.TaskContext; import io.serverlessworkflow.impl.WorkflowApplication; import io.serverlessworkflow.impl.WorkflowContext; +import io.serverlessworkflow.impl.WorkflowFilter; import io.serverlessworkflow.impl.WorkflowPosition; -import io.serverlessworkflow.impl.expressions.ExpressionUtils; -import io.serverlessworkflow.impl.json.JsonUtils; +import io.serverlessworkflow.impl.WorkflowUtils; import io.serverlessworkflow.impl.resources.ResourceLoader; -import java.util.Map; import java.util.concurrent.CompletableFuture; public class SetExecutor extends RegularTaskExecutor { - private final Map toBeSet; + private final WorkflowFilter setFilter; public static class SetExecutorBuilder extends RegularTaskExecutorBuilder { - private final Map toBeSet; + private final WorkflowFilter setFilter; protected SetExecutorBuilder( WorkflowPosition position, @@ -43,9 +44,13 @@ protected SetExecutorBuilder( WorkflowApplication application, ResourceLoader resourceLoader) { super(position, task, workflow, application, resourceLoader); - this.toBeSet = - ExpressionUtils.buildExpressionMap( - task.getSet().getAdditionalProperties(), application.expressionFactory()); + Set setInfo = task.getSet(); + SetTaskConfiguration setConfig = setInfo.getSetTaskConfiguration(); + this.setFilter = + WorkflowUtils.buildWorkflowFilter( + application.expressionFactory(), + setInfo.getString(), + setConfig != null ? setConfig.getAdditionalProperties() : null); } @Override @@ -56,15 +61,13 @@ public TaskExecutor buildInstance() { private SetExecutor(SetExecutorBuilder builder) { super(builder); - this.toBeSet = builder.toBeSet; + this.setFilter = builder.setFilter; } @Override protected CompletableFuture internalExecute( WorkflowContext workflow, TaskContext taskContext) { return CompletableFuture.completedFuture( - JsonUtils.fromValue( - ExpressionUtils.evaluateExpressionMap( - toBeSet, workflow, taskContext, taskContext.input()))); + setFilter.apply(workflow, taskContext, taskContext.input())); } } diff --git a/impl/http/src/main/java/io/serverlessworkflow/impl/executors/HttpExecutor.java b/impl/http/src/main/java/io/serverlessworkflow/impl/executors/HttpExecutor.java index 3c078309..d60c4655 100644 --- a/impl/http/src/main/java/io/serverlessworkflow/impl/executors/HttpExecutor.java +++ b/impl/http/src/main/java/io/serverlessworkflow/impl/executors/HttpExecutor.java @@ -28,6 +28,8 @@ import io.serverlessworkflow.impl.WorkflowContext; import io.serverlessworkflow.impl.WorkflowError; import io.serverlessworkflow.impl.WorkflowException; +import io.serverlessworkflow.impl.WorkflowFilter; +import io.serverlessworkflow.impl.WorkflowUtils; import io.serverlessworkflow.impl.expressions.Expression; import io.serverlessworkflow.impl.expressions.ExpressionFactory; import io.serverlessworkflow.impl.expressions.ExpressionUtils; @@ -40,8 +42,10 @@ import jakarta.ws.rs.client.Entity; import jakarta.ws.rs.client.Invocation.Builder; import jakarta.ws.rs.client.WebTarget; +import java.util.Iterator; import java.util.Map; import java.util.Map.Entry; +import java.util.Optional; import java.util.concurrent.CompletableFuture; public class HttpExecutor implements CallableTask { @@ -49,8 +53,8 @@ public class HttpExecutor implements CallableTask { private static final Client client = ClientBuilder.newClient(); private TargetSupplier targetSupplier; - private Map headersMap; - private Map queryMap; + private Optional headersMap; + private Optional queryMap; private RequestSupplier requestFunction; @FunctionalInterface @@ -70,14 +74,24 @@ public void init(CallHTTP task, WorkflowApplication application, ResourceLoader getTargetSupplier(httpArgs.getEndpoint(), application.expressionFactory()); this.headersMap = httpArgs.getHeaders() != null - ? ExpressionUtils.buildExpressionMap( - httpArgs.getHeaders().getAdditionalProperties(), application.expressionFactory()) - : Map.of(); + ? Optional.of( + WorkflowUtils.buildWorkflowFilter( + application.expressionFactory(), + httpArgs.getHeaders().getRuntimeExpression(), + httpArgs.getHeaders().getHTTPHeaders() != null + ? httpArgs.getHeaders().getHTTPHeaders().getAdditionalProperties() + : null)) + : Optional.empty(); this.queryMap = httpArgs.getQuery() != null - ? ExpressionUtils.buildExpressionMap( - httpArgs.getQuery().getAdditionalProperties(), application.expressionFactory()) - : Map.of(); + ? Optional.of( + WorkflowUtils.buildWorkflowFilter( + application.expressionFactory(), + httpArgs.getQuery().getRuntimeExpression(), + httpArgs.getQuery().getHTTPQuery() != null + ? httpArgs.getQuery().getHTTPQuery().getAdditionalProperties() + : null)) + : Optional.empty(); switch (httpArgs.getMethod().toUpperCase()) { case HttpMethod.POST: Object body = @@ -100,13 +114,22 @@ public void init(CallHTTP task, WorkflowApplication application, ResourceLoader public CompletableFuture apply( WorkflowContext workflow, TaskContext taskContext, JsonNode input) { WebTarget target = targetSupplier.apply(workflow, taskContext, input); - for (Entry entry : - ExpressionUtils.evaluateExpressionMap(queryMap, workflow, taskContext, input).entrySet()) { - target = target.queryParam(entry.getKey(), entry.getValue()); + Optional queryJson = queryMap.map(q -> q.apply(workflow, taskContext, input)); + if (queryJson.isPresent()) { + Iterator> iter = queryJson.orElseThrow().fields(); + while (iter.hasNext()) { + Entry item = iter.next(); + target = target.queryParam(item.getKey(), JsonUtils.toJavaValue(item.getValue())); + } } + Builder request = target.request(); - ExpressionUtils.evaluateExpressionMap(headersMap, workflow, taskContext, input) - .forEach(request::header); + headersMap.ifPresent( + h -> + h.apply(workflow, taskContext, input) + .fields() + .forEachRemaining( + e -> request.header(e.getKey(), JsonUtils.toJavaValue(e.getValue())))); return CompletableFuture.supplyAsync( () -> { try {