Migrar do AWS Functions para o Workflows

Nesta página, você verá as principais diferenças e diferenças entre os dois produtos para migrar do Amazon Web Services (AWS) para funções de fluxo de trabalho no Google Cloud. Essas informações têm o objetivo de ajudar aqueles que já estão familiarizados com o Functions a implementar uma arquitetura semelhante usando fluxos de trabalho.

Assim como o Functions, o Workflows é uma plataforma de orquestração com base em estado totalmente gerenciada que executa serviços na ordem que você definir: um fluxo de trabalho. Esses fluxos de trabalho podem combinar serviços como serviços personalizados hospedados no Cloud Run ou Cloud Functions, serviços do Google Cloud como o Cloud Vision AI e o BigQuery e qualquer API baseada em HTTP.

Observe que o Fluxo de trabalho do Express Express é um tipo de fluxo de trabalho que não é considerado aqui porque a duração de um fluxo de trabalho Express é limitada. Não é possível executar um fluxo de trabalho exatamente uma vez.

Hello World

No Step Functions, uma máquina de estado é um fluxo de trabalho, e uma tarefa é um estado em um fluxo de trabalho que representa uma única unidade de trabalho que outro serviço da AWS executa. As funções de etapa exigem que cada estado defina o próximo.

No Workflows, uma série de etapas que usa a sintaxe de fluxos de trabalho descreve as tarefas a serem executadas. Os fluxos de trabalho tratam as etapas como se estivessem em uma lista ordenada e as executam uma de cada vez até que todas sejam executadas.

O exemplo "Hello world" a seguir demonstra o uso de estados nas funções de etapa e de etapas em fluxos de trabalho:

Funções Step

  {
    "Comment": "Hello world example of Pass states in Amazon States Language",
    "StartAt": "Hello",
    "States": {
      "Hello": {
        "Type": "Pass",
        "Result": "Hello",
        "Next": "World"
      },
      "World": {
        "Type": "Pass",
        "Result": "World",
        "End": true
      }
    }
  }

YAML de fluxos de trabalho

  ---
  # Hello world example of steps using Google Cloud Workflows syntax
  main:
      steps:
      - Hello:
          next: World
      - World:
          next: end
  ...

JSON de fluxos de trabalho

  {
    "main": {
      "steps": [
        {
          "Hello": {
            "next": "World"
          }
        },
        {
          "World": {
            "next": "end"
          }
        }
      ]
    }
  }

Visão geral da comparação

Esta seção compara os dois produtos com mais detalhes.

Funções StepFluxos de trabalho
SintaxeJSON (YAML nas ferramentas) YAML ou JSON
Fluxo de controleTransição entre estados Controle de fluxo imperativo com etapas
Worker (em inglês)Recursos (ARN) Solicitações e conectores HTTP
ConfiabilidadeCapturar/tentar novamente Capturar/tentar novamente
ParalelismoCompatível Disponível até experimental.executions.map
Dados de estadoO estado é transmitido Variáveis de fluxos de trabalho
AuthenticationIAM IAM
Experiência do usuárioWorkflow Studio, CLI, SDK, IaC Console do Google Cloud, CLI, SDK, IaC
Preços Preços do Functions Preços do Workflows
Sintaxe

O Step Functions usa principalmente o JSON para definir funções e não é diretamente compatível com o YAML. No entanto, no AWS Toolkit para o Visual Studio Code e no AWS CloudFormation, você pode usar o YAML para uma definição do Functions.

É possível descrever etapas do Workflows usando a sintaxe de fluxos de trabalho e elas podem ser escritas em YAML ou JSON. A maioria dos fluxos de trabalho está em YAML. Os exemplos nesta página demonstram as vantagens do YAML, incluindo a facilidade de leitura e gravação e o suporte nativo de comentários. Para uma explicação detalhada da sintaxe dos fluxos de trabalho, consulte a referência de sintaxe.

Fluxo de controle

