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

ترشدك هذه الصفحة إلى الخطوات المطلوبة لإنشاء "إضافة" 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:

    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 الذي تم طباعته عند تشغيلها.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

extension.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

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

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

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

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

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

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

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