১. ভূমিকা
Google Workspace Studio আপনাকে Gmail, Google Chat, Google Drive ও Google Sheets-এর মতো Google Workspace অ্যাপ্লিকেশন জুড়ে ব্যবসার প্রসেস অটোমেট করতে দেয়। আপনার ওয়ার্কফ্লো কোনও এক্সটার্নাল ওয়েব অ্যাপ্লিকেশন থেকে শুরু করতে হলে অথবা আপনার নিজস্ব ক্লাউড এনভায়রনমেন্টে কাস্টম লজিক রান করতে হলে, আপনি Cloud Run-এ Node.js-এ HTTP Google Workspace অ্যাড-অন-এর মাধ্যমে Google Workspace Studio এক্সটেন্ড করতে পারবেন:
- কাস্টম স্টার্টার ধাপ (
workflowTrigger): কোনও এক্সটার্নাল ইভেন্ট ঘটলে Google Workspace Studio ফ্লো শুরু করে—যেমন কোনও গ্রাহক ইন্টার্নাল ওয়েব পোর্টালে টিকিট জমা দিলে—ট্রিগারেরnotifyUri(বা Google Workspace Studiotriggers.fireREST API) কল করে। - কাস্টম অ্যাকশন স্টেপ (
workflowAction): ফ্লোতে আগের স্টেপ থেকে ভেরিয়েবল গ্রহণ করে, আপনার Node.js পরিষেবায় কাস্টম লজিক বা AI ইনফারেন্স রান করে এবং Google Workspace Studio-তে আউটপুট ভেরিয়েবল ফিরিয়ে দেয়।
এই কোডল্যাবে, আপনি Cloud Run-এ Google Workspace Studio-এর জন্য একটি সম্পূর্ণ Node.js HTTP এক্সটেনশন তৈরি এবং ডিপ্লয় করবেন, কন্টেনার রিভিশন জুড়ে অ্যাক্টিভ ফ্লো ট্রিগার সাবস্ক্রিপশন এবং জমা দেওয়া টিকিট বজায় রাখতে Cloud Firestore ব্যবহার করবেন, notifyUri সফল রেসপন্স না পাঠালে ট্রিগার বিজ্ঞপ্তি অটোমেটিক আবার চেষ্টা করতে Cloud Tasks ব্যবহার করবেন এবং Gemini Enterprise Agent Platform-এ Gemini 3.8 Flash (gemini-3.8-flash) ব্যবহার করে আপনার কাস্টম অ্যাকশন ধাপে টিকিটের জরুরি অবস্থা শ্রেণীবদ্ধ করবেন, যার সাথে একটি স্থিতিস্থাপক কীওয়ার্ড-ম্যাচিং ফলব্যাক থাকবে।
আপনি কী তৈরি করবেন
- Cloud Run-এ Node.js, Express, Firestore ও Cloud Tasks-এর মাধ্যমে তৈরি করা একটি Support Quick Portal ওয়েব অ্যাপ ও HTTP Google Workspace অ্যাড-অন।
- নতুন সহায়তা টিকিট কাস্টম স্টার্টার স্টেপ (
supportTicketTrigger) যাstudio_triggersFirestore কালেকশনে অ্যাক্টিভ ফ্লো সাবস্ক্রিপশন বজায় রাখে,support_ticketsFirestore কালেকশনে জমা দেওয়া টিকিট লগ করে এবং ওয়েব পোর্টালে টিকিট জমা দেওয়া হলে ফায়ার করে—ticketTitleওticketDescriptionGoogle Workspace Studio ফ্লোতে পাস করে। - একটি Cloud Tasks-এর আবার চেষ্টা করার ওয়ার্কার (
studio-trigger-retriesসারি এবংPOST /api/tasks/fire-trigger) যা Firestore-এ সেভ করা লেটেস্টuserOAuthTokenব্যবহার করে এক্সপোনেনশিয়াল ব্যাকঅফ সহ ব্যর্থ ট্রিগার কলগুলিকে সারিবদ্ধ করে এবং আবার চেষ্টা করে,notifyUri-এ ডেলিভারির গ্যারান্টি দেয়। - জরুরি অবস্থা শনাক্ত করুন কাস্টম অ্যাকশন স্টেপ (
urgencyDetectorStep) যা Gemini Enterprise Agent Platform (aiplatform.googleapis.com)-এ Gemini 3.8 Flash (gemini-3.8-flash) ব্যবহার করে টিকিটের জরুরি অবস্থা শ্রেণীবদ্ধ করে, সাথে কীওয়ার্ড ম্যাচিংয়ের (urgent,blocker,critical) অটোমেটিক ফলব্যাক।
আর্কিটেকচার
নিম্নলিখিত ডায়াগ্রাম থেকে বোঝা যায় যে কীভাবে Support Quick Portal, Cloud Run-এ আপনার Node.js অ্যাড-অন, Cloud Firestore, Cloud Tasks (studio-trigger-retries), Gemini 3.8 Flash এবং Google Workspace Studio ফ্লো রেজিস্ট্রেশন থেকে শুরু করে রেজিলিয়েন্ট ট্রিগার ডেলিভারি ও অ্যাকশন এক্সিকিউশন পর্যন্ত ইন্টার্যাক্ট করে:

