Rozpocznij tworzenie rozszerzenia

Na tej stronie dowiesz się, jak utworzyć proste rozszerzenie Firebase, które możesz zainstalować w swoich projektach lub udostępnić innym. Ten prosty przykład rozszerzenia Firebase będzie sprawdzać wiadomości w Bazie danych czasu rzeczywistego i zapisywać je wielkimi literami.

1. Konfigurowanie środowiska i inicjowanie projektu

Zanim zaczniesz tworzyć rozszerzenie, musisz skonfigurować środowisko kompilacji za pomocą wymaganych narzędzi.

  1. Zainstaluj Node.js w wersji 16 lub nowszej. Jednym ze sposobów zainstalowania węzła jest użycie nvm (lub nvm-windows).

  2. Zainstaluj lub zaktualizuj interfejs wiersza poleceń Firebase do najnowszej wersji. Aby zainstalować lub zaktualizować dane za pomocą npm, uruchom to polecenie:

    npm install -g firebase-tools
    

Teraz zainicjuj nowy projekt rozszerzenia za pomocą interfejsu wiersza poleceń Firebase:

  1. Utwórz katalog dla rozszerzenia i w nim cd:

    mkdir rtdb-uppercase-messages && cd rtdb-uppercase-messages
    
  2. Uruchom polecenie ext:dev:init w interfejsie wiersza poleceń Firebase:

    firebase ext:dev:init
    

    Gdy pojawi się prośba, jako język funkcji wybierz JavaScript (pamiętaj jednak, że przy tworzeniu własnego rozszerzenia możesz też użyć skryptu TypeScript), a gdy pojawi się prośba o zainstalowanie zależności, odpowiedz „Tak”. (Zaakceptuj wartości domyślne w przypadku pozostałych opcji). To polecenie spowoduje skonfigurowanie szkieletowej bazy kodu dla nowego rozszerzenia, na podstawie której możesz zacząć tworzyć swoje rozszerzenie.

2. Wypróbuj przykładowe rozszerzenie za pomocą emulatora

Gdy interfejs wiersza poleceń Firebase zainicjował nowy katalog rozszerzeń, utworzył prostą funkcję przykładową i katalog integration-tests, który zawiera pliki potrzebne do uruchomienia rozszerzenia korzystającego z pakietu emulatora Firebase.

Spróbuj uruchomić przykładowe rozszerzenie w emulatorze:

  1. Przejdź do katalogu integration-tests:

    cd functions/integration-tests
    
  2. Uruchom emulator przy użyciu projektu demonstracyjnego:

    firebase emulators:start --project=demo-test
    

    Emulator wczytuje rozszerzenie do wstępnie zdefiniowanego projektu „dummy” (demo-test). Dotychczas rozszerzenie składa się z jednej funkcji wywoływanej przez HTTP – greetTheWorld, która po uzyskaniu dostępu zwraca komunikat „hello world”.

  3. Gdy emulator wciąż działa, wypróbuj funkcję greetTheWorld rozszerzenia – otwórz adres URL wyświetlony w momencie uruchomienia.

    W przeglądarce wyświetli się komunikat „Witaj świecie z powitania”.

  4. Kod źródłowy tej funkcji znajduje się w katalogu functions rozszerzenia. Otwórz źródło w wybranym edytorze lub IDE:

    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. Gdy emulator jest uruchomiony, automatycznie wczytuje ponownie wszystkie zmiany wprowadzone w kodzie funkcji. Spróbuj wprowadzić niewielką zmianę w funkcji greetTheWorld:

    functions/index.js

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

    Zapisz zmiany. Emulator ponownie załaduje kod i pod adresem URL funkcji zobaczysz zaktualizowane powitanie.

3. Dodaj podstawowe informacje do plikuextension.yaml

Po skonfigurowaniu środowiska programistycznego i uruchomieniu emulatora rozszerzeń możesz zacząć tworzyć własne rozszerzenie.

