Sử dụng lại mã Cloud Functions làm tiện ích của Firebase

1. Trước khi bắt đầu

Tiện ích Firebase thực hiện một nhiệm vụ hoặc một nhóm nhiệm vụ cụ thể để phản hồi các yêu cầu HTTP hoặc các sự kiện kích hoạt từ các sản phẩm khác của Firebase và Google, chẳng hạn như Giải pháp gửi thông báo qua đám mây của Firebase, Cloud Firestore hoặc Pub/Sub.

Sản phẩm bạn sẽ tạo ra

Trong lớp học lập trình này, bạn sẽ tạo một tiện ích Firebase để băm địa lý. Sau khi triển khai, tiện ích của bạn sẽ chuyển đổi toạ độ X và Y thành geohash để phản hồi các sự kiện Firestore hoặc thông qua lệnh gọi hàm có thể gọi. Bạn có thể sử dụng tính năng này thay vì triển khai thư viện geofire trên tất cả các nền tảng mục tiêu để lưu trữ dữ liệu, giúp bạn tiết kiệm thời gian.

Tiện ích geohash xuất hiện trong bảng điều khiển của Firebase

Kiến thức bạn sẽ học được

  • Cách lấy mã Cloud Functions hiện có và biến mã đó thành một Tiện ích Firebase có thể phân phối
  • Cách thiết lập tệp extension.yaml
  • Cách lưu trữ các chuỗi nhạy cảm (khoá API) trong một tiện ích
  • Cách cho phép nhà phát triển tiện ích định cấu hình tiện ích đó cho phù hợp với nhu cầu của họ
  • Cách kiểm thử và triển khai tiện ích

Bạn cần

2. Bắt đầu thiết lập

Lấy mã nguồn

Mọi thứ bạn cần cho tiện ích này đều nằm trong một kho lưu trữ GitHub. Để bắt đầu, hãy lấy mã và mở mã đó trong môi trường phát triển mà bạn yêu thích.

  1. Giải nén tệp zip đã tải xuống.
  2. Để cài đặt các phần phụ thuộc bắt buộc, hãy mở cửa sổ dòng lệnh trong thư mục functions rồi chạy lệnh npm install.

Thiết lập Firebase

Lớp học lập trình này khuyến khích bạn sử dụng trình mô phỏng Firebase. Nếu bạn muốn dùng thử tính năng phát triển tiện ích bằng một dự án Firebase thực, hãy xem phần tạo dự án Firebase. Lớp học lập trình này sử dụng Cloud Functions, vì vậy, nếu đang dùng một dự án Firebase thực thay vì trình mô phỏng, bạn cần nâng cấp lên gói giá linh hoạt.

Bạn muốn bỏ qua?

Bạn có thể tải phiên bản hoàn chỉnh của lớp học lập trình này xuống. Nếu bạn gặp khó khăn trong quá trình thực hiện hoặc muốn xem tiện ích đã hoàn thành trông như thế nào, hãy xem nhánh codelab-end của kho lưu trữ GitHub hoặc tải tệp zip đã hoàn thành xuống.

3. Xem lại mã

  • Mở tệp index.ts trong tệp zip. Xin lưu ý rằng tệp này chứa 2 khai báo Cloud Functions.

Những hàm này có chức năng gì?

Các hàm minh hoạ này được dùng để băm địa lý. Chúng lấy một cặp toạ độ và chuyển đổi cặp toạ độ đó thành một định dạng được tối ưu hoá cho các truy vấn địa lý trong Firestore. Các hàm này mô phỏng việc sử dụng một lệnh gọi API để bạn có thể tìm hiểu thêm về cách xử lý các loại dữ liệu nhạy cảm trong tiện ích. Để biết thêm thông tin, hãy xem tài liệu về chạy truy vấn Geo trên dữ liệu trong Firestore.

Hằng số hàm

Các hằng số được khai báo từ sớm, ở đầu tệp index.ts. Một số hằng số trong số này được tham chiếu trong các điều kiện kích hoạt đã xác định của tiện ích.

index.ts

import {firestore} from "firebase-functions";
import {initializeApp} from "firebase-admin/app";
import {GeoHashService, ResultStatusCode} from "./fake-geohash-service";
import {onCall} from "firebase-functions/v1/https";
import {fieldValueExists} from "./utils";

const documentPath = "users/{uid}";
const xField = "xv";
const yField = "yv";
const apiKey = "1234567890";
const outputField = "hash";

initializeApp();

const service = new GeoHashService(apiKey);

Điều kiện kích hoạt Firestore

Hàm đầu tiên trong tệp index.ts có dạng như sau:

index.ts

export const locationUpdate = firestore.document(documentPath)
  .onWrite((change) => {
    // item deleted
    if (change.after == null) {
      return 0;
    }
    // double check that both values exist for computation
    if (
      !fieldValueExists(change.after.data(), xField) ||
      !fieldValueExists(change.after.data(), yField)
    ) {
      return 0;
    }
    const x: number = change.after.data()![xField];
    const y: number = change.after.data()![yField];
    const hash = service.convertToHash(x, y);
    // This is to check whether the hash value has changed. If
    // it hasn't, you don't want to write to the document again as it
    // would create a recursive write loop.
    if (fieldValueExists(change.after.data(), outputField)
      && change.after.data()![outputField] == hash) {
      return 0;
    }
    return change.after.ref
      .update(
        {
          [outputField]: hash.hash,
        }
      );
  });

