Crea passaggi personalizzati di Google Workspace Studio in Node.js su Cloud Run

1. Introduzione

Google Workspace Studio ti consente di automatizzare i processi aziendali nelle applicazioni Google Workspace come Gmail, Google Chat, Google Drive e Fogli Google. Quando i tuoi workflow devono iniziare da un'applicazione web esterna o eseguire una logica personalizzata nel tuo ambiente cloud, puoi estendere Google Workspace Studio con un componente aggiuntivo di Google Workspace HTTP in Node.js su Cloud Run:

  1. Passaggio iniziale personalizzato (workflowTrigger): avvia un flusso di Google Workspace Studio quando si verifica un evento esterno, ad esempio un cliente che invia un ticket in un portale web interno, chiamando l'API REST triggers.fire di Google Workspace Studio.
  2. Passaggio di azione personalizzata (workflowAction): riceve le variabili dai passaggi precedenti del flusso, esegue la logica personalizzata o l'inferenza dell'AI nel servizio Node.js e restituisce le variabili di output a Google Workspace Studio.

In questo codelab, crei ed esegui il deployment di un'estensione HTTP Node.js completa per Google Workspace Studio su Cloud Run, utilizzi Cloud Firestore per rendere persistenti gli abbonamenti ai trigger di flusso attivi e i ticket inviati nelle revisioni dei container e classifichi l'urgenza dei ticket nel passaggio dell'azione personalizzata utilizzando Gemini 3.8 Flash (gemini-3.8-flash) sulla Gemini Enterprise Agent Platform con un fallback resiliente di corrispondenza delle parole chiave.

Cosa creerai

  • Un'app web Support Quick Portal e un componente aggiuntivo HTTP di Google Workspace creato con Node.js, Express e Firestore su Cloud Run.
  • Un passaggio iniziale personalizzato Nuovo ticket di assistenza (supportTicketTrigger) che mantiene le iscrizioni ai flussi attivi in una raccolta studio_triggers Firestore, registra i ticket inviati in una raccolta support_tickets Firestore e viene attivato ogni volta che un ticket viene inviato nel portale web, passando ticketTitle e ticketDescription a un flusso di Google Workspace Studio.
  • Un passaggio di azione personalizzata Rileva urgenza (urgencyDetectorStep) che classifica l'urgenza dei ticket utilizzando Gemini 3.8 Flash (gemini-3.8-flash) su Gemini Enterprise Agent Platform (aiplatform.googleapis.com), con un fallback automatico alla corrispondenza delle parole chiave (urgent, blocker, critical).

Obiettivi didattici

  • Come configurare un manifest di deployment di un componente aggiuntivo Google Workspace HTTP (deployment.json) con elementi workflowTrigger e workflowAction.
  • Come rendere persistenti gli abbonamenti ai trigger di Google Workspace Studio (triggerId, notifyUri e userOAuthToken) in una raccolta studio_triggers Firestore e attivare i flussi utilizzando l'API REST triggers.fire.
  • Come creare un passaggio personalizzato workflowAction in Node.js utilizzando l'output strutturato di Gemini 3.8 Flash (gemini-3.8-flash) su Gemini Enterprise Agent Platform con un fallback di parole chiave.

Prerequisiti

  • Un account Google Workspace con accesso a Google Workspace Studio.
  • Un progetto Google Cloud con la fatturazione abilitata, creato nella stessa organizzazione Google Workspace del tuo account utente, in modo da poter installare deployment di componenti aggiuntivi non pubblicati per i test.

2. Configurare il progetto Google Cloud e Firestore