Na początek zmodyfikuj wstępnie zdefiniowane metadane rozszerzenia, aby odzwierciedlały to rozszerzenie, które chcesz zapisać zamiast greet-the-world. Te metadane są przechowywane w pliku extension.yaml.

  1. Otwórz plik extension.yaml w edytorze i zastąp całą jego zawartość:

    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
    

    Zwróć uwagę na konwencję nazewnictwa w polu name: nazwy oficjalnych rozszerzeń Firebase zawierają prefiks wskazujący główną usługę Firebase, w której działa dane rozszerzenie, oraz opis jego działania. Stosuj tę samą konwencję we własnych rozszerzeniach.

  2. Ponieważ nazwa rozszerzenia się zmieniła, musisz też zaktualizować konfigurację emulatora o nową nazwę:

    1. W functions/integration-tests/firebase.json zmień greet-the-world na rtdb-uppercase-messages.
    2. Zmień nazwę functions/integration-tests/extensions/greet-the-world.env na functions/integration-tests/extensions/rtdb-uppercase-messages.env.

W kodzie rozszerzenia pozostał jeszcze trochę kodu greet-the-world, ale zostaw je na razie. Zmienisz je w kilku kolejnych sekcjach.

4. Napisz funkcję w Cloud Functions i zadeklaruj ją jako zasób rozszerzenia

Teraz możesz zacząć pisać kod. W tym kroku napiszesz funkcję w Cloud Functions, która wykona główne zadanie rozszerzenia, czyli będzie obserwować wiadomości w bazie danych czasu rzeczywistego i konwertować je na wielkie litery.

  1. Otwórz źródło funkcji rozszerzenia (w katalogu functions rozszerzenia) w wybranym edytorze lub IDE. Zamień jego zawartość na taką:

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

    Stara funkcja, którą zastąpiłeś, była funkcją wyzwalaną przez HTTP, która uruchamiała się po uzyskaniu dostępu do punktu końcowego HTTP. Nowa funkcja jest wyzwalana przez zdarzenia bazy danych w czasie rzeczywistym: monitoruje nowe elementy w określonej ścieżce, a gdy taki wykryje, zapisuje z powrotem w bazie danych wersję pisanej wielkimi literami.

    Przy okazji – nowy plik używa składni modułów ECMAScript (import i export) zamiast CommonJS (require). Aby używać modułów ES w Node.js, określ "type": "module" w functions/package.json:

    {
      "name": "rtdb-uppercase-messages",
      "main": "index.js",
      "type": "module",
      …
    }
    
  2. Wszystkie funkcje rozszerzenia muszą być zadeklarowane w pliku extension.yaml. Przykładowe rozszerzenie zadeklarowało greetTheWorld jako jedyną funkcję w Cloud Functions dla rozszerzenia. Po zastąpieniu go elementem makeuppercase musisz też zaktualizować jego deklarację.

    Otwórz extension.yaml i dodaj pole 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. Ponieważ Twoje rozszerzenie używa teraz Bazy danych czasu rzeczywistego jako aktywatora, musisz zaktualizować konfigurację emulatora, aby go uruchamiać równolegle z emulatorem Cloud Functions:

    1. Jeśli emulator nadal działa, zatrzymaj go, naciskając Ctrl+C.

    2. W katalogu functions/integration-tests uruchom to polecenie:

      firebase init emulators
      

      Gdy pojawi się prośba, pomiń konfigurowanie projektu domyślnego, a potem wybierz emulatory funkcji i bazy danych. Zaakceptuj porty domyślne i pozwól narzędziu konfiguracyjnemu pobrać wymagane pliki.

    3. Ponownie uruchom emulator:

      firebase emulators:start --project=demo-test
      
  4. Wypróbuj zaktualizowane rozszerzenie:

    1. Otwórz interfejs emulatora bazy danych, korzystając z linku wyświetlonego w chwili jego uruchomienia.

    2. Edytuj węzeł główny bazy danych:

      • Pole: messages
      • Typ: json
      • Wartość: {"11": {"original": "recipe"}}

      Jeśli wszystko jest skonfigurowane prawidłowo, po zapisaniu zmian w bazie danych funkcja makeuppercase rozszerzenia powinna się uruchomić i dodać rekord podrzędny do wiadomości 11 z treścią "upper": "RECIPE". Sprawdź logi i karty bazy danych w interfejsie emulatora, aby potwierdzić oczekiwane wyniki.

    3. Spróbuj dodać więcej elementów podrzędnych do węzła messages ({"original":"any text"}). Za każdym razem, gdy dodajesz nowy rekord, rozszerzenie powinno dodawać pole uppercase zawierające zawartość pola original pisane wielkimi literami.

