Rozpocznij tworzenie rozszerzenia

Na tej stronie znajdziesz instrukcje tworzenia prostego rozszerzenia Firebase, które możesz zainstalować w swoich projektach lub udostępnić innym. Ten prosty przykład rozszerzenia Firebase będzie monitorować Bazę danych czasu rzeczywistego pod kątem wiadomości i zamieniać je na wielkie litery.

1. Konfigurowanie środowiska i inicjowanie projektu

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

  1. Zainstaluj Node.js w wersji 16 lub nowszej. Jednym ze sposobów instalacji Node.js jest użycie nvm (lub nvm-windows).

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

    npm install -g firebase-tools

Teraz użyj wiersza poleceń Firebase, aby zainicjować nowy projekt rozszerzenia:

  1. Utwórz katalog rozszerzenia i cd do niego:

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

    firebase ext:dev:init

    Gdy pojawi się odpowiednie pytanie, wybierz JavaScript jako język funkcji (pamiętaj jednak, że podczas tworzenia własnego rozszerzenia możesz też używać TypeScriptu), a gdy pojawi się pytanie o zainstalowanie zależności, odpowiedz „yes” (tak). (Zaakceptuj wartości domyślne wszystkich innych opcji). To polecenie skonfiguruje szkielet kodu nowej wtyczki, od którego możesz zacząć tworzyć wtyczkę.

2. Wypróbuj przykładowe rozszerzenie przy użyciu emulatora

Gdy wiersz poleceń Firebase zainicjował nowy katalog rozszerzeń, utworzył prostą funkcję przykładową i katalog integration-tests, który zawiera pliki niezbędne do uruchomienia rozszerzenia za pomocą pakietu emulatorów 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 projektu „testowego” (demo-test). Rozszerzenie składa się na razie z jednej funkcji wywoływanej przez HTTP (greetTheWorld), która po wywołaniu zwraca komunikat „hello world”.

  3. Gdy emulator jest nadal uruchomiony, wypróbuj greetTheWorldfunkcję rozszerzenia, otwierając adres URL, który został wyświetlony po uruchomieniu emulatora.

    W przeglądarce wyświetli się komunikat „Hello World from greet-the-world”.

  4. Kod źródłowy tej funkcji znajduje się w katalogu functionsrozszerzenia. Otwórz źródło 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 ponownie wczytuje 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 teraz, gdy otworzysz adres URL funkcji, zobaczysz zaktualizowany komunikat powitalny.

3. Dodawanie podstawowych informacji do pliku extension.yaml

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

Na początek zmodyfikuj predefiniowane 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 kodem:

    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ą prefiks wskazujący główną usługę Firebase, z którą współpracuje rozszerzenie, a następnie opis działania rozszerzenia. W swoich rozszerzeniach używaj tej samej konwencji.

  2. Po zmianie nazwy rozszerzenia zaktualizuj też konfigurację emulatora, podając nową nazwę:

    1. 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 nadal znajdują się pozostałości rozszerzenia greet-the-world, ale na razie je zostaw. Zaktualizujesz je w 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ę Cloud, która będzie wykonywać podstawowe zadanie rozszerzenia, czyli monitorować bazę danych czasu rzeczywistego pod kątem wiadomości i przekształcać je na wielkie litery.

  1. Otwórz źródło funkcji rozszerzenia (w katalogu functions rozszerzenia) w wybranym edytorze lub środowisku IDE. Zastąp jego zawartość tym:

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

    Zastąpiona funkcja była funkcją aktywowaną przez HTTP, która była uruchamiana po uzyskaniu dostępu do punktu końcowego HTTP. Nowa funkcja jest wywoływana przez zdarzenia bazy danych w czasie rzeczywistym: monitoruje nowe elementy w określonej ścieżce i gdy wykryje nowy element, zapisuje w bazie danych jego wersję pisaną wielkimi literami.

    Nowy plik używa składni modułów ECMAScript (importexport) zamiast CommonJS (require). Aby używać modułów ES w Node, w functions/package.json określ "type": "module":

    {
      "name": "rtdb-uppercase-messages",
      "main": "index.js",
      "type": "module",
      
    }
    
  2. Każda funkcja w rozszerzeniu musi być zadeklarowana w pliku extension.yaml. W przykładzie rozszerzenia zadeklarowano greetTheWorld jako jedyną funkcję Cloud Function rozszerzenia. Teraz, gdy zastąpiono 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 wyzwalacza, musisz zaktualizować konfigurację emulatora, aby uruchamiać emulator RTDB razem 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ę pytanie, pomiń konfigurowanie domyślnego projektu, a następnie wybierz emulatory Funkcji i bazy danych. Zaakceptuj domyślne porty i pozwól narzędziu do konfiguracji pobrać wymagane pliki.

    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, który emulator wyświetlił po uruchomieniu.

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

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

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

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

