إعادة استخدام رمز وظائف السحابة الإلكترونية كإضافة في Firebase

1. قبل البدء

تنفّذ إحدى إضافات Firebase مهمة أو مجموعة مهام محدّدة استجابةً لطلبات HTTP أو أحداث التشغيل من منتجات Firebase وGoogle الأخرى، مثل "المراسلة عبر السحابة الإلكترونية من Firebase" أو Cloud Firestore أو Pub/Sub.

ما ستنشئه

في هذا الدرس التطبيقي حول الترميز، ستنشئ إضافة Firebase لـ الترميز الجغرافي. بعد نشر الإضافة، تحوّل الإضافة إحداثيات X وY إلى رموز جغرافية مجزّأة استجابةً لأحداث Firestore أو من خلال استدعاءات الدوال القابلة للاستدعاء. يمكن استخدام هذه الطريقة كبديل لتنفيذ مكتبة GeoFire على جميع المنصات المستهدَفة لتخزين البيانات، ما يوفّر لك الوقت.

إضافة Geohash المعروضة في وحدة تحكّم Firebase

ما ستتعلمه

  • كيفية أخذ رمز Cloud Functions الحالي وتحويله إلى إضافة Firebase قابلة للتوزيع
  • كيفية إعداد ملف extension.yaml
  • كيفية تخزين السلاسل الحسّاسة (مفاتيح واجهة برمجة التطبيقات) في إضافة
  • كيفية السماح لمطوّري الإضافة بضبطها لتناسب احتياجاتهم
  • كيفية اختبار الإضافة ونشرها

المتطلبات

  • Firebase CLI (التثبيت وتسجيل الدخول)
  • حساب Google، مثل حساب Gmail
  • ‫Node.js وnpm
  • بيئة التطوير المفضّلة لديك

2. بدء الإعداد

الحصول على الرمز

كل ما تحتاج إليه لهذه الإضافة متوفّر في مستودع GitHub. للبدء، احصل على الرمز وافتحه في بيئة التطوير المفضّلة لديك.

  1. فكّ ضغط ملف ZIP الذي تم تنزيله.
  2. لتثبيت العناصر التابعة المطلوبة، افتح الوحدة الطرفية في الدليل functions وشغِّل الأمر npm install.

إعداد Firebase

يشجّع هذا الدرس التطبيقي بشدة على استخدام محاكيات Firebase. إذا أردت تجربة تطوير الإضافات باستخدام مشروع حقيقي على Firebase، اطّلِع على إنشاء مشروع على Firebase. يستخدم هذا الدرس العملي Cloud Functions، لذا إذا كنت تستخدم مشروعًا حقيقيًا على Firebase بدلاً من المحاكيات، عليك الترقية إلى خطة أسعار Blaze.

هل تريد الانتقال إلى الأمام؟

يمكنك تنزيل نسخة مكتملة من الدرس العملي. إذا واجهتك أي مشاكل أثناء التنفيذ أو أردت الاطّلاع على شكل الإضافة بعد إكمالها، يمكنك مراجعة الفرع codelab-end من مستودع GitHub أو تنزيل ملف zip المكتمل.

3- مراجعة الرمز البرمجي

  • افتح ملف index.ts من ملف zip. لاحظ أنّها تحتوي على تعريفَين لوظائف Cloud Functions.

ما هي وظيفة هذه الدوال؟

تُستخدَم وظائف العرض التوضيحي هذه في التجزئة الجغرافية. تأخذ هذه الدوال زوجًا من الإحداثيات وتحوّله إلى تنسيق محسّن لطلبات البحث الجغرافية في Firestore. تحاكي الدوال استخدام طلب من واجهة برمجة التطبيقات حتى تتمكّن من معرفة المزيد عن كيفية التعامل مع أنواع البيانات الحساسة في الإضافات. لمزيد من المعلومات، اطّلِع على المستندات حول تنفيذ طلبات بحث جغرافية على البيانات في 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، وإذا كان كلا الحقلين موجودًا، يتم احتساب رمز 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 للإضافة. يمكنك تحديد resource كموقع للمستندات. بما أنّ هدفك الحالي هو عرض ما هو موجود في الرمز، يستمع مسار المستند إلى users/{uid}، مع تحديد الموقع الجغرافي التلقائي لقاعدة البيانات قبله.

  1. تحتاج الإضافة إلى أذونات القراءة والكتابة لقاعدة بيانات Firestore. في نهاية ملف extension.yaml، حدِّد أدوار "إدارة الهوية وإمكانية الوصول" التي يجب أن تتمكّن الإضافة من الوصول إليها للعمل مع قاعدة البيانات في مشروع Firebase الخاص بالمطوّر.
