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ć bazę danych Realtime Database pod kątem wiadomości i przekształcać je na wielkie litery.

1. Konfigurowanie środowiska i inicjowanie projektu

Zanim zaczniesz kompilować rozszerzenie, musisz skonfigurować środowisko kompilacji z wymaganymi narzędziami.

  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ć za pomocą npm, uruchom to polecenie:

    npm install -g firebase-tools
    

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

  1. Utwórz katalog dla rozszerzenia i cd:

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

    firebase ext:dev:init
    

    Gdy pojawi się odpowiedni komunikat, wybierz JavaScript jako język funkcji (pamiętaj, że podczas tworzenia własnego rozszerzenia możesz też użyć TypeScript). Gdy pojawi się pytanie o instalację zależności, odpowiedz „tak”. (Zaakceptuj wartości domyślne w przypadku pozostałych opcji). To polecenie skonfiguruje szkielet bazy kodu nowego rozszerzenia, od którego możesz rozpocząć tworzenie rozszerzenia.

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

Gdy interfejs wiersza poleceń Firebase zainicjował nowy katalog rozszerzeń, utworzył prostą przykładową funkcję i katalog integration-tests, które zawierają pliki niezbędne 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 z projektem demonstracyjnym:

    firebase emulators:start --project=demo-test
    

    Emulator wczytuje rozszerzenie do wstępnie zdefiniowanego „fikcyjnego” projektu (demo-test). Dotychczasowe rozszerzenie składa się z pojedynczej funkcji wywoływanej przez HTTP (greetTheWorld), która po wywołaniu zwraca wiadomość „hello world”.

  3. Gdy emulator nadal działa, wypróbuj funkcję greetTheWorld rozszerzenia, odwiedzając adres URL wydrukowany po jego uruchomieniu.

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

  4. Kod źródłowy tej funkcji znajduje się w katalogu functionsrozszerzenia. Otwórz kod źródłowy w wybranym edytorze lub środowisku IDE:

    functions/index.js

    const functions = require("firebase-functions/v1");
    
    exports.greetTheWorld = functions.https.onRequest((req, res) => {
      // Here we reference a user-provided parameter
      // (its value is provided by the user during installation)
      const consumerProvidedGreeting = process.env.GREETING;
    
      // And here we reference an auto-populated parameter
      // (its value is provided by Firebase after installation)
      const instanceId = process.env.EXT_INSTANCE_ID;
    
      const greeting = `${consumerProvidedGreeting} World from ${instanceId}`;
    
      res.send(greeting);
    });
    
  5. 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 wczyta kod, a teraz, gdy otworzysz adres URL funkcji, zobaczysz zaktualizowane powitanie.

3. Dodawanie podstawowych informacji do pliku extension.yaml

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

Jako pierwszy krok edytuj zdefiniowane wstępnie metadane rozszerzenia, aby odzwierciedlały rozszerzenie, które chcesz napisać 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ść tym fragmentem kodu:

    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 używaną w polu name: oficjalne rozszerzenia Firebase mają nazwy z prefiksem wskazującym główny produkt Firebase, z którym działają, a następnie opis działania rozszerzenia. Stosuj tę samą konwencję we własnych rozszerzeniach.

  2. Ponieważ zmieniłeś nazwę rozszerzenia, musisz też zaktualizować konfigurację emulatora, podając nową nazwę:

    1. W pliku functions/integration-tests/firebase.json zmień wartość 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 nadal znajdują się pozostałości po rozszerzeniu greet-the-world, ale na razie nie zmieniaj ich. Zaktualizujesz je w kolejnych sekcjach.

