Bắt đầu tạo tiện ích

Trang này hướng dẫn bạn các bước cần thiết để tạo một Tiện ích Firebase đơn giản. Bạn có thể cài đặt tiện ích này trong các dự án của mình hoặc chia sẻ với người khác. Ví dụ đơn giản này về Tiện ích Firebase sẽ theo dõi Cơ sở dữ liệu theo thời gian thực của bạn để tìm thông báo và chuyển đổi các thông báo đó thành chữ hoa.

1. Thiết lập môi trường và khởi chạy dự án

Trước khi có thể bắt đầu tạo một tiện ích, bạn cần thiết lập môi trường xây dựng bằng các công cụ bắt buộc.

  1. Cài đặt Node.js 16 trở lên. Một cách để cài đặt Node là sử dụng nvm (hoặc nvm-windows).

  2. Cài đặt hoặc cập nhật lên phiên bản mới nhất của Giao diện dòng lệnh (CLI) của Firebase. Để cài đặt hoặc cập nhật bằng npm, hãy chạy lệnh sau:

    npm install -g firebase-tools
    

Bây giờ, hãy sử dụng Firebase CLI để khởi chạy một dự án tiện ích mới:

  1. Tạo thư mục cho tiện ích và cd vào thư mục đó:

    mkdir rtdb-uppercase-messages && cd rtdb-uppercase-messages
    
  2. Chạy lệnh ext:dev:init của Firebase CLI:

    firebase ext:dev:init
    

    Khi được nhắc, hãy chọn JavaScript làm ngôn ngữ cho các hàm (nhưng lưu ý rằng bạn cũng có thể sử dụng TypeScript khi phát triển tiện ích của riêng mình) và khi được yêu cầu cài đặt các phần phụ thuộc, hãy trả lời "có". (Chấp nhận các tuỳ chọn mặc định cho mọi tuỳ chọn khác.) Lệnh này sẽ thiết lập cơ sở mã khung cho một tiện ích mới, từ đó bạn có thể bắt đầu phát triển tiện ích của mình.

2. Thử phần mở rộng mẫu bằng trình mô phỏng

Khi khởi chạy thư mục tiện ích mới, Firebase CLI đã tạo một hàm ví dụ đơn giản và thư mục integration-tests chứa các tệp cần thiết để chạy tiện ích bằng bộ trình mô phỏng Firebase.

Hãy thử chạy tiện ích mẫu trong trình mô phỏng:

  1. Thay đổi thành thư mục integration-tests:

    cd functions/integration-tests
    
  2. Khởi động trình mô phỏng bằng một dự án minh hoạ:

    firebase emulators:start --project=demo-test
    

    Trình mô phỏng tải tiện ích vào một dự án "giả" được xác định trước (demo-test). Cho đến nay, tiện ích này bao gồm một hàm duy nhất được kích hoạt bằng HTTP, greetTheWorld, hàm này trả về thông báo "xin chào thế giới" khi được truy cập.

  3. Khi trình mô phỏng vẫn đang chạy, hãy thử hàm greetTheWorld của tiện ích bằng cách truy cập vào URL mà trình mô phỏng đã in khi bạn khởi động.

    Trình duyệt của bạn sẽ hiển thị thông báo "Hello World from greet-the-world" (Xin chào thế giới từ greet-the-world).

  4. Mã nguồn của hàm này nằm trong thư mục functions của tiện ích. Mở nguồn trong trình chỉnh sửa hoặc IDE mà bạn chọn:

    functions/index.js

    const functions = require("firebase-functions/v1");
    
    exports.greetTheWorld = functions.https.onRequest((req, res) => {
      // Here we reference a user-provided parameter
      // (its value is provided by the user during installation)
      const consumerProvidedGreeting = process.env.GREETING;
    
      // And here we reference an auto-populated parameter
      // (its value is provided by Firebase after installation)
      const instanceId = process.env.EXT_INSTANCE_ID;
    
      const greeting = `${consumerProvidedGreeting} World from ${instanceId}`;
    
      res.send(greeting);
    });
    
  5. Khi đang chạy, trình mô phỏng sẽ tự động tải lại mọi thay đổi mà bạn thực hiện đối với mã Hàm. Hãy thử thay đổi một chút hàm greetTheWorld:

    functions/index.js

    const greeting = `${consumerProvidedGreeting} everyone, from ${instanceId}`;
    

    Lưu thay đổi. Trình mô phỏng sẽ tải lại mã của bạn và giờ đây, khi truy cập vào URL của hàm, bạn sẽ thấy lời chào đã cập nhật.

3. Thêm thông tin cơ bản vào extension.yaml

Giờ đây, khi đã thiết lập môi trường phát triển và đang chạy trình mô phỏng tiện ích, bạn có thể bắt đầu viết tiện ích của riêng mình.

Bước đầu tiên, hãy chỉnh sửa siêu dữ liệu của tiện ích được xác định trước để phản ánh tiện ích bạn muốn viết thay vì greet-the-world. Siêu dữ liệu này được lưu trữ trong tệp extension.yaml.

  1. Mở extension.yaml trong trình chỉnh sửa và thay thế toàn bộ nội dung của tệp bằng nội dung sau:

    name: rtdb-uppercase-messages
    version: 0.0.1
    specVersion: v1beta  # Firebase Extensions specification version; don't change
    
    # Friendly display name for your extension (~3-5 words)
    displayName: Convert messages to upper case
    
    # Brief description of the task your extension performs (~1 sentence)
    description: >-
      Converts messages in RTDB to upper case
    
    author:
      authorName: Your Name
      url: https://your-site.example.com
    
    license: Apache-2.0  # Required license
    
    # Public URL for the source code of your extension
    sourceUrl: https://github.com/your-name/your-repo
    

    Lưu ý quy ước đặt tên được sử dụng trong trường name: các tiện ích Firebase chính thức được đặt tên bằng một tiền tố cho biết sản phẩm Firebase chính mà tiện ích hoạt động, theo sau là nội dung mô tả về chức năng của tiện ích. Bạn nên sử dụng cùng một quy ước trong các tiện ích của riêng mình.

  2. Vì đã thay đổi tên của tiện ích, nên bạn cũng nên cập nhật tên mới cho cấu hình trình mô phỏng:

    1. Trong functions/integration-tests/firebase.json, hãy thay đổi greet-the-world thành rtdb-uppercase-messages.
    2. Đổi tên functions/integration-tests/extensions/greet-the-world.env thành functions/integration-tests/extensions/rtdb-uppercase-messages.env.

Vẫn còn một số phần còn lại của tiện ích greet-the-world trong mã tiện ích của bạn, nhưng hiện tại, hãy để lại các phần đó. Bạn sẽ cập nhật các thông tin đó trong vài phần tiếp theo.

4. Viết một Chức năng trên đám mây và khai báo hàm đó dưới dạng tài nguyên tiện ích

Bây giờ, bạn có thể bắt đầu viết một số mã. Ở bước này, bạn sẽ viết một Hàm trên đám mây thực hiện nhiệm vụ cốt lõi của tiện ích, đó là theo dõi Cơ sở dữ liệu theo thời gian thực để tìm thông báo và chuyển đổi thông báo đó thành viết hoa.

  1. Mở nguồn cho các hàm của tiện ích (trong thư mục functions của tiện ích) trong trình chỉnh sửa hoặc IDE mà bạn chọn. Thay thế nội dung của tệp bằng nội dung sau:

    functions/index.js

    import { database, logger } from "firebase-functions/v1";
    
    const app = initializeApp();
    
    // Listens for new messages added to /messages/{pushId}/original and creates an
    // uppercase version of the message to /messages/{pushId}/uppercase
    // for all databases in 'us-central1'
    export const makeuppercase = database
      .ref("/messages/{pushId}/uppercase")
      .onCreate(async (snapshot, context) => {
        // Grab the current value of what was written to the Realtime Database.
        const original = snapshot.val();
    
        // Convert it to upper case.
        logger.log("Uppercasing", context.params.pushId, original);
        const uppercase = original.toUpperCase();
    
        // Setting an "uppercase" sibling in the Realtime Database.
        const upperRef = snapshot.ref.parent.child("upper");
        await upperRef.set(uppercase);
    });
    

    Hàm cũ mà bạn đã thay thế là một hàm được kích hoạt bằng HTTP, chạy khi truy cập vào một điểm cuối HTTP. Hàm mới được kích hoạt bằng các sự kiện cơ sở dữ liệu theo thời gian thực: hàm này theo dõi các mục mới tại một đường dẫn cụ thể và khi phát hiện thấy một mục, hàm này sẽ ghi phiên bản viết hoa của giá trị đó vào cơ sở dữ liệu.

    Nhân tiện, tệp mới này sử dụng cú pháp mô-đun ECMAScript (importexport) thay vì CommonJS (require). Để sử dụng các mô-đun ES trong Node, hãy chỉ định "type": "module" trong functions/package.json:

    {
      "name": "rtdb-uppercase-messages",
      "main": "index.js",
      "type": "module",
      …
    }
    
  2. Bạn phải khai báo mọi hàm trong tiện ích trong tệp extension.yaml. Tiện ích mẫu đã khai báo greetTheWorld là Hàm trên đám mây duy nhất của tiện ích; giờ đây, khi đã thay thế hàm này bằng makeuppercase, bạn cũng cần cập nhật nội dung khai báo của hàm.

    Mở extension.yaml rồi thêm trường resources:

    resources:
      - name: makeuppercase
        type: firebaseextensions.v1beta.function
        properties:
          eventTrigger:
            eventType: providers/google.firebase.database/eventTypes/ref.create
            # DATABASE_INSTANCE (project's default instance) is an auto-populated
            # parameter value. You can also specify an instance.
            resource: projects/_/instances/${DATABASE_INSTANCE}/refs/messages/{pushId}/original
          runtime: "nodejs18"
    
  3. Vì tiện ích của bạn hiện đang sử dụng Cơ sở dữ liệu theo thời gian thực làm trình kích hoạt, nên bạn cần cập nhật cấu hình trình mô phỏng để chạy trình mô phỏng RTDB cùng với trình mô phỏng Cloud Functions:

    1. Nếu trình mô phỏng vẫn đang chạy, hãy dừng trình mô phỏng bằng cách nhấn tổ hợp phím Ctrl-C.

    2. Từ thư mục functions/integration-tests, hãy chạy lệnh sau:

      firebase init emulators
      

      Khi được hỏi, hãy bỏ qua việc thiết lập dự án mặc định, sau đó chọn trình mô phỏng Hàm và Cơ sở dữ liệu. Chấp nhận các cổng mặc định và cho phép công cụ thiết lập tải mọi tệp cần thiết xuống.

    3. Khởi động lại trình mô phỏng:

      firebase emulators:start --project=demo-test
      
  4. Dùng thử tiện ích đã cập nhật:

    1. Mở giao diện người dùng của trình mô phỏng Cơ sở dữ liệu bằng đường liên kết mà trình mô phỏng in ra khi bạn khởi động.

    2. Chỉnh sửa nút gốc của cơ sở dữ liệu:

      • Trường: messages
      • Loại: json
      • Giá trị: {"11": {"original": "recipe"}}

      Nếu bạn thiết lập mọi thứ chính xác, khi bạn lưu các thay đổi đối với cơ sở dữ liệu, hàm makeuppercase của tiện ích sẽ kích hoạt và thêm một bản ghi con vào thông báo 11 với nội dung "upper": "RECIPE". Hãy xem các nhật ký và thẻ cơ sở dữ liệu của giao diện người dùng trình mô phỏng để xác nhận kết quả dự kiến.

    3. Hãy thử thêm một số phần tử con khác vào nút messages ({"original":"any text"}). Bất cứ khi nào bạn thêm một bản ghi mới, tiện ích sẽ thêm một trường uppercase chứa nội dung viết hoa của trường original.

Giờ đây, bạn đã có một tiện ích hoàn chỉnh, mặc dù đơn giản, hoạt động trên một thực thể RTDB. Trong các phần sau, bạn sẽ tinh chỉnh tiện ích này bằng một số tính năng bổ sung. Sau đó, bạn sẽ sẵn sàng phân phối tiện ích cho người khác và cuối cùng, tìm hiểu cách phát hành tiện ích trên Trung tâm tiện ích.

5. Khai báo API và vai trò

Firebase cấp cho mỗi thực thể của một tiện ích đã cài đặt quyền truy cập hạn chế vào dự án và dữ liệu của dự án đó bằng cách sử dụng tài khoản dịch vụ cho mỗi thực thể. Mỗi tài khoản có một nhóm quyền tối thiểu cần thiết để hoạt động. Vì lý do này, bạn phải khai báo rõ ràng mọi vai trò IAM mà tiện ích của bạn yêu cầu; khi người dùng cài đặt tiện ích, Firebase sẽ tạo một tài khoản dịch vụ được cấp các vai trò này và sử dụng tài khoản đó để chạy tiện ích.

Bạn không cần khai báo vai trò để kích hoạt các sự kiện của sản phẩm, nhưng bạn cần khai báo vai trò để tương tác với sản phẩm. Vì hàm bạn thêm vào bước cuối cùng sẽ ghi vào Cơ sở dữ liệu theo thời gian thực, nên bạn cần thêm nội dung khai báo sau vào extension.yaml:

roles:
  - role: firebasedatabase.admin
    reason: Allows the extension to write to RTDB.

Tương tự, bạn khai báo các API Google mà một tiện ích sử dụng trong trường apis. Khi người dùng cài đặt tiện ích của bạn, họ sẽ được hỏi xem họ có muốn tự động bật các API này cho dự án của mình hay không. Việc này thường chỉ cần thiết đối với các API Google không phải Firebase và không cần thiết đối với hướng dẫn này.

6. Xác định các thông số mà người dùng có thể định cấu hình

Hàm bạn đã tạo trong hai bước cuối cùng đã theo dõi một vị trí RTDB cụ thể cho các thông báo đến. Đôi khi, bạn thực sự muốn theo dõi một vị trí cụ thể, chẳng hạn như khi tiện ích của bạn hoạt động trên một cấu trúc cơ sở dữ liệu mà bạn chỉ sử dụng cho tiện ích của mình. Tuy nhiên, trong hầu hết trường hợp, bạn nên cho phép người dùng cài đặt tiện ích của bạn trong dự án có thể định cấu hình các giá trị này. Bằng cách này, người dùng có thể sử dụng tiện ích của bạn để làm việc với chế độ thiết lập cơ sở dữ liệu hiện có.

Đặt đường dẫn mà tiện ích theo dõi để người dùng có thể định cấu hình tin nhắn mới:

  1. Trong tệp extension.yaml, hãy thêm phần params:

    - param: MESSAGE_PATH
      label: Message path
      description: >-
        What is the path at which the original text of a message can be found?
      type: string
      default: /messages/{pushId}/original
      required: true
      immutable: false
    

    Thao tác này xác định một tham số chuỗi mới mà người dùng sẽ được nhắc đặt khi cài đặt tiện ích của bạn.

  2. Vẫn trong tệp extension.yaml, hãy quay lại phần khai báo makeuppercase và thay đổi trường resource thành nội dung sau:

    resource: projects/_/instances/${DATABASE_INSTANCE}/refs/${param:MESSAGE_PATH}
    

    Mã thông báo ${param:MESSAGE_PATH} là tham chiếu đến tham số bạn vừa xác định. Khi tiện ích của bạn chạy, mã thông báo này sẽ được thay thế bằng bất kỳ giá trị nào mà người dùng đã định cấu hình cho tham số đó, kết quả là hàm makeuppercase sẽ theo dõi đường dẫn mà người dùng chỉ định. Bạn có thể sử dụng cú pháp này để tham chiếu đến bất kỳ tham số nào do người dùng xác định ở bất kỳ đâu trong extension.yaml (và trong POSTINSTALL.md – sẽ nói thêm về điều này sau).

  3. Bạn cũng có thể truy cập vào các tham số do người dùng xác định từ mã hàm.

    Trong hàm bạn đã viết ở phần cuối, bạn đã mã hoá cứng đường dẫn để theo dõi các thay đổi. Thay đổi định nghĩa điều kiện kích hoạt để tham chiếu đến giá trị do người dùng xác định:

    functions/index.js

    export const makeuppercase = database.ref(process.env.MESSAGE_PATH).onCreate
    

    Xin lưu ý rằng trong Tiện ích Firebase, thay đổi này chỉ nhằm mục đích tài liệu: khi một Hàm trên đám mây được triển khai trong một tiện ích, hàm đó sẽ sử dụng định nghĩa điều kiện kích hoạt từ tệp extension.yaml và bỏ qua giá trị được chỉ định trong định nghĩa hàm. Tuy nhiên, bạn nên ghi lại trong mã nguồn nguồn gốc của giá trị này.

  4. Bạn có thể thất vọng khi thay đổi mã không có hiệu lực về thời gian chạy, nhưng bài học quan trọng cần rút ra là bạn có thể truy cập vào bất kỳ tham số nào do người dùng xác định trong mã hàm và sử dụng tham số đó làm giá trị thông thường trong logic của hàm. Để thể hiện khả năng này, hãy thêm câu lệnh nhật ký sau để chứng minh rằng bạn thực sự đang truy cập vào giá trị mà người dùng đã xác định:

    functions/index.js

    export const makeuppercase = database.ref(process.env.MESSAGE_PATH).onCreate(
      async (snapshot, context) => {
        logger.log("Found new message at ", snapshot.ref);
    
        // Grab the current value of what was written to the Realtime Database.
        ...
    
  5. Thông thường, người dùng sẽ được nhắc cung cấp giá trị cho các thông số khi họ cài đặt một tiện ích. Tuy nhiên, khi sử dụng trình mô phỏng để kiểm thử và phát triển, bạn sẽ bỏ qua quy trình cài đặt, do đó, bạn sẽ cung cấp giá trị cho các thông số do người dùng xác định bằng cách sử dụng tệp env.

    Mở functions/integration-tests/extensions/rtdb-uppercase-messages.env rồi thay thế định nghĩa GREETING bằng nội dung sau:

    MESSAGE_PATH=/msgs/{pushId}/original
    

    Lưu ý rằng đường dẫn ở trên khác với đường dẫn mặc định và đường dẫn bạn đã xác định trước đó; điều này chỉ để chứng minh cho bạn khi bạn thử phần mở rộng đã cập nhật rằng định nghĩa của bạn đang có hiệu lực.

  6. Bây giờ, hãy khởi động lại trình mô phỏng và truy cập lại giao diện người dùng của trình mô phỏng cơ sở dữ liệu.

    Chỉnh sửa nút gốc của cơ sở dữ liệu bằng đường dẫn bạn đã xác định ở trên:

    • Trường: msgs
    • Loại: json
    • Giá trị: {"11": {"original": "recipe"}}

    Khi bạn lưu các thay đổi đối với cơ sở dữ liệu, hàm makeuppercase của tiện ích sẽ kích hoạt như trước, nhưng giờ đây, hàm này cũng sẽ in tham số do người dùng xác định vào nhật ký bảng điều khiển.

7. Cung cấp các trình kích hoạt sự kiện cho logic do người dùng xác định

Là tác giả của tiện ích, bạn đã thấy cách một sản phẩm Firebase có thể kích hoạt logic do tiện ích cung cấp: việc tạo bản ghi mới trong Cơ sở dữ liệu theo thời gian thực sẽ kích hoạt hàm makeuppercase. Tiện ích của bạn có thể có mối quan hệ tương tự với những người dùng cài đặt tiện ích: tiện ích có thể kích hoạt logic mà người dùng xác định.

Một tiện ích có thể cung cấp lệnh gọi lại đồng bộ, lệnh gọi lại không đồng bộ hoặc cả hai. Các móc đồng bộ cho phép người dùng thực hiện các tác vụ chặn việc hoàn tất một trong các chức năng của tiện ích. Điều này có thể hữu ích, chẳng hạn như để cung cấp cho người dùng một cách để thực hiện quá trình xử lý trước tuỳ chỉnh trước khi một tiện ích thực hiện công việc của mình.

Trong hướng dẫn này, bạn sẽ thêm một trình kích hoạt không đồng bộ vào tiện ích của mình. Trình kích hoạt này sẽ cho phép người dùng xác định các bước xử lý của riêng họ để chạy sau khi tiện ích của bạn ghi thông báo viết hoa vào Cơ sở dữ liệu theo thời gian thực. Các móc không đồng bộ sử dụng Eventarc để kích hoạt các hàm do người dùng xác định. Tiện ích khai báo các loại sự kiện mà chúng phát ra và khi người dùng cài đặt tiện ích, họ sẽ chọn loại sự kiện mà họ quan tâm. Nếu người dùng chọn ít nhất một sự kiện, Firebase sẽ cấp một kênh Eventarc cho tiện ích trong quá trình cài đặt. Sau đó, người dùng có thể triển khai các chức năng đám mây của riêng họ để nghe trên kênh đó và kích hoạt khi tiện ích phát hành sự kiện mới.

Hãy làm theo các bước sau để thêm một trình bổ trợ không đồng bộ:

  1. Trong tệp extension.yaml, hãy thêm phần sau đây để khai báo một loại sự kiện mà tiện ích phát ra:

    events:
      - type: test-publisher.rtdb-uppercase-messages.v1.complete
        description: >-
          Occurs when message uppercasing completes. The event subject will contain
          the RTDB URL of the uppercase message.
    

    Các loại sự kiện phải là duy nhất trên toàn hệ thống; để đảm bảo tính duy nhất, hãy luôn đặt tên cho sự kiện theo định dạng sau: <publisher-id>.<extension-id>.<version>.<description>. (Bạn chưa có mã nhận dạng nhà xuất bản, vì vậy, hãy sử dụng test-publisher.)

  2. Ở cuối hàm makeuppercase, hãy thêm một số mã phát hành một sự kiện thuộc loại bạn vừa khai báo:

    functions/index.js

    // Import the Eventarc library:
    import { initializeApp } from "firebase-admin/app";
    import { getEventarc } from "firebase-admin/eventarc";
    
    const app = initializeApp();
    
    // In makeuppercase, after upperRef.set(uppercase), add:
    
    // 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: "test-publisher.rtdb-uppercase-messages.v1.complete",
        subject: upperRef.toString(),
        data: {
          "original": original,
          "uppercase": uppercase,
        },
      });
    

    Mã ví dụ này tận dụng việc biến môi trường EVENTARC_CHANNEL chỉ được xác định khi người dùng bật ít nhất một loại sự kiện. Nếu không xác định EVENTARC_CHANNEL, mã sẽ không cố gắng phát hành bất kỳ sự kiện nào.

    Bạn có thể đính kèm thêm thông tin vào một sự kiện Eventarc. Trong ví dụ trên, sự kiện có một trường subject chứa tham chiếu đến giá trị mới tạo và một tải trọng data chứa thông báo gốc và thông báo viết hoa. Các hàm do người dùng xác định kích hoạt sự kiện có thể sử dụng thông tin này.

  3. Thông thường, các biến môi trường EVENTARC_CHANNELEXT_SELECTED_EVENTS được xác định dựa trên các tuỳ chọn mà người dùng đã chọn trong quá trình cài đặt. Để kiểm thử bằng trình mô phỏng, hãy xác định các biến này theo cách thủ công trong tệp rtdb-uppercase-messages.env:

    EVENTARC_CHANNEL=locations/us-central1/channels/firebase
    EXT_SELECTED_EVENTS=test-publisher.rtdb-uppercase-messages.v1.complete
    

Tại thời điểm này, bạn đã hoàn tất các bước cần thiết để thêm một trình kích hoạt sự kiện không đồng bộ vào tiện ích.

Để dùng thử tính năng mới mà bạn vừa triển khai, trong vài bước tiếp theo, hãy giả định vai trò của một người dùng đang cài đặt tiện ích:

  1. Từ thư mục functions/integration-tests, hãy khởi chạy một dự án Firebase mới:

    firebase init functions
    

    Khi được nhắc, hãy từ chối thiết lập dự án mặc định, chọn JavaScript làm ngôn ngữ của Hàm trên đám mây và cài đặt các phần phụ thuộc bắt buộc. Dự án này đại diện cho dự án của người dùng, trong đó đã cài đặt tiện ích của bạn.

  2. Chỉnh sửa integration-tests/functions/index.js và dán mã sau:

    import { logger } from "firebase-functions/v1";
    import { onCustomEventPublished } from "firebase-functions/v2/eventarc";
    
    import { initializeApp } from "firebase-admin/app";
    import { getDatabase } from "firebase-admin/database";
    
    const app = initializeApp();
    
    export const extraemphasis = onCustomEventPublished(
      "test-publisher.rtdb-uppercase-messages.v1.complete",
      async (event) => {
        logger.info("Received makeuppercase completed event", event);
    
        const refUrl = event.subject;
        const ref = getDatabase().refFromURL(refUrl);
        const upper = (await ref.get()).val();
        return ref.set(`${upper}!!!`);
      }
    );
    

    Đây là ví dụ về một hàm xử lý hậu kỳ mà người dùng có thể viết. Trong trường hợp này, hàm sẽ nghe tiện ích phát hành một sự kiện complete và khi được kích hoạt, hàm sẽ thêm ba dấu chấm than vào thông báo mới được viết hoa.

  3. Khởi động lại trình mô phỏng. Trình mô phỏng sẽ tải các hàm của tiện ích cũng như hàm xử lý sau mà "người dùng" đã xác định.

  4. Truy cập vào giao diện người dùng của trình mô phỏng cơ sở dữ liệu và chỉnh sửa nút gốc của cơ sở dữ liệu bằng cách sử dụng đường dẫn mà bạn đã xác định ở trên:

    • Trường:msgs
    • Loại: json
    • Giá trị: {"11": {"original": "recipe"}}

    Khi bạn lưu các thay đổi đối với cơ sở dữ liệu, hàm makeuppercase của tiện ích và hàm extraemphasis của người dùng sẽ kích hoạt theo trình tự, dẫn đến trường upper nhận được giá trị RECIPE!!!.

8. Thêm trình xử lý sự kiện trong vòng đời

Tiện ích bạn đã viết cho đến nay xử lý các thông báo khi chúng được tạo. Nhưng nếu người dùng của bạn đã có cơ sở dữ liệu tin nhắn khi cài đặt tiện ích thì sao? Tiện ích Firebase có một tính năng gọi là móc sự kiện trong vòng đời mà bạn có thể sử dụng để kích hoạt các hành động khi tiện ích được cài đặt, cập nhật hoặc định cấu hình lại. Trong phần này, bạn sẽ sử dụng các trình bổ trợ sự kiện trong vòng đời để điền sẵn cơ sở dữ liệu thông báo hiện có của dự án bằng các thông báo viết hoa khi người dùng cài đặt tiện ích của bạn.

Tiện ích Firebase sử dụng Cloud Tasks để chạy trình xử lý sự kiện trong vòng đời. Bạn xác định trình xử lý sự kiện bằng cách sử dụng Hàm trên đám mây; bất cứ khi nào một thực thể của tiện ích đạt đến một trong các sự kiện trong vòng đời được hỗ trợ, nếu bạn đã xác định trình xử lý, thì trình xử lý đó sẽ thêm trình xử lý vào hàng đợi của Cloud Tasks. Sau đó, Cloud Tasks sẽ thực thi trình xử lý một cách không đồng bộ. Trong khi trình xử lý sự kiện trong vòng đời đang chạy, bảng điều khiển Firebase sẽ báo cáo cho người dùng rằng thực thể tiện ích đang xử lý một tác vụ. Hàm xử lý của bạn sẽ báo cáo trạng thái đang diễn ra và hoàn thành tác vụ cho người dùng.

