serverlessworkflow / specification

Contains the official specification for the Serverless Workflow Domain Specific Language. It provides detailed guidelines and standards for defining, executing, and managing workflows in serverless environments, ensuring consistency and interoperability across implementations.
http://serverlessworkflow.io
Apache License 2.0
741 stars 147 forks source link

Multiple then with who can perform what #976

Closed DhruvBarot closed 2 months ago

DhruvBarot commented 2 months ago

Hello Team, We are reading the specifications and it's very detailed documents that achieve most of the use cases, I would like thanks to the team, We are trying to build a workflow engine in our organization based on the serverless workflow specification.

There are a couple of use cases that are not possible with the current spec. kindly help us with how we can achieve those. Let's assume we want to build a workflow to manage GitHub issues,

  1. User-A from the QA team will open an issue,
  2. User-B from the Dev team can start work on this or give it back to the QA team for further details, (Only the Dev team user can advance workflow )
  3. Once User-B completes work on the issue then he can send it for review (Only Other than user-B from the Dev team can review it)
  4. Once it's reviewed, User-B can assign the issue to the QA team
  5. Now, User-A from the QA team can close the issue or give back Dev Team

With this use case, we are looking to have the support of multiple "then" with configuration of who can take which "then" action, Also, there is a fixed status of the workflow, How We can set the custom status of the workflow based on the current stage workflow?

cdavernas commented 2 months ago

Hello @DhruvBarot, and thanks for you interest in the Serverless Workflow DSL.

There are a couple of use cases that are not possible with the current spec

Unless I'm missing something from your use case, it is actually totally feasible with the current version of the DSL. As far I understand it, you could even achieve your goals in a plethora of different ways.

Here follows a sample workflow that does what you are asking, AFAIK:

document:
  dsl: '1.0.0'
  namespace: default
  name: manage-github-issues
  version: '0.1.0'
schedule:
  on:
    one:
      with:
        type: com.github.events.issues.opened.v1
        data: ${ .data.author.team == "QA" }
