From a6571fabfd5145e7636e592ca73dc89f1639ca78 Mon Sep 17 00:00:00 2001 From: Marcel Hoppe Date: Fri, 2 May 2025 16:41:38 +0400 Subject: [PATCH] Tried adding automatic index creation, but seems like it is struggling with permissions within our internal project, so it might not have been the best solution --- extension.yaml | 15 ++++++++ functions/package.json | 1 + functions/src/index.ts | 2 + functions/src/installFunctions.ts | 63 +++++++++++++++++++++++++++++++ 4 files changed, 81 insertions(+) create mode 100644 functions/src/installFunctions.ts diff --git a/extension.yaml b/extension.yaml index 70750d0..1277289 100644 --- a/extension.yaml +++ b/extension.yaml @@ -37,6 +37,9 @@ roles: - role: datastore.user reason: >- Allows Slack Support Chat to store support messages in Cloud Firestore. + - role: datastore.indexAdmin + reason: >- + Allows the extension to create a composite index on install. # In the `resources` field, list each of your extension's functions, including the trigger for each function. # Learn more in the docs: @@ -58,6 +61,18 @@ resources: properties: httpsTrigger: {} + - name: createSupportIndex + type: firebaseextensions.v1beta.function + description: >- + Lifecycle task that creates the required collection group index on install. + properties: + taskQueueTrigger: {} + +lifecycleEvents: + onInstall: + function: createSupportIndex + processingMessage: "Creating Firestore composite index for Slack Support Chat…" + # In the `params` field, set up your extension's user-configured parameters. # Learn more in the docs: # https://firebase.google.com/docs/extensions/reference/extension-yaml#params-field diff --git a/functions/package.json b/functions/package.json index 79be928..a28bd64 100644 --- a/functions/package.json +++ b/functions/package.json @@ -13,6 +13,7 @@ "@slack/web-api": "^7.9.1", "firebase-admin": "^12.6.0", "firebase-functions": "^6.0.1", + "google-auth-library": "^9.15.1", "node-emoji": "^2.2.0" }, "devDependencies": { diff --git a/functions/src/index.ts b/functions/src/index.ts index 5e1d54a..35b4b06 100644 --- a/functions/src/index.ts +++ b/functions/src/index.ts @@ -5,6 +5,8 @@ import { getFirestore } from 'firebase-admin/firestore'; initializeApp(); export const db = getFirestore(); +import { createSupportIndex } from './installFunctions'; import { onSupportMessageCreated, slackSupportEvents } from './supportFunctions'; +export { createSupportIndex }; export { onSupportMessageCreated, slackSupportEvents }; diff --git a/functions/src/installFunctions.ts b/functions/src/installFunctions.ts new file mode 100644 index 0000000..0a7e181 --- /dev/null +++ b/functions/src/installFunctions.ts @@ -0,0 +1,63 @@ +import { onTaskDispatched } from 'firebase-functions/v2/tasks'; +import { GoogleAuth } from "google-auth-library"; +import { logger } from 'firebase-functions'; + +/** + * Lifecycle task (`taskQueueTrigger`) that creates a composite index on the + * `support` collection group for the `slackThreadTs` field. + * + * • Runs once at extension install. + * • Ignores `ALREADY_EXISTS` errors to keep the install idempotent. + */ +export const createSupportIndex = onTaskDispatched( + { + retryConfig: { maxAttempts: 3 } + }, + async () => { + logger.info('createSupportIndex(): Function started'); + + // MARK: - Get environment variables + + const projectId = process.env.GCP_PROJECT ?? process.env.PROJECT_ID; + const configPath = process.env.CONFIG_PATH; + if (!projectId || !configPath) { + logger.error('createSupportIndex(): Project ID or config path is not set', { projectId, configPath }); + return; + } + + const configSegments = configPath.split('/'); + const lastCollection = configSegments[configSegments.length - 2]; + const url = `https://firestore.googleapis.com/v1/projects/${projectId}/databases/(default)/collectionGroups/${lastCollection}/indexes`; + + // MARK: - Composite index definition + + const body = { + fields: [ + { fieldPath: "slackThreadTs", order: "ASCENDING" }, + { fieldPath: "__name__", order: "ASCENDING" } + ], + queryScope: "COLLECTION_GROUP" + }; + + // MARK: - Acquire an access token and call Firestore Admin REST API + + const auth = new GoogleAuth({ + scopes: ["https://www.googleapis.com/auth/cloud-platform"] + }); + const client = await auth.getClient(); + + // MARK: - Send the request + + try { + await client.request({ url, method: "POST", data: body }); + logger.info(`createSupportIndex(): Support index creation request sent for collection group '${lastCollection}'`); + } catch (err: any) { + // 409 == ALREADY_EXISTS; any other status re‑throws to let the task retry + if (err.response?.status === 409) { + logger.info("createSupportIndex(): Support index already exists – nothing to do"); + } else { + logger.error("createSupportIndex(): Index creation failed", err.response?.data ?? err); + throw err; // triggers task retry / surfacing to installer + } + } + });