Migrer des fonctions d'étape AWS vers Workflows

Pour vous aider à vous préparer à la migration d'Amazon Web Services (AWS) Step Functions vers Workflows sur Google Cloud, cette page décrit les principales similitudes et différences entre les deux produits. Ces informations sont destinées à aider ceux qui connaissent déjà les fonctions par étapes à mettre en œuvre une architecture similaire à l'aide de Workflows.

Tout comme Step Functions, Workflows est une plate-forme d'orchestration entièrement gérée basée sur l'état qui exécute les services dans l'ordre que vous définissez: un workflow. Ces workflows peuvent combiner des services tels que des services personnalisés hébergés sur Cloud Run ou Cloud Functions, des services Google Cloud tels que Cloud Vision AI et BigQuery, et toute API basée sur HTTP.

Notez que les Workflows Express de Step Functions sont un type de workflow AWS Step Functions qui n'est pas pris en compte ici, car la durée d'un workflow Express est limitée, et l'exécution d'un workflow de type "exactement une fois" n'est pas prise en charge.

Hello World

Dans Step Functions, une machine à états est un workflow et une tâche est un état dans un workflow qui représente une seule unité de travail exécutée par un autre service AWS. Les fonctions par étapes nécessitent que chaque état définisse l'état suivant.

Dans Workflows, une série d'étapes utilisant la syntaxe Workflows décrit les tâches à exécuter. Les workflows traitent les étapes comme si elles se trouvaient dans une liste numérotée et les exécutent une par une jusqu'à ce que toutes les étapes soient exécutées.

L'exemple "Hello world" suivant illustre l'utilisation des states (états) dans les fonctions étape et steps (étapes) dans Workflows:

Fonctions de pas

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

Fichier YAML des workflows

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

Workflows JSON

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

Présentation de la comparaison

Cette section compare les deux produits plus en détail.

Fonctions de pasWorkflows
SyntaxeJSON (YAML dans les outils) YAML ou JSON
Flux de contrôleTransition entre les états Contrôle de flux impératif avec des étapes
Nœud de calculRessources (ARN) Requêtes et connecteurs HTTP
Fiabilitéintercepter/réessayer intercepter/réessayer
ParallélismeCompatible Compatible
Données d'étatL'état est transmis Variables des workflows
AuthentificationIAM IAM
Expérience utilisateurWorkflow Studio, CLI, SDK, IaC Console Google Cloud, CLI, SDK, IaC
Tarifs Tarifs de Step Functions Tarifs de Workflows
Syntaxe

Step Functions utilise principalement JSON pour définir des fonctions et n'est pas directement compatible avec YAML. Toutefois, dans le kit AWS pour Visual Studio Code et dans AWS CloudFormation, vous pouvez utiliser YAML pour une définition des fonctions par étapes.

Vous pouvez décrire les étapes de Workflows à l'aide de la syntaxe Workflows. Elles peuvent être écrites en YAML ou JSON. La majorité des workflows sont en YAML. Les exemples de cette page illustrent les avantages de YAML, tels que la facilité de lecture et d'écriture, et la compatibilité native avec les commentaires. Pour obtenir une explication détaillée de la syntaxe de Workflows, consultez la documentation de référence sur la syntaxe.

Flux de contrôle

Les workflows et les fonctions de pas modélisent les workflows sous la forme d'une série de tâches: les étapes dans les workflows et les états dans les étapes des fonctions. Les deux permettent à une tâche d'indiquer quelle tâche exécuter ensuite et prennent en charge des conditions de type commutateur pour choisir l'unité de travail suivante en fonction de l'état actuel. L'une des principales différences est que les fonctions de pas nécessitent que chaque état définisse le suivant, tandis que Workflows exécute les étapes dans l'ordre dans lequel elles sont spécifiées (y compris les étapes suivantes alternatives). Pour en savoir plus, consultez Conditions et Étapes.

Nœud de calcul

Les deux produits orchestrent des ressources de calcul telles que des fonctions, des conteneurs et d'autres services Web pour mener à bien les tâches. Dans Step Functions, le nœud de calcul est identifié par un champ Resource, qui est, d'un point de vue syntaxique, un URI. Les URI utilisés pour identifier les ressources de nœuds de calcul sont au format Amazon Resource Name (ARN), et les utilisateurs ne peuvent pas appeler directement un point de terminaison HTTP arbitraire.

Les workflows peuvent envoyer des requêtes HTTP à un point de terminaison HTTP arbitraire et obtenir une réponse. Les connecteurs facilitent la connexion à d'autres API Google Cloud dans un workflow et l'intégration de vos workflows à d'autres produits Google Cloud tels que Pub/Sub, BigQuery ou Cloud Build. Les connecteurs simplifient les services d'appel, car ils gèrent la mise en forme des requêtes à votre place, en fournissant des méthodes et des arguments qui vous évitent d'avoir à connaître les détails d'une API Google Cloud. Vous pouvez également configurer le délai avant expiration et les règles d'interrogation.

