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 auslösende 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 Geohashing. Nach der Bereitstellung wandelt Ihre Erweiterung dann als Reaktion auf Firestore-Ereignisse oder durch Aufrufe aufrufbarer Funktionen X- und Y-Koordinaten in Geohashes um. Dies kann als Alternative zur Implementierung der Geofire-Bibliothek auf allen Zielplattformen zum Speichern von Daten verwendet werden, was Ihnen Zeit spart.

Die Geohash-Erweiterung in der Firebase Console

Lerninhalte

  • Vorhandenen Cloud Functions-Code in eine verteilbare Firebase-Erweiterung umwandeln
  • extension.yaml-Datei einrichten
  • Sensible Strings (API-Schlüssel) in einer Erweiterung speichern
  • Entwicklern der Erweiterung ermöglichen, sie an ihre Bedürfnisse anzupassen
  • Erweiterung testen und bereitstellen

Voraussetzungen

  • Firebase CLI (installieren und anmelden)
  • 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 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 Entwicklung von Erweiterungen mit einem echten Firebase-Projekt ausprobieren möchten, erstellen Sie ein Firebase-Projekt. In diesem Codelab werden Cloud Functions verwendet. Wenn Sie also ein echtes Firebase-Projekt anstelle der Emulatoren verwenden, müssen Sie auf den Blaze-Tarif upgraden.

Möchten Sie eine Lerneinheit überspringen?

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 codelab-end-Zweig 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 sie zwei Cloud Functions-Deklarationen enthält.

Was machen diese Funktionen?

Diese Demofunktionen werden für das Geohashing verwendet. Sie nehmen ein Koordinatenpaar und wandeln es in ein Format um, das für Geo-Abfragen 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 Geo-Abfragen für Daten in Firestore.

Funktionskonstanten

Konstanten werden früh deklariert, nämlich oben in der Datei index.ts. 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 eintritt, reagiert die Funktion darauf, indem sie nach einem xv-Feld und einem yv-Feld sucht. Wenn beide Felder vorhanden sind, wird der Geohash berechnet und die Ausgabe an einen angegebenen Dokumentausgabeort geschrieben. Das Eingabedokument wird durch die Konstante users/{uid} definiert. Das bedeutet, dass die Funktion jedes Dokument liest, das in die Sammlung users/ geschrieben wird, und dann einen Geohash für diese Dokumente verarbeitet. Anschließend wird der Hash in ein Hash-Feld im selben Dokument ausgegeben.

Aufrufbare Funktionen

Die nächste Funktion in der Datei index.ts 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. Das bedeutet, dass diese Funktion eine aufrufbare Funktion ist, die aus dem Code Ihrer Clientanwendung aufgerufen werden kann. Diese aufrufbare Funktion verwendet die Parameter x und y und gibt einen Geohash zurück. Diese Funktion wird in diesem Codelab zwar nicht direkt aufgerufen, ist aber als Beispiel für eine Konfiguration in der Firebase-Erweiterung enthalten.

4. Datei „extension.yaml“ einrichten

Nachdem Sie nun wissen, was der Cloud Functions-Code in Ihrer Erweiterung macht, können Sie ihn für die Verteilung verpacken. Jede Firebase-Erweiterung enthält eine extension.yaml-Datei, in der beschrieben wird, was die Erweiterung tut und wie sie sich verhält.

Für eine extension.yaml-Datei sind einige erste Metadaten zu Ihrer Erweiterung erforderlich. In den folgenden Schritten wird erläutert, 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 installieren, die jeweils eine eigene ID haben. Firebase generiert dann den Namen der Dienstkonten und der extensionspezifischen Ressourcen der Erweiterung anhand dieser Instanz-ID. Die Versionsnummer gibt die Version Ihrer Erweiterung an. Sie muss der semantischen Versionierung entsprechen und Sie müssen sie aktualisieren, wenn Sie Änderungen an der Funktionalität der Erweiterung vornehmen. Die Erweiterungsspezifikationsversion wird verwendet, um zu bestimmen, welcher Firebase-Erweiterungsspezifikation gefolgt werden soll. In diesem Fall wird v1beta verwendet.

  1. Fügen Sie der YAML-Datei einige benutzerfreundliche 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 benutzerfreundliche 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 das so aus:

Geohash Converter-Erweiterung auf extensions.dev

  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 geschrieben 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 Ihren 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 erforderlicher Parameter und muss auf true gesetzt werden, da alle Erweiterungen auf Cloud Functions basieren, für das der Blaze-Tarif erforderlich ist.

Dazu gehört die Mindestanzahl an Feldern, die in der Datei extension.yaml erforderlich sind, um diese Erweiterung zu identifizieren. Weitere Informationen zu anderen Identifikationsinformationen, die Sie in einer Erweiterung angeben können, finden Sie in der Dokumentation.

5. Cloud Functions-Code in eine Extensions-Ressource umwandeln

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

Benutzerdefinierter Bereitstellungsort

  1. Ermöglichen Sie dem Nutzer, den Speicherort für die Bereitstellung der Erweiterung anzugeben, und entscheiden Sie, ob es besser wäre, die Erweiterung näher an den Endnutzern oder näher an der Datenbank zu hosten. Fügen Sie in die extension.yaml-Datei die Option ein, einen Standort auszuwählen.

extension.yaml

Jetzt können Sie 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 der Datei extension.yaml Folgendes 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. Sie geben die type der Funktion an, die bereitgestellt wird. Diese sollte vorerst immer firebaseextensions.v1beta.function sein. Als Nächstes definieren Sie die properties dieser Funktion. Die erste Eigenschaft, die Sie definieren, ist die eventTrigger, die mit dieser Funktion verknüpft ist. Um die aktuelle Unterstützung der Erweiterung widerzuspiegeln, 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 das, was im Code vorhanden ist, spiegeln möchten, wird der Dokumentpfad auf users/{uid} gesetzt, wobei der Standarddatenbankspeicherort vorangestellt ist.

  1. Die Erweiterung benötigt Lese- und Schreibberechtigungen für die Firestore-Datenbank. Ganz am Ende der Datei extension.yaml geben Sie 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 Daten lesen und schreiben soll, ist die Rolle datastore.user hier gut geeignet.

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

In der vorherigen Ressource wurde eventTrigger verwendet. Hier verwenden Sie httpsTrigger, das sowohl aufrufbare Funktionen als auch HTTPS-Funktionen abdeckt.

Code-Check

Das war viel Konfiguration, damit extension.yaml mit allem übereinstimmt, was durch den Code in Ihrer index.ts-Datei erledigt wird. So sollte die fertige extension.yaml-Datei jetzt 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

An diesem Punkt haben Sie die ersten funktionalen Teile der Erweiterung eingerichtet und können sie mit den Firebase-Emulatoren ausprobieren.

  1. Falls noch nicht geschehen, rufen Sie npm run build im Ordner „functions“ des heruntergeladenen Erweiterungsprojekts auf.
  2. Erstellen Sie ein neues Verzeichnis auf Ihrem Hostsystem und verbinden Sie es mit Ihrem Firebase-Projekt über firebase init.
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 zu dem Verzeichnis, das Ihre extension.yaml-Datei enthält.
firebase ext:install /path/to/extension
    This command does two things:
  • Sie werden aufgefordert, die Konfiguration für die Erweiterungsinstanz anzugeben, und es wird eine *.env-Datei erstellt, die die Konfigurationsinformationen für die Instanz enthält.
  • Dadurch wird die Erweiterungsinstanz dem Abschnitt extensions Ihrer firebase.json hinzugefügt. Dies dient als Zuordnung von Instanz-ID zu Erweiterungsversion.
  • Da Sie das Projekt lokal bereitstellen, können Sie angeben, dass Sie eine lokale Datei anstelle von Google Cloud Secret Manager verwenden möchten.

Screenshot des Installationsvorgangs für die Erweiterung, der zeigt, dass beim Installieren dieser Erweiterung eine lokale Datei für Secrets verwendet wird

  1. Starten Sie die Firebase-Emulatoren mit der neuen Konfiguration:
firebase emulators:start
  1. Wechseln Sie nach der Ausführung von emulators:start in der Webansicht der Emulatoren zum Tab „Firestore“.
  2. Fügen Sie der Sammlung users ein Dokument mit dem Zahlenfeld xv und dem Zahlenfeld yv hinzu.

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

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

Die Sammlung „users“ mit einem Nutzerdokument mit den Feldern „xv“, „yv“ und „hash“.

Bereinigung durchführen, um Konflikte zu vermeiden

  • Wenn Sie mit dem Testen fertig sind, deinstallieren Sie die Erweiterung. Sie werden den Erweiterungscode aktualisieren und möchten später keine Konflikte mit der aktuellen Erweiterung haben.

