1. 簡介
在本程式碼研究室中,您將建構可直接與 Google Workspace Studio 互動的支援快速入口。您將瞭解如何建構自訂起始步驟和自訂動作步驟,不必依賴任何第三方基礎架構,即可立即測試。
我們會使用 Google Apps Script 建立:
- 網頁應用程式:提供簡潔的表單,供使用者手動提交支援單。
- 自訂啟動條件:擷取這些網頁應用程式提交內容,並觸發 Workspace Studio 工作流程。
- 自訂步驟:分析傳入工單的說明,判斷緊急程度 (「高」或「一般」)。
課程內容
- 如何在
appsscript.json資訊清單中設定啟動器和步驟。 - 如何在入門版註冊生命週期中建構
notifyUri。 - 如何使用
UrlFetchApp從外部程式碼以程式輔助方式觸發 Workspace Studio 工作流程。 - 如何在自訂步驟中建構輸出變數,將資料傳遞至下游。
必要條件
- 已啟用 Google Workspace Studio 的 Google Workspace 帳戶。
- 在網域的管理控制台中啟用「允許使用未發布 (測試) 的自訂步驟」設定 (依序前往「應用程式」>「Google Workspace」>「Workspace Studio」>「自訂步驟設定」)。
- 熟悉 Google Apps Script。
2. 設定 Apps Script 專案
首先,請建立新的 Apps Script 專案來存放程式碼:
- 前往 script.google.com,然後按一下「新專案」。
- 將專案命名為「Support Quick Portal」。
- 按一下左側邊欄中的「專案設定」
。
- 勾選「在編輯器中顯示『appsscript.json』資訊清單檔案」。
- 返回「編輯器」
檢視畫面。
連結標準 Google Cloud 專案
根據預設,Apps Script 專案會與隱藏的預設 Google Cloud 雲端專案建立關聯。如要呼叫 Google Workspace Studio API 並避免 PERMISSION_DENIED 錯誤,您必須將指令碼切換至標準雲端專案、啟用 API,並正確設定 Google Auth Platform。
- 選取或建立專案:前往 Google Cloud 控制台,然後使用頁面頂端的專案下拉式選單,選擇現有專案或建立新專案。
- 啟用 API:在頂端列中啟用所選專案後,開啟 API 啟用流程,然後依序點選「下一步」和「啟用」,啟用 Workspace Studio API。
- 設定 OAuth 品牌宣傳:開啟 Google Auth Platform 品牌宣傳頁面。
- 如果系統提示,請按一下「開始使用」。(如已設定,請跳到步驟 4)。
- 在「App Information」(應用程式資訊) 下方,輸入應用程式名稱和使用者支援電子郵件地址。點選「下一步」。
- 在「目標對象」下方,選取「內部」 (或「外部」,如果沒有「內部」選項),然後點選「下一步」。
- 提供聯絡資訊、同意資料政策,然後點按「建立」。
- 設定資料存取權:開啟「資料存取」頁面,然後按一下「新增或移除範圍」。
- 在「手動新增範圍」下方,貼上
https://www.googleapis.com/auth/workspace.studio.trigger。 - 依序點選「新增至表格」和「更新」,然後按一下頁面底部的「儲存」。
- 在「手動新增範圍」下方,貼上
- 前往「IAM & Admin」>「Settings」(IAM 與管理 > 設定) (或 Cloud Console 資訊主頁),然後複製「Project number」(專案編號)。
- 返回 Apps Script 專案,然後按一下左側邊欄的「專案設定」
。
- 在「Google Cloud Platform (GCP) 專案」部分下方,點選「變更專案」。
- 輸入您複製的「GCP 專案編號」,然後按一下「設定專案」。
設定資訊清單
開啟新顯示的 appsscript.json 檔案。將其內容替換為下列程式碼。這份資訊清單會明確列出必要的 OAuth 範圍,並在 studio.flows.workflowElements 區塊下,將啟動條件定義為 workflowTrigger,動作定義為 workflowAction。
{
"timeZone": "America/Los_Angeles",
"exceptionLogging": "STACKDRIVER",
"runtimeVersion": "V8",
"webapp": {
"executeAs": "USER_ACCESSING",
"access": "MYSELF"
},
"oauthScopes": [
"https://www.googleapis.com/auth/script.external_request",
"https://www.googleapis.com/auth/workspace.studio.trigger",
"https://www.googleapis.com/auth/script.storage",
"https://www.googleapis.com/auth/script.locale"
],
"urlFetchWhitelist": [
"https://workspacestudio.googleapis.com/"
],
"addOns": {
"common": {
"name": "Support Extension",
"logoUrl": "https://www.gstatic.com/images/branding/productlogos/buganizer/v1/192px.svg",
"useLocaleFromApp": true
},
"studio": {
"flows": {
"workflowElements": [
{
"id": "supportTicketTrigger",
"state": "ACTIVE",
"name": "New Support Ticket",
"description": "Fires when someone submits a ticket via the Web App.",
"workflowTrigger": {
"inputs": [],
"outputs": [
{
"id": "ticketTitle",
"description": "The title of the ticket",
"cardinality": "SINGLE",
"dataType": { "basicType": "STRING" }
},
{
"id": "ticketDescription",
"description": "The full description of the issue",
"cardinality": "SINGLE",
"dataType": { "basicType": "STRING" }
}
],
"onConfigFunction": "onConfigTrigger",
"onManageFunction": "onManageTrigger"
}
},
{
"id": "urgencyDetectorStep",
"state": "ACTIVE",
"name": "Detect Urgency",
"description": "Scans the ticket description to detect High or Normal urgency.",
"workflowAction": {
"inputs": [
{
"id": "ticketDescription",
"description": "The ticket description",
"cardinality": "SINGLE",
"dataType": { "basicType": "STRING" }
}
],
"outputs": [
{
"id": "urgencyLevel",
"description": "The urgency High/Normal",
"cardinality": "SINGLE",
"dataType": { "basicType": "STRING" }
}
],
"onConfigFunction": "onConfigUrgency",
"onExecuteFunction": "onExecuteUrgency"
}
}
]
}
}
}
}
3. 建構網頁應用程式 UI
為了提供觸發啟動器的視覺化測試方式,不必使用終端機工具或第三方 Webhook,我們將建構 Apps Script 網頁應用程式。
在 Apps Script 指令碼編輯器中,按一下「檔案」旁的 「新增檔案」,然後選取「HTML」。將其命名為
index.html (系統會自動新增 .html 擴充功能)。
貼上下列簡化的 UI 程式碼:
<!DOCTYPE html>
<html>
<head>
<base target="_top">
<style>
body { font-family: Arial, sans-serif; padding: 20px; max-width: 500px; margin: auto; }
label { font-weight: bold; display: block; margin-top: 15px; }
input, textarea { width: 100%; margin-top: 5px; padding: 10px; box-sizing: border-box; }
button { background-color: #1a73e8; color: white; border: none; padding: 10px 15px; margin-top: 15px; font-weight: bold; cursor: pointer; border-radius: 4px; }
button:hover { background-color: #1557b0; }
#status { margin-top: 15px; color: green; font-weight: bold; }
</style>
</head>
<body>
<h2>Support Quick Portal</h2>
<p>Submit your issue below to trigger the Workspace Studio flow.</p>
<label for="title">Ticket Title</label>
<input type="text" id="title" placeholder="e.g. Broken monitor">
<label for="description">Issue Description</label>
<textarea id="description" rows="4" placeholder="Explain your issue (use words like 'urgent' to test the step!)..."></textarea>
<button onclick="submitTicket()">Submit Ticket</button>
<div id="status"></div>
<script>
function submitTicket() {
const title = document.getElementById('title').value;
const description = document.getElementById('description').value;
const statusEl = document.getElementById('status');
if (!title || !description) {
statusEl.style.color = "red";
statusEl.innerText = "Please fill out both fields.";
return;
}
statusEl.style.color = "green";
statusEl.innerText = "Submitting ticket...";
google.script.run
.withSuccessHandler(function(response) {
statusEl.innerText = response;
})
.withFailureHandler(function(error) {
statusEl.style.color = 'red';
statusEl.innerText = 'Error: ' + error.message;
})
.submitTicketToStudio(title, description);
}
</script>
</body>
</html>
4. 建構後端指令碼
現在來整合所有內容。為確保程式碼可讀性,我們會將 Web 應用程式、Starter 邏輯和動作步驟邏輯分成三個不同的指令碼檔案。
WebApp.gs
在編輯器中將預設的 Code.gs 檔案重新命名為 WebApp.gs,刪除樣板 myFunction,然後複製下列程式碼區塊並貼入其中。這個檔案會處理網頁應用程式代管作業:
/** --- WEB APP HOSTING --- **/
/**
* Serves the HTML UI when users visit the web app URL.
*/
function doGet() {
return HtmlService.createHtmlOutputFromFile('index')
.setTitle('Support Quick Portal')
.setXFrameOptionsMode(HtmlService.XFrameOptionsMode.ALLOWALL);
}
/**
* Called by the Web App form. It calls the Workspace Studio API.
*/
function submitTicketToStudio(title, description) {
const props = PropertiesService.getUserProperties();
const notifyUri = props.getProperty('notifyUri');
const triggerId = props.getProperty('triggerId');
if (!notifyUri || !triggerId) {
throw new Error('Trigger URL missing! Make sure the flow is enabled in Workspace Studio.');
}
// Use ScriptApp.getOAuthToken() (requires the workspace.studio.trigger scope in manifest)
const token = ScriptApp.getOAuthToken();
const requestId = Utilities.getUuid();
// Package our outputs mapped in appsscript.json
const payload = {
"name": "triggers/" + triggerId,
"outputs": {
"ticketTitle": { "stringValues": [title] },
"ticketDescription": { "stringValues": [description] }
},
"requestId": requestId
};
const options = {
"method": "POST",
"contentType": "application/json",
"headers": { "Authorization": "Bearer " + token },
"payload": JSON.stringify(payload),
"muteHttpExceptions": true
};
// Fire!
const response = UrlFetchApp.fetch(notifyUri, options);
if (response.getResponseCode() !== 200) {
console.error(response.getContentText());
throw new Error('Workspace Studio API Error: ' + response.getResponseCode());
}
return 'Ticket successfully routed to Workspace Studio!';
}
Starter.gs
接著,將游標懸停在「檔案」上,按一下「新增檔案」,選取「指令碼」,然後將其命名為
Starter.gs。複製下列程式碼區塊並貼到該檔案中。這個檔案會處理起始範本設定和 Workspace Studio 生命週期管理:
/** --- STARTER LOGIC --- **/
/**
* Workspace Studio calls this to display the config UI when the user adds the starter.
* We just return a static message since there are no properties to configure.
*/
function onConfigTrigger() {
const section = CardService.newCardSection()
.setHeader("Configure Web App Starter")
.addWidget(CardService.newTextParagraph().setText("Ready to go! Once you enable this flow, deploy your Apps Script project as a Web App to submit tickets."));
return CardService.newCardBuilder().addSection(section).build();
}
/**
* Workspace Studio posts data here to manage the lifecycle (enable/disable the trigger).
* Critically, we must construct or save the `notifyUri` and `triggerId` to call it later.
*/
function onManageTrigger(event) {
const triggerCreation = event.workflow.triggerCreation;
const triggerDeletion = event.workflow.triggerDeletion;
const props = PropertiesService.getUserProperties();
if (triggerCreation) {
// Save the unique URL and ID when the flow is enabled
// Note: notifyUri might be omitted, so fallback to constructing it dynamically
const notifyUri = triggerCreation.notifyUri || `https://workspacestudio.googleapis.com/v1/triggers/${triggerCreation.triggerId}:fire`;
props.setProperty('notifyUri', notifyUri);
props.setProperty('triggerId', triggerCreation.triggerId);
} else if (triggerDeletion) {
// Cleanup if the user deletes the flow
props.deleteProperty('notifyUri');
props.deleteProperty('triggerId');
}
}
Action.gs
接著,將游標懸停在「檔案」上,按一下「新增檔案」,選取「指令碼」,然後將其命名為
Action.gs。複製下列「緊急程度步驟」邏輯並貼到其中:
/** --- ACTION STEP LOGIC --- **/
/**
* Workspace Studio configuration card for the Step.
* We prompt the user to bind a variable to our "ticketDescription" input.
*/
function onConfigUrgency() {
const input = CardService.newTextInput()
.setFieldName("ticketDescription")
.setTitle("Source Description")
.setHint('Select the description variable outputted by the starter')
.setHostAppDataSource(
CardService.newHostAppDataSource().setWorkflowDataSource(CardService.newWorkflowDataSource().setIncludeVariables(true))
);
const section = CardService.newCardSection()
.setHeader("Urgency Detector Step")
.addWidget(input);
return CardService.newCardBuilder().addSection(section).build();
}
/**
* The synchronous logic executed during the Step.
*/
function onExecuteUrgency(event) {
// Read mapped inputs
let description = event.workflow.actionInvocation.inputs["ticketDescription"].stringValues[0];
if (!description) description = "";
const urgentKeywords = ['urgent', 'crash', 'broken', 'down', 'fire'];
let urgencyLevel = 'Normal';
if (urgentKeywords.some(keyword => description.toLowerCase().includes(keyword))) {
urgencyLevel = 'High';
}
// Package outputs correctly
const variableDataMap = {
"urgencyLevel": AddOnsResponseService.newVariableData().addStringValue(urgencyLevel)
};
return outputVariables(variableDataMap);
}
/**
* Helper strictly enforcing the expected Step object return type.
*/
function outputVariables(variableDataMap) {
const workflowAction = AddOnsResponseService.newReturnOutputVariablesAction()
.setVariableDataMap(variableDataMap);
const hostAppAction = AddOnsResponseService.newHostAppAction()
.setWorkflowAction(workflowAction);
return AddOnsResponseService.newRenderActionBuilder()
.setHostAppAction(hostAppAction)
.build();
}
5. 部署及測試
程式碼編寫完成後,請部署及設定專案。
步驟 1:重新授權指令碼
由於您連結了新的標準 Google Cloud 專案,因此必須針對新專案明確授權指令碼的範圍 (例如 script.external_request 使用的 UrlFetchApp)。
- 在 Apps Script 指令碼編輯器中,選取「檔案」下方的
WebApp.gs,然後從頂端工具列下拉式選單中選取doGet函式。 - 按一下「執行」。
- 系統會顯示「需要授權」提示。按一下「Review permissions」(查看權限)。
- 選擇帳戶並授權指令碼。如果看到「未經驗證的應用程式」警告,請依序點選「進階」->「前往支援快速入口」來略過警告。
- 等待「執行記錄」顯示「執行完成」,確認指令碼已獲得完整授權。
步驟 2:測試擴充功能和網頁應用程式
由於 appsscript.json 同時定義外掛程式和 Web 應用程式,因此您可以同時設定兩者。
- 在 Apps Script 編輯器中,依序點選右上角的「部署」>「測試部署作業」。
- 按一下「選取類型」旁的
「啟用部署類型」,確認已勾選「Google Workspace 外掛程式」,然後按一下「安裝」。
- 再次點按「啟用部署類型」
,確認已勾選「網頁應用程式」,然後複製「網址」 (結尾為
/dev)。這是您建立的「支援快速入口」網頁應用程式網址。 - 按一下「完成」,您可能需要重新整理瀏覽器,Workspace Studio 才會註冊外掛程式。
步驟 3:設定 Studio Flow
- 前往 studio.workspace.google.com 開啟 Google Workspace Studio。
- 按一下左側的「新增工具」圖示
,然後選取「流程」。
- 如要設定啟動器,請點選「選擇啟動器」,然後依序選取已安裝的「支援擴充功能」 >「建立新的支援單」。如果系統顯示「需要權限」訊息,請按一下「授予權限」,允許外掛程式存取資料。
- 接著,在「動作」下方點選「選擇步驟」,捲動至「支援擴充功能」,然後選取「偵測緊急程度」步驟。
- 在「來源說明」中,按一下「變數」圖示
,然後依序選取「步驟 1:新支援單」 >「問題的完整說明」圖示
ticketDescription。 - 再次點按「選擇步驟」,然後選取內建步驟,例如「Gmail」>「傳送電子郵件」或「Chat」>「在 Chat 中通知我」。
- 如果選擇 Gmail:
- 收件者:輸入自己的電子郵件地址。
- 主旨:支援單
ticketTitle優先順序為「urgencyLevel」 - 訊息:
ticketDescription
- 如果選擇透過即時通訊聯絡:
- 訊息:票證
ticketTitle優先順序為urgencyLevel。ticketDescription
- 訊息:票證
- 如果選擇 Gmail:
- 啟用工作流程。如果系統提示重新命名流程,請輸入名稱 (例如
Support Ticket Router) 並儲存。這會觸發onManageTrigger生命週期,儲存notifyUri。
步驟 4:執行模擬
- 在新的瀏覽器分頁中,開啟您稍早在本頁面步驟 2 複製的 Web 應用程式網址 (結尾為 /dev)。
- 填寫表單。請務必在說明中加入觸發關鍵字 (例如「緊急」或「當機」)。
- 按一下「提交」。等待系統顯示「Ticket successfully routed」(支援單已成功轉送) 訊息。
- 查看 Workspace Studio 活動記錄、Gmail 收件匣或 Google Chat (視您選擇的動作而定)。您應該會看到以自訂 Apps Script 元素原生整合的「緊急」訊息或電子郵件。
6. 清除
為避免工作區和 Google Cloud 帳戶雜亂無章,您可以清理在本 Codelab 建立的資源。
- 刪除 Workspace Studio 工作流程:
- 前往 studio.workspace.google.com。
- 掌握
Support Ticket Router敘事節奏。 - 按一下旁邊的三點選單,然後選取「刪除」。
- 刪除 Apps Script 專案:
- 前往 script.google.com。
- 找出
Support Quick Portal專案。 - 按一下三點圖示選單,然後選取「移除」。
- 關閉 Google Cloud 專案:
- 前往 Google Cloud 控制台。
- 確認頂端下拉式選單中已選取新專案。
- 依序前往「IAM 與管理」>「設定」,然後按一下「關閉」。
7. 結語
您已成功為 Google Workspace Studio 自訂擴充功能建構完整的生態系統,完全不需依賴任何外部第三方訂閱項目。
課程內容:
- 搭配使用
workflowTrigger和onManageFunction,正確擷取及處理 Google 的生命週期事件。 - 使用
ScriptApp.getOAuthToken()對workspacestudio.googleapis.comAPI 端點驗證後端要求。 - 使用
workflowAction同步處理輸入變數,並產生下游輸出內容。
現在,您可以修改 Web 應用程式 (或傳入的 Webhook) 的觸發位置,將這些原則水平擴展至 Jira、Salesforce 或 Zendesk 等實際的 SaaS 平台。