Beginnen Sie mit dem Aufbau einer Erweiterung

Diese Seite führt Sie durch die erforderlichen Schritte zum Erstellen einer einfachen Firebase-Erweiterung, die Sie in Ihren Projekten installieren oder mit anderen teilen können. Dieses einfache Beispiel einer Firebase-Erweiterung überwacht Ihre Echtzeitdatenbank auf Nachrichten und konvertiert diese in Großbuchstaben.

1. Richten Sie Ihre Umgebung ein und initialisieren Sie ein Projekt

Bevor Sie mit der Erstellung einer Erweiterung beginnen können, müssen Sie eine Build-Umgebung mit den erforderlichen Tools einrichten.

  1. Installieren Sie Node.js 16 oder neuer. Eine Möglichkeit, Node zu installieren, ist die Verwendung von nvm (oder nvm-windows ).

  2. Installieren oder aktualisieren Sie die neueste Version der Firebase-CLI . Um mit npm zu installieren oder zu aktualisieren, führen Sie diesen Befehl aus:

    npm install -g firebase-tools
    

Verwenden Sie nun die Firebase-CLI, um ein neues Erweiterungsprojekt zu initialisieren:

  1. Erstellen Sie ein Verzeichnis für Ihre Erweiterung und cd hinein:

    mkdir rtdb-uppercase-messages && cd rtdb-uppercase-messages
    
  2. Führen Sie den Befehl ext:dev:init der Firebase-CLI aus:

    firebase ext:dev:init
    

    Wenn Sie dazu aufgefordert werden, wählen Sie JavaScript als Sprache für Funktionen (beachten Sie jedoch, dass Sie TypeScript auch verwenden können, wenn Sie Ihre eigene Erweiterung entwickeln). Wenn Sie aufgefordert werden, Abhängigkeiten zu installieren, antworten Sie mit „Ja“. (Akzeptieren Sie die Standardeinstellungen für alle anderen Optionen.) Dieser Befehl richtet eine Grundcodebasis für eine neue Erweiterung ein, von der aus Sie mit der Entwicklung Ihrer Erweiterung beginnen können.

2. Probieren Sie die Beispielerweiterung mit dem Emulator aus

Als die Firebase-CLI das neue Erweiterungsverzeichnis initialisierte, erstellte sie eine einfache Beispielfunktion und ein integration-tests Verzeichnis, das die Dateien enthält, die zum Ausführen einer Erweiterung mit der Firebase-Emulator-Suite erforderlich sind.

Versuchen Sie, die Beispielerweiterung im Emulator auszuführen:

  1. Wechseln Sie in das Verzeichnis integration-tests :

    cd functions/integration-tests
    
  2. Starten Sie den Emulator mit einem Demoprojekt:

    firebase emulators:start --project=demo-test
    

    Der Emulator lädt die Erweiterung in ein vordefiniertes „Dummy“-Projekt ( demo-test ). Bisher besteht die Erweiterung aus einer einzigen HTTP-ausgelösten Funktion, greetTheWorld , die beim Zugriff eine „Hallo Welt“-Nachricht zurückgibt.

  3. Versuchen Sie bei laufendem Emulator die Funktion greetTheWorld der Erweiterung, indem Sie die URL aufrufen, die beim Starten ausgegeben wurde.

    Ihr Browser zeigt die Meldung „Hallo Welt von Greet-the-World“ an.

  4. Der Quellcode für diese Funktion befindet sich im functions der Erweiterung. Öffnen Sie die Quelle im Editor oder in der IDE Ihrer Wahl:

    Funktionen/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. Während der Emulator ausgeführt wird, lädt er alle Änderungen, die Sie an Ihrem Funktionscode vornehmen, automatisch neu. Versuchen Sie, eine kleine Änderung an der Funktion greetTheWorld vorzunehmen:

    Funktionen/index.js

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

    Speichern Sie Ihre Änderungen. Der Emulator lädt Ihren Code neu und wenn Sie nun die Funktions-URL besuchen, sehen Sie die aktualisierte Begrüßung.

3. Fügen Sie grundlegende Informationen zu extension.yaml hinzu

Nachdem Sie nun eine Entwicklungsumgebung eingerichtet haben und den Erweiterungsemulator ausführen, können Sie mit dem Schreiben Ihrer eigenen Erweiterung beginnen.