Masz teraz pełne, choć proste rozszerzenie, które działa w instancji RTDB. W kolejnych sekcjach to rozszerzenie możesz doprecyzować, dodając kilka dodatkowych funkcji. Następnie przygotujesz rozszerzenie do rozpowszechniania wśród innych osób i na koniec dowiesz się, jak opublikować je w Centrum rozszerzeń.

5. Deklarowanie interfejsów API i ról

Każda instancja zainstalowanego rozszerzenia przyznaje ograniczony dostęp do projektu i jego danych za pomocą konta usługi dla poszczególnych instancji. Każde konto ma minimalny zestaw uprawnień potrzebnych do działania. Z tego powodu musisz wyraźnie zadeklarować wszelkie role uprawnień wymagane przez rozszerzenie. Gdy użytkownik zainstaluje rozszerzenie, Firebase tworzy konto usługi z przypisanymi rolami i wykorzystuje je do uruchomienia rozszerzenia.

Nie musisz deklarować ról, aby aktywować zdarzenia usługi, ale musisz zadeklarować rolę, aby wchodzić z nią w interakcje. Funkcja dodana w ostatnim kroku zapisuje w bazie danych czasu rzeczywistego, więc musisz dodać do extension.yaml tę deklarację:

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

W podobny sposób deklarujesz interfejsy API Google używane przez rozszerzenie w polu apis. Podczas instalowania Twojego rozszerzenia użytkownicy będą pytani, czy chcą automatycznie włączyć te interfejsy API w swoim projekcie. Zwykle jest to wymagane tylko w przypadku interfejsów API Google spoza Firebase, nie jest potrzebne w tym przewodniku.

6. Zdefiniuj parametry konfigurowane przez użytkownika

Funkcja utworzona w ostatnich 2 krokach monitorowała konkretną lokalizację bazy danych czasu rzeczywistego dla wiadomości przychodzących. Czasami lepiej jest obejrzeć konkretną lokalizację, np. gdy rozszerzenie działa na strukturze bazy danych używanej wyłącznie na jego potrzeby. Najczęściej jednak warto umożliwić konfigurowanie tych wartości użytkownikom, którzy instalują Twoje rozszerzenie w swoich projektach. Dzięki temu użytkownicy mogą korzystać z tego rozszerzenia do pracy z dotychczasową konfiguracją bazy danych.