Bei Erweiterungen können mehrere Versionen derselben Erweiterung gleichzeitig installiert sein. Durch die Deinstallation wird sichergestellt, dass es keine Konflikte mit einer zuvor installierten Erweiterung gibt.

firebase ext:uninstall geohash-ext

Die aktuelle Lösung funktioniert, aber wie zu Beginn des Projekts erwähnt, ist ein API-Schlüssel fest codiert, um die Kommunikation mit einem Dienst zu simulieren. Wie können Sie den API-Schlüssel des Endnutzers anstelle des ursprünglich angegebenen verwenden? Lies weiter, um es herauszufinden.

6. Erweiterung nutzerkonfigurierbar machen

An diesem Punkt des Codelabs haben Sie eine Erweiterung, die für die Verwendung mit der vordefinierten Einrichtung der Funktionen konfiguriert ist, die Sie bereits geschrieben haben. Was aber, wenn Ihr Nutzer Breiten- und Längengrad anstelle von y und x für die Felder verwenden möchte, die den Standort auf einer kartesischen Ebene angeben? Wie können Sie außerdem erreichen, dass der Endnutzer seinen eigenen API-Schlüssel angibt, anstatt den bereitgestellten API-Schlüssel zu verwenden? Das Kontingent für diese API kann schnell überschritten werden. In diesem Fall richten Sie Parameter ein und verwenden sie.

Grundlegende Parameter in der extension.yaml-Datei definieren

Beginnen Sie mit der Konvertierung der Elemente, für die Entwickler möglicherweise eine benutzerdefinierte Konfiguration haben. Die ersten beiden 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 Erweiterungsersteller sichtbar ist. Verwenden Sie diesen Wert später, wenn Sie die Parameterwerte angeben.
  • label ist eine für Menschen lesbare Kennung, mit der der Entwickler erfährt, was der Parameter bewirkt.
  • description enthält eine detaillierte Beschreibung des Werts. Da Markdown unterstützt wird, kann hier auf zusätzliche Dokumentation verlinkt oder wichtige Wörter für den Entwickler hervorgehoben werden.
  • Mit type wird der Eingabemechanismus dafür definiert, wie ein Nutzer den Parameterwert festlegt. Es gibt viele Typen, darunter string, select, multiSelect, selectResource und secret. Weitere Informationen zu den einzelnen Optionen
  • validationRegex schränkt die Entwicklereingabe auf einen bestimmten regulären Ausdruck ein (im Beispiel basiert er auf den einfachen Richtlinien für Feldnamen , die Sie hier finden). Wenn das fehlschlägt…
  • validationErrorMessage informiert den Entwickler über den Fehlerwert.
  • default ist der Wert, der verwendet wird, wenn der Entwickler keinen Text eingegeben hat.
  • Erforderlich 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 Feldnamen ändern können, wenn sich seine Anforderungen ändern.
  • Beispiel zeigt, wie eine gültige Eingabe aussehen kann.

Das war ganz schön viel auf einmal.

  1. Sie müssen der Datei extension.yaml drei weitere Parameter hinzufügen, bevor Sie einen speziellen Parameter hinzufügen können.
  - 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 API-Schlüssel verwalten, den der Nutzer angibt. Dies ist ein vertraulicher String, der nicht als Nur-Text 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 zu verhindern, dass sie versehentlich offengelegt werden. Dazu muss der Entwickler für die Nutzung dieses Dienstes bezahlen, aber es wird eine zusätzliche Sicherheitsebene für seine API-Schlüssel hinzugefügt und betrügerische Aktivitäten werden möglicherweise eingeschränkt. In der Nutzerdokumentation wird der Entwickler darauf hingewiesen, dass es sich um einen kostenpflichtigen Dienst handelt, damit es bei der Abrechnung keine Überraschungen gibt. Insgesamt ist die Verwendung ähnlich wie bei den anderen oben genannten String-Ressourcen. Der einzige Unterschied ist der 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

Aktualisieren Sie die resource-Attribute, um Parameter zu verwenden.

Wie bereits erwähnt, wird die Beobachtung der Ressource durch die Ressource selbst (nicht durch die Funktion) definiert. Daher muss die locationUpdate-Ressource aktualisiert werden, um den neuen Parameter zu verwenden.

  • 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 Datei extension.yaml konfiguriert wurden, fügen Sie sie der Datei index.ts 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 Nullprüfungen mit den Umgebungsvariablenwerten durchführen. In diesem Fall gehen Sie jedoch davon aus, dass die Parameterwerte korrekt kopiert werden. Der Code ist jetzt für die Verwendung mit den Erweiterungsparametern konfiguriert.

