นำโค้ด Cloud Functions มาใช้เป็นส่วนขยาย Firebase

1. ก่อนเริ่มต้น

ส่วนขยาย Firebase จะทำงานเฉพาะอย่างหรือชุดงานเพื่อตอบสนองต่อคำขอ HTTP หรือเหตุการณ์ที่ทริกเกอร์จากผลิตภัณฑ์อื่นๆ ของ Firebase และ Google เช่น Firebase Cloud Messaging, Cloud Firestore หรือ Pub/Sub

สิ่งที่คุณจะสร้าง

ในโค้ดแล็บนี้ คุณจะได้สร้างส่วนขยาย Firebase สำหรับการเข้ารหัสตำแหน่งทางภูมิศาสตร์ เมื่อติดตั้งใช้งานแล้ว ส่วนขยายจะแปลงพิกัด X และ Y เป็น Geohash เพื่อตอบสนองต่อเหตุการณ์ Firestore หรือผ่านการเรียกใช้ฟังก์ชันที่เรียกใช้ได้ ซึ่งสามารถใช้แทนการติดตั้งใช้งานไลบรารี GeoFire ในแพลตฟอร์มเป้าหมายทั้งหมดเพื่อจัดเก็บข้อมูล ซึ่งช่วยประหยัดเวลาได้

ส่วนขยาย Geohash ที่แสดงในคอนโซล Firebase

สิ่งที่คุณจะได้เรียนรู้

  • วิธีนำโค้ด Cloud Functions ที่มีอยู่มาเปลี่ยนให้เป็น Firebase Extension ที่แจกจ่ายได้
  • วิธีตั้งค่าไฟล์ extension.yaml
  • วิธีจัดเก็บสตริงที่ละเอียดอ่อน (คีย์ API) ในส่วนขยาย
  • วิธีอนุญาตให้นักพัฒนาส่วนขยายกำหนดค่าให้เหมาะกับความต้องการ
  • วิธีทดสอบและติดตั้งใช้งานส่วนขยาย

สิ่งที่คุณต้องมี

  • Firebase CLI (ติดตั้งและเข้าสู่ระบบ)
  • บัญชี Google เช่น บัญชี Gmail
  • Node.js และ npm
  • สภาพแวดล้อมในการพัฒนาที่คุณชื่นชอบ

2. ตั้งค่า

รับรหัส

ทุกอย่างที่คุณต้องการสำหรับส่วนขยายนี้อยู่ในที่เก็บ GitHub หากต้องการเริ่มต้นใช้งาน ให้คัดลอกโค้ดแล้วเปิดในสภาพแวดล้อมในการพัฒนาที่คุณชื่นชอบ

  1. คลายไฟล์ ZIP ที่ดาวน์โหลด
  2. หากต้องการติดตั้งทรัพยากร Dependency ที่จำเป็น ให้เปิดเทอร์มินัลในไดเรกทอรี functions แล้วเรียกใช้คำสั่ง npm install

ตั้งค่า Firebase

Codelab นี้ขอแนะนำให้ใช้โปรแกรมจำลอง Firebase หากต้องการลองพัฒนาส่วนขยายด้วยโปรเจ็กต์ Firebase จริง โปรดดูสร้างโปรเจ็กต์ Firebase Codelab นี้ใช้ Cloud Functions ดังนั้นหากคุณใช้โปรเจ็กต์ Firebase จริงแทนโปรแกรมจำลอง คุณจะต้องอัปเกรดเป็นแพ็กเกจราคา Blaze

หากต้องการข้ามไปข้างหน้า

คุณสามารถดาวน์โหลดเวอร์ชันที่สมบูรณ์ของโค้ดแล็บได้ หากติดขัดระหว่างทางหรือต้องการดูว่าส่วนขยายที่เสร็จสมบูรณ์แล้วมีลักษณะอย่างไร ให้ดูcodelab-endสาขาของที่เก็บ GitHub หรือดาวน์โหลดไฟล์ ZIP ที่เสร็จสมบูรณ์แล้ว

3. ตรวจสอบโค้ด

  • เปิดไฟล์ index.ts จากไฟล์ ZIP โปรดทราบว่ามีประกาศ Cloud Functions 2 รายการอยู่ภายใน

ฟังก์ชันเหล่านี้ทำอะไร

ฟังก์ชันการสาธิตเหล่านี้ใช้สำหรับการแฮชทางภูมิศาสตร์ โดยจะใช้คู่พิกัดและเปลี่ยนเป็นรูปแบบที่เพิ่มประสิทธิภาพสำหรับการค้นหาทางภูมิศาสตร์ใน Firestore ฟังก์ชันจะจำลองการใช้การเรียก API เพื่อให้คุณทราบข้อมูลเพิ่มเติมเกี่ยวกับการจัดการประเภทข้อมูลที่ละเอียดอ่อนในส่วนขยาย ดูข้อมูลเพิ่มเติมได้ที่เอกสารประกอบเกี่ยวกับการเรียกใช้การค้นหาทางภูมิศาสตร์ในข้อมูลใน Firestore

