Cloud Functions-Code als Firebase-Erweiterung wiederverwenden

1. Hinweis

Eine Firebase-Erweiterung führt eine bestimmte Aufgabe oder eine Reihe von Aufgaben als Reaktion auf HTTP-Anfragen oder ausgelöste Ereignisse von anderen Firebase- und Google-Produkten wie Firebase Cloud Messaging, Cloud Firestore oder Pub/Sub aus.

Umfang

In diesem Codelab erstellen Sie eine Firebase-Erweiterung für das Geohashing. Nach der Bereitstellung wandelt Ihre Erweiterung X- und Y-Koordinaten als Reaktion auf Firestore-Ereignisse oder durch aufrufbare Funktionsaufrufe in Geohashes um. Dies kann als Alternative zur Implementierung der GeoFire-Bibliothek auf allen Zielplattformen zum Speichern von Daten verwendet werden und spart Zeit.

Die Geohash-Erweiterung, die in der Firebase Console angezeigt wird

Aufgaben in diesem Lab

  • Vorhandenen Cloud Functions-Code in eine distributierbare Firebase-Erweiterung umwandeln
  • extension.yaml-Datei einrichten
  • Vertrauliche Strings (API-Schlüssel) in einer Erweiterung speichern
  • Entwicklern der Erweiterung erlauben, sie nach ihren Bedürfnissen zu konfigurieren
  • Erweiterung testen und bereitstellen

Voraussetzungen

  • Firebase CLI (Installation und Anmeldung)
  • Ein Google-Konto, z. B. ein Gmail-Konto
  • Node.js und npm
  • Ihre bevorzugte Entwicklungsumgebung

2. Einrichten

Code abrufen

Alles, was Sie für diese Erweiterung benötigen, befindet sich in einem GitHub-Repository. Kopieren Sie zuerst den Code und öffnen Sie ihn in Ihrer bevorzugten Entwicklungsumgebung.

  1. Entpacken Sie die heruntergeladene ZIP-Datei.
  2. Öffnen Sie das Terminal im Verzeichnis functions und führen Sie den Befehl npm install aus, um die erforderlichen Abhängigkeiten zu installieren.

Firebase einrichten

In diesem Codelab wird die Verwendung von Firebase-Emulatoren dringend empfohlen. Wenn Sie die Erweiterungsentwicklung mit einem echten Firebase-Projekt ausprobieren möchten, lesen Sie den Hilfeartikel Firebase-Projekt erstellen. In diesem Codelab werden Cloud Functions verwendet. Wenn Sie also anstelle der Emulatoren ein echtes Firebase-Projekt verwenden, müssen Sie auf den Blaze-Tarif umstellen.

Möchtest du vorspringen?

Sie können eine fertige Version des Codelabs herunterladen. Wenn Sie nicht weiterkommen oder sehen möchten, wie eine fertige Erweiterung aussieht, sehen Sie sich den Zweig codelab-end des GitHub-Repositorys an oder laden Sie die fertige ZIP-Datei herunter.

3. Code ansehen

  • Öffnen Sie die Datei index.ts aus der ZIP-Datei. Beachten Sie, dass es zwei Cloud Functions-Deklarationen enthält.

Welche Funktionen haben diese Schaltflächen?

Diese Demofunktionen werden für das Geohashing verwendet. Sie wandeln ein Koordinatenpaar in ein Format um, das für Geoabfragen in Firestore optimiert ist. Die Funktionen simulieren die Verwendung eines API-Aufrufs, damit Sie mehr über den Umgang mit sensiblen Datentypen in Erweiterungen erfahren können. Weitere Informationen finden Sie in der Dokumentation zum Ausführen von Geoabfragen für Daten in Firestore.

Funktionskonstanten

Konstanten werden früh deklariert, ganz oben in der index.ts-Datei. Auf einige dieser Konstanten wird in den definierten Triggern der Erweiterung verwiesen.

index.ts

import {firestore} from "firebase-functions";
import {initializeApp} from "firebase-admin/app";
import {GeoHashService, ResultStatusCode} from "./fake-geohash-service";
import {onCall} from "firebase-functions/v1/https";
import {fieldValueExists} from "./utils";

const documentPath = "users/{uid}";
const xField = "xv";
const yField = "yv";
const apiKey = "1234567890";
const outputField = "hash";

initializeApp();

const service = new GeoHashService(apiKey);

Firestore-Trigger

Die erste Funktion in der Datei index.ts sieht so aus:

index.ts

export const locationUpdate = firestore.document(documentPath)
  .onWrite((change) => {
    // item deleted
    if (change.after == null) {
      return 0;
    }
    // double check that both values exist for computation
    if (
      !fieldValueExists(change.after.data(), xField) ||
      !fieldValueExists(change.after.data(), yField)
    ) {
      return 0;
    }
    const x: number = change.after.data()![xField];
    const y: number = change.after.data()![yField];
    const hash = service.convertToHash(x, y);
    // This is to check whether the hash value has changed. If
    // it hasn't, you don't want to write to the document again as it
    // would create a recursive write loop.
    if (fieldValueExists(change.after.data(), outputField)
      && change.after.data()![outputField] == hash) {
      return 0;
    }
    return change.after.ref
      .update(
        {
          [outputField]: hash.hash,
        }
      );
  });

