เพิ่มฮุกผู้ใช้ลงในส่วนขยาย
จัดทุกอย่างให้เป็นระเบียบอยู่เสมอด้วยคอลเล็กชัน
บันทึกและจัดหมวดหมู่เนื้อหาตามค่ากำหนดของคุณ
คุณสามารถให้สิทธิ์แก่ผู้ใช้ที่ติดตั้งส่วนขยายของคุณในการแทรกตรรกะที่กำหนดเองลงในการดำเนินการส่วนขยายของคุณได้
ซึ่งทำได้ 2 วิธีดังนี้
เหตุการณ์ Eventarc: หากต้องการให้ผู้ใช้มีวิธีตอบสนองต่อเหตุการณ์แบบไม่พร้อมกัน
คุณสามารถเผยแพร่ไปยัง Eventarc ได้ ผู้ใช้สามารถติดตั้งใช้งานฟังก์ชันตัวแฮนเดิลเหตุการณ์
ที่ส่งการแจ้งเตือนหลังจากงานที่ใช้เวลานาน
เสร็จสมบูรณ์ หรือจะกำหนดฟังก์ชันการประมวลผลภายหลังของตนเองก็ได้
Hook แบบซิงโครนัส: หากต้องการให้ผู้ใช้เพิ่มตรรกะการบล็อกลงในส่วนขยาย คุณสามารถเพิ่ม Hook แบบซิงโครนัสในจุดที่กำหนดไว้ล่วงหน้าในการดำเนินการของส่วนขยายได้ ในจุดเหล่านี้ คุณจะเรียกใช้ฟังก์ชันผู้ให้บริการของผู้ใช้
และดำเนินการต่อหลังจากที่ฟังก์ชันเสร็จสมบูรณ์แล้วเท่านั้น โดยงานประมวลผลล่วงหน้ามักจะอยู่ในหมวดหมู่นี้
ส่วนขยายจะใช้วิธีใดวิธีหนึ่งหรือทั้ง 2 วิธีก็ได้
เหตุการณ์ Eventarc
วิธีเผยแพร่กิจกรรมจากส่วนขยาย
ประกาศประเภทเหตุการณ์ที่คุณจะเผยแพร่ในไฟล์ extension.yaml
ดังนี้
events:
- type: publisher-id.extension-name.version.event-name
description: event-description
- type: publisher-id.extension-name.version.another-event-name
description: another-event-description
ตัวระบุ type
ประกอบด้วยฟิลด์หลายรายการที่คั่นด้วยจุด ต้องระบุฟิลด์รหัสผู้เผยแพร่โฆษณา ชื่อส่วนขยาย และชื่อเหตุการณ์
ขอแนะนำให้ใช้ช่องเวอร์ชัน เลือกชื่อกิจกรรมที่ไม่ซ้ำและสื่อความหมาย
สำหรับประเภทกิจกรรมแต่ละประเภทที่คุณเผยแพร่
เช่น ส่วนขยาย storage-resize-images
ประกาศประเภทเหตุการณ์เดียว
events:
- type: firebase.extensions.storage-resize-images.v1.complete
description: |
Occurs when image resizing completes. The event will contain further
details about specific formats and sizes.
ผู้ใช้จะเลือกกิจกรรมที่ต้องการติดตามได้เมื่อ
ติดตั้งส่วนขยาย
ในฟังก์ชันส่วนขยาย ให้นำเข้า Eventarc API จาก Admin SDK
และเริ่มต้นช่องเหตุการณ์โดยใช้การตั้งค่าการติดตั้งของผู้ใช้
การตั้งค่าเหล่านี้จะแสดงโดยใช้ตัวแปรสภาพแวดล้อมต่อไปนี้
EVENTARC_CHANNEL
: ชื่อที่สมบูรณ์ของแชแนล Eventarc ที่ผู้ใช้เลือกเผยแพร่เหตุการณ์
EXT_SELECTED_EVENTS
: รายการประเภทกิจกรรมที่คั่นด้วยคอมมาซึ่งผู้ใช้เลือกที่จะเผยแพร่
เมื่อเริ่มต้นช่องด้วยค่านี้ Admin SDK จะกรองเหตุการณ์ที่ผู้ใช้ไม่ได้เลือกออกโดยอัตโนมัติ
EVENTARC_CLOUD_EVENT_SOURCE
: ตัวระบุแหล่งที่มาของ CloudEvent
Admin SDK จะส่งค่านี้โดยอัตโนมัติในฟิลด์ source
ของ
เหตุการณ์ที่เผยแพร่ โดยปกติแล้ว คุณไม่จำเป็นต้องใช้ตัวแปรนี้อย่างชัดแจ้ง
หากไม่ได้เปิดใช้เหตุการณ์ในระหว่างการติดตั้ง ตัวแปรเหล่านี้จะเป็น
undefined คุณใช้ข้อเท็จจริงนี้เพื่อเริ่มต้นช่องเหตุการณ์ได้เฉพาะเมื่อเปิดใช้เหตุการณ์เท่านั้น โดยทำดังนี้
import * as admin from "firebase-admin";
import {getEventarc} from 'firebase-admin/eventarc';
admin.initializeApp();
// Set eventChannel to a newly-initialized channel, or `undefined` if events
// aren't enabled.
const eventChannel =
process.env.EVENTARC_CHANNEL &&
getEventarc().channel(process.env.EVENTARC_CHANNEL, {
allowedEventTypes: process.env.EXT_SELECTED_EVENTS,
});
เผยแพร่เหตุการณ์ไปยังช่องในส่วนของส่วนขยายที่คุณต้องการ
แสดงต่อผู้ใช้ เช่น
// If events are enabled, publish a `complete` event to the configured
// channel.
eventChannel && eventChannel.publish({
type: 'firebase.extensions.storage-resize-images.v1.complete',
subject: filename, // the name of the original file
data: {
// ...
}
});
บันทึกเหตุการณ์ที่คุณเผยแพร่ในไฟล์ PREINSTALL หรือ POSTINSTALL
สำหรับแต่ละเหตุการณ์ ให้บันทึกข้อมูลต่อไปนี้
- วัตถุประสงค์ที่ตั้งใจไว้
- จุดในตรรกะของส่วนขยายที่ทำงาน
- ข้อมูลเอาต์พุตที่รวมอยู่
- เงื่อนไขในการดำเนินการ
นอกจากนี้ ให้เตือนผู้ใช้ไม่ให้ดำเนินการใดๆ ในตัวแฮนเดิลเหตุการณ์ที่อาจทริกเกอร์ส่วนขยายเดียวกัน ซึ่งจะส่งผลให้เกิดลูปไม่สิ้นสุด
เมื่อเผยแพร่เหตุการณ์จากส่วนขยาย ผู้ใช้จะสามารถติดตั้งใช้งานตัวแฮนเดิลเหตุการณ์
เพื่อตอบสนองด้วยตรรกะที่กำหนดเองได้
เช่น ตัวอย่างต่อไปนี้จะลบรูปภาพต้นฉบับหลังจากที่ปรับขนาดแล้ว
โปรดทราบว่าตัวแฮนเดิลตัวอย่างนี้ใช้พร็อพเพอร์ตี้ subject
ของเหตุการณ์ ซึ่งในกรณีนี้คือชื่อไฟล์เดิมของรูปภาพ
exports.onimageresized = onCustomEventPublished(
"firebase.extensions.storage-resize-images.v1.complete",
(event) => {
logger.info("Received image resize completed event", event);
// For example, delete the original.
return admin.storage()
.bucket("my-project.firebasestorage.app")
.file(event.subject)
.delete();
});
ดูข้อมูลเพิ่มเติมได้ที่ทริกเกอร์เหตุการณ์ที่กําหนดเอง
ตัวอย่าง
ส่วนขยายปรับขนาดรูปภาพอย่างเป็นทางการ
มี Hook แบบไม่พร้อมกันโดยการเผยแพร่ไปยัง Eventarc
หลังจากปรับขนาดรูปภาพ
ฮุกแบบซิงโครนัส
เมื่อต้องการมอบฮุกให้แก่ผู้ใช้ซึ่งต้องดำเนินการให้เสร็จสมบูรณ์
เพื่อให้ฟังก์ชันส่วนขยายอย่างใดอย่างหนึ่งทำงาน ให้ใช้ฮุกแบบซิงโครนัส
Hook แบบซิงโครนัสจะเรียกใช้ HTTPS Callable Cloud
Function ที่ผู้ใช้กำหนด และรอให้เสร็จสมบูรณ์ (อาจมีค่าที่ส่งคืน) ก่อนที่จะดำเนินการต่อ ข้อผิดพลาดในฟังก์ชันที่ผู้ใช้ระบุ
ส่งผลให้เกิดข้อผิดพลาดในฟังก์ชันส่วนขยาย
วิธีเปิดเผย Hook แบบซิงโครนัส
เพิ่มพารามิเตอร์ลงในส่วนขยายเพื่อให้ผู้ใช้กำหนดค่าส่วนขยายด้วย URL ไปยัง Cloud Function ที่กำหนดเองได้ เช่น
- param: PREPROCESSING_FUNCTION
label: Pre-processing function URL
description: >
An HTTPS callable function that will be called to transform the input data
before it is processed by this function.
type: string
example: https://us-west1-my-project-id.cloudfunctions.net/preprocessData
required: false
ในจุดที่ต้องการแสดง Hook ในส่วนขยาย ให้เรียกฟังก์ชันโดยใช้ URL เช่น
const functions = require('firebase-functions/v1');
const fetch = require('node-fetch');
const preprocessFunctionURL = process.env.PREPROCESSING_FUNCTION;
exports.yourFunctionName = functions.firestore.document("collection/{doc_id}")
.onWrite((change, context) => {
// PREPROCESSING_FUNCTION hook begins here.
// If a preprocessing function is defined, call it before continuing.
if (preprocessFunctionURL) {
try {
await fetch(preprocessFunctionURL); // Could also be a POST request if you want to send data.
} catch (e) {
// Preprocessing failure causes the function to fail.
functions.logger.error("Preprocessor error:", e);
return;
}
}
// End of PREPROCESSING_FUNCTION hook.
// Main function logic follows.
// ...
});
บันทึก Hook ที่คุณทำให้พร้อมใช้งานในไฟล์ PREINSTALL หรือ POSTINSTALL
สำหรับแต่ละฮุก ให้บันทึกข้อมูลต่อไปนี้
- วัตถุประสงค์ที่ตั้งใจไว้
- จุดในตรรกะของส่วนขยายที่ทำงาน
- อินพุตและเอาต์พุตที่คาดไว้
- เงื่อนไข (หรือตัวเลือก) ในการดำเนินการ
นอกจากนี้ ให้เตือนผู้ใช้ไม่ให้ดำเนินการใดๆ ในฟังก์ชัน Hook
ที่อาจทริกเกอร์ส่วนขยายเดียวกัน ซึ่งจะส่งผลให้เกิดลูป
ไม่สิ้นสุด
ตัวอย่าง
ส่วนขยาย Algolia Search
มี Hook แบบซิงโครนัสสำหรับเรียกใช้ฟังก์ชันการแปลงที่ผู้ใช้ระบุ
ก่อนที่จะเขียนไปยัง Algolia
เนื้อหาของหน้าเว็บนี้ได้รับอนุญาตภายใต้ใบอนุญาตที่ต้องระบุที่มาของครีเอทีฟคอมมอนส์ 4.0 และตัวอย่างโค้ดได้รับอนุญาตภายใต้ใบอนุญาต Apache 2.0 เว้นแต่จะระบุไว้เป็นอย่างอื่น โปรดดูรายละเอียดที่นโยบายเว็บไซต์ Google Developers Java เป็นเครื่องหมายการค้าจดทะเบียนของ Oracle และ/หรือบริษัทในเครือ
อัปเดตล่าสุด 2025-07-25 UTC
[null,null,["อัปเดตล่าสุด 2025-07-25 UTC"],[],[],null,["# Add user hooks to an extension\n\n\u003cbr /\u003e\n\nYou can provide users who install your extension the ability to insert their own\ncustom logic into the execution of your extension. There are two ways to\naccomplish this:\n\n- **Eventarc events**: to give users a way to asynchronously react to\n events, you can publish to Eventarc. Users can deploy event handler\n functions that, for example, send notifications after long-running\n tasks complete, or they can define their own post-processing functions.\n\n- **Synchronous hooks**: to give users a way to add blocking logic to your\n extension, you can add synchronous hooks at predefined points in the\n extension's operation. At these points, you run a user-provider function\n and proceed only after it completes. Pre-processing tasks often fall under\n this category.\n\nAn extension can use either or both methods.\n\nEventarc events\n---------------\n\nTo publish events from an extension:\n\n1. Declare the event types you will publish in the `extension.yaml` file:\n\n events:\n - type: publisher-id.extension-name.version.event-name\n description: event-description\n - type: publisher-id.extension-name.version.another-event-name\n description: another-event-description\n\n The `type` identifier is made of several dot-delimited fields. The\n [publisher ID](/docs/extensions/publishers/register), extension name, and event name fields are\n required. The version field is recommended. Choose a unique and descriptive\n event name for each event type you publish.\n\n For example, the [`storage-resize-images` extension](https://github.com/firebase/extensions/blob/next/storage-resize-images/extension.yaml)\n declares a single event type: \n\n events:\n - type: firebase.extensions.storage-resize-images.v1.complete\n description: |\n Occurs when image resizing completes. The event will contain further\n details about specific formats and sizes.\n\n Users will be able to choose which events to subscribe to when they\n install the extension.\n2. In your extension functions, import the Eventarc API from the Admin SDK\n and initialize an event channel using the user's installation settings.\n These settings are exposed using the following environment variables:\n\n - `EVENTARC_CHANNEL`: the fully-qualified name of the Eventarc channel to which the user chose to publish events.\n - `EXT_SELECTED_EVENTS`: a comma-separated list of event types the user chose to publish. When you initialize a channel with this value, the Admin SDK automatically filters out events user did not select.\n - `EVENTARC_CLOUD_EVENT_SOURCE`: the Cloud Event source identifier. The Admin SDK automatically passes this value in the `source` field of published events. You typically don't need to explicitly use this variable.\n\n If events weren't enabled at installation, these variables will be\n undefined. You can use this fact to initialize an event channel only when\n events are enabled: \n\n import * as admin from \"firebase-admin\";\n import {getEventarc} from 'firebase-admin/eventarc';\n\n admin.initializeApp();\n\n // Set eventChannel to a newly-initialized channel, or `undefined` if events\n // aren't enabled.\n const eventChannel =\n process.env.EVENTARC_CHANNEL &&\n getEventarc().channel(process.env.EVENTARC_CHANNEL, {\n allowedEventTypes: process.env.EXT_SELECTED_EVENTS,\n });\n\n3. Publish events to the channel at the points in your extension you want to\n expose to users. For example:\n\n // If events are enabled, publish a `complete` event to the configured\n // channel.\n eventChannel && eventChannel.publish({\n type: 'firebase.extensions.storage-resize-images.v1.complete',\n subject: filename, // the name of the original file\n data: {\n // ...\n }\n });\n\n4. Document the events you publish, in either the PREINSTALL or POSTINSTALL\n file.\n\n For each event, document the following:\n - Its intended purpose\n - The point in your extension's logic it runs\n - The output data it includes\n - The conditions for its execution\n\n Additionally, warn users not to perform any actions in their event\n handlers that might trigger the same extension, resulting in an infinite\n loop.\n\nWhen you publish events from an extension, users can deploy event handlers\nto respond with custom logic.\n\nFor example, the following example deletes the original image after it has been\nresized. Note that this example handler makes use of the `subject` property of\nthe event, which in this case is the image's original filename. \n\n exports.onimageresized = onCustomEventPublished(\n \"firebase.extensions.storage-resize-images.v1.complete\",\n (event) =\u003e {\n logger.info(\"Received image resize completed event\", event);\n // For example, delete the original.\n return admin.storage()\n .bucket(\"my-project.firebasestorage.app\")\n .file(event.subject)\n .delete();\n });\n\nSee [Custom event triggers](/docs/functions/custom-events#handle-events) for more\ninformation.\n\n### Example\n\nThe official [Resize Images extension](https://github.com/firebase/extensions/tree/next/storage-resize-images)\nprovides an asynchronous hook by [publishing to Eventarc](https://github.com/firebase/extensions/blob/c29781c7e67c004e2491e4ce3c43b25b05bd3de6/storage-resize-images/functions/src/index.ts#L109-L117)\nafter resizing an image.\n\nSynchronous hooks\n-----------------\n\nWhen you want to provide users with a hook that must complete successfully\nfor one of your extension functions to operate, use *synchronous hooks*.\n\nA synchronous hook calls a user-defined [HTTPS callable Cloud\nFunction](/docs/functions/http-events) and awaits completion (possibly with a\nreturned value) before continuing. An error in the user-provided function\nresults in an error in the extension function.\n\nTo expose a synchronous hook:\n\n1. Add a parameter to your extension that allows users to configure the\n extension with the URL to their custom Cloud Function. For example:\n\n - param: PREPROCESSING_FUNCTION\n label: Pre-processing function URL\n description: \u003e\n An HTTPS callable function that will be called to transform the input data\n before it is processed by this function.\n type: string\n example: https://us-west1-my-project-id.cloudfunctions.net/preprocessData\n required: false\n\n2. At the point in your extension where you want to expose the hook, call the\n function using its URL. For example:\n\n const functions = require('firebase-functions/v1');\n const fetch = require('node-fetch');\n\n const preprocessFunctionURL = process.env.PREPROCESSING_FUNCTION;\n\n exports.yourFunctionName = functions.firestore.document(\"collection/{doc_id}\")\n .onWrite((change, context) =\u003e {\n // PREPROCESSING_FUNCTION hook begins here.\n // If a preprocessing function is defined, call it before continuing.\n if (preprocessFunctionURL) {\n try {\n await fetch(preprocessFunctionURL); // Could also be a POST request if you want to send data.\n } catch (e) {\n // Preprocessing failure causes the function to fail.\n functions.logger.error(\"Preprocessor error:\", e);\n return;\n }\n }\n // End of PREPROCESSING_FUNCTION hook.\n\n // Main function logic follows.\n // ...\n });\n\n3. Document any hooks you make available in either the PREINSTALL or\n POSTINSTALL file.\n\n For each hook, document the following:\n - Its intended purpose\n - The point in your extension's logic it runs\n - Its expected inputs and outputs\n - The conditions (or options) for its execution\n\n Additionally, warn users not to perform any actions in the hook\n function that might trigger the same extension, resulting in an infinite\n loop.\n\n### Example\n\nThe [Algolia Search extension](https://github.com/algolia/firestore-algolia-search/)\nprovides a synchronous hook to [call a user-supplied transform function](https://github.com/algolia/firestore-algolia-search/blob/34592d513eac22691d76917874a6466032976f67/functions/src/transform.ts)\nprior to writing to Algolia."]]