do:

  - initialize: #use to pull up data from the trigger event
      set:
        issue: ${ $workflow.input.events[0].data }
      export:
        as: .issue

  - awaitForDevWork:
      do:
        - assign: #not required, it's only used to provide additional contextual data and set custom status
            set:
              issue:
                assignedTo: DevTeam
                status: inProgress
        - notify: #can be replaced with an API call
            emit:
              event:
                with:
                  source: https://serverlessworkflow.io
                  type: com.github.events.issues.assignedToDevTeam.v1
                  data:
                    issue: ${ .issue }
        - await:
            listen:
              to:
                one:
                  with:
                    type: com.github.events.issues.devWorkCompleted.v1
            export:
              as: '$context + { issue: ($context.issue + { action: .data.nextAction, dev: .data.dev }) }'
      then: evaluateDevWorkOutcome

  - evaluateDevWorkOutcome:
      switch:
        - review:
            when: $context.issue.action == "review"
            then: reviewIssue
        - requestDetails:
            when: $context.issue.action == "requestDetails"
            then: awaitDetailsFromQA
        - default:
            then: raiseUnsupportedActionError

  - awaitDetailsFromQA:
      do:
        - assign: #not required, it's only used to provide additional contextual data and set custom status
            set:
              issue:
                assignedTo: QA
                status: awaitingDetails
                assignTo: ${ $context.issue.author }
        - notify: #can be replaced with an API call
            emit:
              event:
                with:
                  source: https://serverlessworkflow.io
                  type: com.github.events.issues.assignedToQATeam.v1
                  data:
                    issue: ${ $context.issue }
        - await:
            listen:
              to:
                one:
                  with:
                    type: com.github.events.issues.detailsProvided.v1
            export:
              as: '$context + { issue: ($context.issue + { action: .data.nextAction }) }'
      then: awaitForDevWork

  - reviewIssue:
      do:
        - assign: #not required, it's only used to provide additional contextual data and set custom status
            set:
              issue:
                assignedTo: DevTeam
                status: reviewing
        - notify: #can be replaced with an API call
            emit:
              event:
                with:
                  source: https://serverlessworkflow.io
                  type: com.github.events.issues.pendingReview.v1
                  data:
                    issue: ${ $context.issue }
                    review:
                      exclude: ${ $context.issue.dev }
        - await:
            listen:
              to:
                one:
                  with:
                    type: com.github.events.issues.reviewed.v1
            export:
              as: '$context + { issue: ($context.issue + { reviewer: .data.reviewer }) }'

  - validateReview:
       switch:
         - reviewerIsNotAssignedDev:
              when: $context.issue.reviewer != $context.issue.dev
              then: evaluateReview
         - reviewerIsAssignedDev:
              then: raiseAssignedDevCannotBeReviewer

  - evaluateReview:
      do:
        - assign: #not required, it's only used to provide additional contextual data and set custom status
            set:
              issue:
                assignedTo: QA
                status: evaluating
        - notify: #can be replaced with an API call
            emit:
              event:
                with:
                  source: https://serverlessworkflow.io
                  type: com.github.events.issues.evaluateReview.v1
                  data:
                    issue: ${ $context.issue }
                    assignTo: ${ $context.issue.author }
        - await:
            listen:
              to:
                one:
                  with:
                    type: com.github.events.issues.evaluated.v1
            export:
              as: '$context + { issue: ($context.issue + { action: .data.nextAction }) }'
        - evaluate:
            switch:
              - closeIssue:
                  when: $context.issue.action == "close"
                  then: closeIssue
              - default:
                  then: exit
        - closeIssue:
            do:
              - initialize: #the only purpose of this step is to pull up data at top level so it can be used in URI interpolation without having to rely on expressions
                  set:
                    organization: ${ $context.issue.repository.organization }
                    repository: ${ $context.issue.repository.name }
                    issueNumber: ${ $context.issue.number }
              - closeIssueOnGithub:
                  call: http
                  with:
                    endpoint: https://api.github.com/repos/{organization}/{repository}/issues/{issueNumber} #here we are using URI interpolation thanks to the top level data pulled up by initialize
                    method: patch
                    body:
                      state: closed
              - setIssueInfo: #not required, it's only used to provide additional contextual data and set custom status
                  set:
                    issue:
                      status: closed
              - notify: #can be replaced with an API call
                  emit:
                    event:
                      with:
                        source: https://serverlessworkflow.io
                        type: com.github.events.issues.closed.v1
                        data:
                          issue: ${ $context.issue }
            then: end
      then: awaitForDevWork

  - raiseUnsupportedActionError:
      raise:
        error:
          type: https://serverlessworkflow.io/spec/1.0.0/errors/runtime
          status: 400
          title: Unsupported Action
          detail: The specified action is not supported in this context
      then: end

  - raiseAssignedDevCannotBeReviewer:
      raise:
        error:
          type: https://serverlessworkflow.io/spec/1.0.0/errors/runtime
          status: 400
          title: Invalid Reviewer
          detail: The developper that has performed the work associated with the issue cannot be the reviewer of its own work
      then: end