Diese Funktion ist ein Firestore-Trigger. Wenn in der Datenbank ein Schreibereignis auftritt, reagiert die Funktion darauf, indem sie nach einem xv-Feld und einem yv-Feld sucht. Wenn beide Felder vorhanden sind, berechnet sie den Geohash und schreibt die Ausgabe an einen angegebenen Speicherort für die Dokumentausgabe. Das Eingabedokument wird durch die Konstante users/{uid} definiert. Das bedeutet, dass die Funktion jedes Dokument liest, das in die Sammlung users/ geschrieben wurde, und dann einen Geohash für diese Dokumente verarbeitet. Der Hash wird dann in ein Hash-Feld im selben Dokument ausgegeben.

Aufrufbare Funktionen

Die nächste Funktion in der index.ts-Datei sieht so aus:

index.ts

export const callableHash = onCall((data, context) => {
  if (context.auth == undefined) {
    return {error: "Only authorized users are allowed to call this endpoint"};
  }
  const x = data[xField];
  const y = data[yField];
  if (x == undefined || y == undefined) {
    return {error: "Either x or y parameter was not declared"};
  }
  const result = service.convertToHash(x, y);
  if (result.status != ResultStatusCode.ok) {
    return {error: `Something went wrong ${result.message}`};
  }
  return {result: result.hash};
});

Beachten Sie die Funktion onCall. Sie gibt an, dass diese Funktion eine aufrufbare Funktion ist, die von Ihrem Clientanwendungscode aufgerufen werden kann. Diese aufrufbare Funktion nimmt die Parameter x und y entgegen und gibt einen Geohash zurück. Diese Funktion wird in diesem Codelab zwar nicht direkt aufgerufen, sie dient aber als Beispiel für eine Funktion, die in der Firebase-Erweiterung konfiguriert werden kann.

4. Datei „extension.yaml“ einrichten

Da Sie jetzt wissen, was der Cloud Functions-Code in Ihrer Erweiterung bewirkt, können Sie ihn für die Verteilung verpacken. Jede Firebase-Erweiterung enthält eine extension.yaml-Datei, in der ihre Funktion und ihr Verhalten beschrieben werden.

Für eine extension.yaml-Datei sind einige anfängliche Metadaten zu Ihrer Erweiterung erforderlich. In den folgenden Schritten erfahren Sie, was die einzelnen Felder bedeuten und warum Sie sie benötigen.

  1. Erstellen Sie im Stammverzeichnis des zuvor heruntergeladenen Projekts eine extension.yaml-Datei. Fügen Sie zuerst Folgendes hinzu:
name: geohash-ext
version: 0.0.1
specVersion: v1beta  # Firebase Extensions specification version (do not edit)

Der Name der Erweiterung wird als Grundlage für die Instanz-ID der Erweiterung verwendet. Nutzer können mehrere Instanzen einer Erweiterung mit jeweils einer eigenen ID installieren. Firebase generiert dann anhand dieser Instanz-ID den Namen der Dienstkonten und die erweiterungsspezifischen Ressourcen der Erweiterung. Die Versionsnummer gibt die Version Ihrer Erweiterung an. Sie muss dem semantischen Versionierungsschema folgen und Sie müssen sie aktualisieren, wenn Sie Änderungen an der Funktionalität der Erweiterung vornehmen. Anhand der Version der Erweiterungsspezifikation wird ermittelt, welcher Firebase-Erweiterungsspezifikation zu folgen ist. In diesem Fall wird v1beta verwendet.

  1. Fügen Sie der YAML-Datei einige nutzerfreundliche Details hinzu:
...

displayName: Latitude and longitude to GeoHash converter
description: A converter for changing your Latitude and longitude coordinates to geohashes.

Der Anzeigename ist eine nutzerfreundliche Darstellung des Namens Ihrer Erweiterung, wenn Entwickler mit Ihrer Erweiterung interagieren. Die Beschreibung gibt einen kurzen Überblick über die Funktion der Erweiterung. Wenn die Erweiterung auf extensions.dev bereitgestellt wird, sieht sie in etwa so aus:

Geohash-Konverter-Erweiterung, wie auf extensions.dev zu sehen

  1. Geben Sie die Lizenz für den Code in Ihrer Erweiterung an.
...

license: Apache-2.0  # The license you want for the extension
  1. Geben Sie an, wer die Erweiterung erstellt hat und ob für die Installation eine Abrechnung erforderlich ist:
...

author:
  authorName: AUTHOR_NAME
  url: https://github.com/Firebase

billingRequired: true

Im Abschnitt author können Sie Nutzern mitteilen, an wen sie sich wenden können, wenn sie Probleme mit der Erweiterung haben oder weitere Informationen dazu benötigen. billingRequired ist ein Pflichtparameter und muss auf true festgelegt werden, da alle Erweiterungen auf Cloud Functions basieren, für die der Blaze-Tarif erforderlich ist.