Zezwól użytkownikowi na konfigurowanie ścieżki obserwowanej przez rozszerzenie dla nowych wiadomości:

  1. W pliku extension.yaml dodaj sekcję 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
    

    Definiuje on nowy parametr ciągu znaków, który użytkownicy będą musieli ustawić podczas instalowania rozszerzenia.

  2. W pliku extension.yaml wróć do deklaracji makeuppercase i zmień pole resource na:

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

    Token ${param:MESSAGE_PATH} jest odniesieniem do zdefiniowanego przed chwilą parametru. Po uruchomieniu rozszerzenia ten token zostanie zastąpiony dowolną wartością skonfigurowaną przez użytkownika dla tego parametru. Spowoduje to, że funkcja makeuppercase będzie nasłuchiwać ścieżki podanej przez użytkownika. Możesz jej użyć, aby odwołać się do dowolnego parametru zdefiniowanego przez użytkownika w dowolnym miejscu w polu extension.yaml (i w POSTINSTALL.md – szczegóły w dalszej części tego artykułu).

  3. Z kodu funkcji możesz też uzyskać dostęp do parametrów zdefiniowanych przez użytkownika.

    W funkcji zapisanej w ostatniej sekcji zakodowano na stałe ścieżkę, aby obserwować zmiany. Zmień definicję aktywatora, aby odwoływać się do wartości zdefiniowanej przez użytkownika:

    functions/index.js

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

    Pamiętaj, że w rozszerzeniach w Firebase ta zmiana ma na celu wyłącznie dokumentację: gdy funkcja w Cloud Functions jest wdrożona jako część rozszerzenia, używa definicji aktywatora z pliku extension.yaml i ignoruje wartość określoną w definicji funkcji. Dobrze jest jednak udokumentować w kodzie, skąd pochodzi ta wartość.

  4. Zmiana kodu, która nie wpływa na środowisko wykonawcze, może być rozczarowana, ale należy wyciągnąć wnioski, że można uzyskać dostęp do dowolnego parametru zdefiniowanego przez użytkownika w kodzie funkcji i użyć go jako wartości zwykłej w logice funkcji. Uznając tę funkcję, dodaj poniższe polecenie logu, aby udowodnić, że uzyskujesz dostęp do wartości zdefiniowanej przez użytkownika:

    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. Zwykle podczas instalowania rozszerzenia użytkownicy są proszeni o podanie wartości parametrów. Jeśli używasz emulatora do testowania i programowania, pomijasz proces instalacji i zamiast tego podajesz wartości parametrów zdefiniowanych przez użytkownika w pliku env.

    Otwórz functions/integration-tests/extensions/rtdb-uppercase-messages.env i zastąp definicję GREETING taką:

    MESSAGE_PATH=/msgs/{pushId}/original
    

    Zwróć uwagę, że powyższa ścieżka różni się od ścieżki domyślnej i zdefiniowanej wcześniej. Ma to na celu sprawdzenie, czy przy użyciu zaktualizowanego rozszerzenia masz pewność, że definicja działa.

  6. Teraz uruchom emulator ponownie i jeszcze raz otwórz jego interfejs.

    Zmodyfikuj węzeł główny bazy danych, stosując ścieżkę określoną powyżej:

    • Pole: msgs
    • Typ: json
    • Wartość: {"11": {"original": "recipe"}}

    Gdy zapiszesz zmiany w bazie danych, funkcja makeuppercase rozszerzenia powinna aktywować się tak jak wcześniej, ale teraz powinna też wyświetlić w dzienniku konsoli parametr zdefiniowany przez użytkownika.

7. Udostępnij punkty zaczepienia zdarzeń na potrzeby logiki zdefiniowanej przez użytkownika

Jako autor rozszerzenia wiesz już, jak usługa Firebase wyzwala logikę zapewnianą przez rozszerzenie: tworzenie nowych rekordów w bazie danych czasu rzeczywistego uruchamia funkcję makeuppercase. Rozszerzenie może być powiązane analogicznie z użytkownikami, którzy je zainstalowali – rozszerzenie może wywoływać logikę zdefiniowaną przez użytkownika.

Rozszerzenie może udostępniać synchroniczne elementy zaczepienia, asynchroniczne lub oba te elementy. Synchroniczne punkty zaczepienia umożliwiają użytkownikom wykonywanie zadań, które blokują realizację jednej z funkcji rozszerzenia. Dzięki temu można na przykład dać użytkownikom możliwość przeprowadzenia niestandardowego wstępnego przetwarzania danych, zanim rozszerzenie zacznie działać.

Z tego przewodnika dowiesz się, jak dodać do rozszerzenia asynchroniczny punkt zaczepienia, który pozwoli użytkownikom określić własne etapy przetwarzania, które będą uruchamiane po zapisaniu przez rozszerzenie w bazie danych czasu rzeczywistego zapisanej wielkimi literami. Punkty zaczepienia asynchroniczne używają Eventarc do aktywowania funkcji zdefiniowanych przez użytkownika. Rozszerzenia deklarują typy emitowanych zdarzeń, a gdy użytkownicy je instalują, wybierają te typy zdarzeń, które ich interesują. Jeśli wybiorą co najmniej 1 zdarzenie, Firebase udostępni kanał Eventarc na potrzeby rozszerzenia w ramach procesu instalacji. Użytkownicy mogą następnie wdrażać własne funkcje w Cloud Functions, które nasłuchują w tym kanale i aktywują, gdy rozszerzenie publikuje nowe zdarzenia.