Để thêm trình xử lý sự kiện trong vòng đời giúp bổ sung các thông báo hiện có, hãy làm như sau:

  1. Xác định một Hàm trên đám mây mới được kích hoạt bởi các sự kiện hàng đợi tác vụ:

    functions/index.js

    import { tasks } from "firebase-functions/v1";
    
    import { getDatabase } from "firebase-admin/database";
    import { getExtensions } from "firebase-admin/extensions";
    import { getFunctions } from "firebase-admin/functions";
    
    export const backfilldata = tasks.taskQueue().onDispatch(async () => {
      const batch = await getDatabase()
        .ref(process.env.MESSAGE_PATH)
        .parent.parent.orderByChild("upper")
        .limitToFirst(20)
        .get();
    
      const promises = [];
      for (const key in batch.val()) {
        const msg = batch.child(key);
        if (msg.hasChild("original") && !msg.hasChild("upper")) {
          const upper = msg.child("original").val().toUpperCase();
          promises.push(msg.child("upper").ref.set(upper));
        }
      }
      await Promise.all(promises);
    
      if (promises.length > 0) {
        const queue = getFunctions().taskQueue(
          "backfilldata",
          process.env.EXT_INSTANCE_ID
        );
        return queue.enqueue({});
      } else {
        return getExtensions()
          .runtime()
          .setProcessingState("PROCESSING_COMPLETE", "Backfill complete.");
      }
    });
    

    Lưu ý rằng hàm này chỉ xử lý một vài bản ghi trước khi tự thêm vào hàng đợi tác vụ. Đây là chiến lược thường dùng để xử lý các tác vụ xử lý không thể hoàn tất trong khoảng thời gian chờ của Hàm trên đám mây. Vì bạn không thể dự đoán số lượng tin nhắn mà người dùng có thể đã có trong cơ sở dữ liệu của họ khi cài đặt tiện ích của bạn, nên chiến lược này rất phù hợp.

  2. Trong tệp extension.yaml, hãy khai báo hàm điền lại dưới dạng tài nguyên mở rộng có thuộc tính taskQueueTrigger:

    resources:
      - name: makeuppercase
        ...
      - name: backfilldata
        type: firebaseextensions.v1beta.function
        description: >-
          Backfill existing messages with uppercase versions
        properties:
          runtime: "nodejs18"
          taskQueueTrigger: {}
    

    Sau đó, khai báo hàm này làm trình xử lý cho sự kiện vòng đời onInstall:

    lifecycleEvents:
      onInstall:
        function: backfilldata
        processingMessage: Uppercasing existing messages
    
  3. Mặc dù việc điền lại các tin nhắn hiện có là rất hữu ích, nhưng tiện ích này vẫn có thể hoạt động mà không cần tính năng này. Trong những trường hợp như vậy, bạn nên chạy trình xử lý sự kiện trong vòng đời (không bắt buộc).

    Để thực hiện việc này, hãy thêm một tham số mới vào extension.yaml:

    - param: DO_BACKFILL
      label: Backfill existing messages
      description: >-
        Generate uppercase versions of existing messages?
      type: select
      required: true
      options:
        - label: Yes
          value: true
        - label: No
          value: false
    

    Sau đó, ở đầu hàm điền lại, hãy kiểm tra giá trị của tham số DO_BACKFILL và thoát sớm nếu tham số này không được đặt:

    functions/index.js

    if (!process.env.DO_BACKFILL) {
      return getExtensions()
        .runtime()
        .setProcessingState("PROCESSING_COMPLETE", "Backfill skipped.");
    }
    

Với những thay đổi trên, tiện ích này hiện sẽ chuyển đổi các thông báo hiện có sang chữ hoa khi được cài đặt.

Đến thời điểm này, bạn đã sử dụng trình mô phỏng tiện ích để phát triển tiện ích và kiểm thử các thay đổi đang diễn ra. Tuy nhiên, trình mô phỏng tiện ích sẽ bỏ qua quá trình cài đặt. Vì vậy, để kiểm thử trình xử lý sự kiện onInstall, bạn cần cài đặt tiện ích trong một dự án thực. Tuy nhiên, điều đó cũng tốt, vì với việc bổ sung tính năng tự động điền này, tiện ích hướng dẫn hiện đã hoàn tất mã!

9. Triển khai vào một dự án Firebase thực

Mặc dù trình mô phỏng tiện ích là một công cụ tuyệt vời để lặp lại nhanh một tiện ích trong quá trình phát triển, nhưng tại một thời điểm nào đó, bạn sẽ muốn thử công cụ này trong một dự án thực tế.