Dies entspricht der Mindestanzahl von Feldern, die in der extension.yaml-Datei zur Identifizierung dieser Erweiterung erforderlich sind. Weitere Informationen zu anderen identifizierenden Informationen, die Sie in einer Erweiterung angeben können, finden Sie in der Dokumentation.

5. Cloud Functions-Code in eine Erweiterungsressource konvertieren

Eine Erweiterungsressource ist ein Element, das Firebase während der Installation einer Erweiterung im Projekt erstellt. Die Erweiterung ist dann Inhaber dieser Ressourcen und hat ein bestimmtes Dienstkonto, das sie verwaltet. In diesem Projekt sind das Cloud Functions, die in der Datei extension.yaml definiert werden müssen, da die Erweiterung keine Ressourcen automatisch aus Code im Ordner „functions“ erstellt. Wenn Ihre Cloud Functions nicht explizit als Ressource deklariert sind, können sie nicht bereitgestellt werden, wenn die Erweiterung bereitgestellt wird.

Benutzerdefinierter Bereitstellungsort

  1. Der Nutzer kann den Speicherort angeben, an dem er diese Erweiterung bereitstellen möchte, und entscheiden, ob es besser ist, die Erweiterung näher an den Endnutzern oder näher an der Datenbank zu hosten. Fügen Sie in der extension.yaml-Datei die Option zum Auswählen eines Standorts hinzu.

extension.yaml

Sie können jetzt die Konfiguration für die Funktionsressource schreiben.

  1. Erstellen Sie in der Datei extension.yaml ein Ressourcenobjekt für die Funktion locationUpdate. Hängen Sie folgenden Abschnitt an die Datei extension.yaml an:
resources:
  - name: locationUpdate
    type: firebaseextensions.v1beta.function
    properties:
      eventTrigger:
        eventType: providers/cloud.firestore/eventTypes/document.write
        resource: projects/${PROJECT_ID}/databases/(default)/documents/users/{uid}

Sie definieren name als den Funktionsnamen, der in der Datei index.ts des Projekts definiert ist. Geben Sie die type der bereitgestellten Funktion an. Derzeit sollte das immer firebaseextensions.v1beta.function sein. Als Nächstes definieren Sie die properties dieser Funktion. Das erste Attribut, das Sie definieren, ist die eventTrigger, die mit dieser Funktion verknüpft ist. Um zu spiegeln, was die Erweiterung derzeit unterstützt, verwenden Sie die eventType von providers/cloud.firestore/eventTypes/document.write, die in der Dokumentation Cloud Functions für Ihre Erweiterung schreiben zu finden ist. Sie definieren resource als Speicherort der Dokumente. Da Sie derzeit das im Code vorhandene Element spiegeln möchten, überwacht der Dokumentpfad users/{uid}, wobei der Standardspeicherort der Datenbank davor steht.

  1. Die Erweiterung benötigt Lese- und Schreibberechtigungen für die Firestore-Datenbank. Geben Sie ganz am Ende der Datei extension.yaml die IAM-Rollen an, auf die die Erweiterung zugreifen soll, um mit der Datenbank im Firebase-Projekt des Entwicklers zu arbeiten.
roles:
  - role: datastore.user
    reason: Allows the extension to read / write to your Firestore instance.

Die Rolle datastore.user stammt aus der Liste der unterstützten IAM-Rollen für Erweiterungen. Da die Erweiterung lesen und schreiben wird, eignet sich die Rolle datastore.user gut.

  1. Außerdem muss die aufrufbare Funktion hinzugefügt werden. Erstellen Sie in der Datei extension.yaml unter der Property „resources“ eine neue Ressource. Diese Properties sind spezifisch für eine aufrufbare Funktion:
  - name: callableHash
    type: firebaseextensions.v1beta.function
    properties:
      httpsTrigger: {}

In der vorherigen Ressource wurde eine eventTrigger verwendet, hier wird eine httpsTrigger verwendet, die sowohl aufrufbare Funktionen als auch HTTPS-Funktionen abdeckt.

Codeprüfung

Es war eine Menge Konfiguration erforderlich, damit Ihre extension.yaml-Datei mit dem Code in Ihrer index.ts-Datei übereinstimmt. Die fertige extension.yaml-Datei sollte jetzt so aussehen:

extension.yaml

name: geohash-ext
version: 0.0.1
specVersion: v1beta  # Firebase Extensions specification version (do not edit)

displayName: Latitude and Longitude to GeoHash converter
description: A converter for changing your Latitude and Longitude coordinates to geohashes.

license: Apache-2.0  # The license you want for the extension

author:
  authorName: Sparky
  url: https://github.com/Firebase

billingRequired: true

resources:
  - name: locationUpdate
    type: firebaseextensions.v1beta.function
    properties:
      eventTrigger:
        eventType: providers/cloud.firestore/eventTypes/document.write
        resource: projects/${PROJECT_ID}/databases/(default)/documents/users/{uid}
  - name: callableHash
    type: firebaseextensions.v1beta.function
    properties:
      httpsTrigger: {}

