คู่มือนี้จะแสดงวิธีย้ายข้อมูลส่วนขยายจากสภาพแวดล้อม Firebase Extensions ที่เลิกใช้งานแล้วไปยังฟังก์ชันที่ผู้ใช้ติดตั้งและทำให้ใช้งานได้ในฐานของโค้ด Cloud Functions สำหรับ Firebase (รุ่นที่ 2) ของตนเอง
ซึ่งเป็นเส้นทางการย้ายข้อมูลที่แนะนำ Firebase จะเก็บรักษารายการส่วนขยายที่มี npm ที่เทียบเท่าอย่างเป็นทางการ คู่มือนี้จะแนะนำคุณเกี่ยวกับการสร้างส่วนขยายของคุณเอง
ตลอดทั้งคู่มือนี้ เราจะใช้ส่วนขยาย Stream Cloud Firestore to
BigQuery (firestore-bigquery-export) เป็นตัวอย่างในการอธิบาย แต่ละส่วนจะลงท้ายด้วยตัวอย่างที่ใช้ได้จริงซึ่งแสดงให้เห็นว่าส่วนขยายนั้นมีลักษณะอย่างไรก่อนการย้ายข้อมูล และมีลักษณะอย่างไรหลังจากนั้นในฐานะแพ็กเกจ @firebase-function-kits/firestore-bigquery-export
ลงชื่อสมัครใช้เพื่อรับข้อมูลเพิ่มเติมและความช่วยเหลือเกี่ยวกับการย้ายข้อมูลส่วนขยาย
หากมีคำถามเกี่ยวกับวิธีย้ายข้อมูลจาก Firebase Extensions โปรดติดต่อเราที่ firebase-extensions-migrator-support-external@google.com นอกจากนี้ เราจะส่งอีเมลถึงกลุ่มนี้เมื่อเราอัปเดตคู่มือพร้อมข้อมูลเพิ่มเติมเกี่ยวกับการแพ็กเกจ การทดสอบ และการเผยแพร่ฟังก์ชันรุ่นที่ 2
หากต้องการเข้าร่วมกลุ่มนี้ ให้ส่งข้อความไปที่ firebase-extensions-migrator-support-external+subscribe@google.com ซึ่งจะตอบกลับด้วยอีเมลคำขอเป็นสมาชิก คุณต้องตอบกลับอีเมลนั้น ไม่ใช่คลิกปุ่ม "เข้าร่วมกลุ่มนี้"
ก่อนเริ่มต้น
หากต้องการย้ายข้อมูลนี้ให้เสร็จสมบูรณ์ตามที่ระบุไว้ คุณจะต้องใช้ฟีเจอร์ต่อไปนี้ของ Cloud Functions
การกำหนดค่าที่มีพารามิเตอร์ พารามิเตอร์แต่ละรายการที่คุณประกาศใน
extension.yamlจะกลายเป็นพารามิเตอร์ที่กำหนดในโค้ดแพ็กเกจบทบาท IAM แบบประกาศและ API ที่จำเป็น แต่ละบทบาทที่คุณประกาศใน
extension.yamlจะกลายเป็นการเรียกrequiresRole(...)และแต่ละ API จะกลายเป็นการเรียกrequiresAPI(...)ในโค้ดแพ็กเกจของคุณ เมื่อติดตั้งใช้งาน Firebase CLI จะให้บทบาทที่ประกาศไว้แก่บัญชีบริการรันไทม์ที่มีการจัดการ และเปิดใช้ API ที่ประกาศไว้ในนามของคุณเหตุการณ์ในวงจรสำหรับฐานของโค้ด Cloud Functions ตอนนี้ฐานของโค้ด Cloud Functions รองรับเหตุการณ์ในวงจรที่คล้ายกับ Firebase Extensions แล้ว ประกาศการตั้งค่าเวลาติดตั้งและเวลาอัปเดตด้วยฮุกวงจร
afterFirstDeploy(...)และafterRedeploy(...)ซึ่งจะแทนที่lifecycleEventsที่คุณประกาศในextension.yaml
ย้ายข้อมูลแหล่งที่มา Firebase Extensions ไปยังฟังก์ชันรุ่นที่ 2
(ไม่บังคับ) การย้ายข้อมูลอัตโนมัติด้วยทักษะ Agent ของ Firebase
คุณสามารถทำให้ขั้นตอนที่ 1 ถึง 8 (การจัดทำรายการทรัพยากร, ทริกเกอร์การอัปเกรด, การแปลงพารามิเตอร์และข้อมูลลับ, IAM แบบประกาศ, Lifecycle Hook และการสร้าง README ของแพ็กเกจ) เป็นแบบอัตโนมัติได้โดยใช้ความสามารถของ AI Agent extension-to-functions-codebase อย่างเป็นทางการ
ติดตั้งทักษะ
หากคุณหรือผู้ช่วยเขียนโค้ด AI (Gemini ใน Firebase, Cursor, Claude Code, GitHub Copilot) ยังไม่ได้ติดตั้งทักษะ ให้เรียกใช้คำสั่งต่อไปนี้โดยใช้ CLI ของทักษะ
npx skills add firebase/agent-skills --skill extension-to-functions-codebase
เมื่อติดตั้งทักษะในโปรเจ็กต์แล้ว ผู้ช่วยเขียนโค้ด AI จะทำตามกฎการย้ายข้อมูลและขั้นตอนการเปลี่ยนรูปแบบโดยอัตโนมัติ คุณสามารถใช้พรอมต์ต่อไปนี้ได้
"โปรดย้ายข้อมูล Firebase Extensions นี้ไปยังแพ็กเกจ Function Kit รุ่นที่ 2 ที่เผยแพร่ได้โดยทำตามวิธีการในทักษะ extension-to-functions-codebase"
1. ตรวจสอบส่วนขยาย
เริ่มต้นด้วยการทำรายการของส่วนขยาย ซึ่งเป็นรายการที่สมบูรณ์ของทุกสิ่งที่ส่วนขยายประกาศ จัดส่ง และจัดทำเอกสาร เพื่อให้ทุกพฤติกรรมมีปลายทางที่กำหนดไว้ในฟังก์ชันรุ่นที่ 2 และไม่มีสิ่งใดสูญหายในการย้ายข้อมูล
ตรวจสอบแต่ละรายการต่อไปนี้และจดบันทึกสิ่งที่คุณพบ
extension.yamlซึ่งประกาศพารามิเตอร์, ฟังก์ชัน, เหตุการณ์, บทบาท IAM, API ที่จำเป็น, ข้อมูลลับ และฮุกวงจรfunctions/ซึ่งมีโค้ดฟังก์ชัน ทรัพยากร Dependency การกำหนดค่าบิลด์ ทริกเกอร์ และฟังก์ชันคิวงานREADME.md,PREINSTALL.mdและPOSTINSTALL.mdซึ่งมีขั้นตอนการตั้งค่า คำเตือน และหมายเหตุสำหรับการเรียกเก็บเงินscripts/ซึ่งประกอบด้วยยูทิลิตีการนำเข้า การทดแทนโฆษณา IAM การซ่อมแซม หรือการย้ายข้อมูล และเครื่องมืออื่นๆ ที่คุณจัดส่งพร้อมกับส่วนขยาย
จากนั้นให้ตัดสินใจว่าจะวางแต่ละรายการใน extension.yaml ไว้ที่ใดในแพ็กเกจ npm ดังนี้
แปลงการกำหนดค่าของผู้ใช้เป็นพารามิเตอร์ Cloud Functions (ขั้นตอนที่ 4)
แปลงข้อมูลลับเป็นข้อมูลลับของ Cloud Functions (ขั้นตอนที่ 4)
แปลงบทบาท IAM เป็นการประกาศ
requiresRole(...)(ขั้นตอนที่ 6)แปลง Google API ที่จำเป็นเป็นการประกาศ
requiresAPI(...)ตามความเหมาะสม (ขั้นตอนที่ 6)แปลงฮุกการติดตั้งและการอัปเดตเป็นการประกาศ
afterFirstDeploy(...)และafterRedeploy(...)(ขั้นตอนที่ 7)แปลงรหัสอินสแตนซ์จาก
EXT_INSTANCE_IDเป็นFIREBASE_KIT_INSTANCE_ID(ขั้นตอนที่ 4)
ตัวอย่างการใช้งาน: สตรีม Cloud Firestore ไปยัง BigQuery
การอ่าน firestore-bigquery-export/extension.yaml และ functions/ จะสร้างพื้นที่โฆษณานี้
ใน extension.yaml |
จำนวน/มูลค่า | ตำแหน่งที่ข้อมูลของคุณ |
|---|---|---|
params |
25 (COLLECTION_PATH, DATASET_ID, TABLE_ID, DATASET_LOCATION, VIEW_TYPE, …) |
พารามิเตอร์ Cloud Functions (ขั้นตอนที่ 4) |
apis |
bigquery.googleapis.com |
requiresAPI(...) (ขั้นตอนที่ 6) |
roles |
bigquery.dataEditor, datastore.user, bigquery.user |
requiresRole(...) (ขั้นตอนที่ 6) |
resources |
ทริกเกอร์เหตุการณ์ 1 รายการ (fsexportbigquery) + ฟังก์ชันคิวงาน (initBigQuerySync, setupBigQuerySync) |
ฟังก์ชันแพ็กเกจที่ส่งออก (ขั้นตอนที่ 3) |
lifecycleEvents |
onInstall → initBigQuerySync; onUpdate / onConfigure → setupBigQuerySync |
afterFirstDeploy / afterRedeploy (ขั้นตอนที่ 7) |
| รหัสอินสแตนซ์ | ไม่ได้ใช้ (ไม่มีการอ่าน EXT_INSTANCE_ID) |
ไม่มีข้อมูลที่จะย้าย |
scripts/ |
import/ (โฆษณาทดแทน), gen-schema-view/ |
เก็บไว้เป็นสคริปต์ (อยู่นอกขอบเขตที่นี่) |
การวิเคราะห์ ส่วนขยายไม่ได้ประกาศพารามิเตอร์ type: secret จึงไม่มีอะไรให้ย้ายข้อมูลสำหรับข้อมูลลับในขั้นตอนที่ 4 ทริกเกอร์เหตุการณ์เป็นรุ่นที่ 2 อยู่แล้ว มีเพียงฟังก์ชันคิวงานเท่านั้นที่ยังคงเป็นรุ่นที่ 1 (เกี่ยวข้องในขั้นตอนที่ 3)
2. อัปเดต package.json
อัปเดตไฟล์ package.json ของส่วนขยาย หากคุณกำลังย้ายข้อมูลส่วนขยายเดียว
นี่อาจเป็น package.json รูท หากคุณกำลังย้ายข้อมูลส่วนขยายจำนวนมากในที่เก็บเดียว ให้สร้างแพ็กเกจของตัวเองสำหรับส่วนขยายแต่ละรายการ
เวอร์ชัน SDK ขั้นต่ำ: ประกาศ firebase-functions >=
7.4.0 และ firebase-admin >=
14.2.0 เป็นทรัพยากร Dependency ประกาศเวอร์ชันของ firebase-functions เป็นทรัพยากร Dependency แบบเพียร์ด้วย เพื่อให้โปรเจ็กต์ Cloud Functions ของผู้ใช้มี SDK เวอร์ชันเดียวกับที่ใช้เขียนไลบรารีของคุณ
{
"name": "<package-name>",
"version": "1.0.0",
"main": "lib/index.js",
"types": "lib/index.d.ts",
"exports": {
".": {
"types": "./lib/index.d.ts",
"default": "./lib/index.js"
}
},
"engines": {
"node": "22"
},
"peerDependencies": {
"firebase-functions": "^7.4.0"
},
"dependencies": {
"firebase-functions": "^7.4.0",
"firebase-admin": "^14.2.0"
}
}
ตัวอย่างการใช้งาน: สตรีม Cloud Firestore ไปยัง BigQuery
ก่อน functions/package.json ของส่วนขยายเป็นแบบส่วนตัว โดยระบุรหัสส่วนขยายและประกาศ firebase-functions เป็นทรัพยากร Dependency โดยตรง ดังนี้
{
"name": "firestore-bigquery-export",
"main": "lib/index.js",
"private": true,
"dependencies": {
"@firebaseextensions/firestore-bigquery-change-tracker": "^2.0.4",
"firebase-admin": "^14.2.0",
"firebase-functions": "^6.3.2"
}
}
หลัง แพ็กเกจที่เผยแพร่ได้: ชื่อที่กำหนดขอบเขต, แมป exports และ firebase-functions ย้ายไปที่ peerDependencies:
{
"name": "@firebase-function-kits/firestore-bigquery-export",
"version": "0.1.0",
"main": "lib/index.js",
"types": "lib/index.d.ts",
"exports": {
".": {
"types": "./lib/index.d.ts",
"default": "./lib/index.js"
}
},
"engines": {
"node": "22"
},
"peerDependencies": {
"firebase-functions": "^7.4.0"
},
"dependencies": {
"@firebaseextensions/firestore-bigquery-change-tracker": "^2.0.4",
"firebase-admin": "^14.2.0",
"firebase-functions": "^7.4.0"
}
}
3. อัปเกรดฟังก์ชันจากรุ่นที่ 1 เป็นรุ่นที่ 2
หากส่วนขยายยังคงส่งออกฟังก์ชันรุ่นที่ 1 ให้แปลงทริกเกอร์แต่ละรายการเป็นฟังก์ชันรุ่นที่ 2 ที่เทียบเท่า นำเข้าจากโมดูล firebase-functions/... และส่งการตั้งค่ารันไทม์ในตัวเลือกทริกเกอร์
ดูคู่มือการอัปเกรด Cloud Functions รุ่นที่ 2 โดยเฉพาะอย่างยิ่ง คุณสามารถลดความพยายามในการเขียนใหม่ได้โดยใช้การแยกโครงสร้างเหตุการณ์ที่แพตช์แล้วของรุ่นที่ 2 และหลีกเลี่ยงการเขียนตรรกะของฟังก์ชันใหม่ เนื่องจาก SDK รุ่นที่ 2 จะแสดงพารามิเตอร์ v1 เป็นฟิลด์ในออบเจ็กต์เหตุการณ์ ซึ่งช่วยให้คุณใช้พารามิเตอร์ที่แยกโครงสร้าง/มีชื่อได้ และคงตรรกะทางธุรกิจไว้โดยไม่มีการเปลี่ยนแปลง
ก่อน รุ่นที่ 1:
import * as functions from "firebase-functions/v1";
export const sync = functions.firestore
.document("{collectionId}/{documentId}")
.onWrite(async (change, context) => {
await handleWrite(change.before, change.after, context.params);
});
หลัง รุ่นที่ 2:
import { onDocumentWritten } from "firebase-functions/firestore";
export const syncV2 = onDocumentWritten(
{ document: "{collectionId}/{documentId}" },
async ({ change, context }) =>
await handleWrite(change.before, change.after, context.params)
);
ดูการเปรียบเทียบเวอร์ชัน Cloud Functions เพื่อดูรายการความแตกต่างทั้งหมดระหว่างฟังก์ชันรุ่นที่ 1 และรุ่นที่ 2
ความแตกต่างที่สำคัญระหว่างฟังก์ชันรุ่นที่ 1 และรุ่นที่ 2 คือฟังก์ชันรุ่นที่ 2 ต้องอยู่ร่วมกับทรัพยากรทริกเกอร์ เมื่อผู้ใช้ย้ายข้อมูลจากส่วนขยายที่ใช้ฟังก์ชันรุ่นที่ 1 ไปยังชุดที่ใช้ฟังก์ชันรุ่นที่ 2 ผู้ใช้อาจต้องเปลี่ยนตำแหน่งฟังก์ชันเพื่อให้เป็นไปตามข้อกำหนดนี้
ตำแหน่ง Cloud Functions ที่ผู้ใช้เลือกไว้อาจใช้งานไม่ได้ในระหว่างการย้ายข้อมูลเนื่องจากระบบจะเก็บตำแหน่งที่มีอยู่ไว้ หากฟังก์ชันที่ทริกเกอร์เหตุการณ์ของส่วนขยายอาศัยตำแหน่งฟังก์ชันที่ผู้ใช้ระบุ เราขอแนะนำให้เพิ่มพารามิเตอร์ใหม่ลงในส่วนขยายเพื่อรวบรวมตำแหน่งทริกเกอร์เหตุการณ์และแมปกับตำแหน่งฟังก์ชันใหม่
ตัวอย่างการใช้งาน: สตรีม Cloud Firestore ไปยัง BigQuery
defineString("DATABASE_REGION", {
label: "Firestore Instance Location",
description:
"Where is the Firestore database located? You can check your current database location at https://console.cloud.google.com/firestore/databases. The functions in this kit deploy to the Cloud Run region closest to this location.",
input: select({
"Multi-region (Europe - Belgium and Netherlands)": "eur3",
"Multi-region (United States)": "nam5",
"Multi-region (Iowa, North Virginia, and Oklahoma)": "nam7",
"Iowa (us-central1)": "us-central1",
// More locations...
})
});
// Firestore multi-region locations are not Cloud Run regions; deploying a
// function to one hard-fails, so they map to a region inside the multi-region.
const MULTI_REGION_TO_FUNCTION_REGION: Record<string, string> = {
nam5: "us-central1",
nam7: "us-central1",
eur3: "europe-west1",
};
/**
* Maps a Firestore database location to the Cloud Run region the functions
* should deploy to. The lookup is case-insensitive and ignores surrounding
* whitespace, as the CLI's own region handling is. Regional locations pass
* through lowercased; an unset or blank location returns `undefined`, meaning
* the functions declare no region.
*/
export function firestoreLocationToFunctionRegion(
location: string | undefined
): string | undefined {
const normalized = location?.trim().toLowerCase();
if (!normalized) {
return undefined;
}
return MULTI_REGION_TO_FUNCTION_REGION[normalized] ?? normalized;
}
const functionRegion = firestoreLocationToFunctionRegion(
process.env.DATABASE_REGION
);
export const fsexportbigquery = onDocumentWritten(
{
region: functionRegion,
// Other configuration
},
(event) => handleDocumentWrite(event, getHandlerContext())
);
เมื่อติดตั้งใช้งานชุดเครื่องมือเป็นครั้งแรก ระบบจะแจ้งให้ผู้ใช้ระบุ DATABASE_REGION และจะติดตั้งใช้งานฟังก์ชันใน functionRegion ที่เกี่ยวข้องด้านบนเพื่อแก้ไขปัญหาเกี่ยวกับตำแหน่งสำหรับฟังก์ชันที่ทริกเกอร์เหตุการณ์ Gen2
4. แปลงพารามิเตอร์และข้อมูลลับของส่วนขยาย
พารามิเตอร์
พารามิเตอร์แต่ละรายการที่คุณประกาศใน extension.yaml จะกลายเป็นพารามิเตอร์ Cloud Functions
แปลงการอ่านสภาพแวดล้อมโดยตรง:
const collectionPath = process.env.COLLECTION_PATH;
เป็นพารามิเตอร์ Cloud Functions:
import { defineString } from "firebase-functions/params";
import { onDocumentWritten } from "firebase-functions/firestore";
const collectionPath = defineString("COLLECTION_PATH");
// Pass the param directly when used as a placeholder (e.g. trigger path)
export const sync = onDocumentWritten(
{ document: collectionPath },
async (event) => {
// Call .value() to read the string inside a handler
const path = collectionPath.value();
await handleWrite(path, event);
}
);
ใช้ collectionPath.value() เพื่ออ่านสตริงภายในเครื่องจัดการ ใช้ collectionPath โดยตรงในตำแหน่งที่คาดว่าจะมีตัวยึดตำแหน่ง เช่น เส้นทางทริกเกอร์ฟังก์ชัน
Firebase CLI จะค้นหาพารามิเตอร์และอ่านค่าจาก .env, .env.<projectId> หรือแจ้งผู้ใช้ในระหว่างการติดตั้งใช้งาน ใช้ชื่อพารามิเตอร์เดิมเพื่อให้ค่าจากการติดตั้งที่มีอยู่ยังคงอยู่
สิ่งสำคัญคือคุณต้องไม่เปลี่ยนชื่อพารามิเตอร์ที่ประกาศไว้ในโค้ดโดยเด็ดขาด การย้ายข้อมูลส่วนขยายจะเก็บค่าพารามิเตอร์ของผู้ใช้ปลายทางที่มีอยู่โดยอัตโนมัติ แต่จะเก็บเฉพาะเมื่อชื่อไม่มีการเปลี่ยนแปลงเท่านั้น
ตัวอย่างการใช้งาน: สตรีม Cloud Firestore ไปยัง BigQuery
ก่อน พารามิเตอร์ที่ประกาศใน extension.yaml ซึ่งอ่านเป็นตัวแปรสภาพแวดล้อมดิบใน config.ts:
# extension.yaml
- param: COLLECTION_PATH
label: Collection path
type: string
required: true
// functions/src/config.ts
collectionPath: process.env.COLLECTION_PATH,
หลัง defineString 1 รายการ โดย CLI จะค้นพบและอ่านจาก .env ดังนี้
// src/config.ts
import { defineString } from "firebase-functions/params";
collectionPath: defineString("COLLECTION_PATH", {
label: "Collection path",
// We now support "nonEmpty: true" to ensure a value other than the empty string
// is entered, analogous to "required: true" in extension.yaml
input: { text: { nonEmpty: true } }
}),
ชื่อพารามิเตอร์ไม่มีการเปลี่ยนแปลง ดังนั้น .env ที่มีอยู่จะยังคงทำงานต่อไป
รหัสอินสแตนซ์
ส่วนขยายจะอ่านรหัสอินสแตนซ์จาก EXT_INSTANCE_ID ซึ่งแทรกโดยรันไทม์ของส่วนขยาย ชุดฟังก์ชันจะอ่านรหัสอินสแตนซ์จาก FIREBASE_KIT_INSTANCE_ID ซึ่ง Firebase CLI จะตั้งค่าสำหรับอินสแตนซ์ชุดแต่ละรายการเป็นคีย์ของอินสแตนซ์ในแมป instances ใน firebase.json CLI จะระบุค่านี้ในระหว่างการค้นหาเวลาทำให้ใช้งานได้ ในโปรแกรมจำลอง และฟังก์ชันที่ทำให้ใช้งานได้
รหัสอินสแตนซ์ไม่ใช่พารามิเตอร์ จึงไม่ต้องประกาศด้วย defineString อันที่จริงแล้ว FIREBASE_... เป็นคำนำหน้าที่สงวนไว้ในไฟล์ .env ดังนั้นผู้ใช้จึงไม่สามารถตั้งค่าหรือลบล้างได้ ค่าที่ CLI แทรกจะไม่ปรากฏในระบบ params อ่านได้โดยตรงจากสภาพแวดล้อม ดังนี้
// Before
const instanceId = process.env.EXT_INSTANCE_ID;
// After
const instanceId = process.env.FIREBASE_KIT_INSTANCE_ID;
ระบบจะตั้งค่าตัวแปรเมื่อติดตั้งใช้งานแพ็กเกจเป็นชุดอุปกรณ์เท่านั้น หากโค้ดของคุณยังติดตั้งใช้งานเป็นฐานของโค้ดแบบสแตนด์อโลนด้วย (ดูขั้นตอนที่ 9) ให้ถือว่าเป็นตัวเลือกหรือไม่ก็ทำให้โค้ดทำงานไม่สำเร็จอย่างรวดเร็วพร้อมข้อความที่ชัดเจนเมื่อไม่มีโค้ดดังกล่าว หากส่วนขยายแสดงรหัสอินสแตนซ์เป็นพารามิเตอร์ที่แสดงต่อผู้ใช้ คุณต้องนำพารามิเตอร์นั้นออก เนื่องจากตอนนี้ Firebase CLI เป็นเจ้าของค่าดังกล่าวแล้ว
ตัวอย่างที่ใช้ได้จริง: ลบข้อมูลผู้ใช้
(ส่วนขยาย Stream Cloud Firestore to BigQuery ไม่อ่านรหัสอินสแตนซ์ จึงไม่มีข้อมูลที่จะย้ายข้อมูล) ส่วนขยายลบข้อมูลผู้ใช้จะใช้รหัสนี้เพื่อตั้งชื่อหัวข้อ Pub/Sub)
ก่อน อ่านเป็นตัวแปรสภาพแวดล้อมดิบใน config.ts โดยมีคำนำหน้า ext- ที่ส่วนขยายใช้สำหรับทรัพยากร
// functions/src/config.ts
discoveryTopic: `ext-${process.env.EXT_INSTANCE_ID}-discovery`,
deletionTopic: `ext-${process.env.EXT_INSTANCE_ID}-deletion`,
หลัง การอ่าน process.env แบบธรรมดาของ FIREBASE_KIT_INSTANCE_ID ที่ใช้สำหรับพารามิเตอร์ทั่วไป 2 รายการเพื่อให้ผู้ใช้ลบล้างชื่อหัวข้อได้
// src/config.ts
const instanceId = process.env.FIREBASE_KIT_INSTANCE_ID;
// Non-empty defaults so Pub/Sub trigger bindings resolve during deploy
// discovery without freezing an empty topic name into the manifest.
discoveryTopicName: defineString("DISCOVERY_TOPIC_NAME", {
default: `kit-${instanceId}-discovery`,
}),
deletionTopicName: defineString("DELETION_TOPIC_NAME", {
default: `kit-${instanceId}-deletion`,
}),
ค่าเริ่มต้นต้องไม่ว่างเปล่าเนื่องจากการเชื่อมโยงทริกเกอร์จะได้รับการแก้ไขเมื่อค้นพบ ระบบจะเขียนค่าเริ่มต้นที่ว่างเปล่าลงในไฟล์ Manifest ของการติดตั้งใช้งานเป็นชื่อหัวข้อ นอกจากนี้ ชุดเครื่องมือยังป้องกันการทำงานนอกบริบทของชุดเครื่องมือด้วย หากไม่มีตัวแปร ค่าเริ่มต้นระดับโมดูลจะประเมินเป็น kit-undefined-discovery ดังนั้นตัวโหลดการกำหนดค่าจะดำเนินการไม่สำเร็จพร้อมข้อผิดพลาดที่อธิบายแทน ดังนี้
// ...
const instanceId = process.env.FIREBASE_KIT_INSTANCE_ID;
if (!instanceId) {
throw new Error(
"FIREBASE_KIT_INSTANCE_ID is not set. It is provided automatically to " +
"kit instances by firebase-tools >= 15.32.0; deploy or emulate this " +
"kit with a supported CLI version."
);
}
// ...
การตรวจสอบนี้จะทำงานเมื่อเครื่องจัดการแก้ไขการกำหนดค่าเป็นครั้งแรก ดังนั้นตัวแปรที่ขาดหายไปจะทำให้เกิดข้อผิดพลาดขณะรันไทม์ที่ชัดเจน แทนที่จะเป็นฟังก์ชันที่เชื่อมโยงกับหัวข้อ kit-undefined-* อย่างเงียบๆ หากต้องการปฏิเสธการติดตั้งใช้งาน ให้ตรวจสอบที่ขอบเขตโมดูลเพื่อให้ทำงานระหว่างการค้นพบ เนื่องจาก CLI ได้รับรหัสอินสแตนซ์จาก firebase.json จึงไม่มี INSTANCE_ID ที่กำหนดค่าได้ และไม่มีอะไรที่ต้องซิงค์ในหลายอินสแตนซ์
ข้อมูลลับ
ใน extension.yaml คุณประกาศข้อมูลลับด้วย type: secret รันไทม์ของส่วนขยายจะจัดเก็บและเชื่อมโยงข้อมูลเหล่านั้น เพื่อให้โค้ดส่วนขยายอ่าน process.env.PARAM_NAME ได้โดยตรง ในฐานของโค้ด Cloud Functions ทั่วไป
คุณจะประกาศและเชื่อมโยงข้อมูลลับแต่ละรายการอย่างชัดเจน ดังนี้
import { defineSecret } from "firebase-functions/params";
import { onRequest } from "firebase-functions/https";
const apiKey = defineSecret("API_KEY");
export const fn = onRequest({ secrets: [apiKey] }, handler);
เมื่อย้ายข้อมูลส่วนขยายไปยังแพ็กเกจ/ชุด npm แล้ว ระบบจะจัดการข้อมูลอ้างอิงข้อมูลลับในไฟล์ .env ของผู้ใช้ปลายทาง สิ่งสำคัญคือคุณต้องไม่เปลี่ยนชื่อข้อมูลลับที่ประกาศไว้ในโค้ดโดยเด็ดขาด ในระหว่างการย้ายข้อมูล ระบบจะย้ายข้อมูลลับของผู้ใช้ปลายทางตามไปด้วย
ตัวอย่างที่ใช้ได้จริง: ทริกเกอร์อีเมลจาก Cloud Firestore
ก่อน MAIL_COLLECTION และ SMTP_PASSWORD จะอ่านเป็นตัวแปรสภาพแวดล้อมดิบใน config.ts ดังนี้
# extension.yaml
- param: MAIL_COLLECTION
label: Email documents collection
type: string
default: mail
required: true
- param: SMTP_PASSWORD
label: SMTP password
type: secret
// functions/src/config.ts
mailCollection: process.env.MAIL_COLLECTION,
smtpPassword: process.env.SMTP_PASSWORD,
หลัง defineString 1 รายการและ defineSecret 1 รายการ โดย CLI จะค้นพบทั้ง 2 รายการและอ่านจาก .env:
import { defineString, defineSecret } from "firebase-functions/params";
import { onDocumentWritten } from "firebase-functions/firestore";
const mailCollection = defineString("MAIL_COLLECTION", {
label: "Email documents collection",
default: "mail"
});
const smtpPassword = defineSecret("SMTP_PASSWORD", { label: "SMTP password" });
export const processQueue = onDocumentWritten(
{ document: `${mailCollection}/{documentId}`, secrets: [smtpPassword] },
async (event) => {
const collection = mailCollection.value();
const password = smtpPassword.value();
// ...
}
);
5. ย้ายข้อมูลการโทรเข้าคิวงานภายใน
ส่วนขยายบางรายการจะจัดคิวงานลงในคิวงานของตัวเองจากภายในโค้ดฟังก์ชันโดยใช้ Firebase Admin SDK ซึ่งแตกต่างจากการรับงานที่ส่ง (ครอบคลุมในส่วนฟังก์ชันการอัปเกรดและฮุกวงจร Conversion) ในกรณีนี้ โค้ดของคุณคือผู้ผลิตที่เรียกใช้ queue.enqueue(...)
Admin SDK เวอร์ชันก่อนหน้ากำหนดให้ส่วนขยายต้องส่งรหัสอินสแตนซ์ส่วนขยายของตนเองเป็นพารามิเตอร์ที่ 2 เพื่อกำหนดเป้าหมายฟังก์ชันคิวงานในส่วนขยายเดียวกัน ตั้งแต่เวอร์ชัน firebase-admin 14.2.0 เป็นต้นไป ไม่จำเป็นต้องใช้และไม่แนะนำให้ใช้ ตอนนี้ Task Queue API จะกำหนดเป้าหมายคิวงานในบริบทเดียวกัน (เช่น ส่วนขยายหรือชุดเครื่องมือ) โดยค่าเริ่มต้น คุณสามารถนำพารามิเตอร์นี้ออกจากโค้ดได้อย่างปลอดภัยและเราขอแนะนำให้ทำเช่นนั้น ทั้งในรูปแบบส่วนขยายและฟังก์ชันแบบสแตนด์อโลน
การนำพารามิเตอร์นี้ออกจะช่วยให้มั่นใจได้ถึงความสามารถในการพกพาและความเข้ากันได้ในอนาคต
ส่วนอื่นๆ ทั้งหมดเกี่ยวกับการเรียกใช้ enqueue เช่น เส้นทางทรัพยากร locations/<region>/functions/<name>, เพย์โหลดของงาน และตรรกะการลองใหม่จะยังคงเหมือนเดิม
ดูรายละเอียดเพิ่มเติมเกี่ยวกับการจัดคิวฟังก์ชันด้วย Cloud Tasks ได้ที่จัดคิวฟังก์ชันด้วย Cloud Tasks
ก่อน ส่วนขยายรุ่นที่ 1:
import { getFunctions } from "firebase-admin/functions";
const queue = getFunctions().taskQueue(
`locations/${config.location}/functions/syncBigQuery`,
process.env.EXT_INSTANCE_ID, // extension instance ID, injected by the runtime
);
await queue.enqueue(taskData);
หลัง ส่วนขยายรุ่นที่ 2:
import { getFunctions } from "firebase-admin/functions";
const queue = getFunctions().taskQueue(
`locations/${process.env.FUNCTION_REGION}/functions/syncBigQuery`
);
await queue.enqueue(taskData);
หากการเรียก enqueue กำหนดเป้าหมายเป็นฐานของโค้ดที่มีคำนำหน้า ชื่อฟังก์ชันที่ค้นพบก็จะมีคำนำหน้าด้วย (เช่น orders-syncBigQuery) โปรดดูตรวจสอบและติดตั้งอินสแตนซ์ชุดฟังก์ชันทดแทนและทดสอบเป็นชุดฟังก์ชัน
6. ประกาศ API และบทบาท IAM ที่จำเป็น
ย้ายข้อกำหนด IAM และ API ของส่วนขยายออกจาก extension.yaml ไปยังโค้ด:
import { requiresAPI, requiresRole } from "firebase-functions";
requiresAPI("bigquery.googleapis.com", "Needed to write changelog rows");
requiresRole("roles/bigquery.dataEditor");
requiresRole("roles/bigquery.user");
เมื่อใช้การรักษาความปลอดภัยแบบประกาศ CLI ของ Firebase จะสร้างหรืออัปเดตบัญชีบริการรันไทม์ที่มีการจัดการสำหรับฐานของโค้ด และให้สิทธิ์รวมของบทบาทที่ประกาศทั้งหมด จัดทำเอกสารสำหรับผู้ใช้ว่าฟังก์ชันทั้งหมดในฐานของโค้ดจะทำงานด้วยบทบาทเหล่านั้น เว้นแต่ API สุดท้ายจะรองรับโมเดลที่แคบกว่า
ตัวอย่างการใช้งาน: สตรีม Cloud Firestore ไปยัง BigQuery
ก่อน ประกาศใน extension.yaml รันไทม์ของส่วนขยายเปิดใช้ API และให้บทบาทแก่บัญชีที่จัดการ
apis:
- apiName: bigquery.googleapis.com
roles:
- role: bigquery.dataEditor
- role: datastore.user
- role: bigquery.user
หลัง ประกาศในโค้ดด้วย requiresAPI และ requiresRole:
import { requiresAPI, requiresRole } from "firebase-functions";
requiresAPI(
"bigquery.googleapis.com",
"Needed to write changelog rows and views"
);
requiresRole("roles/bigquery.dataEditor");
requiresRole("roles/datastore.user");
requiresRole("roles/bigquery.user");
หากส่วนขยายเผยแพร่เหตุการณ์ Eventarc คุณยังต้องตั้งค่าบทบาทและ API ที่จำเป็นที่เหมาะสมสำหรับการเผยแพร่เหตุการณ์ด้วย ก่อนหน้านี้ส่วนขยายจะจัดการเรื่องนี้โดยที่คุณไม่ต้องทำการเปลี่ยนแปลงใดๆ ใน extensions.yaml คุณสามารถทำเช่นนี้แบบมีเงื่อนไขในโค้ดเพื่อให้ชุดเครื่องมือขอสิทธิ์เหล่านี้เฉพาะเมื่อใช้ช่องทาง Eventarc ที่กำหนดเองเท่านั้น
if (!!process.env.EVENTARC_CHANNEL) {
requiresRole("roles/eventarc.publisher");
requiresAPI(
"eventarcpublishing.googleapis.com",
"Publishes the extension's custom events to its Eventarc channel."
);
}
7. แปลงฮุกวงจร
หากส่วนขยายเรียกใช้ getExtensions().runtime() (เช่น setProcessingState หรือ setFatalError) ให้ลบการเรียกใช้เหล่านั้น เนื่องจากจะแสดงข้อผิดพลาดหากเรียกใช้จากฟังก์ชันรุ่นที่ 2 ที่ติดตั้งใช้งานตามปกติ ตอนนี้สถานะวงจรจะขับเคลื่อนโดย afterFirstDeploy และ afterRedeploy ซึ่งจะไม่มีการใช้การติดตามสถานะนี้
Firebase Extensions สามารถเรียกใช้การตั้งค่าเมื่อผู้ใช้ติดตั้ง อัปเดต หรือกำหนดค่าส่วนขยายใหม่ ในแพ็กเกจ npm ให้ประกาศการดำเนินการในวงจรที่เทียบเท่าในโค้ด
สำหรับการตั้งค่าแบบครั้งเดียว
import { afterFirstDeploy } from "firebase-functions/lifecycle";
import { onTaskDispatched } from "firebase-functions/tasks";
export const runInitialSetup = onTaskDispatched(async (request) => {
await initializeResources(request.data);
});
afterFirstDeploy({
task: {
function: "runInitialSetup",
body: {}
}
});
สำหรับการอัปเดตการกำหนดค่าหรือโค้ด
import { afterRedeploy } from "firebase-functions/lifecycle";
afterRedeploy({
task: {
function: "runInitialSetup",
body: { reconcile: true }
}
});
ทำให้การดำเนินการตามวงจรเป็นแบบ Idempotent ผู้ใช้อาจต้องเรียกใช้สคริปต์อีกครั้งด้วยตนเองหากการส่งหรือการดำเนินการล้มเหลว โดยทำดังนี้
firebase functions:lifecycle:run afterFirstDeploy CODEBASE_NAME
firebase functions:lifecycle:run afterRedeploy CODEBASE_NAME
ตัวอย่างการใช้งาน: สตรีม Cloud Firestore ไปยัง BigQuery
ก่อน lifecycleEvents ใน extension.yaml ซึ่งขับเคลื่อนโดยรันไทม์ของส่วนขยาย
lifecycleEvents:
onInstall:
function: initBigQuerySync
processingMessage: Configuring BigQuery Sync.
onUpdate:
function: setupBigQuerySync
processingMessage: Configuring BigQuery Sync
onConfigure:
function: setupBigQuerySync
processingMessage: Configuring BigQuery Sync
หลัง ประกาศในโค้ด งานจะจัดสรร BigQuery ในการติดตั้งใช้งานครั้งแรก:
import { afterFirstDeploy, afterRedeploy } from "firebase-functions/lifecycle";
afterFirstDeploy({ task: { function: "initBigQuerySync" } });
afterRedeploy({ task: { function: "setupBigQuerySync" } });
การจัดสรรเป็นแบบ Idempotent ดังนั้นการเรียกใช้ซ้ำจะปรับยอดชุดข้อมูล ตาราง และมุมมอง
ผู้ใช้สามารถเรียกใช้ซ้ำด้วยตนเองได้โดยใช้ firebase functions:lifecycle:run afterFirstDeploy
CODEBASE_NAME
8. การตั้งค่าเอกสารสำหรับผู้ใช้
เขียนREADMEแพ็กเกจที่อธิบายข้อมูลต่อไปนี้เป็นอย่างน้อย
- ค่า
.envที่แพ็กเกจต้องการ - ข้อมูลลับที่แพ็กเกจต้องการ และวิธีย้ายข้อมูลค่าข้อมูลลับที่มีอยู่
- บทบาท IAM ที่แพ็กเกจประกาศด้วย
requiresRole(...) - Google API ที่แพ็กเกจเปิดใช้หรือต้องใช้
- ฮุกวงจรที่แพ็กเกจประกาศ และวิธีเรียกใช้ซ้ำด้วยตนเอง
- หมายเหตุการเรียกเก็บเงิน
- สิ่งที่เปลี่ยนแปลงไปเมื่อเทียบกับส่วนขยายเดิม
- วิธีที่แพ็กเกจได้รับรหัสอินสแตนซ์ (
FIREBASE_KIT_INSTANCE_IDที่ตั้งค่าโดย CLI) และฟังก์ชันทั้งหมดของอินสแตนซ์ได้รับการติดตั้งใช้งานด้วยคำนำหน้าkit-<instanceId>-
ตัวอย่างการใช้งาน: สตรีม Cloud Firestore ไปยัง BigQuery
แพ็กเกจ README จะจัดส่งตาราง "สิ่งที่เปลี่ยนแปลง" ที่เป็นรูปธรรม ดังนี้
| ข้อกังวล | ในฐานะส่วนขยาย | ในฐานะ @firebase-function-kits/firestore-bigquery-export |
|---|---|---|
| การกำหนดค่า | พารามิเตอร์ส่วนขยาย | Cloud Functions พารามิเตอร์ผ่าน .env |
| IAM | ได้รับสิทธิ์จากส่วนขยาย | requiresRole(...), ใช้เมื่อติดตั้งใช้งาน |
| การจัดสรร | งานในวงจรโดยส่วนขยาย | afterFirstDeploy / afterRedeploy งาน |
| ชื่อฟังก์ชัน | ext-<instanceId>-fsexportbigquery |
fsexportbigquery (มีคำนำหน้าหรือไม่ก็ได้) |
| รหัสอินสแตนซ์ | EXT_INSTANCE_ID ที่แทรกโดยส่วนขยาย |
FIREBASE_KIT_INSTANCE_ID ตั้งค่าโดย CLI จาก firebase.json |
9. ทดสอบฟังก์ชันรุ่นที่ 2
ตอนนี้คุณควรมีฟังก์ชันรุ่นที่ 2 ซึ่งเมื่อติดตั้งใช้งานแล้วจะทำงานเหมือนกับการติดตั้งส่วนขยายใหม่ ขั้นตอนถัดไปคือการยืนยันและแก้ไขปัญหาที่อาจเกิดขึ้นโดยไม่ตั้งใจระหว่างการดำเนินการ
หากต้องการเรียกใช้ setGlobalOptions เพื่อตั้งค่าตัวเลือกส่วนกลาง เช่น ภูมิภาคหรือ CPU เริ่มต้น คุณต้องดำเนินการดังกล่าวเมื่อติดตั้งใช้งานชุดเป็นฟังก์ชันรุ่นที่ 2 แบบสแตนด์อโลนเท่านั้น เมื่อติดตั้งชุดเครื่องมือเป็นแพ็กเกจ npm ผู้ใช้จะเรียกใช้ setGlobalOptions ในโค้ดที่รวมไว้เพื่อกำหนดค่าพารามิเตอร์เหล่านี้ และจะได้รับคำเตือนหากเกิดเหตุการณ์นี้ขึ้น 2 ครั้ง
คุณสามารถป้องกันการเรียกนี้ได้โดยตรวจสอบตัวแปรสภาพแวดล้อม FIREBASE_KIT_INSTANCE_ID ดังนี้
import { setGlobalOptions } from "firebase-functions";
if (!process.env.FIREBASE_KIT_INSTANCE_ID) {
setGlobalOptions({
region: "us-east1",
maxInstances: 10,
});
}
ตรวจสอบว่าคุณใช้
firebase-tools
>= 15.32.0 และทำให้ฟังก์ชันรุ่นที่ 2 ที่แปลงแล้วใช้งานได้ในโปรเจ็กต์ทดสอบ
พร้อมทรัพยากรที่เหมาะสมเพื่อทดสอบลักษณะการทำงาน หากคุณตั้งค่าโปรเจ็กต์ทดสอบจากการทดสอบส่วนขยายไว้แล้ว ให้เรียกใช้คำสั่งต่อไปนี้
firebase deploy --only functions
กรอกข้อมูลในวิซาร์ดที่แสดงขึ้นซึ่งจะแจ้งให้คุณป้อนค่าพารามิเตอร์ในลักษณะเดียวกับที่คุณกรอกแบบฟอร์มการติดตั้งในคอนโซล Firebase สำหรับส่วนขยาย
ตัวอย่างการใช้งาน: สตรีม Cloud Firestore ไปยัง BigQuery
เราจะตรวจสอบการซิงค์ Cloud Firestore กับ BigQuery ตั้งแต่ต้นจนจบ ดังนี้
- ในหน้า Cloud Firestore ของคอนโซล Firebase ให้สร้างคอลเล็กชันที่คุณตั้งค่าเป็น
COLLECTION_PATH(users) หากยังไม่มี - สร้างเอกสารชื่อ
bigquery-mirror-testที่มีช่องใดก็ได้พร้อมค่าใดก็ได้ ในหน้า BigQuery ของคอนโซล Google Cloud ให้ค้นหาตารางบันทึกการเปลี่ยนแปลงดิบ โดยควรมีแถวเดียวที่บันทึกการสร้างเอกสาร ดังนี้
SELECT * FROM `PROJECT_ID.analytics.users_raw_changelog`ค้นหามุมมองล่าสุด ซึ่งควรแสดงเหตุการณ์การเปลี่ยนแปลงล่าสุดสำหรับเอกสารเดียวที่มีอยู่ (
bigquery-mirror-test) ดังนี้SELECT * FROM `PROJECT_ID.analytics.users_raw_latest`ลบเอกสาร
bigquery-mirror-testใน Cloud Firestore โดยจะหายไปจากมุมมองล่าสุด และระบบจะเพิ่มเหตุการณ์DELETEต่อท้ายตารางบันทึกการเปลี่ยนแปลงดิบคุณสามารถตรวจสอบประวัติทั้งหมดของเอกสารรายการเดียวได้โดยทำดังนี้
SELECT * FROM `PROJECT_ID.analytics.users_raw_changelog` WHERE document_name = "bigquery-mirror-test" ORDER BY timestamp ASC
ความแตกต่างจากการทดสอบส่วนขยาย
- ทริกเกอร์จะติดตั้งใช้งานเป็น
fsexportbigquery(ไม่มีคำนำหน้าเมื่อติดตั้งใช้งานเป็นฟังก์ชันรุ่นที่ 2 ทั่วไปและไม่ใช่ชุดอุปกรณ์) ไม่ใช่ext-<instanceId>-fsexportbigqueryมองหาชื่อนั้นในแดชบอร์ดและบันทึกของ Cloud Functions - ตอนนี้โค้ดของคุณจะทำงานใน Firebase Local Emulator Suite เป็นฟังก์ชันปกติ
คุณตั้งค่าพารามิเตอร์เพื่อใช้ในโปรแกรมจำลองได้ด้วย
.env.localนอกจากนี้ คุณยังทำ Unit Test โค้ดโดยใช้ SDK ของfirebase-functions-testได้ตามที่อธิบายไว้ในการทำ Unit Test ของ Cloud Functions - รันไทม์ของส่วนขยายจะไม่ขับเคลื่อนการจัดสรรอีกต่อไป หากตารางบันทึกการเปลี่ยนแปลงหายไปหลังจากติดตั้งใช้งาน ให้เรียกใช้งานการตั้งค่าอีกครั้งด้วยตนเอง:
firebase functions:lifecycle:run afterFirstDeploy CODEBASE_NAMEงานนี้เป็นแบบ Idempotent ดังนั้นการเรียกใช้อีกครั้งจะปรับยอดชุดข้อมูล ตาราง และมุมมอง - ค่าพารามิเตอร์มาจาก
.envแทนที่จะเป็นแบบฟอร์มการติดตั้ง ดังนั้นการเรียกใช้firebase deployซ้ำจึงไม่จำเป็นต้องมีการโต้ตอบเมื่อ.envเสร็จสมบูรณ์แล้ว
10. เผยแพร่ชุดฟังก์ชันใน npm
เมื่อตรวจสอบ Conversion จากส่วนขยายเป็นฟังก์ชันรุ่นที่ 2 แล้ว คุณจะเผยแพร่รุ่นที่อาจได้รับการเผยแพร่ไปยัง npm เพื่อทำการทดสอบตั้งแต่ต้นจนจบได้โดยใช้คำแนะนำอย่างใดอย่างหนึ่งต่อไปนี้
เมื่อเผยแพร่ชุดเครื่องมือไปยัง npm แล้ว คุณจะติดตั้งชุดเครื่องมือดังกล่าวได้ด้วย firebase functions:kits:install และแสดงเป็นรายการทดแทนอย่างเป็นทางการสำหรับส่วนขยาย
เราขอแนะนำอย่างยิ่งให้เผยแพร่รุ่นที่อาจได้รับการเผยแพร่ก่อน ระบบจะติดตั้งชุดเครื่องมือตามชื่อและเวอร์ชันของแพ็กเกจ ดังนั้นรุ่นทดลองจะช่วยให้คุณทดสอบขั้นตอนการติดตั้งจริงกับรีจิสทรีได้โดยไม่ต้องแสดงแพ็กเกจที่ยังไม่เสร็จสมบูรณ์ต่อผู้ใช้ที่ติดตั้งด้วยแท็ก latest เริ่มต้น
ก่อนเผยแพร่ ให้ทำดังนี้
- เลือกชื่อแพ็กเกจ ทั้งชื่อที่กำหนดขอบเขตและไม่ได้กำหนดขอบเขตจะใช้ได้ (ดูคำแนะนำด้านบน) โปรดทราบว่าแพ็กเกจที่กำหนดขอบเขตจะเป็นแบบส่วนตัวโดยค่าเริ่มต้น ดังนั้นให้ส่ง
--access public - สร้างและตรวจสอบสิ่งที่จัดส่ง
mainและtypesชี้ไปที่เอาต์พุตที่คอมไพล์แล้ว (lib/ในตัวอย่างที่ใช้งานได้) ดังนั้นไดเรกทอรีดังกล่าวต้องรวมอยู่ในไฟล์ tar ที่เผยแพร่ ใช้.npmignoreหรือรายการที่อนุญาตfilesและตรวจสอบผลลัพธ์ด้วยnpm pack --dry-runสคริปต์prepublishOnlyที่เรียกใช้บิลด์จะป้องกันไม่ให้เผยแพร่เอาต์พุตที่ล้าสมัย
package.json สิ่งที่เพิ่มเข้ามาเพื่อทำให้การเผยแพร่ปลอดภัยโดยค่าเริ่มต้นมีดังนี้
{
"files": ["lib", "README.md", "CHANGELOG.md"],
"publishConfig": { "access": "public", "tag": "next" },
"scripts": {
"build": "tsc -b",
"prepublishOnly": "npm run build && npm test"
}
}
ช่อง "publishConfig": { "tag": "next" } ช่วยให้มั่นใจว่า npm
publish แบบธรรมดาจะไม่เขียนทับ latest
การตัดรุ่นที่อาจได้รับการเผยแพร่:
เช่น หากต้องการเพิ่มเวอร์ชันในเครื่องจาก 0.0.2-rc.3 เป็น 0.0.2-rc.4
(การดำเนินการนี้จะคอมมิตและติดแท็กใน Git หาก package.json อยู่ที่รูทของที่เก็บ) ให้ทำดังนี้
npm version prerelease --preid rc
วิธีเผยแพร่รุ่นที่อาจได้รับการเผยแพร่และลงทะเบียน @your-org/your-kit@0.0.2-rc.4 ใน npm ภายใต้แท็ก next
npm publish
เว็บไซต์ npm อาจใช้เวลา 2-3 นาทีในการแสดงเวอร์ชันใหม่ npm view จะอ่านรีจิสทรีโดยตรง ดังนี้
npm view @your-org/your-kit versions dist-tags
เมื่อขั้นตอนที่ 11 และขั้นตอนที่ 12 ของคู่มือนี้เสร็จสมบูรณ์แล้ว คุณจะโปรโมตแพ็กเกจเป็นเวอร์ชันเสถียรได้โดยทำดังนี้
npm version 0.0.2
npm publish --tag latest
npm dist-tag add @your-org/your-kit@0.0.2 next
หมายเหตุเกี่ยวกับ npm-shrinkwrap.json: เราขอแนะนำอย่างยิ่งให้รวมไฟล์ npm-shrinkwrap.json ไว้ในแพ็กเกจ โดย CLI จะเตือนผู้ใช้เมื่อติดตั้งหากคุณไม่รวมไฟล์นี้ไว้ ซึ่งจะช่วยให้มั่นใจได้ว่าผู้ใช้จะใช้ทรัพยากร Dependency เดียวกันกับที่คุณทดสอบ และช่วยป้องกันการโจมตีซัพพลายเชน อย่างไรก็ตาม ระบบจะใช้ Shrinkwrap แบบคำต่อคำในโปรเจ็กต์ของผู้ใช้ รวมถึงในระหว่างการบิลด์ Cloud Functions (npm ci) ซึ่งรายการสำหรับนักพัฒนาแอปเท่านั้นอาจไม่สำเร็จด้วย EBADPLATFORM คุณอาจต้องนำรายการ "dev": true และ devDependencies ออกจากสำเนา Shrinkwrap ที่เผยแพร่
ตัวอย่างการใช้งาน: สตรีม Cloud Firestore ไปยัง BigQuery
package.json ของชุดเครื่องมือ ณ เวลาที่เปิดตัวรุ่นที่อาจได้รับการเผยแพร่รุ่นที่ 4 มีดังนี้
{
"name": "@firebase-function-kits/firestore-bigquery-export",
"version": "0.0.2-rc.4",
"repository": {
"type": "git",
"url": "https://github.com/firebase/extensions.git",
"directory": "kits/firestore-bigquery-export"
},
"main": "lib/index.js",
"types": "lib/index.d.ts",
"engines": { "node": "22" },
"scripts": { "build": "tsc -b" }
}
โปรดทราบว่าในกรณีนี้ ชุดเครื่องมือจะอยู่ใน Monorepo ดังนั้นจึงต้องใส่ repository.directory เพื่อให้ลิงก์รีจิสทรี npm ชี้ไปยังโฟลเดอร์ที่ถูกต้อง CHANGELOG.md มีบันทึกสำหรับรุ่นที่รอดำเนินการ
11. ทดสอบเป็นชุดฟังก์ชัน
เมื่อเผยแพร่ชุดแล้ว เราขอแนะนำให้ทดสอบชุดโดยใช้ npm
ตรวจสอบว่าคุณใช้ firebase-tools เวอร์ชัน >= 15.32.0 และติดตั้งชุดเครื่องมือต่อไปนี้
firebase functions:kits:install --package <your-package-name>@<your-prerelease-version>
ซึ่งจะดาวน์โหลดแพ็กเกจจาก npm, ตั้งค่าภายในไดเรกทอรีต้นทางใหม่สำหรับชุด และแนะนำวิธีกำหนดค่าอินสแตนซ์แรกที่คล้ายกับขั้นตอนการติดตั้งส่วนขยาย เมื่อติดตั้งและตั้งค่าแพ็กเกจในเครื่องแล้ว ให้เรียกใช้การทำให้ใช้งานได้เพื่อสร้างทรัพยากรในโปรเจ็กต์ Google Cloud ดังนี้
firebase deploy --only functions:<your-kit-instance-id>
หลังจากติดตั้งแล้ว Firebase CLI จะพิมพ์คำสั่งทำให้ใช้งานได้ที่คล้ายกันพร้อมรหัสอินสแตนซ์ที่แน่นอนที่คุณเลือกไว้ระหว่างการติดตั้ง
ตรวจสอบชุดอุปกรณ์อีกครั้งโดยทำตามวิธีการในขั้นตอนที่ 9 ทดสอบฟังก์ชันรุ่นที่ 2 ตอนนี้คุณกำลังติดตั้งใช้งานโดยใช้ชุดเครื่องมือ ฟังก์ชันของคุณจึงมีคำนำหน้าและชื่อเป็น kit-<instance-id>-<method-name> ซึ่งช่วยให้ชุดอุปกรณ์มีอินสแตนซ์หลายรายการ โดยติดตั้งใช้งานฟังก์ชันเดียวกันหลายครั้งในโปรเจ็กต์ โดยแต่ละรายการมีชื่อที่ไม่ซ้ำกัน
12. ทดสอบการแทนที่การย้ายข้อมูล
คุณสามารถตั้งค่าอินสแตนซ์ส่วนขยายที่ใช้งานได้ แล้วใช้คำแนะนำในการย้ายข้อมูลผู้ใช้ (โดยใช้ firebase ext:migrate --package หรือคำสั่ง CLI ของชุดฟังก์ชัน) เพื่อทดสอบชุดฟังก์ชันให้เสร็จสิ้นเพื่อใช้แทนการย้ายข้อมูล
13. แจ้งให้ผู้ใช้และ Google ทราบถึงส่วนขยายอย่างเป็นทางการที่ใช้แทน
เมื่อชุดฟังก์ชันทดแทนพร้อมใช้งานเป็นแพ็กเกจ npm ที่ผู้ใช้ควรย้ายข้อมูลไปใช้แล้ว ให้แจ้งทั้งผู้ใช้และ Google เกี่ยวกับการแทนที่อย่างเป็นทางการนี้ อัปเดตไฟล์ README.md ในที่เก็บ GitHub ที่โฮสต์ส่วนขยายด้วยข้อมูลต่อไปนี้
<!-- FIREBASE_EXTENSION_REPLACEMENT: extension="<your-extesion-id>" package="<your-npm-package-name>" -->
> [!WARNING]
> **Deprecation Notice:** The Firebase Extension `<your-extension>` is deprecated. Migrate to the [<your-npm-package-name>](<link-to-your-npm-package>) package.
Google จะสแกน README ของส่วนขยายที่รู้จักเพื่อหาความคิดเห็น เช่น <!--
FIREBASE_EXTENSION_REPLACEMENT: extension="firebase/firestore-bigquery-export"
package="@firebase-function-kits/firestore-bigquery-export" --> และใช้ข้อมูลนี้เพื่อสร้างรีจิสทรีอย่างเป็นทางการของรายการทดแทนที่จัดเก็บไว้ในที่เก็บ firebase-tools เป็น replacements.json
นอกจากนี้ คุณยังตรวจสอบ replacements.json เพื่อดูว่าระบบจะสแกน README.md ใดสำหรับส่วนขยายของคุณได้ด้วย รายการอย่างเป็นทางการของผลิตภัณฑ์ทดแทนจะได้รับการอัปเดตทุกสัปดาห์
ตัวอย่างการใช้งาน: สตรีม Cloud Firestore ไปยัง BigQuery
ส่วนขยาย firestore-bigquery-export
README.md
มีข้อมูลต่อไปนี้
<!-- FIREBASE_EXTENSION_REPLACEMENT: extension="firebase/firestore-bigquery-export" package="@firebase-function-kits/firestore-bigquery-export" -->
> [!WARNING]
> **Deprecation Notice:** The Firebase Extension `firebase/firestore-bigquery-export` is deprecated. Please migrate to the [`@firebase-function-kits/firestore-bigquery-export`](https://www.npmjs.com/package/@firebase-function-kits/firestore-bigquery-export) package.