Hàm này là một trình kích hoạt Firestore. Khi một sự kiện ghi xảy ra trong cơ sở dữ liệu, hàm sẽ phản ứng với sự kiện đó bằng cách tìm kiếm một trường xv và một trường yv. Nếu cả hai trường đó đều tồn tại, hàm sẽ tính toán geohash và ghi đầu ra vào một vị trí đầu ra của tài liệu được chỉ định. Tài liệu đầu vào được xác định bằng hằng số users/{uid}, tức là hàm sẽ đọc mọi tài liệu được ghi vào tập hợp users/ rồi xử lý một geohash cho những tài liệu đó. Sau đó, hàm này sẽ xuất hàm băm vào một trường băm trong cùng một tài liệu.

Hàm có thể gọi

Hàm tiếp theo trong tệp index.ts có dạng như sau:

index.ts

export const callableHash = onCall((data, context) => {
  if (context.auth == undefined) {
    return {error: "Only authorized users are allowed to call this endpoint"};
  }
  const x = data[xField];
  const y = data[yField];
  if (x == undefined || y == undefined) {
    return {error: "Either x or y parameter was not declared"};
  }
  const result = service.convertToHash(x, y);
  if (result.status != ResultStatusCode.ok) {
    return {error: `Something went wrong ${result.message}`};
  }
  return {result: result.hash};
});

Hãy lưu ý đến hàm onCall. Điều này cho biết hàm này là một hàm có thể gọi, có thể được gọi từ trong mã ứng dụng khách của bạn. Hàm có thể gọi này lấy các tham số xy rồi trả về một geohash. Mặc dù hàm này sẽ không được gọi trực tiếp trong lớp học lập trình này, nhưng hàm này được đưa vào đây làm ví dụ về một nội dung cần định cấu hình trong tiện ích Firebase.

4. Thiết lập tệp extension.yaml

Giờ đây, khi đã biết mã Cloud Functions trong tiện ích của bạn làm gì, bạn đã sẵn sàng đóng gói mã đó để phân phối. Mỗi Tiện ích Firebase đều đi kèm với một tệp extension.yaml mô tả chức năng và cách hoạt động của tiện ích.

Tệp extension.yaml yêu cầu một số siêu dữ liệu ban đầu về tiện ích của bạn. Từng bước sau đây sẽ giúp bạn hiểu ý nghĩa của tất cả các trường và lý do bạn cần những trường đó.

  1. Tạo một tệp extension.yaml trong thư mục gốc của dự án mà bạn đã tải xuống trước đó. Bắt đầu bằng cách thêm những nội dung sau:
name: geohash-ext
version: 0.0.1
specVersion: v1beta  # Firebase Extensions specification version (do not edit)

Tên của tiện ích được dùng làm cơ sở cho mã nhận dạng phiên bản của tiện ích (người dùng có thể cài đặt nhiều phiên bản của một tiện ích, mỗi phiên bản có mã nhận dạng riêng). Sau đó, Firebase sẽ tạo tên cho tài khoản dịch vụ của tiện ích và các tài nguyên dành riêng cho tiện ích bằng cách sử dụng mã nhận dạng phiên bản đó. Số phiên bản cho biết phiên bản của tiện ích. Bạn phải tuân theo phiên bản ngữ nghĩa và cần cập nhật phiên bản này bất cứ khi nào bạn thay đổi chức năng của tiện ích. Phiên bản đặc tả tiện ích được dùng để xác định đặc tả tiện ích Firebase cần tuân theo, trong trường hợp này, v1beta sẽ được dùng.

  1. Thêm một số thông tin chi tiết thân thiện với người dùng vào tệp YAML:
...

displayName: Latitude and longitude to GeoHash converter
description: A converter for changing your Latitude and longitude coordinates to geohashes.

Tên hiển thị là một phiên bản thân thiện của tên tiện ích khi nhà phát triển tương tác với tiện ích của bạn. Nội dung mô tả cung cấp thông tin tổng quan ngắn gọn về chức năng của tiện ích. Khi tiện ích được triển khai trên extensions.dev, tiện ích sẽ có dạng như sau:

Tiện ích Geohash Converter như trong extensions.dev

  1. Chỉ định giấy phép cho mã trong tiện ích của bạn.
...

license: Apache-2.0  # The license you want for the extension
  1. Cho biết ai đã viết tiện ích và liệu người dùng có cần thanh toán để cài đặt tiện ích đó hay không:
...

author:
  authorName: AUTHOR_NAME
  url: https://github.com/Firebase

billingRequired: true