4. Pisanie funkcji w Cloud Functions i deklarowanie jej jako zasobu 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 kod źródłowy funkcji rozszerzenia (w katalogu functions rozszerzenia) w wybranym edytorze lub środowisku 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ą została zastąpiona, była funkcją aktywowaną przez HTTP, która działała po uzyskaniu dostępu do punktu końcowego HTTP. Nowa funkcja jest wywoływana przez zdarzenia bazy danych w czasie rzeczywistym: sprawdza ona nowe elementy na określonym ścieżce i gdy wykryje nowy element, zapisuje z powrotem w bazie danych jego wartość w wersji z dużymi 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 deklaruje greetTheWorld jako jedyną funkcję rozszerzenia w usłudze Cloud Functions. Teraz, gdy zastąpisz ją funkcją makeuppercase, musisz też zaktualizować jej 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 emulowany system nadal działa, zatrzymaj go, naciskając Ctrl+C.

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

      firebase init emulators
      

      Gdy pojawi się taka prośba, pomiń konfigurowanie projektu domyślnego, a potem wybierz emulatory funkcji i bazy danych. Zaakceptuj domyślne porty i zezwól narzędziu konfiguracyjnemu na pobranie wymaganych plików.

    3. Uruchom ponownie emulator:

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

    1. Otwórz interfejs emulatora bazy danych, korzystając z linku wydrukowanego przez emulator podczas jego uruchamiania.

    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". Aby sprawdzić oczekiwane wyniki, zajrzyj do logów i kart bazy danych w interfejsie emulatora.

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

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 dystrybucji, a na koniec dowiesz się, jak opublikować je w Centrum rozszerzeń.

5. Deklarowanie interfejsów API i ról

Firebase przyznaje każdej instancji zainstalowanego rozszerzenia ograniczony dostęp do projektu i jego danych za pomocą konta usługi na potrzeby danej 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 aby wchodzić z nią w interakcje, musisz zadeklarować rolę. Funkcja dodana w ostatnim kroku zapisuje dane w danych w czasie rzeczywistym, więc musisz dodać do funkcji extension.yaml tę deklarację:

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

Podobnie w polu apis deklarujesz interfejsy API Google, których używa rozszerzenie. Podczas instalowania Twojego rozszerzenia użytkownicy będą pytani, czy chcą automatycznie włączyć te interfejsy API w swoim projekcie. Jest to zwykle konieczne tylko w przypadku interfejsów API Google innych niż Firebase. Nie jest to wymagane w tym przewodniku.

6. Definiowanie parametrów konfigurowalnych przez użytkownika

Funkcja utworzona w ostatnich 2 krokach monitorowała konkretną lokalizację Bazy danych czasu rzeczywistego dla wiadomości przychodzących. Czasami obserwowanie określonej lokalizacji jest tym, czego naprawdę potrzebujesz, np. gdy rozszerzenie działa na podstawie struktury bazy danych, której używasz wyłącznie do rozszerzenia. W większości przypadków warto jednak umożliwić użytkownikom instalującym rozszerzenie w swoich projektach konfigurowanie tych wartości. Dzięki temu użytkownicy będą mogli korzystać z Twojego rozszerzenia w połączeniu 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
    

    Określa 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 zmodyfikuj pole resource w ten sposób:

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

    Token ${param:MESSAGE_PATH} jest odniesieniem do zdefiniowanego przed chwilą parametru. Gdy rozszerzenie zostanie uruchomione, ten token zostanie zastąpiony wartością skonfigurowaną przez użytkownika dla tego parametru, co spowoduje, że funkcja makeuppercase będzie nasłuchiwać ścieżki określonej 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 napisanej w poprzedniej sekcji ścieżka do pliku watch została zakodowana na stałe. Zmień definicję reguły, aby odwoływała 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 przypadku Rozszerzeń w Firebase ta zmiana ma na celu wyłącznie ułatwienie dokumentacji: gdy funkcja Cloud Functions jest wdrażana 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ć rozczarowująca, 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 użytkownicy otrzymują prośbę o podanie wartości parametrów podczas instalowania rozszerzenia. 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 plik functions/integration-tests/extensions/rtdb-uppercase-messages.env i zamień definicję GREETING na tę:

    MESSAGE_PATH=/msgs/{pushId}/original
    

    Zwróć uwagę, że powyższa ścieżka różni się od ścieżki domyślnej i ścieżki zdefiniowanej wcześniej. Ma to na celu potwierdzenie, że gdy spróbujesz użyć zaktualizowanego rozszerzenia, Twoja definicja zacznie obowiązywać.

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

    Zmień węzeł względny bazy danych, korzystając ze ścieżki zdefiniowanej powyżej:

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

    Gdy zapiszesz zmiany w bazie danych, funkcja makeuppercase rozszerzenia powinna zostać uruchomiona tak jak wcześniej, ale teraz powinna też wydrukować parametr zdefiniowany przez użytkownika w logu konsoli.

