Crear un flujo de trabajo con intervención humana mediante retrollamadas


En este tutorial se explica cómo crear un flujo de trabajo de traducción que espere tu intervención (el humano en el bucle) y que conecte una base de datos de Firestore, dos funciones de Cloud Run, la API Cloud Translation y una página web que use el SDK de Firebase para actualizarse en tiempo real.

Con los flujos de trabajo, puedes admitir un endpoint de retrollamada (o webhook) que espera a que lleguen solicitudes HTTP a ese endpoint y reanuda la ejecución del flujo de trabajo más adelante. En este caso, el flujo de trabajo espera a que rechaces o valides la traducción de un texto, pero también podría esperar a que se complete un proceso externo. Para obtener más información, consulta Esperar usando retrollamadas.

Arquitectura

En este tutorial se crea una aplicación web que te permite hacer lo siguiente:

  1. En la página web de traducción, escribe el texto que quieras traducir del inglés al francés. Haz clic en Traducir.
  2. Desde la página web, se llama a una función de Cloud Run que inicia la ejecución del flujo de trabajo. El texto que se va a traducir se pasa como parámetro tanto a la función como al flujo de trabajo.
  3. El texto se guarda en una base de datos de Cloud Firestore. Se llama a la API Cloud Translation. La traducción devuelta se almacena en la base de datos. La aplicación web se implementa mediante Firebase Hosting y se actualiza en tiempo real para mostrar el texto traducido.
  4. En el paso create_callback del flujo de trabajo se crea una URL de endpoint de retrollamada, que también se guarda en la base de datos de Firestore. En la página web ahora se muestran los botones Validar y Rechazar.
  5. El flujo de trabajo se ha pausado y espera una solicitud HTTP POST explícita a la URL del endpoint de retrollamada.
  6. Puedes validar o rechazar la traducción. Al hacer clic en un botón, se llama a una segunda función de Cloud Run, que a su vez llama al endpoint de retrollamada creado por el flujo de trabajo y le transmite el estado de aprobación. El flujo de trabajo reanuda su ejecución y guarda el estado de aprobación true o false en la base de datos de Firestore.

En este diagrama se muestra un resumen del proceso:

Flujo de trabajo con retrollamada

Objetivos

  • Despliega una aplicación web.
  • Crea una base de datos de Firestore para almacenar las solicitudes de traducción.
  • Despliega funciones de Cloud Run para ejecutar el flujo de trabajo.
  • Despliega y ejecuta un flujo de trabajo para orquestar todo el proceso.

Costes

En este documento, se utilizan los siguientes componentes facturables de Google Cloud:

Para generar una estimación de costes basada en el uso previsto, utiliza la calculadora de precios.

Los usuarios nuevos Google Cloud pueden disfrutar de una prueba gratuita.

Antes de empezar

