Benutzerdefinierte Google Workspace Studio-Schritte in Node.js in Cloud Run erstellen

1. Einführung

Mit Google Workspace Studio können Sie Geschäftsprozesse in Google Workspace-Anwendungen wie Gmail, Google Chat, Google Drive und Google Sheets automatisieren. Wenn Ihre Arbeitsabläufe in einer externen Webanwendung gestartet werden müssen oder benutzerdefinierte Logik in Ihrer eigenen Cloud-Umgebung ausgeführt werden soll, können Sie Google Workspace Studio mit einem HTTP-Google Workspace-Add-on in Node.js auf Cloud Run erweitern:

  1. Benutzerdefinierter Startschritt (workflowTrigger): Startet einen Google Workspace Studio-Ablauf, wenn ein externes Ereignis eintritt, z. B. wenn ein Kunde ein Ticket in einem internen Webportal einreicht. Dazu wird die Google Workspace Studio REST API triggers.fire aufgerufen.
  2. Benutzerdefinierter Aktionsschritt (workflowAction): Empfängt Variablen aus früheren Schritten im Flow, führt benutzerdefinierte Logik oder KI-Inferenz in Ihrem Node.js-Dienst aus und gibt Ausgabevariablen an Google Workspace Studio zurück.

In diesem Codelab erstellen und stellen Sie eine vollständige Node.js-HTTP-Erweiterung für Google Workspace Studio in Cloud Run bereit, verwenden Cloud Firestore, um aktive Flow-Trigger-Abos und eingereichte Tickets über Containerrevisionen hinweg beizubehalten, und klassifizieren die Dringlichkeit von Tickets in Ihrem benutzerdefinierten Aktionsschritt mit Gemini 3.8 Flash (gemini-3.8-flash) auf der Gemini Enterprise Agent Platform mit einem robusten Fallback für den Keyword-Abgleich.

Aufgaben

  • Eine Support Quick Portal-Webanwendung und ein HTTP-Google Workspace-Add-on, die mit Node.js, Express und Firestore in Cloud Run erstellt wurden.
  • Ein benutzerdefinierter Startschritt New Support Ticket (supportTicketTrigger), mit dem aktive Flow-Abos in einer studio_triggers Firestore-Sammlung gespeichert, eingereichte Tickets in einer support_tickets Firestore-Sammlung protokolliert und ausgelöst werden, wenn ein Ticket im Webportal eingereicht wird. Dabei werden ticketTitle und ticketDescription in einen Google Workspace Studio-Flow übergeben.
  • Ein benutzerdefinierter Aktionsschritt vom Typ Detect Urgency (urgencyDetectorStep), der die Dringlichkeit von Tickets mit Gemini 3.8 Flash (gemini-3.8-flash) auf der Gemini Enterprise Agent Platform (aiplatform.googleapis.com) klassifiziert. Es erfolgt ein automatisches Fallback auf die Keyword-Optionen (urgent, blocker, critical).

Lerninhalte

  • So konfigurieren Sie ein HTTP-Google Workspace-Add‑on-Bereitstellungsmanifest (deployment.json) mit den Elementen workflowTrigger und workflowAction.
  • Anleitung zum persistenten Speichern von Google Workspace Studio-Triggerabos (triggerId, notifyUri und userOAuthToken) in einer studio_triggers Firestore-Sammlung und zum Auslösen von Flows mit der triggers.fire REST API.
  • So erstellen Sie einen benutzerdefinierten workflowAction-Schritt in Node.js mit strukturierter Ausgabe von Gemini 3.8 Flash (gemini-3.8-flash) auf der Gemini Enterprise Agent Platform mit einem Keyword-Fallback.

Vorbereitung

  • Ein Google Workspace-Konto mit Zugriff auf Google Workspace Studio.
  • Ein Google Cloud-Projekt mit aktivierter Abrechnung, das in derselben Google Workspace-Organisation wie Ihr Nutzerkonto erstellt wurde, damit Sie nicht veröffentlichte Add‑on-Bereitstellungen zum Testen installieren können.

2. Google Cloud-Projekt und Firestore einrichten