Masz teraz kompletne, choć proste, rozszerzenie, które działa na instancji RTDB. W kolejnych sekcjach udoskonalisz to rozszerzenie, dodając do niego kilka dodatkowych funkcji. Następnie przygotujesz rozszerzenie do rozpowszechniania wśród innych użytkowników i 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 dla poszczególnych instancji. Każde konto ma minimalny zestaw uprawnień wymaganych do działania. Dlatego musisz jawnie zadeklarować wszystkie role uprawnień, których wymaga Twoje rozszerzenie. Gdy użytkownicy zainstalują Twoje rozszerzenie, Firebase utworzy konto usługi z przyznanymi tymi rolami i będzie go używać do uruchamiania rozszerzenia.

Aby wywoływać zdarzenia produktu, nie musisz deklarować ról, ale musisz to zrobić, aby w inny sposób wchodzić z nim w interakcję. Funkcja dodana w ostatnim kroku zapisuje dane w bazie danych czasu rzeczywistego, więc musisz dodać do pliku extension.yaml tę deklarację:

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

Podobnie w apis deklarujesz interfejsy API Google, których używa rozszerzenie. Gdy użytkownicy zainstalują rozszerzenie, zostaną zapytani, 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 i nie jest potrzebne w tym przewodniku.

6. Definiowanie parametrów konfigurowanych przez użytkownika

Funkcja utworzona w 2 ostatnich krokach monitorowała określone miejsce w RTDB pod kątem przychodzących wiadomości. Czasami śledzenie konkretnej lokalizacji jest tym, czego potrzebujesz, np. gdy rozszerzenie działa na strukturze bazy danych, której używasz wyłącznie na potrzeby tego rozszerzenia. W większości przypadków warto jednak umożliwić użytkownikom, którzy instalują rozszerzenie w swoich projektach, konfigurowanie tych wartości. Dzięki temu użytkownicy mogą korzystać z rozszerzenia, aby pracować z dotychczasową konfiguracją bazy danych.

Umożliwia użytkownikowi skonfigurowanie ścieżki, w której rozszerzenie będzie szukać 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 nowy parametr tekstowy, o którego ustawienie użytkownicy będą proszeni 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} odwołuje się do parametru, który został właśnie zdefiniowany. Gdy rozszerzenie jest uruchomione, ten token zostanie zastąpiony wartością skonfigurowaną przez użytkownika dla tego parametru. W rezultacie funkcja makeuppercase będzie nasłuchiwać ścieżki określonej przez użytkownika. Za pomocą tej składni możesz odwoływać się do dowolnego parametru zdefiniowanego przez użytkownika w dowolnym miejscu w pliku extension.yaml (i w pliku POSTINSTALL.md – więcej informacji znajdziesz poniżej).

  3. Możesz też uzyskać dostęp do parametrów zdefiniowanych przez użytkownika w kodzie funkcji.

    W funkcji napisanej w ostatniej sekcji na stałe zakodowano ścieżkę, w której mają być śledzone zmiany. Zmień definicję wyzwalacza, 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ń Firebase ta zmiana ma charakter wyłącznie dokumentacyjny: gdy funkcja Cloud Functions jest wdrażana w ramach rozszerzenia, korzysta z definicji wyzwalacza z pliku extension.yaml i ignoruje wartość określoną w definicji funkcji. Warto jednak udokumentować w kodzie, skąd pochodzi ta wartość.

  4. Może Cię rozczarować wprowadzenie zmiany w kodzie, która nie ma wpływu na działanie programu, ale ważna lekcja, jaką możesz z tego wyciągnąć, jest taka, że w kodzie funkcji możesz uzyskać dostęp do dowolnego parametru zdefiniowanego przez użytkownika i używać go jako zwykłej wartości w logice funkcji. Aby to potwierdzić, dodaj do kodu ten wiersz:

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

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

    MESSAGE_PATH=/msgs/{pushId}/original
    

    Zwróć uwagę, że ścieżka powyżej różni się od ścieżki domyślnej i od ścieżki zdefiniowanej wcześniej. Ma to na celu sprawdzenie, czy po wypróbowaniu zaktualizowanego rozszerzenia Twoja definicja jest stosowana.

  6. Teraz uruchom ponownie emulator i ponownie otwórz interfejs emulatora bazy danych.

    Edytuj węzeł główny bazy danych, używając ścieżki zdefiniowanej powyżej:

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

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