Bearbeiten Sie als bescheidenen ersten Schritt die vordefinierten Erweiterungsmetadaten, um die Erweiterung widerzuspiegeln, die Sie schreiben möchten, und nicht greet-the-world . Diese Metadaten werden in der Datei extension.yaml gespeichert.

  1. Öffnen Sie extension.yaml in Ihrem Editor und ersetzen Sie den gesamten Inhalt der Datei durch Folgendes:

    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
    

    Beachten Sie die im name verwendete Namenskonvention: Offizielle Firebase-Erweiterungen werden mit einem Präfix benannt, das das primäre Firebase-Produkt angibt, auf dem die Erweiterung ausgeführt wird, gefolgt von einer Beschreibung der Funktion der Erweiterung. Sie sollten dieselbe Konvention in Ihren eigenen Erweiterungen verwenden.

  2. Da Sie den Namen Ihrer Erweiterung geändert haben, sollten Sie auch Ihre Emulatorkonfiguration mit dem neuen Namen aktualisieren:

    1. Ändern Sie in functions/integration-tests/firebase.json greet-the-world in rtdb-uppercase-messages .
    2. Benennen Sie functions/integration-tests/extensions/greet-the-world.env in functions/integration-tests/extensions/rtdb-uppercase-messages.env .

In Ihrem Erweiterungscode sind noch einige Reste der Erweiterung greet-the-world vorhanden, aber belassen Sie diese vorerst. Sie werden diese in den nächsten Abschnitten aktualisieren.

4. Schreiben Sie eine Cloud-Funktion und deklarieren Sie sie als Erweiterungsressource

Jetzt können Sie mit dem Schreiben von Code beginnen. In diesem Schritt schreiben Sie eine Cloud-Funktion, die die Kernaufgabe Ihrer Erweiterung ausführt, nämlich die Überwachung Ihrer Echtzeitdatenbank auf Nachrichten und deren Konvertierung in Großbuchstaben.

  1. Öffnen Sie die Quelle für die Funktionen der Erweiterung (im functions der Erweiterung) im Editor oder in der IDE Ihrer Wahl. Ersetzen Sie den Inhalt durch Folgendes:

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

    Die alte Funktion, die Sie ersetzt haben, war eine HTTP-ausgelöste Funktion, die ausgeführt wurde, wenn auf einen HTTP-Endpunkt zugegriffen wurde. Die neue Funktion wird durch Echtzeit-Datenbankereignisse ausgelöst: Sie sucht nach neuen Elementen in einem bestimmten Pfad und schreibt, wenn eines erkannt wird, die Großbuchstabenversion des Werts zurück in die Datenbank.

    Übrigens verwendet diese neue Datei die ECMAScript-Modulsyntax ( import und export ) anstelle von CommonJS ( require ). Um ES-Module in Node zu verwenden, geben Sie "type": "module" in functions/package.json an:

    {
      "name": "rtdb-uppercase-messages",
      "main": "index.js",
      "type": "module",
      …
    }
    
  2. Jede Funktion in Ihrer Erweiterung muss in der Datei extension.yaml deklariert werden. Die Beispielerweiterung deklarierte greetTheWorld als einzige Cloud-Funktion der Erweiterung. Nachdem Sie es nun durch makeuppercase ersetzt haben, müssen Sie auch seine Deklaration aktualisieren.

    Öffnen Sie extension.yaml und fügen Sie ein resources hinzu:

    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. Da Ihre Erweiterung jetzt Realtime Database als Auslöser verwendet, müssen Sie Ihre Emulatorkonfiguration aktualisieren, um den RTDB-Emulator neben dem Cloud Functions-Emulator auszuführen:

    1. Wenn der Emulator noch läuft, stoppen Sie ihn, indem Sie Strg-C drücken.

    2. Führen Sie im Verzeichnis functions/integration-tests den folgenden Befehl aus:

      firebase init emulators
      

      Wenn Sie dazu aufgefordert werden, überspringen Sie die Einrichtung eines Standardprojekts und wählen Sie dann die Emulatoren „Funktionen“ und „Datenbank“ aus. Akzeptieren Sie die Standardports und erlauben Sie dem Setup-Tool, alle erforderlichen Dateien herunterzuladen.

    3. Starten Sie den Emulator neu:

      firebase emulators:start --project=demo-test
      
  4. Probieren Sie Ihre aktualisierte Erweiterung aus:

    1. Öffnen Sie die Benutzeroberfläche des Datenbankemulators über den Link, den der Emulator beim Starten ausgegeben hat.

    2. Bearbeiten Sie den Stammknoten der Datenbank:

      • Feld: messages
      • Typ: json
      • Wert: {"11": {"original": "recipe"}}

      Wenn alles richtig eingerichtet ist und Sie Ihre Datenbankänderungen speichern, sollte die makeuppercase Funktion der Erweiterung ausgelöst werden und einen untergeordneten Datensatz zu Nachricht 11 mit dem Inhalt "upper": "RECIPE" hinzufügen. Sehen Sie sich die Protokolle und die Datenbankregisterkarten der Emulator-Benutzeroberfläche an, um die erwarteten Ergebnisse zu bestätigen.

    3. Versuchen Sie, dem messages ( {"original":"any text"} ) weitere untergeordnete Elemente hinzuzufügen. Immer wenn Sie einen neuen Datensatz hinzufügen, sollte die Erweiterung ein uppercase hinzufügen, das den Inhalt des original in Großbuchstaben enthält.