In diesem Schritt konfigurieren Sie den OAuth-Zustimmungsbildschirm Ihres Projekts, aktivieren die erforderlichen Google Cloud- und Google Workspace-APIs in Cloud Shell, stellen Ihre (default)-Firestore-Datenbank bereit und initialisieren das Dienstkonto für die Add-on-Autorisierung.

  1. Öffnen Sie die Google Cloud Console, wählen Sie Ihr Google Cloud-Projekt aus und klicken Sie in der oberen Navigationsleiste auf das Symbol Cloud Shell aktivieren.
  2. Konfigurieren Sie den OAuth-Zustimmungsbildschirm, damit Google Workspace einen OAuth-Client für Ihr Add-on bereitstellen kann:
    1. Öffnen Sie die Branding-Seite der Google Auth Platform.
    2. Klicken Sie bei Aufforderung auf Jetzt starten. Wenn Ihr Zustimmungsbildschirm bereits konfiguriert ist, fahren Sie mit Schritt 3 fort.
    3. Geben Sie unter App-Informationen Support Extension für den App-Namen ein, wählen Sie Ihre E‑Mail-Adresse für Nutzersupport aus und klicken Sie auf Weiter.
    4. Wählen Sie unter Zielgruppe die Option Intern aus und klicken Sie auf Weiter.
    5. Geben Sie unter Kontaktdaten Ihre E‑Mail-Adresse ein und klicken Sie auf Weiter.
    6. Klicken Sie auf Abschließen, akzeptieren Sie die Richtlinie zu Nutzerdaten für Google API-Dienste, klicken Sie auf Weiter und dann auf Erstellen.
  3. Legen Sie in Cloud Shell Ihr aktives Projekt fest (ersetzen Sie YOUR_PROJECT_ID durch Ihre Google Cloud-Projekt-ID) und aktivieren Sie die erforderlichen APIs:
    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. Stellen Sie Ihre (default)-Firestore-Datenbank in us-central1 bereit (falls sie noch nicht vorhanden ist) und initialisieren Sie Ihr Google Workspace-Add-ons-Dienstkonto:
    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. Node.js-App mit Firestore und Gemini 3.8 Flash erstellen

In diesem Schritt erstellen Sie die Node.js-Anwendung in ~/studio-support-addon. Damit der Code modular und lesbar bleibt, organisieren Sie das Projekt in fünf Dateien:

  • firestore.js: Initialisiert den Cloud Firestore-Client (studio_triggers- und support_tickets-Sammlungen) und aktualisiert zwischengespeicherte OAuth-Tokens.
  • starter.js: Implementiert die Webhooks für den benutzerdefinierten Starter-Schritt Neues Support-Ticket (/onConfigTrigger und /onManageTrigger).
  • action.js: Implementiert die Webhooks für den benutzerdefinierten Aktionsschritt Detect Urgency (/onConfigUrgency und /onExecuteUrgency) mit Gemini 3.8 Flash (gemini-3.8-flash) und einem Fallback für den Keyword-Abgleich.
  • portal.js: Stellt die Web-UI des Support Quick Portal (GET /) bereit und löst aktive Google Workspace Studio-Trigger (POST /api/submit-ticket) aus.
  • index.js: Verbindet alle Express-Routen und startet den HTTP-Server.

Projekt und package.json initialisieren

  1. Erstellen Sie ein Projektverzeichnis und rufen Sie es auf:
    mkdir -p ~/studio-support-addon && cd ~/studio-support-addon
    
  2. Erstellen Sie package.json mit express, @google-cloud/firestore und @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 – Gemeinsamer Firestore-Status und Aktualisierung von OAuth-Tokens

Erstellen Sie firestore.js, um Ihre beiden Firestore-Sammlungen zu verwalten und zwischengespeicherte OAuth-Tokens für Nutzer aktuell zu halten:

  • studio_triggers: Hier werden aktive Google Workspace Studio-Abo-Flows (triggerId, notifyUri und userOAuthToken) gespeichert, damit sie Cloud Run-Containerneustarts und Skalierung-auf-null-Ereignisse überstehen.
  • support_tickets: Speichert einen Datensatz für jedes Support-Ticket, das über das Portal eingereicht wurde.
  • refreshTokenInFirestore(req): Wenn Google Workspace Studio einen Ihrer Webhook-Endpunkte aufruft, wird ein kurzlebiger authorizationEventObject.userOAuthToken eingefügt. Mit diesem Helfer werden aktive Triggerdokumente mit dem neuesten Token aktualisiert, damit triggers.fire-Hintergrundaufrufe autorisiert bleiben.
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 – Benutzerdefinierter Auslöserschritt (workflowTrigger)