Es posible que las restricciones de seguridad definidas por tu organización te impidan completar los siguientes pasos. Para obtener información sobre cómo solucionar problemas, consulta el artículo Desarrollar aplicaciones en un entorno limitado 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. Si utilizas un proveedor de identidades (IdP) externo, primero debes iniciar sesión en la CLI de gcloud con tu identidad federada.

  4. Para inicializar gcloud CLI, ejecuta el siguiente comando:

    gcloud init
  5. 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.

  6. Verify that billing is enabled for your Google Cloud project.

  7. 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
  8. Install the Google Cloud CLI.

  9. Si utilizas un proveedor de identidades (IdP) externo, primero debes iniciar sesión en la CLI de gcloud con tu identidad federada.

  10. Para inicializar gcloud CLI, ejecuta el siguiente comando:

    gcloud init
  11. 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.

  12. Verify that billing is enabled for your Google Cloud project.

  13. 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
  14. Actualiza los componentes de Google Cloud CLI:
    gcloud components update
  15. Inicia sesión con tu cuenta:
    gcloud auth login
  16. Define el ID del proyecto y la ubicación predeterminada que se usarán en este tutorial:
    export GOOGLE_CLOUD_PROJECT=PROJECT_ID
    export REGION=REGION
    gcloud config set workflows/location ${REGION}

    Haz los cambios siguientes:

    • PROJECT_ID: tu ID de proyecto Google Cloud . Puedes encontrar el ID de tu proyecto en la página Bienvenido de la consola de Google Cloud .
    • REGION: la ubicación de los flujos de trabajo admitidos que elijas.

  17. Desplegar la primera función de Cloud Run

    Esta función de Cloud Run inicia la ejecución del flujo de trabajo. El texto que se va a traducir se pasa como parámetro tanto a la función como al flujo de trabajo.

    1. Crea un directorio llamado callback-translation con los subdirectorios invokeTranslationWorkflow, translationCallbackCall y public:

      mkdir -p ~/callback-translation/{invokeTranslationWorkflow,translationCallbackCall,public}
    2. Cambia al directorio invokeTranslationWorkflow:

      cd ~/callback-translation/invokeTranslationWorkflow
    3. Crea un archivo de texto con el nombre index.js que contenga el siguiente código de 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. Crea un archivo de texto con el nombre package.json que contenga los siguientes metadatos npm:

      {
        "name": "launch-translation-workflow",
        "version": "0.0.1",
        "dependencies": {
          "@google-cloud/workflows": "^1.2.5",
          "cors": "^2.8.5"
        }
      }
      
    5. Despliega la función con un activador HTTP y permite el acceso sin autenticar:

      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

      La función puede tardar unos minutos en implementarse. También puedes usar la interfaz de funciones de Cloud Run en la Google Cloud consola para desplegar la función.

    6. Una vez que se haya implementado la función, puede confirmar la propiedad httpsTrigger.url:

      gcloud functions describe invokeTranslationWorkflow

      Anota la URL que se devuelve para poder usarla en un paso posterior.

    Desplegar la segunda función de Cloud Run

    Esta función de Cloud Run envía una solicitud HTTP POST al endpoint de retrollamada creado por el flujo de trabajo y le transfiere un estado de aprobación que refleja si la traducción se ha validado o rechazado.

    1. Cambia al directorio translationCallbackCall:

      cd ../translationCallbackCall
    2. Crea un archivo de texto con el nombre index.js que contenga el siguiente código de 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. Crea un archivo de texto con el nombre package.json que contenga los siguientes metadatos 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. Despliega la función con un activador HTTP y permite el acceso sin autenticar:

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

      La función puede tardar unos minutos en implementarse. También puedes usar la interfaz de funciones de Cloud Run en la Google Cloud consola para desplegar la función.

    5. Una vez que se haya implementado la función, puede confirmar la propiedad httpsTrigger.url:

      gcloud functions describe translationCallbackCall

      Anota la URL que se devuelve para poder usarla en un paso posterior.

    Desplegar el flujo de trabajo

    Un flujo de trabajo se compone de una serie de pasos descritos mediante la sintaxis de Workflows, que se puede escribir en formato YAML o JSON. Esta es la definición del flujo de trabajo. Después de crear un flujo de trabajo, debes implementarlo para que esté disponible.

    1. Cambia al directorio callback-translation:

      cd ..
    2. Crea un archivo de texto con el nombre translation-validation.yaml y el siguiente contenido:

      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. Después de crear el flujo de trabajo, puedes implementarlo, pero no lo ejecutes:

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

    Crear una aplicación web

    Crea una aplicación web que llame a una función de Cloud Functions que inicie la ejecución del flujo de trabajo. La página web se actualiza en tiempo real para mostrar el resultado de la solicitud de traducción.

    1. Cambia al directorio public:

      cd public
    2. Crea un archivo de texto llamado index.html que contenga el siguiente marcado HTML. En un paso posterior, modificará el archivo index.html y añadirá las secuencias de comandos del SDK de 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. Crea un archivo de texto con el nombre script.js que contenga el siguiente 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. Edita el archivo script.js y sustituye los marcadores de posición UPDATE_ME por las URLs de las funciones de Cloud Run que has anotado anteriormente.

      1. En el método translateBtn.addEventListener, sustituye const fnUrl = UPDATE_ME; por:

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

      2. En la función callCallbackUrl, sustituye const fnUrl = UPDATE_ME; por:

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

    5. Crea un archivo de texto con el nombre style.css que contenga los siguientes estilos en cascada:

      * {
          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;
      }
      

    Añade Firebase a tu aplicación web

    En este tutorial, la página HTML, la secuencia de comandos de JavaScript y la hoja de estilo CSS se implementan como recursos estáticos mediante Firebase Hosting, pero se pueden alojar en cualquier lugar y servir de forma local en tu propio ordenador para hacer pruebas.

    Crear un proyecto de Firebase

    Antes de añadir Firebase a tu aplicación, debes crear un proyecto de Firebase al que conectarla.

    1. En la consola de Firebase, haga clic en Crear un proyecto y, a continuación, seleccione su proyecto de Google Cloud en el menú desplegable para añadir recursos de Firebase a ese proyecto.

    2. Haz clic en Continuar hasta que veas la opción de añadir Firebase.

    3. Saltar la configuración de Google Analytics en tu proyecto.

    4. Haz clic en Añadir Firebase.

    Firebase aprovisiona automáticamente los recursos de tu proyecto de Firebase. Cuando se complete el proceso, se le dirigirá a la página de resumen de su proyecto en la consola de Firebase.

    Registrar tu aplicación en Firebase

    Una vez que tengas un proyecto de Firebase, puedes añadirle tu aplicación web.

    1. En el centro de la página de descripción general del proyecto, haga clic en el icono Web (</>) para iniciar el flujo de trabajo de configuración.

    2. Introduce un nombre para tu aplicación.

      Solo tú puedes verla en la consola de Firebase.

    3. Por ahora, no configures Firebase Hosting.

    4. Haz clic en Registrar aplicación y continúa en la consola.

    Habilitar Cloud Firestore

    La aplicación web usa Cloud Firestore para recibir y guardar datos. Deberás habilitar Cloud Firestore.

    1. En la sección Desarrollo de la consola de Firebase, haz clic en Base de datos de Firestore.

      Es posible que tengas que ampliar el panel de navegación de la izquierda para ver la sección Crear.

    2. En el panel de Cloud Firestore, haz clic en Crear base de datos.

    3. Selecciona Iniciar en modo de prueba con una regla de seguridad como la siguiente:

      rules_version = '2';
      service cloud.firestore {
      match /databases/{database}/documents {
        match /{document=**} {
          allow read, write;
        }
      }
      }
    4. Después de leer la renuncia de responsabilidad sobre las reglas de seguridad, haz clic en Siguiente.

    5. Define la ubicación en la que se almacenarán tus datos de Cloud Firestore. Puedes aceptar el valor predeterminado o elegir una región cercana.

    6. Haz clic en Habilitar para aprovisionar Firestore.

    Añadir el SDK de Firebase e inicializar Firebase

    Firebase proporciona bibliotecas de JavaScript para la mayoría de los productos de Firebase. Antes de usar Firebase Hosting, debe añadir los SDKs de Firebase a su aplicación web.

    1. Para inicializar Firebase en tu aplicación, debes proporcionar la configuración del proyecto de Firebase de tu aplicación.
      1. En la consola de Firebase, vaya a Configuración del proyecto .
      2. En el panel Tus aplicaciones, selecciona tu aplicación.
      3. En el panel Configuración del SDK, selecciona CDN para cargar las bibliotecas del SDK de Firebase desde la CDN.
      4. Copia el fragmento en el archivo index.html, en la parte inferior de la etiqueta <body>, y sustituye los valores de los marcadores de posición XXXX.
    2. Instala el SDK de JavaScript de Firebase.

      1. Si aún no tienes un archivo package.json, crea uno ejecutando el siguiente comando desde el directorio callback-translation:

        npm init
      2. Instala el paquete firebase npm y guárdalo en el archivo package.json ejecutando el siguiente comando:

        npm install firebase

    Inicializar y desplegar un proyecto

    Para conectar los archivos de tu proyecto local con tu proyecto de Firebase, debes inicializarlo. Después, puedes desplegar tu aplicación web.

    1. En el directorio callback-translation, ejecuta el siguiente comando:

      firebase init
    2. Selecciona la opción Configure files for Firebase Hosting and (optionally) set up GitHub Action deploys.

    3. Elige usar un proyecto y escribe su ID.

    4. Acepta public como directorio raíz público predeterminado.

    5. Elige configurar una aplicación de una sola página.

    6. Saltarse la configuración de compilaciones y despliegues automáticos con GitHub.

    7. En la petición File public/index.html already exists. Overwrite?, escribe No.

    8. Cambia al directorio public:

      cd public
    9. Desde el directorio public, ejecuta el siguiente comando para implementar tu proyecto en tu sitio:

      firebase deploy --only hosting

    Probar la aplicación web de forma local

    Firebase Hosting te permite ver y probar los cambios de forma local e interactuar con los recursos del proyecto de backend emulado. Cuando usas firebase serve, tu aplicación interactúa con un backend emulado para tu contenido y configuración de hosting, pero con tu backend real para todos los demás recursos del proyecto. En este tutorial, puedes usar firebase serve, pero no se recomienda si vas a hacer pruebas más exhaustivas.

    1. En el directorio public, ejecuta el siguiente comando:

      firebase serve
    2. Abre tu aplicación web en la URL local devuelta (normalmente, http://localhost:5000).

    3. Escribe un texto en inglés y haz clic en Traducir.

      Debería aparecer una traducción del texto al francés.

    4. Ahora puede hacer clic en Validar o en Rechazar.

      En la base de datos de Firestore, puedes verificar el contenido. Debería ser similar a lo siguiente:

      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."
      

      El estado approved es true o false en función de si valida o rechaza la traducción.

    ¡Enhorabuena! Has creado un flujo de trabajo de traducción con intervención humana que usa retrollamadas de Workflows.

    Limpieza

    Si has creado un proyecto para este tutorial, elimínalo. Si has usado un proyecto y quieres conservarlo sin los cambios que se han añadido en este tutorial, elimina los recursos creados para el tutorial.

    Eliminar el proyecto

    La forma más fácil de evitar que te cobren es eliminar el proyecto que has creado para el tutorial.

    Para ello, sigue las instrucciones que aparecen a continuación:

    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.

    Eliminar recursos del tutorial

    1. Elimina la configuración predeterminada de la CLI de gcloud que has añadido durante la configuración del tutorial:

      gcloud config unset workflows/location
    2. Elimina el flujo de trabajo que has creado en este tutorial:

      gcloud workflows delete WORKFLOW_NAME
    3. Elimina las funciones de Cloud Run que has creado en este tutorial:

      gcloud functions delete FUNCTION_NAME

      También puedes eliminar funciones de Cloud Run desde la Google Cloud consola.

    Siguientes pasos