diff --git a/.github/project.yml b/.github/project.yml index e787d911..1d03de1c 100644 --- a/.github/project.yml +++ b/.github/project.yml @@ -1,4 +1,4 @@ # Retriggering release again release: - current-version: 7.0.0.Final + current-version: 7.1.0.Final next-version: 8.0.0-SNAPSHOT diff --git a/api/pom.xml b/api/pom.xml index 69f8c2f5..9f0802fe 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -4,7 +4,7 @@ io.serverlessworkflow serverlessworkflow-parent - 8.0.0-SNAPSHOT + 7.1.0.Final serverlessworkflow-api @@ -40,10 +40,12 @@ org.hibernate.validator hibernate-validator + runtime org.glassfish.expressly expressly + runtime 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/pom.xml b/custom-generator/pom.xml index 3660e286..6313203e 100644 --- a/custom-generator/pom.xml +++ b/custom-generator/pom.xml @@ -1,10 +1,9 @@ - + 4.0.0 io.serverlessworkflow serverlessworkflow-parent - 8.0.0-SNAPSHOT + 7.1.0.Final serverless-workflow-custom-generator Serverless Workflow :: Custom Generator 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/pom.xml b/impl/core/pom.xml index a5fac29a..e8f808b1 100644 --- a/impl/core/pom.xml +++ b/impl/core/pom.xml @@ -1,10 +1,9 @@ - + 4.0.0 io.serverlessworkflow serverlessworkflow-impl - 8.0.0-SNAPSHOT + 7.1.0.Final serverlessworkflow-impl-core Serverless Workflow :: Impl :: Core diff --git a/impl/core/src/main/java/io/serverlessworkflow/impl/executors/AbstractTaskExecutor.java b/impl/core/src/main/java/io/serverlessworkflow/impl/executors/AbstractTaskExecutor.java index f51b7a01..23ca9a22 100644 --- a/impl/core/src/main/java/io/serverlessworkflow/impl/executors/AbstractTaskExecutor.java +++ b/impl/core/src/main/java/io/serverlessworkflow/impl/executors/AbstractTaskExecutor.java @@ -49,12 +49,14 @@ public abstract class AbstractTaskExecutor implements TaskEx private final Optional inputSchemaValidator; private final Optional outputSchemaValidator; private final Optional contextSchemaValidator; + private final Optional ifFilter; public abstract static class AbstractTaskExecutorBuilder implements TaskExecutorBuilder { private Optional inputProcessor = Optional.empty(); private Optional outputProcessor = Optional.empty(); private Optional contextProcessor = Optional.empty(); + private Optional ifFilter = Optional.empty(); private Optional inputSchemaValidator = Optional.empty(); private Optional outputSchemaValidator = Optional.empty(); private Optional contextSchemaValidator = Optional.empty(); @@ -100,6 +102,7 @@ protected AbstractTaskExecutorBuilder( this.contextSchemaValidator = getSchemaValidator(application.validatorFactory(), resourceLoader, export.getSchema()); } + this.ifFilter = optionalFilter(application.expressionFactory(), task.getIf()); } protected final TransitionInfoBuilder next( @@ -153,6 +156,7 @@ protected AbstractTaskExecutor(AbstractTaskExecutorBuilder builder) { this.inputSchemaValidator = builder.inputSchemaValidator; this.outputSchemaValidator = builder.outputSchemaValidator; this.contextSchemaValidator = builder.contextSchemaValidator; + this.ifFilter = builder.ifFilter; } protected final CompletableFuture executeNext( @@ -177,40 +181,49 @@ public CompletableFuture apply( if (!TaskExecutorHelper.isActive(workflowContext)) { return completable; } - return executeNext( - completable - .thenApply( - t -> { - workflowContext - .definition() - .listeners() - .forEach(l -> l.onTaskStarted(position, task)); - inputSchemaValidator.ifPresent(s -> s.validate(t.rawInput())); - inputProcessor.ifPresent( - p -> taskContext.input(p.apply(workflowContext, t, t.rawInput()))); - return t; - }) - .thenCompose(t -> execute(workflowContext, t)) - .thenApply( - t -> { - outputProcessor.ifPresent( - p -> t.output(p.apply(workflowContext, t, t.rawOutput()))); - outputSchemaValidator.ifPresent(s -> s.validate(t.output())); - contextProcessor.ifPresent( - p -> - workflowContext.context( - p.apply(workflowContext, t, workflowContext.context()))); - contextSchemaValidator.ifPresent(s -> s.validate(workflowContext.context())); - t.completedAt(Instant.now()); - workflowContext - .definition() - .listeners() - .forEach(l -> l.onTaskEnded(position, task)); - return t; - }), - workflowContext); + if (ifFilter + .map(f -> f.apply(workflowContext, taskContext, input).asBoolean(true)) + .orElse(true)) { + return executeNext( + completable + .thenApply( + t -> { + workflowContext + .definition() + .listeners() + .forEach(l -> l.onTaskStarted(position, task)); + inputSchemaValidator.ifPresent(s -> s.validate(t.rawInput())); + inputProcessor.ifPresent( + p -> taskContext.input(p.apply(workflowContext, t, t.rawInput()))); + return t; + }) + .thenCompose(t -> execute(workflowContext, t)) + .thenApply( + t -> { + outputProcessor.ifPresent( + p -> t.output(p.apply(workflowContext, t, t.rawOutput()))); + outputSchemaValidator.ifPresent(s -> s.validate(t.output())); + contextProcessor.ifPresent( + p -> + workflowContext.context( + p.apply(workflowContext, t, workflowContext.context()))); + contextSchemaValidator.ifPresent(s -> s.validate(workflowContext.context())); + t.completedAt(Instant.now()); + workflowContext + .definition() + .listeners() + .forEach(l -> l.onTaskEnded(position, task)); + return t; + }), + workflowContext); + } else { + taskContext.transition(getSkipTransition()); + return executeNext(completable, workflowContext); + } } + protected abstract TransitionInfo getSkipTransition(); + protected abstract CompletableFuture execute( WorkflowContext workflow, TaskContext taskContext); } 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/RegularTaskExecutor.java b/impl/core/src/main/java/io/serverlessworkflow/impl/executors/RegularTaskExecutor.java index 24c1e841..7cac9a8e 100644 --- a/impl/core/src/main/java/io/serverlessworkflow/impl/executors/RegularTaskExecutor.java +++ b/impl/core/src/main/java/io/serverlessworkflow/impl/executors/RegularTaskExecutor.java @@ -54,6 +54,11 @@ public void connect(Map> connections) { } } + @Override + protected TransitionInfo getSkipTransition() { + return transition; + } + protected CompletableFuture execute( WorkflowContext workflow, TaskContext taskContext) { CompletableFuture future = 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/core/src/main/java/io/serverlessworkflow/impl/executors/SwitchExecutor.java b/impl/core/src/main/java/io/serverlessworkflow/impl/executors/SwitchExecutor.java index 70b127c4..9bd3a74a 100644 --- a/impl/core/src/main/java/io/serverlessworkflow/impl/executors/SwitchExecutor.java +++ b/impl/core/src/main/java/io/serverlessworkflow/impl/executors/SwitchExecutor.java @@ -79,6 +79,11 @@ protected TaskExecutor buildInstance() { } } + @Override + protected TransitionInfo getSkipTransition() { + return defaultTask; + } + private SwitchExecutor(SwitchExecutorBuilder builder) { super(builder); this.defaultTask = TransitionInfo.build(builder.defaultTask); diff --git a/impl/core/src/test/java/io/serverlessworkflow/impl/WorkflowDefinitionTest.java b/impl/core/src/test/java/io/serverlessworkflow/impl/WorkflowDefinitionTest.java index 4ea87283..4a0a073f 100644 --- a/impl/core/src/test/java/io/serverlessworkflow/impl/WorkflowDefinitionTest.java +++ b/impl/core/src/test/java/io/serverlessworkflow/impl/WorkflowDefinitionTest.java @@ -81,6 +81,14 @@ private static Stream provideParameters() { "simple-expression.yaml", Map.of("input", Arrays.asList(1, 2, 3)), WorkflowDefinitionTest::checkSpecialKeywords), + args( + "conditional-set.yaml", + Map.of("enabled", true), + WorkflowDefinitionTest::checkEnableCondition), + args( + "conditional-set.yaml", + Map.of("enabled", false), + WorkflowDefinitionTest::checkDisableCondition), args( "raise-inline copy.yaml", WorkflowDefinitionTest::checkWorkflowException, @@ -166,4 +174,14 @@ private static void checkSpecialKeywords(Object obj) { assertThat(result.get("id").toString()).hasSize(26); assertThat(result.get("version").toString()).contains("alpha"); } + + private static void checkEnableCondition(Object obj) { + Map result = (Map) obj; + assertThat(result.get("name")).isEqualTo("javierito"); + } + + private static void checkDisableCondition(Object obj) { + Map result = (Map) obj; + assertThat(result.get("enabled")).isEqualTo(false); + } } diff --git a/impl/core/src/test/resources/conditional-set.yaml b/impl/core/src/test/resources/conditional-set.yaml new file mode 100644 index 00000000..5e06622d --- /dev/null +++ b/impl/core/src/test/resources/conditional-set.yaml @@ -0,0 +1,10 @@ +document: + dsl: '1.0.0-alpha5' + namespace: test + name: conditional-set + version: '0.1.0' +do: + - conditionalExpression: + if: .enabled + set: + name: javierito \ No newline at end of file diff --git a/impl/http/pom.xml b/impl/http/pom.xml index fdf2a168..14e7126e 100644 --- a/impl/http/pom.xml +++ b/impl/http/pom.xml @@ -1,10 +1,9 @@ - + 4.0.0 io.serverlessworkflow serverlessworkflow-impl - 8.0.0-SNAPSHOT + 7.1.0.Final serverlessworkflow-impl-http Serverless Workflow :: Impl :: HTTP @@ -41,5 +40,10 @@ assertj-core test + + ch.qos.logback + logback-classic + test + \ No newline at end of file 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 { diff --git a/impl/http/src/test/java/io/serverlessworkflow/impl/HTTPWorkflowDefinitionTest.java b/impl/http/src/test/java/io/serverlessworkflow/impl/HTTPWorkflowDefinitionTest.java index 7492be53..badb6403 100644 --- a/impl/http/src/test/java/io/serverlessworkflow/impl/HTTPWorkflowDefinitionTest.java +++ b/impl/http/src/test/java/io/serverlessworkflow/impl/HTTPWorkflowDefinitionTest.java @@ -98,7 +98,7 @@ private static Stream provideParameters() { "call-http-query-parameters-external-schema.yaml", starTrekInput, starTrekCondition), Arguments.of( "callPostHttp.yaml", - Map.of("name", "Javierito", "status", "available"), + Map.of("name", "Javierito", "surname", "Unknown"), new Condition<>(o -> o.equals("Javierito"), "CallHttpPostCondition"))); } } diff --git a/impl/http/src/test/resources/callPostHttp.yaml b/impl/http/src/test/resources/callPostHttp.yaml index d66dcfaa..f12fec42 100644 --- a/impl/http/src/test/resources/callPostHttp.yaml +++ b/impl/http/src/test/resources/callPostHttp.yaml @@ -9,20 +9,9 @@ do: with: method: post endpoint: - uri: https://petstore.swagger.io/v2/pet + uri: https://fakerestapi.azurewebsites.net/api/v1/Authors body: - name: ${.name} - status: ${.status} + firstName: ${.name} + lastName: ${.surname } output: - as: .id - - getPet: - call: http - with: - method: get - endpoint: - uri: https://petstore.swagger.io/v2/pet/{petId} - input: - from: - petId: ${.} - output: - as: .name \ No newline at end of file + as: .firstName \ No newline at end of file diff --git a/impl/pom.xml b/impl/pom.xml index 65fa9095..a8297955 100644 --- a/impl/pom.xml +++ b/impl/pom.xml @@ -1,10 +1,9 @@ - + 4.0.0 io.serverlessworkflow serverlessworkflow-parent - 8.0.0-SNAPSHOT + 7.1.0.Final serverlessworkflow-impl Serverless Workflow :: Impl diff --git a/pom.xml b/pom.xml index e054f843..54ef626d 100644 --- a/pom.xml +++ b/pom.xml @@ -1,11 +1,10 @@ - + 4.0.0 io.serverlessworkflow serverlessworkflow-parent - 8.0.0-SNAPSHOT + 7.1.0.Final pom Serverless Workflow :: Parent @@ -34,7 +33,7 @@ scm:git:git@github.com:serverlessworkflow/sdk-java.git scm:git:git@github.com:serverlessworkflow/sdk-java.git https://github.com/serverlessworkflow/sdk-java - HEAD + 7.1.0.Final @@ -53,10 +52,10 @@ 3.2.1 3.6.0 - 3.13.0 - 3.1.3 + 3.14.0 + 3.1.4 3.5.0 - 3.5.2 + 3.5.3 2.25 3.2.7 3.4.2 @@ -65,20 +64,20 @@ 3.11.2 3.1.1 3.3.1 - 3.5.2 - 1.6.13 + 3.5.3 + 1.7.0 - 1.5.16 - 2.18.2 - 1.5.5 + 1.5.18 + 2.18.3 + 1.5.6 3.1.1 1.5.2 3.27.3 - 5.11.4 - 5.15.2 - 2.0.16 + 5.12.2 + 5.17.0 + 2.0.17 8.0.2.Final 5.0.0 @@ -241,13 +240,13 @@ - - + + - - + + pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy