شروع به ساخت یک افزونه کنید

این صفحه شما را در مراحل لازم برای ساخت یک افزونه ساده Firebase راهنمایی می‌کند، که می‌توانید آن را در پروژه‌های خود نصب کنید یا با دیگران به اشتراک بگذارید. این مثال ساده از افزونه Firebase، پایگاه داده بلادرنگ شما را برای پیام‌ها رصد می‌کند و آنها را به حروف بزرگ تبدیل می‌کند.

۱. محیط خود را تنظیم کنید و یک پروژه را مقداردهی اولیه کنید

قبل از اینکه بتوانید ساخت یک افزونه را شروع کنید، باید یک محیط ساخت با ابزارهای مورد نیاز راه‌اندازی کنید.

  1. 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 ext:dev:init

    وقتی از شما خواسته شد، جاوا اسکریپت را به عنوان زبان توابع انتخاب کنید (اما توجه داشته باشید که می‌توانید هنگام توسعه افزونه خود از TypeScript نیز استفاده کنید)، و وقتی از شما خواسته شد وابستگی‌ها را نصب کنید، به «بله» پاسخ دهید. (پیش‌فرض‌های سایر گزینه‌ها را بپذیرید.) این دستور یک پایگاه کد اسکلتی برای یک افزونه جدید ایجاد می‌کند که می‌توانید از آن شروع به توسعه افزونه خود کنید.

۲. افزونه‌ی نمونه را با استفاده از شبیه‌ساز امتحان کنید

وقتی Firebase CLI دایرکتوری افزونه‌های جدید را مقداردهی اولیه کرد، یک تابع مثال ساده و یک دایرکتوری integration-tests ایجاد کرد که شامل فایل‌های لازم برای اجرای یک افزونه با استفاده از مجموعه شبیه‌ساز Firebase است.

سعی کنید افزونه‌ی مثال را در شبیه‌ساز اجرا کنید:

  1. به دایرکتوری integration-tests بروید:

    cd functions/integration-tests
  2. شبیه‌ساز را با یک پروژه آزمایشی شروع کنید:

    firebase emulators:start --project=demo-test

    شبیه‌ساز، افزونه را در یک پروژه از پیش تعریف شده "dummy" ( demo-test ) بارگذاری می‌کند. افزونه تاکنون شامل یک تابع HTTP به نام greetTheWorld است که هنگام دسترسی، یک پیام "hello world" را برمی‌گرداند.

  3. در حالی که شبیه‌ساز هنوز در حال اجرا است، با مراجعه به URL که هنگام اجرای افزونه چاپ شده است، تابع greetTheWorld را امتحان کنید.

    مرورگر شما پیام «سلام دنیا از طرف greet-the-world» را نمایش می‌دهد.

  4. کد منبع این تابع در پوشه functions افزونه قرار دارد. کد منبع را در ویرایشگر یا IDE مورد نظر خود باز کنید:

    توابع/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}`;
    

    تغییرات خود را ذخیره کنید. شبیه‌ساز کد شما را مجدداً بارگذاری می‌کند و اکنون، وقتی به آدرس تابع مراجعه می‌کنید، پیام خوشامدگویی به‌روزرسانی‌شده را مشاهده خواهید کرد.

۳. اطلاعات اولیه را به extension.yaml اضافه کنید

اکنون که یک محیط توسعه راه‌اندازی کرده‌اید و شبیه‌ساز افزونه‌ها را اجرا می‌کنید، می‌توانید شروع به نوشتن افزونه‌ی خود کنید.

به عنوان اولین قدم ساده، به جای greet-the-world متادیتای افزونه از پیش تعریف شده را ویرایش کنید تا افزونه‌ای که می‌خواهید بنویسید را منعکس کند. این متادیتا در فایل extension.yaml ذخیره می‌شود.

  1. extension.yaml در ویرایشگر خود باز کنید و کل محتوای فایل را با موارد زیر جایگزین کنید:

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

    به قرارداد نامگذاری استفاده شده در فیلد name توجه کنید: افزونه‌های رسمی فایربیس با پیشوندی نامگذاری می‌شوند که نشان‌دهنده محصول اصلی فایربیس است که افزونه روی آن کار می‌کند و به دنبال آن توضیحی در مورد عملکرد افزونه ارائه می‌شود. شما باید از همین قرارداد در افزونه‌های خود استفاده کنید.

  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 در کد افزونه شما وجود دارد، اما فعلاً آنها را به حال خود رها کنید. آنها را در چند بخش بعدی به‌روزرسانی خواهید کرد.

۴. یک تابع ابری بنویسید و آن را به عنوان یک منبع افزونه اعلام کنید

حالا می‌توانید شروع به نوشتن کد کنید. در این مرحله، یک تابع ابری (Cloud Function) خواهید نوشت که وظیفه اصلی افزونه شما را انجام می‌دهد، یعنی مشاهده پیام‌ها در پایگاه داده بلادرنگ (Realtime Database) و تبدیل آنها به حروف بزرگ.

  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 افزونه باید فعال شود و یک رکورد فرزند به پیام ۱۱ با محتوای "upper": "RECIPE" اضافه کند. برای تأیید نتایج مورد انتظار، به گزارش‌ها و تب‌های پایگاه داده رابط کاربری شبیه‌ساز نگاهی بیندازید.

    3. سعی کنید چند فرزند دیگر به گره messages اضافه کنید ( {"original":"any text"} ). هر زمان که یک رکورد جدید اضافه می‌کنید، افزونه باید یک فیلد uppercase حاوی محتوای فیلد original اضافه کند.

اکنون شما یک افزونه کامل، هرچند ساده، دارید که روی یک نمونه RTDB کار می‌کند. در بخش‌های بعدی، این افزونه را با برخی ویژگی‌های اضافی اصلاح خواهید کرد. سپس، افزونه را برای توزیع به دیگران آماده خواهید کرد و در نهایت، یاد خواهید گرفت که چگونه افزونه خود را در Extensions Hub منتشر کنید.

۵. تعریف APIها و نقش‌ها

فایربیس به هر نمونه از افزونه نصب شده، با استفاده از یک حساب کاربری سرویس به ازای هر نمونه، دسترسی محدودی به پروژه و داده‌های آن می‌دهد. هر حساب کاربری حداقل مجوزهای لازم برای کار را دارد. به همین دلیل، شما باید صریحاً هر نقش IAM مورد نیاز افزونه خود را اعلام کنید. وقتی کاربران افزونه شما را نصب می‌کنند، فایربیس یک حساب کاربری سرویس با این نقش‌های اعطا شده ایجاد می‌کند و از آن برای اجرای افزونه استفاده می‌کند.

برای فعال کردن رویدادهای یک محصول نیازی به تعریف نقش‌ها ندارید، اما برای تعامل با آن باید یک نقش تعریف کنید. از آنجایی که تابعی که در مرحله قبل اضافه کردید در پایگاه داده Realtime می‌نویسد، باید تعریف زیر را به extension.yaml اضافه کنید:

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

به طور مشابه، شما APIهای گوگلی که یک افزونه از آنها استفاده می‌کند را در فیلد apis اعلام می‌کنید. وقتی کاربران افزونه شما را نصب می‌کنند، از آنها پرسیده می‌شود که آیا می‌خواهند این APIها را به طور خودکار برای پروژه خود فعال کنند یا خیر. این معمولاً فقط برای APIهای گوگل غیر فایربیس ضروری است و برای این راهنما نیازی به آن نیست.

۶. پارامترهای قابل تنظیم توسط کاربر را تعریف کنید

تابعی که در دو مرحله آخر ایجاد کردید، یک مکان خاص 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. همچنین می‌توانید از طریق کد توابع خود به پارامترهای تعریف‌شده توسط کاربر دسترسی پیدا کنید.

    در تابعی که در بخش قبل نوشتید، مسیر را برای مشاهده تغییرات، به صورت hard-code تعریف کردید. تعریف تریگر را طوری تغییر دهید که به مقدار تعریف شده توسط کاربر ارجاع دهد:

    توابع/index.js

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

    توجه داشته باشید که در افزونه‌های فایربیس، این تغییر صرفاً برای مستندسازی است: وقتی یک تابع ابری به عنوان بخشی از یک افزونه مستقر می‌شود، از تعریف تریگر از فایل extension.yaml استفاده می‌کند و مقدار مشخص شده در تعریف تابع را نادیده می‌گیرد. با این وجود، ایده خوبی است که در کد خود مستند کنید که این مقدار از کجا می‌آید.

  4. ممکن است ایجاد تغییر در کدی که هیچ تأثیری در زمان اجرا ندارد، ناامیدکننده باشد، اما درس مهمی که می‌توان از آن گرفت این است که می‌توانید به هر پارامتر تعریف‌شده توسط کاربر در کد تابع خود دسترسی پیدا کنید و از آن به عنوان یک مقدار معمولی در منطق تابع استفاده کنید. به عنوان تأییدی بر این قابلیت، عبارت log زیر را اضافه کنید تا نشان دهید که واقعاً به مقداری که کاربر تعریف کرده است دسترسی دارید:

    توابع/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 افزونه باید مانند قبل فعال شود، اما اکنون باید پارامتر تعریف شده توسط کاربر را نیز در گزارش کنسول چاپ کند.

۷. برای منطق تعریف‌شده توسط کاربر، قلاب‌های رویداد ارائه دهید

شما به عنوان یک نویسنده افزونه، قبلاً دیده‌اید که چگونه یک محصول 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

    وقتی از شما خواسته شد، از تنظیم پروژه پیش‌فرض خودداری کنید، جاوا اسکریپت را به عنوان زبان توابع ابری انتخاب کنید و وابستگی‌های مورد نیاز را نصب کنید. این پروژه نشان دهنده یک پروژه کاربر است که افزونه شما روی آن نصب شده است.

  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!!! را دریافت کند.

۸. اضافه کردن کنترل‌کننده‌های رویداد چرخه عمر

افزونه‌ای که تاکنون نوشته‌اید، پیام‌ها را هنگام ایجاد پردازش می‌کند. اما اگر کاربران شما هنگام نصب افزونه، از قبل پایگاه داده‌ای از پیام‌ها داشته باشند، چه می‌شود؟ افزونه‌های Firebase دارای قابلیتی به نام قلاب‌های رویداد چرخه عمر هستند که می‌توانید هنگام نصب، به‌روزرسانی یا پیکربندی مجدد افزونه، از آنها برای ایجاد اقدامات استفاده کنید. در این بخش، از قلاب‌های رویداد چرخه عمر برای پر کردن پایگاه داده پیام‌های موجود در پروژه با پیام‌های با حروف بزرگ هنگام نصب افزونه توسط کاربر استفاده خواهید کرد.

افزونه‌های فایربیس از Cloud Tasks برای اجرای کنترل‌کننده‌های رویداد چرخه عمر شما استفاده می‌کنند. شما با استفاده از توابع ابری، کنترل‌کننده‌های رویداد را تعریف می‌کنید؛ هر زمان که نمونه‌ای از افزونه شما به یکی از رویدادهای چرخه عمر پشتیبانی‌شده برسد، اگر یک کنترل‌کننده تعریف کرده باشید، آن کنترل‌کننده را به صف Cloud Tasks اضافه می‌کند. سپس Cloud Tasks به صورت غیرهمزمان کنترل‌کننده را اجرا می‌کند. در حالی که یک کنترل‌کننده رویداد چرخه عمر در حال اجرا است، کنسول فایربیس به کاربر گزارش می‌دهد که نمونه افزونه یک وظیفه پردازشی در حال انجام دارد. گزارش وضعیت جاری و تکمیل وظیفه به کاربر بر عهده تابع کنترل‌کننده شماست.

برای افزودن یک کنترل‌کننده رویداد چرخه عمر که پیام‌های موجود را دوباره پر می‌کند، موارد زیر را انجام دهید:

  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 ، تابع backfill خود را به عنوان یک منبع افزونه که دارای ویژگی 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
    

    سپس در ابتدای تابع backfill، مقدار پارامتر DO_BACKFILL را بررسی کنید و اگر تنظیم نشده بود، زود خارج شوید:

    توابع/index.js

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

با تغییرات فوق، این افزونه اکنون پیام‌های موجود را هنگام نصب به حروف بزرگ تبدیل می‌کند.

تا این مرحله، شما از شبیه‌ساز افزونه برای توسعه افزونه خود و آزمایش تغییرات مداوم استفاده کردید. با این حال، شبیه‌ساز افزونه از فرآیند نصب صرف نظر می‌کند، بنابراین برای آزمایش کنترل‌کننده رویداد onInstall خود، باید افزونه را در یک پروژه واقعی نصب کنید. البته این هم خوب است، زیرا با اضافه شدن این ویژگی پر کردن خودکار، افزونه آموزشی اکنون از نظر کد کامل است!

۹. استقرار در یک پروژه واقعی Firebase

اگرچه شبیه‌ساز افزونه‌ها ابزاری عالی برای تکرار سریع یک افزونه در طول توسعه است، اما در برهه‌ای از زمان می‌خواهید آن را در یک پروژه واقعی امتحان کنید.

برای انجام این کار، ابتدا یک پروژه جدید با برخی از سرویس‌های فعال راه‌اندازی کنید:

  1. در کنسول Firebase ، یک پروژه جدید اضافه کنید.
  2. پروژه خود را به طرح پرداخت در حین استفاده Blaze ارتقا دهید . Cloud Functions for Firebase نیاز دارد که پروژه شما یک حساب صورتحساب داشته باشد، بنابراین برای نصب افزونه نیز به یک حساب صورتحساب نیاز دارید.
  3. در پروژه جدید خود، پایگاه داده بلادرنگ (Real-time Database) را فعال کنید .
  4. از آنجایی که می‌خواهید توانایی افزونه خود را در پر کردن مجدد داده‌های موجود در هنگام نصب آزمایش کنید، برخی از داده‌های نمونه را در نمونه پایگاه داده بلادرنگ خود وارد کنید:
    1. مقداری از داده‌های اولیه RTDB را دانلود کنید.
    2. در صفحه پایگاه داده بلادرنگ کنسول Firebase، روی (more) > Import JSON کلیک کنید و فایلی را که دانلود کرده‌اید انتخاب کنید.
  5. برای فعال کردن تابع backfill برای استفاده از متد 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 نسخه‌های خرد شده از حداقل مستندات مورد نیاز را ایجاد کرد. این فایل‌ها را به‌روزرسانی کنید تا دقیقاً افزونه‌ای را که ساخته‌اید منعکس کنند.

پسوند.yaml

شما قبلاً همزمان با توسعه این افزونه، این فایل را به‌روزرسانی کرده‌اید، بنابراین نیازی به به‌روزرسانی بیشتر در حال حاضر ندارید.

با این حال، اهمیت مستندات موجود در این فایل را نادیده نگیرید. علاوه بر اطلاعات شناسایی حیاتی یک افزونه - نام، توضیحات، نویسنده، محل رسمی مخزن - فایل extension.yaml شامل مستندات کاربرپسند برای هر منبع و پارامتر قابل تنظیم توسط کاربر است. این اطلاعات در کنسول Firebase، Extensions Hub و Firebase CLI برای کاربران نمایش داده می‌شود.

پیش نصب.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).

نصب پست.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.

README.md

اکثر افزونه‌ها همچنین یک فایل readme برای کاربرانی که از مخزن افزونه بازدید می‌کنند، ارائه می‌دهند. می‌توانید این فایل را به صورت دستی بنویسید یا با استفاده از دستور read me آن را ایجاد کنید.

برای اهداف این راهنما، از نوشتن فایل readme صرف نظر کنید.

مستندات اضافی

مستنداتی که در بالا مورد بحث قرار گرفت، حداقل مجموعه مستنداتی است که باید در اختیار کاربران قرار دهید. بسیاری از افزونه‌ها برای استفاده موفقیت‌آمیز کاربران به مستندات دقیق‌تری نیاز دارند. در این صورت، باید مستندات بیشتری بنویسید و آنها را در جایی قرار دهید که بتوانید کاربران را به آن ارجاع دهید.

برای اهداف این راهنما، از نوشتن مستندات گسترده‌تر صرف نظر کنید.

۱۱. انتشار در Extensions Hub

حالا که کد افزونه شما کامل و مستند شده است، آماده‌اید تا آن را در Extensions Hub با دنیا به اشتراک بگذارید. اما از آنجایی که این فقط یک آموزش است، واقعاً این کار را نکنید. بروید و با استفاده از آنچه در اینجا و در بقیه مستندات منتشرکننده افزونه‌های Firebase و با بررسی منبع افزونه‌های رسمی نوشته شده توسط Firebase آموخته‌اید، شروع به نوشتن افزونه خودتان کنید.

وقتی آماده انتشار کار خود در Extensions Hub شدید، مراحل زیر را دنبال کنید:

  1. اگر اولین افزونه خود را منتشر می‌کنید، به عنوان ناشر افزونه ثبت نام کنید . وقتی به عنوان ناشر افزونه‌ها ثبت نام می‌کنید، یک شناسه ناشر ایجاد می‌کنید که به کاربران امکان می‌دهد به سرعت شما را به عنوان نویسنده افزونه‌هایتان شناسایی کنند.
  2. کد منبع افزونه خود را در یک مکان قابل تأیید عمومی قرار دهید. وقتی کد شما از یک منبع قابل تأیید در دسترس باشد، Firebase می‌تواند افزونه شما را مستقیماً از این مکان منتشر کند. انجام این کار به شما اطمینان می‌دهد که نسخه منتشر شده فعلی افزونه خود را منتشر می‌کنید و به کاربران اجازه می‌دهد کدی را که در پروژه‌های خود نصب می‌کنند، بررسی کنند.

    در حال حاضر، این به معنای در دسترس قرار دادن افزونه شما در یک مخزن عمومی گیت‌هاب است.

  3. افزونه‌ی خود را با استفاده از دستور firebase ext:dev:upload در Extensions Hub آپلود کنید.

  4. به داشبورد ناشر خود در کنسول Firebase بروید، افزونه‌ای را که تازه آپلود کرده‌اید پیدا کنید و روی «انتشار در Extensions Hub» کلیک کنید. این کار درخواست بررسی از کارکنان بررسی ما را می‌دهد که می‌تواند چند روز طول بکشد. در صورت تأیید، افزونه در Extensions Hub منتشر می‌شود. در صورت رد شدن، پیامی دریافت خواهید کرد که دلیل آن را توضیح می‌دهد. سپس می‌توانید مشکلات گزارش شده را برطرف کرده و دوباره برای بررسی ارسال کنید.