Mục author được dùng để cho người dùng biết họ cần liên hệ với ai trong trường hợp gặp vấn đề với tiện ích hoặc muốn biết thêm thông tin về tiện ích đó. billingRequired là một tham số bắt buộc và phải được đặt thành true vì tất cả các tiện ích đều dựa vào Cloud Functions, mà Cloud Functions yêu cầu gói Blaze.

Phần này đề cập đến số lượng tối thiểu các trường bắt buộc trong tệp extension.yaml để xác định tiện ích này. Để biết thêm thông tin về những thông tin nhận dạng khác mà bạn có thể chỉ định trong một tiện ích, hãy xem tài liệu này.

5. Chuyển đổi mã Cloud Functions thành tài nguyên Extensions

Tài nguyên tiện ích là một mục mà Firebase tạo trong dự án trong quá trình cài đặt tiện ích. Sau đó, tiện ích sẽ sở hữu những tài nguyên đó và có một tài khoản dịch vụ cụ thể hoạt động trên các tài nguyên đó. Trong dự án này, các tài nguyên đó là Cloud Functions. Bạn phải xác định các tài nguyên này trong tệp extension.yaml vì tiện ích sẽ không tự động tạo tài nguyên từ mã trong thư mục functions. Nếu Cloud Functions không được khai báo rõ ràng là một tài nguyên, thì chúng không thể được triển khai khi tiện ích được triển khai.

Vị trí triển khai do người dùng xác định

  1. Cho phép người dùng chỉ định vị trí mà họ muốn triển khai tiện ích này và quyết định xem có nên lưu trữ tiện ích gần người dùng cuối hơn hay gần cơ sở dữ liệu hơn. Trong tệp extension.yaml, hãy thêm lựa chọn chọn vị trí.

extension.yaml

Bây giờ, bạn đã sẵn sàng viết cấu hình cho tài nguyên hàm.

  1. Trong tệp extension.yaml, hãy tạo một đối tượng tài nguyên cho hàm locationUpdate. Thêm nội dung sau vào tệp extension.yaml:
resources:
  - name: locationUpdate
    type: firebaseextensions.v1beta.function
    properties:
      eventTrigger:
        eventType: providers/cloud.firestore/eventTypes/document.write
        resource: projects/${PROJECT_ID}/databases/(default)/documents/users/{uid}

Bạn xác định name là tên hàm được xác định trong tệp index.ts của dự án. Bạn chỉ định type của hàm đang được triển khai, hàm này hiện tại phải luôn là firebaseextensions.v1beta.function. Sau đó, bạn xác định properties của hàm này. Thuộc tính đầu tiên mà bạn xác định là eventTrigger được liên kết với hàm này. Để phản ánh những gì mà tiện ích hiện hỗ trợ, bạn hãy sử dụng eventType của providers/cloud.firestore/eventTypes/document.write. Bạn có thể tìm thấy thông tin này trong tài liệu Viết Cloud Functions cho tiện ích của bạn. Bạn xác định resource là vị trí của các tài liệu. Vì mục tiêu hiện tại của bạn là phản ánh những gì có trong mã, nên đường dẫn tài liệu sẽ theo dõi users/{uid}, với vị trí cơ sở dữ liệu mặc định đứng trước.

  1. Tiện ích này cần có quyền đọc và ghi cho cơ sở dữ liệu Firestore. Ở cuối tệp extension.yaml, hãy chỉ định các vai trò IAM mà tiện ích cần có quyền truy cập để làm việc với cơ sở dữ liệu trong dự án Firebase của nhà phát triển.
roles:
  - role: datastore.user
    reason: Allows the extension to read / write to your Firestore instance.

Vai trò datastore.user nằm trong danh sách các vai trò IAM được hỗ trợ cho tiện ích. Vì tiện ích sẽ đọc và ghi nên vai trò datastore.user là lựa chọn phù hợp ở đây.

  1. Bạn cũng phải thêm hàm có thể gọi. Trong tệp extension.yaml, hãy tạo một tài nguyên mới trong thuộc tính tài nguyên. Đây là các thuộc tính dành riêng cho một hàm có thể gọi:
  - name: callableHash
    type: firebaseextensions.v1beta.function
    properties:
      httpsTrigger: {}

Mặc dù tài nguyên trước đó sử dụng eventTrigger, nhưng ở đây bạn sử dụng httpsTrigger, bao gồm cả hàm có thể gọi và hàm HTTPS.

Kiểm tra mã

Đó là rất nhiều cấu hình để extension.yaml của bạn khớp với mọi thứ mà mã trong tệp index.ts của bạn thực hiện. Đây là nội dung của tệp extension.yaml hoàn chỉnh tại thời điểm này:

extension.yaml

name: geohash-ext
version: 0.0.1
specVersion: v1beta  # Firebase Extensions specification version (do not edit)

displayName: Latitude and Longitude to GeoHash converter
description: A converter for changing your Latitude and Longitude coordinates to geohashes.

license: Apache-2.0  # The license you want for the extension

author:
  authorName: Sparky
  url: https://github.com/Firebase

billingRequired: true