Sie verfügen nun über eine vollständige, wenn auch einfache Erweiterung, die auf einer RTDB-Instanz ausgeführt wird. In den folgenden Abschnitten verfeinern Sie diese Erweiterung mit einigen zusätzlichen Funktionen. Anschließend bereiten Sie die Erweiterung für die Verteilung an andere vor und erfahren schließlich, wie Sie Ihre Erweiterung auf Extensions Hub veröffentlichen.

5. Deklarieren Sie APIs und Rollen

Firebase gewährt jeder Instanz einer installierten Erweiterung über ein instanzspezifisches Dienstkonto eingeschränkten Zugriff auf das Projekt und seine Daten. Jedes Konto verfügt über die für den Betrieb erforderlichen Mindestberechtigungen. Aus diesem Grund müssen Sie alle IAM-Rollen, die Ihre Erweiterung benötigt, explizit deklarieren; Wenn Benutzer Ihre Erweiterung installieren, erstellt Firebase ein Dienstkonto mit diesen Rollen und verwendet es zum Ausführen der Erweiterung.

Sie müssen keine Rollen deklarieren, um die Ereignisse eines Produkts auszulösen, aber Sie müssen eine Rolle deklarieren, um anderweitig mit dem Produkt zu interagieren. Da die Funktion, die Sie im letzten Schritt hinzugefügt haben, in die Echtzeitdatenbank schreibt, müssen Sie die folgende Deklaration zu extension.yaml hinzufügen:

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

Ebenso deklarieren Sie die Google APIs, die eine Erweiterung im apis Feld verwendet. Wenn Benutzer Ihre Erweiterung installieren, werden sie gefragt, ob sie diese APIs automatisch für ihr Projekt aktivieren möchten. Dies ist normalerweise nur für Nicht-Firebase-Google-APIs erforderlich und wird für dieses Handbuch nicht benötigt.

6. Definieren Sie vom Benutzer konfigurierbare Parameter

Die Funktion, die Sie in den letzten beiden Schritten erstellt haben, überwacht einen bestimmten RTDB-Speicherort auf eingehende Nachrichten. Manchmal möchten Sie tatsächlich einen bestimmten Standort überwachen, beispielsweise wenn Ihre Erweiterung auf einer Datenbankstruktur arbeitet, die Sie ausschließlich für Ihre Erweiterung verwenden. In den meisten Fällen möchten Sie diese Werte jedoch für Benutzer konfigurierbar machen, die Ihre Erweiterung in ihren Projekten installieren. Auf diese Weise können Benutzer Ihre Erweiterung nutzen, um mit ihrem vorhandenen Datenbank-Setup zu arbeiten.

