البدء في إنشاء إضافة

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

1- إعداد بيئتك وتهيئة مشروع

قبل البدء في إنشاء إضافة، يجب إعداد بيئة إنشاء باستخدام الأدوات المطلوبة.

  1. ثبِّت الإصدار 16 من Node.js أو إصدار أحدث. ويمكنك تثبيت Node باستخدام nvm (أو nvm-Windows).

  2. ثبِّت أحدث إصدار من واجهة سطر الأوامر في Firebase أو حدِّثه. للتثبيت أو التحديث باستخدام npm، شغِّل الأمر التالي:

    npm install -g firebase-tools
    

استخدِم الآن واجهة سطر الأوامر في Firebase لإعداد مشروع إضافة جديد:

  1. أنشئ دليلاً للإضافة وcd فيه:

    mkdir rtdb-uppercase-messages && cd rtdb-uppercase-messages
    
  2. شغِّل الأمر ext:dev:init في واجهة سطر الأوامر في Firebase:

    firebase ext:dev:init
    

    اختَر JavaScript كلغة للدوال عندما يُطلب منك ذلك (ولكن لاحظ أنه يمكنك أيضًا استخدام TypeScript عند إعداد إضافتك الخاصة)، وأجب بـ "نعم" عندما يُطلب منك ذلك. (اقبل الإعدادات الافتراضية لأي خيارات أخرى.) سيؤدي هذا الأمر إلى إعداد قاعدة أكواد أساسية لإضافة جديدة، والتي يمكنك من خلالها البدء في تطوير الإضافة.

2- جرِّب نموذج الإضافة باستخدام المحاكي.

عندما بدأ واجهة سطر الأوامر في Firebase دليل الإضافات الجديد، أنشأ مثالاً بسيطًا للدالة ودليل integration-tests يحتوي على الملفات اللازمة لتشغيل إضافة باستخدام مجموعة محاكي Firebase.

يمكنك تجربة تشغيل الإضافة النموذجية في المحاكي:

  1. التغيير إلى الدليل integration-tests:

    cd functions/integration-tests
    
  2. بدء المحاكي من خلال مشروع تجريبي:

    firebase emulators:start --project=demo-test
    

    يُحمِّل المحاكي الإضافة في مشروع "وهمي" محدّد مسبقًا (demo-test). تتكوّن الإضافة حتى الآن من دالة واحدة يتم تشغيلها باستخدام بروتوكول HTTP، greetTheWorld، وتعرض هذه الإضافة رسالة "hello world" عند الوصول إليها.

  3. عندما لا يزال المحاكي قيد التشغيل، جرِّب وظيفة greetTheWorld الخاصة بالإضافة من خلال الانتقال إلى عنوان URL الذي طبعته عند بدء تشغيله.

    يعرض متصفحك الرسالة "مرحبًا بالعالم من تحية العالم".

  4. ويوجد رمز المصدر لهذه الدالة في دليل functions الخاص بالإضافة. افتح المصدر في المحرّر أو IDE الذي تختاره:

    Functions/index.js

    const functions = require("firebase-functions");
    
    exports.greetTheWorld = functions.https.onRequest((req, res) => {
      // Here we reference a user-provided parameter
      // (its value is provided by the user during installation)
      const consumerProvidedGreeting = process.env.GREETING;
    
      // And here we reference an auto-populated parameter
      // (its value is provided by Firebase after installation)
      const instanceId = process.env.EXT_INSTANCE_ID;
    
      const greeting = `${consumerProvidedGreeting} World from ${instanceId}`;
    
      res.send(greeting);
    });
    
  5. أثناء تشغيل المحاكي، ستتم تلقائيًا إعادة تحميل أي تغييرات تجريها على رمز الدوال. جرِّب إجراء تغيير بسيط على الدالة greetTheWorld:

    Functions/index.js

    const greeting = `${consumerProvidedGreeting} everyone, from ${instanceId}`;
    

    احفظ التغييرات. سيعيد المحاكي إعادة تحميل التعليمات البرمجية، والآن عندما تنتقل إلى عنوان URL للدالة، سترى رسالة الترحيب المحدّثة.

3- إضافة المعلومات الأساسية إلى extension.yaml

الآن بعد إعداد بيئة تطوير وتشغيل محاكي الإضافات، يمكنك البدء في كتابة إضافتك الخاصة.