resources:
  - name: locationUpdate
    type: firebaseextensions.v1beta.function
    properties:
      eventTrigger:
        eventType: providers/cloud.firestore/eventTypes/document.write
        resource: projects/${PROJECT_ID}/databases/(default)/documents/users/{uid}
  - name: callableHash
    type: firebaseextensions.v1beta.function
    properties:
      httpsTrigger: {}

roles:
  - role: datastore.user
    reason: Allows the extension to read / write to your Firestore instance.

Kiểm tra trạng thái

Đến đây, bạn đã thiết lập các phần chức năng ban đầu của tiện ích, vì vậy, bạn có thể dùng thử bằng trình mô phỏng Firebase!

  1. Nếu chưa, hãy gọi npm run build trong thư mục functions của dự án tiện ích đã tải xuống.
  2. Tạo một thư mục mới trên hệ thống máy chủ và kết nối thư mục đó với dự án Firebase bằng cách sử dụng firebase init.
cd ..
mkdir sample-proj
cd sample-proj
firebase init --project=projectID-or-alias
    This command creates a `firebase.json` file in the directory. In the following steps, you push the configuration specified in this file to Firebase.
  1. Trong cùng thư mục đó, hãy chạy firebase ext:install. Thay thế /path/to/extension bằng đường dẫn tuyệt đối đến thư mục chứa tệp extension.yaml.
firebase ext:install /path/to/extension
    This command does two things:
  • Thao tác này sẽ nhắc bạn chỉ định cấu hình cho phiên bản tiện ích và tạo một tệp *.env chứa thông tin cấu hình cho phiên bản đó.
  • Thao tác này sẽ thêm thực thể tiện ích vào phần extensions của firebase.json. Đây là bản đồ mã nhận dạng phiên bản cho phiên bản tiện ích.
  • Vì đang triển khai dự án trên máy, nên bạn có thể chỉ định rằng bạn muốn sử dụng một tệp cục bộ thay vì Google Cloud Secret Manager.

Ảnh chụp màn hình quy trình cài đặt tiện ích cho thấy Tệp cục bộ đang được dùng cho các khoá bí mật khi cài đặt tiện ích này

  1. Khởi động trình mô phỏng Firebase bằng cấu hình mới:
firebase emulators:start
  1. Sau khi chạy emulators:start, hãy chuyển đến thẻ Firestore trong webview của trình mô phỏng.
  2. Thêm một tài liệu vào bộ sưu tập users bằng trường số xv và trường số yv.

Một hộp thoại xuất hiện trong Firebase Emulators để bắt đầu một bộ sưu tập có mã bộ sưu tập chứa cụm từ

  1. Nếu bạn cài đặt thành công tiện ích này, thì tiện ích sẽ tạo một trường mới có tên là hash trong tài liệu.

Tập hợp người dùng có tài liệu người dùng có trường xv, yv và hash.

Dọn dẹp để tránh xung đột

  • Sau khi kiểm thử xong, hãy gỡ cài đặt tiện ích vì bạn sẽ cập nhật mã tiện ích và không muốn xảy ra xung đột với tiện ích hiện tại sau này.

Tiện ích cho phép bạn cài đặt nhiều phiên bản của cùng một tiện ích cùng một lúc. Vì vậy, khi gỡ cài đặt, bạn sẽ đảm bảo không xảy ra xung đột với một tiện ích đã cài đặt trước đó.

firebase ext:uninstall geohash-ext

Giải pháp hiện tại hoạt động, nhưng như đã đề cập ở phần đầu dự án, có một khoá API được mã hoá cứng để mô phỏng việc giao tiếp với một dịch vụ. Làm cách nào để bạn có thể sử dụng khoá API của người dùng cuối thay vì khoá được cung cấp ban đầu? Hãy đọc tiếp để tìm hiểu.

6. Cho phép người dùng định cấu hình tiện ích

Đến thời điểm này trong lớp học lập trình, bạn đã có một tiện ích được định cấu hình để sử dụng với chế độ thiết lập có chủ đích của các hàm mà bạn đã viết. Tuy nhiên, điều gì sẽ xảy ra nếu người dùng muốn sử dụng vĩ độ và kinh độ thay vì yx cho các trường cho biết vị trí trên mặt phẳng Đề các? Ngoài ra, làm cách nào để người dùng cuối cung cấp khoá API của riêng họ thay vì để họ sử dụng khoá API được cung cấp? Bạn có thể nhanh chóng vượt quá hạn mức cho API đó. Trong trường hợp này, bạn thiết lập và sử dụng các tham số.

Xác định các tham số cơ bản trong tệp extension.yaml

Bắt đầu bằng cách chuyển đổi những mục mà nhà phát triển có thể có cấu hình tuỳ chỉnh. Tham số đầu tiên sẽ là XFIELDYFIELD.

  1. Trong tệp extension.yaml, hãy thêm mã sau. Mã này sử dụng XFIELD và các tham số trường YFIELD. Các tham số này nằm trong thuộc tính YAML params mà bạn đã xác định trước đó:

extension.yaml