আপনি কী শিখবেন
workflowTriggerওworkflowActionএলিমেন্ট সহ HTTP Google Workspace অ্যাড-অন ডেপ্লয়মেন্ট ম্যানিফেস্ট (deployment.json) কীভাবে কনফিগার করতে হয়।- কীভাবে Google Workspace Studio ট্রিগার সাবস্ক্রিপশন (
triggerId,notifyUriএবংuserOAuthToken)studio_triggersFirestore সংগ্রহে রাখা যায় এবংnotifyUri/triggers.fireব্যবহার করে ফ্লো ফায়ার করা যায়। notifyUriথেকে2xxসাকসেস রেসপন্স না পাওয়া পর্যন্ত এক্সপোনেনশিয়াল ব্যাকঅফ সহ ট্রিগার বিজ্ঞপ্তি আবার পাঠানোর জন্য কীভাবে Cloud Tasks (@google-cloud/tasks) ব্যবহার করবেন।- কীওয়ার্ড ফলব্যাক সহ Gemini Enterprise Agent Platform-এ Gemini 3.8 Flash (
gemini-3.8-flash) স্ট্রাকচার্ড আউটপুট ব্যবহার করে Node.js-এ কীভাবেworkflowActionকাস্টম স্টেপ তৈরি করতে হয়।
পূর্বশর্ত
- Google Workspace Studio-তে অ্যাক্সেস সহ একটি Google Workspace অ্যাকাউন্ট।
- বিলিং চালু থাকা একটি Google Cloud প্রোজেক্ট, আপনার ব্যবহারকারী অ্যাকাউন্টের মতো একই Google Workspace সংস্থার মধ্যে তৈরি করা হয়েছে যাতে আপনি পরীক্ষার জন্য অপ্রকাশিত অ্যাড-অন ডেপ্লয়মেন্ট ইনস্টল করতে পারেন।
২. আপনার Google Cloud প্রোজেক্ট, Firestore ও Cloud Tasks সেট-আপ করা
এই ধাপে, আপনার প্রোজেক্টের OAuth সম্মতি স্ক্রিন কনফিগার করুন, Cloud Shell-এ প্রয়োজনীয় Google Cloud ও Google Workspace API চালু করুন, আপনার (default) Firestore ডেটাবেস প্রোভিশন করুন, আপনার Cloud Tasks আবার চেষ্টা করার কোয়েরি তৈরি করুন এবং আপনার অ্যাড-অন অনুমোদনের পরিষেবা অ্যাকাউন্ট চালু করুন।
- Google Cloud console খুলুন, আপনার Google Cloud প্রোজেক্ট বেছে নিন এবং উপরের নেভিগেশন বারে Cloud Shell অ্যাক্টিভেট করুন আইকনে ক্লিক করুন।
- OAuth-এর সম্মতি সংক্রান্ত স্ক্রিন কনফিগার করুন যাতে Google Workspace আপনার অ্যাড-অনের জন্য OAuth ক্লায়েন্ট প্রদান করতে পারে:
- Google Auth প্ল্যাটফর্ম ব্র্যান্ডিং পৃষ্ঠা খুলুন।
- প্রম্পট করা হলে, শুরু করুন বিকল্পে ক্লিক করুন। (আপনার সম্মতি স্ক্রিন আগে থেকেই কনফিগার করা থাকলে, ধাপ ৩-এ যান।)
- অ্যাপের তথ্য বিকল্পের অধীনে, অ্যাপের নাম হিসেবে
Support Extensionলিখুন, আপনার ব্যবহারকারী সহায়তা ইমেল বেছে নিন এবং পরবর্তী বিকল্পে ক্লিক করুন। - দর্শক বিকল্পের মধ্যে, ইন্টার্নাল বিকল্প বেছে নিয়ে পরবর্তী বিকল্পে ক্লিক করুন।
- যোগাযোগের তথ্য বিকল্পের অধীনে, আপনার ইমেল আইডি লিখুন এবং পরবর্তী বিকল্পে ক্লিক করুন।
- সম্পূর্ণ করুন বিকল্পের অধীনে, Google API পরিষেবা: ব্যবহারকারীর ডেটা নীতিতে সম্মতি জানান, চালিয়ে যান বিকল্পে ক্লিক করুন এবং তৈরি করুন বিকল্পে ক্লিক করুন।
- Cloud Shell-এ, আপনার অ্যাক্টিভ প্রোজেক্ট সেট করুন (
YOUR_PROJECT_ID-এর জায়গায় আপনার Google Cloud প্রোজেক্ট আইডি লিখুন) এবং প্রয়োজনীয় API চালু করুন:gcloud config set project YOUR_PROJECT_ID gcloud services enable \ aiplatform.googleapis.com \ firestore.googleapis.com \ cloudtasks.googleapis.com \ run.googleapis.com \ cloudbuild.googleapis.com \ artifactregistry.googleapis.com \ appsmarket-component.googleapis.com \ gsuiteaddons.googleapis.com \ workspacestudio.googleapis.com us-central1-এ আপনার(default)Firestore ডেটাবেস ওstudio-trigger-retriesCloud Tasks কিউ প্রোভিশন করুন (আগে থেকে না থাকলে) এবং আপনার Google Workspace অ্যাড-অন পরিষেবা অ্যাকাউন্ট ইনিশিয়ালাইজ করুন:gcloud firestore databases describe --database="(default)" >/dev/null 2>&1 || \ gcloud firestore databases create \ --database="(default)" \ --location=us-central1 \ --type=firestore-native \ --quiet gcloud tasks queues describe studio-trigger-retries --location=us-central1 >/dev/null 2>&1 || \ gcloud tasks queues create studio-trigger-retries \ --location=us-central1 \ --max-attempts=5 \ --min-backoff=10s \ --max-backoff=300s \ --max-doublings=4 gcloud workspace-add-ons get-authorization
৩. Firestore, Cloud Tasks ও Gemini 3.8 Flash ব্যবহার করে Node.js অ্যাপ তৈরি করা
এই ধাপে, আপনি ~/studio-support-addon-এ Node.js অ্যাপ্লিকেশন তৈরি করেন। কোড মডিউলার ও সহজে পড়ার উপযোগী রাখতে, আপনি প্রোজেক্টটি ছয়টি ফোকাসড ফাইলে সাজান:
firestore.js: Cloud Firestore ক্লায়েন্ট (studio_triggersএবংsupport_ticketsকালেকশন) শুরু করে এবং ক্যাশে করা OAuth টোকেন রিফ্রেশ করে।starter.js: নতুন সহায়তা টিকিট কাস্টম স্টার্টার স্টেপ ওয়েবহুক (/onConfigTriggerএবং/onManageTrigger) প্রয়োগ করে।action.js: জরুরি অবস্থা শনাক্ত করুন কাস্টম অ্যাকশন স্টেপ ওয়েবহুক (/onConfigUrgencyএবং/onExecuteUrgency) প্রয়োগ করে, এর জন্য Gemini 3.8 Flash (gemini-3.8-flash) ব্যবহার করা হয় এবং কি-ওয়ার্ড ম্যাচিং ফলব্যাক থাকে।tasks.js: ডিসপ্যাচtrigger.notifyUri-এ ট্রিগার বিজ্ঞপ্তি পাঠায় (https://workspacestudio.googleapis.com/v1/triggers/{triggerId}:fire-এ ফিরে যায়),studio-trigger-retriesCloud Tasks সারিতে ডেলিভারি করতে না পারা আইটেমগুলি সারিবদ্ধ করে এবংPOST /api/tasks/fire-triggerআবার চেষ্টা করার ওয়ার্কার এন্ডপয়েন্ট প্রয়োগ করে।portal.js: সহায়তা দ্রুত পোর্টাল ওয়েব UI (GET /) পরিবেশন করে এবং অ্যাক্টিভ Google Workspace Studio ট্রিগার (POST /api/submit-ticket) ফায়ার করে, এর সাথে ক্ষণস্থায়ী ব্যর্থতা সিমুলেট করা এবং Cloud Tasks আবার চেষ্টা করা দেখার বিকল্প থাকে।index.js: সব এক্সপ্রেস রুট একসাথে কানেক্ট করে এবং HTTP সার্ভার চালু করে।
প্রোজেক্ট ও package.json শুরু করুন
- প্রোজেক্ট ডিরেক্টরি তৈরি করে সেখানে নেভিগেট করুন:
mkdir -p ~/studio-support-addon && cd ~/studio-support-addon express,@google-cloud/firestore,@google-cloud/tasksএবং@google/genai-এর সাথেpackage.jsonতৈরি করুন: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-cloud/tasks": "^5.6.0", "@google/genai": "^1.0.0", "express": "^4.21.2" } } EOF
firestore.js — শেয়ার করা Firestore স্টেট ও OAuth টোকেন রিফ্রেশ
আপনার দুটি Firestore সংগ্রহ ম্যানেজ করতে এবং ক্যাশে করা ব্যবহারকারীর OAuth টোকেন ফ্রেশ রাখতে firestore.js তৈরি করুন:
studio_triggers: অ্যাক্টিভ Google Workspace Studio ফ্লো সাবস্ক্রিপশন (triggerId,notifyUriএবংuserOAuthToken) স্টোর করে যাতে Cloud Run কন্টেনার রিস্টার্ট এবং স্কেল-টু-জিরো ইভেন্ট থেকে সেগুলি সুরক্ষিত থাকে।support_tickets: পোর্টালের মাধ্যমে জমা দেওয়া প্রতিটি সহায়তা টিকিটের রেকর্ড স্টোর করে।refreshTokenInFirestore(req): Google Workspace Studio যখনই আপনার কোনও ওয়েবহুক এন্ডপয়েন্ট কল করে, তখন এটি একটি স্বল্প-মেয়াদীauthorizationEventObject.userOAuthTokenঅন্তর্ভুক্ত করে। এই হেল্পার লেটেস্ট টোকেন সহ অ্যাক্টিভ ট্রিগার ডকুমেন্ট আপডেট করে যাতে ব্যাকগ্রাউন্ডtriggers.fireকল অনুমোদিত থাকে।
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 — কাস্টম স্টার্টার ধাপ (workflowTrigger)
Google Workspace Studio কাস্টম স্টার্টার স্টেপের জন্য প্রয়োজনীয় দুটি ওয়েবহুক এন্ডপয়েন্ট প্রয়োগ করতে starter.js তৈরি করুন:
onConfigTrigger(POST /onConfigTrigger): Google Workspace Studio-তে কোনও ব্যবহারকারী তার ফ্লো-এর স্টার্টার হিসেবে নতুন সহায়তা টিকিট বেছে নিলে কল করা হয়। স্টার্টার বর্ণনা করে এমন একটিpushCardনেভিগেশন অ্যাকশন রিটার্ন করে।onManageTrigger(POST /onManageTrigger): কোনও ব্যবহারকারী ফ্লো চালু (req.body.workflow.triggerCreation) করলে অথবা সেটি বন্ধ করলে / মুছে দিলে (req.body.workflow.triggerDeletion) কল করা হয়।studio_triggersFirestore কালেকশনে সাবস্ক্রিপশন ডকুমেন্ট সেভ করে বা মুছে দেয়।
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 — Gemini 3.8 Flash সহ কাস্টম অ্যাকশন ধাপ (workflowAction)
Google Workspace Studio কাস্টম অ্যাকশন স্টেপের জন্য প্রয়োজনীয় দুটি ওয়েবহুক এন্ডপয়েন্ট প্রয়োগ করতে action.js তৈরি করুন:
onConfigUrgency(POST /onConfigUrgency):textInputউইজেট সহ একটি কনফিগারেশন কার্ড রিটার্ন করে যাhostAppDataSource.workflowDataSource.includeVariables: trueসেট করে, ব্যবহারকারীদের স্টার্টার ধাপের মাধ্যমে আউটপুট করাticketDescriptionভেরিয়েবল বেছে নেওয়ার অনুমতি দেয়।onExecuteUrgency(POST /onExecuteUrgency):req.body.workflow.actionInvocation.inputsথেকেticketDescriptionইনপুট পড়ে, Gemini 3.8 Flash (gemini-3.8-flash) ব্যবহার করে Gemini Enterprise Agent Platform-এ এর ব্যবসায়িক প্রভাব ও জরুরি অবস্থা শ্রেণীবদ্ধ করে, স্ট্রাকচার্ড আউটপুট (responseSchemaenum["High", "Normal"]) দেয়, মডেল কল ব্যর্থ হলেdetectKeywordUrgency()(urgent,blocker,critical)-এ ফলব্যাক করে এবংhostAppAction.workflowAction.returnOutputVariablesActionব্যবহার করেurgencyLevelআউটপুট ভেরিয়েবল রিটার্ন করে।
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
tasks.js — Cloud Tasks-এর আবার চেষ্টা করার সারি ও ওয়ার্কার এন্ডপয়েন্ট
trigger.notifyUri-এ ট্রিগার কল ডিসপ্যাচ করতে tasks.js তৈরি করুন (https://workspacestudio.googleapis.com/v1/triggers/{triggerId}:fire-এ ফিরে যাওয়া), আপনার studio-trigger-retries Cloud Tasks সারিতে ব্যর্থ কলগুলি সারিবদ্ধ করুন এবং POST /api/tasks/fire-trigger-এ আবার চেষ্টা করার প্রসেস করুন:
resolveTriggerUrl(trigger):triggerCreation.notifyUriথেকেtrigger.notifyUriব্যবহার করা হয়, যদি থাকে, অথবাhttps://workspacestudio.googleapis.com/v1/triggers/{triggerId}:fire-এ ফিরে যায়।fireTriggerDirect(trigger, payload):POSTঅনুরোধটি ট্রিগার URL-এticketTitleওticketDescriptionআউটপুট ভেরিয়েবল এবং একটি আইডেম্পোটেন্টrequestIdসহ পাঠায়।enqueueTriggerRetry(req, payload): প্রাথমিক ট্রিগার কল ব্যর্থ হলে বা নন-2xx HTTP স্ট্যাটাস রিটার্ন করলে, আপনার Cloud Run পরিষেবারPOST /api/tasks/fire-triggerএন্ডপয়েন্টকে টার্গেট করেstudio-trigger-retriesসারিতে একটি HTTP টাস্ক এনকিউ করে।handleFireTriggerTask(POST /api/tasks/fire-trigger): প্রতিটি আবার চেষ্টা করার সময় Cloud Tasks-এর মাধ্যমে আহ্বান করা হয়। Firestore থেকেstudio_triggers-এ লেটেস্টtriggerডকুমেন্ট খোঁজে (নিশ্চিত করে যে আবার চেষ্টা করার সময় সবসময় লেটেস্টuserOAuthTokenব্যবহার করা হয়),X-CloudTasks-TaskRetryCountহেডার চেক করে (simulateFailureচালু থাকলে, এটি503-এর সাথে প্রথম Cloud Task এক্সিকিউশন ব্যর্থ করে যাতে আপনি Cloud Tasks ব্যাক অফ দেখতে পারেন এবং আবার চেষ্টা করার সময় সফল হতে পারেন#1), এবং শুধুমাত্র তখনই200 OKফিরিয়ে দেয় যখনnotifyUriএকটি2xxসফল উত্তর ফিরিয়ে দেয়। যেকোনও5xxউত্তর Cloud Tasks-কে আপনার সারির ব্যাকঅফ নীতি অনুযায়ী আবার চেষ্টা করতে বলে।
cat << 'EOF' > tasks.js
import { CloudTasksClient } from '@google-cloud/tasks';
import { triggersCollection } from './firestore.js';
const tasksClient = new CloudTasksClient();
const PROJECT_ID = process.env.GOOGLE_CLOUD_PROJECT;
const LOCATION = process.env.CLOUD_TASKS_LOCATION || 'us-central1';
const QUEUE = process.env.CLOUD_TASKS_QUEUE || 'studio-trigger-retries';
// Prefer trigger.notifyUri from triggerCreation, falling back to triggers.fire
export function resolveTriggerUrl(trigger) {
return (
trigger.notifyUri ||
`https://workspacestudio.googleapis.com/v1/triggers/${encodeURIComponent(trigger.triggerId)}:fire`
);
}
// Call the Google Workspace Studio trigger URL directly
export async function fireTriggerDirect(
trigger,
{ title, description, requestId }
) {
const targetUrl = resolveTriggerUrl(trigger);
const response = await fetch(targetUrl, {
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,
}),
});
return { ok: response.ok, status: response.status, targetUrl };
}
// Enqueue a retry task in Cloud Tasks targeting POST /api/tasks/fire-trigger
export async function enqueueTriggerRetry(req, payload) {
const parent = tasksClient.queuePath(PROJECT_ID, LOCATION, QUEUE);
const baseUrl = process.env.SERVICE_URL || `https://${req.get('host')}`;
const workerUrl = `${baseUrl}/api/tasks/fire-trigger`;
const task = {
httpRequest: {
httpMethod: 'POST',
url: workerUrl,
headers: { 'Content-Type': 'application/json' },
body: Buffer.from(JSON.stringify(payload)).toString('base64'),
},
};
const [response] = await tasksClient.createTask({ parent, task });
return response.name;
}
// Cloud Tasks worker endpoint: retries calling notifyUri using fresh Firestore state
export async function handleFireTriggerTask(req, res) {
const { triggerId, title, description, requestId, simulateFailure } =
req.body || {};
const retryCount = Number(req.get('X-CloudTasks-TaskRetryCount') || 0);
if (!triggerId) {
return res.status(200).json({ skipped: true, reason: 'Missing triggerId' });
}
try {
const docId = encodeURIComponent(String(triggerId));
const doc = await triggersCollection.doc(docId).get();
if (!doc.exists) {
return res
.status(200)
.json({ skipped: true, reason: 'Trigger no longer active' });
}
const trigger = doc.data();
if (!trigger.userOAuthToken) {
return res
.status(503)
.json({ ok: false, error: 'Missing userOAuthToken in Firestore' });
}
// When simulateFailure is enabled, fail the 1st Cloud Task attempt (retryCount === 0)
// so learners can observe Cloud Tasks back off and succeed on retry #1.
if (simulateFailure && retryCount < 1) {
console.warn(
`[Cloud Tasks] Simulated transient failure on initial task attempt (retryCount=${retryCount}) for trigger ${triggerId}`
);
return res
.status(503)
.json({ ok: false, simulated: true, retryCount, triggerId });
}
const outcome = await fireTriggerDirect(trigger, {
title,
description,
requestId,
});
if (outcome.ok) {
console.log(
`[Cloud Tasks] Successfully delivered trigger ${triggerId} to ${outcome.targetUrl} on retryCount=${retryCount}`
);
return res
.status(200)
.json({ ok: true, status: outcome.status, retryCount });
}
console.error(
`[Cloud Tasks] Non-2xx response (${outcome.status}) from ${outcome.targetUrl} on retryCount=${retryCount}`
);
return res
.status(502)
.json({ ok: false, status: outcome.status, retryCount });
} catch (err) {
console.error(
`[Cloud Tasks] Error firing trigger ${triggerId} on retryCount=${retryCount}:`,
err.message
);
return res
.status(503)
.json({ ok: false, error: err.message, retryCount });
}
}
EOF
portal.js — Cloud Tasks ফলব্যাক সহ কুইক পোর্টাল UI ও notifyUri ডিসপ্যাচার কাজ করে
ওয়েব পোর্টাল পরিবেশন করতে এবং অটোমেটিক Cloud Tasks আবার শিডিউল করার মাধ্যমে Google Workspace Studio-তে টিকিট জমা দেওয়ার সুবিধা ডিসপ্যাচ করতে portal.js তৈরি করুন:
renderPortal(GET /): কতগুলি অ্যাক্টিভ Google Workspace Studio ট্রিগার রেজিস্টার করা আছে তা দেখাতেstudio_triggersFirestore সংগ্রহ কোয়েরি করে এবং HTML টিকিট জমা দেওয়ার ফর্ম (Cloud Tasks আবার চেষ্টা করার সুবিধা পরীক্ষা করার জন্য অস্থায়ী ব্যর্থতা সিমুলেট করুন চেকবক্স সহ) পরিবেশন করে।submitTicket(POST /api/submit-ticket):studio_triggers-এ সব অ্যাক্টিভ ট্রিগার ডকুমেন্ট ইটারেট করে এবংfireTriggerDirect()কল করে। ট্রিগার URL যদি নন-2xx রেসপন্স রিটার্ন করে, নেটওয়ার্কের সমস্যা দেখায় অথবাsimulateFailureচালু থাকে, তাহলে এটি অটোমেটিকenqueueTriggerRetry()ব্যবহার করেstudio-trigger-retries-এ আবার চেষ্টা করার টাস্ক সারিবদ্ধ করে এবংsupport_ticketsFirestore সংগ্রহে টিকিট লগ করে।
cat << 'EOF' > portal.js
import { triggersCollection, ticketsCollection } from './firestore.js';
import { fireTriggerDirect, enqueueTriggerRetry } from './tasks.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; }
.checkbox-label { font-weight: normal; margin-top: 14px; display: flex; align-items: center; gap: 8px; cursor: pointer; }
.checkbox-label input { width: auto; margin: 0; }
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>
<label class="checkbox-label" for="simulateFailure">
<input type="checkbox" id="simulateFailure">
Simulate transient failure (enqueue in Cloud Tasks and retry notifyUri)
</label>
<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 simulateFailure = document.getElementById('simulateFailure').checked;
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, simulateFailure }),
});
const data = await res.json();
if (data.ok) {
if (data.tasksQueued > 0) {
statusEl.textContent =
'Ticket saved! Initial notifyUri call did not succeed; queued ' +
data.tasksQueued +
' retry task(s) in Cloud Tasks.';
} else {
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 -> Call notifyUri directly, or enqueue Cloud Task on failure
export async function submitTicket(req, res) {
const title = req.body?.title || 'Untitled Ticket';
const description = req.body?.description || '';
const simulateFailure = Boolean(req.body?.simulateFailure);
const results = [];
let triggersFired = 0;
let tasksQueued = 0;
try {
const snapshot = await triggersCollection.get();
for (const doc of snapshot.docs) {
const trigger = doc.data();
if (!trigger.triggerId || !trigger.userOAuthToken) continue;
const requestId =
'req_' + Date.now() + '_' + Math.floor(Math.random() * 1000);
let outcome = { ok: false, status: 503 };
if (!simulateFailure) {
try {
outcome = await fireTriggerDirect(trigger, {
title,
description,
requestId,
});
} catch (fetchErr) {
console.error(
`Direct trigger call failed for ${trigger.triggerId}:`,
fetchErr.message
);
outcome = { ok: false, status: 0, error: fetchErr.message };
}
}
if (outcome.ok) {
triggersFired++;
results.push({
triggerId: trigger.triggerId,
status: outcome.status,
queuedRetry: false,
});
} else {
const taskName = await enqueueTriggerRetry(req, {
triggerId: trigger.triggerId,
title,
description,
requestId,
simulateFailure,
});
tasksQueued++;
results.push({
triggerId: trigger.triggerId,
status: outcome.status,
queuedRetry: true,
taskName,
});
}
}
await ticketsCollection.add({
title,
description,
triggersFired,
tasksQueued,
createdAt: new Date().toISOString(),
});
res.status(200).json({
ok: true,
triggersFired,
tasksQueued,
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 সার্ভার এন্ট্রি পয়েন্ট
আপনার পোর্টাল রুট, Cloud Tasks-এর আবার চেষ্টা করার ওয়ার্কার এন্ডপয়েন্ট এবং Google Workspace Studio ওয়েবহুক এন্ডপয়েন্ট একসাথে PORT-এ (Cloud Run-এ ডিফল্ট 8080) লিসেনিং করা Express HTTP সার্ভারে ওয়্যার করতে index.js তৈরি করুন:
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';
import { handleFireTriggerTask } from './tasks.js';
const app = express();
app.use(express.json());
// Support Quick Portal routes
app.get('/', renderPortal);
app.post('/api/submit-ticket', submitTicket);
// Cloud Tasks retry worker route
app.post('/api/tasks/fire-trigger', handleFireTriggerTask);
// 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
৪. Cloud Run-এ ডিপ্লয় করুন এবং অ্যাড-অন ইনস্টল করুন
এই ধাপে, আপনি Cloud Run-এ Node.js পরিষেবা ডিপ্লয় করেন, Google Workspace অ্যাড-অন ইনভোকার এবং Firestore, Cloud Tasks ও Agent Platform রানটাইম অ্যাক্সেসের জন্য IAM অনুমতি দেন, deployment.json ম্যানিফেস্ট তৈরি করেন এবং অ্যাড-অন ডিপ্লয়মেন্ট ইনস্টল করেন।
- আপনার প্রোজেক্ট, Firestore কালেকশন এবং Cloud Tasks কিউ এনভায়রনমেন্ট ভেরিয়েবল সহ Cloud Run-এ
support-extensionপরিষেবা ডিপ্লয় করুন: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,CLOUD_TASKS_QUEUE=studio-trigger-retries,CLOUD_TASKS_LOCATION=us-central1" \ --quiet - Google Workspace Studio যাতে আপনার ওয়েবহুক এন্ডপয়েন্ট কল করতে পারে, তার জন্য Google Workspace অ্যাড-অন পরিষেবা অ্যাকাউন্টে Cloud Run পরিষেবায়
roles/run.invoker-কে অনুমতি দিন: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 - Cloud Run রানটাইম পরিষেবা অ্যাকাউন্টে
roles/datastore.user(studio_triggersএবংsupport_ticketsFirestore কালেকশন পড়া ও লেখার জন্য),roles/cloudtasks.enqueuer(studio-trigger-retries-এ আবার চেষ্টা করার টাস্ক এনকিউ করার জন্য) এবংroles/aiplatform.user(Gemini Enterprise Agent Platform-এ Gemini-কে কল করার জন্য) অনুমতি দিন: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/cloudtasks.enqueuer" \ --condition=None \ --quiet gcloud projects add-iam-policy-binding "${PROJECT_ID}" \ --member="serviceAccount:${RUNTIME_SA}" \ --role="roles/aiplatform.user" \ --condition=None \ --quiet - আপনার ডিপ্লয় করা Cloud Run URL রিট্রিভ করুন এবং
deployment.jsonম্যানিফেস্ট তৈরি করুন যাsupportTicketTrigger(workflowTrigger) ও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 - আপনার অ্যাকাউন্টের জন্য Google Workspace অ্যাড-অন ডিপ্লয়মেন্ট তৈরি (বা পরিবর্তন) ও ইনস্টল করুন এবং আপনার সহায়তা দ্রুত পোর্টালের URL প্রিন্ট করুন:
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}"
৫. আপনার কাস্টম ধাপ এবং Cloud Tasks আবার চেষ্টা করার বিকল্প পরীক্ষা করুন
- Google Workspace Studio খুলুন (আগে থেকেই খোলা থাকলে ব্রাউজার ট্যাব রিফ্রেশ করুন) এবং নতুন ফ্লো বিকল্পে ক্লিক করুন।
- স্টার্টার বেছে নিন > সহায়তা এক্সটেনশন > নতুন সহায়তা টিকিট বিকল্পে ক্লিক করুন।
- ধাপ যোগ করুন (
+) > সহায়তা এক্সটেনশন > জরুরি অবস্থা শনাক্ত করুন বিকল্পে ক্লিক করুন। সোর্স বিবরণ ফিল্ডে, + ভেরিয়েবল বিকল্পে ক্লিক করে টিকিট বিবরণ বেছে নিন। - ধাপ যোগ করুন (
+) বিকল্পে ক্লিক করুন এবং চ্যাট > চ্যাটে আমাকে বিজ্ঞপ্তি দাও ধাপ যোগ করুন, যার মধ্যে মেসেজে টিকিটের নাম, জরুরি অবস্থার লেভেল এবং টিকিটের বিবরণ ভেরিয়েবল অন্তর্ভুক্ত থাকে। - উপরের ডানদিকের কোণে ফ্লো চালু করুন বিকল্প টগল করুন। Google Workspace Studio, Cloud Run-এ আপনার
/onManageTriggerএন্ডপয়েন্টে অবিলম্বে কল করে, যা Firestore-এstudio_triggersকালেকশনে অ্যাক্টিভ ট্রিগার সাবস্ক্রিপশন (triggerId,notifyUriওuserOAuthTokenসহ) লেখে। - নতুন ট্যাবে আপনার সহায়তা সংক্রান্ত কুইক পোর্টাল URL (
https://support-extension-...run.app) খুলুন, পোর্টালটি Firestore-এ1অ্যাক্টিভ ট্রিগার দেখাচ্ছে কিনা তা কনফার্ম করুন এবং Gemini 3.8 Flash (gemini-3.8-flash) ক্লাসিফিকেশন পরীক্ষা করার জন্য দুটি টিকিট জমা দিন:- গুরুত্বপূর্ণ টিকিট (টিকিটের নাম:
London office lockout, সমস্যার বিবরণ:All 500 employees in the London office are locked out of their workstations 20 minutes before the board presentation.):urgentবাblocker-এর মতো হার্ডকোড করা কীওয়ার্ড না থাকলেও,gemini-3.8-flashএই টিকিটকেHighজরুরি হিসেবে শ্রেণীবদ্ধ করে। - রুটিন টিকিট (টিকিটের নাম:
Conference room adapters, সমস্যার বিবরণ:Could we order two extra HDMI adapters for the third-floor conference room next month?):gemini-3.8-flashএই টিকিটকেNormalজরুরি হিসেবে শ্রেণীবদ্ধ করে।
- গুরুত্বপূর্ণ টিকিট (টিকিটের নাম:
- Google Chat ও Google Workspace Studio-এর অ্যাক্টিভিটি ট্যাবে যাচাই করে দেখুন যে আপনার ফ্লো
Urgency Level: HighওUrgency Level: Normal, দুটি টিকিটেই রান করেছে। notifyUriকাজ না করলে Cloud Tasks আবার ডেলিভার করার সুবিধা পরীক্ষা করে দেখুন:- সহায়তা কুইক পোর্টাল-এ, অস্থায়ী ব্যর্থতা সিমুলেট করুন (Cloud Tasks-এ এনকিউ করুন এবং notifyUri আবার চেষ্টা করুন) বিকল্পে টিকচিহ্ন দিন।
- টিকিটের শীর্ষক:
Checkout service outageও সমস্যার বিবরণ:Production payment gateway is timing out for all customersলিখুন, তারপর টিকিট জমা দিন বিকল্পে ক্লিক করুন। - লক্ষ্য করুন, পোর্টালটি সাথে সাথেই এই উত্তরটি দিচ্ছে:
Ticket saved! Initial notifyUri call did not succeed; queued 1 retry task(s) in Cloud Tasks. - ব্যাকগ্রাউন্ডে, Cloud Tasks
POST /api/tasks/fire-triggerএক্সিকিউট করে। প্রথম টাস্কের চেষ্টা (retryCount=0)503ব্যর্থতার সিমুলেশন করে, যার ফলে Cloud Tasks10s(--min-backoff=10s) সময়ের জন্য ব্যাক অফ করে এবং আবার চেষ্টা করে (retryCount=1), যাnotifyUriকল করে এবং সফল হয়। প্রায় ১০–১৫ সেকেন্ড পরে, আপনার Google Chat বিজ্ঞপ্তি এসেছে কিনা তা যাচাই করুন!
৬. অপ্রয়োজনীয়গুলি মুছুন
আপনার Google Cloud অ্যাকাউন্টে চলমান চার্জ এড়াতে, 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 tasks queues delete studio-trigger-retries --location=us-central1 --quiet
gcloud firestore databases delete --database="(default)" --quiet
৭. অভিনন্দন!
আপনি Gemini Enterprise Agent Platform-এ Firestore, Cloud Tasks, Cloud Run ও Gemini 3.8 Flash ব্যবহার করে Google Workspace Studio-এর জন্য Node.js HTTP এক্সটেনশন তৈরি, ডিপ্লয় ও পরীক্ষা করেছেন!
আমরা কী কী কভার করেছি
workflowTriggerওworkflowActionএলিমেন্ট সহ Google Workspace Studio HTTP অ্যাড-অন ম্যানিফেস্ট (deployment.json) কনফিগার করা।studio_triggersFirestore কালেকশনে Google Workspace StudioworkflowTriggerসাবস্ক্রিপশন এবংsupport_tickets-এ লগিং পোর্টাল সাবমিশন পারসিস্ট করা যাতে ট্রিগারগুলি ক্লাউড রান স্কেলিং এবং রিডিপ্লয়মেন্টে টিকে থাকে।- Cloud Tasks (
studio-trigger-retries)-এর মাধ্যমেnotifyUri(triggers.fire) ডেলিভারির গ্যারান্টি দেওয়া যাতে Firestore থেকে লেটেস্টuserOAuthTokenব্যবহার করে এক্সপোনেনশিয়াল ব্যাকঅফের মাধ্যমে অস্থায়ী HTTP সংক্রান্ত সমস্যা অটোমেটিক আবার চেষ্টা করা হয়। - Gemini Enterprise Agent Platform-এ Gemini 3.8 Flash (
gemini-3.8-flash) স্ট্রাকচার্ড আউটপুট সহworkflowActionস্টেপ তৈরি করা যা সহায়তা টিকিটকে শ্রেণীবদ্ধ করে, এর সাথে কীওয়ার্ড ম্যাচিং ফলব্যাকও আছে।