7. Dostarczanie uchwytów zdarzeń na potrzeby logiki zdefiniowanej przez użytkownika

Jako autor rozszerzenia wiesz już, jak usługa Firebase może wywołać logikę udostępnioną przez rozszerzenie: tworzenie nowych rekordów w Baza danych czasu rzeczywistym powoduje wywołanie funkcji makeuppercase. Twoje rozszerzenie może mieć analogiczne relacje z użytkownikami, którzy je zainstalowali: rozszerzenie może aktywować logikę zdefiniowaną przez użytkownika.

Rozszerzenie może zawierać punkty synchronizacji, punkty asynchroniczne lub oba te elementy. Zaczepy synchroniczne umożliwiają użytkownikom wykonywanie zadań, które blokują wykonanie 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ć.

W tym przewodniku dowiesz się, jak dodać do rozszerzenia asynchroniczny element wywołujący, który umożliwi użytkownikom definiowanie własnych kroków przetwarzania, które mają być wykonywane po zapisaniu przez rozszerzenie wiadomości w wielkich literach w Realtime Database. Punkty zaczepienia asynchroniczne używają Eventarc do aktywowania funkcji zdefiniowanych przez użytkownika. Rozszerzenia deklarują typy emitowanych zdarzeń, a użytkownicy, gdy je instalują, wybierają te, które ich interesują. Jeśli użytkownik wybierze co najmniej 1 zdarzenie, Firebase zarezerwuje kanał Eventarc dla 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 element wywoławczy:

  1. W pliku extension.yaml dodaj tę sekcję, która deklaruje jeden typ zdarzenia emitowany 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ć unikalne. Aby zapewnić ich unikalność, zawsze nadawaj im nazwy w tym formacie: <publisher-id>.<extension-id>.<version>.<description>. (nie masz jeszcze identyfikatora wydawcy, więc na razie użyj wartości test-publisher).

  2. Na końcu funkcji makeuppercase dodaj kod, który publikuje zdarzenie o typie, który właśnie zadeklarowałeś:

    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 tego, że zmienna środowiskowa EVENTARC_CHANNEL jest zdefiniowana tylko wtedy, gdy użytkownik włączył co najmniej 1 typ zdarzenia. Jeśli zmienna EVENTARC_CHANNEL nie jest zdefiniowana, kod nie próbuje publikować żadnych zdarzeń.

    Do wydarzenia 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. Funkcje zdefiniowane przez użytkownika, które uruchamiają zdarzenie, mogą korzystać z tych informacji.

  3. Zwykle zmienne środowiska EVENTARC_CHANNELEXT_SELECTED_EVENTS są definiowane na podstawie opcji wybranych przez użytkownika podczas instalacji. Aby przetestować emulator, 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
    

W tym momencie wykonałeś/wykonałaś już czynności potrzebne do dodania do rozszerzenia asynchronicznego punktu wywołania zdarzenia.

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ę taka prośba, zrezygnuj z konfigurowania 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. Otwórz plik 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 tym przypadku funkcja nasłuchuje, czy rozszerzenie opublikowało zdarzenie complete, a gdy tak się stanie, dodaje do wiadomości, która zostanie zapisana wielkimi literami, 3 znaki wykrzyknika.

  3. Uruchom ponownie emulator. Emulator wczyta funkcje rozszerzenia, a także funkcję przetwarzania wtórnego zdefiniowaną przez „użytkownika”.

  4. Otwórz interfejs emulatora bazy danych i edytuj węzeł główny bazy danych, korzystając ze zdefiniowanej wcześniej ścieżki:

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

    Gdy zapiszesz zmiany w bazie danych, funkcja makeuppercase rozszerzenia i funkcja extraemphasis użytkownika powinny być wywoływane kolejno, co spowoduje, że pole upper otrzyma wartość RECIPE!!!.

8. Dodawanie modułów obsługi zdarzeń cyklu życia