ค่าคงที่ของฟังก์ชัน

ค่าคงที่จะประกาศตั้งแต่เนิ่นๆ ที่ด้านบนของไฟล์ index.ts ค่าคงที่บางรายการเหล่านี้อ้างอิงถึงทริกเกอร์ที่กำหนดของส่วนขยาย

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);

ทริกเกอร์ Firestore

ฟังก์ชันแรกในไฟล์ index.ts มีลักษณะดังนี้

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,
        }
      );
  });

ฟังก์ชันนี้คือทริกเกอร์ Firestore เมื่อเกิดเหตุการณ์การเขียนในฐานข้อมูล ฟังก์ชันจะตอบสนองต่อเหตุการณ์นั้นโดยการค้นหาฟิลด์ xv และฟิลด์ yv และหากมีทั้ง 2 ฟิลด์ ฟังก์ชันจะคำนวณ Geohash และเขียนเอาต์พุตไปยังตำแหน่งเอาต์พุตของเอกสารที่ระบุ เอกสารอินพุตกำหนดโดยค่าคงที่ users/{uid} ซึ่งหมายความว่าฟังก์ชันจะอ่านทุกเอกสารที่เขียนไปยังคอลเล็กชัน users/ แล้วประมวลผล Geohash สำหรับเอกสารเหล่านั้น จากนั้นจะแสดงผลแฮชไปยังช่องแฮชในเอกสารเดียวกัน

ฟังก์ชันที่เรียกใช้ได้

ฟังก์ชันถัดไปในไฟล์ index.ts มีลักษณะดังนี้

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};
});

สังเกตฟังก์ชัน onCall ซึ่งระบุว่าฟังก์ชันนี้เป็นฟังก์ชันที่เรียกใช้ได้ ซึ่งเรียกใช้ได้จากภายในโค้ดแอปพลิเคชันไคลเอ็นต์ ฟังก์ชันที่เรียกใช้ได้นี้ใช้พารามิเตอร์ x และ y และแสดงผล Geohash แม้ว่าฟังก์ชันนี้จะไม่ได้รับการเรียกใช้โดยตรงในโค้ดแล็บนี้ แต่เราได้รวมฟังก์ชันนี้ไว้ที่นี่เป็นตัวอย่างของสิ่งที่ต้องกำหนดค่าในส่วนขยาย Firebase

4. ตั้งค่าไฟล์ extension.yaml

เมื่อทราบแล้วว่าโค้ด Cloud Functions ในส่วนขยายทำอะไรได้บ้าง ตอนนี้คุณก็พร้อมที่จะแพ็กเกจเพื่อจัดจำหน่ายแล้ว ส่วนขยาย Firebase ทุกรายการมาพร้อมกับไฟล์ extension.yaml ที่อธิบายสิ่งที่ส่วนขยายทำและลักษณะการทำงานของส่วนขยาย

ไฟล์ extension.yaml ต้องมีข้อมูลเมตาเริ่มต้นเกี่ยวกับส่วนขยาย แต่ละขั้นตอนต่อไปนี้จะช่วยให้คุณเข้าใจความหมายของช่องทั้งหมดและเหตุผลที่ต้องใช้

  1. สร้างไฟล์ extension.yaml ในไดเรกทอรีรากของโปรเจ็กต์ที่คุณดาวน์โหลดไว้ก่อนหน้านี้ เริ่มต้นด้วยการเพิ่มข้อมูลต่อไปนี้
name: geohash-ext
version: 0.0.1
specVersion: v1beta  # Firebase Extensions specification version (do not edit)

ชื่อของส่วนขยายจะใช้เป็นฐานของรหัสอินสแตนซ์ของส่วนขยาย (ผู้ใช้สามารถติดตั้งส่วนขยายหลายอินสแตนซ์ โดยแต่ละอินสแตนซ์จะมีรหัสของตัวเอง) จากนั้น Firebase จะสร้างชื่อบัญชีบริการของส่วนขยายและทรัพยากรเฉพาะส่วนขยายโดยใช้รหัสอินสแตนซ์นั้น หมายเลขเวอร์ชันจะระบุเวอร์ชันของส่วนขยาย โดยต้องเป็นไปตามการกำหนดเวอร์ชันเชิงความหมาย และคุณต้องอัปเดตทุกครั้งที่เปลี่ยนแปลงฟังก์ชันการทำงานของส่วนขยาย ระบบจะใช้เวอร์ชันข้อกำหนดของส่วนขยายเพื่อกำหนดข้อกำหนดของส่วนขยาย Firebase ที่จะใช้ ในกรณีนี้ ระบบจะใช้ v1beta

  1. เพิ่มรายละเอียดที่ใช้งานง่ายลงในไฟล์ YAML ดังนี้
