Criar um fluxo de trabalho human-in-the-loop usando callbacks


Neste tutorial, mostramos como criar um fluxo de trabalho de tradução que aguarda sua entrada (human-in-the-loop) e conecta um banco de dados do Firestore, duas funções do Cloud Run, a API Cloud Translation e uma página da Web que usa o SDK do Firebase para atualizar em tempo real.

Com o Workflows, é possível oferecer suporte a um endpoint de callback (ou webhook) que aguarda a chegada de solicitações HTTP nesse endpoint, retomando a execução do fluxo de trabalho posteriormente. Nesse caso, o fluxo de trabalho aguarda a rejeição da entrada ou a validação da tradução de algum texto, mas também pode aguardar um processo externo. Para mais informações, consulte Aguardar usando callbacks.

Arquitetura

Neste tutorial, criamos um app da Web que permite fazer o seguinte:

  1. Na página de tradução da Web, digite o texto que você quer traduzir do inglês para o francês. Clique em Traduzir.
  2. Na página da Web, uma função do Cloud Run é chamada para iniciar a execução do fluxo de trabalho. O texto a ser traduzido é transmitido como um parâmetro para a função e o fluxo de trabalho.
  3. O texto é salvo em um banco de dados do Cloud Firestore. A API Cloud Translation é chamada. A tradução retornada é armazenada no banco de dados. O app da Web é implantado usando o Firebase Hosting e as atualizações em tempo real para exibir o texto traduzido.
  4. A etapa create_callback no fluxo de trabalho cria um URL de endpoint de callback que também é salvo no banco de dados do Firestore. A página da Web exibe os botões Validar e Rejeitar.
  5. O fluxo de trabalho está pausado e aguarda uma solicitação HTTP POST explícita para o URL do endpoint de callback.
  6. É possível validar ou rejeitar a tradução. Clicando em um chama uma segunda função do Cloud Run que, por sua vez, chama o de retorno de chamada criado pelo fluxo de trabalho, transmitindo o status de aprovação. O fluxo de trabalho retoma a execução e salva um status de aprovação de true ou false no banco de dados do Firestore.

Este diagrama apresenta uma visão geral do processo:

Fluxo de trabalho com callback

Objetivos

  • Implantar um app da Web.
  • Criar um banco de dados do Firestore para armazenar solicitações de tradução.
  • Implantar funções do Cloud Run para executar o fluxo de trabalho.
  • Implantar e executar um fluxo de trabalho para orquestrar todo o processo.

Custos

Neste documento, você usará os seguintes componentes faturáveis do Google Cloud:

Para gerar uma estimativa de custo baseada na projeção de uso deste tutorial, use a calculadora de preços. Novos usuários do Google Cloud podem estar qualificados para uma avaliação gratuita.

Antes de começar

As restrições de segurança definidas pela sua organização podem impedir que você conclua as etapas a seguir. Para informações sobre solução de problemas, consulte Desenvolver aplicativos em um ambiente restrito do Google Cloud.

  1. Sign in to your Google Cloud account. If you're new to Google Cloud, create an account to evaluate how our products perform in real-world scenarios. New customers also get $300 in free credits to run, test, and deploy workloads.
  2. Install the Google Cloud CLI.
  3. To initialize the gcloud CLI, run the following command:

    gcloud init
  4. Create or select a Google Cloud project.

    • Create a Google Cloud project:

      gcloud projects create PROJECT_ID

      Replace PROJECT_ID with a name for the Google Cloud project you are creating.

    • Select the Google Cloud project that you created:

      gcloud config set project PROJECT_ID

      Replace PROJECT_ID with your Google Cloud project name.

  5. Make sure that billing is enabled for your Google Cloud project.

  6. Enable the App Engine, Cloud Build, Cloud Run functions, Firestore, Translation, and Workflows APIs:

    gcloud services enable appengine.googleapis.com cloudbuild.googleapis.com cloudfunctions.googleapis.com firestore.googleapis.com translate.googleapis.com workflows.googleapis.com
  7. Install the Google Cloud CLI.
  8. To initialize the gcloud CLI, run the following command:

    gcloud init
  9. Create or select a Google Cloud project.

    • Create a Google Cloud project:

      gcloud projects create PROJECT_ID

      Replace PROJECT_ID with a name for the Google Cloud project you are creating.

    • Select the Google Cloud project that you created:

      gcloud config set project PROJECT_ID

      Replace PROJECT_ID with your Google Cloud project name.

  10. Make sure that billing is enabled for your Google Cloud project.

  11. Enable the App Engine, Cloud Build, Cloud Run functions, Firestore, Translation, and Workflows APIs:

    gcloud services enable appengine.googleapis.com cloudbuild.googleapis.com cloudfunctions.googleapis.com firestore.googleapis.com translate.googleapis.com workflows.googleapis.com
  12. Atualize os componentes da CLI do Google Cloud:
    gcloud components update
  13. Faça login usando sua conta:
    gcloud auth login
  14. Defina o ID do projeto e o local padrão usados neste tutorial:
    export GOOGLE_CLOUD_PROJECT=PROJECT_ID
    export REGION=REGION
    gcloud config set workflows/location ${REGION}

    Substitua:

    • PROJECT_ID: é seu ID do projeto no Google Cloud. Você pode encontre o ID do projeto na Olá! do console do Google Cloud.
    • REGION: o local do fluxo de trabalho compatível de sua escolha.