7. Udostępnianie punktów zaczepienia zdarzeń dla logiki zdefiniowanej przez użytkownika

Jako autor rozszerzenia wiesz już, jak usługa Firebase może wywoływać logikę dostarczoną przez rozszerzenie: utworzenie nowych rekordów w Bazie danych czasu rzeczywistego wywołuje funkcję makeuppercase. Rozszerzenie może mieć analogiczną relację z użytkownikami, którzy je zainstalują: rozszerzenie może wywoływać logikę zdefiniowaną przez użytkownika.

Rozszerzenie może udostępniać synchroniczne punkty zaczepienia, asynchroniczne punkty zaczepienia lub oba te rodzaje. Synchroniczne punkty zaczepienia umożliwiają użytkownikom wykonywanie zadań, które blokują ukończenie jednej z funkcji rozszerzenia. Może to być przydatne np. w sytuacji, gdy chcesz umożliwić użytkownikom wykonanie niestandardowego przetwarzania wstępnego przed rozpoczęciem działania rozszerzenia.

W tym przewodniku dodasz do rozszerzenia asynchroniczny hook, który umożliwi użytkownikom zdefiniowanie własnych kroków przetwarzania, które będą wykonywane po zapisaniu przez rozszerzenie wiadomości pisanej wielkimi literami w bazie danych czasu rzeczywistego. Haki asynchroniczne używają Eventarc do aktywowania funkcji zdefiniowanych przez użytkownika. Rozszerzenia deklarują typy zdarzeń, które emitują, a gdy użytkownicy instalują rozszerzenie, wybierają typy zdarzeń, które ich interesują. Jeśli wybiorą co najmniej 1 zdarzenie, Firebase w ramach procesu instalacji udostępni rozszerzeniu kanał Eventarc. Użytkownicy mogą następnie wdrożyć własne funkcje w Cloud Functions, które nasłuchują na tym kanale i są wywoływane, gdy rozszerzenie publikuje nowe zdarzenia.

Aby dodać asynchroniczny hook, wykonaj te czynności:

  1. W pliku extension.yaml dodaj tę sekcję, która deklaruje 1 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 na całym świecie. Aby zapewnić ich niepowtarzalność, zawsze nadawaj im nazwy w tym formacie:<publisher-id>.<extension-id>.<version>.<description>. (Nie masz jeszcze identyfikatora wydawcy, więc na razie użyj symbolu test-publisher).

  2. Na końcu funkcji makeuppercase dodaj kod, który publikuje zdarzenie typu, który został właśnie zadeklarowany:

    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 wykorzystuje fakt, ż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 zdarzenia Eventarc możesz dołączyć dodatkowe informacje. W powyższym przykładzie zdarzenie ma pole subject, które zawiera odwołanie do nowo utworzonej wartości, oraz ładunek data, który zawiera oryginalne i pisane wielkimi literami wiadomości. Funkcje zdefiniowane przez użytkownika, które są wywoływane przez zdarzenie, mogą korzystać z tych informacji.

  3. Zwykle zmienne środowiskowe EVENTARC_CHANNELEXT_SELECTED_EVENTS są definiowane na podstawie opcji wybranych przez użytkownika podczas instalacji. Aby przeprowadzić testy na emulatorze, 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 masz już za sobą wszystkie czynności potrzebne do dodania do rozszerzenia asynchronicznego wywołania zwrotnego zdarzenia.

Aby wypróbować nową funkcję, którą właśnie wdrożono, w kolejnych krokach wciel się w rolę użytkownika instalującego rozszerzenie:

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

    firebase init functions

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

  2. Edytuj 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 przykład funkcji przetwarzania końcowego, którą może napisać użytkownik. W tym przypadku funkcja nasłuchuje zdarzenia complete publikowanego przez rozszerzenie, a gdy zostanie wywołana, dodaje do nowo utworzonej wiadomości z wielkimi literami 3 wykrzykniki.

  3. Uruchom ponownie emulator. Emulator wczyta funkcje rozszerzenia oraz funkcję przetwarzania końcowego zdefiniowaną przez „użytkownika”.

  4. Otwórz interfejs emulatora bazy danych i edytuj węzeł główny bazy danych, używając ścieżki zdefiniowanej powyżej:

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

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

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

