Zmień przeznaczenie kodu Cloud Functions jako rozszerzenia Firebase

1. Zanim zaczniesz

Rozszerzenie Firebase wykonuje określone zadanie lub zestaw zadań w odpowiedzi na żądania HTTP lub zdarzenia wyzwalające z innych produktów Firebase i Google, takich jak Firebase Cloud Messaging, Cloud Firestore lub Pub/Sub.

Co zbudujesz

Podczas tych zajęć z programowania utworzysz rozszerzenie Firebase do geohashingu . Po wdrożeniu rozszerzenie konwertuje współrzędne X i Y na geohasze w odpowiedzi na zdarzenia Firestore lub poprzez wywołania funkcji, które można wywołać. Można to wykorzystać jako alternatywę dla wdrożenia biblioteki geofire na wszystkich platformach docelowych do przechowywania danych, oszczędzając czas.

Rozszerzenie geohash wyświetlane w konsoli Firebase

Czego się dowiesz

  • Jak wziąć istniejący kod Cloud Functions i przekształcić go w dystrybuowalne rozszerzenie Firebase
  • Jak skonfigurować plik extension.yaml
  • Jak przechowywać wrażliwe ciągi znaków (klucze API) w rozszerzeniu
  • Jak umożliwić twórcom rozszerzenia skonfigurowanie go pod własne potrzeby
  • Jak przetestować i wdrożyć rozszerzenie

Co będziesz potrzebował

  • Firebase CLI (instalacja i logowanie)
  • Konto Google, takie jak konto Gmail
  • Node.js i npm
  • Twoje ulubione środowisko programistyczne

2. Przygotuj się

Zdobądź kod

Wszystko, czego potrzebujesz do tego rozszerzenia, znajduje się w repozytorium GitHub. Aby rozpocząć, pobierz kod i otwórz go w swoim ulubionym środowisku programistycznym.

  1. Rozpakuj pobrany plik zip.
  2. Aby zainstalować wymagane zależności, otwórz terminal w katalogu functions i uruchom polecenie npm install .

Skonfiguruj Firebase

To ćwiczenie z programowania bardzo zachęca do korzystania z emulatorów Firebase. Jeśli chcesz wypróbować tworzenie rozszerzeń w prawdziwym projekcie Firebase, zobacz tworzenie projektu Firebase . To ćwiczenie z programowania korzysta z Cloud Functions, więc jeśli zamiast emulatorów używasz prawdziwego projektu Firebase, musisz przejść na plan cenowy Blaze .

Chcesz przejść dalej?

Możesz pobrać ukończoną wersję ćwiczeń z programowania. Jeśli utkniesz po drodze lub chcesz zobaczyć, jak wygląda ukończone rozszerzenie, sprawdź gałąź codelab-end w repozytorium GitHub lub pobierz ukończony plik ZIP.

3. Przejrzyj kod

  • Otwórz plik index.ts z pliku ZIP. Zauważ, że zawiera dwie deklaracje Cloud Functions.

Co robią te funkcje?

Te funkcje demonstracyjne służą do geohashingu. Biorą parę współrzędnych i przekształcają je w format zoptymalizowany pod kątem zapytań geograficznych w Firestore. Funkcje symulują użycie wywołania API, dzięki czemu możesz dowiedzieć się więcej o obsłudze wrażliwych typów danych w rozszerzeniach. Aby uzyskać więcej informacji, zapoznaj się z dokumentacją dotyczącą uruchamiania zapytań Geo na danych w Firestore .

Stałe funkcji

Stałe deklarowane są na początku, na górze pliku index.ts . Do niektórych z tych stałych odwołują się zdefiniowane wyzwalacze rozszerzenia.

indeks.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);

Wyzwalacz Firestore

Pierwsza funkcja w pliku index.ts wygląda następująco:

indeks.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,
        }
      );
  });

Ta funkcja jest wyzwalaczem Firestore . Gdy w bazie danych wystąpi zdarzenie zapisu, funkcja reaguje na to zdarzenie, wyszukując pola xv i pole yv , a jeśli oba te pola istnieją, oblicza geohash i zapisuje wynik w określonej lokalizacji wyjściowej dokumentu. Dokument wejściowy jest definiowany przez stałą users/{uid} , co oznacza, że ​​funkcja czyta każdy dokument zapisany w kolekcji users/ , a następnie przetwarza geohash dla tych dokumentów. Następnie wyprowadza skrót do pola skrótu w tym samym dokumencie.