7. Nutzerdokumentation erstellen

Bevor Sie den Code auf Emulatoren oder im Firebase Extensions Marketplace testen, muss die Erweiterung dokumentiert werden, damit Entwickler wissen, was sie erwartet, wenn sie die Erweiterung verwenden.

  1. Erstellen Sie zuerst die Datei PREINSTALL.md. Darin werden die Funktionen, alle Voraussetzungen für die Installation und mögliche Abrechnungsimplikationen beschrieben.

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 von README.md für dieses Projekt zu sparen, verwenden Sie die Convenience-Methode:
firebase ext:info . --markdown > README.md

Dabei werden die Inhalte Ihrer PREINSTALL.md-Datei mit zusätzlichen Details zu Ihrer Erweiterung aus Ihrer extension.yaml-Datei kombiniert.

Informiere den Entwickler der Erweiterung über einige zusätzliche Details zur gerade installierten Erweiterung. Der Entwickler erhält nach Abschluss der Installation möglicherweise zusätzliche Anleitungen und Informationen sowie detaillierte Aufgaben nach der Installation, z. B. zum Einrichten von Clientcode.

  1. Erstellen Sie eine POSTINSTALL.md-Datei und fügen Sie die folgenden Informationen zur Nachinstallation 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

Als Best Practice können Sie die Aktivitäten Ihrer installierten Erweiterung überwachen, einschließlich der Überprüfung des Zustands, der Nutzung und der Logs.

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 für die Bereitstellung zusammen mit der Erweiterung bereit ist, 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 können. Da die Erweiterung aus vorhandenen Funktionen entwickelt wurde, sollten Sie sie nicht aus dem Ordner testen, in dem sie konfiguriert wurde, da sonst auch die Funktionen und Firebase-Regeln bereitgestellt werden.

Mit den Firebase-Emulatoren installieren und testen

  1. Erstellen Sie ein neues Verzeichnis auf Ihrem Hostsystem und verbinden Sie es mit Ihrem Firebase-Projekt über firebase init.
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 zu dem Verzeichnis, das Ihre extension.yaml-Datei enthält. Dadurch wird die Installation Ihrer Erweiterung gestartet und eine .env-Datei mit Ihren Konfigurationen erstellt, bevor die Konfiguration an Firebase oder die Emulatoren übertragen wird.
firebase ext:install /path/to/extension
  • Da Sie das Projekt lokal bereitstellen, geben Sie an, dass Sie eine lokale Datei anstelle von 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. Es wird empfohlen, für Ihre Tests ein Testprojekt zu verwenden. Verwenden Sie diesen Test-Workflow, wenn Sie den End-to-End-Ablauf Ihrer Erweiterung testen möchten oder wenn der Trigger Ihrer Erweiterung noch nicht von der Firebase Emulator Suite unterstützt wird (siehe Option für Erweiterungsemulator). Die Emulatoren unterstützen derzeit Funktionen, die durch HTTP-Anfragen ausgelöst werden, und Funktionen, die durch Hintergrundereignisse für Cloud Firestore, Realtime Database und Pub/Sub ausgelöst werden.

  1. Erstellen Sie ein neues Verzeichnis auf Ihrem Hostsystem und verbinden Sie es mit Ihrem Firebase-Projekt über firebase init.
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 zu dem Verzeichnis, das Ihre extension.yaml-Datei enthält. Dadurch wird die Installation Ihrer Erweiterung gestartet und eine .env-Datei mit Ihren Konfigurationen erstellt, bevor die Konfiguration an Firebase oder die Emulatoren übertragen wird.
firebase ext:install /path/to/extension
  • Da Sie 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. Stellen Sie die App in Ihrem Firebase-Projekt bereit.
firebase deploy

Erweiterung testen

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

Firebase-Emulatoren-Bildschirm zum Hinzufügen eines Firestore-Datensatzes

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

Firestore-Datenbankbildschirm aus einem Emulator, auf dem ein Hash hinzugefügt wurde

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 kennen jetzt die wichtigsten Schritte, die erforderlich sind, um eine Firebase-Funktion in eine verteilbare Firebase-Erweiterung zu konvertieren.

Wie geht es weiter?