Erstellen Sie starter.js, um die beiden Webhook-Endpunkte zu implementieren, die für einen benutzerdefinierten Starter-Schritt in Google Workspace Studio erforderlich sind:

  • onConfigTrigger (POST /onConfigTrigger): Wird aufgerufen, wenn ein Nutzer Neues Support-Ticket als Starter für den Flow in Google Workspace Studio auswählt. Gibt eine pushCard-Navigationsaktion zurück, die den Starter beschreibt.
  • onManageTrigger (POST /onManageTrigger): Wird aufgerufen, wenn ein Nutzer einen Ablauf aktiviert (req.body.workflow.triggerCreation) oder deaktiviert / löscht (req.body.workflow.triggerDeletion). Speichert oder löscht das Abo-Dokument in der Firestore-Sammlung 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 – Benutzerdefinierter Aktionsschritt (workflowAction) mit Gemini 3.8 Flash

Erstellen Sie action.js, um die beiden Webhook-Endpunkte zu implementieren, die für einen benutzerdefinierten Aktionsschritt in Google Workspace Studio erforderlich sind:

  • onConfigUrgency (POST /onConfigUrgency): Gibt eine Konfigurationskarte mit einem textInput-Widget zurück, das hostAppDataSource.workflowDataSource.includeVariables: true festlegt. So können Nutzer die ticketDescription-Variable auswählen, die vom Startschritt ausgegeben wird.
  • onExecuteUrgency (POST /onExecuteUrgency): Liest die ticketDescription-Eingabe von req.body.workflow.actionInvocation.inputs, klassifiziert ihre geschäftlichen Auswirkungen und Dringlichkeit mit Gemini 3.8 Flash (gemini-3.8-flash) auf der Gemini Enterprise Agent Platform mit strukturierter Ausgabe (responseSchema-Enum ["High", "Normal"]), greift auf detectKeywordUrgency() (urgent, blocker, critical) zurück, wenn der Modellaufruf fehlschlägt, und gibt die urgencyLevel-Ausgabevariable mit hostAppAction.workflowAction.returnOutputVariablesAction zurück.
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 – Unterstützung für die Quick Portal-Benutzeroberfläche und den triggers.fire-Dispatcher