Machen Sie den Pfad, den die Erweiterung auf neue Nachrichten überwacht, vom Benutzer konfigurierbar:

  1. Fügen Sie in der Datei extension.yaml einen Abschnitt params hinzu:

    - 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
    

    Dadurch wird ein neuer Zeichenfolgenparameter definiert, den Benutzer bei der Installation Ihrer Erweiterung festlegen müssen.

  2. Gehen Sie immer noch in der Datei extension.yaml zurück zu Ihrer makeuppercase Deklaration und ändern Sie das resource wie folgt:

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

    Das ${param:MESSAGE_PATH} -Token ist eine Referenz auf den Parameter, den Sie gerade definiert haben. Wenn Ihre Erweiterung ausgeführt wird, wird dieses Token durch den Wert ersetzt, den der Benutzer für diesen Parameter konfiguriert hat, mit dem Ergebnis, dass die makeuppercase Funktion den vom Benutzer angegebenen Pfad abhört. Sie können diese Syntax verwenden, um auf jeden benutzerdefinierten Parameter an einer beliebigen Stelle in extension.yaml (und in POSTINSTALL.md – mehr dazu später) zu verweisen.

  3. Sie können auch über Ihren Funktionscode auf benutzerdefinierte Parameter zugreifen.

    In der Funktion, die Sie im letzten Abschnitt geschrieben haben, haben Sie den Pfad fest codiert, um auf Änderungen zu achten. Ändern Sie stattdessen die Triggerdefinition, um auf den benutzerdefinierten Wert zu verweisen:

    Funktionen/index.js

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

    Beachten Sie, dass diese Änderung in Firebase-Erweiterungen lediglich der Dokumentation dient: Wenn eine Cloud-Funktion als Teil einer Erweiterung bereitgestellt wird, verwendet sie die Triggerdefinition aus der Datei extension.yaml und ignoriert den in der Funktionsdefinition angegebenen Wert. Dennoch ist es eine gute Idee, in Ihrem Code zu dokumentieren, woher dieser Wert kommt.

  4. Es könnte für Sie enttäuschend sein, eine Codeänderung vorzunehmen, die keine Auswirkung auf die Laufzeit hat, aber die wichtige Lektion, die Sie daraus ziehen können, ist, dass Sie auf jeden benutzerdefinierten Parameter in Ihrem Funktionscode zugreifen und ihn als gewöhnlichen Wert in der Funktionslogik verwenden können. Fügen Sie als Anspielung auf diese Funktion die folgende Protokollanweisung hinzu, um zu zeigen, dass Sie tatsächlich auf den vom Benutzer definierten Wert zugreifen:

    Funktionen/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. Normalerweise werden Benutzer bei der Installation einer Erweiterung aufgefordert, Werte für Parameter anzugeben. Wenn Sie den Emulator jedoch zum Testen und Entwickeln verwenden, überspringen Sie den Installationsprozess und stellen stattdessen Werte für benutzerdefinierte Parameter mithilfe einer env Datei bereit.

    Öffnen Sie functions/integration-tests/extensions/rtdb-uppercase-messages.env und ersetzen Sie die GREETING -Definition durch Folgendes:

    MESSAGE_PATH=/msgs/{pushId}/original
    

    Beachten Sie, dass sich der obige Pfad vom Standardpfad und dem zuvor definierten Pfad unterscheidet. Dies dient lediglich dazu, sich selbst zu beweisen, dass Ihre Definition wirksam wird, wenn Sie Ihre aktualisierte Erweiterung ausprobieren.

  6. Starten Sie nun den Emulator neu und besuchen Sie erneut die Benutzeroberfläche des Datenbankemulators.

    Bearbeiten Sie den Stammknoten der Datenbank, indem Sie den oben definierten Pfad verwenden:

    • Feld: msgs
    • Typ: json
    • Wert: {"11": {"original": "recipe"}}

    Wenn Sie Ihre Datenbankänderungen speichern, sollte die makeuppercase Funktion der Erweiterung wie zuvor ausgelöst werden, aber jetzt sollte sie auch den benutzerdefinierten Parameter im Konsolenprotokoll ausgeben.

7. Stellen Sie Ereignis-Hooks für benutzerdefinierte Logik bereit

Als Erweiterungsautor haben Sie bereits gesehen, wie ein Firebase-Produkt Ihre von der Erweiterung bereitgestellte Logik auslösen kann: Die Erstellung neuer Datensätze in der Echtzeitdatenbank löst Ihre makeuppercase Funktion aus. Ihre Erweiterung kann eine analoge Beziehung zu den Benutzern haben, die Ihre Erweiterung installieren: Ihre Erweiterung kann eine vom Benutzer definierte Logik auslösen.

Eine Erweiterung kann synchrone Hooks , asynchrone Hooks oder beides bereitstellen. Synchrone Hooks bieten Benutzern die Möglichkeit, Aufgaben auszuführen, die den Abschluss einer der Funktionen der Erweiterung blockieren. Dies kann beispielsweise nützlich sein, um Benutzern die Möglichkeit zu geben, eine benutzerdefinierte Vorverarbeitung durchzuführen, bevor eine Erweiterung ihre Arbeit erledigt.

In dieser Anleitung fügen Sie Ihrer Erweiterung einen asynchronen Hook hinzu, der es Benutzern ermöglicht, ihre eigenen Verarbeitungsschritte zu definieren, die ausgeführt werden sollen, nachdem Ihre Erweiterung die Großbuchstabennachricht in die Echtzeitdatenbank geschrieben hat. Asynchrone Hooks verwenden Eventarc, um benutzerdefinierte Funktionen auszulösen. Erweiterungen deklarieren die Ereignistypen, die sie ausgeben, und wenn Benutzer die Erweiterung installieren, wählen sie aus, an welchen Ereignistypen sie interessiert sind. Wenn sie mindestens ein Ereignis auswählen, stellt Firebase im Rahmen des Installationsprozesses einen Eventarc-Kanal für die Erweiterung bereit . Benutzer können dann ihre eigenen Cloud-Funktionen bereitstellen, die diesen Kanal abhören und auslösen, wenn die Erweiterung neue Ereignisse veröffentlicht.

