Migra de AWS Step Functions a Workflows

En esta página, se explican las similitudes y diferencias clave entre los dos productos a fin de ayudarte a preparar la migración de las funciones paso a paso de Amazon Web Services (AWS) a Workflows en Google Cloud. El objetivo de esta información es ayudar a quienes ya están familiarizados con Step Functions a implementar una arquitectura similar mediante Workflows.

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

Ten en cuenta que Express Workflows es 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 de Express es limitada y no se admite la ejecución de un flujo de trabajo de tipo “exactamente una vez”.

Hello World

En las funciones paso a paso, una máquina de estado 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. Las funciones paso requieren que cada estado defina el siguiente estado.

En Workflows, una serie de pasos que usan la sintaxis de Workflows describe las tareas que se ejecutarán. Workflows tratan los pasos como si estuvieran en una lista ordenada y los ejecutan uno a la vez hasta que se hayan ejecutado todos.

En el siguiente ejemplo de “Hello World” se demuestra el uso de los estados en las funciones paso a paso y los pasos en los Workflows:

Funciones de pasos

  {
    "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 con flujos de trabajo

  ---
  # 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 ambos productos en más detalle.

Funciones de pasosWorkflows
SintaxisJSON (YAML en herramientas) YAML o JSON
Flujo de controlTransición entre estados Control de flujo imperativo con pasos
TrabajadorRecursos (ARN) Solicitudes y conectores HTTP
ConfiabilidadCapturar/reintentar Capturar/reintentar
ParalelismoAdmitido Admitido
Datos de estadoEl estado se transmite Variables de Workflows
AuthenticationIAM IAM
Experiencia del usuarioWorkflow Studio, CLI, IaC y SDK Consola de Google Cloud, CLI, IaC y SDK
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 funciones de paso.

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 demuestran las ventajas de YAML, incluida la facilidad de lectura y escritura, y la compatibilidad nativa de 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 los flujos de trabajo como una serie de tareas: steps en Workflows y states en Step Functions. Ambos permiten que una tarea indique qué tarea ejecutar a continuación y admiten condicionales tipo interruptor 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 el que se especifican (incluidos los pasos siguientes alternativos). Para obtener más información, consulta Condiciones y Pasos.

Trabajador

Ambos productos organizan recursos de procesamiento, como funciones, contenedores y otros servicios web para realizar tareas. En las funciones paso a paso, el trabajador se identifica mediante un campo Resource que es, de forma sintáctica un URI. Los URI que se usan para identificar los recursos de trabajador están en formato Amazon Resource Name (ARN), y los usuarios no pueden invocar directamente un extremo HTTP arbitrario.

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

Confiabilidad

Si una tarea falla, el flujo de trabajo debe poder reintentarse de manera adecuada, detectar excepciones cuando sea necesario y redirigir el flujo de trabajo según sea necesario. Tanto las Step Functions como los Workflows logran la confiabilidad mediante mecanismos similares: la captura de excepciones con reintentos y el 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. Step Functions proporciona dos formas de lograr esto: puedes tomar un elemento de datos y pasarlo en paralelo a varias tareas diferentes, o puedes usar un array y pasar sus elementos a la misma tarea.

En Workflows, puedes definir una parte del 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 donde las iteraciones se ejecutan de forma simultánea. Para obtener más información, consulta Ejecuta pasos del flujo de trabajo en paralelo.

Datos de estado

Uno de los beneficios de un motor de flujo de trabajo es que los datos de estado se mantienen sin un almacén de datos externo. En las funciones paso a paso, los datos de estado se pasan en una estructura JSON de un estado a otro.

En Workflows, puedes guardar los datos del estado en variables globales. Dado que se permite una duración de ejecución de hasta un año, puedes conservar los datos de estado mientras la instancia siga en ejecución.

Authentication

Ambos productos dependen de un sistema subyacente de Identity and Access Management (IAM) para la autenticación y el control de acceso. Por ejemplo, puedes usar un rol de IAM para invocar funciones del paso.

En Workflows, puedes usar una cuenta de servicio a fin de invocar un flujo de trabajo, OIDC o OAuth 2.0 para conectarte con las API de Google Cloud y un encabezado de solicitud de autorización a fin de autenticar con una API de terceros. Si quieres obtener más información, consulta Otorga permiso a un flujo de trabajo para acceder a los recursos de Google Cloud 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 las funciones de los pasos y Workflows.

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

Precios

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

Asigna tipos de estado a pasos

Existen ocho tipos de estados en las funciones paso a paso. Los estados son elementos de una máquina de estado que pueden tomar decisiones basadas en sus entradas, realizar acciones y pasar resultados a otros estados. Antes de migrar de Step Functions a Workflows, 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 ejecución de un flujo de trabajo. Si un valor coincide, se ejecuta la instrucción de esa condición. Para obtener más información, consulta Condiciones.

Funciones de pasos

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

YAML con flujos de trabajo

  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

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

En Workflows, puedes generar errores personalizados mediante la sintaxis raise, y puedes detectar y resolver errores mediante un bloque try/except. Para obtener más información, consulta Genera errores.

Funciones de pasos

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

YAML con flujos de trabajo

  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 a fin de ejecutar un conjunto de pasos para cada elemento de un array de entrada.

En Workflows, puedes usar bucles for para las iteraciones.

Funciones de pasos

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

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 paralelas de ejecución en tu máquina de estado. En el siguiente ejemplo de funciones paso a paso, se realiza una búsqueda de dirección y teléfono en paralelo.

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

Funciones de pasos

  { "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 con flujos de trabajo

  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 ninguna tarea. Por lo general, se usa para manipular los datos de estado en el archivo JSON.

Como los flujos de trabajo no pasan datos de esta manera, puedes dejar el estado como no-op o puedes usar un paso de asignación para modificar las variables. Para obtener más información, consulta Cómo asignar variables.

Funciones de pasos

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

YAML con flujos de trabajo

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

Sin errores

El estado Succeed detiene una ejecución de forma correcta.

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 si completas el paso final (si se supone que el paso no salta 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.

Funciones de pasos

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

YAML con flujos de trabajo

  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 estado. En el siguiente ejemplo de Step Functions, invoca una función de Lambda. (Las actividades son una función de AWS Step Functions que te permite tener una tarea en tu máquina de estado en la que el trabajo se realiza en otro lugar).

En el ejemplo de Workflows, se realiza una llamada a un extremo HTTP para invocar una Cloud Function. También puedes usar un conector que permita un fácil acceso a otros productos de Google Cloud. Además, puedes pausar un flujo de trabajo y sondear en busca de datos. También puedes usar un extremo de devolución de llamada para indicarle a tu flujo de trabajo que se produjo un evento específico y esperar en ese evento sin sondeo.

Funciones de pasos

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

YAML con flujos de trabajo

  - 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

El estado Wait retrasa la máquina de estado para que no continúe durante un tiempo especificado.

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

Funciones de pasos

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

YAML con flujos de trabajo

  - 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 las acciones, se determina si se deben comprar o vender y se informa el resultado. La máquina de estado de la muestra se integra en AWS Lambda pasando parámetros, usa una cola de Amazon SQS para solicitar la aprobación humana y usa un tema de Amazon SNS a fin de 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
          }
      }
  }

Migrate to Workflows

Para migrar el ejemplo anterior de las funciones del paso a Workflows, puedes crear los pasos equivalentes de Workflows si integras Cloud Functions, admites un extremo de devolución de llamada que espera a que las solicitudes HTTP lleguen a ese extremo y usas 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 aún no lo implementes.

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

    YAML con 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
      ...

    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ón de Google Cloud compatible. Por ejemplo, us-central1.
    • PUBSUB_TOPIC_NAME: Es el nombre de tu 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 al extremo de devolución de llamada.

  5. Una vez que se haya completado el flujo de trabajo de forma correcta, podrás 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 resultado debe ser similar al siguiente (ya sea buy o sell):

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

¿Qué sigue?