Erstellen Sie portal.js, um das Webportal bereitzustellen und Ticketanfragen an Google Workspace Studio zu senden:

  • renderPortal (GET /): Fragt die Firestore-Sammlung studio_triggers ab, um die Anzahl der registrierten aktiven Google Workspace Studio-Trigger anzuzeigen, und stellt das HTML-Formular für die Ticketübermittlung bereit.
  • submitTicket (POST /api/submit-ticket): Durchläuft alle aktiven Triggerdokumente in studio_triggers, ruft den Google Workspace Studio-Endpunkt triggers.fire (https://workspacestudio.googleapis.com/v1/triggers/{triggerId}:fire) mit den Ausgabevariablen ticketTitle und ticketDescription sowie einer eindeutigen requestId auf und speichert das Ticket in der Firestore-Sammlung support_tickets.
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 – Express-Server-Einstiegspunkt

Erstellen Sie index.js, um Ihre Portalrouten und Google Workspace Studio-Webhook-Endpunkte in einem Express-HTTP-Server zu verbinden, der an PORT (Standard 8080 in Cloud Run) lauscht:

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. In Cloud Run bereitstellen und Add-on installieren

In diesem Schritt stellen Sie den Node.js-Dienst in Cloud Run bereit, erteilen IAM-Berechtigungen für den Aufrufer von Google Workspace-Add-ons sowie für den Firestore- und Agent Platform-Laufzeitzugriff, generieren das deployment.json-Manifest und installieren die Add-on-Bereitstellung.

  1. Stellen Sie den support-extension-Dienst mit Ihren Umgebungsvariablen für Projekt und Firestore-Sammlung in Cloud Run bereit:
    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. Erteilen Sie dem Google Workspace-Dienstkonto für Add-ons die Rolle roles/run.invoker für den Cloud Run-Dienst, damit Google Workspace Studio Ihre Webhook-Endpunkte aufrufen kann:
    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. Weisen Sie dem Cloud Run-Laufzeitdienstkonto roles/datastore.user (zum Lesen und Schreiben der Firestore-Sammlungen studio_triggers und support_tickets) und roles/aiplatform.user (zum Aufrufen von Gemini auf der Gemini Enterprise Agent Platform) zu:
    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. Rufen Sie die bereitgestellte Cloud Run-URL ab und generieren Sie das deployment.json-Manifest, in dem die Elemente supportTicketTrigger (workflowTrigger) und urgencyDetectorStep (workflowAction) definiert sind:
    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. Erstellen (oder ersetzen) und installieren Sie die Google Workspace-Add-on-Bereitstellung für Ihr Konto und drucken Sie die URL des Support Quick Portal:
    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. Benutzerdefinierte Schritte in Google Workspace Studio testen

  1. Öffnen Sie Google Workspace Studio (aktualisieren Sie den Browsertab, falls er bereits geöffnet war) und klicken Sie auf Neuer Flow.
  2. Klicken Sie auf Starter auswählen > Support-Erweiterung > Neues Support-Ticket.
  3. Klicken Sie auf Schritt hinzufügen (+) > Support-Erweiterung > Dringlichkeit erkennen. Klicken Sie im Feld Quellbeschreibung auf + Variablen und wählen Sie Ticketbeschreibung aus.
  4. Klicken Sie auf Schritt hinzufügen (+) und fügen Sie den Schritt Chat > Mich in Google Chat benachrichtigen hinzu. Die Nachricht sollte die Variablen Ticket Title (Ticket-Titel), Urgency Level (Dringlichkeitsstufe) und Ticket Description (Ticketbeschreibung) enthalten.
  5. Stellen Sie den Flow rechts oben auf Ein. Google Workspace Studio ruft sofort Ihren /onManageTrigger-Endpunkt in Cloud Run auf, der das aktive Trigger-Abo in die studio_triggers-Sammlung in Firestore schreibt.
  6. Öffnen Sie die Support Quick Portal-URL (https://support-extension-...run.app) in einem neuen Tab, bestätigen Sie, dass im Portal der aktive Trigger 1 in Firestore angezeigt wird, und senden Sie zwei Tickets, um die Klassifizierung von Gemini 3.8 Flash (gemini-3.8-flash) zu testen:
    • Ticket mit hoher Priorität (Titel des Tickets: London office lockout, Beschreibung des Problems: All 500 employees in the London office are locked out of their workstations 20 minutes before the board presentation.): Auch ohne fest codierte Keywords wie urgent oder blocker stuft gemini-3.8-flash dieses Ticket als High ein.
    • Routine-Ticket (Ticket Title: Conference room adapters, Issue Description: Could we order two extra HDMI adapters for the third-floor conference room next month?): gemini-3.8-flash klassifiziert dieses Ticket als Normal.
  7. Prüfen Sie in Google Chat und auf dem Tab Aktivität in Google Workspace Studio, ob Ihr Flow für beide Tickets mit Urgency Level: High und Urgency Level: Normal ausgeführt wurde.

6. Bereinigen

So vermeiden Sie, dass Ihrem Google Cloud-Konto laufende Gebühren in Rechnung gestellt werden:

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. Glückwunsch!

Sie haben eine Node.js-HTTP-Erweiterung für Google Workspace Studio mit Firestore, Cloud Run und Gemini 3.8 Flash auf der Gemini Enterprise Agent Platform erstellt, bereitgestellt und getestet.

Behandelte Themen

  • Konfiguration eines Google Workspace Studio-HTTP-Add-on-Manifests (deployment.json) mit workflowTrigger- und workflowAction-Elementen.
  • Google Workspace Studio-Abos workflowTrigger in der studio_triggers-Sammlung Firestore beibehalten und Portaleinsendungen in support_tickets protokollieren, damit Trigger Cloud Run-Skalierungen und ‑Neubereitstellungen überstehen.
  • Erstellen eines workflowAction-Schritts, der Support-Tickets mit Gemini 3.8 Flash (gemini-3.8-flash) mit strukturierter Ausgabe auf der Gemini Enterprise Agent Platform klassifiziert, mit einem robusten Fallback für den Keyword-Abgleich.

Nächste Schritte