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

Trang này hướng dẫn bạn các bước bắt buộc để tạo một Tiện ích Firebase đơn giản mà bạn có thể cài đặt 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 cho các thông báo và chuyển đổi chúng 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 xây dựng một tiện ích, bạn cần thiết lập một môi trường xây dựng bằng các công cụ cần thiết.

  1. Cài đặt Node.js 16 trở lên. Có một cách để cài đặt Nút 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 Firebase CLI. Để 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 Giao diện dòng lệnh (CLI) của Firebase để khởi chạy một dự án tiện ích mới:

  1. Tạo một thư mục cho tiện ích của bạn 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à trả lời "có" khi được yêu cầu cài đặt các phần phụ thuộc. (Chấp nhận các giá trị 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. Dùng thử tiện ích mẫu bằng trình mô phỏng

Khi Firebase CLI khởi chạy thư mục tiện ích mới, thư mục này đã tạo một hàm mẫu đơn giản và một 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 sang 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 này vào một dự án "dummy" được xác định trước (demo-test). Cho đến nay, tiện ích này chỉ bao gồm một hàm được kích hoạt HTTP là greetTheWorld. Hàm này sẽ trả về thông báo "hello world" ("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 URL được in khi bạn khởi động.

    Trình duyệt của bạn hiển thị thông báo "Hello World from Greeting-the-world".

  4. Mã nguồn cho 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");
    
    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ử thực hiện một thay đổi nhỏ đối với 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 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 extensions.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.

Một bước đầu tiên đơn giản là 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 rồi 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
    

    Vui lòng lưu ý quy ước đặt tên 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ì đã đổi tên tiện ích, bạn cũng nên cập nhật cấu hình trình mô phỏng bằng tên mới:

    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.

Mã tiện ích của bạn vẫn còn một số phần còn lại của tiện ích greet-the-world, nhưng hãy tạm giữ chúng. Bạn sẽ cập nhật các tính năng đó trong các phần tiếp theo.

4. Viết một hàm đám mây và khai báo hàm đó là 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 đá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 tin nhắn và chuyển đổi các hàm đó thành chữ hoa.

  1. Mở nguồn của 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 này bằng:

    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 HTTP. Hàm này chạy khi truy cập vào điểm cuối HTTP. Hàm mới được kích hoạt bởi 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 một đường dẫn được phát hiện, hàm này sẽ ghi phiên bản viết hoa của giá trị trở lại 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 Nút, 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 này đã khai báo greetTheWorld là Hàm đám mây duy nhất của tiện ích. Giờ đây, khi đã thay thế tiện ích này bằng makeuppercase, bạn cũng cần cập nhật nội dung khai báo.

    Mở extension.yaml và 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 đang sử dụng Cơ sở dữ liệu theo thời gian thực làm điều kiện 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 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 bướ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 của bạn:

    1. Mở Giao diện người dùng của trình mô phỏng cơ sở dữ liệu bằng cách sử dụng đường liên kết mà trình mô phỏng đã in 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 mọi thứ được thiết lập chính xác, thì khi bạn lưu các thay đổi về cơ sở dữ liệu, hàm makeuppercase của tiện ích sẽ kích hoạt và thêm bản ghi con vào thông báo 11 có nội dung "upper": "RECIPE". Hãy xem nhật ký và các thẻ cơ sở dữ liệu trong giao diện người dùng của 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"}). Mỗi khi bạn thêm một bản ghi mới, tiện ích này sẽ thêm trường uppercase chứa nội dung viết hoa của trường original.

Bây giờ, bạn đã có một tiện ích hoàn chỉnh, tuy đơn giản, hoạt động trên phiên bản RTDB. Trong các phần tiếp theo, 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ẽ chuẩn bị sẵn sàng để phân phối tiện ích này cho người khác và cuối cùng là 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 phiên bản của tiện ích đã cài đặt quyền truy cập có giới hạn vào dự án và dữ liệu của dự án bằng cách sử dụng một tài khoản dịch vụ cho mỗi phiên bản. Mỗi tài khoản có số lượng 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 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ụ với các vai trò đã được cấp này rồi 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ự kiện đó. Vì hàm mà bạn đã thêm ở bước trước 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 của Google mà một tiện ích sử dụng trong trường apis. Khi cài đặt tiện ích của bạn, người dùng sẽ được hỏi xem có muốn tự động bật các API này cho dự án của mình hay không. Điều này thường chỉ cần thiết đối với các API của Google không phải của Firebase và không cần thiết trong 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 mà bạn tạo trong 2 bước cuối đã theo dõi vị trí RTDB cụ thể để nhận các tin nhắn đến. Đôi khi, việc theo dõi một vị trí cụ thể thực sự là điều bạn muốn, chẳng hạn như khi tiện ích hoạt động trên 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 sẽ muốn những người dùng cài đặt tiện ích của bạn trong dự án của họ 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 để thiết lập cơ sở dữ liệu hiện có của họ.

Làm cho đường dẫn mà tiện ích theo dõi đối với các tin nhắn mới ở chế độ có thể định cấu hình cho người dùng:

  1. Trong tệp extension.yaml, hãy thêm một 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 như sau:

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

    Mã thông báo ${param:MESSAGE_PATH} là mã tham chiếu đến tham số mà 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ố đó, với kết quả là hàm makeuppercase sẽ lắng nghe đườ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 mọi tham số do người dùng xác định ở bất cứ đâu trong extension.yaml (và trong POSTINSTALL.md – bạn sẽ tìm hiểu thêm ở phần 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 đã viết ở phần trước, bạn đã mã hoá cứng đường dẫn để xem các thay đổi. Thay đổi định nghĩa của điều kiện kích hoạt để tham chiếu 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 các Tiện ích Firebase, thay đổi này chỉ nhằm mục đích cung cấp tài liệu: khi triển khai một Hàm đám mây dưới dạng một phần của 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 nguồn gốc của giá trị này trong mã.

  4. Có thể bạn sẽ không hài lòng khi thực hiện một thay đổi mã mà không có ảnh hưởng đến 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. Để làm nổi bật tính 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 được nhắc cung cấp giá trị cho các tham 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. Thay vào đó, bạn cung cấp giá trị cho các tham 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 và thay thế định nghĩa GREETING bằng định nghĩa 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à khác với đường dẫn bạn đã xác định trước đó; đây chỉ là để chứng minh cho chính bạn khi dùng thử tiện ích 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à một lần nữa truy cập vào 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, 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 về cơ sở dữ liệu, hàm makeuppercase của tiện ích sẽ kích hoạt như trước đây, nhưng giờ đâ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 hook sự kiện cho logic do người dùng xác định

Với tư cách là tác giả tiện ích, bạn đã biết 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 của bạn. Tiện ích của bạn có thể có mối quan hệ tương tự với người dùng cài đặt tiện ích: tiện ích của bạn có thể kích hoạt logic mà người dùng xác định.

Mỗi tiện ích có thể cung cấp hook đồng bộ, hook không đồng bộ hoặc cả hai. Hook đồng bộ giúp người dùng thực hiện các thao tác chặn việc hoàn thành một trong các hàm của tiện ích. Ví dụ: điều này có thể hữu ích khi cung cấp cho người dùng cách thực hiện xử lý trước tuỳ chỉnh trước khi tiện ích thực hiện hoạt động.

Trong hướng dẫn này, bạn sẽ thêm một hook không đồng bộ vào tiện ích. Điều này cho phép người dùng xác định các bước xử lý của riêng họ sẽ chạy sau khi tiện ích ghi thông báo viết hoa vào Cơ sở dữ liệu theo thời gian thực. Các hook không đồng bộ sử dụng Eventarc để kích hoạt các hàm do người dùng xác định. Các tiện ích khai báo loại sự kiện mà chúng phát hành 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 họ chọn ít nhất một sự kiện, Firebase sẽ cung cấp kênh Eventarc cho phần mở rộng trong quá trình cài đặt. Sau đó, người dùng có thể triển khai các hàm đám mây của riêng họ để theo dõi kênh đó và kích hoạt khi tiện ích phát hành các sự kiện mới.

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

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

    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 cầu. Để đảm bảo tính duy nhất, hãy luôn đặt tên cho các sự kiện theo định dạng sau: <publisher-id>.<extension-id>.<version>.<description>. (Bạn chưa có mã nhà xuất bản, vì vậy tạm thời chỉ cần sử dụng test-publisher.)

  2. Ở cuối hàm makeuppercase, hãy thêm một số mã phát hành 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 thực tế là 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 tìm cách xuất bản bất kỳ sự kiện nào.

    Bạn có thể đính kèm thông tin bổ sung vào sự kiện Eventarc. Trong ví dụ trên, sự kiện này có trường subject chứa tham chiếu đến giá trị mới tạo và tải trọng data chứa thông điệp gốc và 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ì 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ử với trình mô phỏng, hãy xác định các biến này trong tệp rtdb-uppercase-messages.env theo cách thủ công:

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

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

Để 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 nhập vai 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ữ Hàm đá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 một người dùng đã 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ề hàm xử lý hậu kỳ mà người dùng có thể ghi. Trong trường hợp này, hàm này sẽ theo dõi tiện ích để xuất bản sự kiện complete. Khi được kích hoạt, hàm này sẽ thêm 3 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ý hậu kỳ mà "người dùng" đã xác định.

  4. Truy cập 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 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 về cơ sở dữ liệu, hàm makeuppercase của tiện ích và hàm extraemphasis của người dùng phải kích hoạt theo trình tự, dẫn đến việc 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 mà bạn đã viết cho đến nay sẽ xử lý thư khi chúng được tạo. Nhưng nếu người dùng đã có một cơ sở dữ liệu gồm các thông báo khi cài đặt tiện ích này thì sao? Tiện ích Firebase có một tính năng gọi là hook 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 hook sự kiện trong vòng đời để bổ sung các thông báo được viết hoa khi người dùng cài đặt tiện ích của bạn vào cơ sở dữ liệu thông báo hiện có của dự án.

Tiện ích Firebase sử dụng Cloud Tasks để chạy các trình xử lý sự kiện trong vòng đời của bạn. Bạn xác định trình xử lý sự kiện bằng Cloud Functions; 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 một trình xử lý, thì trình xử lý đó sẽ được thêm vào hàng đợi Cloud Tasks. Sau đó, Cloud Tasks sẽ thực thi trình xử lý không đồng bộ. Khi trình xử lý sự kiện trong vòng đời đang chạy, bảng điều khiển của Firebase sẽ báo cáo cho người dùng rằng phiên bản tiện ích có một tác vụ xử lý đang được xử lý. Chức năng xử lý của bạn phụ thuộc vào việc báo cáo trạng thái liên tục và việc hoàn thành tác vụ cho người dùng.

Để thêm một trình xử lý sự kiện trong vòng đời nhằm bổ sung các tin nhắn hiện có, hãy làm như sau:

  1. Xác định một Hàm đám mây mới được kích hoạt bởi các sự kiện trong 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 số bản ghi trước khi tự thêm lại vào hàng đợi tác vụ. Đây là một chiến lược thường dùng để xử lý các công việc xử lý không thể hoàn thành trong khoảng thời gian chờ của một Hàm đám mây. Vì bạn không thể dự đoán số lượng thông báo mà người dùng có thể đã có trong cơ sở dữ liệu khi họ 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 thay thế dưới dạng một tài nguyên tiện ích 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 đó, hãy khai báo hàm này làm trình xử lý cho sự kiện trong vòng đời onInstall:

    lifecycleEvents:
      onInstall:
        function: backfilldata
        processingMessage: Uppercasing existing messages
    
  3. Mặc dù việc bổ sung các tin nhắn hiện có thì tốt, nhưng tiện ích này vẫn có thể hoạt động nếu không có. Trong các trường hợp như thế này, bạn nên tuỳ ý chạy trình xử lý sự kiện trong vòng đời.

    Để làm như vậ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 chèn lấp, hãy kiểm tra giá trị của thông số DO_BACKFILL và thoát sớm nếu bạn chưa đặt thông số này:

    functions/index.js

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

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

Cho đế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 cũng như kiểm thử các thay đổi đang diễn ra. Tuy nhiên, trình mô phỏng tiện ích 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 này trong một dự án thực tế. Mặc dù vậy, cũng chỉ là như vậy, vì khi được bổ sung tính năng chèn lấp tự động này, tiện ích hướng dẫn hiện đã mã hoàn chỉnh!

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

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

Để làm như vậy, trước tiên hãy thiết lập dự án mới có 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 của bạn lên gói linh hoạt trả tiền theo mức dùng. Cloud Functions cho 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. Do bạn muốn kiểm thử khả năng bổ sung dữ liệu hiện có khi cài đặt của tiện ích, 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 RTDB gốc xuống.
    2. Trên trang Cơ sở dữ liệu theo thời gian thực trong bảng điều khiển của Firebase, hãy nhấp vào (khác) > Nhập JSON rồi chọn tệp bạn vừa tải xuống.
  5. Để bật hàm chèn lấp nhằm 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ị 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 một thư mục mới cho dự án Firebase của bạn:

    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 mà bạn vừa tạo.

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

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

    Tại đây, bạn có thể thấy 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 "yes" khi công cụ cấu hình hỏi bạn có muốn bổ sung cơ sở dữ liệu hiện có hay không.

    Sau khi bạn chọn các lựa 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 của tiện ích trong tệp firebase.json. Hai bản ghi này được gọi chung 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 các dự án khác nhau.

  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 việc suôn sẻ, CLI của Firebase sẽ tải tiện ích lên dự án của bạn và cài đặt tiện ích đó. Sau khi cài đặt xong, tác vụ chèn lấp sẽ chạy và sau vài phút, cơ sở dữ liệu của bạn sẽ được cập nhật với 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 thông báo và đảm bảo tiện ích này cũng đang hoạt động với các thông báo mới.

10. Viết tài liệu

Trước khi bạn 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 để giúp họ thành công.

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

tiện ích mở rộng.yaml

Bạn đã cập nhật tệp này vì bạn đã 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 ngay bây giờ.

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 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à thông số mà người dùng có thể định cấu hình. Người dùng sẽ thấy thông tin này 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 người dùng cần trước khi cài đặt tiện ích của bạn: mô tả ngắn gọn 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ề ảnh hưởng của 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, thì đây cũng là nơi phù hợp để liên kết trang web đó.

Người dùng sẽ thấy văn bản của tệp này trong Trung tâm tiện ích và bằng 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ề tiện ích đang hoạt động, v.v.

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

Dưới đây là tệp ví dụ 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.

Hàm 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ì tiện ích mẫu chưa từng được xuất bản trước đó, nên nhật ký thay đổi chỉ có một mục nhập:

## Version 0.0.1

Initial release of the _Convert messages to upper case_ extension.

Hàm README.md

Hầu hết tiện ích cũng cung cấp tệp đọc tôi để 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 một tệp đọc cho tôi bằng lệnh.

Trong phạm vi của hướng dẫn này, hãy bỏ qua bước ghi tệp readme.

Tài liệu bổ sung

Tài liệu 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 mà bạn có thể hướng người dùng đến.

Trong phạm vi của hướng dẫn này, hãy bỏ qua bước viết tài liệu bao quát hơn.

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

Giờ đây, tiện ích của bạn đã hoàn tất mã và được ghi lại, bạn có thể chia sẻ tiện ích đó với mọi người trên Trung tâm tiện ích. Nhưng vì đây chỉ là hướng dẫn nên bạn không thực hiện điều đó. Hãy bắt đầu viết tiện ích của riêng bạn bằng cách sử dụng những gì bạn đã tìm hiểu 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 về Tiện ích Firebase, đồng thời kiểm tra nguồn của 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, sau đây là cách bạn sẽ thực hiện:

  1. Nếu bạn đang phát hành tiện ích đầu tiên, hãy đăng ký với tư cách là 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 có thể nhanh chóng xác định bạn là tác giả của các 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ể xuất bản tiện ích của bạn ngay 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 đang phát hành, đồng thời giúp người dùng bằng cách để họ kiểm tra mã họ đang cài đặt vào dự án.

    Hiện tại, bạn cần cung cấp tiện ích trong một kho lưu trữ GitHub công khai.

  3. Tải tiện ích của bạn lên Trung tâm tiện ích bằng lệnh firebase ext:dev:upload.

  4. Chuyển đến trang tổng quan nhà xuất bản trong bảng điều khiển của Firebase, tìm tiện ích bạn vừa tải lên và 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 này sẽ được xuất bản 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 đề được báo cáo và gửi lại để chúng tôi xem xét.