Funkcje wywoływalne

Następna funkcja w pliku index.ts wygląda następująco:

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

Zwróć uwagę na funkcję onCall . Wskazuje, że ta funkcja jest funkcją wywoływalną , którą można wywołać z poziomu kodu aplikacji klienckiej. Ta wywoływalna funkcja pobiera parametry x i y i zwraca geohash. Chociaż ta funkcja nie będzie wywoływana bezpośrednio w tym ćwiczeniu z programowania, została ona tutaj uwzględniona jako przykład czegoś do skonfigurowania w rozszerzeniu Firebase.

4. Skonfiguruj plik rozszerzenie.yaml

Teraz, gdy wiesz, co robi kod Cloud Functions w Twoim rozszerzeniu, możesz spakować go do dystrybucji. Każde rozszerzenie Firebase zawiera plik extension.yaml , który opisuje, co robi rozszerzenie i jak się zachowuje.

Plik extension.yaml wymaga początkowych metadanych na temat rozszerzenia. Każdy z poniższych kroków pomoże Ci zrozumieć, co oznaczają wszystkie pola i dlaczego są potrzebne.

  1. Utwórz plik extension.yaml w katalogu głównym pobranego wcześniej projektu. Zacznij od dodania następujących elementów:
name: geohash-ext
version: 0.0.1
specVersion: v1beta  # Firebase Extensions specification version (do not edit)

Nazwa rozszerzenia jest używana jako podstawa identyfikatora instancji rozszerzenia (użytkownicy mogą zainstalować wiele wystąpień rozszerzenia, każde z własnym identyfikatorem). Firebase następnie generuje nazwy kont usług rozszerzenia i zasobów specyficznych dla rozszerzenia, korzystając z tego identyfikatora instancji. Numer wersji wskazuje wersję Twojego rozszerzenia. Musi być zgodne z wersjonowaniem semantycznym i należy je aktualizować za każdym razem, gdy wprowadzasz zmiany w funkcjonalności rozszerzenia. Wersja specyfikacji rozszerzenia służy do określenia, której specyfikacji rozszerzeń Firebase należy przestrzegać, w tym przypadku używana jest v1beta .

  1. Dodaj kilka przyjaznych dla użytkownika szczegółów do pliku YAML:
...

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

Nazwa wyświetlana jest przyjazną reprezentacją nazwy Twojego rozszerzenia, gdy programiści wchodzą w interakcję z Twoim rozszerzeniem. Opis zawiera krótki przegląd funkcji rozszerzenia. Po wdrożeniu rozszerzenia na stronie rozszerzenia.dev wygląda to mniej więcej tak:

Rozszerzenie Geohash Converter, jak widać na rozszerzeniach.dev

  1. Określ licencję na kod w swoim rozszerzeniu.
...

license: Apache-2.0  # The license you want for the extension
  1. Wskaż, kto jest autorem rozszerzenia i czy jego instalacja wymaga płatności:
...

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

billingRequired: true

Sekcja author służy do informowania użytkowników, do kogo mogą się zwrócić w przypadku problemów z rozszerzeniem lub chęci uzyskania dodatkowych informacji na jego temat. billingRequired jest wymaganym parametrem i musi mieć wartość true , ponieważ wszystkie rozszerzenia korzystają z Cloud Functions, co wymaga planu Blaze.

Obejmuje to minimalną liczbę pól wymaganych w pliku extension.yaml w celu zidentyfikowania tego rozszerzenia. Więcej informacji na temat innych informacji identyfikacyjnych, które można określić w rozszerzeniu, można znaleźć w dokumentacji .

5. Przekonwertuj kod Cloud Functions na zasób rozszerzeń

Zasób rozszerzenia to element, który Firebase tworzy w projekcie podczas instalacji rozszerzenia. Rozszerzenie jest wówczas właścicielem tych zasobów i ma określone konto usługi, które na nich operuje. W tym projekcie tymi zasobami są Cloud Functions, które należy zdefiniować w pliku extension.yaml , ponieważ rozszerzenie nie utworzy automatycznie zasobów z kodu w folderze funkcji. Jeśli Twoje funkcje Cloud Functions nie zostały jawnie zadeklarowane jako zasób, nie można ich wdrożyć podczas wdrażania rozszerzenia.

