Mulai membangun ekstensi

Halaman ini memandu Anda melakukan langkah-langkah yang diperlukan untuk membangun ekstensi Firebase sederhana, yang dapat diinstal di project Anda atau dibagikan kepada orang lain. Contoh sederhana dari ekstensi Firebase ini akan memantau pesan Realtime Database dan mengonversinya menjadi huruf besar.

1. Menyiapkan lingkungan dan melakukan inisialisasi project

Sebelum dapat mulai membangun ekstensi, Anda harus menyiapkan lingkungan build dengan alat yang diperlukan.

  1. Instal Node.js 16 atau yang lebih baru. Salah satu cara untuk menginstal Node adalah dengan menggunakan nvm (atau nvm-windows).

  2. Instal Firebase CLI atau perbarui ke versi terbaru. Untuk menginstal atau memperbarui menggunakan npm, jalankan perintah ini:

    npm install -g firebase-tools
    

Sekarang, gunakan Firebase CLI untuk menginisialisasi project ekstensi baru:

  1. Buat direktori untuk ekstensi Anda dan cd ke dalamnya:

    mkdir rtdb-uppercase-messages && cd rtdb-uppercase-messages
    
  2. Jalankan perintah ext:dev:init Firebase CLI:

    firebase ext:dev:init
    

    Saat diminta, pilih JavaScript sebagai bahasa untuk fungsi (tetapi perhatikan, Anda juga dapat menggunakan TypeScript saat mengembangkan ekstensi sendiri), dan saat diminta untuk menginstal dependensi, jawab "yes". (Terima setelan default untuk opsi lainnya.) Perintah ini akan menyiapkan codebase kerangka untuk ekstensi baru, yang dapat digunakan untuk mulai mengembangkan ekstensi.

2. Mencoba ekstensi contoh menggunakan emulator

Saat menginisialisasi direktori ekstensi baru, Firebase CLI membuat fungsi contoh sederhana dan direktori integration-tests yang berisi file yang diperlukan untuk menjalankan ekstensi menggunakan Firebase Emulator Suite.

Coba jalankan ekstensi contoh di emulator:

  1. Ubah ke direktori integration-tests:

    cd functions/integration-tests
    
  2. Mulai emulator dengan project demo:

    firebase emulators:start --project=demo-test
    

    Emulator memuat ekstensi ke project "dummy" yang telah ditentukan (demo-test). Sejauh ini, ekstensi terdiri dari satu fungsi yang dipicu HTTP, greetTheWorld, yang menampilkan pesan "hello world" saat diakses.

  3. Dengan emulator yang masih berjalan, coba fungsi greetTheWorld ekstensi dengan membuka URL yang dicetak saat Anda memulainya.

    Browser akan menampilkan pesan "Hello World from greet-the-world".

  4. Kode sumber untuk fungsi ini berada di direktori functions ekstensi. Buka sumber di editor atau IDE pilihan Anda:

    functions/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. Saat berjalan, emulator akan otomatis memuat ulang perubahan apa pun yang Anda lakukan pada kode Functions. Coba buat perubahan kecil pada fungsi greetTheWorld:

    functions/index.js

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

    Simpan perubahan Anda. Emulator akan memuat ulang kode Anda, dan kini, saat mengunjungi URL fungsi, Anda akan melihat salam yang diperbarui.

3. Menambahkan informasi dasar ke extension.yaml

Setelah menyiapkan lingkungan pengembangan dan menjalankan emulator ekstensi, Anda dapat mulai menulis ekstensi Anda sendiri.

Sebagai langkah pertama yang sederhana, edit metadata ekstensi yang telah ditentukan agar mencerminkan ekstensi yang ingin ditulis, bukan greet-the-world. Metadata ini disimpan dalam file extension.yaml.

  1. Buka extension.yaml di editor Anda, dan ganti seluruh konten file dengan:

    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
    

    Perhatikan konvensi penamaan yang digunakan dalam kolom name: ekstensi Firebase resmi diberi nama dengan awalan yang menunjukkan produk utama Firebase yang digunakan untuk ekstensi, diikuti dengan deskripsi fungsi ekstensi tersebut. Sebaiknya gunakan konvensi yang sama di ekstensi Anda sendiri.

  2. Karena Anda telah mengubah nama ekstensi, Anda juga harus memperbarui konfigurasi emulator dengan nama baru:

    1. Di functions/integration-tests/firebase.json, ubah greet-the-world menjadi rtdb-uppercase-messages.
    2. Ganti nama functions/integration-tests/extensions/greet-the-world.env menjadi functions/integration-tests/extensions/rtdb-uppercase-messages.env.

Masih ada beberapa sisa ekstensi greet-the-world di kode ekstensi Anda, tetapi biarkan saja untuk saat ini. Anda akan memperbaruinya dalam beberapa bagian berikutnya.

4. Menulis Cloud Function dan mendeklarasikannya sebagai resource ekstensi

Sekarang Anda dapat mulai menulis beberapa kode. Pada langkah ini, Anda akan menulis Cloud Function yang melakukan tugas inti ekstensi, yaitu mengawasi pesan Realtime Database dan mengonversinya menjadi huruf besar.

  1. Buka sumber untuk fungsi ekstensi (di direktori functions ekstensi) di editor atau IDE pilihan Anda. Ganti kontennya dengan kode berikut:

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

    Fungsi lama yang Anda ganti adalah fungsi yang dipicu HTTP, yang dijalankan saat endpoint HTTP diakses. Fungsi baru akan dipicu oleh peristiwa database secara real time: fungsi tersebut mengamati apakah ada item baru di jalur tertentu, dan saat terdeteksi, fungsi tersebut akan menuliskan ulang versi huruf kapital dari nilai tersebut pada database.

    File baru ini menggunakan sintaksis modul ECMAScript (import dan export), bukan CommonJS (require). Untuk menggunakan modul ES di Node, tentukan "type": "module" di functions/package.json:

    {
      "name": "rtdb-uppercase-messages",
      "main": "index.js",
      "type": "module",
      …
    }
    
  2. Setiap fungsi dalam ekstensi Anda harus dideklarasikan dalam file extension.yaml. Ekstensi contoh mendeklarasikan greetTheWorld sebagai satu-satunya Cloud Function ekstensi; setelah menggantinya dengan makeuppercase, Anda juga perlu memperbarui deklarasinya.

    Buka extension.yaml dan tambahkan kolom 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. Karena ekstensi Anda sekarang menggunakan Realtime Database sebagai pemicu, Anda perlu memperbarui konfigurasi emulator untuk menjalankan emulator RTDB bersama dengan emulator Cloud Functions:

    1. Jika emulator masih berjalan, hentikan dengan menekan Ctrl-C.

    2. Jalankan perintah berikut dari direktori functions/integration-tests:

      firebase init emulators
      

      Jika diminta, lewati penyiapan project default, lalu pilih emulator Functions dan Database. Terima port default dan izinkan alat penyiapan untuk mendownload file apa pun yang diperlukan.

    3. Mulai ulang emulator:

      firebase emulators:start --project=demo-test
      
  4. Coba ekstensi yang telah diperbarui:

    1. Buka UI emulator Database menggunakan link yang dicetak emulator saat Anda memulainya.

    2. Edit node root database:

      • Kolom: messages
      • Jenis: json
      • Nilai: {"11": {"original": "recipe"}}

      Jika semuanya sudah disiapkan dengan benar, saat Anda menyimpan perubahan database, fungsi makeuppercase ekstensi akan memicu dan menambahkan record turunan ke pesan 11 dengan konten "upper": "RECIPE". Lihat log dan tab database di UI emulator untuk mengonfirmasi hasil yang diharapkan.

    3. Coba tambahkan beberapa turunan lagi ke node messages ({"original":"any text"}). Setiap kali Anda menambahkan record baru, ekstensi seharusnya menambahkan kolom uppercase yang berisi konten berhuruf besar dari kolom original.

Anda sekarang memiliki ekstensi lengkap, namun sederhana, yang beroperasi di instance RTDB. Di bagian selanjutnya, Anda akan menyempurnakan ekstensi ini dengan beberapa fitur tambahan. Kemudian, Anda akan membuat ekstensi tersebut siap untuk didistribusikan kepada orang lain, dan pada akhirnya, mempelajari cara memublikasikan ekstensi di Extensions Hub.

5. Mendeklarasikan API dan peran

Kepada setiap instance ekstensi yang diinstal, Firebase memberi akses terbatas ke project dan datanya menggunakan akun layanan per instance. Setiap akun memiliki kumpulan izin minimum yang diperlukan untuk beroperasi. Karena alasan ini, Anda harus secara eksplisit mendeklarasikan peran IAM yang diperlukan ekstensi Anda; saat pengguna menginstal ekstensi Anda, Firebase akan membuat akun layanan dengan peran yang diberikan tersebut dan menggunakannya untuk menjalankan ekstensi.

Anda tidak perlu mendeklarasikan peran untuk memicu peristiwa produk, tetapi Anda perlu mendeklarasikan peran untuk berinteraksi dengannya. Karena fungsi yang Anda tambahkan pada langkah terakhir menulis ke Realtime Database, Anda perlu menambahkan deklarasi berikut ke extension.yaml:

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

Demikian pula, Anda mendeklarasikan Google API yang digunakan ekstensi di kolom apis. Saat menginstal ekstensi Anda, pengguna akan ditanya apakah ingin mengaktifkan API tersebut secara otomatis untuk project mereka atau tidak. Hal ini biasanya hanya diperlukan untuk Google API non-Firebase, dan tidak diperlukan untuk panduan ini.

6. Menentukan parameter yang dapat dikonfigurasi oleh pengguna

Fungsi yang Anda buat dalam dua langkah terakhir memantau lokasi RTDB tertentu untuk pesan masuk. Terkadang, memantau lokasi tertentu memang sesuai dengan keinginan Anda, misalnya saat ekstensi beroperasi di struktur database yang digunakan secara eksklusif untuk ekstensi Anda. Namun, sering kali Anda perlu menjadikan nilai-nilai ini dapat dikonfigurasi oleh pengguna yang menginstal ekstensi Anda dalam project mereka. Dengan cara ini, pengguna dapat menggunakan ekstensi Anda untuk menyiapkan database mereka yang sudah ada.

Buat jalur yang dipantau oleh ekstensi untuk mendeteksi pesan baru yang dapat dikonfigurasi pengguna:

  1. Di file extension.yaml, tambahkan bagian 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
    

    Ini menentukan parameter string baru yang harus disiapkan oleh pengguna saat mereka menginstal ekstensi Anda.

  2. Masih di file extension.yaml, kembali ke deklarasi makeuppercase dan ubah kolom resource menjadi berikut:

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

    Token ${param:MESSAGE_PATH} adalah referensi ke parameter yang baru saja Anda tentukan. Saat ekstensi Anda berjalan, token ini akan diganti dengan nilai apa pun yang dikonfigurasi pengguna untuk parameter tersebut, sehingga fungsi makeuppercase akan memproses jalur yang ditentukan pengguna. Anda dapat menggunakan sintaksis ini untuk mereferensikan parameter yang ditentukan pengguna di mana pun di extension.yaml (dan di POSTINSTALL.md—selengkapnya tentang hal tersebut akan dibahas nanti).

  3. Anda juga dapat mengakses parameter yang ditentukan pengguna dari kode fungsi Anda.

    Dalam fungsi yang Anda tulis di bagian terakhir, Anda melakukan hard coding pada jalur untuk memantau perubahan. Ubah definisi pemicu untuk mereferensikan nilai yang ditentukan pengguna:

    functions/index.js

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

    Perhatikan bahwa di Firebase Extensions, perubahan ini hanya untuk keperluan dokumentasi: saat Cloud Function di-deploy sebagai bagian dari ekstensi, fungsi tersebut menggunakan definisi pemicu dari file extension.yaml dan mengabaikan nilai yang ditentukan dalam definisi fungsi. Meskipun demikian, sebaiknya dokumentasikan di kode Anda tempat asal nilai ini.

  4. Anda mungkin merasa kecewa karena melakukan perubahan kode tanpa efek runtime, tetapi pelajaran penting yang perlu diambil adalah Anda dapat mengakses parameter yang ditentukan pengguna dalam kode fungsi Anda dan menggunakannya sebagai nilai biasa dalam logika fungsi. Untuk membuktikan kemampuan ini, tambahkan laporan log berikut untuk menunjukkan bahwa Anda benar-benar mengakses nilai yang ditentukan pengguna:

    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. Biasanya, pengguna akan diminta untuk memberikan nilai parameter saat menginstal ekstensi. Namun, saat Anda menggunakan emulator untuk pengujian dan pengembangan, Anda akan melewatkan proses penginstalan, sehingga Anda memberikan nilai untuk parameter yang ditentukan pengguna menggunakan file env.

    Buka functions/integration-tests/extensions/rtdb-uppercase-messages.env dan ganti definisi GREETING dengan yang berikut ini:

    MESSAGE_PATH=/msgs/{pushId}/original
    

    Perhatikan bahwa jalur di atas berbeda dengan jalur default dan jalur yang Anda tentukan sebelumnya; ini hanya untuk membuktikan kepada diri sendiri bahwa definisi Anda telah diterapkan dengan mencoba ekstensi yang diperbarui.

  6. Sekarang, mulai ulang emulator dan sekali lagi kunjungi UI emulator database.

    Edit node root database, menggunakan jalur yang Anda tentukan di atas:

    • Kolom: msgs
    • Jenis: json
    • Nilai: {"11": {"original": "recipe"}}

    Saat Anda menyimpan perubahan database, fungsi makeuppercase ekstensi akan terpicu seperti sebelumnya, tetapi sekarang fungsi tersebut juga akan mencetak parameter yang ditentukan pengguna ke log konsol.

7. Menyediakan hook peristiwa untuk logika yang ditentukan pengguna

Sebagai penulis ekstensi, Anda telah melihat cara produk Firebase dapat memicu logika yang disediakan ekstensi: pembuatan record baru di Realtime Database akan memicu fungsi makeuppercase Anda. Ekstensi Anda dapat memiliki hubungan yang setara dengan pengguna yang menginstal ekstensi tersebut: ekstensi Anda dapat memicu logika yang ditetapkan oleh pengguna.

Ekstensi dapat menyediakan hook sinkron, hook asinkron, atau keduanya. Hook sinkron memberi pengguna cara untuk melakukan tugas yang memblokir penyelesaian salah satu fungsi ekstensi. Hal ini dapat berguna, misalnya, untuk memberi pengguna cara melakukan pra-pemrosesan kustom sebelum ekstensi melakukan pekerjaannya.

Dalam panduan ini, Anda akan menambahkan hook asinkron ke ekstensi Anda, yang memungkinkan pengguna menentukan langkah pemrosesan mereka sendiri untuk dijalankan setelah ekstensi menulis pesan berhuruf besar ke Realtime Database. Hook asinkron menggunakan Eventarc untuk memicu fungsi yang ditentukan pengguna. Ekstensi mendeklarasikan jenis peristiwa yang dimunculkan, dan saat pengguna menginstal ekstensi, mereka memilih jenis peristiwa yang mereka minati. Jika klien memilih setidaknya satu peristiwa, Firebase akan menyediakan saluran Eventarc untuk ekstensi sebagai bagian dari proses penginstalan. Kemudian, pengguna dapat men-deploy fungsi cloud mereka sendiri yang memproses saluran tersebut dan terpicu saat ekstensi memublikasikan peristiwa baru.

Ikuti langkah-langkah berikut untuk menambahkan hook asinkron:

  1. Dalam file extension.yaml, tambahkan bagian berikut, yang mendeklarasikan satu jenis peristiwa yang dihasilkan oleh ekstensi:

    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.
    

    Jenis peristiwa harus unik secara universal; untuk memastikan keunikannya, selalu beri nama peristiwa menggunakan format berikut: <publisher-id>.<extension-id>.<version>.<description>. (Anda belum memiliki ID penayang, jadi cukup gunakan test-publisher untuk saat ini.)

  2. Di akhir fungsi makeuppercase, tambahkan beberapa kode yang memublikasikan peristiwa dari jenis yang baru saja Anda deklarasikan:

    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,
        },
      });
    

    Kode contoh ini memanfaatkan fakta bahwa variabel lingkungan EVENTARC_CHANNEL hanya ditentukan saat pengguna mengaktifkan setidaknya satu jenis peristiwa. Jika EVENTARC_CHANNEL tidak ditentukan, kode tidak akan mencoba memublikasikan peristiwa apa pun.

    Anda dapat melampirkan informasi tambahan ke peristiwa Eventarc. Dalam contoh di atas, peristiwa memiliki kolom subject yang berisi referensi ke nilai yang baru dibuat, dan payload data yang berisi pesan asli dan pesan berhuruf besar. Fungsi yang ditentukan pengguna yang memicu peristiwa tersebut dapat menggunakan informasi ini.

  3. Biasanya, variabel lingkungan EVENTARC_CHANNEL dan EXT_SELECTED_EVENTS ditentukan berdasarkan opsi yang dipilih pengguna selama penginstalan. Untuk menguji dengan emulator, tentukan variabel-variabel ini secara manual dalam file rtdb-uppercase-messages.env:

    EVENTARC_CHANNEL=locations/us-central1/channels/firebase
    EXT_SELECTED_EVENTS=test-publisher.rtdb-uppercase-messages.v1.complete
    

Pada tahap ini, Anda telah menyelesaikan langkah-langkah yang diperlukan untuk menambahkan hook peristiwa asinkron ke ekstensi.