Aby dodać asynchroniczny punkt zaczepienia, wykonaj te czynności:

  1. W pliku extension.yaml dodaj następującą sekcję, która deklaruje jeden typ zdarzenia generowanego przez rozszerzenie:

    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.
    

    Typy zdarzeń muszą być uniwersalnie unikalne. Aby zapewnić niepowtarzalność, zawsze nadawaj swoim zdarzeniom nazwę w tym formacie: <publisher-id>.<extension-id>.<version>.<description>. (Nie masz jeszcze identyfikatora wydawcy, więc na razie użyj identyfikatora test-publisher).

  2. Na końcu funkcji makeuppercase dodaj kod publikujący zdarzenie właśnie zadeklarowanego typu:

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

    Ten przykładowy kod korzysta z faktu, że zmienna środowiskowa EVENTARC_CHANNEL jest definiowana tylko wtedy, gdy użytkownik włączył co najmniej 1 typ zdarzenia. Jeśli nie określono typu EVENTARC_CHANNEL, kod nie próbuje publikować żadnych zdarzeń.

    Do zdarzenia Eventarc możesz dołączyć dodatkowe informacje. W przykładzie powyżej zdarzenie ma pole subject, które zawiera odwołanie do nowo utworzonej wartości, oraz ładunek data, który zawiera komunikaty oryginalne i pisane wielkimi literami. Z tych informacji mogą korzystać funkcje zdefiniowane przez użytkownika, które aktywują zdarzenie.

  3. Normalnie zmienne środowiskowe EVENTARC_CHANNEL i EXT_SELECTED_EVENTS są definiowane na podstawie opcji wybranych przez użytkownika podczas instalacji. Aby przeprowadzić testy za pomocą emulatora, ręcznie zdefiniuj te zmienne w pliku rtdb-uppercase-messages.env:

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

Na tym etapie zostały już wykonane czynności niezbędne do dodania do rozszerzenia asynchronicznego punktu zaczepienia zdarzeń.

Aby wypróbować tę nową funkcję, którą właśnie zaimplementowaliśmy, w kilku kolejnych krokach przyjmij rolę użytkownika, który instaluje rozszerzenie:

  1. W katalogu functions/integration-tests zainicjuj nowy projekt Firebase:

    firebase init functions
    

    Gdy pojawi się prośba, odrzuć prośbę o skonfigurowanie projektu domyślnego, wybierz JavaScript jako język Cloud Functions i zainstaluj wymagane zależności. Ten projekt reprezentuje projekt użytkownika, w którym zainstalowano Twoje rozszerzenie.

  2. Edytuj integration-tests/functions/index.js i wklej ten kod:

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

    To jest przykład funkcji przetwarzania końcowego, którą może utworzyć użytkownik. W takim przypadku funkcja nasłuchuje, aż rozszerzenie opublikuje zdarzenie complete. Po uruchomieniu dodaje 3 wykrzykniki do wiadomości pisanej wielkimi literami.

  3. Ponownie uruchom emulator. Emulator wczyta funkcje rozszerzenia oraz funkcję przetwarzania końcowego zdefiniowanej przez użytkownika.

  4. Otwórz interfejs emulatora bazy danych i zmodyfikuj węzeł główny bazy danych, korzystając ze ścieżki określonej powyżej:

    • Pole:msgs
    • Typ: json
    • Wartość: {"11": {"original": "recipe"}}

    Gdy zapiszesz zmiany w bazie danych, funkcja makeuppercase rozszerzenia i funkcja extraemphasis użytkownika powinny aktywować się w sekwencji, co spowoduje, że pole upper otrzyma wartość RECIPE!!!.

8. Dodaj moduły obsługi zdarzeń cyklu życia

Napisane do tej pory rozszerzenie przetwarza wiadomości od razu po ich utworzeniu. Co jednak w sytuacji, gdy użytkownicy mają już bazę danych wiadomości podczas instalowania rozszerzenia? Rozszerzenia w Firebase mają funkcję o nazwie punkty zaczepienia zdarzeń cyklu życia, dzięki którym możesz wywoływać działania w przypadku instalacji, aktualizacji lub ponownej konfiguracji rozszerzenia. W tej sekcji użyjesz punktów zaczepienia zdarzeń cyklu życia, aby uzupełnić istniejącą bazę danych wiadomości projektu wielkimi literami wiadomości, gdy użytkownik zainstaluje rozszerzenie.

Rozszerzenia w Firebase używają Cloud Tasks do uruchamiania modułów obsługi zdarzeń cyklu życia. Moduły obsługi zdarzeń definiuje się za pomocą Cloud Functions. Gdy instancja rozszerzenia dotrze do jednego z obsługiwanych zdarzeń cyklu życia, to jeśli masz zdefiniowany moduł obsługi, zostanie on dodany do kolejki Cloud Tasks. Cloud Tasks asynchronicznie wykona moduł obsługi. Gdy moduł obsługi zdarzeń cyklu życia jest uruchomiony, konsola Firebase zgłasza użytkownikowi, że w instancji rozszerzenia trwa zadanie przetwarzania. To Twoja funkcja obsługi przesyłania raportów o stanie trwającego zadania i ukończeniu zadania z powrotem użytkownikowi.

Aby dodać moduł obsługi zdarzeń cyklu życia, który uzupełnia istniejące wiadomości, wykonaj te czynności:

  1. Zdefiniuj nową funkcję w Cloud Functions, która będzie aktywowana przez zdarzenia w kolejce zadań:

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

    Zwróć uwagę, że przed dodaniem swojej funkcji z powrotem do kolejki zadań funkcja przetwarza tylko kilka rekordów. Jest to strategia często wykorzystywana do obsługi zadań przetwarzania, które nie mogą zostać ukończone w ramach limitu czasu funkcji w Cloud Functions. Nie można przewidzieć, ile wiadomości użytkownik może mieć w bazie danych po zainstalowaniu rozszerzenia, więc ta strategia jest dobrym rozwiązaniem.

  2. W pliku extension.yaml zadeklaruj funkcję uzupełniania jako zasób rozszerzenia z właściwością taskQueueTrigger:

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

    Następnie zadeklaruj funkcję jako moduł obsługi zdarzenia cyklu życia onInstall:

    lifecycleEvents:
      onInstall:
        function: backfilldata
        processingMessage: Uppercasing existing messages
    
  3. Uzupełnianie istniejących wiadomości jest przydatne, ale bez tego rozszerzenia rozszerzenie może nadal działać. W takich sytuacjach uruchamianie modułów obsługi zdarzeń cyklu życia powinno być opcjonalne.

    Aby to zrobić, dodaj do extension.yaml nowy parametr:

    - 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
    

    Następnie na początku funkcji uzupełniania sprawdź wartość parametru DO_BACKFILL i wyjdź wcześniej, jeśli nie jest ustawiona:

    functions/index.js

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

Po wprowadzeniu powyższych zmian rozszerzenie po zainstalowaniu konwertuje istniejące wiadomości na wielkie litery.

Do tej pory udało Ci się utworzyć rozszerzenie i przetestować bieżące zmiany za pomocą emulatora rozszerzenia. Emulator rozszerzenia pomija jednak proces instalacji, więc aby przetestować moduł obsługi zdarzeń onInstall, musisz zainstalować to rozszerzenie w prawdziwym projekcie. To równie dobrze, ponieważ po dodaniu tej funkcji automatycznego uzupełniania rozszerzenie samouczka jest już w pełni gotowe.

9. Wdróż w prawdziwym projekcie Firebase

Emulator rozszerzeń to świetne narzędzie do szybkiej iteracji rozszerzenia w trakcie programowania, ale w pewnym momencie warto go wypróbować w prawdziwym projekcie.