Napisane do tej pory rozszerzenie przetwarza wiadomości w momencie ich tworzenia. Ale co, jeśli użytkownicy mają już bazę danych wiadomości, gdy instalują rozszerzenie? Rozszerzenia Firebase mają funkcję haków zdarzeń cyklu życia, która umożliwia wywoływanie działań po zainstalowaniu, zaktualizowaniu lub ponownym skonfigurowaniu rozszerzenia. W tej sekcji użyjesz wywołań zwrotnych zdarzeń cyklu życia, aby uzupełnić istniejącą bazę danych wiadomości projektu o wiadomości pisane wielkimi literami, gdy użytkownik zainstaluje Twoje rozszerzenie.

Rozszerzenia Firebase używają Cloud Tasks do uruchamiania modułów obsługi zdarzeń cyklu życia. Moduły obsługi zdarzeń definiujesz za pomocą Cloud Functions. Gdy instancja rozszerzenia osiągnie jedno z obsługiwanych zdarzeń cyklu życia, a Ty zdefiniujesz moduł obsługi, zostanie on dodany do kolejki Cloud Tasks. Cloud Tasks asynchronicznie wykona wtedy procedurę obsługi. Gdy działa moduł obsługi zdarzeń cyklu życia, konsola Firebase informuje użytkownika, że instancja rozszerzenia wykonuje zadanie przetwarzania. To funkcja obsługi musi zgłaszać użytkownikowi bieżący stan i zakończenie zadania.

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 jest aktywowana 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 funkcja przetwarza tylko kilka rekordów, zanim ponownie doda się do kolejki zadań. Jest to powszechnie stosowana strategia radzenia sobie z zadaniami przetwarzania, których nie można ukończyć w okresie limitu czasu funkcji Cloud Functions. Nie możesz przewidzieć, ile wiadomości użytkownik może mieć już w bazie danych, gdy zainstaluje Twoje rozszerzenie, więc ta strategia jest odpowiednia.

  2. W pliku extension.yaml zadeklaruj funkcję wypełniania jako zasób rozszerzenia, który ma właściwość 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 onInstallcyklu życia:

    lifecycleEvents:
      onInstall:
        function: backfilldata
        processingMessage: Uppercasing existing messages
    
  3. Wypełnianie wsteczne istniejących wiadomości jest przydatne, ale rozszerzenie może działać bez niego. W takich sytuacjach należy sprawić, aby uruchamianie procedur obsługi zdarzeń cyklu życia było opcjonalne.

    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 wypełniania sprawdź wartość parametru DO_BACKFILL i w razie potrzeby zakończ działanie funkcji:

    functions/index.js

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

Po wprowadzeniu powyższych zmian rozszerzenie będzie przekształcać istniejące wiadomości na wielkie litery po zainstalowaniu.

Do tej pory używałeś emulatora rozszerzeń do tworzenia rozszerzenia i testowania wprowadzanych zmian. Emulator rozszerzeń pomija jednak proces instalacji, więc aby przetestować onInstall funkcję obsługi zdarzeń, musisz zainstalować rozszerzenie w prawdziwym projekcie. To dobrze, bo dzięki dodaniu tej funkcji automatycznego wypełniania rozszerzenie samouczka jest już gotowe.

9. Wdrażanie w prawdziwym projekcie Firebase

Emulator rozszerzeń to świetne narzędzie do szybkiego iterowania rozszerzenia podczas programowania, ale w pewnym momencie warto wypróbować je w prawdziwym projekcie.

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

  1. W konsoli Firebase dodaj nowy projekt.
  2. Przenieś projekt 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 również potrzebujesz konta rozliczeniowego.
  3. W nowym projekcie włącz Bazę danych czasu rzeczywistego.
  4. Ponieważ chcesz przetestować możliwość uzupełniania przez rozszerzenie istniejących danych po instalacji, zaimportuj do instancji bazy danych w czasie rzeczywistym przykładowe dane:
    1. Pobierz przykładowe dane RTDB.
    2. Na stronie Bazy danych w czasie rzeczywistym w konsoli Firebase kliknij (więcej) > Importuj JSON i wybierz pobrany plik.
  5. Aby włączyć funkcję wypełniania pustych miejsc w celu używania metody orderByChild, skonfiguruj bazę danych tak, aby indeksowała wiadomości na podstawie 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ę prośba, wybierz utworzony projekt.

  3. Zainstaluj rozszerzenie w lokalnym projekcie Firebase:

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

    Tutaj możesz zobaczyć, jak wygląda instalowanie rozszerzenia za pomocą narzędzia wiersza poleceń Firebase z perspektywy użytkownika. Gdy narzędzie do konfiguracji zapyta, czy chcesz wypełnić istniejącą bazę danych, wybierz „Tak”.

    Po wybraniu opcji konfiguracji interfejs Firebase CLI zapisze konfigurację w katalogu extensions i zarejestruje lokalizację źródła rozszerzenia w pliku extensions.firebase.json Te 2 rekordy są łącznie nazywane plikiem 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 produkcyjnym:

    firebase deploy --only extensions