params:
  - param: XFIELD
    label: The X Field Name
    description: >-
      The X Field is also known as the **longitude** value. What does
      your Firestore instance refer to as the X value or the longitude
      value. If no value is specified, the extension searches for
      field 'xv'.
    type: string
    validationRegex: ^\D([0-9a-zA-Z_.]{0,375})$
    validationErrorMessage: >-
      The field can only contain uppercase or lowercase letter, numbers,
      _, and . characters and must be less than 1500 bytes long. The field
      must also not start with a number.
    default: xv
    required: false
    immutable: false
    example: xv
  - param: YFIELD
    label: The Y Field Name
    description: >-
      The Y Field is also known as the **latitude** value. What does
      your Firestore instance refer to as the Y value or the latitude
      value. If no value is specified, the extension searches for
      field 'yv'.
    type: string
    validationRegex: ^\D([0-9a-zA-Z_.]{0,375})$
    validationErrorMessage: >-
      The field can only contain uppercase or lowercase letter, numbers,
      _, and . characters and must be less than 1500 bytes long. The field
      must also not start with a number.
    default: yv
    required: false
    immutable: false
    example: yv
  • param đặt tên cho tham số theo cách mà bạn (nhà sản xuất tiện ích) có thể thấy. Hãy sử dụng giá trị này sau khi chỉ định các giá trị tham số.
  • label là một giá trị nhận dạng mà con người có thể đọc được để cho nhà phát triển biết tham số này làm gì.
  • description cung cấp nội dung mô tả chi tiết về giá trị. Vì hỗ trợ markdown nên bạn có thể liên kết đến tài liệu bổ sung hoặc làm nổi bật những từ có thể quan trọng đối với nhà phát triển.
  • type xác định cơ chế nhập để người dùng đặt giá trị tham số. Có nhiều loại, bao gồm string, select, multiSelect, selectResourcesecret. Để tìm hiểu thêm về từng lựa chọn này, hãy xem tài liệu.
  • validationRegex hạn chế mục nhập của nhà phát triển đối với một giá trị regex nhất định (trong ví dụ này, giá trị đó dựa trên các nguyên tắc đơn giản về tên trường tại đây); và nếu không thành công...
  • validationErrorMessage cảnh báo cho nhà phát triển về giá trị thất bại.
  • default là giá trị sẽ xuất hiện nếu nhà phát triển không nhập văn bản nào.
  • required (bắt buộc) có nghĩa là nhà phát triển không bắt buộc phải nhập bất kỳ văn bản nào.
  • immutable cho phép nhà phát triển cập nhật tiện ích này và thay đổi giá trị này. Trong trường hợp này, nhà phát triển có thể thay đổi tên trường khi yêu cầu của họ thay đổi.
  • example cho biết một đầu vào hợp lệ có thể trông như thế nào.

Có rất nhiều điều cần hiểu!

  1. Bạn cần thêm 3 tham số nữa vào tệp extension.yaml trước khi thêm một tham số đặc biệt.
  - param: INPUTPATH
    label: The input document to listen to for changes
    description: >-
      This is the document where you write an x and y value to. Once
      that document has received a value, it notifies the extension to
      calculate a geohash and store that in an output document in a certain
      field. This accepts function [wildcard parameters](https://firebase.google.com/docs/functions/firestore-events#wildcards-parameters)
    type: string
    validationRegex: ^[^/]+(/[^/]*/[^/]*)*/[^/]+$
    validationErrorMessage: >-
      This must point to a document path, not a collection path from the root
      of the database. It must also not start or end with a '/' character.
    required: true
    immutable: false
    example: users/{uid}
  - param: OUTPUTFIELD
    label: Geohash field
    description: >-
      This specifies the field in the output document to store the geohash in.
    type: string
    validationRegex: ^\D([0-9a-zA-Z_.]{0,375})$
    validationErrorMessage: >-
      The field can only contain uppercase or lowercase letter, numbers,
      _, and . characters and must be less than 1500 bytes long. The field
      must also not start with a number.
    required: false
    default: hash
    immutable: false
    example: hash

Xác định các thông số nhạy cảm

Bây giờ, bạn cần quản lý khoá API mà người dùng chỉ định. Đây là một chuỗi nhạy cảm không được lưu trữ dưới dạng văn bản thuần tuý trong hàm. Thay vào đó, hãy lưu trữ giá trị này trong Trình quản lý khoá bí mật trên đám mây. Đây là một vị trí đặc biệt trên đám mây, nơi lưu trữ các bí mật đã mã hoá và ngăn chặn tình trạng vô tình để lộ các bí mật đó. Điều này đòi hỏi nhà phát triển phải trả phí khi sử dụng dịch vụ này, nhưng nó sẽ bổ sung thêm một lớp bảo mật cho khoá API của họ và có khả năng hạn chế hoạt động gian lận. Tài liệu người dùng cảnh báo nhà phát triển rằng đây là một dịch vụ có tính phí, để không có bất kỳ điều gì bất ngờ trong việc thanh toán. Nhìn chung, cách sử dụng tương tự như các tài nguyên chuỗi khác được đề cập ở trên. Điểm khác biệt duy nhất là loại được gọi là secret.

  • Trong tệp extension.yaml, hãy thêm đoạn mã sau:

extension.yaml

  - param: APIKEY
    label: GeohashService API Key
    description: >-
      Your geohash service API Key. Since this is a demo, and not a real
      service, you can use : 1234567890.
    type: secret
    required: true
    immutable: false

Cập nhật resource các thuộc tính để sử dụng tham số

Như đã đề cập trước đó, tài nguyên (không phải hàm) xác định cách tài nguyên được quan sát, vì vậy, bạn cần cập nhật tài nguyên locationUpdate để sử dụng tham số mới.

  • Trong tệp extension.yaml, hãy thêm đoạn mã sau:

extension.yaml

## Change from this
  - name: locationUpdate
    type: firebaseextensions.v1beta.function
    properties:
      eventTrigger:
        eventType: providers/cloud.firestore/eventTypes/document.write
        resource: projects/${PROJECT_ID}/databases/(default)/documents/users/{uid}]

