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

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

1. إعداد بيئتك وبدء مشروع

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

  1. ثبِّت الإصدار 16 من Node.js أو إصدارًا أحدث. تتمثل إحدى طرق تثبيت Node في استخدام nvm (أو nvm-windows).

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

    npm install -g firebase-tools

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

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

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

    firebase ext:dev:init

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

2- تجربة مثال على الإضافة باستخدام المحاكي

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

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

  1. انتقِل إلى الدليل integration-tests:

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

    firebase emulators:start --project=demo-test

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

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

    يعرض المتصفّح الرسالة "مرحبًا بالجميع من greet-the-world".

  4. يمكن العثور على الرمز المصدر لهذه الدالة في دليل functions الإضافة. افتح المصدر في المحرِّر أو بيئة تطوير البرامج المتكاملة التي تختارها:

    functions/index.js

    const functions = require("firebase-functions/v1");
    
    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. أثناء تشغيل المحاكي، سيعيد تحميل أي تغييرات تُجريها على رمز Functions تلقائيًا. حاوِل إجراء تغيير بسيط على دالة greetTheWorld:

    functions/index.js

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

    احفظ التغييرات. سيعيد المحاكي تحميل الرمز، والآن عند زيارة عنوان URL للدالة، ستظهر لك التحية المعدَّلة.

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

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

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

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

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

      firebase init emulators

      عند مطالبتك، تخطّى إعداد مشروع تلقائي، ثم اختَر "وظائف Google" ومحاكيات قاعدة البيانات. اقبل المنافذ التلقائية واسمح لأداة الإعداد بتنزيل أي ملفات مطلوبة.

    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 باللغة الإنجليزية الكبيرة.

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

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

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

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

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

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

6- تحديد المَعلمات التي يمكن للمستخدم ضبطها

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

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

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

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

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

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

  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
    

في هذه المرحلة، تكون قد أكملت الخطوات اللازمة لإضافة حدث غير متزامن hook إلى إضافتك.

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

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

    firebase init functions

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

  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. إضافة معالِجات أحداث رحلة المستخدِم

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

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

لإضافة معالِج أحداث دورة النشاط الذي يُعيد تعبئة الرسائل الحالية، اتّبِع الخطوات التالية:

  1. حدِّد دالة جديدة في Cloud Function يتم تفعيلها من خلال أحداث قائمة انتظار المهام:

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

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

  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 lifecycle:

    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 المستندة إلى الدفع عند الاستخدام تتطلّب Cloud Functions for Firebase أن يكون لمشروعك حساب فوترة، لذا ستحتاج أيضًا إلى حساب فوترة لتثبيت إضافة.
  3. في مشروعك الجديد، فعِّل قاعدة بيانات الوقت الفعلي.
  4. بما أنّك تريد اختبار قدرة الإضافة على إضافة البيانات الحالية عند التركيب، استورِد بعض نماذج البيانات إلى مثيل قاعدة البيانات في الوقت الفعلي:
    1. نزِّل بعض بيانات قاعدة بيانات الربط في الوقت الفعلي الأساسية.
    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 CLI الإعدادات في الدليل extensions ويُسجِّل موقع مصدر الإضافة في الملف firebase.json. يُطلَق على هذين السجلتَين بشكلٍ مجمع اسم بيان الإضافات. يمكن للمستخدمين استخدام البيان لحفظ إعدادات الإضافات ونشرها في مشاريع مختلفة.

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

    firebase deploy --only extensions

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

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

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

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

extension.yaml

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

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

PREINSTALL.md

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

يتم عرض نص هذا الملف للمستخدم في Extensions Hub ومن خلال الأمر 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

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

يتم عرض محتوى 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 لفائدة المستخدمين الذين يزورون مستودع الإضافات. يمكنك كتابة هذا الملف يدويًا أو إنشاء ملف readme باستخدام الأمر.

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

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

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

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

11. النشر على Extensions Hub

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

عندما تكون مستعدًا لنشر عملك على Extensions Hub، إليك كيفية تنفيذ ذلك:

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

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

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

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