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

1- قبل البدء

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

ما الذي ستنشئه

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

إضافة التجزئة الجغرافية التي تظهر في "وحدة تحكُّم Firebase"

المعلومات التي ستطّلع عليها

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

المتطلبات

2- البدء في الإعداد

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

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

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

إعداد Firebase

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

هل تريد التخطّي إلى العنصر التالي؟

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

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

  • افتح ملف index.ts من ملف ZIP. لاحِظ أنّها تحتوي على إعلانَي دالات في السحابة الإلكترونية داخلها.

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

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

الوظائف القابلة للاستدعاء

تظهر الدالة التالية في ملف 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 وتعرض تجزئة جغرافية. على الرغم من أنّه لن يتم استدعاء هذه الدالة مباشرةً في هذا الدرس التطبيقي حول الترميز، يتم تضمينها هنا كمثال على عنصر يجب ضبطه في إضافة Firebase.

4. إعداد ملف extension.yaml

والآن بعد أن عرفت ما الذي يؤديه رمز دوال Cloud في إضافتك، أصبحت جاهزًا لتجميعه للتوزيع. تأتي كل إضافة من إضافات 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، والتي تتطلّب خطة Blaze.

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

5- تحويل رمز دوال السحابة إلى مورد للإضافات

مورد الإضافة هو عنصر ينشئه Firebase في المشروع أثناء تثبيت إحدى الإضافات. تمتلك الإضافة بعد ذلك هذه الموارد ولديها حساب خدمة محدد يعمل عليها. في هذا المشروع، تكون هذه الموارد هي Cloud Functions، ويجب تحديدها في ملف extension.yaml لأنّ الإضافة لن تنشئ موارد تلقائيًا من الرمز البرمجي في مجلد الدوال. إذا لم يتم تعريف دوال السحابة الإلكترونية صراحةً كمورد، لا يمكن نشرها عند نشر الإضافة.

موقع النشر الذي حدّده المستخدم

  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، حدِّد أدوار "إدارة الهوية وإمكانية الوصول" التي يجب أن تتمكّن الإضافة من الوصول إليها من أجل العمل مع قاعدة البيانات في مشروع 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".

لقطة شاشة لعملية تثبيت الإضافة توضح أنّه يتم استخدام ملف "Local" (ملف محلي) لتنفيذ إجراءات سرّية عند تثبيت هذه الإضافة

  1. ابدأ تشغيل أدوات محاكاة Firebase بالتهيئة الجديدة:
firebase emulators:start
  1. بعد تشغيل emulators:start، انتقِل إلى علامة التبويب Firestore في webview الخاص بالمحاكيات.
  2. أضِف مستندًا إلى مجموعة users مع حقل رقم xv وحقل رقم yv.

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

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

مجموعة المستخدمين التي تتضمن مستند مستخدم يحتوي على حقل xv وyv وتجزئة.

إخلاء مساحة تخزين لتجنّب التعارضات

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

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

firebase ext:uninstall geohash-ext

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

6- جعل مستخدم الإضافة قابلاً للضبط

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

تحديد المَعلمات الأساسية في ملف extension.yaml

ابدأ بتحويل العناصر التي من المحتمل أن يكون لدى المطوِّرين إعدادات مخصّصة لها. ستكون العملية الأولى هي المعلمتَين XFIELD وYFIELD.

  1. في ملف extension.yaml، أضِف الرمز التالي الذي يستخدم مَعلمتَي XFIELD وYFIELD. تتوفّر هذه المَعلمات داخل سمة YAML المحدّدة سابقًا في params:

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. للحصول على مزيد من المعلومات حول كل خيار من هذه الخيارات، يمكنك الاطّلاع على المستندات.
  • تقيد الطريقة VerifyationRegex إدخال المطوّر على قيمة تعبير عادي معيّنة (تستند في المثال إلى إرشادات اسم الحقل البسيطة المتوفّرة هنا). وإذا فشل ذلك...
  • تُنبّه verificationationErrorMessage المطوّر إلى قيمة التعذُّر.
  • default هي القيمة التي ستظهر إذا لم يُدخِل المطور أي نص.
  • تعني الحالة مطلوب أنّ المطوّر غير مطالب بإدخال أي نص.
  • غير قابل للتغيير تسمح لمطور البرامج بتعديل هذه الإضافة وتغيير هذه القيمة. في هذه الحالة، يجب أن يتمكّن المطوّر من تغيير أسماء الحقول مع تغيُّر متطلباتها.
  • يوفر الحقل 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

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

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

da928c65ffa8ce15.png

  1. ابدأ تشغيل مجموعة المحاكي المحلي:
firebase emulators:start

التثبيت والاختبار باستخدام مشروع حقيقي في Firebase

يمكنك تثبيت الإضافة في مشروع فعلي على Firebase. يوصى باستخدام مشروع اختباري للاختبار. استخدِم سير عمل الاختبار هذا إذا كنت تريد اختبار التدفق الشامل للإضافة أو إذا لم يكن مشغّل الإضافة متوافقًا بعد مع مجموعة مُحاكي Firebase (اطّلِع على خيار محاكي الإضافات). تتيح أدوات المحاكاة حاليًا استخدام الدوال التي يتم تشغيلها عن طريق طلبات HTTP والدوال التي يتم تشغيلها في الخلفية لكل من Cloud Firestore و"قاعدة بيانات الوقت الفعلي" و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.

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

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

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

8- تهانينا

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

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

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

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