Aby to zrobić, najpierw skonfiguruj nowy projekt z włączonymi usługami:

  1. W konsoli Firebase dodaj nowy projekt.
  2. Przejdź na abonament Blaze z płatnościami według wykorzystania. Usługa Cloud Functions dla Firebase wymaga, aby projekt miał konto rozliczeniowe. Konto rozliczeniowe jest też potrzebne do zainstalowania rozszerzenia.
  3. W nowym projekcie włącz Bazę danych czasu rzeczywistego.
  4. Chcesz sprawdzić, czy rozszerzenie może uzupełniać dotychczasowe dane o instalacji, dlatego zaimportuj kilka przykładowych danych do instancji bazy danych w czasie rzeczywistym:
    1. Pobierz wyjściowe dane RTDB.
    2. Na stronie Baza danych czasu rzeczywistego w konsoli Firebase kliknij (więcej) > Importuj plik JSON i wybierz pobrany przed chwilą plik.
  5. Aby umożliwić funkcji uzupełniania korzystanie z metody orderByChild, skonfiguruj bazę danych do indeksowania wiadomości o wartości upper:

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

Teraz zainstaluj rozszerzenie ze źródła lokalnego w nowym projekcie:

  1. Utwórz nowy katalog dla projektu Firebase:

    mkdir ~/extensions-live-test && cd ~/extensions-live-test
    
  2. Zainicjuj projekt Firebase w katalogu roboczym:

    firebase init database
    

    Gdy pojawi się odpowiedni komunikat, wybierz utworzony przed chwilą projekt.

  3. Zainstaluj rozszerzenie w lokalnym projekcie Firebase:

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

    Dowiesz się z niego, jak wyglądają wrażenia użytkowników podczas instalowania rozszerzenia za pomocą narzędzia wiersza poleceń Firebase. Gdy narzędzie konfiguracji zapyta, czy chcesz uzupełnić istniejącą bazę danych, wybierz „Tak”.

    Gdy wybierzesz opcje konfiguracji, interfejs wiersza poleceń Firebase zapisze konfigurację w katalogu extensions i zarejestruje lokalizację źródłową rozszerzenia w pliku firebase.json. Łącznie te 2 rekordy są nazywane plikiem manifestu rozszerzeń. Użytkownicy mogą za pomocą pliku manifestu zapisać konfigurację rozszerzeń i wdrożyć go w różnych projektach.

  4. Wdróż konfigurację rozszerzenia w projekcie wersji opublikowanej:

    firebase deploy --only extensions
    

Jeśli wszystko pójdzie dobrze, interfejs wiersza poleceń Firebase powinien przesłać rozszerzenie do projektu i je zainstalować. Po zakończeniu instalacji zostanie uruchomione zadanie uzupełniania, a w ciągu kilku minut Twoja baza danych zostanie zaktualizowana o wielkie litery. Dodaj nowe węzły do bazy danych wiadomości i upewnij się, że rozszerzenie obsługuje nowe wiadomości.

10. Tworzenie dokumentacji

Zanim udostępnisz użytkownikom swoje rozszerzenie, upewnij się, że zapewniasz im wystarczającą dokumentację, aby mogli skutecznie korzystać z tej funkcji.

Po zainicjowaniu projektu rozszerzenia interfejs wiersza poleceń Firebase utworzył wycinki wersji minimalnej wymaganej dokumentacji. Zaktualizuj te pliki, by dokładnie odzwierciedlały utworzone rozszerzenie.

file.yaml

Ten plik był już aktualizowany w miarę tworzenia rozszerzenia, więc nie musisz teraz wprowadzać żadnych dodatkowych zmian.

Nie zapominaj jednak o znaczeniu dokumentacji zawartej w tym pliku. Oprócz kluczowych informacji identyfikujących rozszerzenie (nazwy, opisu, autora i oficjalnej lokalizacji repozytorium) plik extension.yaml zawiera dokumentację dla użytkownika dotyczącą każdego zasobu i parametru, który można skonfigurować przez użytkownika. Te informacje są wyświetlane użytkownikom w konsoli Firebase, Centrum rozszerzeń i interfejsie wiersza poleceń Firebase.

PREINSTALL.md

W tym pliku należy podać informacje, których użytkownik potrzebuje przed zainstalowaniem rozszerzenia: krótko opisać jego działanie, wyjaśnić wszelkie wymagania wstępne oraz poinformować użytkownika o konsekwencjach instalacji rozszerzenia na rozliczeniach. Jeśli masz witrynę z dodatkowymi informacjami, także możesz do niej dodać link.