Befolgen Sie diese Schritte, um einen asynchronen Hook hinzuzufügen:

  1. Fügen Sie in der Datei extension.yaml den folgenden Abschnitt hinzu, der den einen Ereignistyp deklariert, den die Erweiterung ausgibt:

    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.
    

    Ereignistypen müssen universell eindeutig sein; Um die Eindeutigkeit sicherzustellen, benennen Sie Ihre Ereignisse immer im folgenden Format: <publisher-id>.<extension-id>.<version>.<description> . (Sie haben noch keine Herausgeber-ID, also verwenden Sie vorerst einfach test-publisher .)

  2. Fügen Sie am Ende der Funktion makeuppercase Code hinzu, der ein Ereignis des Typs veröffentlicht, den Sie gerade deklariert haben:

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

    Dieser Beispielcode macht sich die Tatsache zunutze, dass die Umgebungsvariable EVENTARC_CHANNEL nur definiert wird, wenn der Benutzer mindestens einen Ereignistyp aktiviert hat. Wenn EVENTARC_CHANNEL nicht definiert ist, versucht der Code nicht, Ereignisse zu veröffentlichen.

    Sie können einem Eventarc-Ereignis zusätzliche Informationen hinzufügen. Im obigen Beispiel verfügt das Ereignis über ein subject , das einen Verweis auf den neu erstellten Wert enthält, und über eine data , die die Original- und Großbuchstabennachrichten enthält. Benutzerdefinierte Funktionen, die das Ereignis auslösen, können diese Informationen nutzen.

  3. Normalerweise werden die Umgebungsvariablen EVENTARC_CHANNEL und EXT_SELECTED_EVENTS basierend auf den Optionen definiert, die der Benutzer während der Installation ausgewählt hat. Definieren Sie zum Testen mit dem Emulator diese Variablen manuell in der Datei rtdb-uppercase-messages.env :

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

An diesem Punkt haben Sie die erforderlichen Schritte zum Hinzufügen eines asynchronen Ereignis-Hooks zu Ihrer Erweiterung abgeschlossen.

Um diese neue Funktion auszuprobieren, die Sie gerade implementiert haben, übernehmen Sie in den nächsten Schritten die Rolle eines Benutzers, der die Erweiterung installiert:

  1. Initialisieren Sie im Verzeichnis functions/integration-tests ein neues Firebase-Projekt:

    firebase init functions
    

    Wenn Sie dazu aufgefordert werden, lehnen Sie die Einrichtung eines Standardprojekts ab, wählen Sie JavaScript als Cloud Functions-Sprache aus und installieren Sie die erforderlichen Abhängigkeiten. Dieses Projekt stellt das Projekt eines Benutzers dar, in dem Ihre Erweiterung installiert ist.

  2. Bearbeiten Sie integration-tests/functions/index.js und fügen Sie den folgenden Code ein:

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

    Dies ist ein Beispiel für eine Nachbearbeitungsfunktion, die ein Benutzer schreiben könnte. In diesem Fall wartet die Funktion darauf, dass die Erweiterung ein complete Ereignis veröffentlicht, und fügt bei Auslösung drei Ausrufezeichen zur neu in Großbuchstaben geschriebenen Nachricht hinzu.

  3. Starten Sie den Emulator neu. Der Emulator lädt die Funktionen der Erweiterung sowie die vom „Benutzer“ definierte Nachbearbeitungsfunktion.

  4. Besuchen Sie die Benutzeroberfläche des Datenbankemulators und bearbeiten Sie den Stammknoten der Datenbank mithilfe des oben definierten Pfads:

    • Feld: msgs
    • Typ: json
    • Wert: {"11": {"original": "recipe"}}

    Wenn Sie Ihre Datenbankänderungen speichern, sollten die makeuppercase Funktion der Erweiterung und die extraemphasis Funktion des Benutzers nacheinander ausgelöst werden, was dazu führt, dass das upper Feld den Wert RECIPE!!! erhält. .

8. Fügen Sie Lebenszyklus-Ereignishandler hinzu

Die Erweiterung, die Sie bisher geschrieben haben, verarbeitet Nachrichten, während sie erstellt werden. Was aber, wenn Ihre Benutzer bei der Installation der Erweiterung bereits über eine Datenbank mit Nachrichten verfügen? Firebase Extensions verfügt über eine Funktion namens Lifecycle Event Hooks , mit der Sie Aktionen auslösen können, wenn Ihre Erweiterung installiert, aktualisiert oder neu konfiguriert wird. In diesem Abschnitt verwenden Sie Lebenszyklus-Ereignis-Hooks, um die vorhandene Nachrichtendatenbank eines Projekts mit Nachrichten in Großbuchstaben aufzufüllen, wenn ein Benutzer Ihre Erweiterung installiert.