roles:
  - role: datastore.user
    reason: Allows the extension to read / write to your Firestore instance.

Status prüfen

Sie haben jetzt die ersten funktionalen Teile der Erweiterung eingerichtet und können sie mit den Firebase-Emulatoren testen.

  1. Rufen Sie npm run build im Ordner „functions“ des heruntergeladenen Erweiterungsprojekts auf, falls noch nicht geschehen.
  2. Erstellen Sie ein neues Verzeichnis auf Ihrem Hostsystem und verknüpfen Sie es mit firebase init mit Ihrem Firebase-Projekt.
cd ..
mkdir sample-proj
cd sample-proj
firebase init --project=projectID-or-alias
    This command creates a `firebase.json` file in the directory. In the following steps, you push the configuration specified in this file to Firebase.
  1. Führen Sie im selben Verzeichnis firebase ext:install aus. Ersetzen Sie /path/to/extension durch den absoluten Pfad zum Verzeichnis, das die Datei extension.yaml enthält.
firebase ext:install /path/to/extension
    This command does two things:
  • Sie werden aufgefordert, die Konfiguration für die Erweiterungs-Instanz anzugeben. Daraufhin wird eine *.env-Datei mit den Konfigurationsinformationen für die Instanz erstellt.
  • Dadurch wird die Erweiterungs-Instanz dem Abschnitt extensions Ihrer firebase.json hinzugefügt. Dies dient als Zuordnung der Instanz-ID zur Erweiterungsversion.
  • Da Sie das Projekt lokal bereitstellen, können Sie angeben, dass Sie anstelle von Google Cloud Secret Manager eine lokale Datei verwenden möchten.

Screenshot des Installationsvorgangs einer Erweiterung, der zeigt, dass bei der Installation dieser Erweiterung eine lokale Datei für Secrets verwendet wird

  1. Starten Sie die Firebase-Emulatoren mit der neuen Konfiguration:
firebase emulators:start
  1. Nachdem Sie emulators:start ausgeführt haben, rufen Sie in der Webansicht der Emulatoren den Tab „Firestore“ auf.
  2. Fügen Sie der Sammlung users ein Dokument mit den Zahlenfeldern xv und yv hinzu.

Ein Dialogfeld, das in den Firebase-Emulatoren angezeigt wird, um eine Sammlung mit der Sammlungs-ID zu starten, die den Ausdruck enthält

  1. Wenn die Installation der Erweiterung erfolgreich war, wird im Dokument ein neues Feld mit dem Namen hash erstellt.

Die Sammlung „Nutzer“ mit einem Nutzerdokument mit den Feldern „xv“, „yv“ und „Hash“.

Bereinigung durchführen, um Konflikte zu vermeiden

  • Entfernen Sie die Erweiterung, sobald Sie mit den Tests fertig sind. Sie aktualisieren den Erweiterungscode und möchten später keine Konflikte mit der aktuellen Erweiterung haben.

Bei Erweiterungen können mehrere Versionen derselben Erweiterung gleichzeitig installiert werden. Wenn Sie eine Erweiterung deinstallieren, können Sie Konflikte mit einer zuvor installierten Erweiterung vermeiden.

firebase ext:uninstall geohash-ext

Die aktuelle Lösung funktioniert, aber wie bereits zu Beginn des Projekts erwähnt, gibt es einen hartcodierten API-Schlüssel, um die Kommunikation mit einem Dienst zu simulieren. Wie kannst du den API-Schlüssel des Endnutzers anstelle des ursprünglich angegebenen verwenden? Lies weiter, um mehr zu erfahren.

6. Erweiterung für Nutzer konfigurierbar machen

An diesem Punkt des Codelabs haben Sie eine Erweiterung, die für die Verwendung mit der voreingestellten Konfiguration der von Ihnen bereits geschriebenen Funktionen konfiguriert ist. Was ist aber, wenn Ihre Nutzer für die Felder, die den Standort auf einer kartesischen Ebene angeben, Breiten- und Längengrad anstelle von y und x verwenden möchten? Wie können Sie den Endnutzer dazu bringen, seinen eigenen API-Schlüssel anzugeben, anstatt den bereitgestellten API-Schlüssel zu verwenden? Sie könnten das Kontingent für diese API schnell überschreiten. In diesem Fall richten Sie Parameter ein und verwenden sie.

Grundlegende Parameter in der extension.yaml-Datei definieren

Konvertieren Sie zuerst die Elemente, für die Entwickler möglicherweise eine benutzerdefinierte Konfiguration haben. Die ersten sind die Parameter XFIELD und YFIELD.

  1. Fügen Sie in der Datei extension.yaml den folgenden Code hinzu, in dem die Feldparameter XFIELD und YFIELD verwendet werden. Diese Parameter befinden sich in der zuvor definierten YAML-Eigenschaft params:

extension.yaml

