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:
- 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 RESTtriggers.firedi Google Workspace Studio. - 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 raccoltastudio_triggersFirestore, registra i ticket inviati in una raccoltasupport_ticketsFirestore e viene attivato ogni volta che un ticket viene inviato nel portale web, passandoticketTitleeticketDescriptiona 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 elementiworkflowTriggereworkflowAction. - Come rendere persistenti gli abbonamenti ai trigger di Google Workspace Studio (
triggerId,notifyUrieuserOAuthToken) in una raccoltastudio_triggersFirestore e attivare i flussi utilizzando l'API RESTtriggers.fire. - Come creare un passaggio personalizzato
workflowActionin 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.
- 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.
- 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:
- Apri la pagina del branding di Google Auth Platform.
- Se richiesto, fai clic su Inizia. Se la schermata per il consenso è già configurata, vai al passaggio 3.
- Nella sezione Informazioni sull'app, inserisci
Support Extensionper il nome dell'app, seleziona l'email per l'assistenza utenti e fai clic su Avanti. - In Pubblico, seleziona Interno e fai clic su Avanti.
- Nella sezione Dati di contatto, inserisci il tuo indirizzo email e fai clic su Avanti.
- In Fine, accetta le Norme relative ai dati utente: servizi API di Google, fai clic su Continua e poi su Crea.
- In Cloud Shell, imposta il progetto attivo (sostituisci
YOUR_PROJECT_IDcon 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 - Esegui il provisioning del database Firestore di
(default)inus-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 (raccoltestudio_triggersesupport_tickets) e aggiorna i token OAuth memorizzati nella cache.starter.js: implementa i webhook del passaggio iniziale personalizzato Nuovo ticket di assistenza (/onConfigTriggere/onManageTrigger).action.js: implementa i webhook del passaggio dell'azione personalizzata Rileva urgenza (/onConfigUrgencye/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
- Crea una directory di progetto e accedi alla directory:
mkdir -p ~/studio-support-addon && cd ~/studio-support-addon - Crea
package.jsonconexpress,@google-cloud/firestoree@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,notifyUrieuserOAuthToken) 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 unauthorizationEventObject.userOAuthTokendi breve durata. Questo helper aggiorna i documenti di attivazione attivi con il token più recente in modo che le chiamatetriggers.firein 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 navigazionepushCardche 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 Firestorestudio_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 widgettextInputche impostahostAppDataSource.workflowDataSource.includeVariables: true, consentendo agli utenti di selezionare la variabileticketDescriptionrestituita dal passaggio iniziale.onExecuteUrgency(POST /onExecuteUrgency): legge l'inputticketDescriptiondareq.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 (responseSchemaenum["High", "Normal"]), esegue il failover sudetectKeywordUrgency()(urgent,blocker,critical) se la chiamata al modello non va a buon fine e restituisce la variabile di outputurgencyLevelutilizzandohostAppAction.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 raccoltastudio_triggersFirestore 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 instudio_triggers, richiama l'endpointtriggers.firedi Google Workspace Studio (https://workspacestudio.googleapis.com/v1/triggers/{triggerId}:fire) con le variabili di outputticketTitleeticketDescriptione unrequestIdunivoco e salva il ticket nella raccoltasupport_ticketsdi 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.
- Esegui il deployment del servizio
support-extensionsu 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 - Concedi
roles/run.invokeral 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 - Concedi
roles/datastore.user(per la lettura e la scrittura delle raccolte Firestorestudio_triggersesupport_tickets) eroles/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 - Recupera l'URL Cloud Run di cui è stato eseguito il deployment e genera il manifest
deployment.jsonche definisce gli elementisupportTicketTrigger(workflowTrigger) eurgencyDetectorStep(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 - 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
- Apri Google Workspace Studio (aggiorna la scheda del browser se era già aperta) e fai clic su Nuovo flusso.
- Fai clic su Seleziona un punto di partenza > Estensione dell'assistenza > Nuovo ticket di assistenza.
- Fai clic su Aggiungi passaggio (
+) > Estensione di supporto > Rileva urgenza. Nel campo Descrizione origine, fai clic su + Variabili e seleziona Descrizione ticket. - 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. - Attiva il flusso On nell'angolo in alto a destra. Google Workspace Studio chiama immediatamente l'endpoint
/onManageTriggersu Cloud Run, che scrive l'abbonamento al trigger attivo nella raccoltastudio_triggersin Firestore. - 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 comeurgentoblocker,gemini-3.8-flashclassifica questo ticket come urgenzaHigh. - 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-flashclassifica questo ticket come urgenzaNormal.
- Ticket ad alto impatto (Titolo del ticket:
- 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: HigheUrgency 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 elementiworkflowTriggereworkflowAction. - Mantenere gli abbonamenti a Google Workspace Studio
workflowTriggernella raccoltastudio_triggersFirestore e gli invii del portale di logging insupport_ticketsin modo che i trigger sopravvivano al ridimensionamento e ai redeployment di Cloud Run. - Creazione di un passaggio
workflowActionche 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.