Os fluxos de trabalho e as funções de modelo modelam fluxos de trabalho como uma série de tarefas: etapas em fluxos de trabalho e estados em funções de etapa. Ambos permitem que uma tarefa indique qual tarefa será executada em seguida e oferecem suporte a condicionais semelhantes a interruptores para escolher a próxima unidade de trabalho com base no estado atual. Uma diferença importante é que as funções de etapa exigem que cada estado defina a próxima, enquanto o fluxo de trabalho executa etapas na ordem especificada (incluindo próximas etapas alternativas). Para mais informações, consulte Condições e Etapas.

Worker

Ambos os produtos orquestram recursos de computação como funções, contêineres e outros serviços da Web para realizar tarefas. Nas funções de etapa, o worker é identificado por um campo Resource, que é um URI em sintaxe. Os URIs usados para identificar recursos do worker estão no formato Amazon Resource Name (ARN), e os usuários não podem invocar diretamente um endpoint HTTP arbitrário.

Os fluxos de trabalho podem enviar solicitações HTTP para um endpoint HTTP arbitrário e receber uma resposta. Os conectores facilitam a conexão com outras APIs do Google Cloud em um fluxo de trabalho e a integração dos fluxos de trabalho com outros produtos do Google Cloud, como o Pub/Sub, o BigQuery ou o Cloud Build. Os conectores simplificam os serviços de chamada porque processam a formatação das solicitações, fornecendo métodos e argumentos para que você não precise conhecer os detalhes de uma API do Google Cloud. Também é possível configurar as políticas de tempo limite e de pesquisa.

Confiabilidade

Se uma tarefa falhar, o fluxo de trabalho precisará tentar novamente, detectar exceções quando necessário e redirecionar o fluxo conforme necessário. As funções de etapa e os fluxos de trabalho alcançam a confiabilidade usando mecanismos semelhantes: captura de exceções com novas tentativas e, eventualmente, envio para outros lugares no fluxo de trabalho. Para mais informações, consulte Erros de fluxo de trabalho.

Paralelismo

Você pode querer que seu fluxo de trabalho orquestre várias tarefas em paralelo. O Functions para etapas oferece duas maneiras de fazer isso: é possível transferir um item de dados em paralelo a várias tarefas diferentes ou usar uma matriz e transmitir os elementos dela para a mesma tarefa.

É possível usar um recurso experimental do Workflows, experimental.executions.map, para compatibilidade com trabalhos paralelos.

Dados de estado

Uma das vantagens de um mecanismo de fluxo de trabalho é que os dados de estado são mantidos sem um repositório de dados externo. Nas funções de etapa, os dados de estado são transmitidos em uma estrutura JSON de um estado para outro.

No Workflows, você pode salvar os dados de estado em variáveis globais. Como você pode ter uma duração de execução de até um ano, é possível manter os dados de estado enquanto a instância ainda estiver em execução.

Autenticação

Os dois produtos dependem de um sistema subjacente de gerenciamento de identidade e acesso (IAM) para autenticação e controle de acesso. Por exemplo, é possível usar um papel do IAM para invocar funções de etapa.

No Workflows, é possível usar uma conta de serviço para invocar um fluxo de trabalho, o OAuth 2.0 ou o OIDC para se conectar a APIs do Google Cloud e um cabeçalho de solicitação de autorização para autenticar com uma API de terceiros. Para mais informações, consulte Conceder uma permissão de fluxo de trabalho para acessar recursos do Google Cloud e Fazer solicitações autenticadas de um fluxo de trabalho.

Experiência do usuário

É possível usar uma ferramenta de linha de comando ou infraestrutura como código (IaC, na sigla em inglês), como o Terraform, para definir e gerenciar funções de etapa e fluxos de trabalho.

Além disso, o Workflows oferece suporte à execução de fluxos de trabalho usando as bibliotecas de cliente, no Console do Cloud, usando a CLI do Google Cloud ou enviando uma solicitação à API REST Workflows. Para mais detalhes, consulte Executar um fluxo de trabalho.

Preços