كخطوة أولى متواضعة، يمكنك تعديل البيانات الوصفية للإضافة المحددة مسبقًا لتعكس الإضافة التي تريد كتابتها بدلاً من greet-the-world. يتم تخزين هذه البيانات الوصفية في ملف extension.yaml.

  1. افتح extension.yaml في المحرِّر واستبدِل محتوى الملف بالكامل بما يلي:

    name: rtdb-uppercase-messages
    version: 0.0.1
    specVersion: v1beta  # Firebase Extensions specification version; don't change
    
    # Friendly display name for your extension (~3-5 words)
    displayName: Convert messages to upper case
    
    # Brief description of the task your extension performs (~1 sentence)
    description: >-
      Converts messages in RTDB to upper case
    
    author:
      authorName: Your Name
      url: https://your-site.example.com
    
    license: Apache-2.0  # Required license
    
    # Public URL for the source code of your extension
    sourceUrl: https://github.com/your-name/your-repo
    

    لاحظ اصطلاح التسمية المستخدَم في الحقل name: يتمّ تسمية إضافات Firebase الرسمية ببادئة تشير إلى منتج Firebase الأساسي الذي تعمل عليه الإضافة، ويليها وصف لوظيفة الإضافة. يجب استخدام الاصطلاح نفسه في الإضافات.

  2. نظرًا لأنك غيّرت اسم الإضافة، يجب أيضًا تحديث تهيئة المحاكي بالاسم الجديد:

    1. بعد functions/integration-tests/firebase.json، غيِّر greet-the-world إلى rtdb-uppercase-messages
    2. إعادة تسمية "functions/integration-tests/extensions/greet-the-world.env" إلى functions/integration-tests/extensions/rtdb-uppercase-messages.env

لا تزال هناك بعض بقايا الإضافة greet-the-world في رمز الإضافة، ولكن اتركها في الوقت الحالي. ستقوم بتحديثها في الأقسام القليلة التالية.

4- كتابة دالة Cloud وتعريفها كمورد للإضافة

يمكنك الآن البدء في كتابة بعض التعليمات البرمجية. في هذه الخطوة، ستكتب دالة Cloud تؤدي المهمة الأساسية للإضافة، وهي مراقبة قاعدة البيانات في الوقت الفعلي بحثًا عن الرسائل وتحويلها إلى أحرف كبيرة.

  1. افتح مصدر دوال الإضافة (في الدليل functions الخاص بالإضافة) في المحرّر أو IDE الذي تختاره. استبدل محتوياته بما يلي:

    Functions/index.js

    import { database, logger } from "firebase-functions/v1";
    
    const app = initializeApp();
    
    // Listens for new messages added to /messages/{pushId}/original and creates an
    // uppercase version of the message to /messages/{pushId}/uppercase
    // for all databases in 'us-central1'
    export const makeuppercase = database
      .ref("/messages/{pushId}/uppercase")
      .onCreate(async (snapshot, context) => {
        // Grab the current value of what was written to the Realtime Database.
        const original = snapshot.val();
    
        // Convert it to upper case.
        logger.log("Uppercasing", context.params.pushId, original);
        const uppercase = original.toUpperCase();
    
        // Setting an "uppercase" sibling in the Realtime Database.
        const upperRef = snapshot.ref.parent.child("upper");
        await upperRef.set(uppercase);
    });
    

    وكانت الدالة القديمة، التي استبدلتها، دالة مشغَّلة عبر HTTP، وكانت تعمل عند الوصول إلى نقطة نهاية HTTP. يتم تشغيل الدالة الجديدة من خلال أحداث قاعدة البيانات في الوقت الفعلي: وهي تراقب العناصر الجديدة في مسار معين، وعندما يتم اكتشاف أحدها، فإنها تكتب النسخة الكبيرة من القيمة إلى قاعدة البيانات.

    بالمناسبة، يستخدم هذا الملف الجديد بنية وحدة ECMAScript (import وexport) بدلاً من CommonJS (require). لاستخدام وحدات ES في Node، حدِّد "type": "module" في functions/package.json:

    {
      "name": "rtdb-uppercase-messages",
      "main": "index.js",
      "type": "module",
      …
    }
    
  2. يجب تعريف كل دالة في إضافتك في ملف extension.yaml. أعلنت الإضافة المثالية عن greetTheWorld على أنّها دالة Cloud الوحيدة للإضافة، والآن بعد أن استبدلتها بـ makeuppercase، عليك أيضًا تعديل تعريفها.

    افتح extension.yaml وأضِف حقل "resources":

    resources:
      - name: makeuppercase
        type: firebaseextensions.v1beta.function
        properties:
          eventTrigger:
            eventType: providers/google.firebase.database/eventTypes/ref.create
            # DATABASE_INSTANCE (project's default instance) is an auto-populated
            # parameter value. You can also specify an instance.
            resource: projects/_/instances/${DATABASE_INSTANCE}/refs/messages/{pushId}/original
          runtime: "nodejs18"
    
  3. بما أنّ إضافتك تستخدم الآن Realtime Database كعامل تشغيل، عليك تعديل إعدادات المحاكي لتشغيل محاكي RTDB إلى جانب محاكي Cloud Functions:

    1. إذا كان المحاكي لا يزال قيد التشغيل، فأوقفه بالضغط على Ctrl-C.

    2. من الدليل functions/integration-tests، شغِّل الأمر التالي:

      firebase init emulators
      

      عند سؤالك، تخطي إعداد مشروع افتراضي، ثم تحديد الدوال ومحاكيات قواعد البيانات. اقبل المنافذ الافتراضية واسمح لأداة الإعداد بتنزيل أي ملفات مطلوبة.

    3. أعِد تشغيل المحاكي:

      firebase emulators:start --project=demo-test
      
  4. جرّب الإضافة المحدّثة:

    1. افتح واجهة مستخدم محاكي قاعدة البيانات باستخدام الرابط الذي تمت طباعته عند بدء المحاكي.

    2. تحرير العقدة الجذر لقاعدة البيانات:

      • الحقل: messages
      • النوع: json
      • القيمة: {"11": {"original": "recipe"}}

      في حال إعداد كل شيء بشكل صحيح، عند حفظ التغييرات في قاعدة البيانات، من المفترض أن يتم تشغيل وظيفة makeuppercase الخاصة بالإضافة وإضافة سجلّ ثانوي إلى الرسالة 11 التي تتضمّن المحتوى "upper": "RECIPE". يمكنك إلقاء نظرة على السجلات وعلامات تبويب قاعدة البيانات في واجهة مستخدم المحاكي للتأكد من النتائج المتوقعة.

    3. جرِّب إضافة المزيد من العناصر الثانوية إلى العقدة messages ({"original":"any text"}). وكلما أضفت سجلاً جديدًا، يجب أن تضيف الإضافة حقل uppercase يحتوي على محتويات الحقل original التي تتضمن الأحرف الكبيرة.

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