Firebase Extensions verwendet Cloud Tasks, um Ihre Lebenszyklus-Ereignishandler auszuführen. Sie definieren Event-Handler mithilfe von Cloud Functions. Wenn eine Instanz Ihrer Erweiterung eines der unterstützten Lebenszyklusereignisse erreicht und Sie einen Handler definiert haben, wird dieser Handler einer Cloud Tasks-Warteschlange hinzugefügt. Cloud Tasks führt den Handler dann asynchron aus. Während ein Lebenszyklus-Ereignishandler ausgeführt wird, meldet die Firebase-Konsole dem Benutzer, dass in der Erweiterungsinstanz eine Verarbeitungsaufgabe ausgeführt wird. Es liegt an Ihrer Handler-Funktion, dem Benutzer den aktuellen Status und den Abschluss der Aufgabe zu melden.

Gehen Sie wie folgt vor, um einen Lebenszyklus-Ereignishandler hinzuzufügen, der vorhandene Nachrichten auffüllt:

  1. Definieren Sie eine neue Cloud-Funktion, die durch Aufgabenwarteschlangenereignisse ausgelöst wird:

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

    Beachten Sie, dass die Funktion nur wenige Datensätze verarbeitet, bevor sie sich wieder zur Aufgabenwarteschlange hinzufügt. Dies ist eine häufig verwendete Strategie zum Umgang mit Verarbeitungsaufgaben, die nicht innerhalb des Timeout-Fensters einer Cloud-Funktion abgeschlossen werden können. Da Sie nicht vorhersagen können, wie viele Nachrichten ein Benutzer möglicherweise bereits in seiner Datenbank hat, wenn er Ihre Erweiterung installiert, eignet sich diese Strategie gut.

  2. Deklarieren Sie in der Datei extension.yaml Ihre Backfill-Funktion als Erweiterungsressource mit der Eigenschaft taskQueueTrigger :

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

    Dann deklarieren Sie die Funktion als Handler für das onInstall Lebenszyklusereignis:

    lifecycleEvents:
      onInstall:
        function: backfilldata
        processingMessage: Uppercasing existing messages
    
  3. Obwohl das Auffüllen vorhandener Nachrichten eine nette Sache ist, könnte die Erweiterung auch ohne funktionieren. In solchen Situationen sollten Sie die Ausführung der Lebenszyklus-Ereignishandler optional machen.

    Fügen Sie dazu einen neuen Parameter zu extension.yaml hinzu:

    - 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
    

    Überprüfen Sie dann zu Beginn der Backfill-Funktion den Wert des Parameters DO_BACKFILL und beenden Sie ihn vorzeitig, wenn er nicht festgelegt ist:

    Funktionen/index.js

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

Mit den oben genannten Änderungen wandelt die Erweiterung nun bei der Installation vorhandene Nachrichten in Großbuchstaben um.

Bis zu diesem Zeitpunkt haben Sie den Erweiterungsemulator verwendet, um Ihre Erweiterung zu entwickeln und laufende Änderungen zu testen. Allerdings überspringt der Erweiterungsemulator den Installationsprozess. Um Ihren onInstall Ereignishandler zu testen, müssen Sie die Erweiterung in einem echten Projekt installieren. Das ist aber auch gut so, denn mit der Hinzufügung dieser automatischen Backfill-Funktion ist die Tutorial-Erweiterung jetzt codevollständig!

9. Stellen Sie es in einem echten Firebase-Projekt bereit

Obwohl der Erweiterungsemulator ein großartiges Tool zum schnellen Iterieren einer Erweiterung während der Entwicklung ist, möchten Sie ihn irgendwann in einem echten Projekt ausprobieren.