Zdefiniowana przez użytkownika lokalizacja wdrożenia

  1. Pozwól użytkownikowi określić lokalizację, w której chce wdrożyć to rozszerzenie, i zdecydować, czy lepiej będzie hostować rozszerzenie bliżej użytkowników końcowych, czy bliżej ich bazy danych. W pliku extension.yaml dołącz opcję wyboru lokalizacji.

rozszerzenie.yaml

Można teraz zapisać konfigurację zasobu funkcji.

  1. W pliku extension.yaml utwórz obiekt zasobu dla funkcji locationUpdate . Dołącz następujący tekst do pliku extension.yaml :
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 definiujesz jako nazwę funkcji zdefiniowaną w pliku index.ts projektu. Określasz type wdrażanej funkcji, czyli na razie zawsze powinna to być firebaseextensions.v1beta.function . Następnie definiujesz properties tej funkcji. pierwszą zdefiniowaną właściwością jest eventTrigger powiązany z tą funkcją. Aby odzwierciedlić to, co rozszerzenie jest obecnie obsługiwane, użyj eventType providers/cloud.firestore/eventTypes/document.write , który można znaleźć w dokumentacji rozszerzenia Write Cloud Functions . resource definiujesz jako lokalizację dokumentów. Ponieważ Twoim obecnym celem jest odzwierciedlenie tego, co istnieje w kodzie, ścieżka dokumentu nasłuchuje users/{uid} , z poprzedzającą domyślną lokalizacją bazy danych.

  1. Rozszerzenie wymaga uprawnień do odczytu i zapisu w bazie danych Firestore. Na samym końcu pliku extension.yaml określ role IAM, do których rozszerzenie powinno mieć dostęp, aby móc pracować z bazą danych w projekcie Firebase dewelopera.
roles:
  - role: datastore.user
    reason: Allows the extension to read / write to your Firestore instance.

Rola datastore.user pochodzi z listy obsługiwanych ról IAM dla rozszerzeń . Ponieważ rozszerzenie będzie czytać i zapisywać, rola datastore.user dobrze tu pasuje.

  1. Należy również dodać funkcję wywoływalną. W pliku extension.yaml utwórz nowy zasób we właściwości Resources. Te właściwości są specyficzne dla funkcji wywoływalnej:
  - name: callableHash
    type: firebaseextensions.v1beta.function
    properties:
      httpsTrigger: {}

Chociaż poprzedni zasób korzystał z eventTrigger , tutaj używasz httpsTrigger , który obejmuje zarówno funkcje wywoływalne, jak i funkcje HTTPS.

Kontrola kodu

Wymagało to dużo konfiguracji, aby plik extension.yaml pasował do wszystkiego, co zrobił kod w pliku index.ts . Tak powinien w tej chwili wyglądać gotowy plik extension.yaml :

rozszerzenie.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.

Kontrola stanu

W tym momencie masz skonfigurowane początkowe elementy funkcjonalne rozszerzenia, więc możesz go wypróbować za pomocą emulatorów Firebase!

  1. Jeśli jeszcze tego nie zrobiłeś, wywołaj npm run build w folderze funkcji pobranego projektu rozszerzeń.
  2. Utwórz nowy katalog w systemie hosta i połącz ten katalog z projektem Firebase za pomocą 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. Z tego samego katalogu uruchom firebase ext:install . Zamień /path/to/extension na bezwzględną ścieżkę do katalogu zawierającego plik extension.yaml .
firebase ext:install /path/to/extension
    This command does two things:
  • Monituje o określenie konfiguracji instancji rozszerzenia i tworzy plik *.env zawierający informacje o konfiguracji instancji.
  • Dodaje instancję rozszerzenia do sekcji extensions pliku firebase.json . Działa to jak mapa identyfikatora instancji na wersję rozszerzenia.
  • Ponieważ wdrażasz projekt lokalnie, możesz określić, że chcesz używać pliku lokalnego, a nie Menedżera Google Cloud Secret.

Zrzut ekranu przedstawiający proces instalacji rozszerzenia pokazujący, że plik lokalny jest używany do przechowywania wpisów tajnych podczas instalowania tego rozszerzenia

  1. Uruchom emulatory Firebase z nową konfiguracją:
firebase emulators:start
  1. Po uruchomieniu emulators:start przejdź do karty Firestore w widoku internetowym emulatorów.
  2. Dodaj dokument do kolekcji users z polem xv i yv .

Okno dialogowe wyświetlane w emulatorach Firebase umożliwiające rozpoczęcie kolekcji z identyfikatorem kolekcji zawierającym frazę

  1. Jeśli pomyślnie zainstalowałeś rozszerzenie, utworzy ono w dokumencie nowe pole o nazwie hash .

Kolekcja użytkowników zawierająca dokument użytkownika zawierający pole xv, yv i mieszające.

Posprzątaj, aby uniknąć konfliktów

  • Po zakończeniu testowania odinstaluj rozszerzenie — zamierzasz zaktualizować kod rozszerzenia i nie chcesz później powodować konfliktu z bieżącym rozszerzeniem.

Rozszerzenia umożliwiają jednoczesne zainstalowanie wielu wersji tego samego rozszerzenia, więc odinstalowując, masz pewność, że nie będzie konfliktów z wcześniej zainstalowanym rozszerzeniem.

firebase ext:uninstall geohash-ext

Obecne rozwiązanie działa, ale jak wspomniano na początku projektu, istnieje zakodowany na stałe klucz API do symulacji komunikacji z usługą. Jak można wykorzystać klucz API użytkownika końcowego zamiast oryginalnie dostarczonego? Czytaj dalej, aby się dowiedzieć.

6. Ustaw rozszerzenie jako konfigurowalne przez użytkownika

Na tym etapie ćwiczeń z kodowania masz rozszerzenie skonfigurowane do użytku z uproszczoną konfiguracją funkcji, które już napisałeś, ale co się stanie, jeśli użytkownik będzie chciał użyć szerokości i długości geograficznej zamiast y i x w polach wskazujących położenie na płaszczyźnie kartezjańskiej? Ponadto, w jaki sposób można nakłonić użytkownika końcowego do dostarczenia własnego klucza API, zamiast pozwalać mu korzystać z dostarczonego klucza API? Możesz szybko przekroczyć limit dla tego interfejsu API. W tym przypadku konfigurujesz i używasz parametrów.

Zdefiniuj podstawowe parametry w pliku extension.yaml

Zacznij od konwersji elementów, dla których programiści mogą potencjalnie mieć niestandardową konfigurację. Pierwszym z nich będą parametry XFIELD i YFIELD .

  1. W pliku extension.yaml dodaj następujący kod, który wykorzystuje parametry pól XFIELD i YFIELD . Parametry te znajdują się wewnątrz wcześniej zdefiniowanej właściwości params YAML:

rozszerzenie.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
  • param nazywa parametr w sposób widoczny dla Ciebie, producenta rozszerzenia. Użyj tej wartości później podczas określania wartości parametrów.
  • etykieta jest czytelnym dla człowieka identyfikatorem dla programisty, informującym go o działaniu parametru.
  • opis zawiera szczegółowy opis wartości. Ponieważ obsługuje to przecenę, może zawierać łącza do dodatkowej dokumentacji lub wyróżniać słowa, które mogą być ważne dla programisty.
  • type definiuje mechanizm wejściowy, w jaki sposób użytkownik ustawi wartość parametru. Istnieje wiele typów, w tym string , select , multiSelect , selectResource i secret . Aby dowiedzieć się więcej o każdej z tych opcji, zapoznaj się z dokumentacją .
  • validationRegex ogranicza wpis programisty do określonej wartości wyrażenia regularnego (w tym przykładzie jest to oparte na prostych wytycznych dotyczących nazw pól , które można znaleźć tutaj ); a jeśli to się nie uda...
  • validationErrorMessage ostrzega programistę o wartości błędu.
  • domyślna to wartość, jaka byłaby, gdyby programista nie wprowadził żadnego tekstu.
  • wymagane oznacza, że ​​programista nie musi wprowadzać żadnego tekstu.
  • immutable pozwala programiście zaktualizować to rozszerzenie i zmienić tę wartość. W takim przypadku programista powinien mieć możliwość zmiany nazw pól w miarę zmiany wymagań.
  • przykład daje wyobrażenie o tym, jak mogą wyglądać prawidłowe dane wejściowe.

