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

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

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

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

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

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

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

  • วิธีการนำโค้ด Cloud Functions ที่มีอยู่มาเปลี่ยนเป็นส่วนขยาย Firebase ที่กระจายได้
  • วิธีตั้งค่าไฟล์ 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 นี้ใช้ฟังก์ชันระบบคลาวด์ ดังนั้นหากคุณใช้โปรเจ็กต์ Firebase จริงแทนโปรแกรมจำลอง คุณต้องอัปเกรดเป็นแพ็กเกจราคา Blaze

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

คุณดาวน์โหลด Codelab เวอร์ชันที่สมบูรณ์ได้ หากยังพบปัญหาอยู่หรือต้องการดูลักษณะของส่วนขยายที่เสร็จสมบูรณ์แล้ว โปรดดูที่ codelab-end Branch ของที่เก็บ 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/ แล้วประมวลผลธรณีแปรสัณฐานสำหรับเอกสารเหล่านั้น จากนั้นระบบจะส่งแฮชไปยังช่องแฮชในเอกสารเดียวกัน

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

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

ดัชนี.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 และแสดงผลพิกัดทางภูมิศาสตร์ แม้ว่าจะไม่มีการเรียกฟังก์ชันนี้ใน Codelab โดยตรง แต่ก็รวมมาไว้ที่นี่เพื่อเป็นตัวอย่างของการกำหนดค่าในส่วนขยาย Firebase

4. ตั้งค่าไฟล์ extensions.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 ซึ่งอยู่ในเอกสารประกอบเขียนฟังก์ชันระบบคลาวด์สำหรับส่วนขยาย คุณกําหนด 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 วิธีนี้เป็นแมปรหัสอินสแตนซ์ไปยังเวอร์ชันส่วนขยาย
  • เนื่องจากคุณทำให้โปรเจ็กต์ใช้งานได้ภายในเครื่อง คุณจึงระบุได้ว่าต้องการใช้ไฟล์ในเครื่องแทนการใช้ Secret Manager ของ Google Cloud

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

  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 ของผู้ใช้ปลายทางแทนคีย์ API ที่ให้มาตั้งแต่แรกได้อย่างไร ถ้าอยากรู้ก็อ่านต่อเลย

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

ณ จุดนี้ของ Codelab คุณมีส่วนขยายที่กำหนดค่าเพื่อใช้กับการตั้งค่าตามความเห็นของฟังก์ชันที่เขียนไว้แล้ว แต่หากผู้ใช้ต้องการใช้ละติจูดและลองจิจูดแทน 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 โปรดดูข้อมูลเพิ่มเติมเกี่ยวกับแต่ละตัวเลือกเหล่านี้ในเอกสารประกอบ
  • verifyationRegex จำกัดรายการของนักพัฒนาซอฟต์แวร์ให้เป็นค่านิพจน์ทั่วไปที่กำหนด (ในตัวอย่างนี้เป็นไปตามหลักเกณฑ์ชื่อช่องง่ายๆ ที่ระบุไว้ที่นี่) และหากไม่ได้ผล...
  • checkationErrorMessage แจ้งเตือนนักพัฒนาซอฟต์แวร์เกี่ยวกับค่าความล้มเหลว
  • default คือค่าที่จะเป็นถ้านักพัฒนาแอปไม่ได้ป้อนข้อความใดๆ
  • ต้องระบุหมายความว่านักพัฒนาแอปไม่จำเป็นต้องป้อนข้อความใดๆ
  • เปลี่ยนแปลงไม่ได้ช่วยให้นักพัฒนาซอฟต์แวร์อัปเดตส่วนขยายนี้และเปลี่ยนค่านี้ได้ ในกรณีนี้ นักพัฒนาแอปควรเปลี่ยนชื่อช่องได้เมื่อมีการเปลี่ยนแปลงข้อกำหนด
  • example ช่วยให้เห็นภาพว่าข้อมูลที่ถูกต้องควรมีลักษณะอย่างไร

เราเข้าใจเป็นอย่างมาก

  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 ที่ผู้ใช้ระบุ นี่เป็นสตริงที่มีความละเอียดอ่อนซึ่งไม่ควรเก็บไว้ในข้อความธรรมดาในฟังก์ชัน แต่ให้เก็บค่านี้ในเครื่องมือจัดการข้อมูลลับของ 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
  • เนื่องจากคุณกำลังทำให้โปรเจ็กต์ใช้งานได้ภายในเครื่อง ให้ระบุว่าคุณต้องการใช้ไฟล์ในเครื่องแทนการใช้ Secret Manager ของ Google Cloud

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 โดยตรง และต้องการใช้ Secret Manager ของ Google Cloud คุณจึงต้องเปิดใช้งาน 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 สำหรับเพิ่มระเบียน Firestore

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

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

8. ยินดีด้วย

คุณแปลง Cloud Function แรกเป็นส่วนขยาย Firebase เรียบร้อยแล้ว

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

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

สิ่งที่ต้องทำต่อไป