Here's a small summary of what the above workflow does:

  1. Issue Initialization:

    • Trigger: The workflow is triggered when an issue is opened by a member of the QA team.
    • Action: The issue details are captured and stored in the workflow context.
  2. Assigning the Issue to Dev Team:

    • Action: The issue is assigned to the Dev team and its status is updated to inProgress. The system sends a notification that the issue is now assigned to the Dev team.
    • Await: The workflow waits for the Dev team to complete their work. Once the work is done, the Dev team can either request further details from the QA team or send the issue for review.
  3. Evaluating Dev Work Outcome:

    • Switch: Depending on the action chosen by the Dev team, the workflow either:
      • Requests further details from the QA team.
      • Moves the issue to the review stage.
      • Raises an error if an unsupported action is chosen.
  4. Requesting Details from QA:

    • Action: If the Dev team requests more details, the issue is reassigned to the QA team with the status awaitingDetails. The workflow waits for the QA team to provide the requested information, after which the issue is reassigned to the Dev team for further work.
  5. Reviewing the Issue:

    • Action: If the issue is sent for review, it is assigned to a member of the Dev team other than the one who worked on the issue. The status is updated to reviewing.
    • Validation: The workflow ensures that the developer who worked on the issue does not review it. If the reviewer is valid, the workflow proceeds. Otherwise, an error is raised.
    • Await: The workflow waits for the review to be completed.
  6. Evaluating the Review:

    • Action: After the review, the issue is assigned back to the QA team with the status evaluating.
    • Switch: The QA team can either close the issue or request further action from the Dev team.
    • Closing the Issue: If the QA team decides to close the issue, the workflow sends a request to the GitHub API to close the issue and emits a final event marking the issue as closed.
  7. Handling Errors:

    • Unsupported Action Error: If an invalid action is attempted at any stage, the workflow raises an Unsupported Action error.
    • Invalid Reviewer Error: If the developer who worked on the issue is assigned as the reviewer, the workflow raises an Invalid Reviewer error.

Custom Statuses and Role-Based Actions


Here's the graph of the workflow, as rendered by Synapse:

workflow-graph


P.S.1: In the future, please try to stick to the supplied issue templates, as this greatly facilitates book keeping 😉

P.S.2: I see that you are a .NET developper. As such, you might be interested in Synapse, instead of factoring your own runtime. If that's not the case, however, and if you want/need to develop your own runtime, we warmly invite you to open an issue asking to advertize it as a ServerlessWorkflow runtime/implementation. In the same spirit, and as we are aiming for incubation, it would be greatly appreciated if your organisation would advertize SW adoption. If that's the case, please open a PR with your org's description and logo, so that we can add it to the website and the readme.

P.S.3: If not already done, please do star the project, it helps us gain visibility. Additionally, you can also follow us on LinkedIn and/or X 😉

DhruvBarot commented 2 months ago

@cdavernas : Thanks for your detailed explanation, I will try to based on your input, If there is any problem then I will update it on the same issue or create a new issue based on templates.

P.S.2 : We already have temporal as a workflow engine, so we are just creating a bridge between serverless workflow DSL and temporal.

ricardozanini commented 2 months ago

@DhruvBarot hi! Even if it's just a bridge, we would appreciate it if you could add it to the adoption section of our website once you understand the project helped your team. Thank you!

DhruvBarot commented 2 months ago

@cdavernas / @ricardozanini : Can you please help me with the Synapse version? I was not able to get a diagram from the workflow, which is created by me as I tried the workflow definition shared by you, For both getting some errors and the diagram is not rendered.

P.S I am using the latest docker for running Synapse.

cdavernas commented 2 months ago

@DhruvBarot Are you using Synapse 1.0.0-alpha1?

What is the error(s) you are facing?

DhruvBarot commented 2 months ago

Are you using Synapse 1.0.0-alpha1?

I am using the latest docker image from Synapase, Do I need to build the solution and run from the 1.0.0-alpha1 branch?

What is the error(s) you are facing?

There is no error on the UI.

cdavernas commented 2 months ago

I am using the latest docker image from Synapse, Do I need to build the solution and run from the 1.0.0-alpha1 branch?

You need to build version 1.0.0-alpha1, it hasn't been released yet. You can check the wiki (docker-compose dev install) on how to get started. Please be aware that links in the docs are pointing to the main branch, but you should use the the alpha one instead until it is being released (probably by the end of this week).

cdavernas commented 2 months ago

@DhruvBarot Can I close the issue as completed? I believe your initial question has been answered, right?

If you have problems with Synapse or the workflow I gave as example, please don't hesistate to open new issues in related repositories.

cdavernas commented 2 months ago

@DhruvBarot Closing as I believe the initial question has been answered.

As said above, though, don't hesitate to open new issues in adequate repositories, if need be 😉

DhruvBarot commented 2 months ago

@cdavernas : Thanks for the detail response, and yes initial question has been answered.