Crea activadores y pasos de acción personalizados de Workspace Studio

1. Introducción

En este codelab, compilarás un Portal de asistencia rápida interactivo que se conectará directamente con Google Workspace Studio. Aprenderás a crear un paso de inicio personalizado y un paso de acción personalizado que se pueden probar de inmediato sin depender de ninguna infraestructura de terceros.

Usaremos Google Apps Script para crear lo siguiente:

  • Una app web con un formulario elegante para que los usuarios envíen manualmente tickets de asistencia.
  • Un activador personalizado que captura esos envíos de la app web y activa un flujo de Workspace Studio.
  • Un paso personalizado que analiza la descripción del ticket entrante para identificar los niveles de urgencia ("Alto" o "Normal").

Qué aprenderás

  • Cómo configurar los iniciadores y los pasos en tu manifiesto de appsscript.json
  • Cómo construir el notifyUri durante el ciclo de vida del registro del starter
  • Cómo activar un flujo de Workspace Studio de forma programática desde código externo con UrlFetchApp
  • Cómo construir variables de salida en tus pasos personalizados para pasar datos de forma descendente

Requisitos previos

  • Una cuenta de Google Workspace con Google Workspace Studio habilitado
  • El parámetro de configuración Permitir pasos personalizados no publicados (de prueba) habilitado en la Consola del administrador de tu dominio (en Apps > Google Workspace > Workspace Studio > Configuración de pasos personalizados).
  • Conocimiento de Google Apps Script

2. Configura el proyecto de Apps Script

Primero, crearemos un nuevo proyecto de Apps Script para alojar nuestro código:

  1. Navega a script.google.com y haz clic en Nuevo proyecto.
  2. Asigna el nombre Support Quick Portal al proyecto.
  3. Haz clic en Configuración del proyecto Configuración del proyecto en la barra lateral izquierda.
  4. Marca la casilla para Mostrar el archivo de manifiesto appsscript.json en el editor.
  5. Regresa a la vista del Editor Editor.

De forma predeterminada, los proyectos de Apps Script se asocian con un proyecto de Google Cloud predeterminado y oculto. Para llamar a la API de Google Workspace Studio y evitar errores de PERMISSION_DENIED, debes cambiar tu secuencia de comandos a un proyecto estándar de Cloud, habilitar la API y configurar correctamente la plataforma de Google Auth.

  1. Selecciona o crea un proyecto: Ve a la consola de Google Cloud y usa el menú desplegable del proyecto que se encuentra en la parte superior de la página para elegir un proyecto existente o crear uno nuevo.
  2. Habilita la API: Una vez que el proyecto que elegiste esté activo en la barra superior, abre el flujo de habilitación de la API y haz clic en Siguiente y Habilitar para la API de Workspace Studio.
  3. Configura la información de la marca de OAuth: Abre la página de información de la marca de Google Auth Platform.
    1. Si se te solicita, haz clic en Comenzar. (Si ya está configurado, ve al paso 4).
    2. En Información de la app, ingresa un nombre de la app y un correo electrónico de asistencia al usuario. Haz clic en Siguiente.
    3. En Público, selecciona Interno (o Externo si Interno no está disponible) y haz clic en Siguiente.
    4. Proporciona la información de contacto, acepta la Política de Datos y haz clic en Crear.
  4. Configura el acceso a los datos: Abre la página Acceso a los datos y haz clic en Agregar o quitar permisos.
    1. En Agregar permisos manualmente, pega https://www.googleapis.com/auth/workspace.studio.trigger.
    2. Haz clic en Agregar a la tabla, luego en Actualizar y, por último, en Guardar en la parte inferior de la página.
  5. Ve a IAM y administración > Configuración (o al panel de la consola de Cloud) y copia el Número de proyecto.
  6. Regresa a tu proyecto de Apps Script y haz clic en Configuración del proyecto Configuración del proyecto en la barra lateral izquierda.
  7. En la sección Proyecto de Google Cloud Platform (GCP), haz clic en Cambiar proyecto.
  8. Ingresa el número de proyecto de GCP que copiaste y haz clic en Establecer el proyecto.

Configura el manifiesto