roles:
  - role: datastore.user
    reason: Allows the extension to read / write to your Firestore instance.

يأتي الدور datastore.user من قائمة أدوار إدارة الهوية وإمكانية الوصول المتوافقة مع الإضافات. بما أنّ الإضافة ستتمكّن من القراءة والكتابة، فإنّ الدور 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 في عرض الويب الخاص بالمحاكيات.
  2. أضِف مستندًا إلى المجموعة users مع حقل الرقم xv وحقل الرقم yv.

مربّع حوار يظهر في محاكيات Firebase لبدء عملية جمع البيانات باستخدام معرّف المجموعة الذي يحتوي على العبارة

  1. إذا تمكّنت من تثبيت الإضافة بنجاح، ستنشئ الإضافة حقلًا جديدًا باسم hash في المستند.

مجموعة المستخدمين التي تتضمّن مستند مستخدم يحتوي على الحقول xv وyv وhash

تنظيف البيانات لتجنُّب التعارضات

  • بعد الانتهاء من الاختبار، أزِل الإضافة لأنّك ستعدّل رمز الإضافة ولا تريد حدوث تعارض مع الإضافة الحالية لاحقًا.

تسمح الإضافات بتثبيت إصدارات متعددة من الإضافة نفسها في الوقت نفسه، لذا من خلال إلغاء التثبيت، يمكنك التأكّد من عدم حدوث أي تعارض مع إضافة تم تثبيتها سابقًا.

firebase ext:uninstall geohash-ext

الحل الحالي يعمل، ولكن كما ذكرنا في بداية المشروع، هناك مفتاح لواجهة برمجة التطبيقات مبرمَج مسبقًا لمحاكاة التواصل مع إحدى الخدمات. كيف يمكن استخدام مفتاح واجهة برمجة التطبيقات الخاص بالمستخدم النهائي بدلاً من المفتاح الذي تم توفيره في الأصل؟ تابِع القراءة لمعرفة المزيد.

6. إتاحة إمكانية ضبط الإضافة من قِبل المستخدم

في هذه المرحلة من الدرس العملي، لديك إضافة تم إعدادها لاستخدامها مع الإعدادات المحدّدة مسبقًا للدوال التي كتبتها، ولكن ماذا لو أراد المستخدم استخدام خطوط الطول والعرض بدلاً من y وx للحقول التي تشير إلى الموقع الجغرافي على مستوى ديكارتي؟ بالإضافة إلى ذلك، كيف يمكن للمستخدم النهائي تقديم مفتاح واجهة برمجة التطبيقات الخاص به بدلاً من السماح له باستخدام مفتاح واجهة برمجة التطبيقات المقدَّم؟ وقد تتجاوز الحصة المخصّصة لواجهة برمجة التطبيقات هذه بسرعة. في هذه الحالة، يمكنك إعداد المَعلمات واستخدامها.

