Integrando Google Forms e App Integration: Um Guia Detalhado

Integrando Google Forms com Google Cloud Pub/Sub e App Integration via Apps Script: Um Guia Detalhado

This article is also available in English here.

Nesse artigo, vou falar muito pouco sobre Application Integration! Basta saber que o objetivo aqui é disparar uma integração no App Integration quando um Google Form é submetido (pegando, claro, o conteúdo do Forms como input para a integração). A ponte aqui será feita via Pub/Sub. Ou seja, o objetivo é entregar o conteúdo do Form submetido num Pub/Sub. Isso feito, basta criar uma integração que usa o PubSub Trigger

Vamos lá!

O Google Forms é uma ferramenta fantástica para coletar dados, mas e se você precisar processar essas respostas em tempo real ou integrá-las com sistemas backend mais complexos? Uma solução poderosa é enviar os dados de cada resposta para um tópico do Google Cloud Pub/Sub assim que o formulário for enviado. Isso abre portas para processamento de dados em tempo real, análise, acionando o App Integration e muito mais.

Neste post, vamos detalhar como configurar um script no Google Apps Script para publicar automaticamente os dados de um Google Form em um tópico Pub/Sub, com foco especial nos detalhes de permissão e configuração do ambiente.

Pré-requisitos:

  1. Conta Google: Necessária para criar o Google Form e o projeto no Google Cloud.
  2. Projeto Google Cloud Platform (GCP): Um projeto ativo onde você criará o tópico Pub/Sub e habilitará as APIs necessárias.
  3. Google Form: O formulário que você deseja integrar.

Passo 1: Configuração do Projeto Google Cloud (GCP)

Antes de mexer no script, precisamos preparar o ambiente no GCP.

  1. Selecione ou Crie um Projeto GCP: Acesse o Google Cloud Console e selecione o projeto que deseja usar ou crie um novo. Anote o ID do Projeto (Project ID), pois você precisará dele no script.
  2. Habilite a API Pub/Sub, Google Forms e AppsScript:
    • No menu de navegação, vá para “APIs & Services” → “Library”.
    • Procure por “Cloud Pub/Sub API” e clique em “Enable”. Se já estiver habilitada, ótimo. Faça o mesmo para Google Forms e AppsScript.
  3. Crie um Tópico Pub/Sub:
    • No menu de navegação, vá para “Pub/Sub” → “Topics”.
    • Clique em “Create Topic”.
    • Dê um nome ao seu tópico (por exemplo, form-submission). Anote esse Nome do Tópico (Topic ID).
    • Mantenha as outras configurações padrão por enquanto e clique em “Create”.

Passo 2: Configuração do Google Apps Script

Agora, vamos conectar nosso Google Form ao script e ao nosso projeto GCP.

  1. Abra o Editor de Scripts: Abra seu Google Form, clique nos três pontos verticais (Mais) no canto superior direito e selecione “Script editor”.
  2. Associe o Script ao Projeto GCP: Este é um passo crucial para que o script possa usar os serviços do GCP e obter o token de autenticação correto.
    • No editor de scripts, clique no ícone de engrenagem (“Project Settings”) na barra lateral esquerda.
    • Marque a caixa “Show “appsscript.json” manifest file in editor”.
    • Role para baixo até a seção “Google Cloud Platform (GCP) Project”.
    • Clique em “Change Project”.
    • Cole o Número do Projeto GCP (GCP Project Number - você pode encontrá-lo na página inicial do seu projeto no Cloud Console, geralmente abaixo do ID do Projeto) e clique em “Set Project”.
    • Confirme a alteração. Isso garante que o ScriptApp.getOAuthToken() solicitará um token com permissão para interagir com o seu projeto GCP configurado.

Passo 3: O Código Apps Script

Copie e cole o seguinte código no editor de scripts (geralmente no arquivo Code.gs).

JavaScript

/**
 * Configuração: Substitua pelos seus valores!
 */
const GcpProjectId = 'SEU_GCP_PROJECT_ID'; // <-- Substitua pelo ID do seu projeto GCP
const PubSubTopicName = 'SEU_PUBSUB_TOPIC_ID'; // <-- Substitua pelo ID do seu tópico Pub/Sub

/**
 * Esta função é acionada quando um formulário é enviado.
 * @param {GoogleAppsScript.Events.FormsOnSubmit} e O objeto de evento da submissão do formulário.
 */