In questo passaggio, configuri la schermata per il consenso OAuth del progetto, abiliti le API Google Cloud e Google Workspace richieste in Cloud Shell, esegui il provisioning del database (default) Firestore e inizializzi il service account di autorizzazione del componente aggiuntivo.

  1. Apri la console Google Cloud, seleziona il tuo progetto Google Cloud e fai clic sull'icona Attiva Cloud Shell nella barra di navigazione in alto.
  2. Configura la schermata per il consenso OAuth in modo che Google Workspace possa eseguire il provisioning di un client OAuth per il tuo componente aggiuntivo:
    1. Apri la pagina del branding di Google Auth Platform.
    2. Se richiesto, fai clic su Inizia. Se la schermata per il consenso è già configurata, vai al passaggio 3.
    3. Nella sezione Informazioni sull'app, inserisci Support Extension per il nome dell'app, seleziona l'email per l'assistenza utenti e fai clic su Avanti.
    4. In Pubblico, seleziona Interno e fai clic su Avanti.
    5. Nella sezione Dati di contatto, inserisci il tuo indirizzo email e fai clic su Avanti.
    6. In Fine, accetta le Norme relative ai dati utente: servizi API di Google, fai clic su Continua e poi su Crea.
  3. In Cloud Shell, imposta il progetto attivo (sostituisci YOUR_PROJECT_ID con l'ID progetto Google Cloud) e abilita le API richieste:
    gcloud config set project YOUR_PROJECT_ID
    gcloud services enable \
      aiplatform.googleapis.com \
      firestore.googleapis.com \
      run.googleapis.com \
      cloudbuild.googleapis.com \
      artifactregistry.googleapis.com \
      appsmarket-component.googleapis.com \
      gsuiteaddons.googleapis.com \
      workspacestudio.googleapis.com
    
  4. Esegui il provisioning del database Firestore di (default) in us-central1 (se non esiste già) e inizializza il service account del servizio Google Workspace Add-on:
    gcloud firestore databases describe --database="(default)" >/dev/null 2>&1 || \
      gcloud firestore databases create \
        --database="(default)" \
        --location=us-central1 \
        --type=firestore-native \
        --quiet
    gcloud workspace-add-ons get-authorization
    

3. Crea l'app Node.js con Firestore e Gemini 3.8 Flash

In questo passaggio, crei l'applicazione Node.js in ~/studio-support-addon. Per mantenere il codice modulare e leggibile, organizza il progetto in cinque file mirati:

  • firestore.js: inizializza il client Cloud Firestore (raccolte studio_triggers e support_tickets) e aggiorna i token OAuth memorizzati nella cache.
  • starter.js: implementa i webhook del passaggio iniziale personalizzato Nuovo ticket di assistenza (/onConfigTrigger e /onManageTrigger).
  • action.js: implementa i webhook del passaggio dell'azione personalizzata Rileva urgenza (/onConfigUrgency e /onExecuteUrgency) utilizzando Gemini 3.8 Flash (gemini-3.8-flash) con un fallback di corrispondenza delle parole chiave.
  • portal.js: gestisce la UI web del portale rapido di assistenza (GET /) e attiva i trigger di Google Workspace Studio (POST /api/submit-ticket).
  • index.js: collega tutte le route Express e avvia il server HTTP.

Inizializza il progetto e package.json

  1. Crea una directory di progetto e accedi alla directory:
    mkdir -p ~/studio-support-addon && cd ~/studio-support-addon
    
  2. Crea package.json con express, @google-cloud/firestore e @google/genai:
    cat << 'EOF' > package.json
    {
      "name": "studio-support-addon",
      "version": "1.0.0",
      "type": "module",
      "main": "index.js",
      "scripts": {
        "start": "node index.js"
      },
      "dependencies": {
        "@google-cloud/firestore": "^7.11.0",
        "@google/genai": "^1.0.0",
        "express": "^4.21.2"
      }
    }
    EOF
    

firestore.js: stato di Firestore condiviso e aggiornamento del token OAuth

Crea firestore.js per gestire le due raccolte Firestore e mantenere aggiornati i token OAuth utente memorizzati nella cache:

  • studio_triggers: memorizza gli abbonamenti attivi ai flussi di Google Workspace Studio (triggerId, notifyUri e userOAuthToken) in modo che sopravvivano ai riavvii dei container Cloud Run e agli eventi di scalabilità fino a zero.
  • support_tickets: memorizza un record di ogni ticket di assistenza inviato tramite il portale.
  • refreshTokenInFirestore(req): ogni volta che Google Workspace Studio chiama uno dei tuoi endpoint webhook, include un authorizationEventObject.userOAuthToken di breve durata. Questo helper aggiorna i documenti di attivazione attivi con il token più recente in modo che le chiamate triggers.fire in background rimangano autorizzate.
cat << 'EOF' > firestore.js
import { Firestore } from '@google-cloud/firestore';

export const db = new Firestore({
  projectId: process.env.GOOGLE_CLOUD_PROJECT || undefined,
  databaseId: '(default)',
  ignoreUndefinedProperties: true,
});

export const triggersCollection = db.collection(
  process.env.FIRESTORE_TRIGGERS_COLLECTION || 'studio_triggers'
);

export const ticketsCollection = db.collection(
  process.env.FIRESTORE_TICKETS_COLLECTION || 'support_tickets'
);

// Keep cached userOAuthToken fresh across active trigger documents
export async function refreshTokenInFirestore(req) {
  const freshToken = req.body?.authorizationEventObject?.userOAuthToken;
  if (!freshToken) return;
  try {
    const snapshot = await triggersCollection.get();
    if (snapshot.empty) return;
    const batch = db.batch();
    snapshot.docs.forEach((doc) => {
      batch.set(
        doc.ref,
        { userOAuthToken: freshToken, updatedAt: new Date().toISOString() },
        { merge: true }
      );
    });
    await batch.commit();
  } catch (err) {
    console.error('Error refreshing OAuth token in Firestore:', err.message);
  }
}
EOF

starter.js - Passaggio iniziale personalizzato (workflowTrigger)

Crea starter.js per implementare i due endpoint webhook richiesti da un passaggio iniziale personalizzato di Google Workspace Studio:

  • onConfigTrigger (POST /onConfigTrigger): viene chiamato quando un utente seleziona Nuovo ticket di assistenza come punto di partenza del flusso in Google Workspace Studio. Restituisce un'azione di navigazione pushCard che descrive l'intent iniziale.
  • onManageTrigger (POST /onManageTrigger): chiamato quando un utente attiva (req.body.workflow.triggerCreation) o disattiva / elimina (req.body.workflow.triggerDeletion) un flusso. Salva o elimina il documento di abbonamento nella raccolta Firestore studio_triggers.
cat << 'EOF' > starter.js
import { triggersCollection, refreshTokenInFirestore } from './firestore.js';

// Custom Starter Configuration Card
export async function onConfigTrigger(req, res) {
  await refreshTokenInFirestore(req);
  res.status(200).json({
    action: {
      navigations: [
        {
          pushCard: {
            sections: [
              {
                header: 'Support Ticket Listener',
                widgets: [
                  {
                    textParagraph: {
                      text: 'This starter listens for new tickets submitted through the Support Quick Portal and stores subscriptions in Firestore.',
                    },
                  },
                ],
              },
            ],
          },
        },
      ],
    },
  });
}

// Custom Starter Subscription Lifecycle (Firestore studio_triggers collection)
export async function onManageTrigger(req, res) {
  try {
    const triggerCreation = req.body?.workflow?.triggerCreation;
    const triggerDeletion = req.body?.workflow?.triggerDeletion;
    const userOAuthToken =
      req.body?.authorizationEventObject?.userOAuthToken || null;

    if (triggerCreation?.triggerId) {
      const triggerId = String(triggerCreation.triggerId);
      const docId = encodeURIComponent(triggerId);
      await triggersCollection.doc(docId).set(
        {
          triggerId,
          notifyUri: triggerCreation.notifyUri || null,
          ...(userOAuthToken ? { userOAuthToken } : {}),
          updatedAt: new Date().toISOString(),
        },
        { merge: true }
      );
    }

    if (triggerDeletion?.triggerId) {
      const triggerId = String(triggerDeletion.triggerId);
      const docId = encodeURIComponent(triggerId);
      await triggersCollection.doc(docId).delete();
    }

    if (!triggerCreation && !triggerDeletion && userOAuthToken) {
      await refreshTokenInFirestore(req);
    }
  } catch (err) {
    console.error('Error in /onManageTrigger:', err.message);
  }
  res.status(200).json({});
}
EOF

action.js - Passaggio Azione personalizzata (workflowAction) con Gemini 3.8 Flash

Crea action.js per implementare i due endpoint webhook richiesti da un passaggio di azione personalizzata di Google Workspace Studio:

  • onConfigUrgency (POST /onConfigUrgency): restituisce una scheda di configurazione con un widget textInput che imposta hostAppDataSource.workflowDataSource.includeVariables: true, consentendo agli utenti di selezionare la variabile ticketDescription restituita dal passaggio iniziale.
  • onExecuteUrgency (POST /onExecuteUrgency): legge l'input ticketDescription da req.body.workflow.actionInvocation.inputs, classifica il suo impatto aziendale e l'urgenza utilizzando Gemini 3.8 Flash (gemini-3.8-flash) sulla Gemini Enterprise Agent Platform con output strutturato (responseSchema enum ["High", "Normal"]), esegue il failover su detectKeywordUrgency() (urgent, blocker, critical) se la chiamata al modello non va a buon fine e restituisce la variabile di output urgencyLevel utilizzando hostAppAction.workflowAction.returnOutputVariablesAction.
cat << 'EOF' > action.js
import { GoogleGenAI, Type } from '@google/genai';
import { refreshTokenInFirestore } from './firestore.js';

// Initialize Google GenAI SDK for the Gemini Enterprise Agent Platform
const ai = new GoogleGenAI({
  vertexai: true,
  project: process.env.GOOGLE_CLOUD_PROJECT,
  location: 'global',
});

// Fallback keyword classifier if the Gemini API call is unavailable
export function detectKeywordUrgency(description) {
  const lower = (description || '').toLowerCase();
  return lower.includes('urgent') ||
    lower.includes('blocker') ||
    lower.includes('critical')
    ? 'High'
    : 'Normal';
}

// Custom Action Step Configuration Card
export async function onConfigUrgency(req, res) {
  await refreshTokenInFirestore(req);
  res.status(200).json({
    action: {
      navigations: [
        {
          pushCard: {
            sections: [
              {
                header: 'Urgency Detector Step',
                widgets: [
                  {
                    textInput: {
                      name: 'ticketDescription',
                      label: 'Source Description',
                      hintText:
                        'Select the description variable outputted by the starter',
                      hostAppDataSource: {
                        workflowDataSource: {
                          includeVariables: true,
                        },
                      },
                    },
                  },
                ],
              },
            ],
          },
        },
      ],
    },
  });
}

// Custom Action Step Execution: Gemini 3.8 Flash with keyword fallback
export async function onExecuteUrgency(req, res) {
  await refreshTokenInFirestore(req);
  const inputs = req.body?.workflow?.actionInvocation?.inputs || {};
  const description = inputs.ticketDescription?.stringValues?.[0] || '';

  let urgencyLevel = detectKeywordUrgency(description);
  try {
    const response = await ai.models.generateContent({
      model: 'gemini-3.8-flash',
      contents: `Evaluate the business impact and time sensitivity of this support ticket description and classify its urgency:\n\n${description}`,
      config: {
        responseMimeType: 'text/x.enum',
        responseSchema: {
          type: Type.STRING,
          enum: ['High', 'Normal'],
        },
      },
    });
    const candidate = (response.text || '').trim();
    if (candidate === 'High' || candidate === 'Normal') {
      urgencyLevel = candidate;
    }
  } catch (err) {
    console.error('Gemini fallback to keyword matching:', err.message);
  }

  res.status(200).json({
    hostAppAction: {
      workflowAction: {
        returnOutputVariablesAction: {
          variables: {
            urgencyLevel: {
              stringValues: [urgencyLevel],
            },
          },
        },
      },
    },
  });
}
EOF

portal.js: supporta l'interfaccia utente di Quick Portal e il dispatcher triggers.fire

Crea portal.js per pubblicare il portale web e inviare le richieste di assistenza a Google Workspace Studio:

  • renderPortal (GET /): esegue una query sulla raccolta studio_triggers Firestore per visualizzare il numero di trigger Google Workspace Studio attivi registrati e mostra il modulo di invio del ticket HTML.
  • submitTicket (POST /api/submit-ticket): scorre tutti i documenti trigger attivi in studio_triggers, richiama l'endpoint triggers.fire di Google Workspace Studio (https://workspacestudio.googleapis.com/v1/triggers/{triggerId}:fire) con le variabili di output ticketTitle e ticketDescription e un requestId univoco e salva il ticket nella raccolta support_tickets di Firestore.
cat << 'EOF' > portal.js
import { triggersCollection, ticketsCollection } from './firestore.js';

// Support Quick Portal UI
export async function renderPortal(req, res) {
  let activeCount = 0;
  try {
    const snapshot = await triggersCollection.get();
    activeCount = snapshot.size;
  } catch (err) {
    console.error('Error reading studio_triggers collection:', err.message);
  }

  res.status(200).send(`<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>Support Quick Portal</title>
    <style>
      body { font-family: Arial, sans-serif; padding: 24px; max-width: 540px; margin: auto; color: #202124; }
      .badge { background: #e8f0fe; color: #1967d2; padding: 8px 12px; border-radius: 6px; font-size: 14px; margin-bottom: 16px; }
      label { font-weight: bold; display: block; margin-top: 16px; }
      input, textarea { width: 100%; margin-top: 6px; padding: 10px; box-sizing: border-box; border: 1px solid #dadce0; border-radius: 4px; }
      button { background-color: #1a73e8; color: white; border: none; padding: 10px 18px; margin-top: 16px; font-weight: bold; cursor: pointer; border-radius: 4px; }
      button:hover { background-color: #1557b0; }
      #status { margin-top: 16px; font-weight: bold; }
    </style>
  </head>
  <body>
    <h2>Support Quick Portal</h2>
    <div class="badge">
      Active Google Workspace Studio triggers in Firestore (<code>studio_triggers</code>):
      <strong id="trigger-count">${activeCount}</strong>
    </div>
    <p>Submit a support ticket below to fire your Google Workspace Studio flow.</p>

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

    <label for="description">Issue Description</label>
    <textarea id="description" rows="4" placeholder="Describe the issue..."></textarea>

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

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

        if (!title || !description) {
          statusEl.style.color = '#d93025';
          statusEl.textContent = 'Please fill out both fields.';
          return;
        }

        statusEl.style.color = '#188038';
        statusEl.textContent = 'Submitting ticket...';

        try {
          const res = await fetch('/api/submit-ticket', {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify({ title, description }),
          });
          const data = await res.json();
          if (data.ok) {
            statusEl.textContent =
              'Ticket saved to Firestore! Fired ' + data.triggersFired + ' Google Workspace Studio trigger(s).';
          } else {
            statusEl.style.color = '#d93025';
            statusEl.textContent = 'Error: ' + (data.error || 'Unknown error');
          }
        } catch (err) {
          statusEl.style.color = '#d93025';
          statusEl.textContent = 'Request failed: ' + err.message;
        }
      }
    </script>
  </body>
</html>`);
}

// Portal Form Submission -> Fire Active Google Workspace Studio Triggers
export async function submitTicket(req, res) {
  const title = req.body?.title || 'Untitled Ticket';
  const description = req.body?.description || '';
  const results = [];

  try {
    const snapshot = await triggersCollection.get();
    for (const doc of snapshot.docs) {
      const trigger = doc.data();
      if (!trigger.triggerId || !trigger.userOAuthToken) continue;

      const response = await fetch(
        `https://workspacestudio.googleapis.com/v1/triggers/${encodeURIComponent(trigger.triggerId)}:fire`,
        {
          method: 'POST',
          headers: {
            Authorization: `Bearer ${trigger.userOAuthToken}`,
            'Content-Type': 'application/json',
          },
          body: JSON.stringify({
            name: 'triggers/' + trigger.triggerId,
            outputs: {
              ticketTitle: { stringValues: [title] },
              ticketDescription: { stringValues: [description] },
            },
            requestId:
              'req_' + Date.now() + '_' + Math.floor(Math.random() * 1000),
          }),
        }
      );
      results.push({ triggerId: trigger.triggerId, status: response.status });
    }

    await ticketsCollection.add({
      title,
      description,
      triggersFired: results.length,
      createdAt: new Date().toISOString(),
    });

    res.status(200).json({ ok: true, triggersFired: results.length, results });
  } catch (err) {
    console.error('Error in /api/submit-ticket:', err.message);
    res.status(500).json({ ok: false, error: err.message });
  }
}
EOF

index.js: punto di accesso al server Express

Crea index.js per collegare le route del portale e gli endpoint webhook di Google Workspace Studio in un server HTTP Express in ascolto su PORT (valore predefinito 8080 su Cloud Run):

cat << 'EOF' > index.js
import express from 'express';
import { onConfigTrigger, onManageTrigger } from './starter.js';
import { onConfigUrgency, onExecuteUrgency } from './action.js';
import { renderPortal, submitTicket } from './portal.js';

const app = express();
app.use(express.json());

// Support Quick Portal routes
app.get('/', renderPortal);
app.post('/api/submit-ticket', submitTicket);

// Google Workspace Studio Custom Starter Step webhooks
app.post('/onConfigTrigger', onConfigTrigger);
app.post('/onManageTrigger', onManageTrigger);

// Google Workspace Studio Custom Action Step webhooks
app.post('/onConfigUrgency', onConfigUrgency);
app.post('/onExecuteUrgency', onExecuteUrgency);

const PORT = process.env.PORT || 8080;
app.listen(PORT, () => {
  console.log(`Support Extension listening on port ${PORT}`);
});
EOF

4. Esegui il deployment in Cloud Run e installa il componente aggiuntivo

In questo passaggio, esegui il deployment del servizio Node.js in Cloud Run, concedi le autorizzazioni IAM per l'invoker dei componenti aggiuntivi di Google Workspace e l'accesso al runtime di Firestore e Agent Platform, genera il manifest deployment.json e installa il deployment del componente aggiuntivo.

  1. Esegui il deployment del servizio support-extension su Cloud Run con le variabili di ambiente del progetto e della raccolta Firestore:
    gcloud run deploy support-extension \
      --source . \
      --region us-central1 \
      --allow-unauthenticated \
      --set-env-vars="GOOGLE_CLOUD_PROJECT=$(gcloud config get-value project),FIRESTORE_TRIGGERS_COLLECTION=studio_triggers,FIRESTORE_TICKETS_COLLECTION=support_tickets" \
      --quiet
    
  2. Concedi roles/run.invoker al servizio Cloud Run al service account dei componenti aggiuntivi Google Workspace in modo che Google Workspace Studio possa chiamare gli endpoint webhook:
    ADDON_SA=$(gcloud workspace-add-ons get-authorization --format="value(serviceAccountEmail)")
    gcloud run services add-iam-policy-binding support-extension \
      --region=us-central1 \
      --member="serviceAccount:${ADDON_SA}" \
      --role="roles/run.invoker" \
      --quiet
    
  3. Concedi roles/datastore.user (per la lettura e la scrittura delle raccolte Firestore studio_triggers e support_tickets) e roles/aiplatform.user (per chiamare Gemini sulla piattaforma Gemini Enterprise Agent) all'account di servizio di runtime Cloud Run:
    PROJECT_ID=$(gcloud config get-value project)
    PROJECT_NUMBER=$(gcloud projects describe "${PROJECT_ID}" --format="value(projectNumber)")
    RUNTIME_SA=$(gcloud run services describe support-extension --region=us-central1 --format="value(spec.template.spec.serviceAccountName)")
    RUNTIME_SA=${RUNTIME_SA:-"${PROJECT_NUMBER}-compute@developer.gserviceaccount.com"}
    
    gcloud projects add-iam-policy-binding "${PROJECT_ID}" \
      --member="serviceAccount:${RUNTIME_SA}" \
      --role="roles/datastore.user" \
      --condition=None \
      --quiet
    
    gcloud projects add-iam-policy-binding "${PROJECT_ID}" \
      --member="serviceAccount:${RUNTIME_SA}" \
      --role="roles/aiplatform.user" \
      --condition=None \
      --quiet
    
  4. Recupera l'URL Cloud Run di cui è stato eseguito il deployment e genera il manifest deployment.json che definisce gli elementi supportTicketTrigger (workflowTrigger) e urgencyDetectorStep (workflowAction):
    SERVICE_URL=$(gcloud run services describe support-extension --region=us-central1 --format="value(status.url)")
    
    cat << EOF > deployment.json
    {
      "oauthScopes": [
        "https://www.googleapis.com/auth/workspace.studio.trigger",
        "https://www.googleapis.com/auth/userinfo.email"
      ],
      "addOns": {
        "common": {
          "name": "Support Extension",
          "logoUrl": "https://www.gstatic.com/images/branding/productlogos/buganizer/v1/192px.svg"
        },
        "studio": {
          "flows": {
            "workflowElements": [
              {
                "id": "supportTicketTrigger",
                "state": "ACTIVE",
                "name": "New Support Ticket",
                "description": "Fires when someone submits a ticket in the Support Quick Portal.",
                "workflowTrigger": {
                  "inputs": [],
                  "outputs": [
                    {
                      "id": "ticketTitle",
                      "description": "Ticket Title",
                      "cardinality": "SINGLE",
                      "dataType": { "basicType": "STRING" }
                    },
                    {
                      "id": "ticketDescription",
                      "description": "Ticket Description",
                      "cardinality": "SINGLE",
                      "dataType": { "basicType": "STRING" }
                    }
                  ],
                  "onConfigFunction": "${SERVICE_URL}/onConfigTrigger",
                  "onManageFunction": "${SERVICE_URL}/onManageTrigger"
                }
              },
              {
                "id": "urgencyDetectorStep",
                "state": "ACTIVE",
                "name": "Detect Urgency",
                "description": "Evaluates the ticket description to detect High or Normal urgency.",
                "workflowAction": {
                  "inputs": [
                    {
                      "id": "ticketDescription",
                      "description": "Description to scan",
                      "cardinality": "SINGLE",
                      "dataType": { "basicType": "STRING" }
                    }
                  ],
                  "outputs": [
                    {
                      "id": "urgencyLevel",
                      "description": "Detected Urgency (High / Normal)",
                      "cardinality": "SINGLE",
                      "dataType": { "basicType": "STRING" }
                    }
                  ],
                  "onConfigFunction": "${SERVICE_URL}/onConfigUrgency",
                  "onExecuteFunction": "${SERVICE_URL}/onExecuteUrgency"
                }
              }
            ]
          }
        },
        "httpOptions": {
          "authorizationHeader": "SYSTEM_ID_TOKEN"
        }
      }
    }
    EOF
    
  5. Crea (o sostituisci) e installa il deployment del componente aggiuntivo Google Workspace per il tuo account e stampa l'URL del portale di assistenza rapida:
    gcloud workspace-add-ons deployments create support-extension --deployment-file=deployment.json || \
      gcloud workspace-add-ons deployments replace support-extension --deployment-file=deployment.json
    gcloud workspace-add-ons deployments install support-extension
    echo "Support Quick Portal URL: ${SERVICE_URL}"
    

5. Testare i passaggi personalizzati in Google Workspace Studio

  1. Apri Google Workspace Studio (aggiorna la scheda del browser se era già aperta) e fai clic su Nuovo flusso.
  2. Fai clic su Seleziona un punto di partenza > Estensione dell'assistenza > Nuovo ticket di assistenza.
  3. Fai clic su Aggiungi passaggio (+) > Estensione di supporto > Rileva urgenza. Nel campo Descrizione origine, fai clic su + Variabili e seleziona Descrizione ticket.
  4. Fai clic su Aggiungi passaggio (+) e aggiungi un passaggio Chat > Inviami una notifica in Chat che includa le variabili Titolo ticket, Livello di urgenza e Descrizione ticket nel messaggio.
  5. Attiva il flusso On nell'angolo in alto a destra. Google Workspace Studio chiama immediatamente l'endpoint /onManageTrigger su Cloud Run, che scrive l'abbonamento al trigger attivo nella raccolta studio_triggers in Firestore.
  6. Apri l'URL del portale rapido di assistenza (https://support-extension-...run.app) in una nuova scheda, verifica che il portale mostri l'1attivatore attivo in Firestore e invia due ticket per testare la classificazione di Gemini 3.8 Flash (gemini-3.8-flash):
    • Ticket ad alto impatto (Titolo del ticket: London office lockout, Descrizione del problema: All 500 employees in the London office are locked out of their workstations 20 minutes before the board presentation.): anche senza parole chiave hardcoded come urgent o blocker, gemini-3.8-flash classifica questo ticket come urgenza High.
    • Ticket di routine (Titolo ticket: Conference room adapters, Descrizione problema: Could we order two extra HDMI adapters for the third-floor conference room next month?): gemini-3.8-flash classifica questo ticket come urgenza Normal.
  7. Verifica in Google Chat e nella scheda Attività di Google Workspace Studio che il flusso sia stato eseguito per entrambi i ticket con Urgency Level: High e Urgency Level: Normal.

6. Esegui la pulizia

Per evitare che al tuo account Google Cloud vengano addebitati costi continui, rimuovi le risorse di cui è stato eseguito il deployment in Cloud Shell:

gcloud workspace-add-ons deployments uninstall support-extension
gcloud workspace-add-ons deployments delete support-extension --quiet
gcloud run services delete support-extension --region=us-central1 --quiet
gcloud firestore databases delete --database="(default)" --quiet

7. Complimenti!

Hai creato, eseguito il deployment e testato un'estensione HTTP Node.js per Google Workspace Studio utilizzando Firestore, Cloud Run e Gemini 3.8 Flash su Gemini Enterprise Agent Platform.

Argomenti trattati

  • Configurazione di un manifest di un componente aggiuntivo HTTP di Google Workspace Studio (deployment.json) con elementi workflowTrigger e workflowAction.
  • Mantenere gli abbonamenti a Google Workspace Studio workflowTrigger nella raccolta studio_triggers Firestore e gli invii del portale di logging in support_tickets in modo che i trigger sopravvivano al ridimensionamento e ai redeployment di Cloud Run.
  • Creazione di un passaggio workflowAction che classifica le richieste di assistenza con Gemini 3.8 Flash (gemini-3.8-flash) output strutturato sulla Gemini Enterprise Agent Platform, con un fallback di corrispondenza delle parole chiave resiliente.

Passaggi successivi