implante a primeira função do Cloud Run

Essa função do Cloud Run inicia a execução do fluxo de trabalho. O texto a ser traduzido é transmitido como um parâmetro para a função e o fluxo de trabalho.

  1. Crie um diretório chamado callback-translation com subdiretórios chamados invokeTranslationWorkflow, translationCallbackCall e public:

    mkdir -p ~/callback-translation/{invokeTranslationWorkflow,translationCallbackCall,public}
  2. Altere para o diretório invokeTranslationWorkflow:

    cd ~/callback-translation/invokeTranslationWorkflow
  3. Crie um arquivo de texto com o nome index.js e o seguinte código Node.js:

    const cors = require('cors')({origin: true});
    const {ExecutionsClient} = require('@google-cloud/workflows');
    const client = new ExecutionsClient();
    
    exports.invokeTranslationWorkflow = async (req, res) => {
      cors(req, res, async () => {
        const text = req.body.text;
        console.log(`Translation request for "${text}"`);
    
        const PROJECT_ID = process.env.PROJECT_ID;
        const CLOUD_REGION = process.env.CLOUD_REGION;
        const WORKFLOW_NAME = process.env.WORKFLOW_NAME;
    
        const execResponse = await client.createExecution({
          parent: client.workflowPath(PROJECT_ID, CLOUD_REGION, WORKFLOW_NAME),
          execution: {
            argument: JSON.stringify({text})
          }
        });
        console.log(`Translation workflow execution request: ${JSON.stringify(execResponse)}`);
    
        const execName = execResponse[0].name;
        console.log(`Created translation workflow execution: ${execName}`);
    
        res.set('Access-Control-Allow-Origin', '*');
        res.status(200).json({executionId: execName});
      });
    };
  4. Crie um arquivo de texto com o nome package.json e os seguintes metadados npm:

    {
      "name": "launch-translation-workflow",
      "version": "0.0.1",
      "dependencies": {
        "@google-cloud/workflows": "^1.2.5",
        "cors": "^2.8.5"
      }
    }
    
  5. Implante a função com um gatilho HTTP e permita acesso não autenticado:

    gcloud functions deploy invokeTranslationWorkflow \
    --region=${REGION} \
    --runtime nodejs14 \
    --entry-point=invokeTranslationWorkflow \
    --set-env-vars PROJECT_ID=${GOOGLE_CLOUD_PROJECT},CLOUD_REGION=${REGION},WORKFLOW_NAME=translation_validation \
    --trigger-http \
    --allow-unauthenticated

    A função pode levar alguns minutos para ser implantada. Se preferir, use a interface de funções do Cloud Run no Console do Google Cloud para implantar a função.

  6. Depois que a função for implantada, confirme a propriedade httpsTrigger.url:

    gcloud functions describe invokeTranslationWorkflow

    Anote o URL retornado para usá-lo em uma etapa posterior.