function onFormSubmit(e) {
  try {
    // Obtém a resposta do formulário do objeto de evento
    const formResponse = e.response;
    if (!formResponse) {
      Logger.log('Objeto de evento não contém uma resposta de formulário.');
      return;
    }

    // Obtém todas as respostas aos itens da submissão
    const itemResponses = formResponse.getItemResponses();

    // Cria um objeto de dados para armazenar os valores do formulário
    const formData = {
      timestamp: formResponse.getTimestamp().toISOString(), // Data/hora da submissão em formato ISO
      respondentEmail: formResponse.getRespondentEmail() || 'anonymous', // Email do respondente (se coletado e permitido)
      responses: {} // Objeto para mapear perguntas e respostas
    };

    // Mapeia as perguntas do formulário para as respostas
    itemResponses.forEach(itemResponse => {
      const question = itemResponse.getItem().getTitle(); // Obtém o texto da pergunta
      const answer = itemResponse.getResponse(); // Obtém a resposta
      formData.responses[question] = answer; // Adiciona ao objeto de respostas
    });

    // Envia para o Pub/Sub usando chamada HTTP direta com token OAuth
    const result = publishToPubSub(formData);
    Logger.log('Mensagem publicada no Pub/Sub: ' + result);

  } catch (error) {
    // Registra qualquer erro que ocorra durante o processo
    Logger.log('Erro em onFormSubmit: ' + error.toString() + '\nStack: ' + error.stack);
  }
}

/**
 * Publica uma mensagem no Pub/Sub usando o token OAuth do Apps Script.
 * @param {Object} data O objeto de dados a ser publicado.
 * @return {string} O corpo da resposta da API Pub/Sub em caso de sucesso.
 * @throws {Error} Se a publicação falhar.
 */
function publishToPubSub(data) {
  // Validação básica das constantes
  if (!GcpProjectId || GcpProjectId === 'SEU_GCP_PROJECT_ID') {
      throw new Error('GcpProjectId não está configurado. Edite o script e substitua o placeholder.');
  }
  if (!PubSubTopicName || PubSubTopicName === 'SEU_PUBSUB_TOPIC_ID') {
      throw new Error('PubSubTopicName não está configurado. Edite o script e substitua o placeholder.');
  }

  // Prepara a mensagem para a API Pub/Sub
  const pubsubData = {
    messages: [
      {
        // Os dados precisam ser codificados em Base64
        data: Utilities.base64Encode(JSON.stringify(data))
      }
    ]
  };

  // Obtém o token OAuth do ambiente Apps Script.
  // ISSO É CRUCIAL: O token pertence ao USUÁRIO que autoriza o script.
  // Este usuário DEVE ter permissão para publicar no tópico Pub/Sub
  // especificado no projeto GCP associado ao script.
  // A permissão necessária no IAM é, tipicamente, 'roles/pubsub.publisher'.
  const token = ScriptApp.getOAuthToken();

  // Monta a URL do endpoint da API Pub/Sub
  const pubsubUrl = `https://pubsub.googleapis.com/v1/projects/${GcpProjectId}/topics/${PubSubTopicName}:publish`;

  // Configura as opções da requisição HTTP
  const options = {
    method: 'post', // Método HTTP POST
    contentType: 'application/json', // Tipo de conteúdo do payload
    // O payload deve ser uma string JSON
    payload: JSON.stringify(pubsubData),
    headers: {
      // Autenticação usando o token OAuth obtido
      Authorization: 'Bearer ' + token
    },
    muteHttpExceptions: true // Importante: Captura erros HTTP para tratamento manual
  };

  // Faz a chamada para a API Pub/Sub usando UrlFetchApp
  const response = UrlFetchApp.fetch(pubsubUrl, options);

  // Verifica o código de status da resposta HTTP
  const responseCode = response.getResponseCode();
  const responseText = response.getContentText();

  // Se a resposta for bem-sucedida (códigos 2xx)
  if (responseCode >= 200 && responseCode < 300) {
    Logger.log('Resposta da API Pub/Sub (Sucesso): ' + responseText);
    return responseText; // Retorna o corpo da resposta (geralmente contém os messageIds)
  } else {
    // Se houver erro, loga detalhes e lança uma exceção
    Logger.log(`Erro ao publicar no Pub/Sub. Código: ${responseCode}. Resposta: ${responseText}`);
    throw new Error(`Falha ao publicar no Pub/Sub: ${responseText} (Código: ${responseCode})`);
  }
}

