Criar acionadores e etapas de ação personalizados do Workspace Studio

1. Introdução

Neste codelab, você vai criar um Portal de suporte rápido interativo que se conecta diretamente ao Google Workspace Studio. Você vai aprender a criar uma etapa inicial personalizada e uma etapa de ação personalizada que podem ser testadas instantaneamente sem depender de nenhuma infraestrutura de terceiros.

Vamos usar o Google Apps Script para criar:

  • Um app da Web com um formulário elegante para os usuários enviarem tíquetes de suporte manualmente.
  • Um acionador personalizado que captura esses envios do web app e aciona um fluxo do Workspace Studio.
  • Uma etapa personalizada que analisa a descrição do tíquete recebido para identificar níveis de urgência ("Alta" ou "Normal").

O que você vai aprender

  • Como configurar inicializadores e etapas no manifesto appsscript.json.
  • Como criar o notifyUri durante o ciclo de vida do registro do starter.
  • Como acionar programaticamente um fluxo do Workspace Studio de um código externo usando UrlFetchApp.
  • Como construir variáveis de saída nas etapas personalizadas para transmitir dados downstream.

Pré-requisitos

  • Uma conta do Google Workspace com o Google Workspace Studio ativado.
  • A configuração Permitir etapas personalizadas não publicadas (teste) ativada no Admin Console do seu domínio (em Apps > Google Workspace > Workspace Studio > Configurações de etapas personalizadas).
  • Familiaridade com o Google Apps Script.

2. Configurar o projeto do Apps Script

Primeiro, vamos criar um projeto do Apps Script para hospedar nosso código:

  1. Acesse script.google.com e clique em Novo projeto.
  2. Nomeie o projeto como Support Quick Portal.
  3. Clique em Configurações do projeto Configurações do projeto na barra lateral esquerda.
  4. Marque a caixa Mostrar arquivo de manifesto appsscript.json no editor.
  5. Volte para a visualização Editor Editor.

Por padrão, os projetos do Apps Script são associados a um projeto padrão oculto do Google Cloud. Para chamar a API Google Workspace Studio e evitar erros PERMISSION_DENIED, mude o script para um projeto padrão do Cloud, ative a API e configure corretamente a plataforma Google Auth.

  1. Selecionar ou criar um projeto:acesse o console do Google Cloud e use o menu suspenso de projetos na parte de cima da página para escolher um projeto ou criar um novo.
  2. Ative a API:depois que o projeto escolhido estiver ativo na barra superior, abra o fluxo de ativação da API e clique em Próxima e Ativar para a API Workspace Studio.
  3. Configurar o branding do OAuth:abra a página de branding da Google Auth Platform.
    1. Se for solicitado, clique em Começar. Se já estiver configurado, pule para a etapa 4.
    2. Em Informações do app, insira um nome e um e-mail de suporte ao usuário. Clique em Próximo.
    3. Em Público-alvo, selecione Interno (ou Externo se "Interno" não estiver disponível) e clique em Próxima.
    4. Forneça as informações de contato, concorde com a Política de Dados e clique em Criar.
  4. Configurar acesso aos dados:abra a página de acesso aos dados e clique em Adicionar ou remover escopos.
    1. Em Adicionar escopos manualmente, cole https://www.googleapis.com/auth/workspace.studio.trigger.
    2. Clique em Adicionar à tabela, depois em Atualizar e, por fim, em Salvar na parte de baixo da página.
  5. Acesse IAM e administrador > Configurações (ou o painel do Console do Cloud) e copie o Número do projeto.
  6. Volte ao projeto do Apps Script e clique em Configurações do projeto Configurações do projeto na barra lateral esquerda.
  7. Na seção Projeto do Google Cloud Platform (GCP), clique em Mudar projeto.
  8. Insira o Número do projeto do GCP que você copiou e clique em Definir projeto.

Configurar o manifesto

Abra o arquivo appsscript.json que acabou de aparecer. Substitua o conteúdo pelo código abaixo. Esse manifesto lista explicitamente os escopos OAuth necessários e define nossa ativação como workflowTrigger e ação como workflowAction no bloco 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. Criar a interface do web app

Para oferecer uma maneira visual e testável de acionar nosso iniciador sem usar ferramentas de terminal ou webhooks de terceiros, vamos criar um app da Web do Apps Script.

No editor de script do Apps Script, ao lado de Arquivos, clique em Adicionar um arquivo Adicionar um arquivo e selecione HTML. Nomeie como index.html. A extensão .html será adicionada automaticamente.

Cole o seguinte código simplificado da interface:

<!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. Criar os scripts de back-end

Agora vamos juntar tudo. Para manter o código legível, vamos separar a lógica do app da Web, do Starter e da etapa de ação em três arquivos de script diferentes.

WebApp.gs

Renomeie o arquivo Code.gs padrão no editor para WebApp.gs, exclua o myFunction boilerplate e copie e cole o bloco abaixo nele. Esse arquivo processa a hospedagem do app da 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

Em seguida, passe o cursor sobre Arquivos, clique em Adicionar um arquivo Adicionar um arquivo, selecione Script e nomeie como Starter.gs. Copie e cole o bloco abaixo nele. Esse arquivo processa a configuração do Starter e o gerenciamento do ciclo de vida do 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

Em seguida, passe o cursor sobre Arquivos, clique em Adicionar um arquivo Adicionar um arquivo, selecione Script e nomeie como Action.gs. Copie e cole a lógica da etapa de urgência abaixo:

/** --- 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. Implantar e testar

Com o código escrito, vamos implantar e configurar o projeto.

Etapa 1: reautorizar o script

Como você vinculou um novo projeto padrão do Google Cloud, é necessário autorizar explicitamente os escopos do script (como script.external_request usado por UrlFetchApp) no novo projeto.

  1. No editor de script do Apps Script, selecione WebApp.gs em Arquivos e escolha a função doGet no menu suspenso da barra de ferramentas na parte de cima.
  2. Clique em Executar.
  3. Uma solicitação de "Autorização necessária" vai aparecer. Clique em Revisar permissões.
  4. Escolha sua conta e autorize o script. Se aparecer um aviso de "App não verificado", clique em Avançado -> Acessar o Portal rápido de suporte para ignorar.
  5. Aguarde até que o Registro de execução mostre Execução concluída, o que confirma que o script está totalmente autorizado.

Etapa 2: testar a extensão e o app da Web

Como seu appsscript.json define um complemento e um web app, é possível configurar os dois simultaneamente.

  1. Clique em Implantar > Testar implantações no canto superior direito do Apps Script.
  2. Ao lado de Selecionar tipo, clique em Ativar tipos de implantação Habilitar tipos de implantação, verifique se a opção Complementos do Google Workspace está marcada e clique em Instalar.
  3. Clique em Ativar tipos de implantação Ativar tipos de implantação novamente, verifique se a opção App da Web está marcada e copie o URL (ele vai terminar em /dev). Esse é o URL do app da Web Portal rápido de suporte que você criou.
  4. Clique em Concluído. Talvez seja necessário atualizar o navegador antes que o Workspace Studio registre o complemento.

Etapa 3: configurar o fluxo do Studio

  1. Abra o Google Workspace Studio em studio.workspace.google.com.
  2. Clique em Adicionar ferramenta Adicionar ferramenta à esquerda e selecione Fluxo.
  3. Para configurar o acionador, clique em Escolher um acionador e selecione a Extensão de suporte instalada > Novo tíquete de suporte. Se a mensagem "Permissão necessária" aparecer, clique em Conceder permissão para permitir que o complemento acesse seus dados.
  4. Em seguida, em "Ações", clique em Escolher uma etapa, role a tela até "Extensão de suporte" e selecione a etapa Detectar urgência.
  5. Em Descrição da origem, clique em Adicionar Variáveis e selecione Etapa 1: novo tíquete de suporte > A descrição completa do problema (ticketDescription).
  6. Clique em Escolher uma etapa novamente e selecione uma etapa integrada, como Gmail > Enviar um e-mail ou Chat > Me avise no Chat.
    • Se você escolheu o Gmail:
      • Para:digite seu e-mail.
      • Assunto:a prioridade do tíquete ticketTitle é urgencyLevel
      • Mensagem: ticketDescription
    • Se você escolheu "Chat":
      • Mensagem:a prioridade do tíquete ticketTitle é urgencyLevel. ticketDescription
  7. Ative o fluxo de trabalho. Se for solicitado a Renomear seu fluxo, insira um nome como Support Ticket Router e salve. Isso aciona o ciclo de vida onManageTrigger, salvando o notifyUri.

Etapa 4: executar a simulação

  1. Abra o URL do aplicativo Web que você copiou na etapa 2 desta página em uma nova guia do navegador (ele termina em /dev).
  2. Preencha o formulário. Inclua uma palavra-chave de acionamento (como "urgente" ou "falha") na descrição.
  3. Clique em Enviar. Aguarde a mensagem "O tíquete foi encaminhado".
  4. Confira o registro de atividades do Workspace Studio, a caixa de entrada do Gmail ou o Google Chat (dependendo da ação escolhida). Você vai ver uma mensagem ou um e-mail marcado como "Alta" urgência integrado de forma nativa usando seus elementos personalizados do Apps Script.

6. Limpeza

Para evitar poluir seu espaço de trabalho e sua conta do Google Cloud, limpe os recursos criados durante este codelab.

  1. Excluir o fluxo do Workspace Studio:
    • Acesse studio.workspace.google.com.
    • Encontre seu fluxo de Support Ticket Router.
    • Clique no menu de três pontos ao lado dele e selecione Excluir.
  2. Exclua o projeto do Apps Script:
    • Acesse script.google.com.
    • Encontre o projeto Support Quick Portal.
    • Clique no menu de três pontos e selecione Remover.
  3. Desligue o projeto do Google Cloud:
    • Navegue até o Console do Google Cloud.
    • Verifique se o novo projeto está selecionado no menu suspenso na parte de cima.
    • Acesse IAM e administrador > Configurações e clique em Encerrar.

7. Conclusão

Você criou um ecossistema completo para extensões personalizadas do Google Workspace Studio sem depender de assinaturas externas de terceiros.

O que abordamos:

  • Usar workflowTrigger com onManageFunction para capturar e processar corretamente os eventos de ciclo de vida do Google.
  • Autenticação de solicitações de back-end usando ScriptApp.getOAuthToken() nos endpoints da API workspacestudio.googleapis.com.
  • Usar workflowAction para processar variáveis de entrada de forma síncrona e gerar saídas downstream.

Agora você pode escalonar horizontalmente esses princípios para se conectar a plataformas SaaS reais, como Jira, Salesforce ou Zendesk, modificando o local de onde o app da Web (ou webhook de entrada) é acionado.