Dotychczasowe rozszerzenie przetwarza wiadomości w miarę ich tworzenia. Co jednak, jeśli użytkownicy mają już bazę danych wiadomości, gdy instalują rozszerzenie? Rozszerzenia Firebase mają funkcję elementów wywołujących zdarzenia cyklu życia, której możesz używać do uruchamiania działań podczas instalowania, aktualizowania lub rekonfigurowania rozszerzenia. W tej sekcji użyjesz haka zdarzenia cyklu życia, aby uzupełnić istniejącą bazę danych wiadomości projektu o wiadomości z wielkimi literami, gdy użytkownik zainstaluje Twoje rozszerzenie.

Rozszerzenia Firebase używają Listy zadań Cloud do uruchamiania obsługi zdarzeń cyklu życia. Moduł obsługi zdarzeń definiujesz za pomocą funkcji Cloud Functions. Gdy instancja rozszerzenia dotrze do jednego z obsługiwanych zdarzeń cyklu życia, a Ty zdefiniujesz moduł obsługi, zostanie on dodany do kolejki zadań w Cloud Tasks. Cloud Tasks asynchronicznie wykona moduł obsługi. Podczas działania modułu obsługi zdarzeń cyklu życia konsola Firebase informuje użytkownika, że instancja rozszerzenia ma w toku zadanie przetwarzania. To Twoja funkcja obsługi przekazuje użytkownikowi raport o stanie trwającego zadania i ukończeniu zadania.

Aby dodać moduł obsługi zdarzenia cyklu życia, który uzupełnia istniejące wiadomości:

  1. Zdefiniuj nową funkcję w Cloud Functions, która jest wywoływana przez zdarzenia kolejki 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 powszechnie stosowana strategia postępowania z zadaniami przetwarzania, które nie mogą zostać ukończone w okresie limitu czasu funkcji Cloud Function. Ponieważ nie możesz przewidzieć, ile wiadomości użytkownik może mieć już w swojej bazie danych, gdy zainstaluje Twoje rozszerzenie, ta strategia jest odpowiednia.

  2. W pliku extension.yaml zadeklaruj funkcję wypełniania jako zasób rozszerzenia o 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 obsługę zdarzenia cyklu życia onInstall:

    lifecycleEvents:
      onInstall:
        function: backfilldata
        processingMessage: Uppercasing existing messages
    
  3. Chociaż uzupełnianie istniejących wiadomości jest przydatne, rozszerzenie może działać bez niego. W takich sytuacjach należy ustawić opcjonalne uruchamianie metod obsługi zdarzeń cyklu życia.

    Aby to zrobić, dodaj nowy parametr do 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
    

    Następnie na początku funkcji uzupełniania danych sprawdź wartość parametru DO_BACKFILL i w razie potrzeby zakończ ją wcześniej:

    functions/index.js

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

Po wprowadzeniu tych zmian rozszerzenie będzie konwertować istniejące wiadomości na wielkie litery po zainstalowaniu.

Do tej pory używasz emulatora rozszerzeń do tworzenia rozszerzenia i testowania wprowadzanych zmian. Jednak emulator rozszerzeń pomija proces instalacji, więc aby przetestować swojego onInstall, musisz zainstalować rozszerzenie w rzeczywistym projekcie. To dobrze, bo dzięki dodaniu funkcji automatycznego uzupełniania samouczek jest już gotowy pod względem kodu.

9. Wdrażanie w prawdziwym projekcie Firebase

Chociaż emulator rozszerzeń to świetne narzędzie do szybkiego iterowania rozszerzenia podczas tworzenia, w pewnym momencie warto wypróbować go w rzeczywistym projekcie.