5- تعريف واجهات برمجة التطبيقات والأدوار

يمنح Firebase كل مثيل من إضافة مثبَّتة إمكانية وصول محدود إلى المشروع وبياناته باستخدام حساب خدمة لكل مثيل. يمتلك كل حساب الحد الأدنى من مجموعة الأذونات المطلوبة للتشغيل. ولهذا السبب، يجب الإفصاح صراحةً عن أي أدوار لإدارة الهوية وإمكانية الوصول (IAM) تتطلّبها إضافتك. وعندما يثبّت المستخدمون إضافتك، ينشئ Firebase حساب خدمة تم منحه هذه الأدوار ويستخدمه لتشغيل الإضافة.

لا تحتاج إلى الإفصاح عن الأدوار التي تؤدي إلى بدء أحداث منتج، ولكن يجب توضيح دور يسمح لك بالتفاعل معه بطريقة أخرى. نظرًا لأن الدالة التي أضفتها في الخطوة الأخيرة تكتب في قاعدة بيانات الوقت الفعلي، فإنك بحاجة إلى إضافة التعريف التالي إلى extension.yaml:

roles:
  - role: firebasedatabase.admin
    reason: Allows the extension to write to RTDB.

كذلك، يعني ذلك أنّك تعلن عن واجهات Google APIs التي تستخدمها إحدى الإضافات في الحقل apis. عندما يثبّت المستخدمون الإضافة، سيتم سؤالهم عما إذا كانوا يريدون تفعيل واجهات برمجة التطبيقات هذه تلقائيًا لمشروعهم أم لا. يكون هذا الإجراء ضروريًا عادةً فقط مع واجهات برمجة تطبيقات Google التي لا تستخدم Firebase، ولا يكون ضروريًا في هذا الدليل.

6- تحديد المعلمات القابلة للتهيئة بواسطة المستخدم

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