Os dois produtos têm um nível gratuito. Para mais detalhes, consulte as respectivas páginas de preços: Preços do Functions e Preços dos fluxos de trabalho.

Como mapear tipos de estado para etapas

Há oito tipos de estado nas funções de etapa. Os estados são elementos em uma máquina de estado que podem tomar decisões com base na entrada, realizar ações e transmitir a saída para outros estados. Antes de migrar das Funções de etapa para os fluxos de trabalho, entenda como converter cada tipo de estado para uma etapa dos Fluxos de trabalho.

Choice

Um estado Choice adiciona lógica de ramificação a uma máquina de estado.

No Workflows, é possível usar um bloco switch como um mecanismo de seleção que permite que o valor de uma expressão controle o fluxo de uma execução de fluxo de trabalho. Se um valor corresponder, a instrução dessa condição será executada. Para saber mais, consulte Condições.

Funções Step

  "ChoiceState": {
    "Type": "Choice",
    "Choices": [
      {
        "Variable": "$.foo",
        "NumericEquals": 1,
        "Next": "FirstMatchState"
      },
      {
        "Variable": "$.foo",
        "NumericEquals": 2,
        "Next": "SecondMatchState"
      }
    ],
    "Default": "DefaultState"
  }

YAML de fluxos de trabalho

  switch:
    - condition: ${result.body.SomeField < 10}
      next: small
    - condition: ${result.body.SomeField < 100}
      next: medium
    - condition: true
      next: default_step

JSON de fluxos de trabalho

  {
    "switch": [
      {
        "condition": "${result.body.SomeField < 10}",
        "next": "small"
      },
      {
        "condition": "${result.body.SomeField < 100}",
        "next": "medium"
      },
      {
        "condition": true,
        "next": "default_step"
      }
    ]
  }

Fail

Um estado Fail interrompe a execução da máquina de estado e a marca como uma falha.

No Workflows, você pode gerar erros personalizados usando a sintaxe raise e pode detectar e lidar com erros usando um bloco try/except. Para mais informações, consulte Relatar erros.

Funções Step

  "FailState": {
      "Type": "Fail",
      "Error": "ErrorA",
      "Cause": "Kaiju attack"
  }

YAML de fluxos de trabalho

  raise:
      code: 55
      message: "Something went wrong."

JSON de fluxos de trabalho

  {
    "raise": {
      "code": 55,
      "message": "Something went wrong."
    }
  }

Map

Um estado Map pode ser usado para executar um conjunto de etapas para cada elemento de uma matriz de entrada.

No Workflows, você pode usar um loop for para iterações.

Funções Step

  { "StartAt": "ExampleMapState",
    "States": {
      "ExampleMapState": {
        "Type": "Map",
        "Iterator": {
           "StartAt": "CallLambda",
           "States": {
             "CallLambda": {
               "Type": "Task",
               "Resource": "arn:aws:lambda:us-east-1:123456789012:function:HelloFunction",
               "End": true
             }
           }
        }, "End": true
      }
    }
  }

YAML de fluxos de trabalho

  - assignStep:
      assign:
        - map:
            1: 10
            2: 20
            3: 30
        - sum: 0
  - loopStep:
      for:
          value: key
          in: ${keys(map)}
          steps:
            - sumStep:
                assign:
                  - sum: ${sum + map[key]}
  - returnStep:
      return: ${sum}

JSON de fluxos de trabalho

  [
    {
      "assignStep": {
        "assign": [
          {
            "map": {
              "1": 10,
              "2": 20,
              "3": 30
            }
          },
          {
            "sum": 0
          }
        ]
      }
    },
    {
      "loopStep": {
        "for": {
          "value": "key",
          "in": "${keys(map)}",
          "steps": [
            {
              "sumStep": {
                "assign": [
                  {
                    "sum": "${sum + map[key]}"
                  }
                ]
              }
            }
          ]
        }
      }
    },
    {
      "returnStep": {
        "return": "${sum}"
      }
    }
  ]

Parallel

