diff --git a/README.md b/README.md index 8cdabce1..557822f6 100644 --- a/README.md +++ b/README.md @@ -4,92 +4,111 @@ [](https://serverlessworkflow.io/) [](https://twitter.com/CNCFWorkflow) +## Table of Contents + +- [About](#about) +- [Ecosystem](#ecosystem) + + [DSL](dsl.md) + + [CTK](/ctk/readme.md) + + [SDKs](#sdks) + + [Runtimes](#runtimes) + + [Tooling](#Tooling) + + [Landscape](#cncf-landscape) +- [Documentation](#documentation) +- [Community](#community) + + [Communication](#communication) + + [Governance](#governance) + + [Code of Conduct](#code-of-conduct) + + [Weekly Meetings](#weekly-meetings) ++ [Support](#support) + - [Adoption](#adoption) + - [Sponsoring](#sponsoring) + ## About -CNCF Serverless Workflow defines a vendor-neutral, open-source, and fully community-driven -ecosystem for defining and running DSL-based workflows that target the Serverless technology domain. +Serverless Workflow presents a vendor-neutral, open-source, and entirely community-driven ecosystem tailored for defining and executing DSL-based workflows in the realm of Serverless technology. -This project is composed of: +The Serverless Workflow DSL is a high-level language that reshapes the terrain of workflow creation, boasting a design that is ubiquitous, intuitive, imperative, and fluent. -* [Specification](specification.md) for defining DSL-based workflows -* [Developer SDKs](#sdks) for different programming languages -* [Workflow runtimes](#runtimes) supporting the specification -* Developer [tooling support](#tooling) for writing DSL-based workflows +Bid farewell to convoluted coding and platform dependencies—now, crafting powerful workflows is effortlessly within reach for everyone! -CNCF Serverless Workflow is hosted by the [Cloud Native Computing Foundation (CNCF)](https://www.cncf.io/) and was approved as a -Cloud Native Sandbox level project on July 14, 2020. +Key features: -## Table of Contents +- **Easy to Use**: Designed for universal understanding, Serverless Workflow DSL enables users to quickly grasp workflow concepts and create complex workflows effortlessly. +- **Event Driven**: Seamlessly integrate events into workflows with support for various formats, including CloudEvents, allowing for event-driven workflow architectures. +- **Service Oriented**: The Serverless Workflow DSL empowers developers to seamlessly integrate with service-oriented architectures, allowing them to define workflows that interact with various services over standard application protocols like HTTP, GRPC, OpenAPI, AxsyncAPI, and more. +- **FaaS Centric**: Seamlessly invoke functions hosted on various platforms within workflows, promoting a function-as-a-service (FaaS) paradigm and enabling microservices architectures. +- **Timely**: Define timeouts for workflows and tasks to manage execution duration effectively. +- **Fault Tolerant**: Easily define error handling strategies to manage and recover from errors that may occur during workflow execution, ensuring robustness and reliability. +- **Schedulable**: Schedule workflows using CRON expressions or trigger them based on events, providing control over workflow execution timing. +- **Interoperable**: Integrates seamlessly with different services and resources. +- **Robust**: Offers features such as conditional branching, event handling, and looping constructs. +- **Scalable**: Promotes code reusability, maintainability, and scalability across different environments. -- [CNCF Landscape](#CNCF-Landscape) -- [Releases](#Releases) -- [Runtimes](#Runtimes) -- [SDKs](#SDKs) -- [Tooling](#Tooling) -- [Community](#Community) - - [Communication](#Communication) - - [Code of Conduct](#Code-of-Conduct) - - [Weekly Meetings](#Weekly-Meetings) -- [Repository Structure](#Repository-Structure) -- [Support](#Support) +## Ecosystem -## CNCF Landscape +Serverless Workflow ecosystem is hosted by the [Cloud Native Computing Foundation (CNCF)](https://www.cncf.io/) and was approved as a +Cloud Native Sandbox level project on July 14, 2020. -Serverless Workflow project falls under the [CNCF "App Definition and Development"](https://landscape.cncf.io/card-mode?category=app-definition-and-development&grouping=category) category. +It encompasses a comprehensive suite of components and tools designed to facilitate the creation, management, and execution of serverless workflows. -It is a member project of the [CNCF Serverless Working Group](https://github.com/cncf/wg-serverless). +1. **[DSL](dsl.md) (Domain Specific Language)**: The core of the ecosystem, defining the fundamental syntax and semantics of Serverless Workflow specifications. -
+2. **[TCK](/tck/readme.md) (Test Conformance Kit)**: A set of Gherkin features utilized for both conformance testing and Behavior Driven Design (BDD), ensuring compliance and facilitating testing across implementations. -Check out our project DevStats [here](https://serverlessworkflow.devstats.cncf.io). +3. **[SDKs](#sdks) (Software Development Kits)**: These enable developers to interact with serverless workflows in various programming languages, providing functionalities such as reading, writing, building, and validating workflows. -## Releases +4. **[Runtimes](#runtimes)**: Dedicated environments for executing workflows defined using the Serverless Workflow DSL, ensuring seamless deployment and operation within diverse runtime environments. -| | Latest release | Latest release branch | Working branch | -| --- | :---: | :---: | :---: | -| **Core Specification** | | -| [Serverless Workflow](https://github.com/serverlessworkflow/specification) | [v0.8](https://github.com/serverlessworkflow/specification/releases) | [0.8.x](https://github.com/serverlessworkflow/specification/tree/0.8.x) | [main](https://github.com/serverlessworkflow/specification) | -| **Additional Components** | | -| [Synapse](https://github.com/serverlessworkflow/synapse) | [0.1.0-alpha1](https://github.com/serverlessworkflow/synapse/releases) | | [main](https://github.com/serverlessworkflow/synapse) | -| [GO SDK](https://github.com/serverlessworkflow/sdk-go) | [v2.0.0](https://github.com/serverlessworkflow/sdk-go/releases) | [1.0.x](https://github.com/serverlessworkflow/sdk-go/tree/1.0.x) | [main](https://github.com/serverlessworkflow/sdk-go) | -| [Java SDK](https://github.com/serverlessworkflow/sdk-java) | [4.0.2.Final](https://github.com/serverlessworkflow/sdk-java/releases) | [4.0.x](https://github.com/serverlessworkflow/sdk-java/tree/4.0.x) | [main](https://github.com/serverlessworkflow/sdk-java) | -| [.NET SDK](https://github.com/serverlessworkflow/sdk-net) | [v0.7.4.4](https://github.com/serverlessworkflow/sdk-net/releases) | | [main](https://github.com/serverlessworkflow/sdk-net) | -| [TypeScript SDK](https://github.com/serverlessworkflow/sdk-typescript) | [v3.0.0](https://github.com/serverlessworkflow/sdk-typescript/releases) | [3.0.x](https://github.com/serverlessworkflow/sdk-typescript/tree/3.0.x) | [main](https://github.com/serverlessworkflow/sdk-typescript) | -| [Python SDK](https://github.com/serverlessworkflow/sdk-python) | [v1.0.0](https://github.com/serverlessworkflow/sdk-python/releases) | [1.0.x](https://github.com/serverlessworkflow/sdk-python/tree/1.0.x) | [main](https://github.com/serverlessworkflow/sdk-python) | -| [VSCode Extension](https://github.com/serverlessworkflow/vscode-extension) | [1.6.0](https://marketplace.visualstudio.com/items?itemName=serverlessworkflow.serverless-workflow-vscode-extension) | | [main](https://github.com/serverlessworkflow/vscode-extension) | +5. **[Tooling](#tooling)**: Additional utilities and resources tailored to enhance the development, debugging, and management of serverless workflows, streamlining the workflow lifecycle from creation to deployment and maintenance. -## Runtimes -- [Synapse](https://github.com/serverlessworkflow/synapse) +### SDKs -Serverless Workflow is open to host open-source runtime implementations that would like to -be part and grow alongside the core specification. +The Serverless Workflow SDKs are essential tools designed to assist developers in consuming, parsing, validating, and testing their workflows utilizing the Serverless Workflow DSL. -[Synapse](https://github.com/serverlessworkflow/synapse) is a Kubernetes-native workflow runtime which supports and is part of the Serverless -Workflow eco-system. +These SDKs empower developers to seamlessly integrate serverless workflows into their applications, providing robust support for various programming languages. By offering comprehensive functionality, they streamline the development process and enhance workflow management. -## SDKs +Explore our SDKs for different programming languages: - [Go](https://github.com/serverlessworkflow/sdk-go) - [Java](https://github.com/serverlessworkflow/sdk-java) - [.NET](https://github.com/serverlessworkflow/sdk-net) -- [TypeScript](https://github.com/serverlessworkflow/sdk-typescript) - [Python](https://github.com/serverlessworkflow/sdk-python) +- [TypeScript](https://github.com/serverlessworkflow/sdk-typescript) + +Don't see your favorite implementation on the list? Shout out to the community about it or, even better, contribute to the ecosystem with a new SDK! + +No matter your preferred language, our SDKs provide the tools you need to leverage the power of serverless workflows effectively. -Serverless Workflow encourages development of SDKs dedicated to help developers with -consuming, parsing, validating and testing their workflows that use the Serverless Workflow DSL. +### Runtimes -## Tooling +| Name | About | +| --- | --- | +| [Apache KIE SonataFlow](https://sonataflow.org) | Apache KIE SonataFlow is a tool for building cloud-native workflow applications. You can use it to do the services and events orchestration and choreography. | +| [Synapse](https://github.com/serverlessworkflow/synapse) | Synapse is a scalable, cross-platform, fully customizable platform for managing and running Serverless Workflows. | -In order to enhance developer experience with the specification, we also provide a [Visual Studio Code extension](https://marketplace.visualstudio.com/items?itemName=serverlessworkflow.serverless-workflow-vscode-extension). -The sources of the extension are found [here](https://github.com/serverlessworkflow/vscode-extension). +### Tooling -## Requirements +In order to enhance developer experience with the Serverless Workflow DSL, we provide a [Visual Studio Code extension](https://marketplace.visualstudio.com/items?itemName=serverlessworkflow.serverless-workflow-vscode-extension). -To generate the SVG diagram from the YAML or JSON file, you need to have the following tools installed: -- https://www.graphviz.org/download/source/ +The sources of the extension can be found [here](https://github.com/serverlessworkflow/vscode-extension). + +### CNCF Landscape + +Serverless Workflow project falls under the [CNCF "App Definition and Development"](https://landscape.cncf.io/card-mode?category=app-definition-and-development&grouping=category) category. + +It is a member project of the [CNCF Serverless Working Group](https://github.com/cncf/wg-serverless). + + + +## Documentation + +The documentation for Serverless Workflow includes: +- [**DSL**](dsl.md): Documents the fundamentals aspects and concepts of the Serverless Workflow DSL +- [**DSL Reference**](dsl-reference.md): References all the definitions used by the Serverless Workflow DSL ## Community @@ -98,10 +117,10 @@ workflow ecosystem. Community contributions are welcome and much needed to foste See [here](community/contributors.md) for the list of community members that have contributed to the specification. -To learn how to contribute to the specification reference the ['how to contribute'](contributing.md) doc. +To learn how to contribute to the specification please refer to ['how to contribute'](contributing.md). If you have any copyright questions when contributing to a CNCF project like this one, -reference the [Ownership of Copyrights in CNCF Project Contributions](https://github.com/cncf/foundation/blob/master/copyright-notices.md) doc. +reference the [Ownership of Copyrights in CNCF Project Contributions](https://github.com/cncf/foundation/blob/master/copyright-notices.md). ### Communication @@ -111,6 +130,18 @@ reference the [Ownership of Copyrights in CNCF Project Contributions](https://gi - Serverless WG Email: [cncf-wg-serverless](mailto:cncf-wg-serverless@lists.cncf.io) - Serverless WG Subscription: [https://lists.cncf.io/g/cncf-wg-serverless](https://lists.cncf.io/g/cncf-wg-serverless) +### Governance + +The Serverless Workflow Project Governance [document](governance.md) delineates the roles, procedures, and principles guiding the collaborative development and maintenance of the project. + +It emphasizes adherence to the CNCF Code of Conduct, defines the responsibilities of maintainers, reviewers, and emeritus maintainers, outlines procedures for their addition and removal, and establishes guidelines for subprojects' inclusion and compliance. + +Decision-making processes are consensus-driven, facilitated through structured proposal and discussion mechanisms, with conflict resolution procedures prioritizing amicable resolution. + +Overall, the document reflects the project's commitment to transparency, accountability, and inclusive collaboration, fostering an environment conducive to sustained growth and innovation. + +See the project's Governance Model [here](governance.md). + ### Code of Conduct As contributors and maintainers of this project, and in the interest of fostering @@ -123,7 +154,7 @@ everyone, regardless of level of experience, gender, gender identity and express sexual orientation, disability, personal appearance, body size, race, ethnicity, age, religion, or nationality. -See our full project Code of Conduct information [here](code-of-conduct.md). +See the project's Code of Conduct [here](code-of-conduct.md). ### Weekly Meetings @@ -133,36 +164,18 @@ To register for meetings please visit the [CNCF Community Calendar](https://tock You can register for individual meetings or for the entire series. -The meeting minutes can be accessed in [the discussions tab](https://github.com/serverlessworkflow/specification/discussions). - -[World Time Zone Converter](http://www.thetimezoneconverter.com/?t=9:00%20am&tz=San%20Francisco&). - -## Repository Structure - -Here is the outline of the repository to help navigate the specification -documents: - -| File/folder | Description | -| --- | --- | -| [specification.md](specification.md) | The main specification document | -| [OWNERS](OWNERS) | Defines the current specification maintainers and approvers | -| [LICENSE](LICENSE) | Specification License doc | -| [MAINTAINERS.md](MAINTAINERS.md) | Project Maintainers Info | -| [GOVERNANCE.md](GOVERNANCE.md) | Project Governance Info | -| [contributing.md](contributing.md) | Documentation on how to contribute to the spec | -| [code-of-conduct.md](code-of-conduct.md) | Defines the spec Code of Conduct | -| [usecases](usecases/README.md) | Specification Use Cases | -| [schema](schema) | Contains all specification JSON Schemas | -| [roadmap](roadmap/README.md) | Specification Roadmap | -| [references](references/README.md) | References used for specification docs | -| [media](media) | Includes all images used in spec docs | -| [extensions](extensions/README.md) | Information on spec extensions | -| [examples](examples) | Specification examples | -| [comparisons](comparisons) | Comparisons of Serverless Workflow with other workflow DSLs | -| [community](community) | Contains info on the spec community | -| [annualreviews](annualreviews) | Contains the project annual reviews presented to the CNCF TOC | -| [meetingminutes](meetingminutes) | Contains the projects community meeting minutes | - ## Support -Support our project by [becoming a Sponsor](https://crowdfunding.lfx.linuxfoundation.org/projects/serverless-workflow). +### Adoption + +If you're using Serverless Workflow in your projects and would like to showcase your adoption, become an Adopter! By joining our community of adopters, you'll have the opportunity to share your experiences, contribute feedback, and collaborate with like-minded individuals and organizations leveraging Serverless Workflow to power their workflows. + +### Sponsoring + +As an open-source project, Serverless Workflow relies on the support of sponsors to sustain its development and growth. + +By becoming a sponsor, you'll not only demonstrate your commitment to advancing serverless technologies but also gain visibility within our vibrant community. + +Sponsorship opportunities range from financial contributions to in-kind support, and every sponsorship makes a meaningful impact on the project's success and sustainability. + +Support our project by [becoming a Sponsor](https://crowdfunding.lfx.linuxfoundation.org/projects/serverless-workflow). \ No newline at end of file diff --git a/annualreviews/2021-ServerlessWorkflowSpecification-annual.md b/annualreviews/2021-ServerlessWorkflowSpecification-annual.md deleted file mode 100644 index ac100023..00000000 --- a/annualreviews/2021-ServerlessWorkflowSpecification-annual.md +++ /dev/null @@ -1,136 +0,0 @@ -# Serverless Workflow Specification 2021 Annual Review - -- [Background](#background) -- [Alignment with Cloud Native](#alignment-with-cloud-native) -- [Year in Review](#year-in-review) -- [Annual Review Contents](#annual-review-contents) -- [Project Links](#project-links) - -## Background - -Serverless Workflow is a vendor-neutral, open-source, and fully community-driven ecosystem -for defining and running DSL-based workflows that target the serverless technology domain. - -This project is composed of: - -* [Specification](https://github.com/serverlessworkflow/specification/blob/main/specification.md) for defining DSL-based workflows. -* [Developer SDKs](https://github.com/serverlessworkflow/specification#sdks) that provide support for many programming languages. -* [Workflow runtimes](https://github.com/serverlessworkflow/specification#runtime) part of the project ecosystem, and support the execution of specification DSL. -* [Developer tooling](https://github.com/serverlessworkflow/specification#Tooling) support for writing DSL-based workflows. - -Serverless Workflow was approved as a Cloud Native Sandbox level project on July 14, 2020. -* [TOC review PDF](https://github.com/serverlessworkflow/specification/blob/main/community/presentations/2020-4-15-toc-pres.pdf). -* [TOC sandbox proposal PR](https://github.com/cncf/toc/pull/376) - -## Alignment with Cloud Native - -Serverless Workflow project falls under the [CNCF "App Definition and Development"](https://landscape.cncf.io/card-mode?category=app-definition-and-development&grouping=category) category. - -Serverless Workflow is a member project of the [CNCF Serverless Working Group](https://github.com/cncf/wg-serverless). - -Serverless Workflow includes [Synapse](https://github.com/serverlessworkflow/synapse), a Kubernetes-native runtime engine for executing workflows that follows the -specification DSL definition. - -In addition, Serverless Workflow provides support for several other open-source projects and specifications in the cloud-native -space: -* CloudEvents -* OpenAPI -* AsyncAPI -* GraphQL -* OData -* OAuth2 - -## Year in Review - -This year was very exciting for the project. Some of the most notable accomplishments include: -* Released specification version [0.6](https://github.com/serverlessworkflow/specification/releases/tag/v0.6) -* Released specification version [0.7](https://github.com/serverlessworkflow/specification/releases/tag/v0.7) -* Released specification version [0.8](https://github.com/serverlessworkflow/specification/releases/tag/v0.8) -* Added [Synapse](https://github.com/serverlessworkflow/synapse), a Kubernetes-native runtime into our ecosystem -* Added [sdk-net](https://github.com/serverlessworkflow/sdk-net), a .NET SDK into our ecosystem -* Added [sdk-typescript](https://github.com/serverlessworkflow/sdk-typescript), a TypeScript SDK into our ecosystem -* Added [two new project maintainers](https://github.com/serverlessworkflow/specification/blob/main/MAINTAINERS.md) - -From the community perspective we also had a good year: -* Over 100 new followers on our [twitter channel](https://twitter.com/CNCFWorkflow). -* Over 200 new stars on our [specification github repo](https://github.com/serverlessworkflow/specification). -* Over 300 people attending our project office hours at 2021 KubeCon EU. -* Presented at KubeCon NA 2021, KubeCon EU 2021, KubeCon NA 2020, and KubeCon EU 2020 -* Mentioned as a key component of open-source microservices architectures at [InfoQ](https://www.infoq.com/articles/microservices-inside-out/) -* Over 40 unique visitors per day on our [website](https://serverlessworkflow.io/) -* Participated at the KubeCon EU 2021 BugBash - -## Annual Review Contents - -- **Include a link to your project’s devstats page. We will be looking for signs of consistent or increasing contribution activity.** - -Project [DevStats page](https://serverlessworkflow.devstats.cncf.io). -The info for the span of one year shows: -* Over 500 merged PRs -* ~2000 commits by 30+ contributors -* Community contributions from 10+ different companies -* Over 100% increase to github stars compared to last year - -- **How many maintainers do you have, and which organizations are they from?** - -Serverless Workflow currently has [5 project maintainers](https://github.com/serverlessworkflow/specification/blob/main/MAINTAINERS.md) - - Tihomir Surdilovic, Temporal Technologies - - Manuel Stein, Nokia Bell Labs - - Ricardo Zanini, Red Hat - - Charles d'Avernas , Neuroglia - - Antonio Mendoza Pérez, Independent - - -- **What do you know about adoption, and how has this changed since your last review / since you joined Sandbox?** - -Both the adoption and community interest has been steadily increasing over the course of the year. -Most notable adoptions have been by: - - [Kogito](https://kogito.kie.org/), a Red Hat project automation runtime - - [Automatiko](https://automatiko.io/), a workflow automation runtime - - [Synapse](https://github.com/serverlessworkflow/synapse), a Kubernetes-based workflow runtime which has joined the Serverless Workflow ecosystem - -We also have a number of integrations that are currently work-in-progress which include -integrations with [Temporal](https://temporal.io/). - -- **How has the project performed against its goals since the last review?** - -This is our first annual review since becoming a Sandbox project. -We have surpassed all of our goals that we have set for this year. -We have been able to not only surpass the goals of the main specifications, -but also to go from just hosting a specification to creating -an entire workflow ecosystem around it. This includes SDKs, Tooling (VSCode, IntelliJ), -rumtimes (Synapse), etc. - -Over this year we were able to create a workflow DSL which is in our opinion -at this time the most feature-rich and most powerful workflow DSL that exists. - -- **What are the current goals of the project?** - -Specification roadmap: https://github.com/serverlessworkflow/specification/tree/main/roadmap - -Our main goals for the project include: - - Release specification version 1.0 by April of 2022 - - Add more SDKs in different languages - - Create a specification TCK - - Add integrations with different workflow DSLs - - Improve our community tooling support - - Add at least 2 more integrations with existing workflow runtimes by middle of 2022 - - -- **How can the CNCF help you achieve your upcoming goals?** - - Help us in promoting the project (Blogs, Twitter, KubeCon, etc) - - If feasible help our project via crowdfunding [here](https://crowdfunding.lfx.linuxfoundation.org/projects/serverless-workflow) - - Keep providing us with opportunities to have project office hours and talks at KubeCons - - -- **Do you think that your project meets the criteria for incubation?** - -We believe we have made significant progress toward this goal and that the project is ready for incubation. -We would like however to release Serverless Workflow specification v1.0 before starting this process -(scheduled for April 2022). - -## Project Links -* [Website](https://serverlessworkflow.io/) -* [GitHub](https://github.com/serverlessworkflow) -* Slack:[CNCF](http://slack.cncf.io) / #serverless-workflow -* [Twitter](https://twitter.com/CNCFWorkflow) \ No newline at end of file diff --git a/annualreviews/2022-ServerlessWorkflowSpecification-annual.md b/annualreviews/2022-ServerlessWorkflowSpecification-annual.md deleted file mode 100644 index 9e802e32..00000000 --- a/annualreviews/2022-ServerlessWorkflowSpecification-annual.md +++ /dev/null @@ -1,157 +0,0 @@ -# Serverless Workflow Specification 2022 Annual Review - -- [Background](#background) -- [Alignment with Cloud Native](#alignment-with-cloud-native) -- [Year in Review](#year-in-review) -- [Annual Review Contents](#annual-review-contents) -- [Project Links](#project-links) - -## Background - -Serverless Workflow is a vendor-neutral, open-source, and fully community-driven ecosystem -for defining and running DSL-based workflows that target the serverless technology domain. - -This project is composed of: - -* [Specification](https://github.com/serverlessworkflow/specification/blob/main/specification.md) for defining DSL-based workflows. -* [Developer SDKs](https://github.com/serverlessworkflow/specification#sdks) that provide support for many programming languages. -* [Workflow runtimes](https://github.com/serverlessworkflow/specification#runtime) part of the project ecosystem, and support the execution of specification DSL. -* [Developer tooling](https://github.com/serverlessworkflow/specification#Tooling) support for writing DSL-based workflows. - -Serverless Workflow was approved as a Cloud Native Sandbox level project on July 14, 2020. - -* [TOC review PDF](https://github.com/serverlessworkflow/specification/blob/main/community/presentations/2020-4-15-toc-pres.pdf). -* [TOC sandbox proposal PR](https://github.com/cncf/toc/pull/376) - -## Alignment with Cloud Native - -Serverless Workflow project falls under the [CNCF "App Definition and Development"](https://landscape.cncf.io/card-mode?category=app-definition-and-development&grouping=category) category. - -Serverless Workflow is a member project of the [CNCF Serverless Working Group](https://github.com/cncf/wg-serverless). - -Serverless Workflow includes [Synapse](https://github.com/serverlessworkflow/synapse), a Kubernetes-native runtime engine for executing workflows that follows the specification DSL definition. - -In addition, Serverless Workflow provides support for several other open-source projects and specifications in the cloud-native -space: - -* CloudEvents -* OpenAPI -* AsyncAPI -* GraphQL -* OData -* OAuth2 - -## Year in Review - -This year was for stabilization and discussions around the upcoming 1.0 release. The ecosystem kept growing: - -* Two new releases for the [sdk-java](https://github.com/serverlessworkflow/sdk-java/releases) -* One new release for the [sdk-typescript](https://github.com/serverlessworkflow/sdk-typescript/releases) -* Four new releses for the [sdk-go](https://github.com/serverlessworkflow/sdk-typescript/releases) -* Seven minor releases for the [sdk-net](https://github.com/serverlessworkflow/sdk-net/tags) -* Four minor releases for the [Synapse](https://github.com/serverlessworkflow/synapse/releases) runtime -* Added [sdk-python](https://github.com/serverlessworkflow/sdk-python), a Python SDK into our ecosystem - -From the community perspective we also had a good year: - - -* Over 210 new stars on our [specification github repo](https://github.com/serverlessworkflow/specification). -* Over 200 people attending our project kiosk at KubeCon NA 2022 -* Presented in-person at KubeCon EU 2022 for more than 100 people. -* Over 200 people attending our project office hours at 2022 KubeCon EU. -* Over 50 people attending our project office hours at 2022 KubeCon China. - -## Annual Review Contents - -- **Include a link to your project’s devstats page. We will be looking for signs of consistent or increasing contribution activity.** - -Project [DevStats page](https://serverlessworkflow.devstats.cncf.io). -The info for the span of one year shows: - -* Over 50 new forks -* Over 80% increase to Synapse (runtime implementation) github stars compared to last year -* Over 101% increase to github stars compared to last year - -- **How many maintainers do you have, and which organizations are they from?** - -Serverless Workflow currently has [3 project maintainers](https://github.com/serverlessworkflow/specification/blob/main/MAINTAINERS.md) - - -- Charles d'Avernas, Neuroglia -- Ricardo Zanini, Red Hat -- Tihomir Surdilovic, Temporal Technologies - -- **What do you know about adoption, and how has this changed since your last review / since you joined Sandbox?** - -Both the adoption and community interest has been steadily increasing over the course of the year. -Most notable adoptions have been by: - - -- [Apache EventMesh](https://eventmesh.apache.org/), a new generation serverless event middleware for building distributed event-driven applications -- [Automatiko](https://automatiko.io/), a workflow automation runtime -- [FaasNet](https://github.com/simpleidserver/FaasNet), FaasNet makes it easy to deploy functions and API to Kubernetes without repetitive, boiler-plate coding. -- [OpenShift Serverless Logic](https://developers.redhat.com/articles/2022/08/15/how-openshift-serverless-logic-evolved-improve-workflows), a Red Hat product under Tech Preview integrated with their flagship product, OpenShift -- [Synapse](https://github.com/serverlessworkflow/synapse), a Kubernetes-based workflow runtime which has joined the Serverless Workflow ecosystem - -These are the companies that have adopeted the Serverless Workflow Specification: - - -- [CAF](https://caf.io), Serverless Workflow is the core technology behind every KYC/KYB solution allowing them to customize it for their clients seamlessly. -- [Huawei](https://www.huaweicloud.com/intl/en-us/product/functiongraph.html), Huawei FunctionGraph hosts event-driven functions in a serverless context while ensuring high availability, high scalability, and zero maintenance. -- [IBM](https://www.ibm.com/) As active members of the open-source KIE community, the BAMOE team from IBM's Digital Business Automation division is highly committed to standards within the business automation domain, and CNCF Serverless Workflow is no different. Eventually, IBM plans to incorporate the CNCF Serverless Workflow format into its product offering, providing more choices to customers to take advantage of BAMOE workflow capabilities. -- [Neuroglia](https://neuroglia.io/), Neuroglia is a consultancy and solution design company for the digital transformation of companies and their services. -- [OpenEnterprise](https://automatiko.io/), OpenEnterprise Automatiko helps you build better services and functions based on workflows expressed with well known standards. -- [Red Hat](https://redhat.com/), Red Hat sponsors the development of Kogito Serverless Workflow, which is a tool for building cloud-native workflow applications. -- [Tantl](https://www.tantl.com/), Tantl is making it easy for developers to build internal workflows, such as allowing customer support reps to quickly process refunds. -- [Temporal](https://temporal.io/), Temporal is the open source microservice orchestration platform for writing durable workflows as code. - -There are a few other companies that are in touch with us and using the specification, but can't disclose at the moment. - -- **How has the project performed against its goals since the last review?** - -This is our second annual review since becoming a Sandbox project. -This year we looked for stabilization of the specification by having more -discussions with current implementation project leaders to achieve a good -balance between the standards and production-level use cases. - -We decided to grow slowly and now we are reaching to a point to fix most of -the open issues and discussions before releasing 0.9 version, and then the final -1.0 by the end of the year. - -One of the goals we achieved, was to set a new governance model to balance the -responsibilities amongst all the maintainers. - -We had to do a little detour duo to the progress of projects implementing and using -the specification in many production-level use cases. - -- **What are the current goals of the project?** - -Specification [roadmap](https://github.com/serverlessworkflow/specification/tree/main/roadmap) and [progress tracker](https://github.com/orgs/serverlessworkflow/projects/1/views/2). - -Our main goals for the project include: - -- Release specification version 1.0 by late 2023 -- Create a specification TCK -- Add integrations with different workflow DSLs -- Improve our community tooling support - -- **How can the CNCF help you achieve your upcoming goals?** - -- Help us in promoting the project (Blogs, Twitter, KubeCon, etc) -- If feasible help our project via crowdfunding [here](https://crowdfunding.lfx.linuxfoundation.org/projects/serverless-workflow) -- Keep providing us with opportunities to have project office hours and talks at KubeCons - -- **Do you think that your project meets the criteria for incubation?** - -We believe we have made significant progress toward this goal and that the project is ready for incubation. -We would like however to release Serverless Workflow specification v1.0 before starting this process -(scheduled for late 2023). - -## Project Links - -* [Website](https://serverlessworkflow.io/) -* [GitHub](https://github.com/serverlessworkflow) -* Slack:[CNCF](http://slack.cncf.io) / #serverless-workflow -* [Twitter](https://twitter.com/CNCFWorkflow) diff --git a/community/contributors.md b/community/contributors.md index d3aa8ff5..b05beb99 100644 --- a/community/contributors.md +++ b/community/contributors.md @@ -13,7 +13,6 @@ us know in chat or team meeting. * **Independent** * Louis Fourie * Achilleas Tzenetopoulos - * Antonio Mendoza Pérez * Richard Gibson * Lucas Stocksmeier @@ -38,6 +37,7 @@ us know in chat or team meeting. * Chathura Ekanayake * **Temporal Technologies** + * Antonio Mendoza Pérez * Tihomir Surdilovic * **Red Hat** @@ -82,5 +82,4 @@ us know in chat or team meeting. * Manickavasagam Sundaram * **OpenEnterprise** - * Maciek Swiderski - + * Maciek Swiderski \ No newline at end of file diff --git a/comparisons/README.md b/comparisons/README.md deleted file mode 100644 index 4c067f4b..00000000 --- a/comparisons/README.md +++ /dev/null @@ -1,9 +0,0 @@ -# Comparisons with other workflow languages - -Following comparison documents are available: - -* [Argo comparison examples](comparison-argo.md) -* [Brigade comparison examples](comparison-brigade.md) -* [Google Cloud Workflow comparison examples](comparison-google-cloud-workflows.md) -* [Temporal comparison examples](comparison-temporal.md) -* [BPMN2 comparison examples](comparison-bpmn.md) diff --git a/comparisons/comparison-argo.md b/comparisons/comparison-argo.md deleted file mode 100644 index 9898f5d7..00000000 --- a/comparisons/comparison-argo.md +++ /dev/null @@ -1,928 +0,0 @@ -# Comparisons - Argo Workflows - -[Argo Workflows](https://github.com/argoproj/argo) is an open source container-native workflow engine for -orchestrating parallel jobs on Kubernetes. -The Argo markup is YAML based and workflows are implemented as a Kubernetes CRD (Custom Resource Definition). -Argo is also a [CNCF](https://www.cncf.io/) Incubating project. - -Argo has a number of [examples](https://github.com/argoproj/argo-workflows/tree/master/examples) which display -different Argo templates. - -The purpose of this document is to show side-by-side the Argo markup and the equivalent markup of the -Serverless Workflow Specification. This can hopefully help compare and contrast the two markups and -give a better understanding of both. - -## Preface - -Argo YAML is defined inside a Kubernetes CRD (Custom Resource Definition). The resource definition contains a "spec" -parameter which contains the entrypoint of the workflow and the template parameter which defines one or more -workflow definitions. When comparing the examples below please note that the Serverless Workflow specification YAML -pertains to the content of the "spec" parameter. Other parameters in the Kubernetes CRD are not considered and can -remain the same. Note that the Serverless Workflow YAML could also be embedded inside the CRD. - -For the sake of comparing the two models, we use the YAML representation as well for the -Serverless Workflow specification part. - -## Table of Contents - -- [Hello World with Parameters](#Hello-World-With-Parameters) -- [Multi Step Workflow](#Multi-Step-Workflow) -- [Directed Acyclic Graph](#Directed-Acyclic-Graph) -- [Scripts and Results](#Scripts-And-Results) -- [Loops](#Loops) -- [Conditionals](#Conditionals) -- [Retrying Failed Steps](#Retrying-Failed-Steps) -- [Recursion](#Recursion) -- [Exit Handlers](#Exit-Handlers) - -### Hello World With Parameters - -[Argo Example](https://github.com/argoproj/argo-workflows/tree/master/examples#parameters) - -Argo | -Serverless Workflow | -
---|---|
- -```yaml -apiVersion: argoproj.io/v1alpha1 -kind: Workflow -metadata: - generateName: hello-world-parameters- -spec: - entrypoint: whalesay - arguments: - parameters: - - name: message - value: hello world - - templates: - - name: whalesay - inputs: - parameters: - - name: message - container: - image: docker/whalesay - command: [cowsay] - args: ["{{inputs.parameters.message}}"] -``` - - | -- -```yaml -id: hello-world-parameters -name: Hello World with parameters -version: '1.0.0' -specVersion: '0.8' -start: whalesay -functions: -- name: whalesayimage - metadata: - image: docker/whalesay - command: cowsay -states: -- name: whalesay - type: operation - actions: - - functionRef: - refName: whalesayimage - arguments: - message: "${ .message }" - end: true -``` - - | -
Argo | -Serverless Workflow | -
---|---|
- -```yaml -apiVersion: argoproj.io/v1alpha1 -kind: Workflow -metadata: - generateName: steps- -spec: - entrypoint: hello-hello-hello - templates: - - name: hello-hello-hello - steps: - - - name: hello1 # hello1 is run before the following steps - template: whalesay - arguments: - parameters: - - name: message - value: "hello1" - - - name: hello2a # double dash => run after previous step - template: whalesay - arguments: - parameters: - - name: message - value: "hello2a" - - name: hello2b # single dash => run in parallel with previous step - template: whalesay - arguments: - parameters: - - name: message - value: "hello2b" - - name: whalesay - inputs: - parameters: - - name: message - container: - image: docker/whalesay - command: [cowsay] - args: ["{{inputs.parameters.message}}"] -``` - - | -- -```yaml -id: hello-hello-hello -name: Multi Step Hello -version: '1.0.0' -specVersion: '0.8' -start: hello1 -functions: -- name: whalesayimage - metadata: - image: docker/whalesay - command: cowsay -states: -- name: hello1 - type: operation - actions: - - functionRef: - refName: whalesayimage - arguments: - message: hello1 - transition: parallelhello -- name: parallelhello - type: parallel - completionType: allOf - branches: - - name: hello2a-branch - actions: - - functionRef: - refName: whalesayimage - arguments: - message: hello2a - - name: hello2b-branch - actions: - - functionRef: - refName: whalesayimage - arguments: - message: hello2b - end: true -``` - - | -
Argo | -Serverless Workflow | -
---|---|
- -```yaml -apiVersion: argoproj.io/v1alpha1 -kind: Workflow -metadata: - generateName: dag-diamond- -spec: - entrypoint: diamond - templates: - - name: echo - inputs: - parameters: - - name: message - container: - image: alpine:3.7 - command: [echo, "{{inputs.parameters.message}}"] - - name: diamond - dag: - tasks: - - name: A - template: echo - arguments: - parameters: [{name: message, value: A}] - - name: B - dependencies: [A] - template: echo - arguments: - parameters: [{name: message, value: B}] - - name: C - dependencies: [A] - template: echo - arguments: - parameters: [{name: message, value: C}] - - name: D - dependencies: [B, C] - template: echo - arguments: - parameters: [{name: message, value: D}] -``` - - | -- -```yaml -id: dag-diamond- -name: DAG Diamond Example -version: '1.0.0' -specVersion: '0.8' -start: A -functions: -- name: echo - metadata: - image: alpine:3.7 - command: '[echo, "{{inputs.parameters.message}}"]' -states: -- name: A - type: operation - actions: - - functionRef: - refName: echo - arguments: - message: A - transition: parallelecho -- name: parallelecho - type: parallel - completionType: allOf - branches: - - name: B-branch - actions: - - functionRef: - refName: echo - arguments: - message: B - - name: C-branch - actions: - - functionRef: - refName: echo - arguments: - message: C - transition: D -- name: D - type: operation - actions: - - functionRef: - refName: echo - arguments: - message: D - end: true -``` - - | -
Argo | -Serverless Workflow | -
---|---|
- -```yaml -apiVersion: argoproj.io/v1alpha1 -kind: Workflow -metadata: - generateName: scripts-bash- -spec: - entrypoint: bash-script-example - templates: - - name: bash-script-example - steps: - - - name: generate - template: gen-random-int-bash - - - name: print - template: print-message - arguments: - parameters: - - name: message - value: "{{steps.generate.outputs.result}}" # The result of the here-script - - - name: gen-random-int-bash - script: - image: debian:9.4 - command: [bash] - source: | # Contents of the here-script - cat /dev/urandom | od -N2 -An -i | awk -v f=1 -v r=100 '{printf "%i\n", f + r * $1 / 65536}' - - - name: gen-random-int-python - script: - image: python:alpine3.6 - command: [python] - source: | - import random - i = random.randint(1, 100) - print(i) - - - name: gen-random-int-javascript - script: - image: node:9.1-alpine - command: [node] - source: | - var rand = Math.floor(Math.random() * 100); - console.log(rand); - - - name: print-message - inputs: - parameters: - - name: message - container: - image: alpine:latest - command: [sh, -c] - args: ["echo result was: {{inputs.parameters.message}}"] -``` - - | -- -```yaml -id: scripts-bash- -name: Scripts and Results Example -version: '1.0.0' -specVersion: '0.8' -start: generate -functions: -- name: gen-random-int-bash - metadata: - image: debian:9.4 - command: bash - source: |- - cat /dev/urandom | od -N2 -An -i | awk -v f=1 -v r=100 '{printf "%i - ", f + r * $1 / 65536}' -- name: gen-random-int-python - metadata: - image: python:alpine3.6 - command: python - source: "import random \ni = random.randint(1, 100) \nprint(i)\n" -- name: gen-random-int-javascript - metadata: - image: node:9.1-alpine - command: node - source: "var rand = Math.floor(Math.random() * 100); \nconsole.log(rand);\n" -- name: printmessagefunc - metadata: - image: alpine:latest - command: sh, -c - source: 'echo result was: ${ .inputs.parameters.message }' -states: -- name: generate - type: operation - actions: - - functionRef: gen-random-int-bash - actionDataFilter: - results: "${ .results }" - transition: print-message -- name: print-message - type: operation - actions: - - functionRef: - refName: printmessagefunc - arguments: - message: "${ .results }" - end: true -``` - - | -
Argo | -Serverless Workflow | -
---|---|
- -```yaml -apiVersion: argoproj.io/v1alpha1 -kind: Workflow -metadata: - generateName: loops- -spec: - entrypoint: loop-example - templates: - - name: loop-example - steps: - - - name: print-message - template: whalesay - arguments: - parameters: - - name: message - value: "{{item}}" - withItems: # invoke whalesay once for each item in parallel - - hello world # item 1 - - goodbye world # item 2 - - - name: whalesay - inputs: - parameters: - - name: message - container: - image: docker/whalesay:latest - command: [cowsay] - args: ["{{inputs.parameters.message}}"] -``` - - | -- -```yaml -id: loops- -name: Loop over data example -version: '1.0.0' -specVersion: '0.8' -start: injectdata -functions: -- name: whalesay - metadata: - image: docker/whalesay:latest - command: cowsay -states: -- name: injectdata - type: inject - data: - greetings: - - hello world - - goodbye world - transition: printgreetings -- name: printgreetings - type: foreach - inputCollection: "${ .greetings }" - iterationParam: greeting - actions: - - name: print-message - functionRef: - refName: whalesay - arguments: - message: "${ .greeting }" - end: true -``` - - | -
Argo | -Serverless Workflow | -
---|---|
- -```yaml -apiVersion: argoproj.io/v1alpha1 -kind: Workflow -metadata: - generateName: coinflip- -spec: - entrypoint: coinflip - templates: - - name: coinflip - steps: - # flip a coin - - - name: flip-coin - template: flip-coin - # evaluate the result in parallel - - - name: heads - template: heads # call heads template if "heads" - when: "{{steps.flip-coin.outputs.result}} == heads" - - name: tails - template: tails # call tails template if "tails" - when: "{{steps.flip-coin.outputs.result}} == tails" - - # Return heads or tails based on a random number - - name: flip-coin - script: - image: python:alpine3.6 - command: [python] - source: | - import random - result = "heads" if random.randint(0,1) == 0 else "tails" - print(result) - - - name: heads - container: - image: alpine:3.6 - command: [sh, -c] - args: ["echo \"it was heads\""] - - - name: tails - container: - image: alpine:3.6 - command: [sh, -c] - args: ["echo \"it was tails\""] -``` - - | -- -```yaml -id: coinflip- -name: Conditionals Example -version: '1.0.0' -specVersion: '0.8' -start: flip-coin -functions: -- name: flip-coin-function - metadata: - image: python:alpine3.6 - command: python - source: import random result = "heads" if random.randint(0,1) == 0 else "tails" - print(result) -- name: echo - metadata: - image: alpine:3.6 - command: sh, -c -states: -- name: flip-coin - type: operation - actions: - - functionRef: flip-coin-function - actionDataFilter: - results: "${ .flip.result }" - transition: show-flip-results -- name: show-flip-results - type: switch - dataConditions: - - condition: "${ .flip | .result == \"heads\" }" - transition: show-results-heads - - condition: "${ .flip | .result == \"tails\" }" - transition: show-results-tails -- name: show-results-heads - type: operation - actions: - - functionRef: echo - actionDataFilter: - results: it was heads - end: true -- name: show-results-tails - type: operation - actions: - - functionRef: echo - actionDataFilter: - results: it was tails - end: true -``` - - | -
Argo | -Serverless Workflow | -
---|---|
- -```yaml -apiVersion: argoproj.io/v1alpha1 -kind: Workflow -metadata: - generateName: retry-backoff- -spec: - entrypoint: retry-backoff - templates: - - name: retry-backoff - retryStrategy: - limit: 10 - retryPolicy: "Always" - backoff: - duration: "1" # Must be a string. Default unit is seconds. Could also be a Duration, e.g.: "2m", "6h", "1d" - factor: 2 - maxDuration: "1m" # Must be a string. Default unit is seconds. Could also be a Duration, e.g.: "2m", "6h", "1d" - affinity: - nodeAntiAffinity: {} - container: - image: python:alpine3.6 - command: ["python", -c] - # fail with a 66% probability - args: ["import random; import sys; exit_code = random.choice([0, 1, 1]); sys.exit(exit_code)"] -``` - - | -- -```yaml -id: retry-backoff- -name: Retry Example -version: '1.0.0' -specVersion: '0.8' -start: retry-backoff -functions: -- name: fail-function - metadata: - image: python:alpine3.6 - command: python -retries: -- name: All workflow errors retry strategy - maxAttempts: 10 - delay: PT1S - maxDelay: PT1M - multiplier: 2 -states: -- name: retry-backoff - type: operation - actions: - - functionRef: - refName: flip-coin-function - arguments: - args: - - import random; import sys; exit_code = random.choice([0, 1, 1]); sys.exit(exit_code) - end: true -``` - - | -
Argo | -Serverless Workflow | -
---|---|
- -```yaml -apiVersion: argoproj.io/v1alpha1 -kind: Workflow -metadata: - generateName: coinflip-recursive- -spec: - entrypoint: coinflip - templates: - - name: coinflip - steps: - # flip a coin - - - name: flip-coin - template: flip-coin - # evaluate the result in parallel - - - name: heads - template: heads # call heads template if "heads" - when: "{{steps.flip-coin.outputs.result}} == heads" - - name: tails # keep flipping coins if "tails" - template: coinflip - when: "{{steps.flip-coin.outputs.result}} == tails" - - - name: flip-coin - script: - image: python:alpine3.6 - command: [python] - source: | - import random - result = "heads" if random.randint(0,1) == 0 else "tails" - print(result) - - - name: heads - container: - image: alpine:3.6 - command: [sh, -c] - args: ["echo \"it was heads\""] -``` - - | -- -```yaml -id: coinflip-recursive- -name: Recursion Example -version: '1.0.0' -specVersion: '0.8' -start: flip-coin-state -functions: -- name: heads-function - metadata: - image: alpine:3.6 - command: echo "it was heads" -- name: flip-coin-function - metadata: - image: python:alpine3.6 - command: python - source: import random result = "heads" if random.randint(0,1) == 0 else "tail" print(result) -states: -- name: flip-coin-state - type: operation - actions: - - functionRef: flip-coin-function - actionDataFilter: - results: "${ .steps.flip-coin.outputs.result }" - transition: flip-coin-check -- name: flip-coin-check - type: switch - dataConditions: - - condition: "${ .steps.flip-coin.outputs | .result == \"tails\" }" - transition: flip-coin-state - - condition: "${ .steps.flip-coin.outputs | .result == \"heads\" }" - transition: heads-state -- name: heads-state - type: operation - actions: - - functionRef: - refName: heads-function - arguments: - args: echo "it was heads" - end: true -``` - - | -
Argo | -Serverless Workflow | -
---|---|
- -```yaml -apiVersion: argoproj.io/v1alpha1 -kind: Workflow -metadata: - generateName: exit-handlers- -spec: - entrypoint: intentional-fail - onExit: exit-handler # invoke exit-handler template at end of the workflow - templates: - # primary workflow template - - name: intentional-fail - container: - image: alpine:latest - command: [sh, -c] - args: ["echo intentional failure; exit 1"] - - name: exit-handler - steps: - - - name: notify - template: send-email - - name: celebrate - template: celebrate - when: "{{workflow.status}} == Succeeded" - - name: cry - template: cry - when: "{{workflow.status}} != Succeeded" - - name: send-email - container: - image: alpine:latest - command: [sh, -c] - args: ["echo send e-mail: {{workflow.name}} {{workflow.status}}"] - - name: celebrate - container: - image: alpine:latest - command: [sh, -c] - args: ["echo hooray!"] - - name: cry - container: - image: alpine:latest - command: [sh, -c] - args: ["echo boohoo!"] -``` - - | -- -```yaml -id: exit-handlers- -name: Exit/Error Handling Example -version: '1.0.0' -specVersion: '0.8' -autoRetries: true -start: intentional-fail-state -functions: - - name: intentional-fail-function - metadata: - image: alpine:latest - command: "[sh, -c]" - - name: send-email-function - metadata: - image: alpine:latest - command: "[sh, -c]" - - name: celebrate-cry-function - metadata: - image: alpine:latest - command: "[sh, -c]" -errors: - - name: IntentionalError - code: '404' -states: - - name: intentional-fail-state - type: operation - actions: - - functionRef: - refName: intentional-fail-function - arguments: - args: echo intentional failure; exit 1 - nonRetryableErrors: - - IntentionalError - onErrors: - - errorRef: IntentionalError - transition: send-email-state - end: true - - name: send-email-state - type: operation - actions: - - functionRef: - refName: send-email-function - arguments: - args: 'echo send e-mail: ${ .workflow.name } ${ .workflow.status }' - transition: emo-state - - name: emo-state - type: switch - dataConditions: - - condition: ${ .workflow| .status == "Succeeded" } - transition: celebrate-state - - condition: ${ .workflow| .status != "Succeeded" } - transition: cry-state - - name: celebrate-state - type: operation - actions: - - functionRef: - refName: celebrate-cry-function - arguments: - args: echo hooray! - end: true - - name: cry-state - type: operation - actions: - - functionRef: - refName: celebrate-cry-function - arguments: - args: echo boohoo! - end: true -``` - - | -
BPMN2 Diagram | -Serverless Workflow | -
---|---|
- - - - |
-- -```yaml -id: processfile -name: Process File Workflow -version: '1.0.0' -specVersion: '0.8' -start: Process File -states: -- name: Process File - type: operation - actions: - - functionRef: processFile - end: true -functions: -- name: processFile - operation: file://myservice.json#process -``` - - | -
BPMN2 Diagram | -Serverless Workflow | -
---|---|
- - - - |
-- -```yaml -id: processapplication -name: Process Application -version: '1.0.0' -specVersion: '0.8' -start: ProcessNewApplication -states: -- name: ProcessNewApplication - type: event - onEvents: - - eventRefs: - - ApplicationReceivedEvent - actions: - - functionRef: processApplicationFunction - - functionRef: acceptApplicantFunction - - functionRef: depositFeesFunction - end: - produceEvents: - - eventRef: NotifyApplicantEvent -functions: -- name: processApplicationFunction - operation: file://myservice.json#process -- name: acceptApplicantFunction - operation: file://myservice.json#accept -- name: depositFeesFunction - operation: file://myservice.json#deposit -events: -- name: ApplicationReceivedEvent - type: application - source: "/applications/new" -- name: NotifyApplicantEvent - type: notifications - source: "/applicants/notify" -``` - - | -
BPMN2 Diagram | -Serverless Workflow | -
---|---|
- - - - |
-- -```yaml -id: simplecompensation -name: Simple Compensation -version: '1.0.0' -specVersion: '0.8' -start: Step 1 -states: -- name: Step 1 - type: operation - actions: - - functionRef: step1function - compensatedBy: Cancel Step 1 - transition: Step 2 -- name: Step 2 - type: operation - actions: - - functionRef: step2function - transition: OK? -- name: OK? - type: switch - dataConditions: - - name: 'yes' - condition: ${ .outcome | .ok == "yes" } - end: true - - name: 'no' - condition: ${ .outcome | .ok == "no" } - end: - compensate: true -- name: Cancel Step 1 - type: operation - usedForCompensation: true - actions: - - functionRef: undostep1 -functions: -- name: step1function - operation: file://myservice.json#step1 -- name: step2function - operation: file://myservice.json#step2 -- name: undostep1function - operation: file://myservice.json#undostep1 -``` - - | -
BPMN2 Diagram | -Serverless Workflow | -
---|---|
- - - - |
-- -```yaml ---- -id: errorwithretries -name: Error Handling With Retries Workflow -version: '1.0.0' -specVersion: '0.8' -start: Make Coffee -states: - - name: Make Coffee - type: operation - actions: - - functionRef: makeCoffee - transition: Add Milk - - name: Add Milk - type: operation - actions: - - functionRef: addMilk - retryRef: noMilkRetries - retryableErrors: - - D'oh! No more Milk! - onErrors: - - errorRef: D'oh! No more Milk! - end: true - transition: Drink Coffee - - name: Drink Coffee - type: operation - actions: - - functionRef: drinkCoffee - end: true -retries: - - name: noMilkRetries - delay: PT1M - maxAttempts: 10 -errors: - - name: D'oh! No more Milk! - code: '123' -functions: - - name: makeCoffee - operation: file://myservice.json#make - - name: addMilk - operation: file://myservice.json#add - - name: drinkCoffee - operation: file://myservice.json#drink - -``` - - | -
BPMN2 Diagram | -Serverless Workflow | -
---|---|
- - - - |
-- -```yaml -id: executiontimeout -name: Execution Timeout Workflow -version: '1.0.0' -specVersion: '0.8' -start: Purchase Parts -timeouts: - workflowExecTimeout: - duration: PT7D - interrupt: true - runBefore: Handle timeout -states: -- name: Purchase Parts - type: operation - actions: - - functionRef: purchasePartsFunction - transition: Unpack Parts -- name: Unpack Parts - type: operation - actions: - - functionRef: unpackPartsFunction - end: true -- name: Handle timeout - type: operation - actions: - - functionRef: handleTimeoutFunction -functions: -- name: purchasePartsFunction - operation: file://myservice.json#purchase -- name: unpackPartsFunction - operation: file://myservice.json#unpack -- name: handleTimeoutFunction - operation: file://myservice.json#handle -``` - - | -
BPMN2 Diagram | -Serverless Workflow | -
---|---|
- - - - |
-- -```yaml -id: foreachWorkflow -name: ForEach State Workflow -version: '1.0.0' -specVersion: '0.8' -start: ForEachItem -states: -- name: ForEachItem - type: foreach - inputCollection: "${ .inputsArray }" - iterationParam: "${ .inputItem }" - outputCollection: "${ .outputsArray }" - actions: - - subFlowRef: doSomethingAndWaitForMessage - end: true -``` - - | -
BPMN2 Diagram | -Serverless Workflow | -
---|---|
- - - - |
-- -```yaml -id: subflowloop -name: SubFlow Loop Workflow -version: '1.0.0' -specVersion: '0.8' -start: SubflowRepeat -states: -- name: SubflowRepeat - type: operation - actions: - - functionRef: checkAndReplyToEmail - actionDataFilter: - fromStateData: ${ .someInput } - toStateData: ${ .someInput } - stateDataFilter: - output: ${ .maxChecks -= 1 } - transition: CheckCount -- name: CheckCount - type: switch - dataConditions: - - condition: ${ .maxChecks > 0 } - transition: SubflowRepeat - defaultCondition: - end: true -``` - - | -
BPMN2 Diagram | -Serverless Workflow | -
---|---|
- - - - |
-- -```yaml -id: approvereport -name: Approve Report Workflow -version: '1.0.0' -specVersion: '0.8' -start: Approve Report -states: -- name: Approve Report - type: callback - action: - functionRef: managerDecideOnReport - eventRef: ReportDecisionMadeEvent - transition: Evaluate Report Decision -- name: Evaluate Report Decision - type: switch - dataConditions: - - name: Approve - condition: "${ .decision | .approved == true }" - end: true - - name: Reject - condition: "${ .decision | .approved != true }" - transition: Update Report -- name: Update Report - type: callback - action: - functionRef: workerUpdateReport - eventRef: ReportUpdatedEvent - transition: Approve Report -events: -- name: ReportDecisionMadeEvent - type: report.decisions - source: reports/decision -- name: ReportUpdatedEvent - type: report.updated - source: reports/updated -functions: -- name: managerDecideOnReport - operation: file://myservice.json#managerapproval -- name: workerUpdateReport - operation: file://myservice.json#workerupdate -``` - - | -
BPMN2 Diagram | -Serverless Workflow | -
---|---|
- - - - |
-- -```yaml -id: eventdecision -name: Event Decision workflow -version: '1.0.0' -specVersion: '0.8' -start: A -states: -- name: A - type: operation - actions: - - subFlowRef: asubflowid - transition: Event Decision -- name: Event Decision - type: switch - eventConditions: - - eventRef: EventB - transition: B - - eventRef: EventC - transition: C -- name: B - type: operation - actions: - - name: doSomething - functionRef: doSomethingFunction - end: true -- name: C - type: operation - actions: - - name: doSomething - functionRef: doSomethingFunction - end: true -events: -- name: EventB - type: my.events.b - source: "/events/+" -- name: EventC - type: my.events.c - source: "/events/+" -functions: -- name: doSomethingFunction - operation: file://myservice.json#dosomething -``` - - | -
Brigade | -Serverless Workflow | -
---|---|
- -```javascript -const { events, Job } = require("brigadier"); - -events.on("exec", exec); - -function exec(e, p) { - let j1 = new Job("j1", "alpine:3.7", ["echo hello"]); - let j2 = new Job("j2", "alpine:3.7", ["echo goodbye"]); - - j1.run() - .then(() => { - return j2.run() - }) - .then(() => { - console.log("done"); - }); -}; -``` - - | -- -```yaml -id: greeting -name: Greeting Workflow -version: '1.0.0' -specVersion: '0.8' -start: GreetingState -events: -- name: execEvent - type: exec -functions: -- name: greetingFunction - metadata: - image: alpine:3.7 - command: echo -- name: consoleLogFunction - type: console -states: -- name: GreetingState - type: event - onEvents: - - eventRefs: - - execEvent - actions: - - name: sayHelloAction - functionRef: - refName: greetingFunction - arguments: - greeting: hello - - name: sayGoodbyeAction - functionRef: - refName: greetingFunction - arguments: - greeting: hello - - name: logDoneAction - functionRef: - refName: consoleLogFunction - arguments: - log: done - end: true -``` - - | -
Brigade | -Serverless Workflow | -
---|---|
- -```javascript -const { events, Job } = require("brigadier"); - -events.on("exec", exec); - -async function exec(e, p) { - let j1 = new Job("j1", "alpine:3.7", ["echo hello"]); - // This will fail - let j2 = new Job("j2", "alpine:3.7", ["exit 1"]); - - try { - await j1.run(); - await j2.run(); - console.log("done"); - } catch (e) { - console.log(`Caught Exception ${e}`); - } -}; -``` - - | -- -```yaml -id: greetingwitherrorcheck -name: Greeting Workflow With Error Check -version: '1.0.0' -specVersion: '0.8' -autoRetries: true -start: GreetingState -events: -- name: execEvent - type: exec -errors: -- name: CommonError - code: '123' -functions: -- name: greetingFunction - metadata: - image: alpine:3.7 - command: echo -- name: consoleLogFunction - metadata: - type: console -states: -- name: GreetingState - type: event - onEvents: - - eventRefs: - - execEvent - actions: - - name: sayHelloAction - functionRef: - refName: greetingFunction - arguments: - greeting: hello - nonRetryableErrors: - - CommonError - - name: sayGoodbyeAction - functionRef: - refName: greetingFunction - arguments: - greeting: hello - nonRetryableErrors: - - CommonError - - name: logDoneAction - functionRef: - refName: consoleLogFunction - arguments: - log: done - nonRetryableErrors: - - CommonError - onErrors: - - errorRef: CommonError - transition: HandleErrorState - end: true -- name: HandleErrorState - type: operation - actions: - - name: logErrorAction - functionRef: - refName: consoleLogFunction - arguments: - log: Caught Exception ${ .exception } - end: true -``` - - | -
Brigade | -Serverless Workflow | -
---|---|
- -```javascript -const { events } = require("brigadier") - -events.on("exec", () => { - console.log("==> handling an 'exec' event") -}) - -events.on("push", () => { - console.log(" **** I'm a GitHub 'push' handler") -}) -``` - - | -- -```yaml -id: multieventworkflow -name: Multiple Events Workflow -version: '1.0.0' -specVersion: '0.8' -start: GreetingState -events: -- name: execEvent - type: exec -- name: pushEvent - type: push -functions: -- name: consoleLogFunction - type: console -states: -- name: GreetingState - type: event - onEvents: - - eventRefs: - - execEvent - actions: - - name: logExecEventAction - functionRef: - refName: consoleLogFunction - arguments: - log: "==> handling an 'exec' event" - - eventRefs: - - pushEvent - actions: - - name: logPushEventAction - functionRef: - refName: consoleLogFunction - arguments: - log: "**** I'm a GitHub 'push' handler" - end: true -``` - - | -
Brigade | -Serverless Workflow | -
---|---|
- -```javascript -const { events, Job, Group } = require("brigadier") - -events.on("exec", () => { - var hello = new Job("hello", "alpine:3.4", ["echo hello"]) - var goodbye = new Job("goodbye", "alpine:3.4", ["echo goodbye"]) - - var helloAgain = new Job("hello-again", "alpine:3.4", ["echo hello again"]) - var goodbyeAgain = new Job("bye-again", "alpine:3.4", ["echo bye again"]) - - - var first = new Group() - first.add(hello) - first.add(goodbye) - - var second = new Group() - second.add(helloAgain) - second.add(goodbyeAgain) - - first.runAll().then( () => second.runAll() ) -}) -``` - - | -- -```yaml -id: groupActionsWorkflow -name: Group Actions Workflow -version: '1.0.0' -specVersion: '0.8' -start: FirstGreetGroup -events: -- name: execEvent - type: exec -functions: -- name: echoFunction - metadata: - image: alpine:3.7 - command: echo -states: -- name: FirstGreetGroup - type: event - onEvents: - - eventRefs: - - execEvent - actions: - - name: firstHelloAction - functionRef: - refName: echoFunction - arguments: - message: hello - - name: firstGoodbyeAction - functionRef: - refName: echoFunction - arguments: - message: goodbye - transition: SecondGreetGroup -- name: SecondGreetGroup - type: operation - actions: - - name: secondHelloAction - functionRef: - refName: echoFunction - arguments: - message: hello-again - - name: secondGoodbyeAction - functionRef: - refName: echoFunction - arguments: - message: bye-again - end: true -``` - - | -
Brigade | -Serverless Workflow | -
---|---|
- -```javascript -const { events } = require("brigadier") - -events.on("exec", (e, p) => { - console.log(">>> event " + e.type + " caused by " + e.provider) - console.log(">>> project " + p.name + " clones the repo at " + p.repo.cloneURL) -}) -``` - - | -- -```yaml -id: eventDataWorkflow -name: Event Data Workflow -version: '1.0.0' -specVersion: '0.8' -start: LogEventData -events: -- name: execEvent - type: exec - dataOnly: false -functions: -- name: consoleFunction - type: console -states: -- name: LogEventData - type: event - onEvents: - - eventRefs: - - execEvent - eventDataFilter: - toStateData: "${ .event }" - actions: - - name: eventInfoAction - functionRef: - refName: consoleFunction - arguments: - log: ">>> event ${ .event.type } caused by ${ .event.data.provider }" - - name: projectInfoAction - functionRef: - refName: consoleFunction - arguments: - log: ">>> project ${ .event.data.project.name } clones the repo at by ${ .event.data.repo.cloneURL }" - end: true - -``` - - | -
Brigade | -Serverless Workflow | -
---|---|
- -```javascript -const { events, Job, Group } = require("brigadier") - -events.on("exec", (e, p) => { - var dest = "/mnt/brigade/share/hello.txt" - var one = new Job("one", "alpine:3.4", ["echo hello > " + dest]) - var two = new Job("two", "alpine:3.4", ["echo world >> " + dest]) - var three = new Job("three", "alpine:3.4", ["cat " + dest]) - - one.storage.enabled = true - two.storage.enabled = true - three.storage.enabled = true - - Group.runEach([one, two, three]) -}) -``` - - | -- -```yaml -id: actionResultsWorkflow -name: Action Results Workflow -version: '1.0.0' -specVersion: '0.8' -start: ExecActionsAndStoreResults -events: -- name: execEvent - type: exec -functions: -- name: greetingFunction - metadata: - image: alpine:3.7 - command: echo -- name: storeToFileFunction - metadata: - image: alpine:3.7 - command: filestore -states: -- name: ExecActionsAndStoreResults - type: event - onEvents: - - eventRefs: - - execEvent - eventDataFilter: - toStateData: "${ .event }" - actions: - - name: helloAction - actionDataFilter: - results: "${ .helloResult }" - functionRef: - refName: greetingFunction - arguments: - message: hello - - name: worldAction - actionDataFilter: - results: "${ .worldResults }" - functionRef: - refName: greetingAction - arguments: - message: world - - name: storeToFileAction - functionRef: - refName: storeToFileFunction - arguments: - destination: "${ .event.destination }" - value: "${ .helloResult } ${ .worldResults }" - end: true - -``` - - | -
Brigade | -Serverless Workflow | -
---|---|
- -```javascript -const {events} = require("brigadier") - -events.on("exec", function(e, project) { - const e2 = { - type: "next", - provider: "exec-handler", - buildID: e.buildID, - workerID: e.workerID, - cause: {event: e} - } - events.fire(e2, project) -}) - -events.on("next", (e) => { - console.log(`fired ${e.type} caused by ${e.cause.event.type}`) -}) -``` - - | -- -```yaml -id: eventDataWorkflow -name: Event Data Workflow -version: '1.0.0' -specVersion: '0.8' -start: ExecEventState -events: -- name: execEvent - type: exec - dataOnly: false -- name: nextEvent - type: next - kind: produced -functions: -- name: consoleLogFunction - type: console -states: -- name: ExecEventState - type: event - onEvents: - - eventRefs: - - execEvent - actions: [] - eventDataFilter: - toStateData: "${ .execEvent }" - transition: - nextState: NextEventState - produceEvents: - - eventRef: nextEvent - data: - type: next - provider: exec-handler - buildID: "${ .execEvent.data.buildID }" - workerID: "${ .execEvent.data.workerID }" - cause: - event: "${ .execEvent }" -- name: NextEventState - type: event - onEvents: - - eventRefs: - - nextEvent - eventDataFilter: - toStateData: "${ .nextEvent }" - actions: - - name: consoleLogAction - functionRef: - refName: consoleLogFunction - arguments: - log: "fired ${ .nextEvent.data.type } caused by ${ .nextEvent.data.cause.event }" - end: true -``` - - | -
Serverless Workflow | -|
---|---|
- -```json -{ - "main": { - "params": [ - "args" - ], - "steps": [ - { - "step1": { - "assign": [ - { - "outputVar": "${\"Hello \" + args.firstName + \" \" + args.lastName}" - } - ] - } - }, - { - "step2": { - "return": "${outputVar}" - } - } - ] - } -} -``` - - | -- -```json -{ - "id": "greetingwithargs", - "name": "Greeting With Args", - "specVersion": "0.8", - "start": "Set Output", - "states": [ - { - "name": "Set Output", - "type": "inject", - "data": { - "outputVar": "Hello ${ .firstname + \" \" + .lastname }" - }, - "stateDataFilter": { - "output": "${ .outputVar }" - }, - "end": true - } - ] -} -``` - - | -
Serverless Workflow | -|
---|---|
- -```json -[ - { - "define": { - "assign": [ - { - "array": [ - "foo", - "ba", - "r" - ] - }, - { - "result": "" - }, - { - "i": 0 - } - ] - } - }, - { - "check_condition": { - "switch": [ - { - "condition": "${len(array) > i}", - "next": "iterate" - } - ], - "next": "exit_loop" - } - }, - { - "iterate": { - "assign": [ - { - "result": "${result + array[i]}" - }, - { - "i": "${i+1}" - } - ], - "next": "check_condition" - } - }, - { - "exit_loop": { - "return": { - "concat_result": "${result}" - } - } - } -] -``` - - | -- -```json -{ - "id": "concatarray", - "name": "Concatenating array values", - "start": "DoConcat", - "specVersion": "0.8", - "states": [ - { - "name": "DoConcat", - "type": "inject", - "data": { - "array": [ - "foo", - "ba", - "r" - ] - }, - "stateDataFilter": { - "output": "${ .array | join(\"\") }" - }, - "end": true - } - ] -} -``` - - | -
Serverless Workflow | -|
---|---|
- -```json -[ - { - "initialize": { - "assign": [ - { - "project": "${sys.get_env(\"GOOGLE_CLOUD_PROJECT_NUMBER\")}" - }, - { - "zone": "us-central1-a" - }, - { - "vmToStop": "examplevm" - } - ] - } - }, - { - "stopInstance": { - "call": "http.post", - "args": { - "url": "${\"https://compute.googleapis.com/compute/v1/projects/\"+project+\"/zones/\"+zone+\"/instances/\"+vmToStop+\"/stop\"}", - "auth": { - "type": "OAuth2" - } - }, - "result": "stopResult" - } - } -] -``` - - | -- -```json -{ - "id": "stopcomputeengine", - "name": "Stop Compute Engine", - "specVersion": "0.8", - "start": "DoStop", - "states": [ - { - "name": "DoStop", - "type": "operation", - "actions": [ - { - "functionRef": { - "refName": "StopComputeEngine", - "arguments": { - "project": "${ .project }", - "zone": "${ .zone }", - "vmToStop": "${ .vmToStop }" - } - } - } - ], - "end": true - } - ], - "functions": [ - { - "name": "StopComputeEngine", - "operation": "computeengineopenapi.json#stopengine" - } - ] -} -``` - - | -
Serverless Workflow | -|
---|---|
- -```json -[ - { - "initVariables": { - "assign": [ - { - "project": "${sys.get_env(\"GOOGLE_CLOUD_PROJECT_ID\")}" - }, - { - "topic": "mytopic1" - }, - { - "message": "Hello world!" - } - ] - } - }, - { - "publish": { - "try": { - "call": "googleapis.pubsub.v1.projects.topics.publish", - "args": { - "topic": "${\"projects/\" + project + \"/topics/\" + topic}", - "body": { - "messages": [ - { - "data": "${base64.encode(text.encode(message))}" - } - ] - } - }, - "result": "publishResult" - }, - "except": { - "as": "e", - "steps": [ - { - "handlePubSubError": { - "switch": [ - { - "condition": "${e.code == 404}", - "raise": "PubSub Topic not found" - }, - { - "condition": "${e.code == 403}", - "raise": "Error authenticating to PubSub" - } - ] - } - }, - { - "unhandledException": { - "raise": "${e}" - } - } - ] - } - } - }, - { - "last": { - "return": "${publishResult}" - } - } -] -``` - - | -- -```json -{ - "id": "publishtotopicwitherrorhandling", - "name": "Publish To Topic With Error Handling", - "specVersion": "0.8", - "start": "DoPublish", - "errors": [ - { - "name": "PubSub Topic not found", - "code": "404" - }, - { - "name": "Error authenticating to PubSub", - "code": "403" - } - ], - "states": [ - { - "name": "DoPublish", - "type": "operation", - "actions": [ - { - "functionRef": { - "refName": "PublishToTopic", - "arguments": { - "project": "${ .project }", - "topic": "${ .topic }", - "message": "${ .message }" - } - } - } - ], - "onErrors": [ - { - "errorRef": "PubSub Topic not found", - "end": { - "produceEvents": [ - { - "eventRef": "TopicError", - "data": { "message": "PubSub Topic not found"} - } - ] - } - }, - { - "errorRef": "Error authenticating to PubSub", - "end": { - "produceEvents": [ - { - "eventRef": "TopicError", - "data": { "message": "Error authenticating to PubSub"} - } - ] - } - } - ], - "end": true - } - ], - "functions": [ - { - "name": "PublishToTopic", - "operation": "pubsubapi.json#publish" - } - ], - "events": [ - { - "name": "TopicError", - "source": "pubsub.topic.events", - "type": "pubsub/events" - } - ] -} -``` - - | -
Serverless Workflow | -|
---|---|
- -```json -{ - "main": { - "steps": [ - { - "read_item": { - "try": { - "call": "http.get", - "args": { - "url": "https://host.com/api" - }, - "result": "api_response" - }, - "retry": { - "predicate": "${custom_predicate}", - "max_retries": 5, - "backoff": { - "initial_delay": 2, - "max_delay": 60, - "multiplier": 2 - } - } - } - }, - { - "last_step": { - "return": "OK" - } - } - ] - }, - "custom_predicate": { - "params": [ - "e" - ], - "steps": [ - { - "what_to_repeat": { - "switch": [ - { - "condition": "${e.code == 500}", - "return": true - } - ] - } - }, - { - "otherwise": { - "return": false - } - } - ] - } -} -``` - - | -- -```json -{ - "id": "errorhandlingwithretries", - "name": "Error Handling with Retries", - "start": "ReadItem", - "specVersion": "0.8", - "states": [ - { - "name": "ReadItem", - "type": "operation", - "actions": [ - { - "functionRef": "ReadItemFromApi", - "retryRef": "ServiceNotAvailableRetryPolicy", - "retryableErrors": ["Service Not Available"] - } - ], - "onErrors": [ - { - "errorRef": "Service Not Available", - "end": true - } - ], - "end": true - } - ], - "functions": [ - { - "name": "ReadItemFromApi", - "operation": "someapi.json#read" - } - ], - "errors": [ - { - "name": "Service Not Available", - "code": "500" - } - ], - "retries": [ - { - "name": "ServiceNotAvailableRetryPolicy", - "maxAttempts": 5, - "delay": "PT2S", - "maxDelay": "PT60S", - "multiplier": 2 - } - ] -} -``` - - | -
Serverless Workflow | -|
---|---|
- -```json -{ - "main": { - "steps": [ - { - "first": { - "call": "hello", - "args": { - "input": "Kristof" - }, - "result": "someOutput" - } - }, - { - "second": { - "return": "${someOutput}" - } - } - ] - }, - "hello": { - "params": [ - "input" - ], - "steps": [ - { - "first": { - "return": "${\"Hello \"+input}" - } - } - ] - } -} -``` - - | -- -```json -{ - "id": "callsubflow", - "name": "Call SubFlow", - "start": "CallSub", - "states": [ - { - "name": "CallSub", - "type":"operation", - "actions": [ - { - "subFlowRef": "calledsubflow" - } - ], - "end": true - } - ] -} -``` - - | -
Serverless Workflow | -|
---|---|
- -```json -[ - { - "firstStep": { - "call": "http.get", - "args": { - "url": "https://www.example.com/callA" - }, - "result": "firstResult" - } - }, - { - "whereToJump": { - "switch": [ - { - "condition": "${firstResult.body.SomeField < 10}", - "next": "small" - }, - { - "condition": "${firstResult.body.SomeField < 100}", - "next": "medium" - } - ], - "next": "large" - } - }, - { - "small": { - "call": "http.get", - "args": { - "url": "https://www.example.com/SmallFunc" - }, - "next": "end" - } - }, - { - "medium": { - "call": "http.get", - "args": { - "url": "https://www.example.com/MediumFunc" - }, - "next": "end" - } - }, - { - "large": { - "call": "http.get", - "args": { - "url": "https://www.example.com/LargeFunc" - }, - "next": "end" - } - } -] -``` - - | -- -```json -{ - "id": "databasedconditions", - "name": "Data Based Conditions", - "start": "CallA", - "states": [ - { - "name": "CallA", - "type":"operation", - "actions": [ - { - "functionRef": "callFunctionA" - } - ], - "transition": "EvaluateAResults" - }, - { - "name": "EvaluateAResults", - "type": "switch", - "dataConditions": [ - { - "name": "Less than 10", - "condition": "${ .body | .SomeField < 10 }", - "transition": "CallSmall" - }, - { - "name": "Less than 100", - "condition": "${ .body | .SomeField < 100 }", - "transition": "CallMedium" - } - ], - "defaultCondition": { - "transition": "CallLarge" - } - }, - { - "name": "CallSmall", - "type":"operation", - "actions": [ - { - "functionRef": "callFunctionSmall" - } - ], - "end": true - }, - { - "name": "CallMedium", - "type":"operation", - "actions": [ - { - "functionRef": "callFunctionMedium" - } - ], - "end": true - }, - { - "name": "CallLarge", - "type":"operation", - "actions": [ - { - "functionRef": "callFunctionMedium" - } - ], - "end": true - } - ], - "functions": [ - { - "name": "callFunctionA", - "operation": "myapi.json#calla" - }, - { - "name": "callFunctionSmall", - "operation": "myapi.json#callsmall" - }, - { - "name": "callFunctionMedium", - "operation": "myapi.json#callmedium" - }, - { - "name": "callFunctionLarge", - "operation": "myapi.json#calllarge" - } - ] -} -``` - - | -
Temporal | -Serverless Workflow | -
---|---|
- -```java -// Workflow implementation -public static class GreetingWorkflowImpl implements GreetingWorkflow { - private final GreetingActivities activities = - Workflow.newActivityStub( - GreetingActivities.class, - ActivityOptions.newBuilder().setScheduleToCloseTimeout(Duration.ofSeconds(2)).build()); - - // Workflow method - @Override - public String getGreeting(String name) { - return activities.composeGreeting("Hello", name); - } -} -``` - - | -- -```json -{ - "id": "greetingworkflow", - "name": "Greeting Workflow", - "version": "1.0.0", - "specVersion": "0.8", - "autoRetries": true, - "states": [ - { - "name": "Greet", - "type": "operation", - "actions": [ - { - "name": "Greet Action", - "functionRef": { - "refName": "GreetingFunction", - "arguments": { - "prefix": "Hello", - "name": "${ .name }" - } - } - } - ], - "timeouts": { - "actionExecTimeout": "PT2S" - }, - "end": true - } - ], - "functions": [ - { - "name": "GreetingFunction", - "operation": "myactionsapi.json#composeGreeting" - } - ] -} -``` - - | -
Temporal | -Serverless Workflow | -
---|---|
- -```java -// Workflow implementation -public static class GreetingWorkflowImpl implements GreetingWorkflow { - private final GreetingActivities activities = - Workflow.newActivityStub( - GreetingActivities.class, - ActivityOptions.newBuilder().setScheduleToCloseTimeout(Duration.ofSeconds(10)).build()); - - @Override - public String greet(String name) { - activities.greet("Hello " + name + "!"); - } -} - -// Client code Workflow Options (cron) -WorkflowOptions workflowOptions = - WorkflowOptions.newBuilder() - .setWorkflowId(CRON_WORKFLOW_ID) - .setTaskQueue(TASK_QUEUE) - .setCronSchedule("* * * * *") - .setWorkflowExecutionTimeout(Duration.ofMinutes(10)) - .build(); - -``` - - | -- -```json -{ - "id": "greetingworkflow", - "name": "Greeting Workflow", - "version": "1.0.0", - "specVersion": "0.8", - "autoRetries": true, - "timeouts": { - "workflowExecTimeout": "PT10M" - } - "start": { - "stateName": "GreetingState", - "schedule": { - "cron": { - "expression": "* * * * *" - } - } - }, - "states": [ - { - "name": "GreetingState", - "type": "operation", - "actions": [ - { - "name": "Greet", - "functionRef": { - "refName": "GreetingFunction", - "arguments": { - "prefix": "Hello", - "name": "${ .name }" - } - } - } - ], - "timeouts": { - "actionExecTimeout": "PT2S" - }, - "end": true - } - ], - "functions": [ - { - "name": "GreetingFunction", - "operation": "myactionsapi.json#greet" - } - ] -} -``` - - | -
Temporal | -Serverless Workflow | -
---|---|
-
-```java
-// Workflow implementation
- public static class SagaWorkflowImpl implements SagaWorkflow {
- ActivityOperation activity =
- Workflow.newActivityStub(
- ActivityOperation.class,
- ActivityOptions.newBuilder().setScheduleToCloseTimeout(Duration.ofSeconds(2)).build());
-
- // Workflow Method
- @Override
- public void execute() {
- Saga saga = new Saga(new Saga.Options.Builder().setParallelCompensation(false).build());
- try {
- // The following demonstrate how to compensate sync invocations.
- ChildWorkflowOperation op1 = Workflow.newChildWorkflowStub(ChildWorkflowOperation.class);
- op1.execute(10);
- ChildWorkflowCompensation c1 =
- Workflow.newChildWorkflowStub(ChildWorkflowCompensation.class);
- saga.addCompensation(c1::compensate, -10);
-
- // The following demonstrate how to compensate async invocations.
- Promise |
-- -```json -{ - "id": "HelloSaga", - "name": "Hello SAGA compensation Workflow", - "version": "1.0.0", - "specVersion": "0.8", - "states": [ - { - "name": "ExecuteState", - "type": "operation", - "compensatedBy": "CompensateState", - "actions": [ - { - "name": "Execute", - "functionRef": { - "refName": "ExecuteFunction", - "arguments": { - "amount": 10 - } - } - } - ], - "end": { - "compensate": true - } - }, - { - "name": "CompensateState", - "type": "operation", - "usedForCompensation": true, - "actions": [ - { - "name": "Compensate", - "functionRef": { - "refName": "CompensateFunction", - "arguments": { - "amount": -10 - } - } - } - ] - } - ], - "functions": [ - { - "name": "ExecuteFunction", - "operation": "myactionsapi.json#execute" - }, - { - "name": "CompensateFunction", - "operation": "myactionsapi.json#compensate" - } - ] -} -``` - - | -
Temporal | -Serverless Workflow | -
---|---|
- -```java -// Workflow Implementation -public static class GreetingWorkflowImpl implements GreetingWorkflow { - private final GreetingActivities activities = - Workflow.newActivityStub( - GreetingActivities.class, - ActivityOptions.newBuilder() - .setScheduleToCloseTimeout(Duration.ofSeconds(10)) - .setRetryOptions( - RetryOptions.newBuilder() - .setInitialInterval(Duration.ofSeconds(1)) - .setDoNotRetry(IllegalArgumentException.class.getName()) - .build()) - .build()); - @Override - public String getGreeting(String name) { - // This is a blocking call that returns only after activity is completed. - return activities.composeGreeting("Hello", name); - } -} - -// Activity Implementation -static class GreetingActivitiesImpl implements GreetingActivities { - private int callCount; - private long lastInvocationTime; - - @Override - public synchronized String composeGreeting(String greeting, String name) { - if (lastInvocationTime != 0) { - long timeSinceLastInvocation = System.currentTimeMillis() - lastInvocationTime; - System.out.print(timeSinceLastInvocation + " milliseconds since last invocation. "); - } - lastInvocationTime = System.currentTimeMillis(); - if (++callCount < 4) { - System.out.println("composeGreeting activity is going to fail"); - throw new IllegalStateException("not yet"); - } - System.out.println("composeGreeting activity is going to complete"); - return greeting + " " + name + "!"; - } -} -``` - - | -- -```json -{ - "id": "HelloActivityRetry", - "name": "Hello Activity with Retries Workflow", - "version": "1.0.0", - "specVersion": "0.8", - "autoRetries": true, - "start": "GreetingState", - "states": [ - { - "name": "GreetingState", - "type": "operation", - "actions": [ - { - "name": "Greet", - "functionRef": { - "refName": "GreetingFunction", - "arguments": { - "name": "World" - }, - "retryRef": "GreetingRetry", - "nonRetryableErrors": ["IllegalArgumentException"] - } - } - ], - "timeouts": { - "actionExecTimeout": "PT10S" - }, - "onErrors": [ - { - "errorRefs": ["IllegalStateException", "IllegalArgumentException"], - "end": true - } - ], - "end": true - } - ], - "functions": [ - { - "name": "GreetingFunction", - "operation": "myactionsapi.json#composeGreeting" - } - ], - "errors": [ - { - "name": "IllegalStateException" - }, - { - "name": "IllegalArgumentException" - } - ], - "retries": [ - { - "name": "GreetingRetry", - "delay": "PT1S" - } - ] -} -``` - - | -
- -
- -#### Workflow Definition - -JSON | -YAML | -
---|---|
- -```json -{ - "id": "helloworld", - "version": "1.0.0", - "specVersion": "0.8", - "name": "Hello World Workflow", - "description": "Inject Hello World", - "start": "hello-state", - "states": [ - { - "name": "hello-state", - "type": "inject", - "data": { - "result": "Hello World!" - }, - "end": true - } - ] -}``` - - | -- -```yaml -id: helloworld -version: 1.0.0 -specVersion: "0.8" -name: Hello World Workflow -description: Inject Hello World -start: hello-state -states: - - name: hello-state - type: inject - data: - result: Hello World! - end: true -``` - - | -
- -
- -#### Workflow Definition - -JSON | -YAML | -
---|---|
- -```json -{ - "id": "greeting", - "version": "1.0.0", - "specVersion": "0.8", - "name": "greeting-workflow", - "description": "Greet Someone", - "start": "greet", - "functions": [ - { - "name": "greeting-function", - "type": "openapi", - "operation": "file://myapis/greetingapis.json#greeting" - } - ], - "states": [ - { - "name": "greet", - "type": "operation", - "actions": [ - { - "name": "greet-action", - "functionRef": { - "refName": "greeting-function", - "arguments": { - "name": "${ .person.name }" - } - }, - "actionDataFilter": { - "results": "${ {greeting: .greeting} }" - } - } - ], - "end": true - } - ] -}``` - - | -- -```yaml -id: greeting -version: 1.0.0 -specVersion: "0.8" -name: greeting-workflow -description: Greet Someone -start: greet -functions: - - name: greeting-function - type: openapi - operation: file://myapis/greetingapis.json#greeting -states: - - name: greet - type: operation - actions: - - name: greet-action - functionRef: - refName: greeting-function - arguments: - name: ${ .person.name } - actionDataFilter: - results: "${ {greeting: .greeting} }" - end: true -``` - - | -
- -
- -#### Workflow Definition - -JSON | -YAML | -
---|---|
- -```json -{ - "id": "eventbasedgreeting", - "version": "1.0.0", - "specVersion": "0.8", - "name": "Event Based Greeting Workflow", - "description": "Event Based Greeting", - "start": "greet", - "events": [ - { - "name": "greeting-event", - "type": "greetingEventType", - "source": "greetingEventSource" - } - ], - "functions": [ - { - "name": "greeting-function", - "operation": "file://myapis/greetingapis.json#greeting" - } - ], - "states": [ - { - "name": "greet", - "type": "event", - "onEvents": [ - { - "eventRefs": [ - "greeting-event" - ], - "eventDataFilter": { - "data": "${ .greet }", - "toStateData": "${ .greet }" - }, - "actions": [ - { - "name": "greet-action", - "functionRef": { - "refName": "greeting-function", - "arguments": { - "name": "${ .greet.name }" - } - } - } - ] - } - ], - "stateDataFilter": { - "output": "${ .payload.greeting }" - }, - "end": true - } - ] -}``` - - | -- -```yaml -id: eventbasedgreeting -version: 1.0.0 -specVersion: "0.8" -name: Event Based Greeting Workflow -description: Event Based Greeting -start: greet -events: - - name: greeting-event - type: greetingEventType - source: greetingEventSource -functions: - - name: greeting-function - operation: file://myapis/greetingapis.json#greeting -states: - - name: greet - type: event - onEvents: - - eventRefs: - - greeting-event - eventDataFilter: - data: ${ .greet } - toStateData: ${ .greet } - actions: - - name: greet-action - functionRef: - refName: greeting-function - arguments: - name: ${ .greet.name } - stateDataFilter: - output: ${ .payload.greeting } - end: true -``` - - | -
- -
- -#### Workflow Definition - -JSON | -YAML | -
---|---|
- -```json -{ - "id": "solvemathproblems", - "version": "1.0.0", - "specVersion": "0.8", - "name": "solve-math-problems-workflow", - "description": "Solve math problems", - "start": "solve", - "functions": [ - { - "name": "solve-math-exp-func", - "operation": "http://myapis.org/mapthapis.json#solveExpression" - } - ], - "states": [ - { - "name": "solve", - "type": "foreach", - "inputCollection": "${ .expressions }", - "iterationParam": "singleexpression", - "outputCollection": "${ .results }", - "actions": [ - { - "name": "solve-action", - "functionRef": { - "refName": "solve-math-exp-func", - "arguments": { - "expression": "${ .singleexpression }" - } - } - } - ], - "stateDataFilter": { - "output": "${ .results }" - }, - "end": true - } - ] -}``` - - | -- -```yaml -id: solvemathproblems -version: 1.0.0 -specVersion: "0.8" -name: solve-math-problems-workflow -description: Solve math problems -start: solve -functions: - - name: solve-math-exp-func - operation: http://myapis.org/mapthapis.json#solveExpression -states: - - name: solve - type: foreach - inputCollection: ${ .expressions } - iterationParam: singleexpression - outputCollection: ${ .results } - actions: - - name: solve-action - functionRef: - refName: solve-math-exp-func - arguments: - expression: ${ .singleexpression } - stateDataFilter: - output: ${ .results } - end: true -``` - - | -
- -
- -#### Workflow Definition - -JSON | -YAML | -
---|---|
- -```json -{ - "id": "parallelexec", - "version": "1.0.0", - "specVersion": "0.8", - "name": "parallel-execution", - "description": "Executes two branches in parallel", - "start": "parallelexec", - "states": [ - { - "name": "parallelexec", - "type": "parallel", - "completionType": "allOf", - "branches": [ - { - "name": "short-delay-branch", - "actions": [ - { - "name": "short-delay-action", - "subFlowRef": "shortdelayworkflowid" - } - ] - }, - { - "name": "long-delay-branch", - "actions": [ - { - "name": "short-delay-action", - "subFlowRef": "longdelayworkflowid" - } - ] - } - ], - "end": true - } - ] -}``` - - | -- -```yaml -id: parallelexec -version: 1.0.0 -specVersion: "0.8" -name: parallel-execution -description: Executes two branches in parallel -start: parallelexec -states: - - name: parallelexec - type: parallel - completionType: allOf - branches: - - name: short-delay-branch - actions: - - name: short-delay-action - subFlowRef: shortdelayworkflowid - - name: long-delay-branch - actions: - - name: short-delay-action - subFlowRef: longdelayworkflowid - end: true -``` - - | -
- -
- -#### Workflow Definition - -JSON | -YAML | -
---|---|
- -```json -{ - "id": "sendcustomeremail", - "version": "1.0.0", - "specVersion": "0.8", - "name": "send-customer-email-workflow", - "description": "Send email to a customer", - "start": "send-email", - "functions": [ - { - "name": "email-function", - "operation": "file://myapis/emailapis.json#sendEmail" - } - ], - "states": [ - { - "name": "send-email", - "type": "operation", - "actions": [ - { - "name": "send-email-action", - "functionRef": { - "invoke": "async", - "refName": "email-function", - "arguments": { - "customer": "${ .customer }" - } - } - } - ], - "end": true - } - ] -}``` - - | -- -```yaml -id: sendcustomeremail -version: 1.0.0 -specVersion: "0.8" -name: send-customer-email-workflow -description: Send email to a customer -start: send-email -functions: - - name: email-function - operation: file://myapis/emailapis.json#sendEmail -states: - - name: send-email - type: operation - actions: - - name: send-email-action - functionRef: - invoke: async - refName: email-function - arguments: - customer: ${ .customer } - end: true -``` - - | -
- -
- -#### Workflow Definition - -JSON | -YAML | -
---|---|
- -```json -{ - "id": "onboardcustomer", - "version": "1.0.0", - "specVersion": "0.8", - "name": "onboard-customer", - "description": "Onboard a Customer", - "start": "onboard", - "states": [ - { - "name": "onboard", - "type": "operation", - "actions": [ - { - "name": "onboard-action", - "subFlowRef": { - "invoke": "async", - "onParentComplete": "continue", - "workflowId": "customeronboardingworkflow", - "version": "1.0.0" - } - } - ], - "end": true - } - ] -}``` - - | -- -```yaml -id: onboardcustomer -version: 1.0.0 -specVersion: "0.8" -name: onboard-customer -description: Onboard a Customer -start: onboard -states: - - name: onboard - type: operation - actions: - - name: onboard-action - subFlowRef: - invoke: async - onParentComplete: continue - workflowId: customeronboardingworkflow - version: 1.0.0 - end: true -``` - - | -
- -
- -#### Workflow Definition - -JSON | -YAML | -
---|---|
- -```json -{ - "id": "eventbasedswitchstate", - "version": "1.0.0", - "specVersion": "0.8", - "name": "event-based-switch-transitions", - "description": "Event Based Switch Transitions", - "start": "checkvisastatus", - "events": [ - { - "name": "visa-approved-event", - "type": "VisaApproved", - "source": "visaCheckSource" - }, - { - "name": "visa-rejected-event", - "type": "VisaRejected", - "source": "visaCheckSource" - } - ], - "states": [ - { - "name": "checkvisastatus", - "type": "switch", - "eventConditions": [ - { - "eventRef": "visa-approved-event", - "transition": "handle-approved-visa", - "name": "approved-condition" - }, - { - "eventRef": "visa-rejected-event", - "transition": "handle-rejected-visa", - "name": "rejected-condition" - } - ], - "timeouts": { - "eventTimeout": "PT1H" - }, - "defaultCondition": { - "transition": "handle-no-visa-decision" - } - }, - { - "name": "handle-approved-visa", - "type": "operation", - "actions": [ - { - "name": "handle-approved-action", - "subFlowRef": "handleApprovedVisaWorkflowID" - } - ], - "end": true - }, - { - "name": "handle-rejected-visa", - "type": "operation", - "actions": [ - { - "name": "handle-rejected-action", - "subFlowRef": "handleRejectedVisaWorkflowID" - } - ], - "end": true - }, - { - "name": "handle-no-visa-decision", - "type": "operation", - "actions": [ - { - "name": "handle-novisa-action", - "subFlowRef": "handleNoVisaDecisionWorkflowId" - } - ], - "end": true - } - ] -}``` - - | -- -```yaml -id: eventbasedswitchstate -version: 1.0.0 -specVersion: "0.8" -name: event-based-switch-transitions -description: Event Based Switch Transitions -start: checkvisastatus -events: - - name: visa-approved-event - type: VisaApproved - source: visaCheckSource - - name: visa-rejected-event - type: VisaRejected - source: visaCheckSource -states: - - name: checkvisastatus - type: switch - eventConditions: - - eventRef: visa-approved-event - transition: handle-approved-visa - name: approved-condition - - eventRef: visa-rejected-event - transition: handle-rejected-visa - name: rejected-condition - timeouts: - eventTimeout: PT1H - defaultCondition: - transition: handle-no-visa-decision - - name: handle-approved-visa - type: operation - actions: - - name: handle-approved-action - subFlowRef: handleApprovedVisaWorkflowID - end: true - - name: handle-rejected-visa - type: operation - actions: - - name: handle-rejected-action - subFlowRef: handleRejectedVisaWorkflowID - end: true - - name: handle-no-visa-decision - type: operation - actions: - - name: handle-novisa-action - subFlowRef: handleNoVisaDecisionWorkflowId - end: true -``` - - | -
- -
- -#### Workflow Definition - -JSON | -YAML | -
---|---|
- -```json -{ - "id": "applicantrequest", - "version": "1.0.0", - "specVersion": "0.8", - "name": "applicant-request-decision-workflow", - "description": "Determine if applicant request is valid", - "start": "check-application", - "functions": [ - { - "name": "send-rejection-email-function", - "operation": "http://myapis.org/applicationapi.json#emailRejection" - } - ], - "states": [ - { - "name": "check-application", - "type": "switch", - "dataConditions": [ - { - "condition": "${ .applicants | .age >= 18 }", - "transition": "start-application", - "name": "adult-condition" - }, - { - "condition": "${ .applicants | .age < 18 }", - "transition": "reject-application", - "name": "minor-condition" - } - ], - "defaultCondition": { - "transition": "reject-application" - } - }, - { - "name": "start-application", - "type": "operation", - "actions": [ - { - "name": "start-app-action", - "subFlowRef": "startApplicationWorkflowId" - } - ], - "end": true - }, - { - "name": "reject-application", - "type": "operation", - "actionMode": "sequential", - "actions": [ - { - "name": "send-reject-action", - "functionRef": { - "refName": "send-rejection-email-function", - "arguments": { - "applicant": "${ .applicant }" - } - } - } - ], - "end": true - } - ] -}``` - - | -- -```yaml -id: applicantrequest -version: 1.0.0 -specVersion: "0.8" -name: applicant-request-decision-workflow -description: Determine if applicant request is valid -start: check-application -functions: - - name: send-rejection-email-function - operation: http://myapis.org/applicationapi.json#emailRejection -states: - - name: check-application - type: switch - dataConditions: - - condition: ${ .applicants | .age >= 18 } - transition: start-application - name: adult-condition - - condition: ${ .applicants | .age < 18 } - transition: reject-application - name: minor-condition - defaultCondition: - transition: reject-application - - name: start-application - type: operation - actions: - - name: start-app-action - subFlowRef: startApplicationWorkflowId - end: true - - name: reject-application - type: operation - actionMode: sequential - actions: - - name: send-reject-action - functionRef: - refName: send-rejection-email-function - arguments: - applicant: ${ .applicant } - end: true -``` - - | -
- -
- -#### Workflow Definition - -JSON | -YAML | -
---|---|
- -```json -{ - "id": "provisionorders", - "version": "1.0.0", - "specVersion": "0.8", - "name": "provision-orders", - "description": "Provision Orders and handle errors thrown", - "start": "provision-order", - "functions": [ - { - "name": "provision-order-function", - "operation": "http://myapis.org/provisioningapi.json#doProvision" - } - ], - "errors": [ - { - "name": "missing-order-id" - }, - { - "name": "missing-order-item" - }, - { - "name": "missing-order-quantity" - } - ], - "states": [ - { - "name": "provision-order", - "type": "operation", - "actionMode": "sequential", - "actions": [ - { - "name": "provision-action", - "functionRef": { - "refName": "provision-order-function", - "arguments": { - "order": "${ .order }" - } - } - } - ], - "stateDataFilter": { - "output": "${ .exceptions }" - }, - "transition": "apply-order", - "onErrors": [ - { - "errorRef": "missing-order-id", - "transition": "missing-id" - }, - { - "errorRef": "missing-order-item", - "transition": "missing-item" - }, - { - "errorRef": "missing-order-quantity", - "transition": "missing-quantity" - } - ] - }, - { - "name": "missing-id", - "type": "operation", - "actions": [ - { - "name": "missing-action", - "subFlowRef": "handleMissingIdExceptionWorkflow" - } - ], - "end": true - }, - { - "name": "missing-item", - "type": "operation", - "actions": [ - { - "name": "missing-item", - "subFlowRef": "handleMissingItemExceptionWorkflow" - } - ], - "end": true - }, - { - "name": "missing-quantity", - "type": "operation", - "actions": [ - { - "name": "missing-quantity", - "subFlowRef": "handleMissingQuantityExceptionWorkflow" - } - ], - "end": true - }, - { - "name": "apply-order", - "type": "operation", - "actions": [ - { - "name": "apply-order", - "subFlowRef": "applyOrderWorkflowId" - } - ], - "end": true - } - ] -}``` - - | -- -```yaml -id: provisionorders -version: 1.0.0 -specVersion: "0.8" -name: provision-orders -description: Provision Orders and handle errors thrown -start: provision-order -functions: - - name: provision-order-function - operation: http://myapis.org/provisioningapi.json#doProvision -errors: - - name: missing-order-id - - name: missing-order-item - - name: missing-order-quantity -states: - - name: provision-order - type: operation - actionMode: sequential - actions: - - name: provision-action - functionRef: - refName: provision-order-function - arguments: - order: ${ .order } - stateDataFilter: - output: ${ .exceptions } - transition: apply-order - onErrors: - - errorRef: missing-order-id - transition: missing-id - - errorRef: missing-order-item - transition: missing-item - - errorRef: missing-order-quantity - transition: missing-quantity - - name: missing-id - type: operation - actions: - - name: missing-action - subFlowRef: handleMissingIdExceptionWorkflow - end: true - - name: missing-item - type: operation - actions: - - name: missing-item - subFlowRef: handleMissingItemExceptionWorkflow - end: true - - name: missing-quantity - type: operation - actions: - - name: missing-quantity - subFlowRef: handleMissingQuantityExceptionWorkflow - end: true - - name: apply-order - type: operation - actions: - - name: apply-order - subFlowRef: applyOrderWorkflowId - end: true -``` - - | -
- -
- -#### Workflow Definition - -JSON | -YAML | -
---|---|
- -```json -{ - "id": "jobmonitoring", - "version": "1.0.0", - "specVersion": "0.8", - "name": "jobmonitoring", - "description": "Monitor finished execution of a submitted job", - "start": "submit-job", - "functions": [ - { - "name": "submit-job", - "operation": "http://myapis.org/monitorapi.json#doSubmit" - }, - { - "name": "check-job-status", - "operation": "http://myapis.org/monitorapi.json#checkStatus" - }, - { - "name": "report-job-suceeded", - "operation": "http://myapis.org/monitorapi.json#reportSucceeded" - }, - { - "name": "report-job-failed", - "operation": "http://myapis.org/monitorapi.json#reportFailure" - } - ], - "states": [ - { - "name": "submit-job", - "type": "operation", - "actionMode": "sequential", - "actions": [ - { - "name": "submit-job", - "functionRef": { - "refName": "submit-job", - "arguments": { - "name": "${ .job.name }" - } - }, - "actionDataFilter": { - "results": "${ .jobuid }" - } - } - ], - "stateDataFilter": { - "output": "${ .jobuid }" - }, - "transition": "wait-for-completion" - }, - { - "name": "wait-for-completion", - "type": "sleep", - "duration": "PT5S", - "transition": "get-job-status" - }, - { - "name": "get-job-status", - "type": "operation", - "actionMode": "sequential", - "actions": [ - { - "name": "get-job-status", - "functionRef": { - "refName": "check-job-status", - "arguments": { - "name": "${ .jobuid }" - } - }, - "actionDataFilter": { - "results": "${ .jobstatus }" - } - } - ], - "stateDataFilter": { - "output": "${ .jobstatus }" - }, - "transition": "determine-completion" - }, - { - "name": "determine-completion", - "type": "switch", - "dataConditions": [ - { - "condition": "${ .jobStatus == \"SUCCEEDED\" }", - "transition": "job-succeeded", - "name": "succeed" - }, - { - "condition": "${ .jobStatus == \"FAILED\" }", - "transition": "job-failed", - "name": "failed" - } - ], - "defaultCondition": { - "transition": "wait-for-completion" - } - }, - { - "name": "job-succeeded", - "type": "operation", - "actionMode": "sequential", - "actions": [ - { - "name": "job-succeeded", - "functionRef": { - "refName": "report-job-suceeded", - "arguments": { - "name": "${ .jobuid }" - } - } - } - ], - "end": true - }, - { - "name": "job-failed", - "type": "operation", - "actionMode": "sequential", - "actions": [ - { - "name": "job-failed", - "functionRef": { - "refName": "report-job-failed", - "arguments": { - "name": "${ .jobuid }" - } - } - } - ], - "end": true - } - ] -}``` - - | -- -```yaml -id: jobmonitoring -version: 1.0.0 -specVersion: "0.8" -name: jobmonitoring -description: Monitor finished execution of a submitted job -start: submit-job -functions: - - name: submit-job - operation: http://myapis.org/monitorapi.json#doSubmit - - name: check-job-status - operation: http://myapis.org/monitorapi.json#checkStatus - - name: report-job-suceeded - operation: http://myapis.org/monitorapi.json#reportSucceeded - - name: report-job-failed - operation: http://myapis.org/monitorapi.json#reportFailure -states: - - name: submit-job - type: operation - actionMode: sequential - actions: - - name: submit-job - functionRef: - refName: submit-job - arguments: - name: ${ .job.name } - actionDataFilter: - results: ${ .jobuid } - stateDataFilter: - output: ${ .jobuid } - transition: wait-for-completion - - name: wait-for-completion - type: sleep - duration: PT5S - transition: get-job-status - - name: get-job-status - type: operation - actionMode: sequential - actions: - - name: get-job-status - functionRef: - refName: check-job-status - arguments: - name: ${ .jobuid } - actionDataFilter: - results: ${ .jobstatus } - stateDataFilter: - output: ${ .jobstatus } - transition: determine-completion - - name: determine-completion - type: switch - dataConditions: - - condition: ${ .jobStatus == "SUCCEEDED" } - transition: job-succeeded - name: succeed - - condition: ${ .jobStatus == "FAILED" } - transition: job-failed - name: failed - defaultCondition: - transition: wait-for-completion - - name: job-succeeded - type: operation - actionMode: sequential - actions: - - name: job-succeeded - functionRef: - refName: report-job-suceeded - arguments: - name: ${ .jobuid } - end: true - - name: job-failed - type: operation - actionMode: sequential - actions: - - name: job-failed - functionRef: - refName: report-job-failed - arguments: - name: ${ .jobuid } - end: true -``` - - | -
- -
- -#### Workflow Definition - -JSON | -YAML | -
---|---|
- -```json -{ - "id": "sendcloudeventonprovision", - "version": "1.0.0", - "specVersion": "0.8", - "name": "sendcloudeventonprovision", - "start": "provision-orders-state", - "events": [ - { - "name": "provisioning-complete-event", - "type": "provisionCompleteType", - "kind": "produced" - } - ], - "functions": [ - { - "name": "provision-order-function", - "operation": "http://myapis.org/provisioning.json#doProvision" - } - ], - "states": [ - { - "name": "provision-orders-state", - "type": "foreach", - "inputCollection": "${ .orders }", - "iterationParam": "singleorder", - "outputCollection": "${ .provisionedOrders }", - "actions": [ - { - "name": "provision-order-function", - "functionRef": { - "refName": "provision-order-function", - "arguments": { - "order": "${ .singleorder }" - } - } - } - ], - "end": { - "produceEvents": [ - { - "eventRef": "provisioning-complete-event", - "data": "${ .provisionedOrders }" - } - ] - } - } - ] -}``` - - | -- -```yaml -id: sendcloudeventonprovision -version: 1.0.0 -specVersion: "0.8" -name: sendcloudeventonprovision -start: provision-orders-state -events: - - name: provisioning-complete-event - type: provisionCompleteType - kind: produced -functions: - - name: provision-order-function - operation: http://myapis.org/provisioning.json#doProvision -states: - - name: provision-orders-state - type: foreach - inputCollection: ${ .orders } - iterationParam: singleorder - outputCollection: ${ .provisionedOrders } - actions: - - name: provision-order-function - functionRef: - refName: provision-order-function - arguments: - order: ${ .singleorder } - end: - produceEvents: - - eventRef: provisioning-complete-event - data: ${ .provisionedOrders } -``` - - | -
- -
- -#### Workflow Definition - -JSON | -YAML | -
---|---|
- -```json -{ - "id": "patientVitalsWorkflow", - "name": "patientVitalsWorkflow", - "version": "1.0.0", - "specVersion": "0.8", - "start": "monitor-vitals", - "events": [ - { - "name": "high-body-temperature", - "type": "org.monitor.highBodyTemp", - "source": "monitoringSource", - "correlation": [ - { - "contextAttributeName": "patientId" - } - ] - }, - { - "name": "high-blood-pressure", - "type": "org.monitor.highBloodPressure", - "source": "monitoringSource", - "correlation": [ - { - "contextAttributeName": "patientId" - } - ] - }, - { - "name": "high-respiration-rate", - "type": "org.monitor.highRespirationRate", - "source": "monitoringSource", - "correlation": [ - { - "contextAttributeName": "patientId" - } - ] - } - ], - "functions": [ - { - "name": "call-pulmonologist", - "operation": "http://myapis.org/patientapis.json#callPulmonologist" - }, - { - "name": "send-tylenol-order", - "operation": "http://myapis.org/patientapis.json#tylenolOrder" - }, - { - "name": "call-nurse", - "operation": "http://myapis.org/patientapis.json#callNurse" - } - ], - "states": [ - { - "name": "monitor-vitals", - "type": "event", - "exclusive": true, - "onEvents": [ - { - "eventRefs": [ - "high-body-temperature" - ], - "actions": [ - { - "name": "send-tylenol-order", - "functionRef": { - "refName": "send-tylenol-order", - "arguments": { - "patientid": "${ .patientId }" - } - } - } - ] - }, - { - "eventRefs": [ - "high-blood-pressure" - ], - "actions": [ - { - "name": "call-nurse", - "functionRef": { - "refName": "call-nurse", - "arguments": { - "patientid": "${ .patientId }" - } - } - } - ] - }, - { - "eventRefs": [ - "high-respiration-rate" - ], - "actions": [ - { - "name": "call-pulmonologist", - "functionRef": { - "refName": "call-pulmonologist", - "arguments": { - "patientid": "${ .patientId }" - } - } - } - ] - } - ], - "end": { - "terminate": true - } - } - ] -}``` - - | -- -```yaml -id: patientVitalsWorkflow -name: patientVitalsWorkflow -version: 1.0.0 -specVersion: "0.8" -start: monitor-vitals -events: - - name: high-body-temperature - type: org.monitor.highBodyTemp - source: monitoringSource - correlation: - - contextAttributeName: patientId - - name: high-blood-pressure - type: org.monitor.highBloodPressure - source: monitoringSource - correlation: - - contextAttributeName: patientId - - name: high-respiration-rate - type: org.monitor.highRespirationRate - source: monitoringSource - correlation: - - contextAttributeName: patientId -functions: - - name: call-pulmonologist - operation: http://myapis.org/patientapis.json#callPulmonologist - - name: send-tylenol-order - operation: http://myapis.org/patientapis.json#tylenolOrder - - name: call-nurse - operation: http://myapis.org/patientapis.json#callNurse -states: - - name: monitor-vitals - type: event - exclusive: true - onEvents: - - eventRefs: - - high-body-temperature - actions: - - name: send-tylenol-order - functionRef: - refName: send-tylenol-order - arguments: - patientid: ${ .patientId } - - eventRefs: - - high-blood-pressure - actions: - - name: call-nurse - functionRef: - refName: call-nurse - arguments: - patientid: ${ .patientId } - - eventRefs: - - high-respiration-rate - actions: - - name: call-pulmonologist - functionRef: - refName: call-pulmonologist - arguments: - patientid: ${ .patientId } - end: - terminate: true -``` - - | -
- -
- -#### Workflow Definition - -JSON | -YAML | -
---|---|
- -```json -{ - "id": "finalize-college-application", - "name": "finalizeCollegeApplication", - "version": "1.0.0", - "specVersion": "0.8", - "start": "finalize-application", - "events": [ - { - "name": "application-submitted", - "type": "org.application.submitted", - "source": "applicationsource", - "correlation": [ - { - "contextAttributeName": "applicantId" - } - ] - }, - { - "name": "sat-scores-received", - "type": "org.application.satscores", - "source": "applicationsource", - "correlation": [ - { - "contextAttributeName": "applicantId" - } - ] - }, - { - "name": "recommendation-letter-received", - "type": "org.application.recommendationLetter", - "source": "applicationsource", - "correlation": [ - { - "contextAttributeName": "applicantId" - } - ] - } - ], - "functions": [ - { - "name": "finalize-application-function", - "operation": "http://myapis.org/collegeapplicationapi.json#finalize" - } - ], - "states": [ - { - "name": "finalize-application", - "type": "event", - "exclusive": false, - "onEvents": [ - { - "eventRefs": [ - "application-submitted", - "sat-scores-received", - "recommendation-letter-received" - ], - "actions": [ - { - "name": "finalize-application", - "functionRef": { - "refName": "finalize-application-function", - "arguments": { - "student": "${ .applicantId }" - } - } - } - ] - } - ], - "end": { - "terminate": true - } - } - ] -}``` - - | -- -```yaml -id: finalize-college-application -name: finalizeCollegeApplication -version: 1.0.0 -specVersion: "0.8" -start: finalize-application -events: - - name: application-submitted - type: org.application.submitted - source: applicationsource - correlation: - - contextAttributeName: applicantId - - name: sat-scores-received - type: org.application.satscores - source: applicationsource - correlation: - - contextAttributeName: applicantId - - name: recommendation-letter-received - type: org.application.recommendationLetter - source: applicationsource - correlation: - - contextAttributeName: applicantId -functions: - - name: finalize-application-function - operation: http://myapis.org/collegeapplicationapi.json#finalize -states: - - name: finalize-application - type: event - exclusive: false - onEvents: - - eventRefs: - - application-submitted - - sat-scores-received - - recommendation-letter-received - actions: - - name: finalize-application - functionRef: - refName: finalize-application-function - arguments: - student: ${ .applicantId } - end: - terminate: true -``` - - | -
- -
- -#### Workflow Definition - -JSON | -YAML | -
---|---|
- -```json -{ - "id": "customercreditcheck", - "version": "1.0.0", - "specVersion": "0.8", - "name": "customercreditcheck", - "description": "Perform Customer Credit Check", - "start": "check-credit", - "functions": [ - { - "name": "credit-check-function", - "operation": "http://myapis.org/creditcheckapi.json#doCreditCheck" - }, - { - "name": "send-rejection-email-function", - "operation": "http://myapis.org/creditcheckapi.json#rejectionEmail" - } - ], - "events": [ - { - "name": "credit-check-completed-event", - "type": "creditCheckCompleteType", - "source": "creditCheckSource", - "correlation": [ - { - "contextAttributeName": "customerId" - } - ] - } - ], - "states": [ - { - "name": "check-credit", - "type": "callback", - "action": { - "name": "check-credit", - "functionRef": { - "refName": "call-credit-check-microservice", - "arguments": { - "customer": "${ .customer }" - } - } - }, - "eventRef": "credit-check-completed-event", - "timeouts": { - "stateExecTimeout": "PT15M" - }, - "transition": "evaluate-decision" - }, - { - "name": "evaluate-decision", - "type": "switch", - "dataConditions": [ - { - "condition": "${ .creditCheck | .decision == \"Approved\" }", - "transition": "start-application", - "name": "start-application" - }, - { - "condition": "${ .creditCheck | .decision == \"Denied\" }", - "transition": "reject-application", - "name": "reject-application" - } - ], - "defaultCondition": { - "transition": "reject-application" - } - }, - { - "name": "start-application", - "type": "operation", - "actions": [ - { - "name": "start-application", - "subFlowRef": "startApplicationWorkflowId" - } - ], - "end": true - }, - { - "name": "reject-application", - "type": "operation", - "actionMode": "sequential", - "actions": [ - { - "name": "reject-application", - "functionRef": { - "refName": "send-rejection-email-function", - "arguments": { - "applicant": "${ .customer }" - } - } - } - ], - "end": true - } - ] -}``` - - | -- -```yaml -id: customercreditcheck -version: 1.0.0 -specVersion: "0.8" -name: customercreditcheck -description: Perform Customer Credit Check -start: check-credit -functions: - - name: credit-check-function - operation: http://myapis.org/creditcheckapi.json#doCreditCheck - - name: send-rejection-email-function - operation: http://myapis.org/creditcheckapi.json#rejectionEmail -events: - - name: credit-check-completed-event - type: creditCheckCompleteType - source: creditCheckSource - correlation: - - contextAttributeName: customerId -states: - - name: check-credit - type: callback - action: - name: check-credit - functionRef: - refName: call-credit-check-microservice - arguments: - customer: ${ .customer } - eventRef: credit-check-completed-event - timeouts: - stateExecTimeout: PT15M - transition: evaluate-decision - - name: evaluate-decision - type: switch - dataConditions: - - condition: ${ .creditCheck | .decision == "Approved" } - transition: start-application - name: start-application - - condition: ${ .creditCheck | .decision == "Denied" } - transition: reject-application - name: reject-application - defaultCondition: - transition: reject-application - - name: start-application - type: operation - actions: - - name: start-application - subFlowRef: startApplicationWorkflowId - end: true - - name: reject-application - type: operation - actionMode: sequential - actions: - - name: reject-application - functionRef: - refName: send-rejection-email-function - arguments: - applicant: ${ .customer } - end: true -``` - - | -
- -
- -#### Workflow Definition - -JSON | -YAML | -
---|---|
- -```json -{ - "id": "handle-car-auction-bid", - "version": "1.0.0", - "specVersion": "0.8", - "name": "handle-car-auction-bid", - "description": "Store a single bid whole the car auction is active", - "start": { - "stateName": "store-car-auction-bid", - "schedule": "R/PT2H" - }, - "functions": [ - { - "name": "store-bid-function", - "operation": "http://myapis.org/carauctionapi.json#storeBid" - } - ], - "events": [ - { - "name": "car-bid-event", - "type": "carBidMadeType", - "source": "carBidEventSource" - } - ], - "states": [ - { - "name": "store-car-auction-bid", - "type": "event", - "exclusive": true, - "onEvents": [ - { - "eventRefs": [ - "car-bid-event" - ], - "actions": [ - { - "name": "car-bid-event", - "functionRef": { - "refName": "store-bid-function", - "arguments": { - "bid": "${ .bid }" - } - } - } - ] - } - ], - "end": true - } - ] -}``` - - | -- -```yaml -id: handle-car-auction-bid -version: 1.0.0 -specVersion: "0.8" -name: handle-car-auction-bid -description: Store a single bid whole the car auction is active -start: - stateName: store-car-auction-bid - schedule: R/PT2H -functions: - - name: store-bid-function - operation: http://myapis.org/carauctionapi.json#storeBid -events: - - name: car-bid-event - type: carBidMadeType - source: carBidEventSource -states: - - name: store-car-auction-bid - type: event - exclusive: true - onEvents: - - eventRefs: - - car-bid-event - actions: - - name: car-bid-event - functionRef: - refName: store-bid-function - arguments: - bid: ${ .bid } - end: true -``` - - | -
- -
- -#### Workflow Definition - -JSON | -YAML | -
---|---|
- -```json -{ - "id": "check-inbox", - "name": "check-inbox", - "version": "1.0.0", - "specVersion": "0.8", - "description": "Periodically Check Inbox", - "start": { - "stateName": "check-inbox", - "schedule": { - "cron": "0 0/15 * * * ?" - } - }, - "functions": [ - { - "name": "check-inbox-function", - "operation": "http://myapis.org/inboxapi.json#checkNewMessages" - }, - { - "name": "send-text-function", - "operation": "http://myapis.org/inboxapi.json#sendText" - } - ], - "states": [ - { - "name": "check-inbox", - "type": "operation", - "actionMode": "sequential", - "actions": [ - { - "name":"check-inbox", - "functionRef": "check-inbox-function" - } - ], - "transition": "send-text-for-high-priority" - }, - { - "name": "send-text-for-high-priority", - "type": "foreach", - "inputCollection": "${ .messages }", - "iterationParam": "singlemessage", - "actions": [ - { - "name": "send-text-for-high-priority", - "functionRef": { - "refName": "send-text-function", - "arguments": { - "message": "${ .singlemessage }" - } - } - } - ], - "end": true - } - ] -}``` - - | -- -```yaml -id: check-inbox -name: check-inbox -version: 1.0.0 -specVersion: "0.8" -description: Periodically Check Inbox -start: - stateName: check-inbox - schedule: - cron: 0 0/15 * * * ? -functions: - - name: check-inbox-function - operation: http://myapis.org/inboxapi.json#checkNewMessages - - name: send-text-function - operation: http://myapis.org/inboxapi.json#sendText -states: - - name: check-inbox - type: operation - actionMode: sequential - actions: - - name: check-inbox - functionRef: check-inbox-function - transition: send-text-for-high-priority - - name: send-text-for-high-priority - type: foreach - inputCollection: ${ .messages } - iterationParam: singlemessage - actions: - - name: send-text-for-high-priority - functionRef: - refName: send-text-function - arguments: - message: ${ .singlemessage } - end: true -``` - - | -
- -
- -#### Workflow Definition - -JSON | -YAML | -
---|---|
- -```json -{ - "id": "vet-appointment-workflow", - "name": "vet-appointment-workflow", - "description": "Vet service call via events", - "version": "1.0.0", - "specVersion": "0.8", - "start": "make-vet-appointment-state", - "events": [ - { - "name": "make-vet-appointment", - "source": "VetServiceSource", - "type": "events.vet.appointments", - "kind": "produced" - }, - { - "name": "vet-appointment-info", - "source": "VetServiceSource", - "type": "events.vet.appointments", - "kind": "consumed" - } - ], - "states": [ - { - "name": "make-vet-appointment-state", - "type": "operation", - "actions": [ - { - "name": "make-appointment-action", - "eventRef": { - "produceEventRef": "make-vet-appointment", - "data": "${ .patientInfo }", - "consumeEventRef": "vet-appointment-info" - }, - "actionDataFilter": { - "results": "${ .appointmentInfo }" - } - } - ], - "timeouts": { - "actionExecTimeout": "PT15M" - }, - "end": true - } - ] -}``` - - | -- -```yaml -id: vet-appointment-workflow -name: vet-appointment-workflow -description: Vet service call via events -version: 1.0.0 -specVersion: "0.8" -start: make-vet-appointment-state -events: - - name: make-vet-appointment - source: VetServiceSource - type: events.vet.appointments - kind: produced - - name: vet-appointment-info - source: VetServiceSource - type: events.vet.appointments - kind: consumed -states: - - name: make-vet-appointment-state - type: operation - actions: - - name: make-appointment-action - eventRef: - produceEventRef: make-vet-appointment - data: ${ .patientInfo } - consumeEventRef: vet-appointment-info - actionDataFilter: - results: ${ .appointmentInfo } - timeouts: - actionExecTimeout: PT15M - end: true -``` - - | -
- -
- -#### Workflow Definitions - -JSON | -YAML | -
---|---|
- -```json -{ - "id": "paymentconfirmation", - "version": "1.0.0", - "specVersion": "0.8", - "name": "paymentconfirmation", - "description": "Performs Payment Confirmation", - "functions": "file://functiondefs.json", - "events": "file://eventdefs.yml", - "states": [ - { - "name": "payment-received", - "type": "event", - "onEvents": [ - { - "eventRefs": [ - "payment-received-event" - ], - "actions": [ - { - "name": "checkfunds", - "functionRef": { - "refName": "check-funds-availability", - "arguments": { - "account": "${ .accountId }", - "paymentamount": "${ .payment.amount }" - } - } - } - ] - } - ], - "transition": "confirm-based-on-funds" - }, - { - "name": "confirm-based-on-funds", - "type": "switch", - "dataConditions": [ - { - "condition": "${ .funds | .available == \"true\" }", - "transition": "send-payment-success", - "name": "success" - }, - { - "condition": "${ .funds | .available == \"false\" }", - "transition": "send-insufficient-results", - "name": "failed" - } - ], - "defaultCondition": { - "transition": "send-payment-success" - } - }, - { - "name": "send-payment-success", - "type": "operation", - "actions": [ - { - "name": "send-payment-success", - "functionRef": { - "refName": "send-success-email", - "arguments": { - "applicant": "${ .customer }" - } - } - } - ], - "end": { - "produceEvents": [ - { - "eventRef": "confirmation-completed-event", - "data": "${ .payment }" - } - ] - } - }, - { - "name": "send-insufficient-results", - "type": "operation", - "actions": [ - { - "name": "send-insufficient-results", - "functionRef": { - "refName": "send-insufficient-funds-email", - "arguments": { - "applicant": "${ .customer }" - } - } - } - ], - "end": { - "produceEvents": [ - { - "eventRef": "confirmation-completed-event", - "data": "${ .payment }" - } - ] - } - } - ] -}``` - - | -- -```yaml -id: paymentconfirmation -version: 1.0.0 -specVersion: "0.8" -name: paymentconfirmation -description: Performs Payment Confirmation -functions: file://functiondefs.json -events: file://eventdefs.yml -states: - - name: payment-received - type: event - onEvents: - - eventRefs: - - payment-received-event - actions: - - name: checkfunds - functionRef: - refName: check-funds-availability - arguments: - account: ${ .accountId } - paymentamount: ${ .payment.amount } - transition: confirm-based-on-funds - - name: confirm-based-on-funds - type: switch - dataConditions: - - condition: ${ .funds | .available == "true" } - transition: send-payment-success - name: success - - condition: ${ .funds | .available == "false" } - transition: send-insufficient-results - name: failed - defaultCondition: - transition: send-payment-success - - name: send-payment-success - type: operation - actions: - - name: send-payment-success - functionRef: - refName: send-success-email - arguments: - applicant: ${ .customer } - end: - produceEvents: - - eventRef: confirmation-completed-event - data: ${ .payment } - - name: send-insufficient-results - type: operation - actions: - - name: send-insufficient-results - functionRef: - refName: send-insufficient-funds-email - arguments: - applicant: ${ .customer } - end: - produceEvents: - - eventRef: confirmation-completed-event - data: ${ .payment } -``` - - | -
- -
- -#### Workflow Definition - -JSON | -YAML | -
---|---|
- -```json -{ - "id": "patientonboarding", - "name": "patientonboarding", - "version": "1.0.0", - "specVersion": "0.8", - "start": "onboard", - "states": [ - { - "name": "onboard", - "type": "event", - "onEvents": [ - { - "eventRefs": [ - "new-patient-event" - ], - "actions": [ - { - "name": "store-patient", - "functionRef": "store-patient", - "retryRef": "services-not-available-retry-strategy", - "retryableErrors": [ - "service-not-available" - ] - }, - { - "name": "assign-doctor", - "functionRef": "assign-doctor", - "retryRef": "services-not-available-retry-strategy", - "retryableErrors": [ - "service-not-available" - ] - }, - { - "name": "schedule-appt", - "functionRef": "schedule-appt", - "retryRef": "services-not-available-retry-strategy", - "retryableErrors": [ - "service-not-available" - ] - } - ] - } - ], - "onErrors": [ - { - "errorRef": "service-not-available", - "end": true - } - ], - "end": true - } - ], - "events": [ - { - "name": "store-patient", - "type": "new.patients.event", - "source": "newpatient/+" - } - ], - "functions": [ - { - "name": "store-new-patient-info", - "operation": "api/services.json#addPatient" - }, - { - "name": "assign-doctor", - "operation": "api/services.json#assignDoctor" - }, - { - "name": "schedule-appt", - "operation": "api/services.json#scheduleAppointment" - } - ], - "errors": [ - { - "name": "service-not-available", - "code": "503" - } - ], - "retries": [ - { - "name": "services-not-available-retry-strategy", - "delay": "PT3S", - "maxAttempts": 10 - } - ] -}``` - - | -- -```yaml -id: patientonboarding -name: patientonboarding -version: 1.0.0 -specVersion: "0.8" -start: onboard -states: - - name: onboard - type: event - onEvents: - - eventRefs: - - new-patient-event - actions: - - name: store-patient - functionRef: store-patient - retryRef: services-not-available-retry-strategy - retryableErrors: - - service-not-available - - name: assign-doctor - functionRef: assign-doctor - retryRef: services-not-available-retry-strategy - retryableErrors: - - service-not-available - - name: schedule-appt - functionRef: schedule-appt - retryRef: services-not-available-retry-strategy - retryableErrors: - - service-not-available - onErrors: - - errorRef: service-not-available - end: true - end: true -events: - - name: store-patient - type: new.patients.event - source: newpatient/+ -functions: - - name: store-new-patient-info - operation: api/services.json#addPatient - - name: assign-doctor - operation: api/services.json#assignDoctor - - name: schedule-appt - operation: api/services.json#scheduleAppointment -errors: - - name: service-not-available - code: "503" -retries: - - name: services-not-available-retry-strategy - delay: PT3S - maxAttempts: 10 -``` - - | -
- -
- -#### Workflow Definition - -JSON | -YAML | -
---|---|
- -```json -{ - "id": "order", - "name": "order", - "description": "Purchase Order Workflow", - "version": "1.0.0", - "specVersion": "0.8", - "start": "start-new-order", - "timeouts": { - "workflowExecTimeout": { - "duration": "PT30D", - "runBefore": "CancelOrder" - } - }, - "states": [ - { - "name": "start-new-order", - "type": "event", - "onEvents": [ - { - "eventRefs": [ - "order-created-event" - ], - "actions": [ - { - "name": "log-new-order-created", - "functionRef": { - "refName": "log-new-order-created" - } - } - ] - } - ], - "transition": { - "nextState": "wait-for-order-confirmation" - } - }, - { - "name": "wait-for-order-confirmation", - "type": "event", - "onEvents": [ - { - "eventRefs": [ - "order-confirmed-event" - ], - "actions": [ - { - "name": "log-order-confirmed", - "functionRef": { - "refName": "log-order-confirmed" - } - } - ] - } - ], - "transition": { - "nextState": "wait-order-shipped" - } - }, - { - "name": "wait-order-shipped", - "type": "event", - "onEvents": [ - { - "eventRefs": [ - "shipment-sent-event" - ], - "actions": [ - { - "name": "log-order-shipped", - "functionRef": { - "refName": "log-order-shipped" - } - } - ] - } - ], - "end": { - "terminate": true, - "produceEvents": [ - { - "eventRef": "order-finished-event" - } - ] - } - }, - { - "name": "cancel-order", - "type": "operation", - "actions": [ - { - "name": "cancel-order", - "functionRef": { - "refName": "cancel-order" - } - } - ], - "end": { - "terminate": true, - "produceEvents": [ - { - "eventRef": "order-cancelled-event" - } - ] - } - } - ], - "events": [ - { - "name": "order-created-event", - "type": "my.company.orders", - "source": "/orders/new", - "correlation": [ - { - "contextAttributeName": "orderid" - } - ] - }, - { - "name": "order-confirmed-event", - "type": "my.company.orders", - "source": "/orders/confirmed", - "correlation": [ - { - "contextAttributeName": "orderid" - } - ] - }, - { - "name": "shipment-sent-event", - "type": "my.company.orders", - "source": "/orders/shipped", - "correlation": [ - { - "contextAttributeName": "orderid" - } - ] - }, - { - "name": "order-finished-event", - "type": "my.company.orders", - "kind": "produced" - }, - { - "name": "order-cancelled-event", - "type": "my.company.orders", - "kind": "produced" - } - ], - "functions": [ - { - "name": "log-new-order-created", - "operation": "http.myorg.io/ordersservices.json#logcreated" - }, - { - "name": "log-order-confirmed", - "operation": "http.myorg.io/ordersservices.json#logconfirmed" - }, - { - "name": "log-order-shipped", - "operation": "http.myorg.io/ordersservices.json#logshipped" - }, - { - "name": "cancel-order", - "operation": "http.myorg.io/ordersservices.json#calcelorder" - } - ] -}``` - - | -- -```yaml -id: order -name: order -description: Purchase Order Workflow -version: 1.0.0 -specVersion: "0.8" -start: start-new-order -timeouts: - workflowExecTimeout: - duration: PT30D - runBefore: CancelOrder -states: - - name: start-new-order - type: event - onEvents: - - eventRefs: - - order-created-event - actions: - - name: log-new-order-created - functionRef: - refName: log-new-order-created - transition: - nextState: wait-for-order-confirmation - - name: wait-for-order-confirmation - type: event - onEvents: - - eventRefs: - - order-confirmed-event - actions: - - name: log-order-confirmed - functionRef: - refName: log-order-confirmed - transition: - nextState: wait-order-shipped - - name: wait-order-shipped - type: event - onEvents: - - eventRefs: - - shipment-sent-event - actions: - - name: log-order-shipped - functionRef: - refName: log-order-shipped - end: - terminate: true - produceEvents: - - eventRef: order-finished-event - - name: cancel-order - type: operation - actions: - - name: cancel-order - functionRef: - refName: cancel-order - end: - terminate: true - produceEvents: - - eventRef: order-cancelled-event -events: - - name: order-created-event - type: my.company.orders - source: /orders/new - correlation: - - contextAttributeName: orderid - - name: order-confirmed-event - type: my.company.orders - source: /orders/confirmed - correlation: - - contextAttributeName: orderid - - name: shipment-sent-event - type: my.company.orders - source: /orders/shipped - correlation: - - contextAttributeName: orderid - - name: order-finished-event - type: my.company.orders - kind: produced - - name: order-cancelled-event - type: my.company.orders - kind: produced -functions: - - name: log-new-order-created - operation: http.myorg.io/ordersservices.json#logcreated - - name: log-order-confirmed - operation: http.myorg.io/ordersservices.json#logconfirmed - - name: log-order-shipped - operation: http.myorg.io/ordersservices.json#logshipped - - name: cancel-order - operation: http.myorg.io/ordersservices.json#calcelorder -``` - - | -
- -
- -#### Workflow Definition - -JSON | -YAML | -
---|---|
- -```json -{ - "id": "roomreadings", - "name": "roomreadings", - "description": "Room Temp and Humidity Workflow", - "version": "1.0.0", - "specVersion": "0.8", - "start": "consume-reading", - "timeouts": { - "workflowExecTimeout": { - "duration": "PT1H", - "runBefore": "generate-report" - } - }, - "keepActive": true, - "states": [ - { - "name": "consume-reading", - "type": "event", - "onEvents": [ - { - "eventRefs": [ - "temperature-event", - "humidity-event" - ], - "actions": [ - { - "name": "log-reading", - "functionRef": { - "refName": "log-reading" - } - } - ], - "eventDataFilter": { - "toStateData": "${ .readings }" - } - } - ], - "end": true - }, - { - "name": "generate-report", - "type": "operation", - "actions": [ - { - "name": "generate-report", - "functionRef": { - "refName": "produce-report", - "arguments": { - "data": "${ .readings }" - } - } - } - ], - "end": { - "terminate": true - } - } - ], - "events": [ - { - "name": "temperature-event", - "type": "my.home.sensors", - "source": "/home/rooms/+", - "correlation": [ - { - "contextAttributeName": "roomId" - } - ] - }, - { - "name": "humidity-event", - "type": "my.home.sensors", - "source": "/home/rooms/+", - "correlation": [ - { - "contextAttributeName": "roomId" - } - ] - } - ], - "functions": [ - { - "name": "log-reading", - "operation": "http.myorg.io/ordersservices.json#logreading" - }, - { - "name": "produce-report", - "operation": "http.myorg.io/ordersservices.json#produceReport" - } - ] -}``` - - | -- -```yaml -id: roomreadings -name: roomreadings -description: Room Temp and Humidity Workflow -version: 1.0.0 -specVersion: "0.8" -start: consume-reading -timeouts: - workflowExecTimeout: - duration: PT1H - runBefore: generate-report -keepActive: true -states: - - name: consume-reading - type: event - onEvents: - - eventRefs: - - temperature-event - - humidity-event - actions: - - name: log-reading - functionRef: - refName: log-reading - eventDataFilter: - toStateData: ${ .readings } - end: true - - name: generate-report - type: operation - actions: - - name: generate-report - functionRef: - refName: produce-report - arguments: - data: ${ .readings } - end: - terminate: true -events: - - name: temperature-event - type: my.home.sensors - source: /home/rooms/+ - correlation: - - contextAttributeName: roomId - - name: humidity-event - type: my.home.sensors - source: /home/rooms/+ - correlation: - - contextAttributeName: roomId -functions: - - name: log-reading - operation: http.myorg.io/ordersservices.json#logreading - - name: produce-report - operation: http.myorg.io/ordersservices.json#produceReport -``` - - | -
- -
- -#### Workflow Definition - -We fist define our top-level workflow for this example: - -JSON | -YAML | -
---|---|
- -```json -{ - "id": "checkcarvitals", - "name": "checkcarvitals", - "description": "Check Car Vitals Workflow", - "version": "1.0.0", - "specVersion": "0.8", - "start": "when-car-is-on", - "states": [ - { - "name": "when-car-is-on", - "type": "event", - "onEvents": [ - { - "eventRefs": [ - "car-turned-on-event" - ] - } - ], - "transition": "do-car-vital-checks" - }, - { - "name": "do-car-vital-checks", - "type": "operation", - "actions": [ - { - "name": "do-car-vital-checks", - "subFlowRef": "vitalscheck", - "sleep": { - "after": "PT1S" - } - } - ], - "transition": "check-continue-vital-checks" - }, - { - "name": "check-continue-vital-checks", - "type": "switch", - "eventConditions": [ - { - "name": "car-turned-off-condition", - "eventRef": "car-turned-off-event", - "end": true - } - ], - "defaultCondition": { - "transition": "do-car-vital-checks" - } - } - ], - "events": [ - { - "name": "car-turned-on-event", - "type": "car.events", - "source": "my/car" - }, - { - "name": "car-turned-off-event", - "type": "car.events", - "source": "my/car" - } - ] -}``` - - | -- -```yaml -id: checkcarvitals -name: checkcarvitals -description: Check Car Vitals Workflow -version: 1.0.0 -specVersion: "0.8" -start: when-car-is-on -states: - - name: when-car-is-on - type: event - onEvents: - - eventRefs: - - car-turned-on-event - transition: do-car-vital-checks - - name: do-car-vital-checks - type: operation - actions: - - name: do-car-vital-checks - subFlowRef: vitalscheck - sleep: - after: PT1S - transition: check-continue-vital-checks - - name: check-continue-vital-checks - type: switch - eventConditions: - - name: car-turned-off-condition - eventRef: car-turned-off-event - end: true - defaultCondition: - transition: do-car-vital-checks -events: - - name: car-turned-on-event - type: car.events - source: my/car - - name: car-turned-off-event - type: car.events - source: my/car -``` - - | -
JSON | -YAML | -|
---|---|---|
- -```json -{ - "id": "vitalscheck", - "name": "vitalscheck", - "description": "Car Vitals Check", - "version": "1.0.0", - "specVersion": "0.8", - "start": "check-vitals", - "states": [ - { - "name": "check-vitals", - "type": "operation", - "actions": [ - { - "name": "check-tire-pressure", - "functionRef": "check-tire-pressure" - }, - { - "name": "check-oil-pressure", - "functionRef": "check-oil-pressure" - }, - { - "name": "check-coolant-level", - "functionRef": "check-coolant-level" - }, - { - "name": "check-battery", - "functionRef": "check-battery" - } - ], - "end": { - "produceEvents": [ - { - "eventRef": "display-checks-on-dashboard", - "data": "${ .evaluations }" - } - ] - } - } - ], - "functions": [ - { - "name": "check-tire-pressure", - "operation": "mycarservices.json#checktirepressure" - }, - { - "name": "check-oil-pressure", - "operation": "mycarservices.json#checkoilpressure" - }, - { - "name": "check-coolant-level", - "operation": "mycarservices.json#checkcoolantlevel" - }, - { - "name": "check-battery", - "operation": "mycarservices.json#checkbattery" - } - ] -}``` - - | -- -```yaml -id: vitalscheck -name: vitalscheck -description: Car Vitals Check -version: 1.0.0 -specVersion: "0.8" -start: check-vitals -states: - - name: check-vitals - type: operation - actions: - - name: check-tire-pressure - functionRef: check-tire-pressure - - name: check-oil-pressure - functionRef: check-oil-pressure - - name: check-coolant-level - functionRef: check-coolant-level - - name: check-battery - functionRef: check-battery - end: - produceEvents: - - eventRef: display-checks-on-dashboard - data: ${ .evaluations } -functions: - - name: check-tire-pressure - operation: mycarservices.json#checktirepressure - - name: check-oil-pressure - operation: mycarservices.json#checkoilpressure - - name: check-coolant-level - operation: mycarservices.json#checkcoolantlevel - - name: check-battery - operation: mycarservices.json#checkbattery -``` - - | -- -```yaml -id: vitalscheck -name: Car Vitals Check -version: '1.0.0' -specVersion: '0.8' -start: CheckVitals -states: - - name: CheckVitals - type: operation - actions: - - functionRef: Check Tire Pressure - - functionRef: Check Oil Pressure - - functionRef: Check Coolant Level - - functionRef: Check Battery - end: - produceEvents: - - eventRef: DisplayChecksOnDashboard - data: "${ .evaluations }" -functions: - - name: checkTirePressure - operation: mycarservices.json#checktirepressure - - name: checkOilPressure - operation: mycarservices.json#checkoilpressure - - name: checkCoolantLevel - operation: mycarservices.json#checkcoolantlevel - - name: checkBattery - operation: mycarservices.json#checkbattery -``` - - | -
- -
- -#### Workflow Definition - -JSON | -YAML | -
---|---|
- -```json -{ - "id": "booklending", - "name": "booklending", - "description": "Book Lending Workflow", - "version": "1.0.0", - "specVersion": "0.8", - "start": "book-lending-request", - "states": [ - { - "name": "book-lending-request", - "type": "event", - "onEvents": [ - { - "eventRefs": [ - "book-lending-request-event" - ] - } - ], - "transition": "get-book-status" - }, - { - "name": "get-book-status", - "type": "operation", - "actions": [ - { - "name": "get-book-status", - "functionRef": { - "refName": "get-status-for-book", - "arguments": { - "bookid": "${ .book.id }" - } - } - } - ], - "transition": "book-status-decision" - }, - { - "name": "book-status-decision", - "type": "switch", - "dataConditions": [ - { - "name": "book-is-on-loan", - "condition": "${ .book.status == \"onloan\" }", - "transition": "report-status-to-lender" - }, - { - "name": "check-is-available", - "condition": "${ .book.status == \"available\" }", - "transition": "check-out-book" - } - ], - "defaultCondition": { - "end": true - } - }, - { - "name": "report-status-to-lender", - "type": "operation", - "actions": [ - { - "name": "report-status-to-lender", - "functionRef": { - "refName": "send-status-to-lender", - "arguments": { - "bookid": "${ .book.id }", - "message": "Book ${ .book.title } is already on loan" - } - } - } - ], - "transition": "wait-for-lender-response" - }, - { - "name": "wait-for-lender-response", - "type": "switch", - "eventConditions": [ - { - "name": "hold-book", - "eventRef": "hold-book-event", - "transition": "request-hold" - }, - { - "name": "decline-book-hold", - "eventRef": "decline-hold-event", - "transition": "cancel-request" - } - ], - "defaultCondition": { - "end": true - } - }, - { - "name": "request-hold", - "type": "operation", - "actions": [ - { - "name": "request-hold", - "functionRef": { - "refName": "request-hold-for-lender", - "arguments": { - "bookid": "${ .book.id }", - "lender": "${ .lender }" - } - } - } - ], - "transition": "sleep-two-weeks" - }, - { - "name": "cancel-request", - "type": "operation", - "actions": [ - { - "name": "cancel-request", - "functionRef": { - "refName": "cancel-hold-request-for-lender", - "arguments": { - "bookid": "${ .book.id }", - "lender": "${ .lender }" - } - } - } - ], - "transition": "sleep-two-weeks" - }, - { - "name": "sleep-two-weeks", - "type": "sleep", - "duration": "PT2W", - "transition": "get-book-status" - }, - { - "name": "check-out-book", - "type": "operation", - "actions": [ - { - "name": "check-out-book", - "functionRef": { - "refName": "check-out-book-with-id", - "arguments": { - "bookid": "${ .book.id }" - } - } - }, - { - "name": "notify-lender-for-checkout", - "functionRef": { - "refName": "notify-lender-for-checkout", - "arguments": { - "bookid": "${ .book.id }", - "lender": "${ .lender }" - } - } - } - ], - "end": true - } - ], - "functions": "file://books/lending/functions.json", - "events": "file://books/lending/events.json" -}``` - - | -- -```yaml -id: booklending -name: booklending -description: Book Lending Workflow -version: 1.0.0 -specVersion: "0.8" -start: book-lending-request -states: - - name: book-lending-request - type: event - onEvents: - - eventRefs: - - book-lending-request-event - transition: get-book-status - - name: get-book-status - type: operation - actions: - - name: get-book-status - functionRef: - refName: get-status-for-book - arguments: - bookid: ${ .book.id } - transition: book-status-decision - - name: book-status-decision - type: switch - dataConditions: - - name: book-is-on-loan - condition: ${ .book.status == "onloan" } - transition: report-status-to-lender - - name: check-is-available - condition: ${ .book.status == "available" } - transition: check-out-book - defaultCondition: - end: true - - name: report-status-to-lender - type: operation - actions: - - name: report-status-to-lender - functionRef: - refName: send-status-to-lender - arguments: - bookid: ${ .book.id } - message: Book ${ .book.title } is already on loan - transition: wait-for-lender-response - - name: wait-for-lender-response - type: switch - eventConditions: - - name: hold-book - eventRef: hold-book-event - transition: request-hold - - name: decline-book-hold - eventRef: decline-hold-event - transition: cancel-request - defaultCondition: - end: true - - name: request-hold - type: operation - actions: - - name: request-hold - functionRef: - refName: request-hold-for-lender - arguments: - bookid: ${ .book.id } - lender: ${ .lender } - transition: sleep-two-weeks - - name: cancel-request - type: operation - actions: - - name: cancel-request - functionRef: - refName: cancel-hold-request-for-lender - arguments: - bookid: ${ .book.id } - lender: ${ .lender } - transition: sleep-two-weeks - - name: sleep-two-weeks - type: sleep - duration: PT2W - transition: get-book-status - - name: check-out-book - type: operation - actions: - - name: check-out-book - functionRef: - refName: check-out-book-with-id - arguments: - bookid: ${ .book.id } - - name: notify-lender-for-checkout - functionRef: - refName: notify-lender-for-checkout - arguments: - bookid: ${ .book.id } - lender: ${ .lender } - end: true -functions: file://books/lending/functions.json -events: file://books/lending/events.json -``` - - | -
- -
- -#### Workflow Definition - -JSON | -YAML | -
---|---|
- -```json -{ - "id": "fillglassofwater", - "name": "fillglassofwater", - "description": "Fill glass of water workflow", - "version": "1.0.0", - "specVersion": "0.8", - "start": "check-if-full", - "functions": [ - { - "name": "increment-current-count-function", - "type": "expression", - "operation": ".counts.current += 1 | .counts.current" - } - ], - "states": [ - { - "name": "check-if-full", - "type": "switch", - "dataConditions": [ - { - "name": "need-to-fill-more", - "condition": "${ .counts.current < .counts.max }", - "transition": "add-water" - }, - { - "name": "glass-full", - "condition": ".counts.current >= .counts.max", - "end": true - } - ], - "defaultCondition": { - "end": true - } - }, - { - "name": "add-water", - "type": "operation", - "actions": [ - { - "name": "add-water", - "functionRef": "increment-current-count-function", - "actionDataFilter": { - "toStateData": ".counts.current" - } - } - ], - "transition": "check-if-full" - } - ] -}``` - - | -- -```yaml -id: fillglassofwater -name: fillglassofwater -description: Fill glass of water workflow -version: 1.0.0 -specVersion: "0.8" -start: check-if-full -functions: - - name: increment-current-count-function - type: expression - operation: .counts.current += 1 | .counts.current -states: - - name: check-if-full - type: switch - dataConditions: - - name: need-to-fill-more - condition: ${ .counts.current < .counts.max } - transition: add-water - - name: glass-full - condition: .counts.current >= .counts.max - end: true - defaultCondition: - end: true - - name: add-water - type: operation - actions: - - name: add-water - functionRef: increment-current-count-function - actionDataFilter: - toStateData: .counts.current - transition: check-if-full -``` - - | -
- -
- -Our workflow starts with the "Place Order" [Subflow](../specification.md#SubFlow-Action), which is responsible -to send the received order to the requested restaurant and the estimated order ETA. -We then wait for the ETA time when our workflow should go into the "Deliver Order" SubFlow, responsible -for dispatching a Courier and sending her/him off to pick up the order. Once the order is picked up, the Courier needs to deliver the order to the customer. -After the order has been delivered to the customer, our workflow needs to charge the customer. - -Our workflow needs to communicate with three services during its execution, namely the Order, Delivery, and -the Payment services. - -For the sake of the example, we assume that our workflow can communicate to the Order and Delivery services via REST and the Payment service via gRPC. -Let's start by defining an example CloudEvent which triggers an instance of our workflow. -This event can be sent by a web UI, for example, or be pushed onto a Kafka/MQTT topic to start our order workflow. - -```json -{ - "specversion": "1.0", - "type": "org.orders", - "source": "/orders/", - "subject": "Food Order", - "id": "A234-1234-1234", - "time": "2021-03-05T17:31:00Z", - "orderid": "ORDER-12345", - "data": { - "id": "ORDER-12345", - "customerId": "CUSTOMER-12345", - "status": [], - "order": { - "restaurantId": "RESTAURANT-54321", - "items": [ - { - "itemId": "ITEM-8765", - "amount": 1, - "addons": "" - } - ] - }, - "delivery":{ - "address": "1234 MyStreet, MyCountry", - "type": "contactless", - "requestedTime": "ASAP", - "location": "Front door", - "instructions": "" - } - } -} -``` - -Note the `orderid` CloudEvent context attribute, which contains the unique ID of the order specified in this event. [Event correlation](../specification.md#Correlation-Definition) is done against CE context attributes, and as such, to be able -to correlate multiple order events to the same order id, it needs to be part of the CE context attributes, and -not its data (payload). - -Now let's start defining our workflow. For the sake of this example, let's define our function and event definitions -as separate YAML files (and then reference them inside our workflow definition). This is useful in cases -when you want to reuse them between multiple workflow definitions. - -#### Workflow Event Definition - -``` yaml -events: -- name: Food Order Event - source: "/orders/" - type: org.orders - correlation: - - contextAttributeName: orderid -- name: ETA Deadline Event - source: "/orderseta" - type: org.orders.eta - correlation: - - contextAttributeName: orderid -- name: Order Picked Up Event - source: "/orderspickup" - type: org.orders.delivery - correlation: - - contextAttributeName: orderid -- name: Order Delievered Event - source: "/orderdelivery" - type: org.orders.delivery - correlation: - - contextAttributeName: orderid -``` - -#### Workflow Function Definition - -``` yaml -functions: -- name: Submit Order Function - operation: http://myorderservice.org/orders.json#submit -- name: Get Order ETA Function - operation: http://myorderservice.org/orders.json#orderETA -- name: Dispatch Courrier Function - operation: http://mydeliveryservice.org/deliveries.json#dispatch -- name: Deliver Order Function - operation: http://mydeliveryservice.org/deliveries.json#deliver -- name: Charge For Order Function - operation: http://mypaymentservice.org/payments.proto#PaymentService#ChargeUser -``` - -#### Main Workflow Definition - -With the function and event definitions in place we can now start writing our main workflow definition: - -```yaml -id: foodorderworkflow -name: Food Order Workflow -version: '1.0.0' -specVersion: '0.8' -start: Place Order -functions: file://orderfunctions.yml -events: file://orderevents.yml -states: -- name: Place Order - type: operation - actions: - - subFlowRef: placeorderworkflow - transition: Wait for ETA Deadline -- name: Wait for ETA Deadline - type: event - onEvents: - - eventRefs: - - ETA Deadline Event - eventDataFilter: - data: "${ .results.status }" - toStateData: "${ .status }" - transition: Deliver Order -- name: Deliver Order - type: operation - actions: - - subFlowRef: deliverorderworkflow - transition: Charge For Order -- name: Charge For Order - type: operation - actions: - - functionRef: - refName: Charge For Order Function - arguments: - order: "${ .order.id }" - actionDataFilter: - results: "${ .outcome.status }" - toStateData: "${ .status }" - stateDataFilter: - output: '${ . | {"orderid": .id, "orderstatus": .status} | .orderstatus += ["Order - Completed"] }' - end: true -``` - -With this in place we can start defining our sub-workflows: - -#### Place Order Sub-Workflow - -```yaml -id: placeorderworkflow -name: Place Order Workflow -version: '1.0.0' -specVersion: '0.8' -start: Submit Order -states: -- name: Submit Order - type: event - onEvents: - - eventRefs: - - Food Order Event - actions: - - functionRef: - refName: Submit Order Function - arguments: - order: "${ .order }" - actionDataFilter: - results: "${ .results.status }" - toStateData: "${ .status }" - - functionRef: - refName: Get Order ETA Function - arguments: - customer: "${ .customerId }" - restaurantid: "${ .order.restaurantId }" - delivery: " ${ .delivery }" - actionDataFilter: - results: "${ .results.status }" - toStateData: "${ .status }" - end: true -``` - -#### Deliver Order Sub-Workflow - -```yaml -id: deliverorderworkflow -name: Deliver Order Workflow -version: '1.0.0' -specVersion: '0.8' -start: Dispatch Courier -states: -- name: Dispatch Courier - type: operation - actions: - - functionRef: Dispatch Courrier Function - transition: Wait for Order Pickup -- name: Wait for Order Pickup - type: event - onEvents: - - eventRefs: - - Order Picked Up Event - eventDataFilter: - data: "${ .data.status }" - toStateData: "${ .status }" - actions: - - functionRef: Deliver Order Function - transition: Wait for Delivery Confirmation -- name: Wait for Delivery Confirmation - type: event - onEvents: - - eventRefs: - - Order Delievered Event - eventDataFilter: - data: "${ .data.status }" - toStateData: "${ .status }" - end: true -``` - -#### Workflow Results - -For the example order event, the workflow output for a successful completion would look like for example: - -```json -{ - "orderid": "ORDER-12345", - "orderstatus": [ - "Order Submitted", - "Order ETA Received", - "Order Picked up", - "Order Delievered", - "Order Charged", - "Order Completed" - ] -} -``` - -### Continuing as a new Execution - -#### Description - -Some runtime implementations on which we run our workflows can have different quotas, such as maximum execution durations, maximum consumed events, etc. We can use the Serverless workflow "continueAs" functionality that can be used to stop the current workflow execution and start another one (of the same or a different type). This is very useful in cases where we have to ensure we don't reach the imposed quotas of single workflow execution. - -This example assumes that the runtime we are using has a quota set to a maximum of one thousand consumed events per single workflow execution. -Our sample workflow consumes a single customer event at a time and invokes the `emailCustomer` function. -Note that we do not set a workflow `workflowExecTimeout`, so we intend to have a long-running workflow. However, because of the runtime restriction, in this case, we would run into the event consume limit, and our workflow would have to terminate. We can fix this problem by using [`continueAs`](../specification.md#Continuing-as-a-new-Execution), which will allow us to make sure that we reach the given limit and then continue our workflow execution as a new run. - -We assume that our workflow input has the runtime-imposed quota: - -```json -{ - "quota": { - "maxConsumedEvents": 1000 - } -} -``` - -#### Workflow Diagram - -- -
- -#### Workflow Definition - -JSON | -YAML | -
---|---|
- -```json -{ - "id": "notifycustomerworkflow", - "name": "notifycustomerworkflow", - "description": "Notify Customer", - "version": "1.0.0", - "specVersion": "0.8", - "start": "wait-for-customer-event", - "states": [ - { - "name": "wait-for-customer-event", - "type": "event", - "onEvents": [ - { - "eventRefs": [ - "customer-event" - ], - "eventDataFilter": { - "data": "${ .customerId }", - "toStateData": "${ .eventCustomerId }" - }, - "actions": [ - { - "name": "notify-customer-function", - "functionRef": { - "refName": "notify-customer-function", - "arguments": { - "customerId": "${ .eventCustomerId }" - } - } - } - ] - } - ], - "stateDataFilter": { - "output": "${ .count = .count + 1 }" - }, - "transition": "check-event-quota" - }, - { - "name": "check-event-quota", - "type": "switch", - "dataConditions": [ - { - "name": "ready", - "condition": "${ try(.customerCount) != null and .customerCount > .quota.maxConsumedEvents }", - "end": { - "continueAs": { - "workflowId": "notifycustomerworkflow", - "version": "1.0.0", - "data": "${ del(.customerCount) }" - } - } - } - ], - "defaultCondition": { - "transition": "wait-for-customer-event" - } - } - ], - "events": [ - { - "name": "customer-event", - "type": "org.events.customerEvent", - "source": "customerSource" - } - ], - "functions": [ - { - "name": "notify-customer-function", - "operation": "http://myapis.org/customerapis.json#notifyCustomer" - } - ] -}``` - - | -- -```yaml -id: notifycustomerworkflow -name: notifycustomerworkflow -description: Notify Customer -version: 1.0.0 -specVersion: "0.8" -start: wait-for-customer-event -states: - - name: wait-for-customer-event - type: event - onEvents: - - eventRefs: - - customer-event - eventDataFilter: - data: ${ .customerId } - toStateData: ${ .eventCustomerId } - actions: - - name: notify-customer-function - functionRef: - refName: notify-customer-function - arguments: - customerId: ${ .eventCustomerId } - stateDataFilter: - output: ${ .count = .count + 1 } - transition: check-event-quota - - name: check-event-quota - type: switch - dataConditions: - - name: ready - condition: ${ try(.customerCount) != null and .customerCount > - .quota.maxConsumedEvents } - end: - continueAs: - workflowId: notifycustomerworkflow - version: 1.0.0 - data: ${ del(.customerCount) } - defaultCondition: - transition: wait-for-customer-event -events: - - name: customer-event - type: org.events.customerEvent - source: customerSource -functions: - - name: notify-customer-function - operation: http://myapis.org/customerapis.json#notifyCustomer -``` - - | -
JSON | -YAML | -
---|---|
- -```json -{ - "id": "customerbankingtransactions", - "name": "customerbankingtransactions", - "description": "Customer Banking Transactions Workflow", - "version": "1.0.0", - "specVersion": "0.8", - "autoRetries": true, - "constants": { - "largetxamount": 5000 - }, - "states": [ - { - "name": "process-transactions", - "type": "foreach", - "inputCollection": "${ .customer.transactions }", - "iterationParam": "${ .tx }", - "actions": [ - { - "name": "process-larger-transaction", - "functionRef": "banking-service-larger-tx", - "condition": "${ .tx >= $CONST.largetxamount }" - }, - { - "name": "process-smaller-transaction", - "functionRef": "banking-service-smaller-tx", - "condition": "${ .tx < $CONST.largetxamount }" - } - ], - "end": true - } - ], - "functions": [ - { - "name": "banking-service-larger-tx", - "type": "asyncapi", - "operation": "banking.yaml#largerTransation" - }, - { - "name": "banking-service-smaller-tx", - "type": "asyncapi", - "operation": "banking.yaml#smallerTransation" - } - ] -}``` - - | -- -```yaml -id: customerbankingtransactions -name: customerbankingtransactions -description: Customer Banking Transactions Workflow -version: 1.0.0 -specVersion: "0.8" -autoRetries: true -constants: - largetxamount: 5000 -states: - - name: process-transactions - type: foreach - inputCollection: ${ .customer.transactions } - iterationParam: ${ .tx } - actions: - - name: process-larger-transaction - functionRef: banking-service-larger-tx - condition: ${ .tx >= $CONST.largetxamount } - - name: process-smaller-transaction - functionRef: banking-service-smaller-tx - condition: ${ .tx < $CONST.largetxamount } - end: true -functions: - - name: banking-service-larger-tx - type: asyncapi - operation: banking.yaml#largerTransation - - name: banking-service-smaller-tx - type: asyncapi - operation: banking.yaml#smallerTransation -``` - - | -
- -
- -#### Workflow Definition - -JSON | -YAML | -
---|---|
-
-```json
- |
-
-
-```yaml
- |
-
- -
- -#### Workflow Definition - -JSON | -YAML | -
---|---|
-
-```json
- |
-
-
-```yaml
- |
-
- -
- -#### Workflow Definition - -JSON | -YAML | -
---|---|
-
-```json
- |
-
-
-```yaml
- |
-
- -
- -#### Workflow Definition - -JSON | -YAML | -
---|---|
-
-```json
- |
-
-
-```yaml
- |
-
- -
- -#### Workflow Definition - -JSON | -YAML | -
---|---|
-
-```json
- |
-
-
-```yaml
- |
-
- -
- -#### Workflow Definition - -JSON | -YAML | -
---|---|
-
-```json
- |
-
-
-```yaml
- |
-
- -
- -#### Workflow Definition - -JSON | -YAML | -
---|---|
-
-```json
- |
-
-
-```yaml
- |
-
- -
- -#### Workflow Definition - -JSON | -YAML | -
---|---|
-
-```json
- |
-
-
-```yaml
- |
-
- -
- -#### Workflow Definition - -JSON | -YAML | -
---|---|
-
-```json
- |
-
-
-```yaml
- |
-
- -
- -#### Workflow Definition - -JSON | -YAML | -
---|---|
-
-```json
- |
-
-
-```yaml
- |
-
- -
- -#### Workflow Definition - -JSON | -YAML | -
---|---|
-
-```json
- |
-
-
-```yaml
- |
-
- -
- -#### Workflow Definition - -JSON | -YAML | -
---|---|
-
-```json
- |
-
-
-```yaml
- |
-
- -
- -#### Workflow Definition - -JSON | -YAML | -
---|---|
-
-```json
- |
-
-
-```yaml
- |
-
- -
- -#### Workflow Definition - -JSON | -YAML | -
---|---|
-
-```json
- |
-
-
-```yaml
- |
-
- -
- -#### Workflow Definition - -JSON | -YAML | -
---|---|
-
-```json
- |
-
-
-```yaml
- |
-
- -
- -#### Workflow Definition - -JSON | -YAML | -
---|---|
-
-```json
- |
-
-
-```yaml
- |
-
- -
- -#### Workflow Definition - -JSON | -YAML | -
---|---|
-
-```json
- |
-
-
-```yaml
- |
-
- -
- -#### Workflow Definition - -JSON | -YAML | -
---|---|
-
-```json
- |
-
-
-```yaml
- |
-
- -
- -#### Workflow Definitions - -JSON | -YAML | -
---|---|
-
-```json
- |
-
-
-```yaml
- |
-
- -
- -#### Workflow Definition - -JSON | -YAML | -
---|---|
-
-```json
- |
-
-
-```yaml
- |
-
- -
- -#### Workflow Definition - -JSON | -YAML | -
---|---|
-
-```json
- |
-
-
-```yaml
- |
-
- -
- -#### Workflow Definition - -JSON | -YAML | -
---|---|
-
-```json
- |
-
-
-```yaml
- |
-
- -
- -#### Workflow Definition - -We fist define our top-level workflow for this example: - -JSON | -YAML | -
---|---|
-
-```json
- |
-
-
-```yaml
- |
-
JSON | -YAML | -|
---|---|---|
-
-```json
- |
-
-
-```yaml
- |
-- -```yaml -id: vitalscheck -name: Car Vitals Check -version: '1.0.0' -specVersion: '0.8' -start: CheckVitals -states: - - name: CheckVitals - type: operation - actions: - - functionRef: Check Tire Pressure - - functionRef: Check Oil Pressure - - functionRef: Check Coolant Level - - functionRef: Check Battery - end: - produceEvents: - - eventRef: DisplayChecksOnDashboard - data: "${ .evaluations }" -functions: - - name: checkTirePressure - operation: mycarservices.json#checktirepressure - - name: checkOilPressure - operation: mycarservices.json#checkoilpressure - - name: checkCoolantLevel - operation: mycarservices.json#checkcoolantlevel - - name: checkBattery - operation: mycarservices.json#checkbattery -``` - - | -
- -
- -#### Workflow Definition - -JSON | -YAML | -
---|---|
-
-```json
- |
-
-
-```yaml
- |
-
- -
- -#### Workflow Definition - -JSON | -YAML | -
---|---|
-
-```json
- |
-
-
-```yaml
- |
-
- -
- -Our workflow starts with the "Place Order" [Subflow](../specification.md#SubFlow-Action), which is responsible -to send the received order to the requested restaurant and the estimated order ETA. -We then wait for the ETA time when our workflow should go into the "Deliver Order" SubFlow, responsible -for dispatching a Courier and sending her/him off to pick up the order. Once the order is picked up, the Courier needs to deliver the order to the customer. -After the order has been delivered to the customer, our workflow needs to charge the customer. - -Our workflow needs to communicate with three services during its execution, namely the Order, Delivery, and -the Payment services. - -For the sake of the example, we assume that our workflow can communicate to the Order and Delivery services via REST and the Payment service via gRPC. -Let's start by defining an example CloudEvent which triggers an instance of our workflow. -This event can be sent by a web UI, for example, or be pushed onto a Kafka/MQTT topic to start our order workflow. - -```json -{ - "specversion": "1.0", - "type": "org.orders", - "source": "/orders/", - "subject": "Food Order", - "id": "A234-1234-1234", - "time": "2021-03-05T17:31:00Z", - "orderid": "ORDER-12345", - "data": { - "id": "ORDER-12345", - "customerId": "CUSTOMER-12345", - "status": [], - "order": { - "restaurantId": "RESTAURANT-54321", - "items": [ - { - "itemId": "ITEM-8765", - "amount": 1, - "addons": "" - } - ] - }, - "delivery":{ - "address": "1234 MyStreet, MyCountry", - "type": "contactless", - "requestedTime": "ASAP", - "location": "Front door", - "instructions": "" - } - } -} -``` - -Note the `orderid` CloudEvent context attribute, which contains the unique ID of the order specified in this event. [Event correlation](../specification.md#Correlation-Definition) is done against CE context attributes, and as such, to be able -to correlate multiple order events to the same order id, it needs to be part of the CE context attributes, and -not its data (payload). - -Now let's start defining our workflow. For the sake of this example, let's define our function and event definitions -as separate YAML files (and then reference them inside our workflow definition). This is useful in cases -when you want to reuse them between multiple workflow definitions. - -#### Workflow Event Definition - -``` yaml -events: -- name: Food Order Event - source: "/orders/" - type: org.orders - correlation: - - contextAttributeName: orderid -- name: ETA Deadline Event - source: "/orderseta" - type: org.orders.eta - correlation: - - contextAttributeName: orderid -- name: Order Picked Up Event - source: "/orderspickup" - type: org.orders.delivery - correlation: - - contextAttributeName: orderid -- name: Order Delievered Event - source: "/orderdelivery" - type: org.orders.delivery - correlation: - - contextAttributeName: orderid -``` - -#### Workflow Function Definition - -``` yaml -functions: -- name: Submit Order Function - operation: http://myorderservice.org/orders.json#submit -- name: Get Order ETA Function - operation: http://myorderservice.org/orders.json#orderETA -- name: Dispatch Courrier Function - operation: http://mydeliveryservice.org/deliveries.json#dispatch -- name: Deliver Order Function - operation: http://mydeliveryservice.org/deliveries.json#deliver -- name: Charge For Order Function - operation: http://mypaymentservice.org/payments.proto#PaymentService#ChargeUser -``` - -#### Main Workflow Definition - -With the function and event definitions in place we can now start writing our main workflow definition: - -```yaml -id: foodorderworkflow -name: Food Order Workflow -version: '1.0.0' -specVersion: '0.8' -start: Place Order -functions: file://orderfunctions.yml -events: file://orderevents.yml -states: -- name: Place Order - type: operation - actions: - - subFlowRef: placeorderworkflow - transition: Wait for ETA Deadline -- name: Wait for ETA Deadline - type: event - onEvents: - - eventRefs: - - ETA Deadline Event - eventDataFilter: - data: "${ .results.status }" - toStateData: "${ .status }" - transition: Deliver Order -- name: Deliver Order - type: operation - actions: - - subFlowRef: deliverorderworkflow - transition: Charge For Order -- name: Charge For Order - type: operation - actions: - - functionRef: - refName: Charge For Order Function - arguments: - order: "${ .order.id }" - actionDataFilter: - results: "${ .outcome.status }" - toStateData: "${ .status }" - stateDataFilter: - output: '${ . | {"orderid": .id, "orderstatus": .status} | .orderstatus += ["Order - Completed"] }' - end: true -``` - -With this in place we can start defining our sub-workflows: - -#### Place Order Sub-Workflow - -```yaml -id: placeorderworkflow -name: Place Order Workflow -version: '1.0.0' -specVersion: '0.8' -start: Submit Order -states: -- name: Submit Order - type: event - onEvents: - - eventRefs: - - Food Order Event - actions: - - functionRef: - refName: Submit Order Function - arguments: - order: "${ .order }" - actionDataFilter: - results: "${ .results.status }" - toStateData: "${ .status }" - - functionRef: - refName: Get Order ETA Function - arguments: - customer: "${ .customerId }" - restaurantid: "${ .order.restaurantId }" - delivery: " ${ .delivery }" - actionDataFilter: - results: "${ .results.status }" - toStateData: "${ .status }" - end: true -``` - -#### Deliver Order Sub-Workflow - -```yaml -id: deliverorderworkflow -name: Deliver Order Workflow -version: '1.0.0' -specVersion: '0.8' -start: Dispatch Courier -states: -- name: Dispatch Courier - type: operation - actions: - - functionRef: Dispatch Courrier Function - transition: Wait for Order Pickup -- name: Wait for Order Pickup - type: event - onEvents: - - eventRefs: - - Order Picked Up Event - eventDataFilter: - data: "${ .data.status }" - toStateData: "${ .status }" - actions: - - functionRef: Deliver Order Function - transition: Wait for Delivery Confirmation -- name: Wait for Delivery Confirmation - type: event - onEvents: - - eventRefs: - - Order Delievered Event - eventDataFilter: - data: "${ .data.status }" - toStateData: "${ .status }" - end: true -``` - -#### Workflow Results - -For the example order event, the workflow output for a successful completion would look like for example: - -```json -{ - "orderid": "ORDER-12345", - "orderstatus": [ - "Order Submitted", - "Order ETA Received", - "Order Picked up", - "Order Delievered", - "Order Charged", - "Order Completed" - ] -} -``` - -### Continuing as a new Execution - -#### Description - -Some runtime implementations on which we run our workflows can have different quotas, such as maximum execution durations, maximum consumed events, etc. We can use the Serverless workflow "continueAs" functionality that can be used to stop the current workflow execution and start another one (of the same or a different type). This is very useful in cases where we have to ensure we don't reach the imposed quotas of single workflow execution. - -This example assumes that the runtime we are using has a quota set to a maximum of one thousand consumed events per single workflow execution. -Our sample workflow consumes a single customer event at a time and invokes the `emailCustomer` function. -Note that we do not set a workflow `workflowExecTimeout`, so we intend to have a long-running workflow. However, because of the runtime restriction, in this case, we would run into the event consume limit, and our workflow would have to terminate. We can fix this problem by using [`continueAs`](../specification.md#Continuing-as-a-new-Execution), which will allow us to make sure that we reach the given limit and then continue our workflow execution as a new run. - -We assume that our workflow input has the runtime-imposed quota: - -```json -{ - "quota": { - "maxConsumedEvents": 1000 - } -} -``` - -#### Workflow Diagram - -- -
- -#### Workflow Definition - -JSON | -YAML | -
---|---|
-
-```json
- |
-
-
-```yaml
- |
-
JSON | -YAML | -
---|---|
-
-```json
- |
-
-
-```yaml
- |
-
Workflow | -KPIs Extension | -
---|---|
- -```yaml -id: patientVitalsWorkflow -name: Monitor Patient Vitals -version: '1.0.0' -specVersion: '0.8' -start: MonitorVitals -extensions: - - extensionId: workflow-kpi-extension - path: file://myextensions/kpi.yml -events: - - name: HighBodyTemperature - type: org.monitor.highBodyTemp - source: monitoringSource - correlation: - - contextAttributeName: patientId - - name: HighBloodPressure - type: org.monitor.highBloodPressure - source: monitoringSource - correlation: - - contextAttributeName: patientId - - name: HighRespirationRate - type: org.monitor.highRespirationRate - source: monitoringSource - correlation: - - contextAttributeName: patientId -functions: - - name: callPulmonologist - operation: http://myapi.org/patientapi.json#callPulmonologist - - name: sendTylenolOrder - operation: http://myapi.org/patientapi.json#sendTylenol - - name: callNurse - operation: http://myapi.org/patientapi.json#callNurse -states: - - name: MonitorVitals - type: event - exclusive: true - onEvents: - - eventRefs: - - HighBodyTemperature - actions: - - functionRef: - refName: sendTylenolOrder - arguments: - patientid: "${ .patientId }" - - eventRefs: - - HighBloodPressure - actions: - - functionRef: - refName: callNurse - arguments: - patientid: "${ .patientId }" - - eventRefs: - - HighRespirationRate - actions: - - functionRef: - refName: callPulmonologist - arguments: - patientid: "${ .patientId }" - end: true -``` - - | -- -```yaml -extensionid: workflow-kpi-extension -currency: USD -workflow: - per: - time: PT1D - maxCost: '1300' - maxInvoked: '500' - minInvoked: '100' -events: -- for: HighBodyTemperature - per: - time: PT1D - avgConsumed: '50' -- for: HighBloodPressure - per: - time: PT1D - avgConsumed: '30' -functions: -- for: callPulmonologist - per: - instances: 1000 - maxCost: '400' - maxErrors: '5' - maxRetry: '10' - maxTimeout: '15' - avgInvoked: '40' -- for: sendTylenolOrder - per: - instances: 1000 - maxCost: '200' - maxErrors: '5' - maxRetry: '10' - maxTimeout: '15' - avgInvoked: '400' -states: -- for: MonitorVitals - per: - time: PT1D - maxCost: '300' - maxExec: '1000' - minExec: '50' -``` - - | -
Workflow | -Rate Limiting Extension | -
---|---|
- -```yaml -id: processapplication -name: Process Application -version: '1.0.0' -specVersion: '0.8' -extensions: - - extensionId: workflow-ratelimiting-extension - path: file://myextensions/ratelimiting.yml -start: ProcessNewApplication -states: - - name: ProcessNewApplication - type: event - onEvents: - - eventRefs: - - ApplicationReceivedEvent - actions: - - functionRef: processApplicationFunction - - functionRef: acceptApplicantFunction - - functionRef: depositFeesFunction - end: - produceEvents: - - eventRef: NotifyApplicantEvent -functions: - - name: processApplicationFunction - operation: file://myservice.json#process - - name: acceptApplicantFunction - operation: file://myservice.json#accept - - name: depositFeesFunction - operation: file://myservice.json#deposit -events: - - name: ApplicationReceivedEvent - type: application - source: "/applications/new" - - name: NotifyApplicantEvent - type: notifications - source: "/applicants/notify" -``` - - | -- -```yaml -extensionid: workflow-ratelimiting-extension -singleInstance: - maxActionsPerSecond: 0.1 - maxConcurrentActions: 200 - maxProducedEventsPerSecond: 2 - maxStates: '1000' - maxTransitions: '1000' -allInstances: - maxActionsPerSecond: 1 - maxConcurrentActions: 500 - maxProducedEventsPerSecond: 20 - maxStates: '10000' - maxTransitions: '10000' - -``` - - | -
- -
- -For more information on the history, development and design rationale behind the specification, see the [Serverless Workflow Wiki](https://github.com/serverlessworkflow/specification/wiki). - -### Focus on standards - -- -
- -Serverless Workflow language takes advantage of well-established and known standards such as [CloudEvents](https://cloudevents.io/), [OpenAPI](https://www.openapis.org/) specifications, -[gRPC](https://grpc.io/) and [GraphQL](https://graphql.org/). - -## Project Components - -- -
- -The specification has multiple components: - -* Definitions of the workflow language. This is defined via the [Workflow JSON Schema](schema/workflow.json). You can use both - [JSON](https://www.json.org/json-en.html) and [YAML](https://yaml.org/) formats to model your workflows. -* Software Development Kits (SDKs) for [Go](https://github.com/serverlessworkflow/sdk-go), [Java](https://github.com/serverlessworkflow/sdk-java), [.NET](https://github.com/serverlessworkflow/sdk-net), [Typescript](https://github.com/serverlessworkflow/sdk-typescript) and [Python](https://github.com/serverlessworkflow/sdk-python), and we plan to add them for more languages in the future. -* Set of [Workflow Extensions](extensions/README.md) which - allow users to define additional, non-execution-related workflow information. This information can be used to improve - workflow performance. - Some example workflow extensions include Key Performance Indicators (KPIs), Rate Limiting, Simulation, Tracing, etc. -* Technology Compatibility Kit (TCK) to be used as a specification conformance tool for runtime implementations. - -## Specification Details - -Following sections provide detailed descriptions of all parts of the Serverless Workflow language. - -### Core Concepts - -This section describes some of the core Serverless Workflow concepts: - -### Workflow Definition - -A workflow definition is a JSON or YAML file that conforms to the Serverless Workflow specification DSL. -It consists of the core [Workflow Definition Structure](#Workflow-Definition-Structure) -and the [Workflow Model](#Workflow-Model) It defines a blueprint used by runtimes for its execution. - -A business solution can be composed of any number of related workflow definitions. -Their relationships are explicitly modeled with the Serverless Workflow language (for example -by using [SubFlowRef Definition](#SubFlowRef-Definition) in actions). - -Runtimes can initialize workflow definitions for some particular set of data inputs or events. - -### Workflow Instance - -A workflow instance represents a single workflow execution corresponding to the instructions provided by a -workflow definition. A workflow instance can be short or long-running. A single workflow instance -should be isolated, meaning it should not share state and data with other workflow instances. -Workflow instances should be able to communicate with each other via events. - -Depending on their workflow definition, workflow instances can be short-lived or -can execute for days, weeks, or years. - -Each workflow instances should have its unique identifier, which should remain -unchanged throughout its execution. - -Workflow instances can be started providing some data input. This is described in detail in the -[workflow data input](#Workflow-Data-Input) section. -Workflow instances can also wait for events to start their execution, which is the case -where a workflow definition contains a [EventState](#Event-State) starting workflow state. - -The workflow definition also explicitly defines when a workflow instance should be completed. -By default, instances should be completed once there are no active workflow paths (all active -paths reach a state containing the default [end definition](#End-Definition)), -or if the defined [`workflowExecTimeout`](#Workflow-Timeouts) time is reached. -Other ways, such as using the `terminate` property of the [end definition](#End-Definition) to terminate instance execution, -or defining an [`workflowExecTimeout`](#Workflow-Timeouts) property are also possible. - -For long-running workflow-executions, you can utilize the `keepActive` workflow property which -provides more control as to when exactly to terminate workflow execution. In cases where a -workflow execution should be continued as a new one, the DSL also provides the `continueAs` property which is described -in detail in the [Continuing a new Execution](#Continuing-as-a-new-Execution) section. - -### Workflow Model - -The Serverless Workflow language is composed of: - -* [Function definitions](#Function-Definition) - Reusable functions that can declare services that need to be invoked, or expressions to be evaluated. -* [Event definitions](#Event-Definition) - Reusable declarations of events that need to be consumed to start or continue workflow instances, trigger function/service execution, or be produced during workflow execution. -* [Retry definitions](#Retry-Definition) - Reusable retry definitions. Can specify retry strategies for service invocations during workflow execution. -* [Timeout definitions](#Workflow-Timeouts) - Reusable timeout definitions. Can specify default workflow execution timeout, as well as workflow state, action, and branch execution timeouts. -* [Errors definition](#Defining-Errors) - Reusable error definitions. Provide domain-specific error definitions which can be referenced in workflow states error handling. -* [State definitions](#Workflow-States) - Definition of states, the building blocks of workflow `control flow logic`. States can reference the reusable function, event and retry definitions. - -### Workflow Data - -Serverless Workflow data is represented in [JSON](https://www.json.org/json-en.html) format. -Data flow and execution logic go hand in hand, meaning as workflow execution follows the workflow definition -logic, so does the workflow data: - -- -
- -The initial [Workflow data input](#Workflow-data-input) is passed to the workflow starting state as its data input. -When a state finishes its execution, [its data output is passed as data input to the next state](#Information-passing-Between-States) that should be executed. - -When workflow execution ends, the last executed workflow state's data output becomes the final [Workflow data output](#Workflow-data-output). - -States can filter their data inputs and outputs using [State Data filters](#State-data-filters). - -States can also consume events as well as invoke services. These event payloads and service invocation results -can be filtered using [Event data filters](#Event-data-filters) and [Action data filters](#Action-data-filters). - -Data filters use [workflow expressions](#Workflow-Expressions) for selecting and manipulating state data -input and output, action inputs and results, and event payloads. - -Multiple filters can be combined to gain high level of control of your workflow state data. You can find an example of that in -[this](#Using-multiple-data-filters) section. - -Data from consumed events,and action execution results are added/merged -to state data. Reference the [data merging section](#Data-Merging) to learn about the merging rules that should be applied. - -#### Workflow Data Input - -The initial data input into a workflow instance. Must be a valid [JSON object](https://tools.ietf.org/html/rfc7159#section-4). -If no input is provided, the default data input should be an empty JSON object: - -```json -{ } -``` - -Workflow data input is passed to the workflow starting state as its data input. - -- -
- -#### Information Passing Between States - -States in a workflow can receive data (data input) and produce a data result (data output). The state's data input is typically the previous state's data output. -When a state completes its execution, its data output is passed to the state's data input it transitions to. -There are two rules to consider here: - -- If the state is the workflow starting state, its data input is the [workflow data input](#Workflow-data-input). -- When workflow execution ends, the data output of the last executed state becomes the [workflow data output](#Workflow-data-output). - -- -
- -#### Workflow data output - -Each workflow execution should produce a data output. -The workflow data output is the data output of the last executed workflow state. - -#### State data filters - -| Parameter | Description | Type | Required | -| --- | --- | --- | --- | -| input | Workflow expression to filter the states data input | string | no | -| output | Workflow expression that filters the states data output | string | no | - -- -
JSON | -YAML | -
---|---|
- -```json -{ - "stateDataFilter": { - "input": "${ .orders }", - "output": "${ .provisionedOrders }" - } -} -``` - - | -- -```yaml -stateDataFilter: - input: "${ .orders }" - output: "${ .provisionedOrders }" -``` - - | -
- -
- -For our second example, let's say that we are interested in the only vegetable "veggie-like". -Here we have two ways of filtering our data, depending on if actions within our state need access to all vegetables, or -only the ones that are "veggie-like". - -The first way would be to use both "input", and "output": - -```json -{ - "stateDataFilter": { - "input": "${ {vegetables: .vegetables} }", - "output": "${ {vegetables: [.vegetables[] | select(.veggieLike == true)]} }" - } -} -``` - -The states data input filter selects all the vegetables from the main data input. Once all actions have performed, before the state transition -or workflow execution completion (if this is an end state), the "output" of the state filter selects only the vegetables which are "veggie like". - -- -
- -The second way would be to directly filter only the "veggie like" vegetables with just the data input path: - -```json -{ - "stateDataFilter": { - "input": "${ {vegetables: [.vegetables[] | select(.veggieLike == true)]} }" - } -} -``` - -#### Action data filters - -| Parameter | Description | Type | Required | -| --- | --- | --- | --- | -| fromStateData | Workflow expression that filters state data that can be used by the action | string | no | -| useResults | If set to `false`, action data results are not added/merged to state data. In this case 'results' and 'toStateData' should be ignored. Default is `true`. | boolean | no | -| results | Workflow expression that filters the actions data results | string | no | -| toStateData | Workflow expression that selects a state data element to which the action results should be added/merged. If not specified denotes the top-level state data element. In case it is not specified and the result of the action is not an object, that result should be merged as the value of an automatically generated key. That key name will be the result of concatenating the action name with `-output` suffix. | string | no | - -- -
JSON | -YAML | -
---|---|
- -```json -{ - "actionDataFilter": { - "fromStateData": "${ .language }", - "results": "${ .results.greeting }", - "toStateData": "${ .finalgreeting }" - } -} -``` - - | -- -```yaml -actionDataFilter: - fromStateData: "${ .language }" - results: "${ .results.greeting }" - toStateData: "${ .finalgreeting }" -``` - - | -
- -
JSON | -YAML | -
---|---|
- -```json -{ - "eventDataFilter": { - "data": "${ .data.results }" - } -} -``` - - | -- -```yaml -eventDataFilter: - data: "${ .data.results }" -``` - - | -
- -
- -Note that the data input to the Event data filters depends on the `dataOnly` property of the associated [Event definition](#Event-Definition). -If this property is not defined (has default value of `true`), Event data filter expressions are evaluated against the event payload (the CloudEvents `data` attribute only). If it is set to -`false`, the expressions should be evaluated against the entire CloudEvent (including its context attributes). - -In the case event data/payload should not be added/merged to state data, we can set the `useData` property to `false`. -In this case, the `data` and `toStateData` properties should be ignored, and nothing is added/merged to state data. -If `useData` is not specified (or it's value set to `true`), event payload, if available, should be added/merged to state data. - - -#### Using multiple data filters - -As [Event states](#Event-State) can take advantage of all defined data filters. In the example below, we define -a workflow with a single event state and show how data filters can be combined. - -```json -{ - "name": "greet-customers-workflow", - "description": "Greet Customers when they arrive", - "version": "1.0.0", - "specVersion": "0.8", - "start": "WaitForCustomerToArrive", - "states":[ - { - "name": "wait-for-customer-to-arrive", - "type": "event", - "onEvents": [{ - "eventRefs": ["customer-arrives-event"], - "eventDataFilter": { - "data": "${ .customer }", - "toStateData": "${ .customerInfo }" - }, - "actions":[ - { - "name": "greet-customer", - "functionRef": { - "refName": "greeting-function", - "arguments": { - "greeting": "${ .hello.spanish } ", - "customerName": "${ .customerInfo.name } " - } - }, - "actionDataFilter": { - "fromStateData": "${ { hello, customerInfo } }", - "results": "${ .greetingMessageResult }", - "toStateData": "${ .finalCustomerGreeting }" - } - } - ] - }], - "stateDataFilter": { - "input": "${ .greetings } ", - "output": "${ { finalCustomerGreeting } }" - }, - "end": true - } - ], - "events": [{ - "name": "customer-arrives-event", - "type": "customer-arrival-type", - "source": "customer-arrival-event-source" - }], - "functions": [{ - "name": "greeting-function", - "operation": "http://my.api.org/myapi.json#greeting" - }] -} -``` - -The workflow data input when starting workflow execution is assumed to include greetings in different languages: - -```json -{ - "greetings": { - "hello": { - "english": "Hello", - "spanish": "Hola", - "german": "Hallo", - "russian": "Здравствуйте" - }, - "goodbye": { - "english": "Goodbye", - "spanish": "Adiós", - "german": "Auf Wiedersehen", - "russian": "Прощай" - } - } -} -``` - -The workflow data input then becomes the data input of the starting workflow state. - -We also assume for this example that the CloudEvent that our event state consumes include the data (payload): - -```json -{ - "customer": { - "name": "John Michaels", - "address": "111 Some Street, SomeCity, SomeCountry", - "age": 40 - } -} -``` - -Here is a sample diagram showing our workflow, each numbered step on this diagram shows a certain defined point during -workflow execution at which data filters are invoked and correspond to the numbered items below. - -- -
- -**(1) Workflow execution starts**: Workflow data is passed to our "WaitForCustomerToArrive" event state as data input. -Workflow executes its starting state, namely the "WaitForCustomerToArrive" event state. - -The event state **stateDataFilter** is invoked to filter its data input. The filters "input" expression is evaluated and -selects only the "greetings" data. The rest of the state data input should be disregarded. - -At this point our state data should be: - -```json -{ - "hello": { - "english": "Hello", - "spanish": "Hola", - "german": "Hallo", - "russian": "Здравствуйте" - }, - "goodbye": { - "english": "Goodbye", - "spanish": "Adiós", - "german": "Auf Wiedersehen", - "russian": "Прощай" - } -} -``` - -**(2) CloudEvent of type "customer-arrival-type" is consumed**: Once the event is consumed, the "eventDataFilter" is triggered. -Its "data" expression selects the "customer" object from the events data. The "toStateData" expression -says that we should add/merge this selected event data to the state data in its "customerInfo" property. If this property -exists it should be merged, if it does not exist, one should be created. - -At this point our state data contains: - -```json -{ - "hello": { - "english": "Hello", - "spanish": "Hola", - "german": "Hallo", - "russian": "Здравствуйте" - }, - "goodbye": { - "english": "Goodbye", - "spanish": "Adiós", - "german": "Auf Wiedersehen", - "russian": "Прощай" - }, - "customerInfo": { - "name": "John Michaels", - "address": "111 Some Street, SomeCity, SomeCountry", - "age": 40 - } -} -``` - -**(3) Event state performs its actions**: -Before the first action is executed, its actionDataFilter is invoked. Its "fromStateData" expression filters -the current state data to select from its data that should be available to action arguments. In this example -it selects the "hello" and "customerInfo" properties from the current state data. -At this point the action is executed. -We assume that for this example "greetingFunction" returns: - -```json -{ - "execInfo": { - "execTime": "10ms", - "failures": false - }, - "greetingMessageResult": "Hola John Michaels!" -} -``` - -After the action is executed, the actionDataFilter "results" expression is evaluated to filter the results returned from the action execution. In this case, we select only the "greetingMessageResult" element from the results. - -The action filters "toStateData" expression then defines that we want to add/merge this action result to -state data under the "finalCustomerGreeting" element. - -At this point, our state data contains: - -```json -{ - "hello": { - "english": "Hello", - "spanish": "Hola", - "german": "Hallo", - "russian": "Здравствуйте" - }, - "goodbye": { - "english": "Goodbye", - "spanish": "Adiós", - "german": "Auf Wiedersehen", - "russian": "Прощай" - }, - "customerInfo": { - "name": "John Michaels", - "address": "111 Some Street, SomeCity, SomeCountry", - "age": 40 - }, - "finalCustomerGreeting": "Hola John Michaels!" -} -``` - -**(4) Event State Completes Execution**: - -When our event state finishes its execution, the states "stateDataFilter" "output" filter expression is executed -to filter the state data to create the final state data output. - -Because our event state is also an end state, its data output becomes the final [workflow data output](#Workflow-data-output). Namely: - -```json -{ - "finalCustomerGreeting": "Hola John Michaels!" -} -``` - -#### Data Merging - -Consumed event data (payload) and action execution results should be merged into the state data. -Event and action data filters can be used to give more details about this operation. - -By default, with no data filters specified, when an event is consumed, its entire data section (payload) should be merged -to the state data. Merging should be applied to the entire state data JSON element. - -In case of event and action filters, their "toStateData" property can be defined to select a specific element -of the state data with which merging should be done against. If this element does not exist, a new one should -be created first. - -When merging, the state data element and the data (payload)/action result should have the same type, meaning -that you should not merge arrays with objects or objects with arrays etc. - -When merging elements of type object should be done by inserting all the key-value pairs from both objects into -a single combined object. If both objects contain a value for the same key, the object of the event data/action results -should "win". To give an example, let's say we have the following state data: - -```json -{ - "customer": { - "name": "John", - "address": "1234 street", - "zip": "12345" - } -} -``` - -and we have the following event payload that needs to be merged into the state data: - -```json -{ - "customer": { - "name": "John", - "zip": "54321" - } -} -``` - -After merging the state data should be: - -```json -{ - "customer": { - "name": "John", - "address": "1234 street", - "zip": "54321" - } -} -``` - -Merging array types should be done by concatenating them into a larger array including unique elements of both arrays. -To give an example, merging: - -```json -{ - "customers": [ - { - "name": "John", - "address": "1234 street", - "zip": "12345" - }, - { - "name": "Jane", - "address": "4321 street", - "zip": "54321" - } - ] -} -``` - -into state data: - -```json -{ - "customers": [ - { - "name": "Michael", - "address": "6789 street", - "zip": "6789" - } - ] -} -``` - -should produce state data: - -```json -{ - "customers": [ - { - "name": "Michael", - "address": "6789 street", - "zip": "6789" - }, - { - "name": "John", - "address": "1234 street", - "zip": "12345" - }, - { - "name": "Jane", - "address": "4321 street", - "zip": "54321" - } - ] -} -``` - -Merging number types should be done by overwriting the data from events data/action results into the merging element of the state data. -For example merging action results: - -```json -{ - "age": 30 -} -``` - -into state data: - -```json -{ - "age": 20 -} -``` - -would produce state data: - -```json -{ - "age": 30 -} -``` - -Merging string types should be done by overwriting the data from events data/action results into the merging element of the state data. - -### Workflow Functions - -Workflow [functions](#Function-Definition) are reusable definitions for service invocations and/or expression evaluation. -They can be referenced by their domain-specific names inside workflow [states](#Workflow-States). - -Reference the following sections to learn more about workflow functions: - -* [Using functions for OpenAPI Service invocations](#using-functions-for-openapi-service-invocations) -+ [Using functions for HTTP Service Invocations](#using-functions-for-http-service-invocations) -* [Using functions for Async API Service Invocations](#Using-Functions-for-Async-API-Service-Invocations) -* [Using functions for gRPC service invocation](#Using-Functions-For-RPC-Service-Invocations) -* [Using functions for GraphQL service invocation](#Using-Functions-For-GraphQL-Service-Invocations) -* [Using Functions for OData Service Invocations](#Using-Functions-for-OData-Service-Invocations) -* [Using functions for expression evaluations](#Using-Functions-For-Expression-Evaluation) -* [Defining custom function types](#defining-custom-function-types) - -We can define if functions are invoked sync or async. Reference -the [functionRef](#FunctionRef-Definition) to learn more on how to do this. - -#### Using Functions for OpenAPI Service Invocations - -[Functions](#Function-Definition) can be used to describe services and their operations that need to be invoked during -workflow execution. They can be referenced by states [action definitions](#Action-Definition) to clearly -define when the service operations should be invoked during workflow execution, as well as the data parameters -passed to them if needed. - -Note that with Serverless Workflow, we can also define invocation of services which are triggered via an event. -To learn more about that, please reference the [event definitions](#Event-Definition) section, -as well as the [actions definitions](#Action-Definition) [eventRef](#EventRef-Definition) property. - -Because of an overall lack of a common way to describe different services and their operations, -many workflow languages typically chose to define custom function definitions. -This approach, however, often runs into issues such as lack of portability, limited capabilities, as well as -forcing non-workflow-specific information, such as service authentication, to be added inside the workflow language. - -To avoid these issues, the Serverless Workflow specification mandates that details about -RESTful services and their operations be described using the [OpenAPI Specification](https://www.openapis.org/). -OpenAPI is a language-agnostic standard that describes discovery of RESTful services. -This allows Serverless Workflow language to describe RESTful services in a portable -way, as well as workflow runtimes to utilize OpenAPI tooling and APIs to invoke service operations. - -Here is an example function definition for a RESTful service operation. - -```json -{ -"functions": [ - { - "name": "send-order-confirmation", - "operation": "file://confirmationapi.json#sendOrderConfirmation" - } -] -} -``` - -It can, as previously mentioned be referenced during workflow execution when the invocation of this service is desired. -For example: - -```json -{ -"states": [ - { - "name":"send-confirm-state", - "type":"operation", - "actions":[ - { - "functionRef": "send-order-confirmation" - }], - "end": true - }] -} -``` - -Note that the referenced function definition type in this case must be `openapi` (default type). - -The specification also supports describing OpenAPI for REST invocations inline in the [functions definition](#Function-Definition) using [OpenAPI Paths Object](https://spec.openapis.org/oas/v3.1.0#paths-object). - -Here is an example function definition for REST requests with method `GET` and request target corresponding with [URI Template](https://www.rfc-editor.org/rfc/rfc6570.html) `/users/{id}`: - -```json -{ - "functions":[ - { - "name":"queryUserById", - "operation": { - "/users": { - "get": { - "parameters": [{ - "name": "id", - "in": "path", - "required": true - }] - } - } - }, - "type":"openapi" - } - ] -} -``` - -Note that the [Function Definition](#Function-Definition)'s `operation` property must follow the [OpenAPI Paths Object](https://spec.openapis.org/oas/v3.1.0#paths-object) specification definition. - -The function can be referenced during workflow execution when the invocation of the REST service is desired. For example: - -```json -{ - "states":[ - { - "name":"QueryUserInfo", - "type":"operation", - "actions":[ - { - "functionRef":"queryUserById", - "arguments":{ - "id":"${ .user.id }" - } - } - ], - "end":true - } - ] -} -``` - -Example of the `POST` request sending the state data as part of the body: - -```json -{ - "functions":[ - { - "name": "createUser", - "type": "openapi", - "operation": { - "/users": { - "post": { - "requestBody": { - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "id": { - "type": "string" - }, - "name": { - "type": "string" - }, - "email": { - "type": "string" - } - }, - "required": ["name", "email"] - } - } - } - } - } - } - } - } - ] -} -``` - -Note that the `requestBody` [`content` attribute](https://spec.openapis.org/oas/v3.1.0#fixed-fields-10) is described inline rather than a reference to an external document. - -You can reference the `createUser` function and filter the input data to invoke it. Given the workflow input data: - -```json -{ - "order":{ - "id":"1234N", - "products":[ - { - "name":"Product 1" - } - ] - }, - "user":{ - "name":"John Doe", - "email":"john@doe.com" - } -} -``` - -Function invocation example: - -```json -{ - "states":[ - { - "name":"CreateNewUser", - "type":"operation", - "actions":[ - { - "functionRef":"createUser", - "actionDataFilter":{ - "fromStateData":"${ .user }", - "toStateData":"${ .user.id }" - } - } - ], - "end":true - } - ] -} -``` - -In this case, only the contents of the `user` attribute will be passed to the function. The user ID returned by the REST request body will then be added to the state data: - -```json -{ - "order":{ - "id":"1234N", - "products":[ - { - "name":"Product 1" - } - ] - }, - "user":{ - "id":"5678U", - "name":"John Doe", - "email":"john@doe.com" - } -} -``` - -When inlining the OpenAPI operation, the specification does not support the [Security Requirement Object](https://spec.openapis.org/oas/v3.1.0#security-requirement-object) since its redundat to function [Auth Definition](#Auth-Definition). If provided, this field is ignored. - -For more information about functions, reference the [Functions definitions](#Function-Definition) section. - -#### Using functions for HTTP Service Invocations - -The HTTP function can make HTTP requests to a given endpoint. It can be used in cases a service doesn't have an OpenAPI definition or users require a simple HTTP, curl-style invocation. - -The table below lists the `operation` properties for the `http` function type. - -| Property | Description | Type | Required | -| --- | --- | --- | --- | - -| uri | The URI where to send the request | String | yes | -| method | The HTTP method according to the [RFC 2616](https://datatracker.ietf.org/doc/html/rfc2616#page-36) | String | yes | -| headers | Headers to send in the HTTP call. The `Content-Type` header mandates the body convertion. | Map | no | -| cookies | Cookies to send in the HTTP call. | Map | no | - -Note that in the function definition, these values are static. When invoking the function in the `actions` definition, `jq` can be used to set the attribute values. - -Here is a function definition example for a HTTP service operation. - -```json -{ -"functions": [ - { - "name": "getPetById", - "type": "http", - "operation": { - "method": "GET", - "uri": "https://petstore.swagger.io/v2/pet/{petId}" - } - } -] -} -``` - -This function can be used later in the workflow definition: - -```json -{ - "states":[ - { - "name": "getpet", - "type": "operation", - "actions":[ - { - "functionRef": "getPetById", - "arguments":{ - "petId": "${ .pet.id }" - } - } - ], - "end":true - } - ] -} -``` - -Not that the `arguments` attribute must map the template in the `uri` definition so the underlying engine can map the arguments correctly. - -The `arguments` attribute accepts the following reserved properties when calling a HTTP function type: - -| Property | Description | Type | Required | -| --- | --- | --- | --- | - -| body | The HTTP body. If an object, it will be sent as a JSON payload by default if the `Content-Type` header is missing. Otherwise, it will try to convert it based on the `Content-Type` header definition | Object or String | no | -| headers | Headers to send in the HTTP call. The `Content-Type` header mandates the body convertion. | Map | no | -| cookies | Cookies to send in the HTTP call. | Map | no | - -These attributes are merged with the ones in the function definition. - -The listing below exemplifies how to define and call a HTTP POST endpoint. - -```json -{ -"functions": [ - { - "name": "createPet", - "type": "http", - "operation": { - "method": "POST", - "uri": "https://petstore.swagger.io/v2/pet/", - "headers": { - "Content-Type": "application/json" - } - } - } -] -}, -{ - "states":[ - { - "name":"create-pet", - "type":"operation", - "actions":[ - { - "functionRef":"createPet", - "arguments":{ - "body": { - "name": "Lulu" - }, - "headers": { - "my-header": "my-value" - } - } - } - ], - "end":true - } - ] -} -``` - -#### Using Functions for Async API Service Invocations - -[Functions](#Function-Definition) can be used to invoke PUBLISH and SUBSCRIBE operations on a message broker documented by the [Async API Specification](https://www.asyncapi.com/docs/specifications/v2.1.0). -[Async API operations](https://www.asyncapi.com/docs/specifications/v2.1.0#operationObject) are bound to a [channel](https://www.asyncapi.com/docs/specifications/v2.1.0#definitionsChannel) which describes the technology, security mechanisms, input and validation to be used for their execution. - -Let's take a look at a hypothetical Async API document (assumed its stored locally with the file name `streetlightsapi.yaml`) and define a single publish operation: - -```yaml -asyncapi: 2.1.0 -info: - title: Streetlights API - version: 1.0.0 - description: | - The Smartylighting Streetlights API allows you - to remotely manage the city lights. - license: - name: Apache 2.0 - url: https://www.apache.org/licenses/LICENSE-2.0 -servers: - mosquitto: - url: mqtt://test.mosquitto.org - protocol: mqtt -channels: - light/measured: - publish: - summary: Inform about environmental lighting conditions for a particular streetlight. - operationId: onLightMeasured - message: - name: LightMeasured - payload: - type: object - properties: - id: - type: integer - minimum: 0 - description: Id of the streetlight. - lumens: - type: integer - minimum: 0 - description: Light intensity measured in lumens. - sentAt: - type: string - format: date-time - description: Date and time when the message was sent. -``` - -To define a workflow action invocation, we can then use the following workflow [Function Definition](#Function-Definition) and set the `operation` to `onLightMeasured`: - -```json -{ - "functions": [ - { - "name": "publish-light-measurements", - "operation": "file://streetlightsapi.yaml#onLightMeasured", - "type": "asyncapi" - }] -} -``` - -Note that the [Function Definition](#Function-Definition)'s `operation` property must have the following format: - -```text -- -
JSON | -YAML | -
---|---|
- -```json -{ - "name": "sample-workflow", - "version": "1.0.0", - "specVersion": "0.8", - "description": "Sample Workflow", - "start": "MyStartingState", - "states": [], - "functions": [], - "events": [], - "errors": [], - "retries":[] -} -``` - - | -- -```yaml -name: sample-workflow -version: '1.0.0' -specVersion: '0.8' -description: Sample Workflow -start: MyStartingState -states: [] -functions: [] -events: [] -errors: [] -retries: [] -``` - - | -
- -
- -The required `name` property defines the unique workflow definition identifier, for example "orders", "payment", etc. - -The optional `key` property is an expression that evaluates to a domain related, unique running workflow instance identifier, for example "orders-1", "orders-2"... - -The `description` property might be used to give further information about the workflow. - -The `version` property can be used to provide a specific workflow version. It must use the [semantic versioning](https://semver.org/) format.If not specified, "latest" is assumed. - -The `annotations` property defines a list of helpful terms describing the workflows intended purpose, subject areas, or other important qualities, -for example "machine learning", "monitoring", "networking", etc - -The `dataInputSchema` and `dataOutputSchema` properties can be used to validate input and output data against a defined JSON Schema. - -The `dataInputSchema` property validates the [workflow data input](#Workflow-Data-Input). Validation should be performed before any states are executed. In case of -a start [Event state](#Event-state) the input schema is ignored, if present. The `failOnValidationErrors` property determines if workflow execution should continue in case of validation errors. - -The `dataOutputSchema` property validates the [Workflow data output](#workflow-data-output). Validation is performed on the output of the workflow execution. -The `failOnValidationErrors` property determines what should be done when the workflow output does not match the provided schema. -If `failOnValidationErrors` is true, an error should be thrown. If executed within a subprocess, that error can be be handled by the parent workflow. -If `failOnValidationErrors` is false, the error should not be propagated. It is up to the implementor to warn the user about that fact. For example, printing a log. - -Both properties can be expressed as object or string type. - -If using object type, their `schema` property might be an URI, which points to the JSON schema used to validate the workflow data input, or it might be the JSON schema object. `failOnValidationErrors` is optional, default value is `true`. - -Example for Json schema reference - -```json -"dataInputSchema": { - "schema": "URI to json schema", - "failOnValidationErrors": false -} -``` - -Example for Json schema included in the workflow file - -```json -"dataOutputSchema": { - "schema": { - "title": "MyJSONSchema", - "properties":{ - "firstName":{ - "type": "string" - }, - "lastName":{ - "type": "string" - } - } - }, - "failOnValidationErrors": true -} - -``` - -If using string type, then the string value is the external schema URI and `failOnValidationErrors` default value of `true` is assumed. - -Example using string type - -```json -"dataInputSchema": "URI_to_json_schema" -``` - -The `secrets` property allows you to use sensitive information such as passwords, OAuth tokens, ssh keys, etc. inside your -Workflow expressions. - -It has two possible types, `string` or `array`. -If `string` type, it is an URI pointing to a JSON or YAML document -which contains an array of names of the secrets, for example: - -```json -"secrets": "file://workflowsecrets.json" -``` - -If `array` type, it defines an array (of string types) which contains the names of the secrets, for example: - -```json -"secrets": ["MY_PASSWORD", "MY_STORAGE_KEY", "MY_ACCOUNT"] -``` - -For more information about Workflow secrets, reference the [Workflow Secrets section](#Workflow-Secrets). - -The `constants` property can be used to define Workflow constants values -which are accessible in [Workflow Expressions](#Workflow-Expressions). - -It has two possible types, `string` or `object`. -If `string` type, it is an URI pointing to a JSON or YAML document -which contains an object of global definitions, for example: - -```json -"constants": "file://workflowconstants.json" -``` - -If `object` type, it defines a JSON object which contains the constants definitions, for example: - -```json -{ - "AGE": { - "MIN_ADULT": 18 - } -} -``` - -For more information see the [Workflow Constants](#Workflow-Constants) section. - -The `start` property defines the workflow starting information. For more information see the [start definition](#Start-Definition) section. -This property is not required. If not defined, the workflow starting state has to be -the very first state defined in the [workflow states array](#Workflow-States). - -The `specVersion` property is used to set the Serverless Workflow specification release version -the workflow markup adheres to. -It has to follow the specification release versions (excluding the leading "v"), meaning that for -the [release version v0.8](https://github.com/serverlessworkflow/specification/releases/tag/v0.8) -its value should be set to `"0.8"`. - -The `expressionLang` property can be used to identify the expression language used for all expressions in -the workflow definition. The default value of this property is ["jq"](https://stedolan.github.io/jq/). -You should set this property if you chose to define [workflow expressions](#Workflow-Expressions) -with an expression language / syntax other than the default. - -The `timeouts` property is used to define the default workflow timeouts for workflow, state, action, and branch -execution. For more information about timeouts and its use cases see the [Workflow Timeouts](#Workflow-Timeouts) section. - -The `error` property is used to define checked errors that can be explicitly handled during workflow execution. -For more information about workflow error handling see [this section](#Defining-Errors). - -The `auth` property can be either an inline [auth](#Auth-Definition) definition array, or a URI reference to -a resource containing an array of [auth](#Auth-Definition) definitions. -If defined in a separate resource file (Json or Yaml), `auth` definitions can be re-used by multiple workflow definitions. -Auth definitions can be used to define authentication that should be used to access -the resource defined in the `operation` property of the [function](#Function-Definition) definitions. -If we have the following function definition: - -```json -{ - "functions": [ - { - "name": "hello-world-function", - "operation": "https://secure.resources.com/myapi.json#helloWorld", - "authRef": "my-basic-auth" - } - ] -} -``` - -The `authRef` property is used to reference an authentication definition in -the `auth` property and should be applied when invoking the `helloWorld` function. An [AuthRef](#AuthRef-Definition) object can alternatively be used to configure the authentication definition to use when accessing the function's resource and/or when invoking the function. - -The `functions` property can be either an in-line [function](#Function-Definition) definition array, or an URI reference to -a resource containing an array of [functions](#Function-Definition) definition. -Referenced resource can be used by multiple workflow definitions. - -Here is an example of using external resource for function definitions: - -1. Workflow definition: - -```json -{ - "name": "sample-workflow", - "version": "1.0.0", - "specVersion": "0.8", - "description": "Sample Workflow", - "start": "MyStartingState", - "functions": "http://myhost:8080/functiondefs.json", - "states":[ - ... - ] -} -``` - -2. Function definitions resource: - -```json -{ - "functions": [ - { - "name":"hello-world-function", - "operation":"file://myapi.json#helloWorld" - } - ] -} -``` - -Referenced resource must conform to the specifications [Workflow Functions JSON Schema](schema/functions.json). - -The `events` property can be either an in-line [event](#Event-Definition) definition array, or an [URI](https://en.wikipedia.org/wiki/Uniform_Resource_Identifier) reference to -a resource containing an array of [event](#Event-Definition) definition. Referenced resource can be used by multiple workflow definitions. - -Here is an example of using external resource for event definitions: - -1. Workflow definition: - -```json -{ - "name": "sample-workflow", - "version": "1.0.0", - "specVersion": "0.8", - "description": "Sample Workflow", - "start": "MyStartingState", - "events": "http://myhost:8080/eventsdefs.json", - "states":[ - ... - ] -} -``` - -2. Event definitions resource: - -```json -{ - "events": [ - { - "name": "applicant-info", - "type": "org.application.info", - "source": "applicationssource", - "correlation": [ - { - "contextAttributeName": "applicantId" - } - ] - } - ] -} -``` - -Referenced resource must conform to the specifications [Workflow Events JSON Schema](schema/events.json). - -The `retries` property can be either an in-line [retry](#Retry-Definition) definition array, or an URI reference to -a resource containing an array of [retry](#Retry-Definition) definition. -Referenced resource can be used by multiple workflow definitions. For more information about -using and referencing retry definitions see the [Workflow Error Handling](#Workflow-Error-Handling) section. - -The `keepActive` property allows you to change the default behavior of workflow instances. -By default, as described in the [Core Concepts](#Core-Concepts) section, a workflow instance is terminated once there are no more -active execution paths, one of its active paths ends in a "terminate" [end definition](#End-Definition), or when -its [`workflowExecTimeout`](#Workflow-Timeouts) time is reached. - -Setting the `keepActive` property to `true` allows you to change this default behavior in that a workflow instance -created from this workflow definition can only be terminated if one of its active paths ends in a "terminate" [end definition](#End-Definition), or when -its [`workflowExecTimeout`](#Workflow-Timeouts) time is reached. -This allows you to explicitly model workflows where an instance should be kept alive, to collect (event) data for example. - -You can reference the [specification examples](#Examples) to see the `keepActive` property in action. - -The `extensions` property can be used to define extensions for this workflow definition. -You can learn more about workflow extensions in the [Extensions](#extensions) section. -Sample `extensions` property definition could look like this for example: - -```json -{ - "extensions": [ - { - "extensionId": "workflow-ratelimiting-extension", - "path": "file://myextensions/ratelimiting.yml" - }, - { - "extensionId": "workflow-kpi-extension", - "path": "file://myextensions/kpi.yml" - } - ] -} -``` - -Here we define two workflow extensions, namely the [rate limiting](extensions/ratelimiting.md) and [kpi](extensions/kpi.md) extensions for our workflow definition. - -#### Workflow States - -Workflow states define building blocks of the workflow execution instructions. They define the -control flow logic instructions on what the workflow is supposed to do. -Serverless Workflow defines the following Workflow States: - -| Name | Description | Consumes events? | Produces events? | Executes actions? | Handles errors/retries? | Allows parallel execution? | Makes data-based transitions? | Can be workflow start state? | Can be workflow end state? | -| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | -| **[Event](#Event-State)** | Define events that trigger action execution | yes | yes | yes | yes | yes | no | yes | yes | -| **[Operation](#Operation-State)** | Execute one or more actions | no | yes | yes | yes | yes | no | yes | yes | -| **[Switch](#Switch-State)** | Define data-based or event-based workflow transitions | no | yes | no | yes | no | yes | yes | no | -| **[Parallel](#Parallel-State)** | Causes parallel execution of branches (set of states) | no | yes | no | yes | yes | no | yes | yes | -| **[Inject](#Inject-State)** | Inject static data into state data | no | yes | no | yes | no | no | yes | yes | -| **[ForEach](#ForEach-State)** | Parallel execution of states for each element of a data array | no | yes | no | yes | yes | no | yes | yes | -| **[Callback](#Callback-State)** | Manual decision step. Executes a function and waits for callback event that indicates completion of the manual decision | yes | yes | yes | yes | no | no | yes | yes | - -##### Event State - -| Parameter | Description | Type | Required | -| --- | --- | --- | --- | -| name | Unique State name | string | yes | -| type | State type | string | yes | -| exclusive | If `true`, consuming one of the defined events causes its associated actions to be performed. If `false`, all of the defined events must be consumed in order for actions to be performed. Default is `true` | boolean | no | -| [onEvents](#OnEvents-Definition) | Define the events to be consumed and optional actions to be performed | array | yes | -| [timeouts](#Workflow-Timeouts) | State specific timeout settings | object | no | -| [stateDataFilter](#State-data-filters) | State data filter definition| object | no | -| [transition](#Transitions) | Next transition of the workflow after all the actions have been performed | string or object | yes (if `end` is not defined) | -| [onErrors](#Error-Definition) | States error handling definitions | array | no | -| [end](#End-Definition) | Is this state an end state | boolean or object | yes (if `transition` is not defined) | -| [compensatedBy](#Workflow-Compensation) | Unique name of a workflow state which is responsible for compensation of this state | string | no | -| [metadata](#Workflow-Metadata) | Metadata information| object | no | - -- -
JSON | -YAML | -
---|---|
- -```json -{ -"name": "monitor-vitals", -"type": "event", -"exclusive": true, -"onEvents": [{ - "eventRefs": ["high-body-temperature"], - "actions": [{ - "functionRef": { - "refName": "send-tylenol-order", - "arguments": { - "patientid": "${ .patientId }" - } - } - }] - }, - { - "eventRefs": ["high-blood-pressure"], - "actions": [{ - "functionRef": { - "refName": "call-nurse", - "arguments": { - "patientid": "${ .patientId }" - } - } - }] - }, - { - "eventRefs": ["high-respiration-rate"], - "actions": [{ - "functionRef": { - "refName": "call-pulmonologist", - "arguments": { - "patientid": "${ .patientId }" - } - } - }] - } -], -"end": { - "terminate": true -} -} -``` - - | -- -```yaml -name: monitor-vitals -type: event -exclusive: true -onEvents: -- eventRefs: - - high-body-temperature - actions: - - functionRef: - refName: send-tylenol-order - arguments: - patientid: "${ .patientId }" -- eventRefs: - - high-blood-pressure - actions: - - functionRef: - refName: call-nurse - arguments: - patientid: "${ .patientId }" -- eventRefs: - - high-respiration-rate - actions: - - functionRef: - refName: call-pulmonologist - arguments: - patientid: "${ .patientId }" -end: - terminate: true -``` - - | -
- -
- -If the Event state in this case is a workflow starting state, the occurrence of *any* of the defined events would start a new workflow instance. - -- -
- -If the Event state in this case is a workflow starting state, the occurrence of *all* defined events would start a new -workflow instance. - -In order to consider only events that are related to each other, we need to set the `correlation` property in the workflow -[events definitions](#Event-Definition). This allows us to set up event correlation rules against the events -extension context attributes. - -If the Event state is not a workflow starting state, the `timeout` property can be used to define the time duration from the -invocation of the event state. If the defined event, or events have not been received during this time, -the state should transition to the next state or can end the workflow execution (if it is an end state). - -The `timeouts` property can be used to define state specific timeout settings. Event states can define the -`stateExecTimeout`, `actionExecTimeout`, and `eventTimeout` properties. -For more information about Event state specific event timeout settings reference [this section](#Event-Timeout-Definition). -For more information about workflow timeouts reference the [Workflow Timeouts](#Workflow-Timeouts) section. - -Note that `transition` and `end` properties are mutually exclusive, meaning that you cannot define both of them at the same time. - -##### Operation State - -| Parameter | Description | Type | Required | -| --- | --- | --- | --- | -| name | Unique State name. Must follow the [Serverless Workflow Naming Convention](#naming-convention) | string | yes | -| type | State type | string | yes | -| actionMode | Should actions be performed sequentially or in parallel. Default is `sequential` | enum | no | -| [actions](#Action-Definition) | Actions to be performed | array | yes | -| [timeouts](#Workflow-Timeouts) | State specific timeout settings | object | no | -| [stateDataFilter](#State-data-filters) | State data filter | object | no | -| [onErrors](#Error-Definition) | States error handling and retries definitions | array | no | -| [transition](#Transitions) | Next transition of the workflow after all the actions have been performed | string or object | yes (if `end` is not defined) | -| [compensatedBy](#Workflow-Compensation) | Unique name of a workflow state which is responsible for compensation of this state | string | no | -| [usedForCompensation](#Workflow-Compensation) | If `true`, this state is used to compensate another state. Default is `false` | boolean | no | -| [metadata](#Workflow-Metadata) | Metadata information| object | no | -| [end](#End-Definition) | Is this state an end state | boolean or object | yes (if `transition` is not defined) | - -- -
JSON | -YAML | -
---|---|
- -```json -{ - "name": "reject-application", - "type": "operation", - "actionMode": "sequential", - "actions": [ - { - "functionRef": { - "refName": "send-rejection-email-function", - "arguments": { - "customer": "${ .customer }" - } - } - } - ], - "end": true -} -``` - - | -- -```yaml -name: reject-application -type: operation -actionMode: sequential -actions: -- functionRef: - refName: send-rejection-email-function - arguments: - customer: "${ .customer }" -end: true -``` - - | -
- -
JSON | -YAML | -
---|---|
- -```json -{ - "name":"check-visa-status", - "type":"switch", - "eventConditions": [ - { - "eventRef": "visa-approved-event", - "transition": "handle-approved-visa" - }, - { - "eventRef": "visa-rejected-event", - "transition": "handle-rejected-visa" - } - ], - "timeouts": { - "eventTimeout": "PT1H" - }, - "defaultCondition": { - "transition": "handle-no-visa-decision" - } -} -``` - - | -- -```yaml -name: check-visa-status -type: switch -eventConditions: -- eventRef: visa-approved-event - transition: handle-approved-visa -- eventRef: visa-rejected-event - transition: handle-rejected-visa -timeouts: - eventTimeout: PT1H -defaultCondition: - transition: handle-no-visa-decision -``` - - | -
- -
JSON | -YAML | -
---|---|
- -```json - { - "name":"parallel-exec", - "type":"parallel", - "completionType": "allOf", - "branches": [ - { - "name": "branch-1", - "actions": [ - { - "functionRef": { - "refName": "function-name-one", - "arguments": { - "order": "${ .someParam }" - } - } - } - ] - }, - { - "name": "branch-2", - "actions": [ - { - "functionRef": { - "refName": "function-name-two", - "arguments": { - "order": "${ .someParam }" - } - } - } - ] - } - ], - "end": true -} -``` - - | -- -```yaml -name: parallel-exec -type: parallel -completionType: allOf -branches: -- name: branch-1 - actions: - - functionRef: - refName: function-name-one - arguments: - order: "${ .someParam }" -- name: branch-2 - actions: - - functionRef: - refName: function-name-two - arguments: - order: "${ .someParam }" -end: true -``` - - | -
- -
JSON | -YAML | -
---|---|
- -```json -{ - "name":"hello", - "type":"inject", - "data": { - "result": "Hello" - }, - "transition": "world" -} -``` - - | -- -```yaml -name: hello -type: inject -data: - result: Hello -transition: world -``` - - | -
JSON | -YAML | -
---|---|
- - ```json - { - "name":"simple-inject-state", - "type":"inject", - "data": { - "person": { - "fname": "John", - "lname": "Doe", - "address": "1234 SomeStreet", - "age": 40 - } - }, - "transition": "greet-person-state" - } - ``` - - | -- -```yaml - name: simple-inject-state - type: inject - data: - person: - fname: John - lname: Doe - address: 1234 SomeStreet - age: 40 - transition: greet-person-state -``` - - | -
JSON | -YAML | -
---|---|
- -```json - { - "name":"simple-inject-state", - "type":"inject", - "data": { - "people": [ - { - "fname": "John", - "lname": "Doe", - "address": "1234 SomeStreet", - "age": 40 - }, - { - "fname": "Marry", - "lname": "Allice", - "address": "1234 SomeStreet", - "age": 25 - }, - { - "fname": "Kelly", - "lname": "Mill", - "address": "1234 SomeStreet", - "age": 30 - } - ] - }, - "stateDataFilter": { - "output": "${ {people: [.people[] | select(.age < 40)]} }" - }, - "transition": "greet-person-state" - } -``` - - | -- -```yaml - name: simple-inject-state - type: inject - data: - people: - - fname: John - lname: Doe - address: 1234 SomeStreet - age: 40 - - fname: Marry - lname: Allice - address: 1234 SomeStreet - age: 25 - - fname: Kelly - lname: Mill - address: 1234 SomeStreet - age: 30 - stateDataFilter: - output: "${ {people: [.people[] | select(.age < 40)]} }" - transition: greet-person-state -``` - - | -
- -
JSON | -YAML | -
---|---|
- -```json -{ - "name": "provision-orders-state", - "type": "foreach", - "inputCollection": "${ .orders }", - "iterationParam": "singleorder", - "outputCollection": "${ .provisionresults }", - "actions": [ - { - "functionRef": { - "refName": "provision-order-function", - "arguments": { - "order": "${ $singleorder }" - } - } - } - ] -} -``` - - | -- -```yaml -name: provision-orders-state -type: foreach -inputCollection: "${ .orders }" -iterationParam: "singleorder" -outputCollection: "${ .provisionresults }" -actions: -- functionRef: - refName: provision-order-function - arguments: - order: "${ $singleorder }" -``` - - | -
JSON | -YAML | -
---|---|
- -```json -{ - "name": "send-confirmation-for-completed-orders", - "version": "1.0.0", - "specVersion": "0.8", - "start": "send-confirm-state", - "functions": [ - { - "name": "send-confirmation-function", - "operation": "file://confirmationapi.json#sendOrderConfirmation" - } - ], - "states": [ - { - "name":"send-confirm-state", - "type":"foreach", - "inputCollection": "${ [.orders[] | select(.completed == true)] }", - "iterationParam": "completedorder", - "outputCollection": "${ .confirmationresults }", - "actions":[ - { - "functionRef": { - "refName": "send-confirmation-function", - "arguments": { - "orderNumber": "${ $completedorder.orderNumber }", - "email": "${ $completedorder.email }" - } - } - }], - "end": true - }] -} -``` - - | -- -```yaml -name: send-confirmation-for-completed-orders -version: '1.0.0' -specVersion: '0.8' -start: send-confirm-state -functions: -- name: send-confirmation-function - operation: file://confirmationapi.json#sendOrderConfirmation -states: -- name: send-confirm-state - type: foreach - inputCollection: "${ [.orders[] | select(.completed == true)] }" - iterationParam: completedorder - outputCollection: "${ .confirmationresults }" - actions: - - functionRef: - refName: send-confirmation-function - arguments: - orderNumber: "${ $completedorder.orderNumber }" - email: "${ $completedorder.email }" - end: true -``` - - | -
- -
JSON | -YAML | -
---|---|
- -```json -{ - "name": "check-credit", - "type": "callback", - "action": { - "functionRef": { - "refName": "call-credit-check-microservice", - "arguments": { - "customer": "${ .customer }" - } - } - }, - "eventRef": "credit-check-completed-event", - "timeouts": { - "stateExecTimeout": "PT15M" - }, - "transition": "evaluate-decision" -} -``` - - | -- -```yaml -name: check-credit -type: callback -action: - functionRef: - refName: call-credit-check-microservice - arguments: - customer: "${ .customer }" -eventRef: credit-check-completed-event -timeouts: - stateExecTimeout: PT15M -transition: evaluate-decision -``` - - | -
- -
JSON | -YAML | -
---|---|
- -```json -{ - "name": "hello-world-function", - "operation": "https://hellworldservice.api.com/api.json#helloWorld" -} -``` - - | -- -```yaml -name: hello-world-function -operation: https://hellworldservice.api.com/api.json#helloWorld -``` - - | -
- -
JSON | -YAML | -
---|---|
- -```json -{ - "name": "applicant-info", - "type": "org.application.info", - "source": "applicationssource", - "correlation": [ - { - "contextAttributeName": "applicantId" - } - ] -} -``` - - | -- -```yaml -name: applicant-info -type: org.application.info -source: applicationssource -correlation: -- contextAttributeName: applicantId -``` - - | -
- -
JSON | -YAML | -
---|---|
- -```json -{ - "correlation": [ - { - "contextAttributeName": "patientId" - }, - { - "contextAttributeName": "department", - "contextAttributeValue" : "UrgentCare" - } - ] -} -``` - - | -- -```yaml -correlation: -- contextAttributeName: patientId -- contextAttributeName: department - contextAttributeValue: UrgentCare -``` - - | -
- -
JSON | -YAML | -
---|---|
- -```json -{ - "eventRefs": ["high-body-temperature"], - "actions": [{ - "functionRef": { - "refName": "send-tylenol-order", - "arguments": { - "patientid": "${ .patientId }" - } - } - }] -} -``` - - | -- -```yaml -eventRefs: -- high-body-temperature -actions: -- functionRef: - refName: send-tylenol-order - arguments: - patientid: "${ .patientId }" -``` - - | -
- -
- -##### Action Definition - -| Parameter | Description | Type | Required | -| --- | --- | --- | --- | -| name | Unique Action name. Must follow the [Serverless Workflow Naming Convention](#naming-convention) | string | yes | -| [functionRef](#FunctionRef-Definition) | References a reusable function definition | object or string | yes (if `eventRef` & `subFlowRef` are not defined) | -| [eventRef](#EventRef-Definition) | References a `produce` and `consume` reusable event definitions | object | yes (if `functionRef` & `subFlowRef` are not defined) | -| [subFlowRef](#SubFlowRef-Definition) | References a workflow to be invoked | object or string | yes (if `eventRef` & `functionRef` are not defined) | -| onErrors | Defines the error handling policy to use | string or array of [error handler references](#error-handler-reference) | no | -| [actionDataFilter](#Action-data-filters) | Action data filter definition | object | no | -| sleep | Defines time periods workflow execution should sleep before / after function execution | object | no | -| [condition](#Workflow-Expressions) | Expression, if defined, must evaluate to `true` for this action to be performed. If `false`, action is disregarded | string | no | - -- -
JSON | -YAML | -
---|---|
- -```json -{ - "name": "finalize-application-action", - "functionRef": { - "refName": "finalize-application-function", - "arguments": { - "applicantid": "${ .applicantId }" - } - } -} -``` - - | -- -```yaml -name: finalize-application-action -functionRef: - refName: finalize-application-function - arguments: - applicantid: "${ .applicantId }" -``` - - | -
- -
- -Reusable workflows are referenced by their `name` property via the SubFlow action `workflowId` parameter. - -For the simple case, `subFlowRef` can be a string containing the `name` of the sub-workflow to invoke. -If you want to specify other parameters then a [subFlowRef](#SubFlowRef-Definition) should be provided instead. - -Each referenced workflow receives the SubFlow actions data as workflow data input. - -Referenced sub-workflows must declare their own [function](#Function-Definition) and [event](#Event-Definition) definitions. - -##### FunctionRef Definition - -`FunctionRef` definition can have two types, either `string` or `object`. -If `string` type, it defines the name of the referenced [function](#Function-Definition). -This can be used as a short-cut definition when you don't need to define any other parameters, for example: - -```json -"functionRef": "my-function" -``` - -Note that if used with `string` type, the invocation of the function is synchronous. - -If you need to define parameters in your `functionRef` definition, you can define -it with its `object` type which has the following properties: - -| Parameter | Description | Type | Required | -| --- | --- | --- | --- | -| refName | Name of the referenced [function](#Function-Definition). Must follow the [Serverless Workflow Naming Convention](#naming-convention) | string | yes | -| arguments | Arguments (inputs) to be passed to the referenced function | object | yes (if function type is `graphql`, otherwise no) | -| selectionSet | Used if function type is `graphql`. String containing a valid GraphQL [selection set](https://spec.graphql.org/June2018/#sec-Selection-Sets) | string | yes (if function type is `graphql`, otherwise no) | -| invoke | Specifies if the function should be invoked `sync` or `async`. Default is `sync` | enum | no | - -- -
JSON | -YAML | -
---|---|
- -```json -{ - "refName": "finalize-application-function", - "arguments": { - "applicantid": "${ .applicantId }" - } -} -``` - - | -- -```yaml -refName: finalize-application-function -arguments: - applicantid: "${ .applicantId }" -``` - - | -
- -
JSON | -YAML | -
---|---|
- -```json -{ - "publish": { - "event": "make-vet-appointment", - "data": "${ .patientInfo }", - } -} -``` - - | -- -```yaml -publish: - event: make-vet-appointment - data: "${ .patientInfo }" -``` - - | -
- -
JSON | -YAML | -
---|---|
- -```json -{ - "subscribe": { - "name": "approved-appointment", - } -} -``` - - | -- -```yaml -eventRef: - subscribe: approved-appointment - -``` - - | -
- -
JSON | -YAML | -
---|---|
- -```json -{ - "workflowId": "handle-approved-visa", - "version": "2.0.0" -} -``` - - | -- -```yaml -workflowId: handle-approved-visa -version: '2.0.0' -``` - - | -
- -
JSON | -YAML | -
---|---|
- -```json -{ - "retries": [ - { - "name": "retry-five-times", - "maxAttempts": 5 - } - ], - "errors": { - "definitions": [ - { - "name": "service-not-available-error", - "type": "https://serverlessworkflow.io/spec/errors/communication", - "status": 503, - "title": "Service Not Available", - "detail": "Failed to contact service, even after multiple retries" - } - ], - "handlers": [ - { - "name": "handle-503", - "when": [ - { - "status": 503 - } - ], - "retry": "retry-five-times", - "then": { - "throw": { - "refName": "service-not-available-error" - } - } - } - ], - "policies": [ - { - "name": "fault-tolerance-policy", - "handlers": [ - { - "refName": "handle-503" - } - ] - } - ] - } -} -``` - - | -- -```yaml -retries: - - name: retry-five-times - maxAttempts: 5 -errors: - definitions: - - name: service-not-available-error - type: https://serverlessworkflow.io/spec/errors/communication - status: 503 - title: Service Not Available - detail: Failed to contact service, even after multiple retries - handlers: - - name: handle-503 - when: - - status: 503 - retry: retry-five-times - then: - throw: - refName: service-not-available-error - policies: - - name: fault-tolerance-policy - handlers: - - refName: handle-503 -``` - - | -
- -
JSON | -YAML | -
---|---|
- -```json -{ - "instance": "/states/0/actions/0", - "type": "https://example.com/errors#timeout", - "status": 504, - "title": "Function Timeout", - "detail": "The function 'my-function' timed out." -} -``` - - | -- -```yaml -instance: "/states/0/actions/0" -type: "https://example.com/errors#timeout" -status: 504 -title: "Function Timeout" -detail: "The function 'my-function' timed out." -``` - - | -
- -
JSON | -YAML | -
---|---|
- -```json -{ - "type": "https://example.com/errors#timeout", - "status": 504 -} -``` - - | -- -```yaml -type: "https://example.com/errors#timeout" -status: 504 -``` - - | -
- -
JSON | -YAML | -
---|---|
- -```json -{ - "errors": { - "handlers": [ - { - "name": "handle-invalid-error", - "when": [ - { "error": "invalid" }, - { "status": 404 }, - { "status": 403 } - ], - "then": { - "transition": "my-state" - } - }, - { - "name": "handle-timeout-error", - "when": [ - { "status": 503 } - ], - "retry": "my-retry-policy", - "then": { - "transition": "my-state" - } - } - ] - } -} -``` - - | -- -```yaml -errors: - handlers: - - name: 'handle-invalid-error' - when: - - type: 'invalid' - - status: 404 - - status: 403 - then: - transition: 'my-state' - - name: 'handle-timeout-error' - when: - - status: 503 - retry: 'my-retry-policy' - then: - transition: 'my-state' -``` - - | -
- -
JSON | -YAML | -
---|---|
- -```json -{ - "errors": { - "policies": [ - { - "name": "my-retry-policy", - "handlers": [ - { - "refName": "handle-timeout-error" - }, - { - "when": [ - { "status": 503 } - ], - "retry": "my-retry-policy" - } - ] - } - ] - } -} -``` - - | -- -```yaml -errors: - policies: - - name: 'my-retry-policy' - handlers: - - refName: 'handle-timeout-error' - - when: - - status: 503 - retry: 'my-retry-policy' -``` - - | -
- -
JSON | -YAML | -
---|---|
- -```json -{ - "errors": { - "policies": [ - { - "name": "my-retry-policy", - "handlers": [ - { - "refName": "handle-timeout-error" - }, - { - "when": [ - { "status": 503 } - ], - "retry": "my-retry-policy" - } - ] - } - ] - } -} -``` - - | -- -```yaml -errors: - policies: - - name: 'my-retry-policy' - handlers: - - refName: 'handle-timeout-error' - - when: - - status: 503 - retry: 'my-retry-policy' -``` - - | -
- -
JSON | -YAML | -
---|---|
- -```json -{ - "when": [ - { - "status": 503 - } - ], - "then": { - "transition": "my-state" - } -} -``` - - | -- -```yaml -when: - - status: 503 -then: - transition: 'my-state' -``` - - | -
- -
JSON | -YAML | -
---|---|
- -```json -{ - "throw": { - "type": "https://serverlessworkflow.io/spec/errors/runtime", - "status": 400, - "detail": "${ $CONST.localizedErrorDetail }" - } -} -``` - - | -- -```yaml -throw: - type: https://serverlessworkflow.io/spec/errors/runtime - status: 400 - detail: ${ $CONST.localizedErrorDetail } -``` - - | -
- -
JSON | -YAML | -
---|---|
- -```json -{ - "name": "timeout-retry-strat", - "delay": "PT2M", - "maxAttempts": 3, - "jitter": "PT0.001S" -} -``` - - | -- -```yaml -name: timeout-retry-strat -delay: PT2M -maxAttempts: 3 -jitter: PT0.001S -``` - - | -
- -
JSON | -YAML | -
---|---|
- -```json -{ - "produceEvents": [{ - "eventRef": "produce-result-event", - "data": "${ .result.data }" - }], - "nextState": "eval-result-state" -} -``` - - | -- -```yaml -produceEvents: -- eventRef: produce-result-event - data: "${ .result.data }" -nextState: eval-result-state -``` - - | -
- -
JSON | -YAML | -
---|---|
- -```json -{ - "name": "eighteen-or-older", - "condition": "${ .applicant | .age >= 18 }", - "transition": "start-application" -} -``` - - | -- -```yaml -name: eighteen-or-older -condition: "${ .applicant | .age >= 18 }" -transition: start-application -``` - - | -
- -
JSON | -YAML | -
---|---|
- -```json -{ - "name": "visa-approved", - "eventRef": "visa-approved-event", - "transition": "handle-approved-visa" -} -``` - - | -- -```yaml -name: visa-approved -eventRef: visa-approved-event -transition: handle-approved-visa -``` - - | -
- -
JSON | -YAML | -
---|---|
- -```json -{ - "name": "branch-1", - "actions": [ - { - "functionRef": { - "refName": "function-name-one", - "arguments": { - "order": "${ .someParam }" - } - } - }, - { - "functionRef": { - "refName": "function-name-two", - "arguments": { - "order": "${ .someParamTwo }" - } - } - } - ] -} -``` - - | -- -```yaml -name: branch-1 -actions: -- functionRef: - refName: function-name-one - arguments: - order: "${ .someParam }" -- functionRef: - refName: function-name-two - arguments: - order: "${ .someParamTwo }" -``` - - | -
- -
JSON | -YAML | -
---|---|
- -```json -{ - "stateName": "my-starting-state", - "schedule": "2020-03-20T09:00:00Z/PT2H" -} -``` - - | -- -```yaml -stateName: my-starting-state -schedule: 2020-03-20T09:00:00Z/PT2H -``` - - | -
- -
JSON | -YAML | -
---|---|
- -```json -{ - "cron": "0 0/15 * * * ?" -} -``` - - | -- -```yaml -cron: 0 0/15 * * * ? -``` - - | -
- -
JSON | -YAML | -
---|---|
- -```json -{ - "expression": "0 15,30,45 * ? * *", - "validUntil": "2021-11-05T08:15:30-05:00" -} -``` - - | -- -```yaml -expression: 0 15,30,45 * ? * * -validUntil: '2021-11-05T08:15:30-05:00' -``` - - | -
- -
JSON | -YAML | -
---|---|
- -```json -{ - "terminate": true, - "produceEvents": [{ - "eventRef": "provisioning-complete-event", - "data": "${ .provisionedOrders }" - }] -} -``` - - | -- -```yaml -terminate: true -produceEvents: -- eventRef: provisioning-complete-event - data: "${ .provisionedOrders }" - -``` - - | -
- -
JSON | -YAML | -
---|---|
- -```json -{ - "eventRef": "provisioning-complete-event", - "data": "${ .provisionedOrders }", - "contextAttributes": { - "buyerId": "${ .buyerId }" - } - } -``` - - | -- -```yaml -eventRef: provisioning-complete-event -data: "${ .provisionedOrders }" -contextAttributes: - buyerId: "${ .buyerId }" -``` - - | -
JSON | -YAML | -
---|---|
- -```json -{ - "actions": [ - { - "name": "my-action", - "functionRef": "my-function", - "onErrors": [ - { - "when": [ - { - "status": 503 - } - ], - "retry": "retry-five-times" - } - ] - } - ] -} - -``` - - | -- -```yaml -actions: - - name: my-action - functionRef: my-function - onErrors: - - when: - - status: 503 - retry: retry-five-times -``` - - | -
JSON | -YAML | -
---|---|
- -```json -{ - "errors": { - "definitions": [ - { - "name": "service-not-available-error", - "type": "https://serverlessworkflow.io/spec/errors/communication", - "status": 503, - "title": "Service Not Available", - "detail": "Failed to contact service, even after multiple retries" - } - ], - "handlers": [ - { - "name": "handle-503", - "when": [ - { - "status": 503 - } - ], - "retry": "retry-five-times", - "then": { - "throw": { - "refName": "service-not-available-error" - } - } - } - ] - }, - "states": [ - { - "name": "my-state", - "type": "operation", - "actions": [ - { - "name": "my-action", - "functionRef": "my-function", - "onErrors": [ - { - "refName": "handle-503" - } - ] - } - ] - } - ] -} - -``` - - | -- -```yaml -errors: - definitions: - - name: service-not-available-error - type: https://serverlessworkflow.io/spec/errors/communication - status: 503 - title: Service Not Available - detail: Failed to contact service, even after multiple retries - handlers: - - name: handle-503 - when: - - status: 503 - retry: retry-five-times - then: - throw: - refName: service-not-available-error -states: - - name: my-state - type: operation - actions: - - name: my-action - functionRef: my-function - onErrors: - - refName: handle-503 -``` - - | -
JSON | -YAML | -
---|---|
- -```json -{ - "errors": { - "definitions": [ - { - "name": "service-not-available-error", - "type": "https://serverlessworkflow.io/spec/errors/communication", - "status": 503, - "title": "Service Not Available", - "detail": "Failed to contact service, even after multiple retries" - } - ], - "handlers": [ - { - "name": "handle-503", - "when": [ - { - "status": 503 - } - ], - "retry": "retry-five-times", - "then": { - "throw": { - "refName": "service-not-available-error" - } - } - } - ] - }, - "states": [ - { - "name": "my-state", - "type": "operation", - "actions": [ - { - "name": "my-action", - "functionRef": "my-function", - "onErrors": [ - { - "refName": "handle-503" - } - ] - } - ] - } - ] -} - -``` - - | -- -```yaml -errors: - definitions: - - name: service-not-available-error - type: https://serverlessworkflow.io/spec/errors/communication - status: 503 - title: Service Not Available - detail: Failed to contact service, even after multiple retries - handlers: - - name: handle-503 - when: - - status: 503 - retry: retry-five-times - then: - throw: - refName: service-not-available-error - policy: - - name: fault-tolerance - handlers: - - refName: handle-503 -states: - - name: my-state - type: operation - actions: - - name: my-action - functionRef: my-function - onErrors: fault-tolerance -``` - - | -
- -
JSON | -YAML | -
---|---|
- -```json -{ - "duration": "PT2M", - "runBefore": "createandsendreport" -} -``` - - | -- -```yaml -duration: PT2M -runBefore: createandsendreport -``` - - | -
JSON | -YAML | -
---|---|
- -```json - { - "states": [ - { - "name": "new-item-purchase", - "type": "event", - "onEvents": [ - { - "eventRefs": [ - "new-purchase" - ], - "actions": [ - { - "functionRef": { - "refName": "debit-customer-function", - "arguments": { - "customerid": "${ .purchase.customerid }", - "amount": "${ .purchase.amount }" - } - } - }, - { - "functionRef": { - "refName": "send-purchase-confirmation-email-function", - "arguments": { - "customerid": "${ .purchase.customerid }" - } - } - } - ] - } - ], - "compensatedBy": "cancel-purchase", - "transition": "some-next-workflow-state" - }, - { - "name": "cancel-purchase", - "type": "operation", - "usedForCompensation": true, - "actions": [ - { - "functionRef": { - "refName": "credit-customer-function", - "arguments": { - "customerid": "${ .purchase.customerid }", - "amount": "${ .purchase.amount }" - } - } - }, - { - "functionRef": { - "refName": "send-purchase-cancellation-email-function", - "arguments": { - "customerid": "${ .purchase.customerid }" - } - } - } - ] - } - ] - } -``` - - | -- -```yaml -states: -- name: new-item-purchase - type: event - onEvents: - - eventRefs: - - new-purchase - actions: - - functionRef: - refName: debit-customer-function - arguments: - customerid: "${ .purchase.customerid }" - amount: "${ .purchase.amount }" - - functionRef: - refName: send-purchase-confirmation-email-function - arguments: - customerid: "${ .purchase.customerid }" - compensatedBy: cancel-purchase - transition: some-next-workflow-state -- name: CancelPurchase - type: operation - usedForCompensation: true - actions: - - functionRef: - refName: credit-customer-function - arguments: - customerid: "${ .purchase.customerid }" - amount: "${ .purchase.amount }" - - functionRef: - refName: send-purchase-cancellation-email-function - arguments: - customerid: "${ .purchase.customerid }" -``` - - | -
JSON | -YAML | -
---|---|
- -```json -{ - "transition": { - "compensate": true, - "nextState": "next-workflow-state" - } -} -``` - - | -- -```yaml -transition: - compensate: true - nextState: next-workflow-state -``` - - | -
JSON | -YAML | -
---|---|
- -```json -{ - "end": { - "compensate": true - } -} -``` - - | -- -```yaml -end: - compensate: true -``` - - | -
- -
- -In this example lets say our workflow execution is at the "End" state which defines the `compensate` property to `true` -as shown in the previous section. States with a red border, namely "A", "B", "D" and "E" are states which have so far -been executed successfully. State "C" has not been executed during workflow execution in our example. - -When workflow execution encounters our "End" state, compensation has to be performed. This is done in **reverse** order: - -1. State "E" is not compensated as it does not define a `compensatedBy` state -2. State "D" is compensated by executing compensation "D1" -3. State "B" is compensated by executing "B1" and then "B1-2" -4. State C is not compensated as it was never active during workflow execution -5. State A is not comped as it does not define a `compensatedBy` state - -So if we look just at the workflow execution flow, the same workflow could be seen as: - -- -
- -In our example, when compensation triggers, -the current workflow data is passed as input to the "D1" state, the first compensation state for our example. -The states data output is then passed as states data input to "B1", and so on. - -#### Compensation and Active States - -In some cases when compensation is triggered, some states such as [Parallel](#Parallel-State) and [ForEach](#ForEach-State) -states can still be "active", meaning they still might have some async executions that are being performed. - -If compensation needs to performed on such still active states, the state execution must be first cancelled. -After it is cancelled, compensation should be performed. - -#### Unrecoverable errors during compensation - -States that are marked as `usedForCompensation` can define [error handling](#Workflow-Error-Handling) via their -`onErrors` property just like any other workflow states. In case of unrecoverable errors during their execution -(errors not explicitly handled), -workflow execution should be stopped, which is the same behavior as when not using compensation as well. - -### Continuing as a new Execution - -In some cases our workflows are deployed and executed on runtimes and/or cloud platforms that expose some -execution limitations such as finite execution duration, finite number of workflow transitions, etc. -Some runtimes, especially when dealing with stateful workflow orchestrations have a finite limit of -execution history log sizes, meaning that once a long-running workflow reaches these limits workflow executions is -likely to be forced to stop before reaching its completion. This can result in unexpected issues, especially with -mission-critical workflows. - -For those cases, the Serverless Workflow DSL provides a way to explicitly define stopping the current workflow -instance execution, and starting a new one (for the same workflow name or a different one). -This can be done via the [end definitions](#end-definition) `continueAs` property. - -The end definitions `continueAs` can be either of type `string` or `object`. -If string type, it contains the unique workflow name of the workflow that the execution should continue as, for example: - - -```json -{ - "end": { - "continueAs": "my-workflow-name" - } -} -``` - -Defining this should stop the current workflow execution, and continue execution as a new workflow instance of the -workflow which defines the workflow name of "my-workflow-name". The state data where this is define should -become the workflow data input of the workflow that is continuing the current workflow execution. - -Note that any defined `produceEvents` and `compensate` definitions should be honored before `continueAs` is applied. - -If `object` type, the `continueAs` property has the following properties: - -| Parameter | Description | Type | Required | -| --- | --- | --- | --- | -| workflowId | Unique name of the workflow to continue execution as. | string | yes | -| version | Version of the workflow to continue execution as. | string | no | -| data | If string type, a workflow expression which selects parts of the states data output to become the workflow data input of continued execution. If object type, a custom object to become the workflow data input of the continued execution. | string or object | no | -| [`workflowExecTimeout`](#Workflow-Timeouts) | Workflow execution timeout to be used by the workflow continuing execution. Overwrites any specific settings set by that workflow. | string or object | no | - -Continuing execution with `continueAs` can also be used inside sub-workflow executions, which brings its next use case. - -#### ContinueAs in sub workflows - -Workflows can invoke sub-workflows during their execution. In Serverless Workflow DSL, sub-workflows are invoked -similarly to other function types via the [SubFlowRef Definition](#SubFlowRef-Definition) -in workflow states [Action](#Action-Definition) definitions. - -Just like "parent" workflows, sub-workflow can also be long-running, and can run into the same type of runtime/serverless platform -limitations as previously discussed. As such they can also use `continueAs` to stop their current execution and continue it as -a new one of the same or different workflow name. - -Note that when a sub-workflow is invoked it can produce a result that is then merged into the parent workflow state data. -This may bring up a question as to what happens when a sub-workflow calls `continueAs` in terms of what is returned as -result to of its invocation by the parent workflow. - -No matter how many times sub-workflow may use `continueAs`, to the parent workflow it should be as a single invocation is performed, -meaning that the results of the last sub-workflow invocation (triggered by `continueAs`) should be used as the -data returned by the invocation of the sub-workflow to the parent workflow. - -### Workflow Versioning - -In any application, regardless of size or type, one thing is for sure: changes happen. -Versioning your workflow definitions is an important task to consider. Versions indicate -changes or updates of your workflow definitions to the associated execution runtimes. - -There are two places in the [workflow definition](#Workflow-Definition-Structure) where versioning can be applied: - -1. Top level workflow definition `version` property. -2. Actions [subflowRef](#SubFlowRef-Definition) `version` property. - -The `version` property must respect the [semantic versioning](https://semver.org/) guidelines. - -### Workflow Constants - -Workflow constants are used to define static, and immutable, data which is available to [Workflow Expressions](#Workflow-Expressions). - -Constants can be defined via the [Workflow top-level "constants" property](#Workflow-Definition-Structure), -for example: - -```json -"constants": { - "Translations": { - "Dog": { - "Serbian": "pas", - "Spanish": "perro", - "French": "chien" - } - } -} -``` - -Constants can only be accessed inside Workflow expressions via the `$CONST` variable. -Runtimes must make `$CONST` available to expressions as a predefined variable. - -Here is an example of using constants in Workflow expressions: - -```json -{ -..., -"constants": { - "AGE": { - "MIN_ADULT": 18 - } -}, -... -"states":[ - { - "name":"check-applicant", - "type":"switch", - "dataConditions": [ - { - "name": "applicant-is-adult", - "condition": "${ .applicant | .age >= $CONST.AGE.MIN_ADULT }", - "transition": "approve-application" - }, - { - "name": "applicant-is-minor", - "condition": "${ .applicant | .age < $CONST.AGE.MIN_ADULT }", - "transition": "reject-application" - } - ], - ... - }, - ... -] -} -``` - -Note that constants can also be used in [expression functions](#Using-Functions-for-Expression-Evaluation), -for example: - -```json -{ -"functions": [ - { - "name": "is-adult", - "operation": ".applicant | .age >= $CONST.AGE.MIN_ADULT", - "type": "expression" - }, - { - "name": "is-minor", - "operation": ".applicant | .age < $CONST.AGE.MIN_ADULT", - "type": "expression" - } -] -} -``` - -Workflow constants values should only contain static data, meaning that their value should not -contain Workflow expressions. -Workflow constants data must be immutable. -Workflow constants should not have access to [Workflow secrets definitions](#Workflow-Secrets). - -### Workflow Secrets - -Secrets allow you access sensitive information, such as passwords, OAuth tokens, ssh keys, etc -inside your [Workflow Expressions](#Workflow-Expressions). - -You can define the names of secrets via the [Workflow top-level "secrets" property](#Workflow-Definition-Structure), -for example: - -```json -"secrets": ["MY_PASSWORD", "MY_STORAGE_KEY", "MY_ACCOUNT"] -``` - -If secrets are defined in a Workflow definition, runtimes must assure to provide their values -during Workflow execution. - -Secrets can be used only in [Workflow expressions](#Workflow-Expressions) by referencing them via the `$SECRETS` variable. -Runtimes must make `$SECRETS` available to expressions as a predefined variable. - -Here is an example on how to use secrets and pass them as arguments to a function invocation: - -```json -"secrets": ["AZURE_STORAGE_ACCOUNT", "AZURE_STORAGE_KEY"], - -... - -{ - "refName": "upload-to-azure", - "arguments": { - "account": "${ $SECRETS.AZURE_STORAGE_ACCOUNT }", - "account-key": "${ $SECRETS.AZURE_STORAGE_KEY }", - ... - } - -} -``` - -Note that secrets can also be used in [expression functions](#Using-Functions-for-Expression-Evaluation). - -Secrets are immutable, meaning that workflow expressions are not allowed to change their values. - -### Workflow Metadata - -Metadata enables you to enrich the serverless workflow model with information beyond its core definitions. -It is intended to be used by clients, such as tools and libraries, as well as users that find this information relevant. - -Metadata should not affect workflow execution. Implementations may choose to use metadata information or ignore it. -Note, however, that using metadata to control workflow execution can lead to vendor-locked implementations that do not comply with the main goals of this specification, which is to be completely vendor-neutral. - -Metadata includes key/value pairs (string types). Both keys and values are completely arbitrary and non-identifying. - -Metadata can be added to: - -- [Workflow Definition](#Workflow-Definition-Structure) -- [Function definitions](#Function-Definition) -- [Event definitions](#Event-Definition) -- [State definitions](#Workflow-States) -- [Switch state](#Switch-State) [data](#Switch-State-Data-Conditions) and [event](#Switch-State-Event-Conditions) conditions. - -Here is an example of metadata attached to the core workflow definition: - -```json -{ - "name": "process-sales-orders", - "description": "Process Sales Orders", - "version": "1.0.0", - "specVersion": "0.8", - "start": "MyStartingState", - "metadata": { - "loglevel": "Info", - "environment": "Production", - "category": "Sales", - "giturl": "github.com/myproject", - "author": "Author Name", - "team": "Team Name", - ... - }, - "states": [ - ... - ] -} -``` - -Some other examples of information that could be recorded in metadata are: - -- UI tooling information such as sizing or scaling factors. -- Build, release, or image information such as timestamps, release ids, git branches, PR numbers, etc. -- Logging, monitoring, analytics, or audit repository information. -- Labels used for organizing/indexing purposes, such as "release" "stable", "track", "daily", etc. - -### Workflow Context - -Similar to [Constants](https://github.com/serverlessworkflow/specification/blob/main/specification.md#workflow-constants) and [Secrets](https://github.com/serverlessworkflow/specification/blob/main/specification.md#workflow-secrets), workflows expressions can have access to the context information of a running instance via the keyword `WORKFLOW`. - -Implementations may use this keyword to give access to any relevant information of the running instance within an expression. For example: - -```json - -{ - "name": "process-sales-orders", - "description": "Process Sales Orders", - "version": "1.0.0", - "specVersion": "0.8", - "start": "my-starting-state", - "functions": [{ - "name": "my-function", - "operation": "myopenapi.json#myFunction" - }], - "states":[ - { - "name":"my-starting-state", - "type":"operation", - "actions": [{ - "functionRef": "my-function", - "args": { - "order": "${ .orderId }", - "callerId": "${ $WORKFLOW.instanceId }" - } - }], - "end": true - }] -} -``` - -In this use case, a third-party service may require information from the caller for traceability purposes. - -The specification doesn't define any specific variable within the `WORKFLOW` bucket, but it's considered a reserved keyword. - -### Naming Convention - -Identifiable components of a workflow definition, such as states, actions, branches, events and functions define a required non-null `name` property which is based on DNS label names as defined by [RFC 1123](https://datatracker.ietf.org/doc/html/rfc1123#page-13) with further restrictions. - -Specifically, `names` must be lowercase, start and end with an alphanumeric character, and consist entirely of alphanumeric characters with optional isolated medial dashes '-' (i.e., dashes must not be adjacent to each other). - -The regular expression used in [schemas](/schema/workflow.json) is: `^[a-z0-9](-?[a-z0-9])*$`. - -## Extensions - -The workflow extension mechanism allows you to enhance your model definitions with additional information useful for -things like analytics, rate limiting, logging, simulation, debugging, tracing, etc. - -Model extensions do no influence control flow logic (workflow execution semantics). -They enhance it with extra information that can be consumed by runtime systems or tooling and -evaluated with the end goal being overall workflow improvements in terms of time, cost, efficiency, etc. - -Serverless Workflow specification provides extensions which can be found [here](extensions/README.md). - -You can define extensions in your workflow definition using its top-level `extensions` property. -For more information about this property, see the `extensions` property in the -[Workflow Definition Structure section](#Workflow-Definition-Structure). - -Even though users can define their own extensions, it is encouraged to use the ones provided by the specification. -We also encourage users to contribute their extensions to the specification. That way they can be shared -with the rest of the community. - -If you have an idea for a new workflow extension, or would like to enhance an existing one, -please open an `New Extension Request` issue in this repository. - -## Use Cases - -You can find different Serverless Workflow use cases [here](usecases/README.md). - -## Examples - -You can find many Serverless Workflow examples [here](examples/README.md). - -## Comparison to other workflow languages - -You can find info how the Serverless Workflow language compares with -other workflow languages [here](comparisons/README.md). - -## References - -You can find a list of other languages, technologies and specifications related to workflows [here](references/README.md). - -## License - -Serverless Workflow specification operates under the -[Apache License version 2.0](LICENSE). diff --git a/usecases/README.md b/usecases/README.md deleted file mode 100644 index cfdb707a..00000000 --- a/usecases/README.md +++ /dev/null @@ -1,63 +0,0 @@ -# Use Cases - -Use cases for the Serverless Workflow Specification highly depend on the reference implementations -and the ecosystem available during workflow execution (available functions/services/events, etc). - -As mentioned in the [main specification document](../README.md) one of the main benefits of Serverless Workflows -is that they provide clear separation of business and orchestration logic in your serverless apps. - -Developers can focus on solving business logic inside functions and utilize workflows to define function invocations, - react to events, as well as provide data management for different microservices. - -So what can you automate with Serverless Workflows? You can get some ideas from the use cases below. - -## Table of Contents - -- [Online Vehicle Auction](#Online-Vehicle-Auction) -- [Payment Processing](#Payment-Processing) -- [Data Analysis](#Data-Analysis) -- [Error Notifications](#Error-Notifications) -- [Continuous Integration And Deployment](#Continuous-Integration-And-Deployment) - -## Online Vehicle Auction - -You can use Serverless Workflows to coordinate all of the steps of an Online Vehicle Auction. -These can include: - -- Authentication of users making bids. -- Communication with Bidding and Inventory services -- Make decisions to start/end the auction under certain conditions - - - -## Payment Processing - -Servlerless Workflows are ideal for coordinating session-based apps such as e-commerce sites. You can -use Serverless Workflows to coordinate all steps of the checkout process allowing for example users to take a picture -of their credit card rather than having to type in the numbers and information. - - - -## Data Analysis - -You can use Serverless Workflows to coordinate data analysis of Marketing and Sales information. -Analysis can be scheduled on a timely basis to trigger workflow coordination of different ETL services. - - - -## Error Notifications - -You can design Serverless Workflows that trigger notifications regarding their success or failure. -In conjunction with available messaging services you can notify developers on different platforms of such possible failures - including error information and exactly the point in the execution the failure happened. - At the same time you can log the workflow execution status to cloud storage services for further analysis. - - - -## Continuous Integration And Deployment - -Serverless Workflows can help you build solid continuous integration and deployment solutions. -Code check-ins can trigger website builds and automatic redeploys. Pull requests can trigger -running automated tests to make sure code is well-tested before human reviews. - -