Abre el archivo appsscript.json que ahora se ve. Reemplaza su contenido con el siguiente código. En este manifiesto, se enumeran de forma explícita nuestros permisos de OAuth obligatorios y se definen nuestros iniciadores como workflowTrigger y nuestras acciones como workflowAction en el bloque studio.flows.workflowElements.

{
  "timeZone": "America/Los_Angeles",
  "exceptionLogging": "STACKDRIVER",
  "runtimeVersion": "V8",
  "webapp": {
     "executeAs": "USER_ACCESSING",
     "access": "MYSELF"
  },
  "oauthScopes": [
    "https://www.googleapis.com/auth/script.external_request",
    "https://www.googleapis.com/auth/workspace.studio.trigger",
    "https://www.googleapis.com/auth/script.storage",
    "https://www.googleapis.com/auth/script.locale"
  ],
  "urlFetchWhitelist": [
    "https://workspacestudio.googleapis.com/"
  ],
  "addOns": {
    "common": {
      "name": "Support Extension",
      "logoUrl": "https://www.gstatic.com/images/branding/productlogos/buganizer/v1/192px.svg",
      "useLocaleFromApp": true
    },
    "studio": {
      "flows": {
        "workflowElements": [
          {
            "id": "supportTicketTrigger",
            "state": "ACTIVE",
            "name": "New Support Ticket",
            "description": "Fires when someone submits a ticket via the Web App.",
            "workflowTrigger": {
              "inputs": [],
              "outputs": [
                {
                  "id": "ticketTitle",
                  "description": "The title of the ticket",
                  "cardinality": "SINGLE",
                  "dataType": { "basicType": "STRING" }
                },
                {
                  "id": "ticketDescription",
                  "description": "The full description of the issue",
                  "cardinality": "SINGLE",
                  "dataType": { "basicType": "STRING" }
                }
              ],
              "onConfigFunction": "onConfigTrigger",
              "onManageFunction": "onManageTrigger"
            }
          },
          {
            "id": "urgencyDetectorStep",
            "state": "ACTIVE",
            "name": "Detect Urgency",
            "description": "Scans the ticket description to detect High or Normal urgency.",
            "workflowAction": {
              "inputs": [
                {
                  "id": "ticketDescription",
                  "description": "The ticket description",
                  "cardinality": "SINGLE",
                  "dataType": { "basicType": "STRING" }
                }
              ],
              "outputs": [
                {
                  "id": "urgencyLevel",
                  "description": "The urgency High/Normal",
                  "cardinality": "SINGLE",
                  "dataType": { "basicType": "STRING" }
                }
              ],
              "onConfigFunction": "onConfigUrgency",
              "onExecuteFunction": "onExecuteUrgency"
            }
          }
        ]
      }
    }
  }
}

3. Compila la IU de la app web

Para proporcionar una forma visual y verificable de activar nuestro iniciador sin usar herramientas de terminal ni webhooks de terceros, compilaremos una app web de Apps Script.

En el editor de secuencias de comandos de Apps Script, junto a Archivos, haz clic en Agregar un archivo Agregar un archivo y selecciona HTML. Asigna el nombre index.html (la extensión .html se agrega automáticamente).

Pega el siguiente código de IU simplificado:

<!DOCTYPE html>
<html>
  <head>
    <base target="_top">
    <style>
      body { font-family: Arial, sans-serif; padding: 20px; max-width: 500px; margin: auto; }
      label { font-weight: bold; display: block; margin-top: 15px; }
      input, textarea { width: 100%; margin-top: 5px; padding: 10px; box-sizing: border-box; }
      button { background-color: #1a73e8; color: white; border: none; padding: 10px 15px; margin-top: 15px; font-weight: bold; cursor: pointer; border-radius: 4px; }
      button:hover { background-color: #1557b0; }
      #status { margin-top: 15px; color: green; font-weight: bold; }
    </style>
  </head>
  <body>
    <h2>Support Quick Portal</h2>
    <p>Submit your issue below to trigger the Workspace Studio flow.</p>

    <label for="title">Ticket Title</label>
    <input type="text" id="title" placeholder="e.g. Broken monitor">

    <label for="description">Issue Description</label>
    <textarea id="description" rows="4" placeholder="Explain your issue (use words like 'urgent' to test the step!)..."></textarea>

    <button onclick="submitTicket()">Submit Ticket</button>
    <div id="status"></div>

    <script>
      function submitTicket() {
        const title = document.getElementById('title').value;
        const description = document.getElementById('description').value;
        const statusEl = document.getElementById('status');

        if (!title || !description) {
           statusEl.style.color = "red";
           statusEl.innerText = "Please fill out both fields.";
           return;
        }

        statusEl.style.color = "green";
        statusEl.innerText = "Submitting ticket...";

        google.script.run
          .withSuccessHandler(function(response) {
            statusEl.innerText = response;
          })
          .withFailureHandler(function(error) {
            statusEl.style.color = 'red';
            statusEl.innerText = 'Error: ' + error.message;
          })
          .submitTicketToStudio(title, description);
      }
    </script>
  </body>
</html>

4. Compila las secuencias de comandos de backend

Ahora, conectemos todo. Para que el código sea legible, separaremos la app web, la lógica de Starter y la lógica de Action Step en tres archivos de secuencia de comandos diferentes.

WebApp.gs

Cambia el nombre del archivo Code.gs predeterminado en el editor a WebApp.gs, borra el archivo myFunction estándar y copia y pega el siguiente bloque en él. Este archivo controla el hosting de la app web:

/** --- WEB APP HOSTING --- **/

/**
 * Serves the HTML UI when users visit the web app URL.
 */
function doGet() {
  return HtmlService.createHtmlOutputFromFile('index')
      .setTitle('Support Quick Portal')
      .setXFrameOptionsMode(HtmlService.XFrameOptionsMode.ALLOWALL);
}

/**
 * Called by the Web App form. It calls the Workspace Studio API.
 */
function submitTicketToStudio(title, description) {
  const props = PropertiesService.getUserProperties();
  const notifyUri = props.getProperty('notifyUri');
  const triggerId = props.getProperty('triggerId');

  if (!notifyUri || !triggerId) {
    throw new Error('Trigger URL missing! Make sure the flow is enabled in Workspace Studio.');
  }

  // Use ScriptApp.getOAuthToken() (requires the workspace.studio.trigger scope in manifest)
  const token = ScriptApp.getOAuthToken();
  const requestId = Utilities.getUuid();

  // Package our outputs mapped in appsscript.json
  const payload = {
    "name": "triggers/" + triggerId,
    "outputs": {
      "ticketTitle": { "stringValues": [title] },
      "ticketDescription": { "stringValues": [description] }
    },
    "requestId": requestId
  };

  const options = {
    "method": "POST",
    "contentType": "application/json",
    "headers": { "Authorization": "Bearer " + token },
    "payload": JSON.stringify(payload),
    "muteHttpExceptions": true
  };

  // Fire!
  const response = UrlFetchApp.fetch(notifyUri, options);
  if (response.getResponseCode() !== 200) {
    console.error(response.getContentText());
    throw new Error('Workspace Studio API Error: ' + response.getResponseCode());
  }
  return 'Ticket successfully routed to Workspace Studio!';
}

Starter.gs

Luego, coloca el cursor sobre Archivos, haz clic en Agregar un archivo Agregar un archivo, selecciona Secuencia de comandos y asígnale el nombre Starter.gs. Copia y pega el siguiente bloque. Este archivo controla la configuración de Starter y la administración del ciclo de vida de Workspace Studio:

/** --- STARTER LOGIC --- **/

/**
 * Workspace Studio calls this to display the config UI when the user adds the starter.
 * We just return a static message since there are no properties to configure.
 */
function onConfigTrigger() {
  const section = CardService.newCardSection()
      .setHeader("Configure Web App Starter")
      .addWidget(CardService.newTextParagraph().setText("Ready to go! Once you enable this flow, deploy your Apps Script project as a Web App to submit tickets."));

  return CardService.newCardBuilder().addSection(section).build();
}

/**
 * Workspace Studio posts data here to manage the lifecycle (enable/disable the trigger).
 * Critically, we must construct or save the `notifyUri` and `triggerId` to call it later.
 */
function onManageTrigger(event) {
  const triggerCreation = event.workflow.triggerCreation;
  const triggerDeletion = event.workflow.triggerDeletion;

  const props = PropertiesService.getUserProperties();

  if (triggerCreation) {
    // Save the unique URL and ID when the flow is enabled
    // Note: notifyUri might be omitted, so fallback to constructing it dynamically
    const notifyUri = triggerCreation.notifyUri || `https://workspacestudio.googleapis.com/v1/triggers/${triggerCreation.triggerId}:fire`;
    props.setProperty('notifyUri', notifyUri);
    props.setProperty('triggerId', triggerCreation.triggerId);
  } else if (triggerDeletion) {
    // Cleanup if the user deletes the flow
    props.deleteProperty('notifyUri');
    props.deleteProperty('triggerId');
  }
}

Action.gs

Luego, coloca el cursor sobre Archivos, haz clic en Agregar un archivo Agregar un archivo, selecciona Secuencia de comandos y asígnale el nombre Action.gs. Copia y pega la siguiente lógica del paso de urgencia:

/** --- ACTION STEP LOGIC --- **/

/**
 * Workspace Studio configuration card for the Step.
 * We prompt the user to bind a variable to our "ticketDescription" input.
 */
function onConfigUrgency() {
  const input = CardService.newTextInput()
      .setFieldName("ticketDescription")
      .setTitle("Source Description")
      .setHint('Select the description variable outputted by the starter')
      .setHostAppDataSource(
        CardService.newHostAppDataSource().setWorkflowDataSource(CardService.newWorkflowDataSource().setIncludeVariables(true))
      );

  const section = CardService.newCardSection()
      .setHeader("Urgency Detector Step")
      .addWidget(input);

  return CardService.newCardBuilder().addSection(section).build();
}

/**
 * The synchronous logic executed during the Step.
 */
function onExecuteUrgency(event) {
  // Read mapped inputs
  let description = event.workflow.actionInvocation.inputs["ticketDescription"].stringValues[0];
  if (!description) description = "";

  const urgentKeywords = ['urgent', 'crash', 'broken', 'down', 'fire'];
  let urgencyLevel = 'Normal';

  if (urgentKeywords.some(keyword => description.toLowerCase().includes(keyword))) {
    urgencyLevel = 'High';
  }

  // Package outputs correctly
  const variableDataMap = {
    "urgencyLevel": AddOnsResponseService.newVariableData().addStringValue(urgencyLevel)
  };

  return outputVariables(variableDataMap);
}

/**
 * Helper strictly enforcing the expected Step object return type.
 */
function outputVariables(variableDataMap) {
  const workflowAction = AddOnsResponseService.newReturnOutputVariablesAction()
      .setVariableDataMap(variableDataMap);
  const hostAppAction = AddOnsResponseService.newHostAppAction()
      .setWorkflowAction(workflowAction);
  return AddOnsResponseService.newRenderActionBuilder()
      .setHostAppAction(hostAppAction)
      .build();
}

5. Implementación y prueba

Con nuestro código escrito, implementemos el proyecto y configuremoslo.

Paso 1: Vuelve a autorizar la secuencia de comandos

Como vinculaste un nuevo proyecto estándar de Google Cloud, debes autorizar de forma explícita los permisos de la secuencia de comandos (como script.external_request que usa UrlFetchApp) en el nuevo proyecto.

  1. En el editor de Apps Script, selecciona WebApp.gs en Archivos y, luego, la función doGet en el menú desplegable de la barra de herramientas superior.
  2. Haz clic en Ejecutar.
  3. Aparecerá un mensaje de "Se requiere autorización". Haz clic en Revisar permisos.
  4. Elige tu cuenta y autoriza la secuencia de comandos. Si ves una advertencia de "App no verificada", haz clic en Avanzado -> Ir al portal de asistencia rápida para omitirla.
  5. Espera a que el Registro de ejecución muestre Ejecución completada, lo que confirma que la secuencia de comandos ahora está completamente autorizada.

Paso 2: Prueba la extensión y la app web

Dado que tu appsscript.json define un complemento y una app web, puedes configurar ambos de forma simultánea.

  1. Haz clic en Implementar > Implementaciones de prueba en la esquina superior derecha de Apps Script.
  2. Junto a Seleccionar tipo, haz clic en Habilita los tipos de implementación Habilitar los tipos de implementación y asegúrate de que Complementos de Google Workspace esté marcado. Luego, haz clic en Instalar.
  3. Vuelve a hacer clic en Habilita los tipos de implementación Habilitar tipos de implementación, asegúrate de que esté marcada la opción App web y copia la URL (terminará en /dev). Esta es la URL de la app web del Portal rápido de asistencia que creaste.
  4. Haz clic en Listo. Es posible que debas actualizar el navegador antes de que Workspace Studio registre el complemento.

Paso 3: Configura el flujo de Studio

  1. Abre Google Workspace Studio en studio.workspace.google.com.
  2. Haz clic en Agregar herramienta Agregar herramienta a la izquierda y selecciona Flujo.
  3. Para configurar el activador, haz clic en Elegir un activador y, luego, selecciona la extensión de asistencia instalada > Nuevo ticket de asistencia. Si aparece el mensaje "Se requiere permiso", haz clic en Otorgar permiso para permitir que el complemento acceda a tus datos.
  4. A continuación, en Actions, haz clic en Choose a step, desplázate hasta "Support Extension" y selecciona el paso Detect Urgency.
  5. En Source Description, haz clic en Agregar Variables y selecciona Step 1: New Support Ticket > The full description of the issue (ticketDescription).
  6. Vuelve a hacer clic en Elegir un paso y selecciona un paso integrado, como Gmail > Enviar un correo electrónico o Chat > Notificarme en Chat.
    • Si elegiste Gmail, haz lo siguiente:
      • Para: Ingresa tu dirección de correo electrónico.
      • Asunto: La prioridad del ticket ticketTitle es urgencyLevel
      • Mensaje: ticketDescription
    • Si elegiste Chat:
      • Mensaje: La prioridad del ticket ticketTitle es urgencyLevel. ticketDescription
  7. Activa el flujo de trabajo. Si se te solicita que cambies el nombre del flujo, ingresa un nombre como Support Ticket Router y guárdalo. Esto activa el ciclo de vida de onManageTrigger y guarda el notifyUri.

Paso 4: Ejecuta la simulación

  1. Abre la URL de la app web que copiaste antes en el paso 2 de esta página en una nueva pestaña del navegador (termina en /dev).
  2. Completa el formulario. Asegúrate de incluir una palabra clave de activación (como "urgente" o "falla") en la descripción.
  3. Haz clic en Enviar. Espera el mensaje "Ticket successfully routed".
  4. Revisa el registro de actividad de Workspace Studio, la carpeta Recibidos de Gmail o Google Chat (según la acción que hayas elegido). Deberías ver un mensaje o un correo electrónico etiquetado como de urgencia "Alta" integrado de forma nativa con tus elementos personalizados de Apps Script.

6. Limpieza

Para evitar que se acumulen elementos innecesarios en tu espacio de trabajo y en tu cuenta de Google Cloud, puedes limpiar los recursos que creaste durante este codelab.

  1. Borra el flujo de Workspace Studio:
    • Abre studio.workspace.google.com.
    • Encuentra tu flujo de Support Ticket Router.
    • Haz clic en el menú de tres puntos junto a él y selecciona Borrar.
  2. Borra el proyecto de Apps Script:
    • Ve a script.google.com.
    • Busca el proyecto Support Quick Portal.
    • Haz clic en el menú de tres puntos y selecciona Quitar.
  3. Apaga el proyecto de Google Cloud:
    • Navega a la consola de Google Cloud.
    • Asegúrate de que tu proyecto nuevo esté seleccionado en el menú desplegable superior.
    • Ve a IAM y administración > Configuración y haz clic en Cerrar.

7. Conclusión

Creaste con éxito un ecosistema completo para las extensiones personalizadas de Google Workspace Studio sin depender de suscripciones externas de terceros.

Temas que abordamos:

  • Usar workflowTrigger junto con onManageFunction para capturar y controlar correctamente los eventos de ciclo de vida de Google
  • Autentica las solicitudes de backend con ScriptApp.getOAuthToken() en los extremos de la API de workspacestudio.googleapis.com.
  • Usa workflowAction para procesar variables de entrada de forma síncrona y generar resultados posteriores.

Ahora puedes escalar horizontalmente estos principios para conectarte a plataformas de SaaS reales, como Jira, Salesforce o Zendesk, modificando el lugar desde el que se activa tu app web (o webhook entrante).