Um estado Parallel pode ser usado para criar ramificações paralelas de execução na máquina de estado. No exemplo de funções de etapa a seguir, um endereço e uma pesquisa de smartphone são realizados em paralelo.

No Workflows, você pode usar um recurso experimental, experimental.executions.map, para compatibilidade com trabalho paralelo.

Funções Step

  { "StartAt": "LookupCustomerInfo",
    "States": {
      "LookupCustomerInfo": {
        "Type": "Parallel",
        "End": true,
        "Branches": [
          {
           "StartAt": "LookupAddress",
           "States": {
             "LookupAddress": {
               "Type": "Task",
               "Resource": "arn:aws:lambda:us-east-1:123456789012:function:AddressFinder",
               "End": true
             }
           }
         },
         {
           "StartAt": "LookupPhone",
           "States": {
             "LookupPhone": {
               "Type": "Task",
               "Resource": "arn:aws:lambda:us-east-1:123456789012:function:PhoneFinder",
               "End": true
             }
           }
         }
        ]
      }
    }
  }

YAML de fluxos de trabalho

  - parallel-executor:
      call: experimental.executions.map
      args:
        workflow_id: LookupCustomerInfo
        arguments: [{"LookupAddress":"${cust_id}"},{"LookupPhone":"${cust_id}"}]
      result: result

JSON de fluxos de trabalho

  [
    {
      "parallel-executor": {
        "call": "experimental.executions.map",
        "args": {
          "workflow_id": "LookupCustomerInfo",
          "arguments": [
            {
              "LookupAddress": "${cust_id}"
            },
            {
              "LookupPhone": "${cust_id}"
            }
          ]
        },
        "result": "result"
      }
    }
  ]

Pass

Um estado Pass transmite a entrada para a saída, sem executar o trabalho. Isso é comumente usado para manipular os dados de estado no JSON.

Como os fluxos de trabalho não transmitem dados dessa maneira, você pode deixar o estado como um ambiente autônomo ou usar uma etapa de atribuição para modificar as variáveis. Para mais informações, consulte Atribuir variáveis.

Funções Step

  "No-op": {
    "Type": "Pass",
    "Result": {
      "x-datum": 0.38,
      "y-datum": 622.22
    },
    "ResultPath": "$.coords",
    "Next": "End"
  }

YAML de fluxos de trabalho

  assign:
      - number: 5
      - number_plus_one: ${number+1}
      - other_number: 10
      - string: "hello"

JSON de fluxos de trabalho

  {
    "assign": [
      {
        "number": 5
      },
      {
        "number_plus_one": "${number+1}"
      },
      {
        "other_number": 10
      },
      {
        "string": "hello"
      }
    ]
  }

Conquiste o sucesso

Um estado Succeed interrompe uma execução.

No Workflows, você pode usar return no fluxo de trabalho principal para interromper uma execução de fluxo de trabalho. Também é possível concluir um fluxo de trabalho concluindo a etapa final (supondo que ela não vá para outra) ou você pode usar next: end para interromper a execução de um fluxo de trabalho se não precisar retornar um valor. Para mais informações, consulte Concluir a execução de um fluxo de trabalho.

Funções Step

  "SuccessState": {
    "Type": "Succeed"
  }

YAML de fluxos de trabalho

  return: "Success!"
  next: end

JSON de fluxos de trabalho

  {
    "return": "Success!",
    "next": "end"
  }

Task

Um estado Task representa uma única unidade de trabalho realizada por uma máquina de estado. No exemplo de funções de etapa a seguir, ele invoca uma função Lambda. As atividades são um recurso das funções de etapas da AWS que permite realizar uma tarefa na máquina de estado em que o trabalho é executado em outro lugar.

No exemplo do Workflows, uma chamada é feita para um endpoint HTTP para invocar uma função do Cloud. Também é possível usar um conector que permite acesso fácil a outros produtos do Google Cloud. Além disso, é possível pausar um fluxo de trabalho e pesquisar dados. Ou então, use um endpoint de callback para sinalizar ao seu fluxo de trabalho que um evento especificado ocorreu e aguarde nesse evento sem pesquisar.