...

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

ชื่อที่แสดงคือชื่อส่วนขยายที่แสดงอย่างเป็นมิตรเมื่อนักพัฒนาแอปโต้ตอบกับส่วนขยายของคุณ คำอธิบายจะให้ภาพรวมคร่าวๆ เกี่ยวกับสิ่งที่ส่วนขยายทำ เมื่อมีการติดตั้งใช้งานส่วนขยายใน extensions.dev ส่วนขยายจะมีลักษณะดังนี้

ส่วนขยาย Geohash Converter ตามที่เห็นใน extensions.dev

  1. ระบุใบอนุญาตสำหรับโค้ดในส่วนขยาย
...

license: Apache-2.0  # The license you want for the extension
  1. ระบุผู้เขียนส่วนขยายและระบุว่าต้องมีการเรียกเก็บเงินเพื่อติดตั้งหรือไม่
...

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

billingRequired: true

ส่วนauthorใช้เพื่อแจ้งให้ผู้ใช้ทราบว่าควรติดต่อใครในกรณีที่พบปัญหาเกี่ยวกับส่วนขยายหรือต้องการข้อมูลเพิ่มเติมเกี่ยวกับส่วนขยาย billingRequired เป็นพารามิเตอร์ที่จำเป็นและต้องตั้งค่าเป็น true เนื่องจากส่วนขยายทั้งหมดใช้ Cloud Functions ซึ่งต้องใช้แพ็กเกจ Blaze

ซึ่งครอบคลุมจำนวนฟิลด์ขั้นต่ำที่ต้องระบุในไฟล์ extension.yaml เพื่อระบุส่วนขยายนี้ ดูรายละเอียดเพิ่มเติมเกี่ยวกับข้อมูลระบุตัวตนอื่นๆ ที่คุณระบุในส่วนขยายได้ที่เอกสารประกอบ

5. แปลงโค้ด Cloud Functions เป็นทรัพยากรส่วนขยาย

ทรัพยากรส่วนขยายคือรายการที่ Firebase สร้างในโปรเจ็กต์ระหว่างการติดตั้งส่วนขยาย จากนั้นส่วนขยายจะเป็นเจ้าของทรัพยากรเหล่านั้นและมีบัญชีบริการเฉพาะที่ดำเนินการกับทรัพยากรเหล่านั้น ในโปรเจ็กต์นี้ ทรัพยากรเหล่านั้นคือ Cloud Functions ซึ่งต้องกำหนดไว้ในไฟล์ extension.yaml เนื่องจากส่วนขยายจะไม่สร้างทรัพยากรจากโค้ดในโฟลเดอร์ฟังก์ชันโดยอัตโนมัติ หากไม่ได้ประกาศ Cloud Functions อย่างชัดเจนว่าเป็นทรัพยากร คุณจะติดตั้งใช้งานฟังก์ชันดังกล่าวไม่ได้เมื่อติดตั้งใช้งานส่วนขยาย

ตำแหน่งการติดตั้งใช้งานที่ผู้ใช้กำหนด

  1. อนุญาตให้ผู้ใช้ระบุตำแหน่งที่ต้องการติดตั้งใช้งานส่วนขยายนี้ และตัดสินใจว่าจะโฮสต์ส่วนขยายใกล้กับผู้ใช้ปลายทางหรือใกล้กับฐานข้อมูลดีกว่ากัน ในextension.yaml ให้ใส่ตัวเลือกในการเลือกตำแหน่ง

extension.yaml

ตอนนี้คุณพร้อมที่จะเขียนการกำหนดค่าสำหรับทรัพยากรฟังก์ชันแล้ว

  1. ในไฟล์ extension.yaml ให้สร้างออบเจ็กต์ทรัพยากรสำหรับฟังก์ชัน locationUpdate เพิ่มข้อความต่อไปนี้ลงในไฟล์ 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}