implante a segunda função do Cloud Run

Esta função do Cloud Run faz uma solicitação HTTP POST ao callback de destino criado pelo fluxo de trabalho, transmitindo um status de aprovação que reflete se a tradução é validada ou rejeitada.

  1. Altere para o diretório translationCallbackCall:

    cd ../translationCallbackCall
  2. Crie um arquivo de texto com o nome index.js e o seguinte código Node.js:

    const cors = require('cors')({origin: true});
    const fetch = require('node-fetch');
    
    exports.translationCallbackCall = async (req, res) => {
      cors(req, res, async () => {
        res.set('Access-Control-Allow-Origin', '*');
    
        const {url, approved} = req.body;
        console.log("Approved? ", approved);
        console.log("URL = ", url);
        const {GoogleAuth} = require('google-auth-library');
        const auth = new GoogleAuth();
        const token = await auth.getAccessToken();
        console.log("Token", token);
    
        try {
          const resp = await fetch(url, {
              method: 'POST',
              headers: {
                  'accept': 'application/json',
                  'content-type': 'application/json',
                  'authorization': `Bearer ${token}`
              },
              body: JSON.stringify({ approved })
          });
          console.log("Response = ", JSON.stringify(resp));
    
          const result = await resp.json();
          console.log("Outcome = ", JSON.stringify(result));
    
          res.status(200).json({status: 'OK'});
        } catch(e) {
          console.error(e);
    
          res.status(200).json({status: 'error'});
        }
      });
    };
  3. Crie um arquivo de texto com o nome package.json e os seguintes metadados npm:

    {
      "name": "approve-translation-workflow",
      "version": "0.0.1",
      "dependencies": {
        "cors": "^2.8.5",
        "node-fetch": "^2.6.1",
        "google-auth-library": "^7.1.1"
      }
    }
    
  4. Implante a função com um gatilho HTTP e permita acesso não autenticado:

    gcloud functions deploy translationCallbackCall \
    --region=${REGION} \
    --runtime nodejs14 \
    --entry-point=translationCallbackCall \
    --trigger-http \
    --allow-unauthenticated

    A função pode levar alguns minutos para ser implantada. Se preferir, use a interface de funções do Cloud Run no Console do Google Cloud para implantar a função.

  5. Depois que a função for implantada, confirme a propriedade httpsTrigger.url:

    gcloud functions describe translationCallbackCall

    Anote o URL retornado para usá-lo em uma etapa posterior.

Implantar o fluxo de trabalho