Funções Step

  "HelloWorld": {
    "Type": "Task",
    "Resource": "arn:aws:lambda:us-east-1:123456789012:function:HelloFunction",
    "End": true
  }

YAML de fluxos de trabalho

  - HelloWorld:
      call: http.get
      args:
          url: https://REGION-PROJECT_ID.cloudfunctions.net/helloworld
      result: helloworld_result

JSON de fluxos de trabalho

  [
    {
      "HelloWorld": {
        "call": "http.get",
        "args": {
          "url": "https://REGION-PROJECT_ID.cloudfunctions.net/helloworld"
        },
        "result": "helloworld_result"
      }
    }
  ]

Wait

Um estado Wait atrasa a máquina de estado por continuar por um tempo especificado.

Você pode usar a sys.sleep função de biblioteca padrão para fluxos de trabalho para suspender a execução por um determinado número de segundos, para no máximo 31536000 (um ano).

Funções Step

  "wait_ten_seconds" : {
    "Type" : "Wait",
    "Seconds" : 10,
    "Next": "NextState"
  }

YAML de fluxos de trabalho

  - someSleep:
      call: sys.sleep
      args:
          seconds: 10

JSON de fluxos de trabalho

  [
    {
      "someSleep": {
        "call": "sys.sleep",
        "args": {
          "seconds": 10
        }
      }
    }
  ]

Exemplo: orquestrar microsserviços

O exemplo de funções de etapa a seguir verifica o preço de uma ação, determina se comprar ou vender e informa o resultado. A máquina de estado na amostra integra-se ao AWS Lambda transmitindo parâmetros, usa uma fila do Amazon SQS para solicitar aprovação humana e usa um tópico do Amazon SNS para retornar os resultados da consulta.

{
      "StartAt": "Check Stock Price",
      "Comment": "An example of integrating Lambda functions in Step Functions state machine",
      "States": {
          "Check Stock Price": {
              "Type": "Task",
              "Resource": "CHECK_STOCK_PRICE_LAMBDA_ARN",
              "Next": "Generate Buy/Sell recommendation"
          },
          "Generate Buy/Sell recommendation": {
              "Type": "Task",
              "Resource": "GENERATE_BUY_SELL_RECOMMENDATION_LAMBDA_ARN",
              "ResultPath": "$.recommended_type",
              "Next": "Request Human Approval"
          },
          "Request Human Approval": {
              "Type": "Task",
              "Resource": "arn:PARTITION:states:::sqs:sendMessage.waitForTaskToken",
              "Parameters": {
                  "QueueUrl": "REQUEST_HUMAN_APPROVAL_SQS_URL",
                  "MessageBody": {
                      "Input.$": "$",
                      "TaskToken.$": "$$.Task.Token"
                  }
              },
              "ResultPath": null,
              "Next": "Buy or Sell?"
          },
          "Buy or Sell?": {
              "Type": "Choice",
              "Choices": [
                  {
                      "Variable": "$.recommended_type",
                      "StringEquals": "buy",
                      "Next": "Buy Stock"
                  },
                  {
                      "Variable": "$.recommended_type",
                      "StringEquals": "sell",
                      "Next": "Sell Stock"
                  }
              ]
          },
          "Buy Stock": {
              "Type": "Task",
              "Resource": "BUY_STOCK_LAMBDA_ARN",
              "Next": "Report Result"
          },
          "Sell Stock": {
              "Type": "Task",
              "Resource": "SELL_STOCK_LAMBDA_ARN",
              "Next": "Report Result"
          },
          "Report Result": {
              "Type": "Task",
              "Resource": "arn:PARTITION:states:::sns:publish",
              "Parameters": {
                  "TopicArn": "REPORT_RESULT_SNS_TOPIC_ARN",
                  "Message": {
                      "Input.$": "$"
                  }
              },
              "End": true
          }
      }
  }

Migrar para fluxos de trabalho