คุณกำหนด name เป็นชื่อฟังก์ชันที่กำหนดไว้ในไฟล์ index.ts ของโปรเจ็กต์ คุณระบุ type ของฟังก์ชันที่กำลังจะติดตั้งใช้งาน ซึ่งควรเป็น firebaseextensions.v1beta.function เสมอในตอนนี้ จากนั้นคุณจะกำหนด properties ของฟังก์ชันนี้ พร็อพเพอร์ตี้แรกที่คุณกำหนดคือ eventTrigger ที่เชื่อมโยงกับฟังก์ชันนี้ หากต้องการจำลองสิ่งที่ส่วนขยายรองรับในปัจจุบัน คุณต้องใช้ eventType ของ providers/cloud.firestore/eventTypes/document.write ซึ่งอยู่ในเอกสารประกอบเขียน Cloud Functions สำหรับส่วนขยาย คุณกำหนด resource เป็นตำแหน่งของเอกสาร เนื่องจากเป้าหมายปัจจุบันของคุณคือการจำลองสิ่งที่อยู่ในโค้ด เส้นทางเอกสารจึงรับฟัง users/{uid} โดยมีตำแหน่งฐานข้อมูลเริ่มต้นอยู่ก่อนหน้า

  1. ส่วนขยายต้องมีสิทธิ์อ่านและเขียนสำหรับฐานข้อมูล Firestore ที่ส่วนท้ายสุดของไฟล์ extension.yaml ให้ระบุบทบาท IAM ที่ส่วนขยายควรมีสิทธิ์เข้าถึงเพื่อทำงานกับฐานข้อมูลในโปรเจ็กต์ Firebase ของนักพัฒนาซอฟต์แวร์
roles:
  - role: datastore.user
    reason: Allows the extension to read / write to your Firestore instance.

datastore.userบทบาทมาจากรายการบทบาท IAM ที่รองรับสำหรับส่วนขยาย เนื่องจากส่วนขยายจะอ่านและเขียนได้ บทบาท datastore.user จึงเหมาะกับกรณีนี้

  1. และต้องเพิ่มฟังก์ชันที่เรียกใช้ได้ด้วย ในไฟล์ extension.yaml ให้สร้างทรัพยากรใหม่ภายใต้พร็อพเพอร์ตี้ทรัพยากร พร็อพเพอร์ตี้ต่อไปนี้มีไว้สำหรับฟังก์ชันที่เรียกใช้ได้โดยเฉพาะ
  - name: callableHash
    type: firebaseextensions.v1beta.function
    properties:
      httpsTrigger: {}

แม้ว่าทรัพยากรที่ผ่านมาจะใช้ eventTrigger แต่ในที่นี้คุณใช้ httpsTrigger ซึ่งครอบคลุมทั้งฟังก์ชันที่เรียกใช้ได้และฟังก์ชัน HTTPS

การตรวจสอบโค้ด

การกำหนดค่าจำนวนมากนี้จะช่วยให้ extension.yaml ตรงกับทุกอย่างที่โค้ดในไฟล์ index.ts ทำ ไฟล์ extension.yaml ที่เสร็จสมบูรณ์แล้วควรมีลักษณะดังนี้

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.

การตรวจสอบสถานะ

ตอนนี้คุณได้ตั้งค่าชิ้นส่วนการทำงานเริ่มต้นของส่วนขยายแล้ว จึงสามารถลองใช้โดยใช้โปรแกรมจำลอง Firebase ได้

  1. หากยังไม่ได้ดำเนินการ ให้เรียกใช้ npm run build ในโฟลเดอร์ฟังก์ชันของโปรเจ็กต์ส่วนขยายที่ดาวน์โหลด
  2. สร้างไดเรกทอรีใหม่ในระบบโฮสต์และเชื่อมต่อไดเรกทอรีนั้นกับโปรเจ็กต์ Firebase โดยใช้ 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. เรียกใช้ firebase ext:install จากไดเรกทอรีเดียวกัน แทนที่ /path/to/extension ด้วยเส้นทางสัมบูรณ์ไปยังไดเรกทอรีที่มีไฟล์ extension.yaml
firebase ext:install /path/to/extension
    This command does two things:
  • โดยจะแจ้งให้คุณระบุการกำหนดค่าสำหรับอินสแตนซ์ส่วนขยาย และสร้างไฟล์ *.env ที่มีข้อมูลการกำหนดค่าสำหรับอินสแตนซ์
  • ซึ่งจะเพิ่มอินสแตนซ์ของส่วนขยายไปยังส่วน extensions ของ firebase.json ซึ่งทำหน้าที่เป็นแผนที่ของรหัสอินสแตนซ์ไปยังเวอร์ชันส่วนขยาย
  • เนื่องจากคุณกำลังติดตั้งใช้งานโปรเจ็กต์ในเครื่อง คุณจึงระบุได้ว่าต้องการใช้ไฟล์ในเครื่องแทน Google Cloud Secret Manager

ภาพหน้าจอของกระบวนการติดตั้งส่วนขยายที่แสดงว่าระบบกำลังใช้ไฟล์ในเครื่องสำหรับข้อมูลลับเมื่อติดตั้งส่วนขยายนี้

  1. เริ่มโปรแกรมจำลอง Firebase ด้วยการกำหนดค่าใหม่