Um fluxo de trabalho é composto por uma série de etapas descritas usando a sintaxe dos fluxos de trabalho, que pode ser escrita em formato YAML ou JSON. Essa é a definição do fluxo de trabalho. Depois de criar um fluxo de trabalho, implante-o para que ele fique disponível para execução.

  1. Altere para o diretório callback-translation:

    cd ..
  2. Crie um arquivo de texto com o nome de arquivo translation-validation.yaml com o seguinte conteúdo:

    main:
        params: [translation_request]
        steps:
            - log_request:
                call: sys.log
                args:
                    text: ${translation_request}
            - vars:
                assign:
                    - exec_id: ${sys.get_env("GOOGLE_CLOUD_WORKFLOW_EXECUTION_ID")}
                    - text_to_translate: ${translation_request.text}
                    - database_root: ${"projects/" + sys.get_env("GOOGLE_CLOUD_PROJECT_ID") + "/databases/(default)/documents/translations/"}
            - log_translation_request:
                call: sys.log
                args:
                    text: ${text_to_translate}
    
            - store_translation_request:
                call: googleapis.firestore.v1.projects.databases.documents.patch
                args:
                    name: ${database_root + exec_id}
                    updateMask:
                        fieldPaths: ['text']
                    body:
                        fields:
                            text:
                                stringValue: ${text_to_translate}
                result: store_translation_request_result
    
            - translate:
                call: googleapis.translate.v2.translations.translate
                args:
                    query:
                        q: ${text_to_translate}
                        target: "FR"
                        format: "text"
                        source: "EN"
                result: translation_result
            - assign_translation:
                assign:
                    - translation: ${translation_result.data.translations[0].translatedText} 
            - log_translation_result:
                call: sys.log
                args:
                    text: ${translation}
    
            - store_translated_text:
                call: googleapis.firestore.v1.projects.databases.documents.patch
                args:
                    name: ${database_root + exec_id}
                    updateMask:
                        fieldPaths: ['translation']
                    body:
                        fields:
                            translation:
                                stringValue: ${translation}
                result: store_translation_request_result   
    
            - create_callback:
                call: events.create_callback_endpoint
                args:
                    http_callback_method: "POST"
                result: callback_details
            - log_callback_details:
                call: sys.log
                args:
                    text: ${callback_details}
    
            - store_callback_details:
                call: googleapis.firestore.v1.projects.databases.documents.patch
                args:
                    name: ${database_root + exec_id}
                    updateMask:
                        fieldPaths: ['callback']
                    body:
                        fields:
                            callback:
                                stringValue: ${callback_details.url}
                result: store_callback_details_result
    
            - await_callback:
                call: events.await_callback
                args:
                    callback: ${callback_details}
                    timeout: 3600
                result: callback_request
            - assign_approval:
                assign:
                    - approved: ${callback_request.http_request.body.approved}
    
            - store_approval:
                call: googleapis.firestore.v1.projects.databases.documents.patch
                args:
                    name: ${database_root + exec_id}
                    updateMask:
                        fieldPaths: ['approved']
                    body:
                        fields:
                            approved:
                                booleanValue: ${approved}
                result: store_approval_result
    
            - return_outcome:
                return:
                    text: ${text_to_translate}
                    translation: ${translation}
                    approved: ${approved}
  3. Depois de criar o fluxo de trabalho, implante-o, mas não execute-o:

    gcloud workflows deploy translation_validation --source=translation-validation.yaml

Criar seu app da Web