Fiabilité

Si une tâche échoue, votre workflow doit pouvoir effectuer de nouvelles tentatives correctement, intercepter les exceptions le cas échéant et réacheminer le workflow si nécessaire. Les fonctions intermédiaires et les workflows assurent la fiabilité à l'aide de mécanismes similaires: interception d'exceptions avec nouvelles tentatives et distribution finale vers un autre emplacement du workflow. Pour en savoir plus, consultez la section Erreurs de workflow.

Parallélisme

Vous souhaitez peut-être que votre workflow orchestre plusieurs tâches en parallèle. Les fonctions intermédiaire offrent deux façons d'y parvenir: vous pouvez prendre un élément de données et le transmettre parallèlement à plusieurs tâches différentes, ou utiliser un tableau et transmettre ses éléments à la même tâche.

Dans Workflows, vous pouvez définir une partie de votre workflow dans laquelle deux étapes ou plus peuvent s'exécuter simultanément. Vous pouvez définir soit des branches qui s'exécutent simultanément, soit une boucle dans laquelle des itérations s'exécutent simultanément. Pour en savoir plus, consultez la section Exécuter les étapes de workflow en parallèle.

Données d'état

L'un des avantages d'un moteur de workflow est que les données d'état sont gérées automatiquement sans datastore externe. Dans Step Functions, les données d'état sont transmises d'un état à un autre dans une structure JSON.

Dans Workflows, vous pouvez enregistrer les données d'état dans des variables globales. Étant donné que vous disposez d'une durée d'exécution maximale d'un an, vous pouvez conserver les données d'état tant que l'instance est toujours en cours d'exécution.

Ratio d'économie d'énergie (EER)

Les deux produits s'appuient sur un système de Identity and Access Management (IAM) sous-jacent pour l'authentification et le contrôle des accès. Par exemple, vous pouvez utiliser un rôle IAM pour appeler des fonctions par étapes.

Dans Workflows, vous pouvez utiliser un compte de service pour appeler un workflow, OAuth 2.0 ou OIDC pour vous connecter aux API Google Cloud, et un en-tête de requête d'autorisation pour vous authentifier auprès d'une API tierce. Pour en savoir plus, consultez Accorder à un workflow l'autorisation d'accéder aux ressources Google Cloud et Effectuer des requêtes authentifiées à partir d'un workflow.

Expérience utilisateur

Vous pouvez utiliser un outil de ligne de commande ou une Infrastructure as Code (IaC) tel que Terraform pour définir et gérer à la fois des fonctions d'étape et des workflows.

En outre, Workflows permet d'exécuter des workflows à l'aide des bibliothèques clientes, de la console Google Cloud, de la Google Cloud CLI ou en envoyant une requête à l'API REST Workflows. Pour en savoir plus, consultez la section Exécuter un workflow.

Tarification

Les deux produits disposent d'une version gratuite. Pour en savoir plus, consultez leurs pages de tarification respectives: Tarifs de Step Functions et Tarifs de Workflows.

Mapper les types d'états avec les étapes

Il existe huit types d'état dans les fonctions de pas. Les états sont des éléments d'une machine à états qui peuvent prendre des décisions en fonction de leur entrée, effectuer des actions et transmettre la sortie à d'autres états. Avant de migrer des fonctions de cette étape vers Workflows, assurez-vous de bien comprendre comment traduire chaque type d'état en une étape de Workflows.

Choice

L'état Choice ajoute une logique de branchement à une machine à états.

Dans Workflows, vous pouvez utiliser un bloc switch comme mécanisme de sélection qui permet à la valeur d'une expression de contrôler le flux d'exécution d'un workflow. Si une valeur correspond, l'instruction de cette condition est exécutée. Pour en savoir plus, consultez la section Conditions.

Fonctions de pas

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

Fichier YAML des workflows

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

Workflows JSON

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

Fail

L'état Fail arrête l'exécution de la machine à états et la marque comme échec.

Dans Workflows, vous pouvez générer des erreurs personnalisées à l'aide de la syntaxe raise, et détecter et gérer les erreurs à l'aide d'un bloc try/except. Pour en savoir plus, consultez la section Générer des erreurs.

Fonctions de pas

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

Fichier YAML des workflows

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

Workflows JSON

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

Map

Un état Map peut être utilisé pour exécuter un ensemble d'étapes pour chaque élément d'un tableau d'entrée.

Dans Workflows, vous pouvez utiliser des boucles for pour les itérations.

Fonctions de pas

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

Fichier YAML des 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}

Workflows JSON

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