To było wiele do zrozumienia!

  1. Przed dodaniem parametru specjalnego musisz dodać jeszcze trzy parametry do pliku extension.yaml .
  - 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

Zdefiniuj wrażliwe parametry

Teraz musisz zarządzać kluczem API określonym przez użytkownika. Jest to wrażliwy ciąg znaków, który nie powinien być przechowywany w funkcji jako zwykły tekst. Zamiast tego zapisz tę wartość w menedżerze tajnych obiektów w chmurze . To specjalne miejsce w chmurze, w którym przechowywane są zaszyfrowane sekrety i zapobiegają ich przypadkowemu wyciekowi. Wymaga to od programisty płacenia za korzystanie z tej usługi, ale dodaje dodatkową warstwę bezpieczeństwa w stosunku do kluczy API i potencjalnie ogranicza nieuczciwą działalność. Dokumentacja użytkownika ostrzega programistę, że jest to usługa płatna, dzięki czemu nie ma żadnych niespodzianek w rozliczeniach. Ogólnie rzecz biorąc, zastosowanie jest podobne do innych zasobów ciągów wymienionych powyżej. Jedyną różnicą jest typ, który nazywa się secret .

  • W pliku extension.yaml dodaj następujący kod:

rozszerzenie.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

Zaktualizuj atrybuty resource , aby używać parametrów

Jak wspomniano wcześniej, zasób (nie funkcja) definiuje sposób obserwowania zasobu, dlatego zasób locationUpdate musi zostać zaktualizowany, aby móc korzystać z nowego parametru.

  • W pliku extension.yaml dodaj następujący kod:

rozszerzenie.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}

Sprawdź plik extension.yaml

  • Przejrzyj plik extension.yaml . Powinno to wyglądać mniej więcej tak:

rozszerzenie.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.

Parametry dostępu w kodzie

Teraz, gdy wszystkie parametry są skonfigurowane w pliku extension.yaml , dodaj je do pliku index.ts .

  • W pliku index.ts zamień wartości domyślne na process.env.PARAMETER_NAME , który pobiera odpowiednie wartości parametrów i umieszcza je w kodzie funkcji wdrożonym w projekcie Firebase dewelopera.

indeks.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!;

Zwykle chcesz sprawdzić wartość null wartości zmiennych środowiskowych, ale w tym przypadku masz pewność, że wartości parametrów zostaną poprawnie skopiowane. Kod jest teraz skonfigurowany do pracy z parametrami rozszerzenia.

7. Utwórz dokumentację użytkownika

Przed przetestowaniem kodu na emulatorach lub na rynku rozszerzeń Firebase rozszerzenie należy udokumentować, aby programiści wiedzieli, co otrzymają, korzystając z rozszerzenia.

  1. Zacznij od utworzenia pliku PREINSTALL.md , który jest używany do opisu funkcjonalności, wszelkich wymagań wstępnych instalacji i potencjalnych konsekwencji rozliczeniowych.

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. Aby zaoszczędzić czas na pisaniu pliku README.md dla tego projektu, użyj wygodnej metody:
firebase ext:info . --markdown > README.md

Łączy to zawartość pliku PREINSTALL.md i dodatkowe szczegóły dotyczące rozszerzenia z pliku extension.yaml .

Na koniec poinformuj twórcę rozszerzenia o dodatkowych szczegółach dotyczących właśnie zainstalowanego rozszerzenia. Programista może otrzymać dodatkowe instrukcje i informacje po zakończeniu instalacji, a także może otrzymać szczegółowe zadania poinstalacyjne, takie jak konfiguracja kodu klienta tutaj.

  1. Utwórz plik POSTINSTALL.md , a następnie dołącz następujące informacje po instalacji:

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

Monitorowanie

Najlepszą praktyką jest monitorowanie aktywności zainstalowanego rozszerzenia, w tym sprawdzanie jego stanu, użycia i dzienników.

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

Spowoduje to ponowną kompilację funkcji, dzięki czemu najnowszy kod źródłowy będzie gotowy do wdrożenia wraz z rozszerzeniem po wdrożeniu w emulatorze lub bezpośrednio w Firebase.

Następnie utwórz nowy katalog, z którego chcesz przetestować rozszerzenie. Ponieważ rozszerzenie zostało opracowane na podstawie istniejących funkcji, nie przeprowadzaj testu z folderu, w którym rozszerzenie zostało skonfigurowane, ponieważ powoduje to również próbę wdrożenia wraz z nim funkcji i reguł Firebase.