Crie um app da Web que chame uma função do Cloud que inicie a execução do fluxo de trabalho. A página da Web é atualizada em tempo real para exibir o resultado da solicitação de tradução.

  1. Altere para o diretório public:

    cd public
  2. Crie um arquivo de texto com o nome index.html e a seguinte marcação HTML. Em uma etapa posterior, você modificará o arquivo index.html e adicionará os scripts do SDK do Firebase.

    <!DOCTYPE html>
    <html lang="en">
    
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width">
    
        <title>Frenglish translation — Feature Workflows callbacks</title>
    
        <link rel="stylesheet"
            href="https://cdn.jsdelivr.net/npm/@shoelace-style/shoelace@2.0.0-beta.42/dist/themes/base.css">
        <script type="module"
            src="https://cdn.jsdelivr.net/npm/@shoelace-style/shoelace@2.0.0-beta.42/dist/shoelace.js"></script>
        <link rel="stylesheet" href="./style.css">
    </head>
    
    <body>
        <h1>Translate from English to French</h1>
    
        <sl-form class="form-overview">
            <sl-textarea id="text" placeholder="The quick brown fox jumps over the lazy dog."
                label="English text to translate"></sl-textarea>
            <p></p>
            <sl-button id="translateBtn" type="primary">Translate</sl-button>
            <p></p>
            <sl-alert id="translation" type="primary">
                Le rapide renard brun saute au dessus du chien paresseux.
            </sl-alert>
            <p></p>
            <div id="buttonRow" style="display: none;">
                <sl-button id="validateBtn" type="success">Validate</sl-button>
                <sl-button id="rejectBtn" type="danger">Reject</sl-button>
            </div>
            <p></p>
            <sl-alert id="validationAlert" type="success">
                <sl-icon slot="icon" name="check2-circle"></sl-icon>
                <strong>The translation has been validated</strong><br>
                Glad that you liked our translation! We'll save it in our database.
            </sl-alert>
            <sl-alert id="rejectionAlert" type="danger">
                <sl-icon slot="icon" name="exclamation-octagon"></sl-icon>
                <strong>The translation has been rejected</strong><br>
                A pity the translation isn't good! We'll do better next time!
            </sl-alert>
            <p></p>
            <sl-button id="newBtn" style="display: none;" type="primary">New translation</sl-button>
        </sl-form>
    
        <script src="https://www.gstatic.com/firebasejs/8.6.3/firebase-app.js"></script>
        <script src="https://www.gstatic.com/firebasejs/8.6.3/firebase-firestore.js"></script>
    
        <script>
            var firebaseConfig = {
                apiKey: "XXXX",
                authDomain: "XXXX",
                projectId: "XXXX",
                storageBucket: "XXXX",
                messagingSenderId: "XXXX",
                appId: "XXXX",
                measurementId: "XXXX"
            };
            // Initialize Firebase
            firebase.initializeApp(firebaseConfig);
        </script>
        <script src="./script.js" type="module"></script>
    </body>
    
    </html>
    
  3. Crie um arquivo de texto com o nome de arquivo script.js que contém o seguinte código JavaScript:

    document.addEventListener("DOMContentLoaded", async function (event) {
        const textArea = document.getElementById("text");
        textArea.focus();
    
        const newBtn = document.getElementById("newBtn");
        newBtn.addEventListener("sl-focus", event => {
            event.target.blur();
            window.location.reload();
        });
    
        const translationAlert = document.getElementById("translation");
        const buttonRow = document.getElementById("buttonRow");
    
        var callbackUrl = "";
    
        const validationAlert = document.getElementById("validationAlert");
        const rejectionAlert = document.getElementById("rejectionAlert");
        const validateBtn = document.getElementById("validateBtn");
        const rejectBtn = document.getElementById("rejectBtn");
    
        const translateBtn = document.getElementById("translateBtn");
        translateBtn.addEventListener("sl-focus", async event => {
            event.target.disabled = true;
            event.target.loading = true;
            textArea.disabled = true;
    
            console.log("Text to translate = ", textArea.value);
    
            const fnUrl = UPDATE_ME;
    
            try {
                console.log("Calling workflow executor function...");
                const resp = await fetch(fnUrl, {
                    method: "POST",
                    headers: {
                        "accept": "application/json",
                        "content-type": "application/json"
                    },
                    body: JSON.stringify({ text: textArea.value })
                });
                const executionResp = await resp.json();
                const executionId = executionResp.executionId.slice(-36);
                console.log("Execution ID = ", executionId);
    
                const db = firebase.firestore();
                const translationDoc = db.collection("translations").doc(executionId);
    
                var translationReceived = false;
                var callbackReceived =  false;
                var approvalReceived = false;
                translationDoc.onSnapshot((doc) => {
                    console.log("Firestore update", doc.data());
                    if (doc.data()) {
                        if ("translation" in doc.data()) {
                            if (!translationReceived) {
                                console.log("Translation = ", doc.data().translation);
                                translationReceived = true;
                                translationAlert.innerText = doc.data().translation;
                                translationAlert.open = true;
                            }
                        }
                        if ("callback" in doc.data()) {
                            if (!callbackReceived) {
                                console.log("Callback URL = ", doc.data().callback);
                                callbackReceived = true;
                                callbackUrl = doc.data().callback;
                                buttonRow.style.display = "block";
                            }
                        }
                        if ("approved" in doc.data()) {
                            if (!approvalReceived) {
                                const approved = doc.data().approved;
                                console.log("Approval received = ", approved);
                                if (approved) {
                                    validationAlert.open = true;
                                    buttonRow.style.display = "none";
                                    newBtn.style.display = "inline-block";   
                                } else {
                                    rejectionAlert.open = true;
                                    buttonRow.style.display = "none";
                                    newBtn.style.display = "inline-block";
                                }
                                approvalReceived = true;
                            }
                        }
                    }
                });
            } catch (e) {
                console.log(e);
            }
            event.target.loading = false;
        });
    
        validateBtn.addEventListener("sl-focus", async event => {
            validateBtn.disabled = true;
            rejectBtn.disabled = true;
            validateBtn.loading = true;
            validateBtn.blur();
    
            // call callback
            await callCallbackUrl(callbackUrl, true);
        });
    
        rejectBtn.addEventListener("sl-focus", async event => {
            rejectBtn.disabled = true;
            validateBtn.disabled = true;
            rejectBtn.loading = true;
            rejectBtn.blur();
    
            // call callback
            await callCallbackUrl(callbackUrl, false);
        });
    
    });
    
    async function callCallbackUrl(url, approved) {
        console.log("Calling callback URL with status = ", approved);
    
        const fnUrl = UPDATE_ME;
        try {
            const resp = await fetch(fnUrl, {
                method: "POST",
                headers: {
                    "accept": "application/json",
                    "content-type": "application/json"
                },
                body: JSON.stringify({ url, approved })
            });
            const result = await resp.json();
            console.log("Callback answer = ", result);
        } catch(e) {
            console.log(e);
        }
    }
  4. Edite o arquivo script.js, substituindo os marcadores UPDATE_ME pelo URLs de função do Cloud Run que você anotou anteriormente.

    1. No método translateBtn.addEventListener, substitua const fnUrl = UPDATE_ME; por:

      const fnUrl = "https://REGION-PROJECT_ID.cloudfunctions.net/invokeTranslationWorkflow";

    2. Na função callCallbackUrl, substitua const fnUrl = UPDATE_ME; por:

      const fnUrl = "https://REGION-PROJECT_ID.cloudfunctions.net/translationCallbackCall";

  5. Crie um arquivo de texto com o nome style.css e os seguintes estilos em cascata:

    * {
        font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
    }
    
    body {
        margin: 20px;
    }
    
    h1, h2, h3, h4 {
        color: #0ea5e9;
    }
    