params:
  - param: XFIELD
    label: The X Field Name
    description: >-
      The X Field is also known as the **longitude** value. What does
      your Firestore instance refer to as the X value or the longitude
      value. If no value is specified, the extension searches for
      field 'xv'.
    type: string
    validationRegex: ^\D([0-9a-zA-Z_.]{0,375})$
    validationErrorMessage: >-
      The field can only contain uppercase or lowercase letter, numbers,
      _, and . characters and must be less than 1500 bytes long. The field
      must also not start with a number.
    default: xv
    required: false
    immutable: false
    example: xv
  - param: YFIELD
    label: The Y Field Name
    description: >-
      The Y Field is also known as the **latitude** value. What does
      your Firestore instance refer to as the Y value or the latitude
      value. If no value is specified, the extension searches for
      field 'yv'.
    type: string
    validationRegex: ^\D([0-9a-zA-Z_.]{0,375})$
    validationErrorMessage: >-
      The field can only contain uppercase or lowercase letter, numbers,
      _, and . characters and must be less than 1500 bytes long. The field
      must also not start with a number.
    default: yv
    required: false
    immutable: false
    example: yv
  • Mit param wird der Parameter so benannt, dass er für Sie als Ersteller der Erweiterung sichtbar ist. Verwenden Sie diesen Wert später bei der Angabe der Parameterwerte.
  • label ist eine für Menschen lesbare Kennung, die Entwicklern mitteilt, welche Funktion der Parameter hat.
  • description gibt eine detaillierte Beschreibung des Werts an. Da Markdown unterstützt wird, können Links zu zusätzlicher Dokumentation hinzugefügt oder Wörter hervorgehoben werden, die für die Entwickler wichtig sein könnten.
  • type definiert den Eingabemechanismus, mit dem ein Nutzer den Parameterwert festlegt. Es gibt viele Typen, darunter string, select, multiSelect, selectResource und secret. Weitere Informationen zu diesen Optionen finden Sie in der Dokumentation.
  • Mit validationRegex wird der Entwicklereintrag auf einen bestimmten regulären Ausdruck beschränkt (im Beispiel basiert er auf den hier aufgeführten Richtlinien für einfache Feldnamen). Andernfalls…
  • Mit validationErrorMessage wird der Entwickler über den Fehlerwert informiert.
  • default ist der Wert, der verwendet würde, wenn der Entwickler keinen Text eingegeben hätte.
  • required bedeutet, dass der Entwickler keinen Text eingeben muss.
  • Mit immutable kann der Entwickler diese Erweiterung aktualisieren und diesen Wert ändern. In diesem Fall sollte der Entwickler die Feldnamen ändern können, wenn sich seine Anforderungen ändern.
  • Beispiel gibt einen Eindruck davon, wie eine gültige Eingabe aussehen könnte.

Das war eine Menge zu verstehen.

  1. Sie müssen der Datei extension.yaml noch drei weitere Parameter hinzufügen, bevor Sie einen speziellen Parameter hinzufügen.
  - param: INPUTPATH
    label: The input document to listen to for changes
    description: >-
      This is the document where you write an x and y value to. Once
      that document has received a value, it notifies the extension to
      calculate a geohash and store that in an output document in a certain
      field. This accepts function [wildcard parameters](https://firebase.google.com/docs/functions/firestore-events#wildcards-parameters)
    type: string
    validationRegex: ^[^/]+(/[^/]*/[^/]*)*/[^/]+$
    validationErrorMessage: >-
      This must point to a document path, not a collection path from the root
      of the database. It must also not start or end with a '/' character.
    required: true
    immutable: false
    example: users/{uid}
  - param: OUTPUTFIELD
    label: Geohash field
    description: >-
      This specifies the field in the output document to store the geohash in.
    type: string
    validationRegex: ^\D([0-9a-zA-Z_.]{0,375})$
    validationErrorMessage: >-
      The field can only contain uppercase or lowercase letter, numbers,
      _, and . characters and must be less than 1500 bytes long. The field
      must also not start with a number.
    required: false
    default: hash
    immutable: false
    example: hash

Sensible Parameter definieren

Jetzt müssen Sie den vom Nutzer angegebenen API-Schlüssel verwalten. Dies ist ein vertraulicher String, der nicht als Klartext in der Funktion gespeichert werden sollte. Speichern Sie diesen Wert stattdessen im Cloud Secret Manager. Dies ist ein spezieller Speicherort in der Cloud, an dem verschlüsselte Secrets gespeichert werden, um ein versehentliches Leck zu verhindern. Dies erfordert, dass der Entwickler für die Nutzung dieses Dienstes bezahlt, bietet aber eine zusätzliche Sicherheitsebene für seine API-Schlüssel und schränkt möglicherweise betrügerische Aktivitäten ein. In der Nutzerdokumentation wird der Entwickler darauf hingewiesen, dass es sich um einen kostenpflichtigen Dienst handelt, damit es bei der Abrechnung keine Überraschungen gibt. Die Verwendung ähnelt insgesamt der der oben genannten anderen Stringressourcen. Der einzige Unterschied besteht im Typ, der secret heißt.

  • Fügen Sie in der Datei extension.yaml den folgenden Code hinzu:

extension.yaml

  - param: APIKEY
    label: GeohashService API Key
    description: >-
      Your geohash service API Key. Since this is a demo, and not a real
      service, you can use : 1234567890.
    type: secret
    required: true
    immutable: false

resource-Attribute mit Parametern aktualisieren

Wie bereits erwähnt, wird in der Ressource (nicht in der Funktion) festgelegt, wie die Ressource beobachtet wird. Daher muss die locationUpdate-Ressource aktualisiert werden, damit der neue Parameter verwendet werden kann.

  • Fügen Sie in der Datei extension.yaml den folgenden Code hinzu:

extension.yaml

## Change from this
  - name: locationUpdate
    type: firebaseextensions.v1beta.function
    properties:
      eventTrigger:
        eventType: providers/cloud.firestore/eventTypes/document.write
        resource: projects/${PROJECT_ID}/databases/(default)/documents/users/{uid}]