Richten Sie dazu zunächst ein neues Projekt mit aktivierten Diensten ein:

  1. Fügen Sie in der Firebase-Konsole ein neues Projekt hinzu.
  2. Rüsten Sie Ihr Projekt auf den nutzungsbasierten Blaze-Plan auf. Für Cloud Functions for Firebase muss Ihr Projekt über ein Rechnungskonto verfügen. Daher benötigen Sie auch ein Rechnungskonto, um eine Erweiterung zu installieren.
  3. Aktivieren Sie in Ihrem neuen Projekt die Echtzeitdatenbank .
  4. Da Sie die Fähigkeit Ihrer Erweiterung testen möchten, vorhandene Daten bei der Installation aufzufüllen, importieren Sie einige Beispieldaten in Ihre Echtzeit-Datenbankinstanz:
    1. Laden Sie einige RTDB-Seeddaten herunter.
    2. Klicken Sie auf der Seite „Echtzeitdatenbank“ der Firebase-Konsole auf (mehr) > „JSON importieren“ und wählen Sie die Datei aus, die Sie gerade heruntergeladen haben.
  5. Um die Backfill-Funktion für die Verwendung der orderByChild -Methode zu aktivieren, konfigurieren Sie die Datenbank so, dass Nachrichten anhand des Werts von upper indiziert werden:

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

Installieren Sie nun Ihre Erweiterung von der lokalen Quelle in das neue Projekt:

  1. Erstellen Sie ein neues Verzeichnis für Ihr Firebase-Projekt:

    mkdir ~/extensions-live-test && cd ~/extensions-live-test
    
  2. Initialisieren Sie ein Firebase-Projekt im Arbeitsverzeichnis:

    firebase init database
    

    Wenn Sie dazu aufgefordert werden, wählen Sie das gerade erstellte Projekt aus.

  3. Installieren Sie die Erweiterung in Ihrem lokalen Firebase-Projekt:

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

    Hier können Sie sehen, wie das Benutzererlebnis bei der Installation einer Erweiterung mit dem Firebase CLI-Tool ist. Wählen Sie unbedingt „Ja“, wenn das Konfigurationstool fragt, ob Sie Ihre vorhandene Datenbank auffüllen möchten.

    Nachdem Sie Konfigurationsoptionen ausgewählt haben, speichert die Firebase-CLI Ihre Konfiguration im extensions und zeichnet den Speicherort der Erweiterungsquelle in der Datei firebase.json auf. Zusammen werden diese beiden Datensätze als Erweiterungsmanifest bezeichnet. Benutzer können das Manifest verwenden, um ihre Erweiterungskonfiguration zu speichern und sie in verschiedenen Projekten bereitzustellen.

  4. Stellen Sie Ihre Erweiterungskonfiguration für Ihr Live-Projekt bereit:

    firebase deploy --only extensions
    

Wenn alles gut geht, sollte die Firebase-CLI Ihre Erweiterung in Ihr Projekt hochladen und installieren. Nach Abschluss der Installation wird die Backfill-Aufgabe ausgeführt und in wenigen Minuten wird Ihre Datenbank mit Meldungen in Großbuchstaben aktualisiert. Fügen Sie der Nachrichtendatenbank einige neue Knoten hinzu und stellen Sie sicher, dass die Erweiterung auch für neue Nachrichten funktioniert.

10. Dokumentation schreiben

Bevor Sie Ihre Erweiterung für Benutzer freigeben, stellen Sie sicher, dass Sie ihnen genügend Dokumentation zur Verfügung stellen, damit sie erfolgreich sind.

Als Sie das Erweiterungsprojekt initialisierten, erstellte die Firebase-CLI Stub-Versionen der mindestens erforderlichen Dokumentation. Aktualisieren Sie diese Dateien, um die von Ihnen erstellte Erweiterung genau widerzuspiegeln.

extension.yaml

Sie haben diese Datei bereits aktualisiert, während Sie diese Erweiterung entwickelt haben, sodass Sie jetzt keine weiteren Aktualisierungen vornehmen müssen.

Übersehen Sie jedoch nicht die Bedeutung der in dieser Datei enthaltenen Dokumentation. Zusätzlich zu den entscheidenden Identifikationsinformationen einer Erweiterung – Name, Beschreibung, Autor, offizieller Repository-Speicherort – enthält die Datei extension.yaml eine benutzerbezogene Dokumentation für jede Ressource und jeden vom Benutzer konfigurierbaren Parameter. Diese Informationen werden Benutzern in der Firebase-Konsole, im Extensions Hub und in der Firebase-CLI angezeigt.

PREINSTALL.md

Geben Sie in dieser Datei Informationen an, die der Benutzer benötigt, bevor er Ihre Erweiterung installiert: Beschreiben Sie kurz, was die Erweiterung bewirkt, erläutern Sie etwaige Voraussetzungen und geben Sie dem Benutzer Informationen zu den Auswirkungen der Installation der Erweiterung auf die Rechnung. Wenn Sie eine Website mit zusätzlichen Informationen haben, ist dies auch ein guter Ort, um diese zu verlinken.

Der Text dieser Datei wird dem Benutzer im Extensions Hub und durch den firebase ext:info angezeigt.