Zainstaluj i przetestuj za pomocą emulatorów Firebase

  1. Utwórz nowy katalog w systemie hosta i połącz ten katalog z projektem Firebase za pomocą firebase init .
mkdir sample-proj
cd sample-proj
firebase init --project=projectID-or-alias
  1. Z tego katalogu uruchom firebase ext:install , aby zainstalować rozszerzenie. Zamień /path/to/extension na bezwzględną ścieżkę do katalogu zawierającego plik extension.yaml . Spowoduje to rozpoczęcie procesu instalacji rozszerzenia i utworzenie pliku .env zawierającego konfiguracje przed wypchnięciem konfiguracji do Firebase lub emulatorów.
firebase ext:install /path/to/extension
  • Ponieważ wdrażasz projekt lokalnie, określ, że chcesz używać pliku lokalnego, a nie Menedżera Google Cloud Secret.

da928c65ffa8ce15.png

  1. Uruchom lokalny pakiet emulatorów:
firebase emulators:start

Zainstaluj i przetestuj z prawdziwym projektem Firebase

Możesz zainstalować rozszerzenie w rzeczywistym projekcie Firebase. Do testów zaleca się użycie projektu testowego. Użyj tego przepływu pracy testowej, jeśli chcesz przetestować kompleksowy przepływ rozszerzenia lub jeśli wyzwalacz Twojego rozszerzenia nie jest jeszcze obsługiwany przez zestaw emulatorów Firebase (zobacz opcję Emulator rozszerzeń ). Emulatory obsługują obecnie funkcje wyzwalane żądaniami HTTP i funkcje wyzwalane zdarzeniami w tle dla Cloud Firestore, Realtime Database i Pub/Sub.

  1. Utwórz nowy katalog w systemie hosta i połącz ten katalog z projektem Firebase za pomocą firebase init .
cd ..
mkdir sample-proj
cd sample-proj
firebase init --project=projectID-or-alias
  1. Następnie z tego katalogu uruchom firebase ext:install , aby zainstalować rozszerzenie. Zamień /path/to/extension na bezwzględną ścieżkę do katalogu zawierającego plik extension.yaml . Spowoduje to rozpoczęcie procesu instalacji rozszerzenia i utworzenie pliku .env zawierającego konfiguracje przed wypchnięciem konfiguracji do Firebase lub emulatorów.
firebase ext:install /path/to/extension
  • Ponieważ chcesz wdrożyć bezpośrednio w Firebase i chcesz korzystać z Google Cloud Secret Manager, przed zainstalowaniem rozszerzenia musisz aktywować API Secret Manager .
  1. Wdróż w swoim projekcie Firebase.
firebase deploy

Przetestuj rozszerzenie

  1. Po uruchomieniu firebase deploy lub firebase emulators:start przejdź do karty Firestore w konsoli Firebase lub w widoku internetowym emulatorów, odpowiednio.
  2. Dodaj dokument do kolekcji określonej przez pole x i pole y . W tym przypadku zaktualizowane dokumenty znajdują się pod adresem u/{uid} z polem x o xv i polem y o wartości yv .

Ekran emulatorów Firebase, aby dodać rekord Firestore

  1. Jeśli instalacja rozszerzenia przebiegła pomyślnie, po zapisaniu obu pól rozszerzenie utworzy w dokumencie nowe pole o nazwie hash .

Ekran bazy danych Firestore z emulatora pokazujący dodany skrót

8. Gratulacje!

Pomyślnie przekonwertowałeś swoją pierwszą funkcję chmury na rozszerzenie Firebase!

Dodałeś plik extension.yaml i skonfigurowałeś go tak, aby programiści mogli wybrać sposób wdrożenia rozszerzenia. Następnie utworzono dokumentację dla użytkownika zawierającą wskazówki dotyczące tego, co twórcy rozszerzenia powinni zrobić przed skonfigurowaniem rozszerzenia i jakie kroki mogą być konieczne do wykonania po pomyślnym zainstalowaniu rozszerzenia.

Znasz już kluczowe kroki wymagane do przekształcenia funkcji Firebase w dystrybuowalne rozszerzenie Firebase.

Co dalej?