Để thực hiện việc này, trước tiên, hãy thiết lập một dự án mới đã bật một số dịch vụ:

  1. Trong bảng điều khiển của Firebase, hãy thêm một dự án mới.
  2. Nâng cấp dự án lên gói Blaze trả theo mức sử dụng. Cloud Functions for Firebase yêu cầu dự án của bạn phải có tài khoản thanh toán, vì vậy, bạn cũng cần có tài khoản thanh toán để cài đặt tiện ích.
  3. Trong dự án mới, hãy bật Cơ sở dữ liệu theo thời gian thực.
  4. Vì bạn muốn kiểm thử khả năng của tiện ích trong việc bổ sung dữ liệu hiện có khi cài đặt, hãy nhập một số dữ liệu mẫu vào thực thể cơ sở dữ liệu theo thời gian thực:
    1. Tải một số dữ liệu hạt giống RTDB xuống.
    2. Trên trang Cơ sở dữ liệu theo thời gian thực của bảng điều khiển Firebase, hãy nhấp vào biểu tượng (thêm) > Nhập JSON rồi chọn tệp bạn vừa tải xuống.
  5. Để cho phép hàm điền lại sử dụng phương thức orderByChild, hãy định cấu hình cơ sở dữ liệu để lập chỉ mục thông báo theo giá trị của upper:

    {
      "rules": {
        ".read": false,
        ".write": false,
        "messages": {
          ".indexOn": "upper"
        }
      }
    }
    

Bây giờ, hãy cài đặt tiện ích của bạn từ nguồn cục bộ vào dự án mới:

  1. Tạo thư mục mới cho dự án Firebase:

    mkdir ~/extensions-live-test && cd ~/extensions-live-test
    
  2. Khởi chạy dự án Firebase trong thư mục đang hoạt động:

    firebase init database
    

    Khi được nhắc, hãy chọn dự án bạn vừa tạo.

  3. Cài đặt tiện ích vào dự án Firebase cục bộ:

    firebase ext:install /path/to/rtdb-uppercase-messages
    

    Tại đây, bạn có thể xem trải nghiệm người dùng khi cài đặt một tiện ích bằng công cụ Firebase CLI. Hãy nhớ chọn "có" khi công cụ định cấu hình hỏi bạn có muốn bổ sung cơ sở dữ liệu hiện có không.

    Sau khi bạn chọn các tuỳ chọn cấu hình, Firebase CLI sẽ lưu cấu hình của bạn trong thư mục extensions và ghi lại vị trí nguồn mở rộng trong tệp firebase.json. Tổng cộng, hai bản ghi này được gọi là tệp kê khai tiện ích. Người dùng có thể sử dụng tệp kê khai để lưu cấu hình tiện ích và triển khai cấu hình đó cho nhiều dự án.

  4. Triển khai cấu hình tiện ích cho dự án đang hoạt động:

    firebase deploy --only extensions
    

Nếu mọi thứ diễn ra suôn sẻ, CLI Firebase sẽ tải tiện ích của bạn lên dự án và cài đặt tiện ích đó. Sau khi cài đặt xong, tác vụ điền lại sẽ chạy và trong vài phút, cơ sở dữ liệu của bạn sẽ được cập nhật bằng các thông báo viết hoa. Thêm một số nút mới vào cơ sở dữ liệu tin nhắn và đảm bảo phần mở rộng cũng hoạt động cho các tin nhắn mới.

10. Viết tài liệu

Trước khi chia sẻ tiện ích với người dùng, hãy đảm bảo bạn cung cấp đủ tài liệu để họ thành công.

Khi bạn khởi chạy dự án tiện ích, CLI Firebase đã tạo các phiên bản mô phỏng của tài liệu tối thiểu bắt buộc. Cập nhật các tệp này để phản ánh chính xác phần mở rộng mà bạn đã tạo.

extension.yaml

Bạn đã cập nhật tệp này khi phát triển tiện ích này, vì vậy, bạn không cần cập nhật thêm nữa.

Tuy nhiên, đừng bỏ qua tầm quan trọng của tài liệu có trong tệp này. Ngoài thông tin nhận dạng quan trọng của một tiện ích (tên, nội dung mô tả, tác giả, vị trí kho lưu trữ chính thức), tệp extension.yaml còn chứa tài liệu dành cho người dùng về mọi tài nguyên và tham số mà người dùng có thể định cấu hình. Thông tin này được hiển thị cho người dùng trong bảng điều khiển của Firebase, Trung tâm tiện ích và Firebase CLI.

PREINSTALL.md

Trong tệp này, hãy cung cấp thông tin mà người dùng cần trước khi cài đặt tiện ích: mô tả ngắn gọn về chức năng của tiện ích, giải thích mọi điều kiện tiên quyết và cung cấp cho người dùng thông tin về các vấn đề liên quan đến việc thanh toán khi cài đặt tiện ích. Nếu bạn có một trang web có thông tin bổ sung, đây cũng là một vị trí phù hợp để liên kết trang web đó.

Văn bản của tệp này sẽ hiển thị cho người dùng trong Trung tâm tiện ích và theo lệnh firebase ext:info.

Dưới đây là ví dụ về tệp PREINSTALL:

Use this extension to automatically convert strings to upper case when added to
a specified Realtime Database path.

This extension expects a database layout like the following example:

    "messages": {
      MESSAGE_ID: {
        "original": MESSAGE_TEXT
      },
      MESSAGE_ID: {
        "original": MESSAGE_TEXT
      },
    }

When you create new string records, this extension creates a new sibling record
with upper-cased text:

    MESSAGE_ID: {
      "original": MESSAGE_TEXT,
      "upper": UPPERCASE_MESSAGE_TEXT,
    }

#### Additional setup