Hier ist ein Beispiel einer PREINSTALL-Datei:

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

Diese Datei enthält Informationen, die für Benutzer nützlich sind, nachdem sie Ihre Erweiterung erfolgreich installiert haben: zum Beispiel weitere Einrichtungsschritte, ein Beispiel der Erweiterung in Aktion und so weiter.

Der Inhalt von POSTINSTALL.md wird in der Firebase-Konsole angezeigt, nachdem eine Erweiterung konfiguriert und installiert wurde. Sie können in dieser Datei auf Benutzerparameter verweisen und diese werden durch die konfigurierten Werte ersetzt.

Hier ist eine Beispiel-Nachinstallationsdatei für die Tutorial-Erweiterung:

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

Sie sollten auch die Änderungen, die Sie zwischen den Versionen einer Erweiterung vornehmen, in der Datei CHANGELOG.md dokumentieren.

Da die Beispielerweiterung noch nie zuvor veröffentlicht wurde, enthält das Änderungsprotokoll nur einen Eintrag:

## Version 0.0.1

Initial release of the _Convert messages to upper case_ extension.

README.md

Die meisten Erweiterungen bieten auch eine Readme-Datei für Benutzer, die das Repository der Erweiterung besuchen. Sie können diese Datei manuell schreiben oder mit dem Befehl eine Readme-Datei erstellen.

Für die Zwecke dieses Leitfadens überspringen Sie das Schreiben einer Readme-Datei.

Zusätzliche Dokumentation

Bei der oben besprochenen Dokumentation handelt es sich um die Mindestdokumentation, die Sie den Benutzern zur Verfügung stellen sollten. Viele Erweiterungen erfordern eine detailliertere Dokumentation, damit Benutzer sie erfolgreich verwenden können. Wenn dies der Fall ist, sollten Sie zusätzliche Dokumentation schreiben und diese an einem Ort hosten, auf den Sie die Benutzer verweisen können.

Für die Zwecke dieses Leitfadens können Sie das Verfassen einer ausführlicheren Dokumentation überspringen.

11. Auf Extensions Hub veröffentlichen

Nachdem der Code für Ihre Erweiterung nun vollständig und dokumentiert ist, können Sie ihn im Extensions Hub mit der Welt teilen. Aber da dies nur ein Tutorial ist, sollten Sie das nicht tun. Beginnen Sie mit dem Schreiben Ihrer eigenen Erweiterung. Nutzen Sie dabei das, was Sie hier und in der restlichen Dokumentation des Firebase Extensions-Herausgebers gelernt haben, und untersuchen Sie die Quelle der offiziellen, von Firebase geschriebenen Erweiterungen.

Wenn Sie bereit sind, Ihre Arbeit auf Extensions Hub zu veröffentlichen, gehen Sie wie folgt vor:

  1. Wenn Sie Ihre erste Erweiterung veröffentlichen, registrieren Sie sich als Herausgeber der Erweiterung . Wenn Sie sich als Herausgeber von Erweiterungen registrieren, erstellen Sie eine Herausgeber-ID, mit der Benutzer Sie schnell als Autor Ihrer Erweiterungen identifizieren können.
  2. Hosten Sie den Quellcode Ihrer Erweiterung an einem öffentlich überprüfbaren Ort. Wenn Ihr Code aus einer überprüfbaren Quelle verfügbar ist, kann Firebase Ihre Erweiterung direkt von diesem Ort aus veröffentlichen. Auf diese Weise stellen Sie sicher, dass Sie die aktuell veröffentlichte Version Ihrer Erweiterung veröffentlichen, und helfen Benutzern, indem sie ihnen die Möglichkeit geben, den Code zu überprüfen, den sie in ihren Projekten installieren.

    Derzeit bedeutet dies, dass Sie Ihre Erweiterung in einem öffentlichen GitHub-Repository verfügbar machen.

  3. Laden Sie Ihre Erweiterung mit dem firebase ext:dev:upload in Extensions Hub hoch.

  4. Gehen Sie in der Firebase-Konsole zu Ihrem Publisher-Dashboard, suchen Sie die Erweiterung, die Sie gerade hochgeladen haben, und klicken Sie auf „Im Extensions Hub veröffentlichen“. Dies erfordert eine Überprüfung durch unser Prüfpersonal, was einige Tage dauern kann. Bei Genehmigung wird die Erweiterung im Extensions Hub veröffentlicht. Bei einer Ablehnung erhalten Sie eine Nachricht mit einer Begründung; Anschließend können Sie die gemeldeten Probleme beheben und erneut zur Überprüfung einreichen.