Migra de AWS Step Functions a Workflows

Para ayudarte a prepararte para migrar de Step Functions de Amazon Web Services (AWS) a flujos de trabajo en Google Cloud, en esta página, se explican las similitudes y diferencias clave entre los dos productos. El objetivo de esta información es ayudar a quienes ya conocen Step Functions a implementar una arquitectura similar con Workflows.

Al igual que Step Functions, Workflows es una plataforma de organización completamente administrada y basada en el estado que ejecuta servicios en el orden que defines: un flujo de trabajo. Estos flujos de trabajo pueden combinar servicios que incluyen servicios personalizados alojados en Cloud Run o funciones de Cloud Run, Google Cloud servicios como la IA de Cloud Vision y BigQuery, y cualquier API basada en HTTP.

Ten en cuenta que los flujos de trabajo exprés de Step Functions son un tipo de flujo de trabajo de AWS Step Functions que no se considera aquí, ya que la duración de un flujo de trabajo exprés es limitada y no se admite una ejecución de flujo de trabajo exactamente una vez.

Hello World

En Step Functions, una máquina de estados es un flujo de trabajo, y una tarea es un estado en un flujo de trabajo que representa una sola unidad de trabajo que realiza otro servicio de AWS. Step Functions requiere que cada estado defina el siguiente.

En Workflows, una serie de pasos con la sintaxis de Workflows describe las tareas que se deben ejecutar. Los flujos de trabajo tratan los pasos como si estuvieran en una lista ordenada y los ejecutan uno a la vez hasta que se ejecutan todos los pasos.

En el siguiente ejemplo de "Hello World", se muestra el uso de estados en las funciones Step y pasos en los flujos de trabajo:

Step Functions

  {
    "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 Workflows

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

JSON de flujos de trabajo

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

Descripción general de la comparación

En esta sección, se comparan los dos productos con más detalle.

Step FunctionsWorkflows
SintaxisJSON (YAML en herramientas) YAML o JSON
Flujo de controlTransición entre estados Control de flujo imperativo con pasos
TrabajadorRecursos (ARN) y tarea HTTP Solicitudes y conectores HTTP
ConfiabilidadCaptura o reinténtalo Captura o reinténtalo
ParalelismoAdmitido Admitido
Datos de estadoSe pasa el estado Variables de flujos de trabajo
AuthenticationIAM IAM
Experiencia del usuarioWorkflow Studio, CLI, SDK y IaC Consola, CLI, SDK y IaC deGoogle Cloud
Precios Precios de Step Functions Precios de Workflows
Sintaxis

Step Functions usa principalmente JSON para definir funciones y no admite YAML directamente. Sin embargo, en AWS Toolkit para Visual Studio Code y en AWS CloudFormation, puedes usar YAML para una definición de Step Functions.

Puedes describir los pasos de Workflows con la sintaxis de Workflows, y se pueden escribir en YAML o JSON. La mayoría de los flujos de trabajo están en YAML. En los ejemplos de esta página, se muestran las ventajas de YAML, como la facilidad de lectura y escritura, y la compatibilidad nativa con los comentarios. Para obtener una explicación detallada de la sintaxis de Workflows, consulta la Referencia de sintaxis.

Flujo de control

Tanto Workflows como Step Functions modelan flujos de trabajo como una serie de tareas: pasos en Workflows y estados en Step Functions. Ambos permiten que una tarea indique qué tarea ejecutar a continuación y admiten condicionales similares a interruptores para elegir la siguiente unidad de trabajo según el estado actual. Una diferencia clave es que Step Functions requiere que cada estado defina el siguiente, mientras que Workflows ejecuta los pasos en el orden en que se especifican (incluidos los próximos pasos alternativos). Para obtener más información, consulta Condiciones y Pasos.

Trabajador

Ambos productos orquestan recursos de procesamiento, como funciones, contenedores y otros servicios web, para realizar tareas. En Step Functions, el trabajador se identifica con un campo Resource, que es un URI sintáctico. Los URIs que se usan para identificar los recursos de trabajo están en formato de nombre de recursos de Amazon (ARN). Para invocar directamente un extremo HTTP arbitrario, puedes definir una tarea HTTP.

Los flujos de trabajo pueden enviar solicitudes HTTP a un extremo HTTP arbitrario y obtener una respuesta. Los conectores facilitan la conexión a otras Google Cloud APIs dentro de un flujo de trabajo y la integración de tus flujos de trabajo con otros Google Cloud productos, como Pub/Sub, BigQuery o Cloud Build. Los conectores simplifican los servicios de llamadas porque manejan el formato de las solicitudes por ti y proporcionan métodos y argumentos para que no necesites conocer los detalles de una API deGoogle Cloud . También puedes configurar las políticas de tiempo de espera y sondeo.

Confiabilidad

Si una tarea falla, tu flujo de trabajo debe poder reintentar de forma adecuada, detectar excepciones cuando sea necesario y redireccionar el flujo de trabajo según sea necesario. Tanto las funciones de paso como los flujos de trabajo logran la confiabilidad con mecanismos similares: captura de excepciones con reintentos y envío eventual a otro lugar del flujo de trabajo. Para obtener más información, consulta Errores de flujo de trabajo.

Paralelismo

Es posible que desees que tu flujo de trabajo organice varias tareas en paralelo. Las funciones Step proporcionan dos maneras de lograr esto: puedes tomar un elemento de datos y pasarlo en paralelo a varias tareas diferentes, o bien puedes usar un array y pasar sus elementos a la misma tarea.

En Workflows, puedes definir una parte de tu flujo de trabajo en la que se puedan ejecutar dos o más pasos de forma simultánea. Puedes definir ramas que se ejecutan de forma simultánea o un bucle en el que las iteraciones se ejecutan de forma simultánea. Para obtener más información, consulta Cómo ejecutar pasos de flujo de trabajo en paralelo.

Datos de estado

Un beneficio de un motor de flujo de trabajo es que los datos de estado se mantienen por ti sin un almacén de datos externo. En Step Functions, los datos de estado se pasan en una estructura JSON de un estado a otro.

En Workflows, puedes guardar los datos de estado en variables globales. Dado que tienes una duración de ejecución de hasta un año, puedes conservar los datos de estado, siempre y cuando la instancia siga en ejecución.

Autenticación

Ambos productos dependen de un sistema subyacente de administración de identidades y accesos (IAM) para la autenticación y el control de acceso. Por ejemplo, puedes usar un rol de IAM para invocarlas.

En Workflows, puedes usar una cuenta de servicio para invocar un flujo de trabajo, puedes usar OAuth 2.0 o OIDC para conectarte con APIs de Google Cloudy puedes usar un encabezado de solicitud de autorización para autenticarte con una API de terceros. Para obtener más información, consulta Otorga permiso a un flujo de trabajo para acceder a Google Cloud recursos y Realiza solicitudes autenticadas desde un flujo de trabajo.

Experiencia del usuario

Puedes usar una herramienta de línea de comandos o una infraestructura como código (IaC), como Terraform, para definir y administrar Step Functions y Workflows.

Además, Workflows admite la ejecución de flujos de trabajo con las bibliotecas cliente, en la consola de Google Cloud, con Google Cloud CLI o mediante el envío de una solicitud a la API de REST de Workflows. Para obtener más información, consulta Ejecuta un flujo de trabajo.

Precios

Ambos productos tienen un nivel gratuito. Para obtener más detalles, consulta sus respectivas páginas de precios: Precios de Step Functions y Precios de Workflows.

Cómo asignar tipos de estado a pasos

Existen ocho tipos de estado en Step Functions. Los estados son elementos de una máquina de estados que pueden tomar decisiones en función de sus entradas, realizar acciones y pasar resultados a otros estados. Antes de migrar de Step Functions a flujos de trabajo, asegúrate de comprender cómo traducir cada tipo de estado a un paso de Workflows.

Choice

Un estado Choice agrega lógica de ramificación a una máquina de estados.

En Workflows, puedes usar un bloque switch como un mecanismo de selección que permite que el valor de una expresión controle el flujo de la ejecución de un flujo de trabajo. Si un valor coincide, se ejecuta la sentencia de esa condición. Para obtener más información, consulta Condiciones.

Step Functions

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

YAML de Workflows

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

JSON de flujos de trabajo

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

Fail

Un estado Fail detiene la ejecución de la máquina de estados y la marca como un error.

En Workflows, puedes generar errores personalizados con la sintaxis raise y detectar y controlar errores con un bloque try/except. Para obtener más información, consulta Cómo generar errores.

Step Functions

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

YAML de Workflows

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

JSON de flujos de trabajo

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

Map

Se puede usar un estado Map para ejecutar un conjunto de pasos para cada elemento de un array de entrada.

En Workflows, puedes usar un bucle for para las iteraciones.

Step Functions

  { "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 Workflows

  - 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 flujos de trabajo

  [
    {
      "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

Se puede usar un estado Parallel para crear ramas de ejecución paralelas en tu máquina de estados. En el siguiente ejemplo de Step Functions, se realiza una búsqueda de dirección y teléfono en paralelo.

En Workflows, puedes usar un paso parallel para definir una parte de tu flujo de trabajo en la que dos o más pasos se pueden ejecutar de forma simultánea. Para obtener más información, consulta Pasos en paralelo.

Step Functions

  { "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 Workflows

  main:
     params: [args]
     steps:
     - init:
         assign:
         - workflow_id: "lookupAddress"
         - customer_to_lookup:
             - address: ${args.customerId}
             - phone: ${args.customerId}
         - addressed: ["", ""] # to write to this variable, you must share it
     - parallel_address:
         parallel:
             shared: [addressed]
             for:
                 in: ${customer_to_lookup}
                 index: i # optional, use if index is required
                 value: arg
                 steps:
                 - address:
                     call: googleapis.workflowexecutions.v1.projects.locations.workflows.executions.run
                     args:
                         workflow_id: ${workflow_id}
                         argument: ${arg}
                     result: r
                 - set_result:
                     assign:
                     - addressed[i]: ${r}
     - return:
             return: ${addressed}

JSON de flujos de trabajo

  {
    "main": {
      "params": [
        "args"
      ],
      "steps": [
        {
          "init": {
            "assign": [
              {
                "workflow_id": "lookupAddress"
              },
              {
                "customer_to_lookup": [
                  {
                    "address": "${args.customerId}"
                  },
                  {
                    "phone": "${args.customerId}"
                  }
                ]
              },
              {
                "addressed": [
                  "",
                  ""
                ]
              }
            ]
          }
        },
        {
          "parallel_address": {
            "parallel": {
              "shared": [
                "addressed"
              ],
              "for": {
                "in": "${customer_to_lookup}",
                "index": "i",
                "value": "arg",
                "steps": [
                  {
                    "address": {
                      "call": "googleapis.workflowexecutions.v1.projects.locations.workflows.executions.run",
                      "args": {
                        "workflow_id": "${workflow_id}",
                        "argument": "${arg}"
                      },
                      "result": "r"
                    }
                  },
                  {
                    "set_result": {
                      "assign": [
                        {
                          "addressed[i]": "${r}"
                        }
                      ]
                    }
                  }
                ]
              }
            }
          }
        },
        {
          "return": {
            "return": "${addressed}"
          }
        }
      ]
    }
  }

Pass

Un estado Pass pasa su entrada a su salida, sin realizar trabajo. Esto se usa comúnmente para manipular los datos de estado en el JSON.

Como Workflows no pasa datos de esta manera, puedes dejar el estado como no operativo o usar un paso de asignación para modificar las variables. Para obtener más información, consulta Cómo asignar variables.

Step Functions

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

YAML de Workflows

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

JSON de flujos de trabajo

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

Succeed

Un estado Succeed detiene una ejecución correctamente.

En Workflows, puedes usar return en el flujo de trabajo principal para detener la ejecución de un flujo de trabajo. También puedes finalizar un flujo de trabajo completando el paso final (suponiendo que el paso no salte a otro) o puedes usar next: end para detener la ejecución de un flujo de trabajo si no necesitas mostrar un valor. Para obtener más información, consulta Cómo completar la ejecución de un flujo de trabajo.

Step Functions

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

YAML de Workflows

  return: "Success!"
  next: end

JSON de flujos de trabajo

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

Task

Un estado Task representa una sola unidad de trabajo que realiza una máquina de estados. En el siguiente ejemplo de Step Functions, se invoca una función Lambda. (Las actividades son una función de AWS Step Functions que te permite tener una tarea en tu máquina de estados en la que el trabajo se realiza en otro lugar).

En el ejemplo de Workflows, se hace una llamada a un extremo HTTP para invocar una función de Cloud Run. También puedes usar un conector que te permita acceder fácilmente a otros productos Google Cloud . Además, puedes pausar un flujo de trabajo y sondear datos. También puedes usar un endpoint de devolución de llamada para indicarle a tu flujo de trabajo que se produjo un evento especificado y esperar a ese evento sin sondeos.

Step Functions

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

YAML de Workflows

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

JSON de flujos de trabajo

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

Wait

Un estado Wait retrasa la continuación de la máquina de estados durante un tiempo especificado.

Puedes usar la función de biblioteca estándar sys.sleep de Workflows para suspender la ejecución durante la cantidad de segundos determinada hasta un máximo de 31536000 (un año).

Step Functions

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

YAML de Workflows

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

JSON de flujos de trabajo

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

Ejemplo: Organiza microservicios

En el siguiente ejemplo de Step Functions, se verifica el precio de una acción, se determina si se debe comprar o vender, y se informa el resultado. La máquina de estados del ejemplo se integra con AWS Lambda pasando parámetros, usa una cola de Amazon SQS para solicitar la aprobación humana y usa un tema de Amazon SNS para mostrar los resultados de la 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
          }
      }
  }

Cómo migrar a flujos de trabajo

Para migrar el ejemplo anterior de Step Functions a Workflows, puedes crear los pasos equivalentes de Workflows integrando funciones de Cloud Run, admitiendo un endpoint de devolución de llamada que espera a que lleguen solicitudes HTTP a ese extremo y usando un conector de Workflows para publicar en un tema de Pub/Sub en lugar del tema de Amazon SNS:

  1. Completa los pasos para crear un flujo de trabajo, pero no lo implementes todavía.

  2. En la definición del flujo de trabajo, agrega un paso para crear un extremo de devolución de llamada que espere una entrada humana y un paso que use un conector de Workflows para publicar en un tema de Pub/Sub. Por ejemplo:

    YAML de Workflows

      ---
      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 flujos de trabajo

      {
        "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"
              }
            }
          ]
        }
      }

    Reemplaza lo siguiente:

    • LOCATION: Es una regiónGoogle Cloud compatible. Por ejemplo, us-central1.
    • PUBSUB_TOPIC_NAME: Es el nombre del tema de Pub/Sub. Por ejemplo, my_stock_example
  3. Implementa y, luego, ejecuta el flujo de trabajo.

  4. Durante la ejecución del flujo de trabajo, se detiene y espera a que invoques el extremo de devolución de llamada. Para ello, puedes usar un comando curl. Por ejemplo:

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

    Reemplaza CALLBACK_URL por el resto de la ruta de acceso a tu extremo de devolución de llamada.

  5. Una vez que el flujo de trabajo se haya completado correctamente, puedes recibir el mensaje de la suscripción a Pub/Sub. Por ejemplo:

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

    El mensaje de salida debería ser similar al siguiente (buy o sell):

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

¿Qué sigue?