## To this
  - name: locationUpdate
    type: firebaseextensions.v1beta.function
    properties:
      eventTrigger:
        eventType: providers/cloud.firestore/eventTypes/document.write
        resource: projects/${PROJECT_ID}/databases/(default)/documents/${INPUTPATH}

Kiểm tra tệp extension.yaml

  • Xem lại tệp extension.yaml. Danh sách sẽ trông giống như sau:

extension.yaml

name: geohash-ext
version: 0.0.1
specVersion: v1beta  # Firebase Extensions specification version (do not edit)

displayName: Latitude and Longitude to GeoHash converter
description: A converter for changing your Latitude and Longitude coordinates to geohashes.

license: Apache-2.0  # The license you want to use for the extension

author:
  authorName: Sparky
  url: https://github.com/Firebase

billingRequired: true

params:
  - param: XFIELD
    label: The X Field Name
    description: >-
      The X Field is also known as the **longitude** value. What does
      your Firestore instance refer to as the X value or the longitude
      value. If you don't provide a value for this field, the extension will use 'xv' as the default value.
    type: string
    validationRegex: ^\D([0-9a-zA-Z_.]{0,375})$
    validationErrorMessage: >-
      The field can only contain uppercase or lowercase letter, numbers,
      _, and . characters and must be less than 1500 bytes long. The field
      must also not start with a number.
    default: xv
    required: false
    immutable: false
    example: xv
  - param: YFIELD
    label: The Y Field Name
    description: >-
      The Y Field is also known as the **latitude** value. What does
      your Firestore instance refer to as the Y value or the latitude
      Value. If you don't provide a value for this field, the extension will use 'yv' as the default value.
    type: string
    validationRegex: ^\D([0-9a-zA-Z_.]{0,375})$
    validationErrorMessage: >-
      The field can only contain uppercase or lowercase letter, numbers,
      _, and . characters and must be less than 1500 bytes long. The field
      must also not start with a number.
    default: yv
    required: false
    immutable: false
    example: yv
  - param: INPUTPATH
    label: The input document to listen to for changes
    description: >-
      This is the document where you write an x and y value to. Once
      that document has been modified, it notifies the extension to
      compute a geohash and store that in an output document in a certain
      field. This accepts function [wildcard parameters](https://firebase.google.com/docs/functions/firestore-events#wildcards-parameters)
    type: string
    validationRegex: ^[^/]+(/[^/]*/[^/]*)*/[^/]+$
    validationErrorMessage: >-
      This must point to a document path, not a collection path from the root
      of the database. It must also not start or end with a '/' character.
    required: true
    immutable: false
    example: users/{uid}
  - param: OUTPUTFIELD
    label: Geohash field
    description: >-
      This specifies the field in the output document to store the geohash in.
    type: string
    validationRegex: ^\D([0-9a-zA-Z_.]{0,375})$
    validationErrorMessage: >-
      The field can only contain uppercase or lowercase letter, numbers,
      _, and . characters and must be less than 1500 bytes long. The field
      must also not start with a number.
    required: false
    default: hash
    immutable: false
    example: hash
  - param: APIKEY
    label: GeohashService API Key
    description: >-
      Your geohash service API Key. Since this is a demo, and not a real
      service, you can use : 1234567890.
    type: secret
    required: true
    immutable: false

resources:
  - name: locationUpdate
    type: firebaseextensions.v1beta.function
    properties:
      eventTrigger:
        eventType: providers/cloud.firestore/eventTypes/document.write
        resource: projects/${PROJECT_ID}/databases/(default)/documents/${INPUTPATH}
  - name: callableHash
    type: firebaseextensions.v1beta.function
    properties:
      httpsTrigger: {}

roles:
  - role: datastore.user
    reason: Allows the extension to read / write to your Firestore instance.

Truy cập vào các tham số trong mã

Bây giờ, khi tất cả các tham số đã được định cấu hình trong tệp extension.yaml, hãy thêm các tham số đó vào tệp index.ts.

  • Trong tệp index.ts, hãy thay thế các giá trị mặc định bằng process.env.PARAMETER_NAME. Thao tác này sẽ tìm nạp các giá trị tham số thích hợp và điền các giá trị đó vào mã hàm được triển khai trên dự án Firebase của nhà phát triển.

index.ts

// Replace this:
const documentPath = "users/{uid}";
const xField = "xv";
const yField = "yv";
const apiKey = "1234567890";
const outputField = "hash";

// with this:
const documentPath = process.env.INPUTPATH!; // this value is ignored since its read from the resource
const xField = process.env.XFIELD!;
const yField = process.env.YFIELD!;
const apiKey = process.env.APIKEY!;
const outputField = process.env.OUTPUTFIELD!;

Thông thường, bạn muốn thực hiện các quy trình kiểm tra giá trị rỗng bằng các giá trị biến môi trường, nhưng trong trường hợp này, bạn tin rằng các giá trị tham số được sao chép chính xác. Giờ đây, mã này được định cấu hình để hoạt động với các tham số của tiện ích.

7. Tạo tài liệu người dùng

Trước khi kiểm thử mã trên trình mô phỏng hoặc trong trang web thương mại của các tiện ích Firebase, bạn cần lập tài liệu cho tiện ích để nhà phát triển biết họ sẽ nhận được gì khi sử dụng tiện ích đó.

  1. Bắt đầu bằng cách tạo tệp PREINSTALL.md. Tệp này dùng để mô tả chức năng, mọi điều kiện tiên quyết để cài đặt và những tác động tiềm ẩn đến việc thanh toán.

PREINSTALL.md

Use this extension to automatically convert documents with a latitude and
longitude to a geohash in your database. Additionally, this extension includes a callable function that allows users to make one-time calls
to convert an x,y coordinate into a geohash.

Geohashing is supported for latitudes between 90 and -90 and longitudes
between 180 and -180.

#### Third Party API Key

This extension uses a fictitious third-party API for calculating the
geohash. You need to supply your own API keys. (Since it's fictitious,
you can use 1234567890 as an API key).

#### Additional setup

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

After installing this extension, you'll need to:

- Update your client code to point to the callable geohash function if you
want to perform arbitrary geohashes.

Detailed information for these post-installation tasks are provided after
you install this extension.

#### 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:
 - Cloud Firestore
 - Cloud Functions (Node.js 16+ runtime. [See
FAQs](https://firebase.google.com/support/faq#extensions-pricing))
 - [Cloud Secret Manager](https://cloud.google.com/secret-manager/pricing)
  1. Để tiết kiệm thời gian viết README.md cho dự án này, hãy sử dụng phương thức thuận tiện sau:
firebase ext:info . --markdown > README.md

Thao tác này kết hợp nội dung của tệp PREINSTALL.md và thông tin chi tiết bổ sung về tiện ích từ tệp extension.yaml.

Cuối cùng, hãy thông báo cho nhà phát triển tiện ích về một số thông tin bổ sung liên quan đến tiện ích vừa được cài đặt. Nhà phát triển có thể nhận được một số hướng dẫn và thông tin bổ sung sau khi hoàn tất quá trình cài đặt và có thể nhận được một số nhiệm vụ chi tiết sau khi cài đặt, chẳng hạn như thiết lập mã ứng dụng ở đây.

  1. Tạo tệp POSTINSTALL.md, sau đó thêm thông tin sau khi cài đặt:

POSTINSTALL.md

Congratulations on installing the geohash extension!

#### Function information

* **Firestore Trigger** - ${function:locationUpdate.name} was installed
and is invoked when both an x field (${param:XFIELD}) and y field
(${param:YFIELD}) contain a value.

* **Callable Trigger** - ${function:callableHash.name} was installed and
can be invoked by writing the following client code:
 ```javascript
import { getFunctions, httpsCallable } from "firebase/functions";
const functions = getFunctions();
const geoHash = httpsCallable(functions, '${function:callableHash.name}');
geoHash({ ${param:XFIELD}: -122.0840, ${param:YFIELD}: 37.4221 })
  .then((result) => {
    // Read result of the Cloud Function.
    /** @type {any} */
    const data = result.data;
    const error = data.error;
    if (error != null) {
        console.error(`callable error : ${error}`);
    }
    const result = data.result;
    console.log(result);
  });

Giám sát

Theo phương pháp hay nhất, bạn có thể giám sát hoạt động của tiện ích đã cài đặt, bao gồm cả việc kiểm tra tình trạng, mức sử dụng và nhật ký của tiện ích.

The output rendering looks something like this when it's deployed:

<img src="img/82b54a5c6ca34b3c.png" alt="A preview of the latitude and longitude geohash converter extension in the firebase console"  width="957.00" />


## Test the extension with the full configuration
Duration: 03:00


It's time to make sure that the user-configurable extension is working the way it is intended.

* Change into the functions folder and ensure that the latest compiled version of the extensions exists. In the extensions project functions directory, call:

```console
npm run build

Thao tác này sẽ biên dịch lại các hàm để mã nguồn mới nhất sẵn sàng triển khai cùng với tiện ích khi tiện ích được triển khai đến một trình mô phỏng hoặc trực tiếp đến Firebase.

Tiếp theo, hãy tạo một thư mục mới để kiểm thử tiện ích. Vì tiện ích được phát triển từ các hàm hiện có, nên đừng kiểm thử từ thư mục mà tiện ích được định cấu hình vì thao tác đó cũng sẽ cố gắng triển khai các hàm và quy tắc Firebase cùng với tiện ích.

Cài đặt và kiểm thử bằng trình mô phỏng Firebase

  1. Tạo một thư mục mới trên hệ thống máy chủ và kết nối thư mục đó với dự án Firebase bằng cách sử dụng firebase init.
mkdir sample-proj
cd sample-proj
firebase init --project=projectID-or-alias
  1. Trong thư mục đó, hãy chạy firebase ext:install để cài đặt tiện ích. Thay thế /path/to/extension bằng đường dẫn tuyệt đối đến thư mục chứa tệp extension.yaml. Thao tác này sẽ bắt đầu quá trình cài đặt tiện ích và tạo một tệp .env chứa các cấu hình của bạn trước khi đẩy cấu hình đó lên Firebase hoặc trình mô phỏng.
firebase ext:install /path/to/extension
  • Vì bạn đang triển khai dự án cục bộ, hãy chỉ định rằng bạn muốn sử dụng một tệp cục bộ thay vì Google Cloud Secret Manager.

da928c65ffa8ce15.png

  1. Khởi động bộ công cụ mô phỏng cục bộ:
firebase emulators:start

Cài đặt và thử nghiệm bằng một dự án Firebase thực

Bạn có thể cài đặt tiện ích trong một dự án Firebase thực tế. Bạn nên sử dụng một dự án kiểm thử để kiểm thử. Hãy sử dụng quy trình kiểm thử này nếu bạn muốn kiểm thử quy trình toàn diện của tiện ích hoặc nếu bộ mô phỏng Firebase chưa hỗ trợ trình kích hoạt của tiện ích (xem Lựa chọn về trình mô phỏng tiện ích). Các trình mô phỏng hiện hỗ trợ các hàm được kích hoạt bằng yêu cầu HTTP và các hàm được kích hoạt bằng sự kiện nền cho Cloud Firestore, Realtime Database và Pub/Sub.

  1. Tạo một thư mục mới trên hệ thống máy chủ và kết nối thư mục đó với dự án Firebase bằng cách sử dụng firebase init.
cd ..
mkdir sample-proj
cd sample-proj
firebase init --project=projectID-or-alias
  1. Sau đó, từ thư mục đó, hãy chạy firebase ext:install để cài đặt tiện ích. Thay thế /path/to/extension bằng đường dẫn tuyệt đối đến thư mục chứa tệp extension.yaml. Thao tác này sẽ bắt đầu quá trình cài đặt tiện ích và tạo một tệp .env chứa các cấu hình của bạn trước khi đẩy cấu hình đó lên Firebase hoặc trình mô phỏng.
firebase ext:install /path/to/extension
  • Vì muốn triển khai trực tiếp lên Firebase và sử dụng Google Cloud Secret Manager, bạn cần kích hoạt Secret Manager API trước khi cài đặt tiện ích.
  1. Triển khai cho dự án Firebase của bạn.
firebase deploy

Dùng thử tiện ích

  1. Sau khi chạy firebase deploy hoặc firebase emulators:start, hãy chuyển đến thẻ Firestore của bảng điều khiển của Firebase hoặc webview của trình mô phỏng, tuỳ theo trường hợp.
  2. Thêm một tài liệu vào bộ sưu tập được chỉ định theo trường x và trường y. Trong trường hợp này, các tài liệu đã cập nhật nằm tại u/{uid} với trường xxv và trường yyv.

Màn hình Trình mô phỏng Firebase để thêm Bản ghi Firestore

  1. Nếu bạn cài đặt thành công tiện ích này, thì tiện ích sẽ tạo một trường mới có tên là hash trong tài liệu sau khi bạn lưu hai trường này.

Màn hình cơ sở dữ liệu Firestore trên một trình mô phỏng cho thấy hàm băm đã được thêm

8. Xin chúc mừng!

Bạn đã chuyển đổi thành công Cloud Functions đầu tiên thành một Tiện ích Firebase!

Bạn đã thêm một tệp extension.yaml và định cấu hình tệp đó để nhà phát triển có thể chọn cách họ muốn triển khai tiện ích của bạn. Sau đó, bạn đã tạo tài liệu người dùng hướng dẫn những việc mà nhà phát triển tiện ích cần làm trước khi thiết lập tiện ích và những bước họ có thể cần thực hiện sau khi cài đặt tiện ích thành công.

Giờ đây, bạn đã biết các bước quan trọng cần thiết để chuyển đổi một Hàm Firebase thành một Tiện ích Firebase có thể phân phối.

Tiếp theo là gì?