firebase emulators:start
  1. หลังจากเรียกใช้ emulators:start แล้ว ให้ไปที่แท็บ Firestore ใน WebView ของโปรแกรมจำลอง
  2. เพิ่มเอกสารลงในusersคอลเล็กชันที่มีฟิลด์xvหมายเลขและฟิลด์yvหมายเลข

กล่องโต้ตอบที่แสดงในโปรแกรมจำลอง Firebase เพื่อเริ่มคอลเล็กชันที่มีรหัสคอลเล็กชันซึ่งมีวลี

  1. หากติดตั้งส่วนขยายสำเร็จ ส่วนขยายจะสร้างฟิลด์ใหม่ชื่อ hash ในเอกสาร

คอลเล็กชันผู้ใช้ที่มีเอกสารผู้ใช้ซึ่งมีช่อง xv, yv และแฮช

ล้างข้อมูลเพื่อหลีกเลี่ยงความขัดแย้ง

  • เมื่อทดสอบเสร็จแล้ว ให้ถอนการติดตั้งส่วนขยาย เนื่องจากคุณจะอัปเดตโค้ดส่วนขยายและไม่ต้องการให้เกิดข้อขัดแย้งกับส่วนขยายปัจจุบันในภายหลัง

ส่วนขยายอนุญาตให้ติดตั้งส่วนขยายเดียวกันหลายเวอร์ชันพร้อมกันได้ ดังนั้นการถอนการติดตั้งจะช่วยให้มั่นใจได้ว่าจะไม่มีความขัดแย้งกับส่วนขยายที่ติดตั้งไว้ก่อนหน้านี้

firebase ext:uninstall geohash-ext

โซลูชันปัจจุบันใช้งานได้ แต่ดังที่กล่าวไว้ในช่วงต้นของโปรเจ็กต์ มีคีย์ API ที่ฮาร์ดโค้ดไว้เพื่อจำลองการสื่อสารกับบริการ คุณจะใช้คีย์ API ของผู้ใช้ปลายทางแทนคีย์ที่ให้มาในตอนแรกได้อย่างไร อ่านต่อเพื่อหาคำตอบได้เลย

6. ทำให้ผู้ใช้กำหนดค่าส่วนขยายได้

ในส่วนนี้ของโค้ดแล็บ คุณมีส่วนขยายที่กำหนดค่าไว้สำหรับใช้กับการตั้งค่าฟังก์ชันที่กำหนดไว้ล่วงหน้าซึ่งคุณเขียนไว้แล้ว แต่จะเกิดอะไรขึ้นหากผู้ใช้ต้องการใช้ละติจูดและลองจิจูดแทน y และ x สำหรับช่องที่ระบุตำแหน่งบนระนาบคาร์ทีเซียน นอกจากนี้ คุณจะขอให้ผู้ใช้ปลายทางระบุคีย์ API ของตนเองแทนที่จะให้ใช้คีย์ API ที่คุณจัดหาให้ได้อย่างไร คุณอาจใช้โควต้าสำหรับ API นั้นเกินอย่างรวดเร็ว ในกรณีนี้ คุณจะต้องตั้งค่าและใช้พารามิเตอร์

กำหนดพารามิเตอร์พื้นฐานในextension.yaml ไฟล์

