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

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

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

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

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

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

    npm install -g firebase-tools
    

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

  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:

    الدوال/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 الخاص بالإضافة) في المحرِّر أو بيئة تطوير البرامج المتكاملة التي تختارها. استبدِل المحتوى بما يلي:

    الدوال/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. بما أنّ الإضافة تستخدم الآن قاعدة بيانات "الوقت الفعلي" كمشغِّل، عليك تعديل إعدادات المحاكي لتشغيل محاكي قاعدة بيانات "الوقت الفعلي" إلى جانب محاكي Cloud Functions:

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

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

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

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

    الدوال/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
    

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

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

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

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

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

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

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

    الدوال/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" (القراءة لي) باستخدام الأمر.

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

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

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

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

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

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

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

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

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

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

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