تحديد المَعلمات الأساسية في 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 وصفًا تفصيليًا للقيمة. بما أنّ هذا الحقل يتيح استخدام لغة الترميز Markdown، يمكنه إنشاء روابط تؤدي إلى مستندات إضافية، أو يمكنه تمييز الكلمات التي قد تكون مهمة للمطوّر.
  • تحدّد السمة type آلية الإدخال التي سيستخدمها المستخدم لضبط قيمة المَعلمة. تتوفّر أنواع عديدة، بما في ذلك string وselect وmultiSelect وselectResource وsecret. لمزيد من المعلومات حول كل خيار من هذه الخيارات، يُرجى الاطّلاع على المستندات.
  • يفرض validationRegex على إدخال المطوّر قيمة تعبير عادي معيّن (في المثال، يستند إلى إرشادات اسم الحقل البسيط الموضّحة هنا)، وفي حال عدم استيفاء هذا الشرط...
  • تنبّه السمة validationErrorMessage المطوّر إلى قيمة الخطأ.
  • default هي القيمة التي ستظهر إذا لم يُدخل المطوّر أي نص.
  • تعني مطلوب أنّه ليس على المطوّر إدخال أي نص.
  • تسمح السمة immutable للمطوّر بتعديل هذه الإضافة وتغيير هذه القيمة. في هذه الحالة، يجب أن يتمكّن المطوّر من تغيير أسماء الحقول عند تغيُّر متطلباته.
  • تقدّم example فكرة عن الشكل الذي قد يبدو عليه الإدخال الصالح.

كانت هذه معلومات كثيرة يجب فهمها.

  1. يجب إضافة ثلاث معلَمات أخرى إلى ملف 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

تحديد المَعلمات الحسّاسة

عليك الآن إدارة مفتاح واجهة برمجة التطبيقات الذي يحدّده المستخدم. هذه سلسلة حساسة يجب عدم تخزينها كنص عادي في الدالة. بدلاً من ذلك، خزِّن هذه القيمة في Cloud Secret Manager. هذا موقع خاص في السحابة الإلكترونية يخزّن الأسرار المشفّرة ويمنع تسريبها عن طريق الخطأ. يتطلّب ذلك أن يدفع المطوّر مقابل استخدام هذه الخدمة، ولكنّها تضيف طبقة أمان إضافية على مفاتيح واجهة برمجة التطبيقات وقد تحدّ من الأنشطة الاحتيالية. تنبّه مستندات المستخدم المطوّر إلى أنّها خدمة مدفوعة، حتى لا تحدث أي مفاجآت في الفوترة. بشكل عام، يكون الاستخدام مشابهًا لموارد السلاسل الأخرى المذكورة أعلاه. الفرق الوحيد هو النوع الذي يُطلق عليه 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!;

عادةً، تريد إجراء عمليات التحقّق من القيم الفارغة باستخدام قيم متغيّرات البيئة، ولكن في هذه الحالة، تثق بأنّه تم نسخ قيم المَعلمات بشكل صحيح. تمّ الآن ضبط الرمز للعمل مع مَعلمات الإضافة.

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
  1. نشر الموقع الإلكتروني في مشروعك على Firebase
firebase deploy

تجربة الإضافة

  1. بعد تنفيذ firebase deploy أو firebase emulators:start، انتقِل إلى علامة التبويب Firestore في وحدة تحكّم Firebase أو في عرض الويب المضمّن للمحاكيات، حسب الاقتضاء.
  2. أضِف مستندًا إلى المجموعة المحدّدة من خلال الحقل x والحقل y. في هذه الحالة، تقع المستندات المعدَّلة في u/{uid} مع حقل x بقيمة xv وحقل y بقيمة yv.

شاشة &quot;محاكيات Firebase&quot; لإضافة سجلّ Firestore

  1. إذا نجحت في تثبيت الإضافة، ستنشئ الإضافة حقلًا جديدًا باسم hash في المستند بعد حفظ الحقلَين.

لقطة شاشة لقاعدة بيانات Firestore من محاكي تعرض إضافة التجزئة

8. تهانينا!

لقد نجحت في تحويل أول "دالة سحابية" إلى "إضافة Firebase".

لقد أضفت ملف extension.yaml وأعددته ليتمكّن المطوّرون من اختيار طريقة نشر الإضافة. بعد ذلك، أنشأت مستندات للمستخدمين تقدّم إرشادات حول ما يجب أن يفعله مطوّرو الإضافة قبل إعدادها والخطوات التي قد يحتاجون إلى اتّخاذها بعد تثبيت الإضافة بنجاح.

أصبحت الآن على دراية بالخطوات الأساسية المطلوبة لتحويل إحدى "وظائف Firebase" إلى "إضافة Firebase" قابلة للتوزيع.

الخطوات التالية