เริ่มด้วยการแปลงรายการที่นักพัฒนาแอปอาจมีการกำหนดค่าที่กำหนดเอง พารามิเตอร์แรกคือพารามิเตอร์ XFIELD และ YFIELD

  1. ในไฟล์ extension.yaml ให้เพิ่มโค้ดต่อไปนี้ซึ่งใช้พารามิเตอร์ฟิลด์ XFIELD และ YFIELD พารามิเตอร์เหล่านี้อยู่ในพร็อพเพอร์ตี้ params YAML ที่กำหนดไว้ก่อนหน้านี้

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 ตั้งชื่อพารามิเตอร์ในลักษณะที่มองเห็นได้สำหรับคุณ ซึ่งเป็นผู้ผลิตส่วนขยาย ใช้ค่านี้ในภายหลังเมื่อระบุค่าพารามิเตอร์
  • label คือตัวระบุที่มนุษย์อ่านได้ซึ่งมีไว้สำหรับนักพัฒนาแอปเพื่อให้ทราบว่าพารามิเตอร์ทำอะไร
  • description ให้คำอธิบายโดยละเอียดเกี่ยวกับมูลค่า เนื่องจากรองรับมาร์กดาวน์ จึงสามารถลิงก์ไปยังเอกสารประกอบเพิ่มเติม หรือไฮไลต์คำที่อาจมีความสำคัญต่อนักพัฒนาแอปได้
  • type จะกำหนดกลไกการป้อนข้อมูลสำหรับวิธีที่ผู้ใช้จะตั้งค่าพารามิเตอร์ โดยมีหลายประเภท เช่น string, select, multiSelect, selectResource และ secret ดูข้อมูลเพิ่มเติมเกี่ยวกับตัวเลือกแต่ละรายการได้ที่เอกสารประกอบ
  • validationRegex จะจำกัดรายการของนักพัฒนาแอปให้เป็นค่า Regex ที่แน่นอน (ในตัวอย่างนี้อิงตามหลักเกณฑ์ชื่อฟิลด์อย่างง่ายที่นี่) และหากไม่สำเร็จ...
  • validationErrorMessage จะแจ้งให้นักพัฒนาแอปทราบค่าที่ล้มเหลว
  • default คือค่าที่จะเป็นหากนักพัฒนาแอปไม่ได้ป้อนข้อความใดๆ
  • required หมายความว่านักพัฒนาแอปไม่จำเป็นต้องป้อนข้อความใดๆ
  • เปลี่ยนแปลงไม่ได้ช่วยให้นักพัฒนาแอปอัปเดตส่วนขยายนี้และเปลี่ยนค่านี้ได้ ในกรณีนี้ นักพัฒนาแอปควรเปลี่ยนชื่อฟิลด์ได้เมื่อข้อกำหนดเปลี่ยนแปลง
  • ตัวอย่างจะช่วยให้คุณเห็นภาพว่าอินพุตที่ถูกต้องมีลักษณะอย่างไร

ข้อมูลเยอะมากเลยนะ

  1. คุณมีพารามิเตอร์อีก 3 รายการที่จะเพิ่มลงในไฟล์ extension.yaml ก่อนเพิ่มพารามิเตอร์พิเศษ
  - 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

กำหนดพารามิเตอร์ที่ละเอียดอ่อน

ตอนนี้คุณต้องจัดการคีย์ API ที่ผู้ใช้ระบุ นี่คือสตริงที่ละเอียดอ่อนซึ่งไม่ควรจัดเก็บเป็นข้อความธรรมดาในฟังก์ชัน ให้จัดเก็บค่านี้ไว้ใน Secret Manager ของ Cloud แทน ซึ่งเป็นตำแหน่งพิเศษในระบบคลาวด์ที่จัดเก็บข้อมูลลับที่เข้ารหัส และป้องกันไม่ให้ข้อมูลลับรั่วไหลโดยไม่ตั้งใจ ซึ่งกำหนดให้นักพัฒนาแอปต้องชำระเงินสำหรับการใช้บริการนี้ แต่จะช่วยเพิ่มความปลอดภัยอีกขั้นให้กับคีย์ API และอาจจำกัดกิจกรรมที่เป็นการฉ้อโกงได้ เอกสารประกอบสำหรับผู้ใช้จะแจ้งเตือนนักพัฒนาแอปว่าเป็นบริการแบบชำระเงิน เพื่อไม่ให้เกิดความประหลาดใจในการเรียกเก็บเงิน โดยรวมแล้ว การใช้งานจะคล้ายกับทรัพยากรสตริงอื่นๆ ที่กล่าวถึงข้างต้น ความแตกต่างเพียงอย่างเดียวคือประเภทที่เรียกว่า secret

  • เพิ่มโค้ดต่อไปนี้ในไฟล์ extension.yaml

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

อัปเดตresource แอตทริบิวต์เพื่อใช้พารามิเตอร์

ดังที่กล่าวไว้ก่อนหน้านี้ ทรัพยากร (ไม่ใช่ฟังก์ชัน) จะกำหนดวิธีสังเกตทรัพยากร ดังนั้นจึงต้องอัปเดตlocationUpdateทรัพยากรเพื่อใช้พารามิเตอร์ใหม่

  • เพิ่มโค้ดต่อไปนี้ในไฟล์ extension.yaml

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}

ตรวจสอบextension.yaml ไฟล์

  • ตรวจสอบไฟล์ extension.yaml ซึ่งควรมีหน้าตาเช่นนี้

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.

เข้าถึงพารามิเตอร์ในโค้ด

ตอนนี้เมื่อกำหนดค่าพารามิเตอร์ทั้งหมดในไฟล์ extension.yaml แล้ว ให้เพิ่มพารามิเตอร์เหล่านั้นลงในไฟล์ index.ts

  • ในไฟล์ index.ts ให้แทนที่ค่าเริ่มต้นด้วย process.env.PARAMETER_NAME ซึ่งจะดึงค่าพารามิเตอร์ที่เหมาะสมและป้อนค่าเหล่านั้นในโค้ดฟังก์ชันที่ติดตั้งใช้งานในโปรเจ็กต์ Firebase ของนักพัฒนาแอป

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!;