Para migrar o exemplo de funções do passo anterior para o Workflows, é possível criar as etapas equivalentes do Workflows integrando o Cloud Functions, oferecendo suporte a um endpoint de callback que aguarda as solicitações HTTP chegar a esse endpoint e usando um conector do Workflows para publicar em um tópico do Pub/Sub no lugar do tópico do Amazon SNS:

  1. Conclua as etapas para criar um fluxo de trabalho, mas não o implante ainda.

  2. Na definição do fluxo de trabalho, adicione uma etapa para criar um endpoint de callback que aguarde a entrada humana e uma etapa que usa um conector de fluxos de trabalho para publicar em um tópico do Pub/Sub. Exemplo:

    YAML de fluxos de trabalho

      ---
      main:
        steps:
          - init:
              assign:
                - projectId: '${sys.get_env("GOOGLE_CLOUD_PROJECT_ID")}'
                - region: LOCATION
                - topic: PUBSUB_TOPIC_NAME
          - Check Stock Price:
              call: http.get
              args:
                url: ${"https://" + region + "-" + projectId + ".cloudfunctions.net/CheckStockPrice"}
                auth:
                  type: OIDC
              result: stockPriceResponse
          - Generate Buy/Sell Recommendation:
              call: http.get
              args:
                url: ${"https://" + region + "-" + projectId + ".cloudfunctions.net/BuySellRecommend"}
                auth:
                  type: OIDC
                query:
                  price: ${stockPriceResponse.body.stock_price}
              result: recommendResponse
          - Create Approval Callback:
              call: events.create_callback_endpoint
              args:
                  http_callback_method: "GET"
              result: callback_details
          - Print Approval Callback Details:
              call: sys.log
              args:
                  severity: "INFO"
                  text: ${"Listening for callbacks on " + callback_details.url}
          - Await Approval Callback:
              call: events.await_callback
              args:
                  callback: ${callback_details}
                  timeout: 3600
              result: approvalResponse
          - Approval?:
              try:
                switch:
                  - condition: ${approvalResponse.http_request.query.response[0] == "yes"}
                    next: Buy or Sell?
              except:
                as: e
                steps:
                  - unknown_response:
                      raise: ${"Unknown response:" + e.message}
                      next: end
          - Buy or Sell?:
              switch:
                - condition: ${recommendResponse.body == "buy"}
                  next: Buy Stock
                - condition: ${recommendResponse.body == "sell"}
                  next: Sell Stock
                - condition: true
                  raise: ${"Unknown recommendation:" + recommendResponse.body}
          - Buy Stock:
              call: http.post
              args:
                url: ${"https://" + region + "-" + projectId + ".cloudfunctions.net/BuyStock"}
                auth:
                  type: OIDC
                body:
                  action: ${recommendResponse.body}
              result: message
          - Sell Stock:
              call: http.post
              args:
                url: ${"https://" + region + "-" + projectId + ".cloudfunctions.net/SellStock"}
                auth:
                  type: OIDC
                body:
                  action: ${recommendResponse.body}
              result: message
          - Report Result:
              call: googleapis.pubsub.v1.projects.topics.publish
              args:
                topic: ${"projects/" + projectId + "/topics/" + topic}
                body:
                  messages:
                  - data: '${base64.encode(json.encode(message))}'
              next: end
      ...

    JSON de fluxos de trabalho

      {
        "main": {
          "steps": [
            {
              "init": {
                "assign": [
                  {
                    "projectId": "${sys.get_env(\"GOOGLE_CLOUD_PROJECT_ID\")}"
                  },
                  {
                    "region": "LOCATION"
                  },
                  {
                    "topic": [
                      "PUBSUB_TOPIC_NAME"
                    ]
                  }
                ]
              }
            },
            {
              "Check Stock Price": {
                "call": "http.get",
                "args": {
                  "url": "${\"https://\" + region + \"-\" + projectId + \".cloudfunctions.net/CheckStockPrice\"}",
                  "auth": {
                    "type": "OIDC"
                  }
                },
                "result": "stockPriceResponse"
              }
            },
            {
              "Generate Buy/Sell Recommendation": {
                "call": "http.get",
                "args": {
                  "url": "${\"https://\" + region + \"-\" + projectId + \".cloudfunctions.net/BuySellRecommend\"}",
                  "auth": {
                    "type": "OIDC"
                  },
                  "query": {
                    "price": "${stockPriceResponse.body.stock_price}"
                  }
                },
                "result": "recommendResponse"
              }
            },
            {
              "Create Approval Callback": {
                "call": "events.create_callback_endpoint",
                "args": {
                  "http_callback_method": "GET"
                },
                "result": "callback_details"
              }
            },
            {
              "Print Approval Callback Details": {
                "call": "sys.log",
                "args": {
                  "severity": "INFO",
                  "text": "${\"Listening for callbacks on \" + callback_details.url}"
                }
              }
            },
            {
              "Await Approval Callback": {
                "call": "events.await_callback",
                "args": {
                  "callback": "${callback_details}",
                  "timeout": 3600
                },
                "result": "approvalResponse"
              }
            },
            {
              "Approval?": {
                "try": {
                  "switch": [
                    {
                      "condition": "${approvalResponse.http_request.query.response[0] == \"yes\"}",
                      "next": "Buy or Sell?"
                    }
                  ]
                },
                "except": {
                  "as": "e",
                  "steps": [
                    {
                      "unknown_response": {
                        "raise": "${\"Unknown response:\" + e.message}",
                        "next": "end"
                      }
                    }
                  ]
                }
              }
            },
            {
              "Buy or Sell?": {
                "switch": [
                  {
                    "condition": "${recommendResponse.body == \"buy\"}",
                    "next": "Buy Stock"
                  },
                  {
                    "condition": "${recommendResponse.body == \"sell\"}",
                    "next": "Sell Stock"
                  },
                  {
                    "condition": true,
                    "raise": "${\"Unknown recommendation:\" + recommendResponse.body}"
                  }
                ]
              }
            },
            {
              "Buy Stock": {
                "call": "http.post",
                "args": {
                  "url": "${\"https://\" + region + \"-\" + projectId + \".cloudfunctions.net/BuyStock\"}",
                  "auth": {
                    "type": "OIDC"
                  },
                  "body": {
                    "action": "${recommendResponse.body}"
                  }
                },
                "result": "message"
              }
            },
            {
              "Sell Stock": {
                "call": "http.post",
                "args": {
                  "url": "${\"https://\" + region + \"-\" + projectId + \".cloudfunctions.net/SellStock\"}",
                  "auth": {
                    "type": "OIDC"
                  },
                  "body": {
                    "action": "${recommendResponse.body}"
                  }
                },
                "result": "message"
              }
            },
            {
              "Report Result": {
                "call": "googleapis.pubsub.v1.projects.topics.publish",
                "args": {
                  "topic": "${\"projects/\" + projectId + \"/topics/\" + topic}",
                  "body": {
                    "messages": [
                      {
                        "data": "${base64.encode(json.encode(message))}"
                      }
                    ]
                  }
                },
                "next": "end"
              }
            }
          ]
        }
      }

    Substitua:

  3. Implante e execute o fluxo de trabalho.

  4. Durante a execução do fluxo de trabalho, ele pausa e aguarda você invocar o endpoint de callback. Para isso, use um comando curl. Exemplo:

    curl -H "Authorization: Bearer $(gcloud auth print-access-token)"
    https://workflowexecutions.googleapis.com/v1/projects/CALLBACK_URL?response=yes
    

    Substitua CALLBACK_URL pelo restante do caminho para o endpoint de callback.

  5. Depois que o fluxo de trabalho for concluído, você poderá receber a mensagem da assinatura do Pub/Sub. Exemplo:

    gcloud pubsub subscriptions pull stock_example-sub  --format="value(message.data)" | jq
    

    A mensagem de saída precisa ser semelhante a esta (uma buy ou uma sell):

    {
      "body": "buy",
      "code": 200,
      "headers": {
      [...]
      }
    

A seguir