Adicionar o Firebase ao seu app da Web

Neste tutorial, a página HTML, o script JavaScript e a folha de estilo CSS são implantados como recursos estáticos usando o Firebase Hosting, mas podem ser hospedados em qualquer lugar e exibidos localmente na sua própria máquina para fins de teste.

Criar um projeto do Firebase

Antes de adicionar o Firebase ao seu projeto de app, é preciso criar um projeto do Firebase para ser conectado ao app.

  1. No console do Firebase, clique em Criar um projeto e selecione seu projeto do Google Cloud no menu suspenso para adicionar recursos do Firebase a esse projeto.

  2. Clique em Continuar até ver a opção de adicionar o Firebase.

  3. Pule a configuração do Google Analytics para o projeto.

  4. Clique em Adicionar Firebase:

O Firebase provisiona recursos automaticamente para seu projeto. Quando o processo for concluído, você será direcionado para a página de visão geral do seu projeto no Console do Firebase.

Registrar o app no Firebase

Depois de criar um projeto do Firebase, é possível adicionar seu app da Web a ele.

  1. No centro da página de visão geral do projeto, clique no ícone Web (</>) para iniciar o fluxo de trabalho de configuração.

  2. Dê um apelido para o app.

    Ele só é visível para você no Console do Firebase.

  3. Ignore a configuração do Firebase Hosting por enquanto.

  4. Clique em Registrar app e passe para o Console.

Ativar o Cloud Firestore

O app da Web usa o Cloud Firestore para receber e salvar dados. Você precisa ativar o Cloud Firestore.

  1. Na seção Build do Console do Firebase, clique em Firestore Database.

    Talvez seja necessário expandir o painel de navegação à esquerda para ver a seção Build.

  2. No painel do Cloud Firestore, clique em Criar banco de dados.

  3. Selecione Iniciar no modo de teste usando uma regra de segurança, como a seguinte:

    rules_version = '2';
    service cloud.firestore {
    match /databases/{database}/documents {
      match /{document=**} {
        allow read, write;
      }
    }
    }
  4. Clique em Próxima depois de ler o aviso sobre as regras de segurança.

  5. Defina o local em que os dados do Cloud Firestore são armazenados. Aceite o padrão ou escolha uma região próxima a você.

  6. Clique em Ativar para provisionar o Firestore.

Adicionar o SDK do Firebase e inicializar o Firebase