โดยปกติแล้ว คุณจะต้องตรวจสอบค่าตัวแปรสภาพแวดล้อมว่ามีค่าเป็น Null หรือไม่ แต่ในกรณีนี้ คุณเชื่อมั่นว่าค่าพารามิเตอร์จะได้รับการคัดลอกอย่างถูกต้อง ตอนนี้โค้ดได้รับการกำหนดค่าให้ทำงานกับพารามิเตอร์ส่วนขยายแล้ว

7. สร้างเอกสารประกอบสำหรับผู้ใช้

ก่อนทดสอบโค้ดในโปรแกรมจำลองหรือในตลาดส่วนขยาย Firebase คุณต้องจัดทำเอกสารประกอบของส่วนขยายเพื่อให้ผู้พัฒนาทราบว่าตนจะได้รับอะไรเมื่อใช้ส่วนขยาย

  1. เริ่มต้นด้วยการสร้างไฟล์ PREINSTALL.md ซึ่งใช้เพื่ออธิบายฟังก์ชันการทำงาน ข้อกำหนดเบื้องต้นสำหรับการติดตั้ง และผลกระทบที่อาจเกิดขึ้นในการเรียกเก็บเงิน

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. หากต้องการประหยัดเวลาในการเขียน README.md สำหรับโปรเจ็กต์นี้ ให้ใช้วิธีที่สะดวกดังนี้
firebase ext:info . --markdown > README.md

ซึ่งจะรวมเนื้อหาของไฟล์ PREINSTALL.md และรายละเอียดเพิ่มเติมเกี่ยวกับส่วนขยายจากไฟล์ extension.yaml

สุดท้าย ให้แจ้งรายละเอียดเพิ่มเติมเกี่ยวกับส่วนขยายที่เพิ่งติดตั้งแก่นักพัฒนาซอฟต์แวร์ของส่วนขยาย นักพัฒนาแอปอาจได้รับวิธีการและข้อมูลเพิ่มเติมหลังจากติดตั้งเสร็จสมบูรณ์ และอาจได้รับงานหลังการติดตั้งโดยละเอียด เช่น การตั้งค่าโค้ดฝั่งไคลเอ็นต์ที่นี่

  1. สร้างไฟล์ POSTINSTALL.md แล้วใส่ข้อมูลต่อไปนี้หลังการติดตั้ง

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);
  });

การตรวจสอบ

แนวทางปฏิบัติแนะนำคือคุณสามารถตรวจสอบกิจกรรมของส่วนขยายที่ติดตั้ง รวมถึงตรวจสอบประสิทธิภาพการทำงาน การใช้งาน และบันทึกได้

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

ซึ่งจะคอมไพล์ฟังก์ชันอีกครั้งเพื่อให้ซอร์สโค้ดล่าสุดพร้อมสำหรับการติดตั้งใช้งานควบคู่ไปกับส่วนขยายเมื่อติดตั้งใช้งานกับโปรแกรมจำลองหรือ Firebase โดยตรง

จากนั้นสร้างไดเรกทอรีใหม่เพื่อทดสอบส่วนขยาย เนื่องจากส่วนขยายได้รับการพัฒนาจากฟังก์ชันที่มีอยู่ จึงไม่ควรทดสอบจากโฟลเดอร์ที่กำหนดค่าส่วนขยายไว้ เนื่องจากจะพยายามติดตั้งใช้งานฟังก์ชันและกฎ Firebase พร้อมกันด้วย

ติดตั้งและทดสอบด้วยโปรแกรมจำลอง Firebase

  1. สร้างไดเรกทอรีใหม่ในระบบโฮสต์และเชื่อมต่อไดเรกทอรีนั้นกับโปรเจ็กต์ Firebase โดยใช้ firebase init
mkdir sample-proj
cd sample-proj
firebase init --project=projectID-or-alias
  1. จากไดเรกทอรีนั้น ให้เรียกใช้ firebase ext:install เพื่อติดตั้งส่วนขยาย แทนที่ /path/to/extension ด้วยเส้นทางสัมบูรณ์ไปยังไดเรกทอรีที่มีไฟล์ extension.yaml ซึ่งจะเริ่มกระบวนการติดตั้งส่วนขยายและสร้างไฟล์ .env ที่มีการกำหนดค่าของคุณก่อนที่จะพุชการกำหนดค่าไปยัง Firebase หรือโปรแกรมจำลอง