/**
 * Configura o gatilho (trigger) 'onFormSubmit' para o formulário ativo.
 * Esta função deve ser executada manualmente UMA VEZ pelo editor de scripts.
 */
function createFormSubmitTrigger() {
  // Remove gatilhos antigos para evitar duplicatas (opcional, mas recomendado)
  const currentTriggers = ScriptApp.getProjectTriggers();
  currentTriggers.forEach(trigger => {
    if (trigger.getHandlerFunction() === 'onFormSubmit' &&
        trigger.getEventType() === ScriptApp.EventType.ON_FORM_SUBMIT) {
      ScriptApp.deleteTrigger(trigger);
      Logger.log('Gatilho onFormSubmit existente removido.');
    }
  });

  // Obtém o formulário ao qual o script está vinculado
  const form = FormApp.getActiveForm();

  // Cria um novo gatilho que chama 'onFormSubmit' quando o formulário é enviado
  ScriptApp.newTrigger('onFormSubmit')
    .forForm(form)
    .onFormSubmit()
    .create();

  Logger.log('Gatilho onFormSubmit criado com sucesso.');
}

Explicação Detalhada do Código:

  1. Constantes Globais (GcpProjectId, PubSubTopicName :disappointed_face: É essencial que você substitua os valores placeholder 'SEU_GCP_PROJECT_ID' e 'SEU_PUBSUB_TOPIC_ID' pelos seus IDs reais obtidos no Passo 1.
  2. onFormSubmit(e):
    • Esta função é o gatilho (trigger). Ela é automaticamente executada pelo Google Apps Script sempre que o formulário vinculado recebe uma nova resposta.
    • e: O objeto de evento que contém informações sobre a submissão, incluindo a e.response.
    • formResponse.getItemResponses(): Obtém um array com cada pergunta e sua respectiva resposta.
    • formData: Criamos um objeto JSON estruturado para enviar ao Pub/Sub. Inclui:
      • timestamp: Data e hora da submissão em formato padronizado ISO 8601.
      • respondentEmail: O email do usuário que respondeu (se a coleta de email estiver habilitada no formulário e o usuário permitir). Caso contrário, ‘anonymous’.
      • responses: Um objeto onde cada chave é o título da pergunta e o valor é a resposta dada.
    • O loop forEach itera sobre as respostas, extraindo o título da pergunta (item.getTitle()) e a resposta (itemResponse.getResponse()) e populando o objeto formData.responses.
    • Chama a função publishToPubSub para enviar os dados coletados.
    • try...catch: Captura e loga quaisquer erros que possam ocorrer durante o processamento da resposta.
  3. publishToPubSub(data):
    • Esta função é responsável pela comunicação direta com a API do Pub/Sub.
    • Prepara o payload (pubsubData) no formato esperado pela API Pub/Sub: um objeto com uma chave messages, que é um array contendo objetos de mensagem. Cada mensagem tem uma chave data cujo valor deve ser o payload real codificado em Base64. Usamos Utilities.base64Encode(JSON.stringify(data)) para isso.
    • ScriptApp.getOAuthToken(): Este é o ponto chave da autenticação. Ele retorna um token OAuth 2.0 que representa o usuário que autorizou o script. Quando você (ou quem configurar o gatilho) executar createFormSubmitTrigger pela primeira vez, o Google solicitará permissão para o script acessar serviços externos (como o Pub/Sub) em nome desse usuário. Portanto, o usuário que autoriza o script DEVE ter as permissões IAM necessárias no projeto GCP associado para publicar mensagens no tópico Pub/Sub especificado (normalmente, o papel roles/pubsub.publisher ou um papel mais amplo que o inclua, como roles/editor ou roles/owner).
    • UrlFetchApp.fetch(): Faz a requisição HTTP POST para o endpoint da API Pub/Sub.
      • A URL é construída usando seu GcpProjectId e PubSubTopicName.
      • headers: Inclui o cabeçalho Authorization: Bearer <token> para autenticar a chamada usando o token obtido.
      • payload: O JSON da mensagem Pub/Sub.
      • muteHttpExceptions: true: Impede que erros HTTP (como 403 Forbidden, 404 Not Found) parem a execução do script imediatamente. Isso nos permite verificar response.getResponseCode() manualmente.
    • Verificação da Resposta: Checa se o código de status HTTP (responseCode) está na faixa de sucesso (200-299). Se sim, loga e retorna a resposta da API. Se não, loga o erro e lança uma exceção para indicar a falha, que será capturada pelo try...catch em onFormSubmit.
  4. createFormSubmitTrigger():
    • Esta função é auxiliar e precisa ser executada manualmente UMA VEZ a partir do editor de scripts.
    • Ela primeiro remove gatilhos onFormSubmit antigos para evitar execuções duplicadas caso você a execute mais de uma vez.
    • Em seguida, ela cria programaticamente o gatilho (trigger) que vincula o evento onFormSubmit do formulário ativo à execução da função onFormSubmit no seu script.
    • Ao executar esta função pela primeira vez, o Google solicitará as autorizações necessárias.

Passo 4: Autorização e Teste

  1. Salve o Script: Clique no ícone de disquete (“Save project”).
  2. Execute createFormSubmitTrigger:
    • No menu suspenso acima do código (onde provavelmente está selecionado onFormSubmit), escolha createFormSubmitTrigger.
    • Clique no botão “Run” (ícone de play).
    • Autorização: Uma janela “Authorization required” aparecerá. Clique em “Review Permissions”.
    • Escolha a conta Google que você está usando (a mesma que tem acesso ao formulário e ao projeto GCP).
    • Você verá uma tela “Google hasn’t verified this app”. Isso é normal para scripts pessoais. Clique em “Advanced” e depois em “Go to [Nome do seu projeto] (unsafe)”.
    • Revise as permissões que o script está solicitando (gerenciar formulários, conectar-se a serviços externos) e clique em “Allow”.
    • A função será executada e você deverá ver “Gatilho onFormSubmit criado com sucesso.” nos logs (View → Logs).
  3. Teste o Formulário: Abra seu Google Form (no modo de preenchimento, não edição) e envie uma resposta de teste.
  4. Verifique os Logs: Volte ao editor de scripts e vá em “View” → “Executions”. Procure pela execução mais recente de onFormSubmit. Clique nela para ver os logs (Logger.log). Você deve ver a mensagem “Mensagem publicada no Pub/Sub: …” se tudo correu bem, ou mensagens de erro caso contrário.
  5. Verifique o Pub/Sub:
    • No Cloud Console, vá para Pub/Sub → Subscriptions. Crie uma nova assinatura (“Create Subscription”) para o seu tópico (form-submission). Dê um nome a ela (ex: form-submission-test-sub), selecione o tópico correto e mantenha o resto como padrão (Pull).
    • Após criar a assinatura, clique nela e vá para a aba “Messages”.
    • Clique em “Pull”. Pode levar alguns segundos, mas você deverá ver a mensagem correspondente à sua resposta do formulário (o conteúdo estará codificado em Base64 na seção “Data”, mas o console geralmente oferece uma opção para decodificá-lo).

Considerações Importantes

  • Permissões IAM: Reforçando: O usuário que autoriza o script (geralmente o criador do formulário/script) precisa ter o papel roles/pubsub.publisher (ou equivalente) no projeto GCP associado para que a chamada publishToPubSub funcione. Se você receber erros 403 Forbidden, verifique as permissões IAM desse usuário no GCP.
  • Tratamento de Erros: O código inclui tratamento básico de erros (try...catch e verificação do código de resposta HTTP). Para produção, você pode querer adicionar um tratamento mais robusto, como notificações de falha.
  • Quotas e Limites: Esteja ciente das quotas do Google Apps Script (tempo de execução, chamadas UrlFetchApp) e do Pub/Sub (throughput, tamanho da mensagem). Para formulários com altíssimo volume, pode ser necessário considerar outras arquiteturas.
  • Segurança: Se o formulário coletar dados sensíveis, certifique-se de que seu tópico Pub/Sub e os sistemas que consomem dele estejam adequadamente protegidos.

Conclusão

Integrar o Google Forms com o Pub/Sub usando Apps Script abre um leque de possibilidades para automatizar fluxos de trabalho e processar dados em tempo real. Seguindo os passos de configuração do GCP, associando corretamente o projeto Apps Script ao GCP, entendendo o fluxo de autenticação via ScriptApp.getOAuthToken() e implementando o código fornecido, você pode ter essa integração funcionando rapidamente. Lembre-se sempre de verificar as permissões e testar o fluxo completo!

Fale com um especialista de vendas do Google Cloud