اجعل المسار الذي تراقبه الإضافة قابلاً للضبط حسب المستخدم للرسائل الجديدة:

  1. في ملف extension.yaml، أضِف قسم params:

    - param: MESSAGE_PATH
      label: Message path
      description: >-
        What is the path at which the original text of a message can be found?
      type: string
      default: /messages/{pushId}/original
      required: true
      immutable: false
    

    يحدِّد ذلك مَعلمة سلسلة جديدة سيُطلب من المستخدمين ضبطها عند تثبيت الإضافة.

  2. في ملف extension.yaml، يمكنك الرجوع إلى بيان makeuppercase وتغيير حقل resource إلى ما يلي:

    resource: projects/_/instances/${DATABASE_INSTANCE}/refs/${param:MESSAGE_PATH}
    

    الرمز المميز ${param:MESSAGE_PATH} هو مرجع للمعلمة التي حددتها للتو. عند تشغيل إضافتك، سيتم استبدال هذا الرمز المميز بأي قيمة ضبطها المستخدم لتلك المَعلمة، وتكون النتيجة التي ستستمع بها الدالة makeuppercase إلى المسار الذي حدّده المستخدم. ويمكنك استخدام هذه البنية للإشارة إلى أي مَعلمة يحدِّدها المستخدِم في أي مكان في extension.yaml (وفي POSTINSTALL.md، وسنتحدّث عن المزيد من التفاصيل لاحقًا).

  3. يمكنك أيضًا الوصول إلى المعلمات التي يحددها المستخدم من رمز الدوال.

    في الدالة التي كتبتها في القسم الأخير، قمت بترميز المسار بشكل ثابت لمشاهدة التغييرات. غيِّر تعريف المشغّل للإشارة إلى القيمة التي يحددها المستخدم بدلاً من ذلك:

    Functions/index.js

    export const makeuppercase = database.ref(process.env.MESSAGE_PATH).onCreate
    

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

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

    Functions/index.js

    export const makeuppercase = database.ref(process.env.MESSAGE_PATH).onCreate(
      async (snapshot, context) => {
        logger.log("Found new message at ", snapshot.ref);
    
        // Grab the current value of what was written to the Realtime Database.
        ...
    
  5. عادةً ما يُطلب من المستخدمين تقديم قيم للمعلمات عند تثبيت إضافة. ومع ذلك، عند استخدام المحاكي للاختبار والتطوير، يمكنك تخطي عملية التثبيت، ومن ثم يمكنك بدلاً من ذلك تقديم قيم للمعلمات التي يحددها المستخدم باستخدام ملف env.

    افتح functions/integration-tests/extensions/rtdb-uppercase-messages.env واستبدِل تعريف GREETING بما يلي:

    MESSAGE_PATH=/msgs/{pushId}/original
    

    لاحظ أن المسار أعلاه يختلف عن المسار الافتراضي والمسار الذي حددته سابقًا؛ وهذا فقط لتثبت لنفسك عند تجربة الإضافة المحدثة أن تعريفك ساري المفعول.

  6. والآن، أعد تشغيل المحاكي وانتقل مرة أخرى إلى واجهة المستخدم لمحاكي قاعدة البيانات.

    تحرير العقدة الجذر لقاعدة البيانات، باستخدام المسار الذي حددته أعلاه:

    • الحقل: msgs
    • النوع: json
    • القيمة: {"11": {"original": "recipe"}}

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

7- توفير عناصر الجذب للأحداث حسب المنطق الذي يحدّده المستخدم

لقد رأيت، بصفتك مؤلف إضافة، كيف يمكن لمنتج Firebase تفعيل المنطق الذي توفّره الإضافة: يؤدي إنشاء سجلات جديدة في قاعدة البيانات في الوقت الفعلي إلى تشغيل دالة makeuppercase. يمكن أن تكون للإضافة علاقة مشابهة مع المستخدمين الذين يثبِّتون الإضافة: يمكن للإضافة أن تؤدي إلى تشغيل منطق يُحدِّده المستخدم.

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

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

اتّبِع الخطوات التالية لإضافة عنصر جذب غير متزامن:

  1. في ملف extension.yaml، أضِف القسم التالي الذي يشير إلى نوع الحدث الوحيد الذي تُصدره الإضافة:

    events:
      - type: test-publisher.rtdb-uppercase-messages.v1.complete
        description: >-
          Occurs when message uppercasing completes. The event subject will contain
          the RTDB URL of the uppercase message.
    

    يجب أن تكون أنواع الأحداث فريدة عالميًا، لذلك ننصحك دائمًا بتسمية الأحداث باستخدام التنسيق التالي: <publisher-id>.<extension-id>.<version>.<description>. (ليس لديك الرقم التعريفي للناشر حتى الآن، لذا ما عليك سوى استخدام test-publisher في الوقت الحالي).

  2. في نهاية الدالة makeuppercase، أضِف بعض الرموز التي تنشر حدثًا من النوع الذي قدّمته للتو:

    Functions/index.js

    // Import the Eventarc library:
    import { initializeApp } from "firebase-admin/app";
    import { getEventarc } from "firebase-admin/eventarc";
    
    const app = initializeApp();
    
    // In makeuppercase, after upperRef.set(uppercase), add:
    
    // Set eventChannel to a newly-initialized channel, or `undefined` if events
    // aren't enabled.
    const eventChannel =
      process.env.EVENTARC_CHANNEL &&
      getEventarc().channel(process.env.EVENTARC_CHANNEL, {
        allowedEventTypes: process.env.EXT_SELECTED_EVENTS,
      });
    
    // If events are enabled, publish a `complete` event to the configured
    // channel.
    eventChannel &&
      eventChannel.publish({
        type: "test-publisher.rtdb-uppercase-messages.v1.complete",
        subject: upperRef.toString(),
        data: {
          "original": original,
          "uppercase": uppercase,
        },
      });
    

    يستفيد نموذج الرمز هذا من حقيقة أنّ متغيّر بيئة EVENTARC_CHANNEL لا يتم تحديده إلّا عندما يفعّل المستخدم نوعًا واحدًا على الأقل من الأحداث. وإذا لم يتم تحديد EVENTARC_CHANNEL، لن يحاول الرمز نشر أي أحداث.

    يمكنك إرفاق معلومات إضافية بحدث Eventarc. في المثال أعلاه، يشتمل الحدث على حقل subject يحتوي على مرجع إلى القيمة التي تم إنشاؤها حديثًا، وحمولة data تحتوي على الرسائل الأصلية والكبيرة. يمكن للدوال التي يحددها المستخدم والتي تؤدي إلى إيقاف الحدث الاستفادة من هذه المعلومات.

  3. عادةً ما يتم تحديد متغيّرات البيئة EVENTARC_CHANNEL وEXT_SELECTED_EVENTS استنادًا إلى الخيارات التي اختارها المستخدم أثناء التثبيت. للاختبار باستخدام المحاكي، حدِّد هذه المتغيّرات يدويًا في ملف rtdb-uppercase-messages.env:

    EVENTARC_CHANNEL=locations/us-central1/channels/firebase
    EXT_SELECTED_EVENTS=test-publisher.rtdb-uppercase-messages.v1.complete
    

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

لتجربة هذه الميزة الجديدة التي انتهيت للتو من تنفيذها، يمكنك في الخطوات القليلة التالية أن يتولى دور المستخدم الذي يثبّت الإضافة:

  1. من دليل functions/integration-tests، يمكنك إعداد مشروع Firebase جديد:

    firebase init functions
    

    ارفض إعداد مشروع تلقائي عندما يُطلب منك ذلك، واختَر JavaScript كلغة لوظائف Cloud، ثم ثبِّت الاعتماديات المطلوبة. يمثّل هذا المشروع مشروع مستخدم، تم تثبيت إضافتك عليه.

  2. عدِّل integration-tests/functions/index.js والصق الرمز التالي:

    import { logger } from "firebase-functions/v1";
    import { onCustomEventPublished } from "firebase-functions/v2/eventarc";
    
    import { initializeApp } from "firebase-admin/app";
    import { getDatabase } from "firebase-admin/database";
    
    const app = initializeApp();
    
    export const extraemphasis = onCustomEventPublished(
      "test-publisher.rtdb-uppercase-messages.v1.complete",
      async (event) => {
        logger.info("Received makeuppercase completed event", event);
    
        const refUrl = event.subject;
        const ref = getDatabase().refFromURL(refUrl);
        const upper = (await ref.get()).val();
        return ref.set(`${upper}!!!`);
      }
    );
    

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

  3. أعِد تشغيل المحاكي. سيحمّل المحاكي دوال الإضافة بالإضافة إلى وظيفة ما بعد المعالجة التي حدّدها "المستخدم".

  4. انتقل إلى واجهة مستخدم محاكي قاعدة البيانات وعدّل العقدة الجذر لقاعدة البيانات، باستخدام المسار الذي حددته أعلاه:

    • الحقل:msgs
    • النوع: json
    • القيمة: {"11": {"original": "recipe"}}

    عند حفظ التغييرات في قاعدة البيانات، من المفترض أن يتم تشغيل دالة makeuppercase للإضافة ودالة extraemphasis للمستخدم بالتسلسل، ما يؤدي إلى ظهور القيمة RECIPE!!! في الحقل upper.

8- إضافة معالِجات أحداث مراحل النشاط

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

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

لإضافة معالج أحداث في مراحل النشاط يؤدي إلى إضافة البيانات السابقة للرسائل الحالية، يمكنك اتّباع الخطوات التالية:

  1. تحديد دالة Cloud جديدة يتم تشغيلها من خلال أحداث قائمة انتظار المهام:

    Functions/index.js

    import { tasks } from "firebase-functions/v1";
    
    import { getDatabase } from "firebase-admin/database";
    import { getExtensions } from "firebase-admin/extensions";
    import { getFunctions } from "firebase-admin/functions";
    
    export const backfilldata = tasks.taskQueue().onDispatch(async () => {
      const batch = await getDatabase()
        .ref(process.env.MESSAGE_PATH)
        .parent.parent.orderByChild("upper")
        .limitToFirst(20)
        .get();
    
      const promises = [];
      for (const key in batch.val()) {
        const msg = batch.child(key);
        if (msg.hasChild("original") && !msg.hasChild("upper")) {
          const upper = msg.child("original").val().toUpperCase();
          promises.push(msg.child("upper").ref.set(upper));
        }
      }
      await Promise.all(promises);
    
      if (promises.length > 0) {
        const queue = getFunctions().taskQueue(
          "backfilldata",
          process.env.EXT_INSTANCE_ID
        );
        return queue.enqueue({});
      } else {
        return getExtensions()
          .runtime()
          .setProcessingState("PROCESSING_COMPLETE", "Backfill complete.");
      }
    });
    

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

  2. في ملف extension.yaml، أفصح عن دالة إعادة التعبئة على أنّها مورد إضافة يتضمّن السمة taskQueueTrigger:

    resources:
      - name: makeuppercase
        ...
      - name: backfilldata
        type: firebaseextensions.v1beta.function
        description: >-
          Backfill existing messages with uppercase versions
        properties:
          runtime: "nodejs18"
          taskQueueTrigger: {}
    

    بعد ذلك، يمكنك تعريف الدالة باعتبارها المعالج لحدث onInstall في مراحل نشاطه:

    lifecycleEvents:
      onInstall:
        function: backfilldata
        processingMessage: Uppercasing existing messages
    
  3. رغم أنه من الجيد الحصول على إعادة تعبئة الرسائل الحالية، يمكن أن تظل الإضافة تعمل بدونها. في مثل هذه المواقف، يجب أن تجعل تشغيل معالِجات أحداث دورة الحياة اختياريًا.

    لإجراء ذلك، أضِف مَعلمة جديدة إلى extension.yaml:

    - param: DO_BACKFILL
      label: Backfill existing messages
      description: >-
        Generate uppercase versions of existing messages?
      type: select
      required: true
      options:
        - label: Yes
          value: true
        - label: No
          value: false
    

    بعد ذلك، في بداية دالة إعادة التعبئة، تحقق من قيمة المَعلمة DO_BACKFILL، واخرج مبكرًا إذا لم يتم ضبطها:

    Functions/index.js

    if (!process.env.DO_BACKFILL) {
      return getExtensions()
        .runtime()
        .setProcessingState("PROCESSING_COMPLETE", "Backfill skipped.");
    }
    

من خلال التغييرات الواردة أعلاه، ستحوِّل الإضافة الآن الرسائل الحالية إلى أحرف كبيرة عند تثبيتها.

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

9- النشر في مشروع Firebase حقيقي

على الرغم من أن محاكي الإضافات يُعد أداة رائعة للتكرار بسرعة على إحدى الإضافات أثناء التطوير، سترغب في وقت ما في تجربتها ضمن مشروع حقيقي.

لإجراء ذلك، عليك أولاً إعداد مشروع جديد مع تفعيل بعض الخدمات:

  1. أضِف مشروعًا جديدًا في وحدة تحكُّم Firebase.
  2. احرص على ترقية مشروعك إلى خطة Blaze بنظام الدفع حسب الاستخدام. تتطلّب وظائف السحابة الإلكترونية لبرنامج Firebase أن يتوفّر في مشروعك حساب فوترة، لذلك تحتاج أيضًا إلى حساب فوترة لتثبيت إضافة.
  3. في مشروعك الجديد، فعِّل قاعدة بيانات الوقت الفعلي.
  4. إذا أردت اختبار قدرة إضافتك على إضافة البيانات السابقة عند التثبيت، يمكنك استيراد بعض نماذج البيانات إلى مثيل قاعدة البيانات في الوقت الفعلي:
    1. نزِّل بعض بيانات RTDB الأولية.
    2. في صفحة "قاعدة بيانات الوقت الفعلي" ضمن "وحدة تحكُّم Firebase"، انقر على (المزيد) > استيراد JSON واختَر الملف الذي نزَّلته للتو.
  5. لتفعيل دالة إعادة التعبئة من استخدام طريقة orderByChild، اضبط قاعدة البيانات لفهرسة الرسائل استنادًا إلى القيمة upper:

    {
      "rules": {
        ".read": false,
        ".write": false,
        "messages": {
          ".indexOn": "upper"
        }
      }
    }
    

تثبيت إضافتك الآن من مصدر محلي في المشروع الجديد:

  1. إنشاء دليل جديد لمشروع Firebase:

    mkdir ~/extensions-live-test && cd ~/extensions-live-test
    
  2. إعداد مشروع Firebase في دليل العمل:

    firebase init database
    

    اختَر المشروع الذي أنشأته للتو عندما يُطلب منك ذلك.

  3. ثبِّت الإضافة في مشروع Firebase المحلي:

    firebase ext:install /path/to/rtdb-uppercase-messages
    

    يمكنك هنا معرفة تجربة المستخدم عند تثبيت إحدى الإضافات باستخدام أداة Firebase CLI. تأكد من تحديد "نعم" عندما تسأل أداة التهيئة ما إذا كنت تريد إضافة بيانات سابقة إلى قاعدة البيانات الحالية أم لا.

    بعد تحديد خيارات الإعداد، سيحفظ واجهة سطر الأوامر في Firebase الإعدادات في دليل extensions ويسجّل موقع مصدر الإضافة في ملف firebase.json. يُطلَق على هذان السجلّان معًا اسم بيان الإضافات. ويمكن للمستخدمين استخدام البيان لحفظ إعداد الإضافات ونشره في مشاريع مختلفة.

  4. فعِّل إعدادات الإضافة في مشروعك المنشور:

    firebase deploy --only extensions
    

وإذا سارت الأمور على ما يرام، من المفترض أن يُحمِّل واجهة سطر الأوامر في Firebase إضافتك إلى مشروعك ويثبِّتها. بعد اكتمال التثبيت، سيتم تشغيل مهمة إعادة التعبئة، وفي غضون بضع دقائق، سيتم تحديث قاعدة البيانات بالرسائل التي تحتوي على أحرف كبيرة. أضف بعض العُقد الجديدة إلى قاعدة بيانات الرسائل وتأكد من أن الإضافة تعمل أيضًا مع الرسائل الجديدة.

10- كتابة المستندات

قبل مشاركة إضافتك مع المستخدمين، تأكد من تقديم وثائق كافية لهم ليكونوا ناجحين.

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

item.yaml

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

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

PREINSTALL.md

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

يتم عرض نص هذا الملف للمستخدم في "مركز الإضافات" ومن خلال الأمر firebase ext:info.

في ما يلي مثال على ملف PREINSTALL:

Use this extension to automatically convert strings to upper case when added to
a specified Realtime Database path.

This extension expects a database layout like the following example:

    "messages": {
      MESSAGE_ID: {
        "original": MESSAGE_TEXT
      },
      MESSAGE_ID: {
        "original": MESSAGE_TEXT
      },
    }

When you create new string records, this extension creates a new sibling record
with upper-cased text:

    MESSAGE_ID: {
      "original": MESSAGE_TEXT,
      "upper": UPPERCASE_MESSAGE_TEXT,
    }

#### Additional setup

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

#### 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:
  - Realtime Database
  - Cloud Functions (Node.js 10+ runtime)
    [See FAQs](https://firebase.google.com/support/faq#extensions-pricing)
- If you enable events,
  [Eventarc fees apply](https://cloud.google.com/eventarc/pricing).

POSTINSTALL.md

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

ويتم عرض محتوى POSTINSTALL.md في وحدة تحكم Firebase بعد إعداد الإضافة وتثبيتها. يمكنك الإشارة إلى معلمات المستخدم في هذا الملف وسيتم استبدالها بالقيم المهيأة.

إليك مثال على ملف ما بعد التثبيت للإضافة التعليمية:

### See it in action

You can test out this extension right away!

1.  Go to your
    [Realtime Database dashboard](https://console.firebase.google.com/project/${param:PROJECT_ID}/database/${param:PROJECT_ID}/data) in the Firebase console.

1.  Add a message string to a path that matches the pattern `${param:MESSAGE_PATH}`.

1.  In a few seconds, you'll see a sibling node named `upper` that contains the
    message in upper case.

### Using the extension

We recommend adding data by pushing -- for example,
`firebase.database().ref().push()` -- because pushing assigns an automatically
generated ID to the node in the database. During retrieval, these nodes are
guaranteed to be ordered by the time they were added. Learn more about reading
and writing data for your platform (iOS, Android, or Web) in the
[Realtime Database documentation](https://firebase.google.com/docs/database/).

### Monitoring

As a best practice, you can
[monitor the activity](https://firebase.google.com/docs/extensions/manage-installed-extensions#monitor)
of your installed extension, including checks on its health, usage, and logs.

CHANGELOG.md

عليك أيضًا توثيق التغييرات التي تجريها بين إصدارات الإضافة في ملف CHANGELOG.md.

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

## Version 0.0.1

Initial release of the _Convert messages to upper case_ extension.

ملف README.md

توفّر معظم الإضافات أيضًا ملفًا تمهيديًا لصالح المستخدمين الذين ينتقلون إلى مستودع الإضافة. يمكنك كتابة هذا الملف يدويًّا أو إنشاء أمر "readme" (القراءة لي) باستخدام الأمر.

لأغراض هذا الدليل، يمكنك تخطي كتابة ملف تمهيدي.

مستندات إضافية

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

لأغراض هذا الدليل، يمكنك تخطي كتابة المزيد من المستندات الشاملة.

11- النشر في "مركز الإضافات"

الآن بعد أن اكتملت إضافتك من خلال الرمز البرمجي وتم توثيقها، أصبحت جاهزًا لمشاركتها مع الجميع حول العالم على "مركز الإضافات". ولكن نظرًا لأن هذا مجرد برنامج تعليمي، فلا تفعل ذلك في الواقع. ابدأ في كتابة إضافتك الخاصة باستخدام ما تعلمته هنا وفي بقية مستندات ناشري إضافات Firebase، ومن خلال فحص مصدر الإضافات الرسمية المكتوبة من Firebase.

عند الاستعداد لنشر عملك على "مركز الإضافات"، يمكنك إجراء ذلك باتّباع الخطوات التالية:

  1. إذا كنت تنشر الإضافة الأولى، يمكنك التسجيل كناشر للإضافة. عند التسجيل كناشر للإضافات، يمكنك إنشاء رقم تعريفي للناشر يتيح للمستخدمين التعرّف عليك بسرعة كمؤلف الإضافات.
  2. استضِف رمز المصدر الخاص بإضافتك في موقع يمكن التحقّق منه بشكل علني. عند توفر الرمز من مصدر قابل للتحقق، يمكن لـ Firebase نشر الإضافة مباشرةً من هذا الموقع. يساعد ذلك في التأكّد من أنّك تنشر الإصدار الذي تم إصداره حاليًا من إضافتك، كما يساعد المستخدمين من خلال السماح لهم بفحص الرمز الذي يثبّتونه في مشاريعهم.

    وهذا يعني في الوقت الحالي إتاحة إضافتك في مستودع GitHub العام.

  3. حمِّل الإضافة إلى "مركز الإضافات" باستخدام الأمر firebase ext:dev:upload.

  4. انتقل إلى لوحة بيانات الناشر في وحدة تحكم Firebase، وابحث عن الإضافة التي حمّلتها للتو، ثم انقر على "النشر على مركز الإضافات". يتطلب هذا مراجعة من فريق المراجعة لدينا، الأمر الذي قد يستغرق بضعة أيام. في حال الموافقة على الإضافة، سيتم نشرها في "مركز الإضافات". في حال الرفض، ستتلقى رسالة توضح السبب، ويمكنك بعد ذلك معالجة المشكلات التي تم الإبلاغ عنها وإعادة إرسالها للمراجعة.