firebase ext:install /path/to/extension
  • เนื่องจากคุณกำลังติดตั้งใช้งานโปรเจ็กต์ในเครื่อง ให้ระบุว่าคุณต้องการใช้ไฟล์ในเครื่องแทน Google Cloud Secret Manager

da928c65ffa8ce15.png

  1. เริ่มชุดโปรแกรมจำลองภายในโดยทำดังนี้
firebase emulators:start

ติดตั้งและทดสอบด้วยโปรเจ็กต์ Firebase จริง

คุณติดตั้งส่วนขยายในโปรเจ็กต์ Firebase จริงได้ เราขอแนะนำให้ใช้โปรเจ็กต์ทดสอบสำหรับการทดสอบ ใช้เวิร์กโฟลว์การทดสอบนี้หากต้องการทดสอบโฟลว์ตั้งแต่ต้นจนจบของส่วนขยาย หรือหากชุดโปรแกรมจำลอง Firebase ยังไม่รองรับทริกเกอร์ของส่วนขยาย (ดูตัวเลือกโปรแกรมจำลองส่วนขยาย) ปัจจุบันโปรแกรมจำลองรองรับฟังก์ชันที่ทริกเกอร์โดยคำขอ HTTP และฟังก์ชันที่ทริกเกอร์โดยเหตุการณ์ในเบื้องหลังสำหรับ Cloud Firestore, Realtime Database และ Pub/Sub

  1. สร้างไดเรกทอรีใหม่ในระบบโฮสต์และเชื่อมต่อไดเรกทอรีนั้นกับโปรเจ็กต์ Firebase โดยใช้ firebase init
cd ..
mkdir sample-proj
cd sample-proj
firebase init --project=projectID-or-alias
  1. จากนั้นเรียกใช้ firebase ext:install จากไดเรกทอรีนั้นเพื่อติดตั้งส่วนขยาย แทนที่ /path/to/extension ด้วยเส้นทางสัมบูรณ์ไปยังไดเรกทอรีที่มีไฟล์ extension.yaml ซึ่งจะเริ่มกระบวนการติดตั้งส่วนขยายและสร้างไฟล์ .env ที่มีการกำหนดค่าของคุณก่อนที่จะพุชการกำหนดค่าไปยัง Firebase หรือโปรแกรมจำลอง
firebase ext:install /path/to/extension
  • เนื่องจากคุณต้องการติดตั้งใช้งานใน Firebase โดยตรงและต้องการใช้ Google Cloud Secret Manager คุณจึงต้องเปิดใช้งาน Secret Manager API ก่อนติดตั้งส่วนขยาย
  1. ทําการติดตั้งใช้งานในโปรเจ็กต์ Firebase
firebase deploy

ทดสอบส่วนขยาย

  1. หลังจากเรียกใช้ firebase deploy หรือ firebase emulators:start แล้ว ให้ไปที่แท็บ Firestore ของคอนโซล Firebase หรือ WebView ของโปรแกรมจำลองตามความเหมาะสม
  2. เพิ่มเอกสารลงในคอลเล็กชันที่ระบุโดยฟิลด์ x และฟิลด์ y ในกรณีนี้ เอกสารที่อัปเดตจะอยู่ที่ u/{uid} โดยมีฟิลด์ x เป็น xv และฟิลด์ y เป็น yv

หน้าจอ Firebase Emulators เพื่อเพิ่มระเบียน Firestore

  1. หากติดตั้งส่วนขยายสำเร็จ ส่วนขยายจะสร้างช่องใหม่ชื่อ hash ในเอกสารหลังจากที่คุณบันทึกช่องทั้ง 2 ช่อง

หน้าจอฐานข้อมูล Firestore จากโปรแกรมจำลองที่แสดงแฮชที่เพิ่ม

8. ยินดีด้วย

คุณแปลง Cloud Functions แรกเป็น Firebase Extension เรียบร้อยแล้ว

คุณได้เพิ่มไฟล์ extension.yaml และกำหนดค่าเพื่อให้ผู้พัฒนาเลือกวิธีที่ต้องการให้ติดตั้งใช้งานส่วนขยายของคุณได้ จากนั้นคุณได้สร้างเอกสารประกอบสำหรับผู้ใช้ซึ่งให้คำแนะนำเกี่ยวกับสิ่งที่นักพัฒนาส่วนขยายควรทำก่อนตั้งค่าส่วนขยาย และขั้นตอนที่อาจต้องดำเนินการหลังจากติดตั้งส่วนขยายเรียบร้อยแล้ว

ตอนนี้คุณทราบขั้นตอนสำคัญที่จำเป็นในการแปลงฟังก์ชันของ Firebase ให้เป็น Firebase Extension ที่แจกจ่ายได้แล้ว

ขั้นตอนถัดไป