Before installing this extension, make sure that you've
[set up Realtime Database](https://firebase.google.com/docs/database/quickstart)
in your Firebase project.

#### Billing

To install an extension, your project must be on the
[Blaze (pay as you go) plan](https://firebase.google.com/pricing).

- This extension uses other Firebase and Google Cloud Platform services, which
  have associated charges if you exceed the service's no-cost tier:
  - Realtime Database
  - Cloud Functions (Node.js 10+ runtime)
    [See FAQs](https://firebase.google.com/support/faq#extensions-pricing)
- If you enable events,
  [Eventarc fees apply](https://cloud.google.com/eventarc/pricing).

POSTINSTALL.md

Tệp này chứa thông tin hữu ích cho người dùng sau khi họ cài đặt thành công tiện ích của bạn: ví dụ: các bước thiết lập tiếp theo, ví dụ về cách sử dụng tiện ích, v.v.

Nội dung của POSTINSTALL.md sẽ xuất hiện trong bảng điều khiển Firebase sau khi bạn định cấu hình và cài đặt một tiện ích. Bạn có thể tham chiếu các thông số người dùng trong tệp này và các thông số đó sẽ được thay thế bằng các giá trị đã định cấu hình.

Dưới đây là ví dụ về tệp sau khi cài đặt cho tiện ích hướng dẫn:

### See it in action

You can test out this extension right away!

1.  Go to your
    [Realtime Database dashboard](https://console.firebase.google.com/project/${param:PROJECT_ID}/database/${param:PROJECT_ID}/data) in the Firebase console.

1.  Add a message string to a path that matches the pattern `${param:MESSAGE_PATH}`.

1.  In a few seconds, you'll see a sibling node named `upper` that contains the
    message in upper case.

### Using the extension

We recommend adding data by pushing -- for example,
`firebase.database().ref().push()` -- because pushing assigns an automatically
generated ID to the node in the database. During retrieval, these nodes are
guaranteed to be ordered by the time they were added. Learn more about reading
and writing data for your platform (iOS, Android, or Web) in the
[Realtime Database documentation](https://firebase.google.com/docs/database/).

### Monitoring

As a best practice, you can
[monitor the activity](https://firebase.google.com/docs/extensions/manage-installed-extensions#monitor)
of your installed extension, including checks on its health, usage, and logs.

CHANGELOG.md

Bạn cũng nên ghi lại những thay đổi mà bạn thực hiện giữa các bản phát hành của một tiện ích trong tệp CHANGELOG.md.

Vì chưa từng phát hành tiện ích mẫu nào trước đây, nên nhật ký thay đổi chỉ có một mục:

## Version 0.0.1

Initial release of the _Convert messages to upper case_ extension.

README.md

Hầu hết các tiện ích cũng cung cấp tệp readme để người dùng truy cập vào kho lưu trữ của tiện ích. Bạn có thể viết tệp này theo cách thủ công hoặc tạo tệp readme bằng lệnh.

Để phù hợp với mục đích của hướng dẫn này, hãy bỏ qua việc viết tệp readme.

Tài liệu bổ sung

Tài liệu được thảo luận ở trên là bộ tài liệu tối thiểu mà bạn nên cung cấp cho người dùng. Nhiều tiện ích yêu cầu tài liệu chi tiết hơn để người dùng có thể sử dụng thành công. Trong trường hợp này, bạn nên viết thêm tài liệu và lưu trữ tài liệu đó ở nơi bạn có thể trỏ người dùng đến.

Để phù hợp với mục đích của hướng dẫn này, hãy bỏ qua việc viết tài liệu chi tiết hơn.

11. Xuất bản trên Trung tâm tiện ích

Giờ đây, khi tiện ích của bạn đã hoàn tất mã và được ghi lại, bạn đã sẵn sàng chia sẻ tiện ích đó với mọi người trên Trung tâm tiện ích. Tuy nhiên, vì đây chỉ là hướng dẫn, nên đừng thực sự làm như vậy. Hãy bắt đầu viết tiện ích của riêng bạn bằng những gì bạn đã học được tại đây và trong phần còn lại của tài liệu dành cho nhà xuất bản Tiện ích Firebase, cũng như bằng cách kiểm tra nguồn của các tiện ích chính thức do Firebase viết.

Khi bạn đã sẵn sàng xuất bản tác phẩm của mình trên Trung tâm tiện ích, hãy làm theo cách sau:

  1. Nếu bạn đang phát hành tiện ích đầu tiên, hãy đăng ký làm nhà xuất bản tiện ích. Khi đăng ký làm nhà xuất bản tiện ích, bạn sẽ tạo một mã nhà xuất bản để người dùng nhanh chóng xác định bạn là tác giả của tiện ích.
  2. Lưu trữ mã nguồn của tiện ích ở một vị trí có thể xác minh công khai. Khi mã của bạn có sẵn từ một nguồn có thể xác minh, Firebase có thể phát hành trực tiếp tiện ích của bạn từ vị trí này. Việc này giúp đảm bảo rằng bạn đang phát hành phiên bản tiện ích hiện đã phát hành và giúp người dùng bằng cách cho phép họ kiểm tra mã mà họ đang cài đặt vào dự án của mình.

    Hiện tại, việc này có nghĩa là cung cấp tiện ích của bạn trong kho lưu trữ GitHub công khai.

  3. Tải tiện ích lên Extensions Hub bằng lệnh firebase ext:dev:upload.

  4. Chuyển đến trang tổng quan dành cho nhà xuất bản trong bảng điều khiển Firebase, tìm tiện ích mà bạn vừa tải lên rồi nhấp vào "Xuất bản lên Trung tâm tiện ích". Việc này sẽ yêu cầu nhân viên đánh giá của chúng tôi xem xét. Quá trình này có thể mất vài ngày. Nếu được phê duyệt, tiện ích sẽ được phát hành lên Trung tâm tiện ích. Nếu bị từ chối, bạn sẽ nhận được thông báo giải thích lý do. Sau đó, bạn có thể giải quyết các vấn đề đã báo cáo rồi gửi lại để được xem xét.