البدء في بناء ملحق

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

1. قم بإعداد البيئة الخاصة بك وتهيئة المشروع

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

  1. قم بتثبيت Node.js 16 أو الأحدث. إحدى طرق تثبيت 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 بتهيئة دليل الامتدادات الجديد، قامت بإنشاء وظيفة نموذجية بسيطة ودليل integration-tests الذي يحتوي على الملفات الضرورية لتشغيل ملحق باستخدام مجموعة محاكي Firebase.

حاول تشغيل ملحق المثال في المحاكي:

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

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

    firebase emulators:start --project=demo-test
    

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

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

    يعرض متصفحك الرسالة "Hello World from Greeting-the-World".

  4. الكود المصدري لهذه الوظيفة موجود في دليل functions الامتداد. افتح المصدر في المحرر أو IDE الذي تختاره:

    وظائف/index.js

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

    وظائف/index.js

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

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

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

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

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

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

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

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

  2. نظرًا لأنك قمت بتغيير اسم الامتداد الخاص بك، فيجب عليك أيضًا تحديث تكوين المحاكي الخاص بك بالاسم الجديد:

    1. في functions/integration-tests/firebase.json ، قم بتغيير greet-the-world إلى rtdb-uppercase-messages .
    2. أعد functions/integration-tests/extensions/greet-the-world.env إلى functions/integration-tests/extensions/rtdb-uppercase-messages.env .

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

4. اكتب وظيفة سحابية وأعلنها كمورد ملحق

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

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

    وظائف/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 هي الوظيفة السحابية الوحيدة للامتداد؛ والآن بعد أن قمت باستبداله بـ makeuppercase ، فأنت بحاجة أيضًا إلى تحديث إعلانه.

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

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

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

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

      firebase init emulators
      

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

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

      firebase emulators:start --project=demo-test
      
  4. جرب الامتداد المحدث:

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

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

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

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

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

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

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

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

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

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

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

6. تحديد المعلمات القابلة للتكوين بواسطة المستخدم

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

اجعل المسار الذي يشاهده الامتداد للرسائل الجديدة قابلاً للتكوين بواسطة المستخدم:

  1. في ملف extension.yaml ، قم بإضافة قسم params :

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

    يحدد هذا معلمة سلسلة جديدة سيُطلب من المستخدمين تعيينها عند تثبيت الامتداد الخاص بك.

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

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

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

  3. يمكنك أيضًا الوصول إلى المعلمات المحددة من قبل المستخدم من كود الوظائف الخاص بك.

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

    وظائف/index.js

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

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

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

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

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

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

    وظائف/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 للمستخدم بالتسلسل، مما يؤدي إلى حصول الحقل upper على القيمة RECIPE!!! .

8. قم بإضافة معالجات أحداث دورة الحياة

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

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

لإضافة معالج أحداث دورة الحياة الذي يقوم بملء الرسائل الموجودة، قم بما يلي:

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

    وظائف/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 واخرج مبكرًا إذا لم يتم تعيينها:

    وظائف/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. قم بتنزيل بعض بيانات RTDB الأولية .
    2. في صفحة قاعدة البيانات في الوقت الفعلي لوحدة تحكم Firebase، انقر فوق (المزيد) > استيراد JSON وحدد الملف الذي قمت بتنزيله للتو.
  5. لتمكين وظيفة الردم من استخدام الأسلوب orderByChild ، قم بتكوين قاعدة البيانات لفهرسة الرسائل بقيمة upper :

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

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

  1. قم بإنشاء دليل جديد لمشروع Firebase الخاص بك:

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

    firebase init database
    

    عند المطالبة، حدد المشروع الذي قمت بإنشائه للتو.

  3. قم بتثبيت الامتداد في مشروع Firebase المحلي الخاص بك:

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

    هنا يمكنك رؤية تجربة المستخدم عند تثبيت ملحق باستخدام أداة Firebase CLI. تأكد من تحديد "نعم" عندما تسألك أداة التكوين عما إذا كنت تريد ملء قاعدة البيانات الموجودة لديك.

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

  4. انشر تكوين الامتداد الخاص بك إلى مشروعك المباشر:

    firebase deploy --only extensions
    

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

10. كتابة الوثائق

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

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

Extension.yaml

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

ومع ذلك، لا تغفل أهمية الوثائق الموجودة في هذا الملف. بالإضافة إلى المعلومات التعريفية الهامة للامتداد - الاسم والوصف والمؤلف وموقع المستودع الرسمي - يحتوي ملف extension.yaml على وثائق واجهة المستخدم لكل مورد ومعلمة قابلة للتكوين بواسطة المستخدم. يتم عرض هذه المعلومات للمستخدمين في وحدة تحكم Firebase، وExtensions Hub، وFirebase CLI.

التثبيت المسبق.md

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

يتم عرض نص هذا الملف للمستخدم في Extensions Hub وبواسطة أمر firebase ext:info .

فيما يلي مثال لملف التثبيت المسبق:

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

This extension expects a database layout like the following example:

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

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

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

#### Additional setup

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

#### Billing

To install an extension, your project must be on the
[Blaze (pay as you go) plan](https://firebase.google.com/pricing).

- This extension uses other Firebase and Google Cloud Platform services, which
  have associated charges if you exceed the service's no-cost tier:
  - Realtime Database
  - Cloud Functions (Node.js 10+ runtime)
    [See FAQs](https://firebase.google.com/support/faq#extensions-pricing)
- If you enable events,
  [Eventarc fees apply](https://cloud.google.com/eventarc/pricing).

POSTINSTALL.md

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

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

فيما يلي مثال لملف ما بعد التثبيت لملحق البرنامج التعليمي:

### See it in action

You can test out this extension right away!

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

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

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

### Using the extension

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

### Monitoring

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

التغيير.md

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

نظرًا لأن ملحق المثال لم يتم نشره من قبل، فإن سجل التغيير يحتوي على إدخال واحد فقط:

## Version 0.0.1

Initial release of the _Convert messages to upper case_ extension.

التمهيدي.md

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

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

وثائق اضافية

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

ولأغراض هذا الدليل، تخطى كتابة وثائق أكثر شمولاً.

11. انشر على مركز الامتدادات

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

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

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

    ويعني هذا حاليًا إتاحة الامتداد الخاص بك في مستودع GitHub العام.

  3. قم بتحميل الامتداد الخاص بك إلى Extensions Hub باستخدام أمر firebase ext:dev:upload .

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