O Firebase fornece bibliotecas JavaScript para a maioria dos produtos do Firebase. Antes de usar o Firebase Hosting, você precisa adicionar os SDKs do Firebase ao seu app da Web.

  1. Para inicializar o Firebase no seu aplicativo, é preciso fornecer a configuração do projeto do Firebase do seu aplicativo.
    1. No Console do Firebase, acesse as Configurações do projeto .
    2. Selecione o app no painel Seus apps.
    3. No painel Configuração do SDK, selecione CDN para carregar as bibliotecas do SDK do Firebase no CDN.
    4. Copie o snippet para o arquivo index.html na parte inferior da tag <body>, substituindo os valores do marcador XXXX.
  2. Instale o SDK do Firebase para JavaScript.

    1. Se você ainda não tiver um arquivo package.json, crie um executando o seguinte comando no diretório callback-translation:

      npm init
    2. Instale o pacote npm firebase e salve-o no arquivo package.json executando:

      npm install --save firebase

Inicializar e implantar o projeto

Para conectar os arquivos do projeto local ao seu projeto do Firebase, inicialize o projeto. Em seguida, implante o app da Web.

  1. No diretório callback-translation, execute o seguinte comando:

    firebase init
  2. Selecione a opção Configure files for Firebase Hosting and (optionally) set up GitHub Action deploys.

  3. Escolha usar um projeto atual e insira o ID do projeto.

  4. Aceite public como o diretório raiz público padrão.

  5. Escolha configurar um app de página única.

  6. Pule a configuração de implantações automáticas com o GitHub.

  7. No prompt File public/index.html already exists. Overwrite?, digite Não.

  8. Altere para o diretório public:

    cd public
  9. No diretório public, execute o seguinte comando para implantar seu projeto no seu local:

    firebase deploy --only hosting

Testar o app da Web localmente

Com o Firebase Hosting, você confere e testa as mudanças localmente e interage com os recursos do projeto de back-end emulado. Ao usar firebase serve, seu app interage com um back-end emulado para seu conteúdo de hospedagem e sua configuração, mas interage com seu back-end real para todos os outros recursos do projeto. Neste tutorial, é possível usar firebase serve, mas isso não é recomendado para testes mais abrangentes.

  1. No diretório public, execute o seguinte comando:

    firebase serve
  2. Abra seu app da Web no URL local retornado (geralmente http://localhost:5000).

  3. Digite um texto em inglês e clique em Traduzir.

    Será exibida uma tradução do texto em francês.

  4. Agora clique em Validar ou Rejeitar.

    No banco de dados do Firestore, é possível verificar o conteúdo. A visualização será parecida com esta:

    approved: true
    callback: "https://workflowexecutions.googleapis.com/v1/projects/26811016474/locations/us-central1/workflows/translation_validation/executions/68bfce75-5f62-445f-9cd5-eda23e6fa693/callbacks/72851c97-6bb2-45e3-9816-1e3dcc610662_1a16697f-6d90-478d-9736-33190bbe222b"
    text: "The quick brown fox jumps over the lazy dog."
    translation: "Le renard brun rapide saute par-dessus le chien paresseux."
    

    O status approved é true ou false, dependendo de você ter validado ou rejeitado a tradução.

Parabéns! Você criou um fluxo de trabalho de tradução human-in-the-loop que usa callbacks do Workflows.

Limpar

Se você criou um novo projeto para este tutorial, exclua o projeto. Se você usou um projeto atual e quer mantê-lo sem as alterações incluídas neste tutorial, exclua os recursos criados para o tutorial.

Exclua o projeto

O jeito mais fácil de evitar cobranças é excluindo o projeto que você criou para o tutorial.

Para excluir o projeto:

  1. In the Google Cloud console, go to the Manage resources page.

    Go to Manage resources

  2. In the project list, select the project that you want to delete, and then click Delete.
  3. In the dialog, type the project ID, and then click Shut down to delete the project.

Excluir recursos do tutorial

  1. Remova a configuração padrão da CLI do gcloud que você adicionou durante a configuração do tutorial:

    gcloud config unset workflows/location
  2. Exclua o fluxo de trabalho criado neste tutorial:

    gcloud workflows delete WORKFLOW_NAME
  3. Exclua as funções do Cloud Run criadas neste tutorial:

    gcloud functions delete FUNCTION_NAME

    Também é possível excluir funções do Cloud Run pelo Console do Google Cloud.

A seguir