Untuk mencoba fitur baru yang baru saja Anda terapkan, pada beberapa langkah berikutnya, asumsikan peran pengguna yang menginstal ekstensi:

  1. Dari direktori functions/integration-tests, lakukan inisialisasi project Firebase baru:

    firebase init functions
    

    Saat diminta, tolak untuk menyiapkan project default, pilih JavaScript sebagai bahasa Cloud Functions, lalu instal dependensi yang diperlukan. Project ini mewakili project pengguna, yang telah menginstal ekstensi Anda.

  2. Edit integration-tests/functions/index.js dan tempelkan kode berikut:

    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}!!!`);
      }
    );
    

    Ini adalah contoh fungsi pascapemrosesan yang mungkin ditulis pengguna. Dalam hal ini, fungsi memproses ekstensi untuk memublikasikan peristiwa complete, dan saat terpicu, menambahkan tiga tanda seru ke pesan baru yang ditulis dalam huruf besar.

  3. Mulai ulang emulator. Emulator akan memuat fungsi ekstensi serta fungsi pascapemrosesan yang ditentukan oleh "pengguna".

  4. Buka UI emulator database dan edit node root database, menggunakan jalur yang Anda tentukan di atas:

    • Kolom:msgs
    • Jenis: json
    • Nilai: {"11": {"original": "recipe"}}

    Saat Anda menyimpan perubahan database, fungsi makeuppercase ekstensi dan fungsi extraemphasis pengguna akan terpicu secara berurutan, sehingga kolom upper mendapatkan nilai RECIPE!!!.

8. Menambahkan pengendali peristiwa siklus proses

Ekstensi yang telah Anda tulis sejauh ini memproses pesan saat dibuat. Namun, bagaimana jika pengguna sudah memiliki database pesan saat mereka menginstal ekstensi? Firebase Extensions memiliki fitur yang disebut hook peristiwa siklus proses yang dapat digunakan untuk memicu tindakan saat ekstensi Anda diinstal, diperbarui, atau dikonfigurasi ulang. Di bagian ini, Anda akan menggunakan hook peristiwa siklus proses untuk mengisi ulang database pesan project yang sudah ada dengan pesan berhuruf besar saat pengguna menginstal ekstensi Anda.

Firebase Extensions menggunakan Cloud Tasks untuk menjalankan pengendali peristiwa siklus proses Anda. Anda menentukan pengendali peristiwa menggunakan Cloud Functions; setiap kali instance ekstensi Anda mencapai salah satu peristiwa siklus proses yang didukung, jika Anda telah menentukan pengendali, instance tersebut akan menambahkan pengendali ke antrean Cloud Tasks. Kemudian, Cloud Tasks akan menjalankan pengendali secara asinkron. Saat pengendali peristiwa siklus proses sedang berjalan, Firebase console akan melaporkan kepada pengguna bahwa instance ekstensi memiliki tugas pemrosesan yang sedang dijalankan. Fungsi pengendali dapat melaporkan kembali kepada pengguna terkait status yang sedang berlangsung dan penyelesaian tugas.

Untuk menambahkan pengendali peristiwa siklus proses yang mengisi ulang pesan yang ada, lakukan tindakan berikut:

  1. Tentukan Cloud Function baru yang dipicu oleh peristiwa task queue:

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

    Perhatikan bahwa fungsi tersebut hanya memproses beberapa record sebelum menambahkan dirinya kembali ke task queue. Ini adalah strategi yang biasa digunakan untuk menangani tugas pemrosesan yang tidak dapat diselesaikan dalam periode waktu tunggu Cloud Functions. Karena Anda tidak dapat memprediksi jumlah pesan yang mungkin sudah dimiliki pengguna dalam database mereka saat menginstal ekstensi Anda, strategi ini sangat cocok.

  2. Dalam file extension.yaml, deklarasikan fungsi pengisian ulang sebagai resource ekstensi yang memiliki properti taskQueueTrigger:

    resources:
      - name: makeuppercase
        ...
      - name: backfilldata
        type: firebaseextensions.v1beta.function
        description: >-
          Backfill existing messages with uppercase versions
        properties:
          runtime: "nodejs18"
          taskQueueTrigger: {}
    

    Selanjutnya, deklarasikan fungsi sebagai pengendali untuk peristiwa siklus proses onInstall:

    lifecycleEvents:
      onInstall:
        function: backfilldata
        processingMessage: Uppercasing existing messages
    
  3. Meskipun mengisi ulang pesan yang sudah ada baik untuk dilakukan, ekstensi tetap dapat berfungsi tanpanya. Dalam situasi seperti ini, Anda harus menjadikan pengendali peristiwa siklus proses bersifat opsional.

    Untuk melakukannya, tambahkan parameter baru ke 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
    

    Kemudian, di awal fungsi pengisian ulang, periksa nilai parameter DO_BACKFILL dan keluar lebih awal jika nilai tersebut tidak ditetapkan:

    functions/index.js

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

Dengan perubahan di atas, ekstensi kini akan mengonversi pesan yang sudah ada menjadi berhuruf besar ketika diinstal.

Hingga titik ini, Anda telah menggunakan emulator ekstensi untuk mengembangkan ekstensi dan menguji perubahan yang sedang berlangsung. Namun, emulator ekstensi melewati proses penginstalan. Jadi, untuk menguji pengendali peristiwa onInstall, Anda harus menginstal ekstensi dalam project sungguhan. Hal itu juga bagus karena dengan penambahan fitur isi ulang otomatis ini, ekstensi tutorial kini telah menyelesaikan kode.

9. Men-deploy ke project Firebase sungguhan

Meskipun emulator ekstensi adalah alat yang efektif untuk melakukan iterasi dengan cepat pada ekstensi selama pengembangan, suatu saat Anda akan ingin mencobanya dalam project sungguhan.

Untuk melakukannya, pertama-tama siapkan project baru dengan beberapa layanan diaktifkan:

  1. Di Firebase console, tambahkan project baru.
  2. Upgrade project Anda ke paket Blaze bayar sesuai penggunaan. Cloud Functions for Firebase mengharuskan project Anda untuk memiliki akun penagihan, sehingga Anda juga memerlukan akun penagihan untuk menginstal ekstensi.
  3. Di project baru Anda, aktifkan Real-Time Database.
  4. Karena Anda ingin menguji kemampuan ekstensi untuk mengisi ulang data yang sudah ada saat penginstalan, impor beberapa data sampel ke dalam instance database real-time Anda:
    1. Download beberapa data RTDB bibit.
    2. Pada halaman Real-time Database di Firebase console, klik (more) > Import JSON, lalu pilih file yang baru saja Anda download.
  5. Agar fungsi pengisian ulang dapat menggunakan metode orderByChild, konfigurasikan database untuk mengindeks pesan pada nilai upper:

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

Sekarang, instal ekstensi Anda dari sumber lokal ke project yang baru:

  1. Buat direktori baru untuk project Firebase Anda:

    mkdir ~/extensions-live-test && cd ~/extensions-live-test
    
  2. Lakukan inisialisasi project Firebase di direktori yang berfungsi:

    firebase init database
    

    Saat diminta, pilih project yang baru saja Anda buat.

  3. Instal ekstensi ke project Firebase lokal Anda:

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

    Di sini Anda dapat melihat pengalaman pengguna saat menginstal ekstensi menggunakan alat Firebase CLI. Pastikan untuk memilih "yes" saat alat konfigurasi menanyakan apakah Anda ingin mengisi ulang database yang ada.

    Setelah Anda memilih opsi konfigurasi, Firebase CLI akan menyimpan konfigurasi Anda di direktori extensions dan mencatat lokasi sumber ekstensi di file firebase.json. Secara kolektif, kedua record ini disebut manifes ekstensi. Pengguna dapat menggunakan manifes tersebut untuk menyimpan konfigurasi ekstensi mereka dan men-deploy-nya ke berbagai project.

  4. Deploy konfigurasi ekstensi ke project live Anda:

    firebase deploy --only extensions
    

Jika semuanya berjalan lancar, Firebase CLI akan mengupload ekstensi Anda ke project Anda, lalu menginstalnya. Setelah penginstalan selesai, tugas pengisian ulang akan dijalankan dan, dalam beberapa menit, database Anda akan diperbarui dengan pesan berhuruf besar. Tambahkan beberapa node baru ke database pesan dan pastikan ekstensi juga berfungsi untuk pesan baru.

10. Menulis dokumentasi

Sebelum membagikan ekstensi kepada pengguna, pastikan Anda menyediakan dokumentasi yang memadai agar mereka berhasil.

Saat Anda menginisialisasi project ekstensi, Firebase CLI membuat versi stub dari dokumentasi minimum yang diperlukan. Perbarui file ini agar mencerminkan ekstensi yang telah Anda build secara akurat.

extension.yaml

Anda telah memperbarui file ini saat mengembangkan ekstensi ini, sehingga Anda tidak perlu melakukan pembaruan lagi saat ini.

Namun, jangan abaikan pentingnya dokumentasi yang terdapat dalam file ini. Selain informasi identifikasi penting ekstensi—nama, deskripsi, penulis, lokasi repositori resmi—file extension.yaml berisi dokumentasi yang ditampilkan kepada pengguna untuk setiap resource dan parameter yang dapat dikonfigurasi pengguna. Informasi ini ditampilkan kepada pengguna di Firebase console, Extensions Hub, dan Firebase CLI.

PREINSTALL.md

Dalam file ini, berikan informasi yang diperlukan pengguna sebelum mereka menginstal ekstensi Anda: jelaskan secara singkat tentang fungsi ekstensi, jelaskan prasyarat, dan berikan informasi kepada pengguna tentang implikasi penagihan jika menginstal ekstensi. Jika Anda memiliki situs dengan informasi tambahan, ini juga merupakan tempat yang tepat untuk menautkannya.

Teks file ini ditampilkan kepada pengguna di Extensions Hub dan oleh perintah firebase ext:info.

Berikut adalah contoh file 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

File ini berisi informasi yang berguna bagi pengguna setelah mereka berhasil menginstal ekstensi: misalnya, langkah-langkah penyiapan selanjutnya, contoh cara kerja ekstensi, dan sebagainya.

Konten POSTINSTALL.md ditampilkan di Firebase console setelah ekstensi dikonfigurasi dan diinstal. Anda dapat mereferensikan parameter pengguna dalam file ini dan parameter tersebut akan diganti dengan nilai yang dikonfigurasi.

Berikut adalah contoh file pasca-penginstalan untuk ekstensi tutorial:

### 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

Anda juga harus mendokumentasikan perubahan yang dibuat di antara rilis ekstensi dalam file CHANGELOG.md.

Karena ekstensi contoh belum pernah dipublikasikan sebelumnya, log perubahan hanya memiliki satu entri:

## Version 0.0.1

Initial release of the _Convert messages to upper case_ extension.

README.md

Sebagian besar ekstensi juga menyediakan file readme untuk kepentingan pengguna yang mengunjungi repositori ekstensi. Anda dapat menulis file ini secara manual atau membuat file readme menggunakan perintah ini.

Untuk tujuan panduan ini, lewati penulisan file readme.

Dokumentasi tambahan

Dokumentasi yang dibahas di atas adalah kumpulan dokumentasi minimum yang harus Anda berikan kepada pengguna. Banyak ekstensi memerlukan dokumentasi yang lebih mendetail agar pengguna berhasil menggunakannya. Jika demikian, Anda harus menulis dokumentasi tambahan dan menghostingnya di tempat yang dapat dituju pengguna.

Untuk tujuan panduan ini, lewati penulisan dokumentasi yang lebih lengkap.

11. Memublikasikan di Extensions Hub

Setelah kode ekstensi selesai dan didokumentasikan, Anda siap membagikannya kepada dunia di Extensions Hub. Namun, karena ini hanyalah tutorial, jangan benar-benar melakukannya. Mulailah dan tulis ekstensi Anda sendiri menggunakan hal yang telah Anda pelajari di sini dan di dokumentasi penayang Firebase Extensions lainnya, serta dengan memeriksa sumber ekstensi resmi yang ditulis di Firebase.

Jika Anda sudah siap untuk memublikasikan hasil pekerjaan Anda di Extensions Hub, berikut cara melakukannya:

  1. Jika Anda memublikasikan ekstensi pertama, daftarkan diri sebagai penayang ekstensi. Saat mendaftar sebagai penayang ekstensi, Anda membuat ID penayang yang memudahkan pengguna mengidentifikasi Anda sebagai penulis ekstensi.
  2. Hosting kode sumber ekstensi di lokasi yang dapat diverifikasi secara publik. Jika kode Anda tersedia dari sumber yang dapat diverifikasi, Firebase dapat memublikasikan ekstensi langsung dari lokasi ini. Cara ini membantu memastikan bahwa Anda memublikasikan versi ekstensi yang saat ini dirilis, dan membantu pengguna dengan mengizinkan mereka memeriksa kode yang mereka instal ke dalam project.

    Saat ini, hal itu berarti membuat ekstensi Anda tersedia di repositori GitHub publik.

  3. Upload ekstensi Anda ke Extensions Hub menggunakan perintah firebase ext:dev:upload.

  4. Buka dasbor penayang di Firebase console, temukan ekstensi yang baru saja diupload, lalu klik "Publish to Extensions Hub". Proses ini akan meminta peninjauan dari staf peninjau kami, yang dapat memakan waktu beberapa hari. Jika disetujui, ekstensi tersebut akan dipublikasikan ke Extensions Hub. Jika ditolak, Anda akan menerima pesan yang menjelaskan alasannya; Anda kemudian dapat mengatasi masalah yang dilaporkan dan mengirimkannya kembali untuk ditinjau.