Jeśli wszystko przebiegnie pomyślnie, interfejs wiersza poleceń Firebase prześle rozszerzenie do projektu i je zainstaluje. Po zakończeniu instalacji zostanie uruchomione zadanie uzupełniania wstecznego i po kilku minutach baza danych zostanie zaktualizowana o wiadomości pisane wielkimi literami. Dodaj do bazy danych wiadomości kilka nowych węzłów i sprawdź, czy rozszerzenie działa też w przypadku nowych wiadomości.

10. Tworzenie dokumentacji

Zanim udostępnisz rozszerzenie użytkownikom, upewnij się, że dostarczasz wystarczającą ilość dokumentacji, aby mogli z niego korzystać.

Podczas inicjowania projektu rozszerzenia interfejs Firebase CLI utworzył wersje zastępcze minimalnej wymaganej dokumentacji. Zaktualizuj te pliki, aby dokładnie odzwierciedlały utworzone rozszerzenie.

extension.yaml

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

Nie lekceważ jednak znaczenia dokumentacji zawartej w tym pliku. Oprócz najważniejszych informacji identyfikacyjnych rozszerzenia – nazwy, opisu, autora i oficjalnej lokalizacji repozytorium – plik extension.yaml zawiera dokumentację dla użytkowników dotyczącą każdego zasobu i parametru konfigurowanego przez użytkownika. Te informacje są udostępniane 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, co robi rozszerzenie, wyjaśnij wszelkie wymagania wstępne i poinformuj użytkownika o konsekwencjach instalacji rozszerzenia w zakresie płatności. Jeśli masz witrynę z dodatkowymi informacjami, możesz ją tutaj połączyć.

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. dalsze kroki konfiguracji, przykład działania rozszerzenia itp.

Zawartość 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 z samouczkiem:

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

CHANGELOG.md należy też udokumentować zmiany wprowadzane między kolejnymi wersjami rozszerzenia.

Ponieważ przykładowe rozszerzenie nie zostało jeszcze opublikowane, dziennik zmian zawiera tylko 1 wpis:

## Version 0.0.1

Initial release of the _Convert messages to upper case_ extension.

README.md

Większość rozszerzeń zawiera też plik readme dla użytkowników odwiedzających repozytorium rozszerzenia. Możesz napisać ten plik ręcznie lub wygenerować go za pomocą polecenia.

Na potrzeby tego przewodnika pomiń tworzenie pliku readme.

Dodatkowa dokumentacja

Wymieniona powyżej dokumentacja to minimalny zestaw dokumentów, które 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 napisać dodatkową dokumentację i umieścić ją w miejscu, do którego można kierować użytkowników.

Na potrzeby tego przewodnika pomiń pisanie bardziej obszernej dokumentacji.

11. Publikowanie w Centrum rozszerzeń

Gdy rozszerzenie będzie gotowe i udokumentowane, możesz udostępnić je w Centrum rozszerzeń. Ale to tylko samouczek, więc nie rób tego. Zacznij pisać własne rozszerzenie, korzystając z informacji, które znajdziesz tutaj i w pozostałej części dokumentacji wydawcy rozszerzeń Firebase, oraz analizując źródło oficjalnych rozszerzeń napisanych przez Firebase.

Gdy zechcesz opublikować swoje rozszerzenie w Centrum rozszerzeń, wykonaj te czynności:

  1. Jeśli publikujesz pierwsze rozszerzenie, zarejestruj się jako wydawca rozszerzeń. Gdy zarejestrujesz się jako wydawca rozszerzeń, utworzysz 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 kod jest dostępny w weryfikowalnym źródle, Firebase może opublikować rozszerzenie bezpośrednio z tej lokalizacji. Dzięki temu masz pewność, że publikujesz aktualnie wydaną wersję rozszerzenia, a użytkownicy mogą 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. Otwórz panel wydawcy w konsoli Firebase, znajdź rozszerzenie, które właśnie przesłano, i kliknij „Opublikuj w Centrum rozszerzeń”. Wysyła to prośbę o sprawdzenie do naszego zespołu, co może potrwać kilka dni. Jeśli rozszerzenie zostanie zatwierdzone, opublikujemy je w Centrum rozszerzeń. Jeśli zostanie odrzucona, otrzymasz wiadomość z wyjaśnieniem przyczyny. Następnie możesz rozwiązać zgłoszone problemy i ponownie przesłać ją do sprawdzenia.