Aby to zrobić, najpierw utwórz nowy projekt z kilkoma włączonymi usługami:

  1. W konsoli Firebase dodaj nowy projekt.
  2. Przejdź na abonament Blaze z płatnościami według wykorzystania. Cloud Functions for Firebase wymaga, aby Twój projekt miał konto rozliczeniowe, więc do zainstalowania rozszerzenia musisz też mieć konto rozliczeniowe.
  3. W nowym projekcie włącz Bazę danych czasu rzeczywistego.
  4. Chcesz przetestować, czy rozszerzenie może uzupełnić istniejące dane w instalacji, więc importujesz do instancji bazy danych w czasie rzeczywistym przykładowe dane:
    1. Pobierz wyjściowe dane RTDB.
    2. Na stronie Bazy danych w czasie rzeczywistym w konsoli Firebase kliknij (Więcej) > Importuj plik JSON i wybierz pobrany właśnie 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 z źródła lokalnego w nowym projekcie:

  1. Utwórz nowy katalog dla projektu Firebase:

    mkdir ~/extensions-live-test && cd ~/extensions-live-test
    
  2. Inicjalizacja projektu 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
    

    Tutaj możesz zobaczyć, jak wygląda proces instalacji rozszerzenia za pomocą narzędzia wiersza poleceń Firebase. Gdy narzędzie do konfiguracji zapyta, czy chcesz uzupełnić istniejące dane w bazie, wybierz „Tak”.

    Po wybraniu opcji konfiguracji narzędzie wiersza poleceń Firebase zapisze konfigurację w katalogu extensions, a lokalizację źródła rozszerzenia w pliku firebase.json. Te 2 rekordy są nazywane pliku manifestu rozszerzeń. Użytkownicy mogą używać pliku manifestu do zapisywania konfiguracji rozszerzeń i wdrażania jej 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 do bazy danych wiadomości kilka nowych węzłów i upewnij się, że rozszerzenie działa również w przypadku nowych wiadomości.

10. Tworzenie dokumentacji

Zanim udostępnisz rozszerzenie użytkownikom, upewnij się, że dostarczasz wystarczającą dokumentację, aby mogli je zainstalować.

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

extension.yaml

Podczas tworzenia tego rozszerzenia plik ten został już zaktualizowany, więc obecnie nie musisz go aktualizować.

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

PREINSTALL.md

W tym pliku podaj informacje, których użytkownik potrzebuje przed zainstalowaniem rozszerzenia: krótko opisz, do czego służy rozszerzenie, wyjaśnij, jakie są wymagania wstępne, oraz podaj informacje o konsekwencjach rozliczeniowych zainstalowania rozszerzenia. Jeśli masz stronę internetową z dodatkowymi informacjami, możesz dodać do niej link.

Tekst tego pliku jest wyświetlany użytkownikowi w Centrum rozszerzeń i za pomocą polecenia 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, np. kolejne kroki konfiguracji, przykład działania rozszerzenia itp.

Treść pliku 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 po instalacji 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 jeszcze nigdy opublikowane, dziennik zmian zawiera tylko 1 pozycję:

## 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 wpisać go ręcznie lub wygenerować plik z możliwością odczytu za pomocą polecenia.

Na potrzeby tego przewodnika pomiń pisanie pliku readme.

Dodatkowa dokumentacja

Omówiona powyżej dokumentacja to minimalny zestaw dokumentów, które należy udostępnić użytkownikom. Aby użytkownicy mogli z nich korzystać, wiele rozszerzeń wymaga bardziej szczegółowej dokumentacji. W takim przypadku należy przygotować dodatkową dokumentację i przechowywać ją w miejscu, do którego użytkownicy będą mogli odsyłać.

Na potrzeby tego przewodnika pomiń tworzenie obszerniejszej dokumentacji.

11. Publikowanie w Centrum rozszerzeń

Teraz, gdy kod rozszerzenia jest gotowy i opublikowany, możesz udostępnić je innym użytkownikom 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 rozszerzenia napisanego przez Firebase.

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. Umieść kod źródłowy rozszerzenia w miejscu, które można publicznie zweryfikować. Gdy Twój kod jest dostępny z weryfikowalnego źródła, Firebase może opublikować rozszerzenie bezpośrednio z tego miejsca. Dzięki temu będziesz mieć pewność, że opublikujesz aktualnie dostępną wersję rozszerzenia, a użytkownicy będą mogli sprawdzić kod, 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. W konsoli Firebase otwórz panel wydawcy, znajdź właśnie przesłane rozszerzenie i kliknij „Opublikuj w Centrum rozszerzeń”. W tym celu nasz zespół musi sprawdzić Twoją prośbę, co może potrwać kilka dni. Po zatwierdzeniu rozszerzenie zostanie opublikowane w Centrum rozszerzeń. Jeśli Twoja prośba zostanie odrzucona, otrzymasz wiadomość z wyjaśnieniem przyczyny. Następnie możesz rozwiązać zgłoszone problemy i ponownie przesłać prośbę do sprawdzenia.