Un état Parallel peut être utilisé pour créer des branches parallèles d'exécution dans votre machine à états. Dans l'exemple "Step Functions" suivant, une recherche d'adresse et de téléphone est effectuée en parallèle.

Dans Workflows, vous pouvez utiliser une étape parallel pour définir une partie de votre workflow dans laquelle deux étapes ou plus peuvent s'exécuter simultanément. Pour en savoir plus, consultez la section Étapes parallèles.

Fonctions de pas

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

Fichier YAML des 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}

Workflows JSON

  {
    "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 état Pass transmet son entrée à sa sortie, sans effectuer de travail. Cette méthode est couramment utilisée pour manipuler les données d'état dans le fichier JSON.

Comme Workflows ne transmet pas les données de cette manière, vous pouvez laisser l'état en mode no-op ou utiliser une étape d'attribution pour modifier les variables. Pour en savoir plus, consultez Attribuer des variables.

Fonctions de pas

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

Fichier YAML des workflows

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

Workflows JSON

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

Réussite

Un état Succeed arrête une exécution.

Dans Workflows, vous pouvez utiliser return dans le workflow principal pour arrêter l'exécution d'un workflow. Vous pouvez également terminer un workflow en terminant la dernière étape (en supposant que l'étape ne passe pas à une autre), ou utiliser next: end pour arrêter l'exécution d'un workflow si vous n'avez pas besoin de renvoyer une valeur. Pour en savoir plus, consultez la section Terminer l'exécution d'un workflow.

Fonctions de pas

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

Fichier YAML des workflows

  return: "Success!"
  next: end

Workflows JSON

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

Task

Un état Task représente une seule unité de travail effectuée par une machine à états. Dans l'exemple suivant, celui-ci appelle une fonction Lambda. (Les activités sont une fonctionnalité AWS Step Functions qui vous permet d'ajouter une tâche dans votre machine d'état où elle est effectuée ailleurs.)

Dans l'exemple Workflows, un appel est effectué vers un point de terminaison HTTP pour appeler une fonction Cloud. Vous pouvez également utiliser un connecteur qui permet d'accéder facilement à d'autres produits Google Cloud. En outre, vous pouvez suspendre un workflow et interroger des données. Vous pouvez également utiliser un point de terminaison de rappel pour signaler à votre workflow qu'un événement spécifié s'est produit, puis attendre cet événement sans interrogation.

Fonctions de pas

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

Fichier YAML des workflows

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

Workflows JSON

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

Wait

L'état Wait retarde la diffusion de la machine à états pendant une durée spécifiée.

Vous pouvez utiliser la fonction de bibliothèque standard Workflows sys.sleep pour suspendre l'exécution pendant le nombre de secondes donné, jusqu'à un maximum de 3 153 6 000 (un an).

Fonctions de pas

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

Fichier YAML des workflows

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

Workflows JSON

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

Exemple: Orchestrer des microservices

L'exemple de fonctions d'étape suivant permet de vérifier le cours d'une action, de déterminer s'il faut acheter ou vendre, puis signaler le résultat. La machine à états de l'exemple s'intègre à AWS Lambda en transmettant des paramètres, utilise une file d'attente Amazon SQS pour demander une approbation humaine et utilise un sujet Amazon SNS pour renvoyer les résultats de la requête.

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

Migrer vers Workflows

Pour migrer l'exemple de Step Functions précédent vers Workflows, vous pouvez créer les étapes équivalentes en intégrant Cloud Functions, en prenant en charge un point de terminaison de rappel qui attend l'arrivée de requêtes HTTP à ce point de terminaison, et en utilisant un connecteur Workflows pour effectuer la publication dans un sujet Pub/Sub à la place du sujet Amazon SNS:

  1. Suivez les étapes pour créer un workflow, mais ne le déployez pas encore.

  2. Dans la définition du workflow, ajoutez une étape permettant de créer un point de terminaison de rappel qui attend une entrée humaine, et une étape qui utilise un connecteur Workflows pour publier dans un sujet Pub/Sub. Exemple :

    Fichier YAML des 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
      ...

    Workflows JSON

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

    Remplacez les éléments suivants :

    • LOCATION: région Google Cloud compatible. Exemple :us-central1
    • PUBSUB_TOPIC_NAME: nom de votre sujet Pub/Sub. Exemple :my_stock_example
  3. Déployez, puis exécutez le workflow.

  4. Lors de l'exécution du workflow, celui-ci s'interrompt et attend que vous invoquiez le point de terminaison de rappel. Pour ce faire, vous pouvez utiliser une commande curl. Exemple :

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

    Remplacez CALLBACK_URL par le reste du chemin d'accès à votre point de terminaison de rappel.

  5. Une fois le workflow terminé, vous pouvez recevoir le message de l'abonnement Pub/Sub. Exemple :

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

    Le message de sortie doit ressembler à ce qui suit (buy ou sell):

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

Étapes suivantes