## To this
  - name: locationUpdate
    type: firebaseextensions.v1beta.function
    properties:
      eventTrigger:
        eventType: providers/cloud.firestore/eventTypes/document.write
        resource: projects/${PROJECT_ID}/databases/(default)/documents/${INPUTPATH}

extension.yaml-Datei prüfen

  • Prüfen Sie die Datei extension.yaml. Die Ausgabe sollte ungefähr so aussehen:

extension.yaml

name: geohash-ext
version: 0.0.1
specVersion: v1beta  # Firebase Extensions specification version (do not edit)

displayName: Latitude and Longitude to GeoHash converter
description: A converter for changing your Latitude and Longitude coordinates to geohashes.

license: Apache-2.0  # The license you want to use for the extension

author:
  authorName: Sparky
  url: https://github.com/Firebase

billingRequired: true

params:
  - param: XFIELD
    label: The X Field Name
    description: >-
      The X Field is also known as the **longitude** value. What does
      your Firestore instance refer to as the X value or the longitude
      value. If you don't provide a value for this field, the extension will use 'xv' as the default value.
    type: string
    validationRegex: ^\D([0-9a-zA-Z_.]{0,375})$
    validationErrorMessage: >-
      The field can only contain uppercase or lowercase letter, numbers,
      _, and . characters and must be less than 1500 bytes long. The field
      must also not start with a number.
    default: xv
    required: false
    immutable: false
    example: xv
  - param: YFIELD
    label: The Y Field Name
    description: >-
      The Y Field is also known as the **latitude** value. What does
      your Firestore instance refer to as the Y value or the latitude
      Value. If you don't provide a value for this field, the extension will use 'yv' as the default value.
    type: string
    validationRegex: ^\D([0-9a-zA-Z_.]{0,375})$
    validationErrorMessage: >-
      The field can only contain uppercase or lowercase letter, numbers,
      _, and . characters and must be less than 1500 bytes long. The field
      must also not start with a number.
    default: yv
    required: false
    immutable: false
    example: yv
  - param: INPUTPATH
    label: The input document to listen to for changes
    description: >-
      This is the document where you write an x and y value to. Once
      that document has been modified, it notifies the extension to
      compute a geohash and store that in an output document in a certain
      field. This accepts function [wildcard parameters](https://firebase.google.com/docs/functions/firestore-events#wildcards-parameters)
    type: string
    validationRegex: ^[^/]+(/[^/]*/[^/]*)*/[^/]+$
    validationErrorMessage: >-
      This must point to a document path, not a collection path from the root
      of the database. It must also not start or end with a '/' character.
    required: true
    immutable: false
    example: users/{uid}
  - param: OUTPUTFIELD
    label: Geohash field
    description: >-
      This specifies the field in the output document to store the geohash in.
    type: string
    validationRegex: ^\D([0-9a-zA-Z_.]{0,375})$
    validationErrorMessage: >-
      The field can only contain uppercase or lowercase letter, numbers,
      _, and . characters and must be less than 1500 bytes long. The field
      must also not start with a number.
    required: false
    default: hash
    immutable: false
    example: hash
  - param: APIKEY
    label: GeohashService API Key
    description: >-
      Your geohash service API Key. Since this is a demo, and not a real
      service, you can use : 1234567890.
    type: secret
    required: true
    immutable: false

resources:
  - name: locationUpdate
    type: firebaseextensions.v1beta.function
    properties:
      eventTrigger:
        eventType: providers/cloud.firestore/eventTypes/document.write
        resource: projects/${PROJECT_ID}/databases/(default)/documents/${INPUTPATH}
  - name: callableHash
    type: firebaseextensions.v1beta.function
    properties:
      httpsTrigger: {}

roles:
  - role: datastore.user
    reason: Allows the extension to read / write to your Firestore instance.

Auf Parameter im Code zugreifen

Nachdem alle Parameter in der extension.yaml-Datei konfiguriert wurden, fügen Sie sie der index.ts-Datei hinzu.

  • Ersetzen Sie in der Datei index.ts die Standardwerte durch process.env.PARAMETER_NAME. Dadurch werden die entsprechenden Parameterwerte abgerufen und in den Funktionscode eingefügt, der im Firebase-Projekt des Entwicklers bereitgestellt wird.

index.ts

// Replace this:
const documentPath = "users/{uid}";
const xField = "xv";
const yField = "yv";
const apiKey = "1234567890";
const outputField = "hash";

// with this:
const documentPath = process.env.INPUTPATH!; // this value is ignored since its read from the resource
const xField = process.env.XFIELD!;
const yField = process.env.YFIELD!;
const apiKey = process.env.APIKEY!;
const outputField = process.env.OUTPUTFIELD!;

Normalerweise sollten Sie Null-Prüfungen mit den Umgebungsvariablenwerten durchführen. In diesem Fall gehen Sie davon aus, dass die Parameterwerte korrekt kopiert wurden. Der Code ist jetzt für die Verwendung der Erweiterungsparameter konfiguriert.

7. Nutzerdokumentation erstellen

Bevor der Code auf Emulatoren oder im Firebase-Erweiterungen-Marktplatz getestet werden kann, muss die Erweiterung dokumentiert werden, damit Entwickler wissen, was sie bei der Verwendung der Erweiterung erhalten.

  1. Erstellen Sie zuerst die PREINSTALL.md-Datei, in der die Funktionen, alle Voraussetzungen für die Installation und mögliche Auswirkungen auf die Abrechnung beschrieben werden.

PREINSTALL.md

Use this extension to automatically convert documents with a latitude and
longitude to a geohash in your database. Additionally, this extension includes a callable function that allows users to make one-time calls
to convert an x,y coordinate into a geohash.

Geohashing is supported for latitudes between 90 and -90 and longitudes
between 180 and -180.

#### Third Party API Key

This extension uses a fictitious third-party API for calculating the
geohash. You need to supply your own API keys. (Since it's fictitious,
you can use 1234567890 as an API key).

#### Additional setup

Before installing this extension, make sure that you've [set up a Cloud
Firestore database](https://firebase.google.com/docs/firestore/quickstart) in your Firebase project.

After installing this extension, you'll need to:

- Update your client code to point to the callable geohash function if you
want to perform arbitrary geohashes.

Detailed information for these post-installation tasks are provided after
you install this extension.

#### 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:
 - Cloud Firestore
 - Cloud Functions (Node.js 16+ runtime. [See
FAQs](https://firebase.google.com/support/faq#extensions-pricing))
 - [Cloud Secret Manager](https://cloud.google.com/secret-manager/pricing)
  1. Um Zeit beim Schreiben der README.md für dieses Projekt zu sparen, verwenden Sie die praktische Methode:
firebase ext:info . --markdown > README.md

Dabei werden der Inhalt der Datei PREINSTALL.md und zusätzliche Details zur Erweiterung aus der Datei extension.yaml kombiniert.

Teilen Sie dem Entwickler der Erweiterung abschließend einige zusätzliche Details zur gerade installierten Erweiterung mit. Nach Abschluss der Installation erhält der Entwickler möglicherweise weitere Anweisungen und Informationen sowie detaillierte Aufgaben nach der Installation, z. B. zum Einrichten des Clientcodes.

  1. Erstellen Sie eine POSTINSTALL.md-Datei und fügen Sie die folgenden Informationen nach der Installation ein:

POSTINSTALL.md

Congratulations on installing the geohash extension!

#### Function information

* **Firestore Trigger** - ${function:locationUpdate.name} was installed
and is invoked when both an x field (${param:XFIELD}) and y field
(${param:YFIELD}) contain a value.

* **Callable Trigger** - ${function:callableHash.name} was installed and
can be invoked by writing the following client code:
 ```javascript
import { getFunctions, httpsCallable } from "firebase/functions";
const functions = getFunctions();
const geoHash = httpsCallable(functions, '${function:callableHash.name}');
geoHash({ ${param:XFIELD}: -122.0840, ${param:YFIELD}: 37.4221 })
  .then((result) => {
    // Read result of the Cloud Function.
    /** @type {any} */
    const data = result.data;
    const error = data.error;
    if (error != null) {
        console.error(`callable error : ${error}`);
    }
    const result = data.result;
    console.log(result);
  });

Monitoring

Sie sollten die Aktivitäten der installierten Erweiterung im Blick behalten, einschließlich Status, Nutzung und Protokollen.

The output rendering looks something like this when it's deployed:

<img src="img/82b54a5c6ca34b3c.png" alt="A preview of the latitude and longitude geohash converter extension in the firebase console"  width="957.00" />


## Test the extension with the full configuration
Duration: 03:00


It's time to make sure that the user-configurable extension is working the way it is intended.

* Change into the functions folder and ensure that the latest compiled version of the extensions exists. In the extensions project functions directory, call:

```console
npm run build

Dadurch werden die Funktionen neu kompiliert, sodass der aktuelle Quellcode zusammen mit der Erweiterung bereitgestellt werden kann, wenn sie in einem Emulator oder direkt in Firebase bereitgestellt wird.

Erstellen Sie als Nächstes ein neues Verzeichnis, in dem Sie die Erweiterung testen möchten. Da die Erweiterung aus vorhandenen Funktionen entwickelt wurde, sollten Sie sie nicht aus dem Ordner testen, in dem sie konfiguriert wurde, da dabei auch versucht wird, die Funktionen und Firebase-Regeln zusammen mit der Erweiterung bereitzustellen.

Mit den Firebase-Emulatoren installieren und testen

  1. Erstellen Sie ein neues Verzeichnis auf Ihrem Hostsystem und verknüpfen Sie es mit firebase init mit Ihrem Firebase-Projekt.
mkdir sample-proj
cd sample-proj
firebase init --project=projectID-or-alias
  1. Führen Sie in diesem Verzeichnis firebase ext:install aus, um die Erweiterung zu installieren. Ersetzen Sie /path/to/extension durch den absoluten Pfad zum Verzeichnis, das die extension.yaml-Datei enthält. Dadurch wird der Installationsprozess für Ihre Erweiterung gestartet und eine .env-Datei mit Ihren Konfigurationen erstellt, bevor die Konfiguration an Firebase oder an die Emulatoren gesendet wird.
firebase ext:install /path/to/extension
  • Da Sie das Projekt lokal bereitstellen, geben Sie an, dass Sie eine lokale Datei anstelle des Google Cloud Secret Manager verwenden möchten.

da928c65ffa8ce15.png

  1. Starten Sie die lokale Emulator-Suite:
firebase emulators:start

Mit einem echten Firebase-Projekt installieren und testen

Sie können Ihre Erweiterung in einem echten Firebase-Projekt installieren. Wir empfehlen, für die Tests ein Testprojekt zu verwenden. Verwenden Sie diesen Testablauf, wenn Sie den End-to-End-Vorgang Ihrer Erweiterung testen möchten oder der Trigger Ihrer Erweiterung noch nicht von der Firebase-Emulator-Suite unterstützt wird (siehe Emulatoroption für Erweiterungen). Die Emulatoren unterstützen derzeit HTTP-Anfrage- und Hintergrundereignis-ausgelöste Funktionen für Cloud Firestore, Realtime Database und Pub/Sub.

  1. Erstellen Sie ein neues Verzeichnis auf Ihrem Hostsystem und verknüpfen Sie es mit firebase init mit Ihrem Firebase-Projekt.
cd ..
mkdir sample-proj
cd sample-proj
firebase init --project=projectID-or-alias
  1. Führen Sie dann in diesem Verzeichnis firebase ext:install aus, um die Erweiterung zu installieren. Ersetzen Sie /path/to/extension durch den absoluten Pfad zum Verzeichnis, das die extension.yaml-Datei enthält. Dadurch wird der Installationsprozess für Ihre Erweiterung gestartet und eine .env-Datei mit Ihren Konfigurationen erstellt, bevor die Konfiguration an Firebase oder an die Emulatoren gesendet wird.
firebase ext:install /path/to/extension
  • Da Sie die Anwendung direkt in Firebase bereitstellen und den Google Cloud Secret Manager verwenden möchten, müssen Sie die Secret Manager API aktivieren, bevor Sie die Erweiterung installieren.
  1. In Ihrem Firebase-Projekt bereitstellen
firebase deploy

Erweiterung testen

  1. Nachdem Sie firebase deploy oder firebase emulators:start ausgeführt haben, rufen Sie den Firestore-Tab entweder in der Firebase Console oder in der Webansicht der Emulatoren auf.
  2. Fügen Sie der Sammlung, die durch das Feld x und das Feld y angegeben ist, ein Dokument hinzu. In diesem Fall befinden sich die aktualisierten Dokumente unter u/{uid} mit dem Wert xv für das Feld x und dem Wert yv für das Feld y.

Firebase Emulators-Bildschirm zum Hinzufügen eines Firestore-Eintrags

  1. Wenn die Installation der Erweiterung erfolgreich war, wird im Dokument nach dem Speichern der beiden Felder ein neues Feld mit dem Namen hash erstellt.

Firestore-Datenbankbildschirm eines Emulators mit hinzugefügtem Hash

8. Glückwunsch!

Sie haben Ihre erste Cloud-Funktion erfolgreich in eine Firebase-Erweiterung umgewandelt.

Sie haben eine extension.yaml-Datei hinzugefügt und so konfiguriert, dass Entwickler auswählen können, wie Ihre Erweiterung bereitgestellt werden soll. Anschließend haben Sie eine Nutzerdokumentation erstellt, in der beschrieben wird, was die Entwickler der Erweiterung vor der Einrichtung der Erweiterung tun sollten und welche Schritte sie nach der erfolgreichen Installation der Erweiterung ausführen müssen.

Sie wissen jetzt, welche Schritte erforderlich sind, um eine Firebase-Funktion in eine distributierbare Firebase-Erweiterung umzuwandeln.

Wie geht es weiter?