Tekst tego pliku jest wyświetlany użytkownikowi w Centrum rozszerzeń i poleceniu firebase ext:info.

Oto przykład pliku 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

Ten plik zawiera informacje przydatne dla użytkowników po zainstalowaniu rozszerzenia, na przykład kolejne kroki konfiguracji, przykład działania rozszerzenia itp.

Zawartość polecenia POSTINSTALL.md jest wyświetlana w konsoli Firebase po skonfigurowaniu i zainstalowaniu rozszerzenia. W tym pliku możesz odwoływać się do parametrów użytkownika, które zostaną zastąpione skonfigurowanymi wartościami.

Oto przykładowy plik używany po zainstalowaniu rozszerzenia samouczka:

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

Zmiany, które wprowadzasz między wersjami rozszerzenia, musisz też udokumentować w pliku CHANGELOG.md.

Ponieważ przykładowe rozszerzenie nie zostało wcześniej opublikowane, historia zmian zawiera tylko 1 wpis:

## Version 0.0.1

Initial release of the _Convert messages to upper case_ extension.

README.md

Większość rozszerzeń udostępnia też plik Readme dostępny dla użytkowników odwiedzających repozytorium rozszerzenia. Możesz napisać ten plik ręcznie lub wygenerować plik z możliwością odczytu za pomocą polecenia.

Na potrzeby tego przewodnika pomiń pisanie pliku Readme.

Dodatkowa dokumentacja

Dokumentacja omówiona powyżej to minimalny zestaw dokumentów, który należy udostępnić użytkownikom. Wiele rozszerzeń wymaga bardziej szczegółowej dokumentacji, aby użytkownicy mogli z nich korzystać. W takim przypadku należy utworzyć dodatkową dokumentację i przechowywać ją w miejscu, do którego będą mogli kierować użytkowników.

W tym przewodniku pomiń pisanie bardziej szczegółowej dokumentacji.

11. Opublikuj w Centrum rozszerzeń

Po utworzeniu i udokumentowaniu kodu rozszerzenie możesz je udostępnić całemu światu w Centrum rozszerzeń. To tylko samouczek, więc nie rób tego. Zacznij tworzyć własne rozszerzenie, korzystając z tego, czego się nauczyliśmy, tutaj i w pozostałej części dokumentacji dla wydawców Rozszerzeń w Firebase. Sprawdź źródło oficjalnego, napisanego przez Firebase rozszerzenia.

Aby opublikować swoje dzieło w Centrum rozszerzeń, wykonaj te czynności:

  1. Jeśli publikujesz pierwsze rozszerzenie, zarejestruj się jako wydawca rozszerzenia. Rejestrując się jako wydawca rozszerzeń, możesz utworzyć identyfikator wydawcy, który pozwoli użytkownikom szybko zidentyfikować Cię jako autora rozszerzeń.
  2. Przechowuj kod źródłowy rozszerzenia w miejscu, które można publicznie zweryfikować. Gdy kod jest dostępny z weryfikowanego źródła, Firebase może opublikować rozszerzenie bezpośrednio z tej lokalizacji. Dzięki temu będziesz mieć pewność, że opublikujesz aktualnie dostępną wersję rozszerzenia, a użytkownikom ułatwi sprawdzenie kodu, który instalują w swoich projektach.

    Obecnie oznacza to udostępnienie rozszerzenia w publicznym repozytorium GitHub.

  3. Prześlij rozszerzenie do Centrum rozszerzeń za pomocą polecenia firebase ext:dev:upload.

  4. Otwórz panel wydawcy w konsoli Firebase, znajdź przesłane rozszerzenie i kliknij „Opublikuj w Centrum rozszerzeń”. Poprosimy nasz zespół sprawdzający, co może potrwać kilka dni. Po zatwierdzeniu rozszerzenie zostanie opublikowane w Centrum rozszerzeń. W przypadku odrzucenia otrzymasz wiadomość z wyjaśnieniem przyczyny. Możesz wtedy rozwiązać zgłoszone problemy i ponownie przesłać zgłoszenie do sprawdzenia.