Riutilizza il tuo codice Cloud Functions come estensione Firebase

1. Prima di iniziare

Un'estensione Firebase esegue un'attività specifica o una serie di attività in risposta a richieste HTTP o all'attivazione di eventi da altri prodotti Firebase e Google come Firebase Cloud Messaging, Cloud Firestore o Pub/Sub.

Cosa costruirai

In questo codelab creerai un'estensione Firebase per il geohashing . Una volta distribuita, l'estensione converte le coordinate X e Y in geohash in risposta agli eventi Firestore o tramite invocazioni di funzioni richiamabili. Questo può essere utilizzato come alternativa all'implementazione della libreria geofire su tutte le piattaforme di destinazione per l'archiviazione dei dati, risparmiando tempo.

L'estensione geohash visualizzata nella console Firebase

Cosa imparerai

  • Come prendere il codice Cloud Functions esistente e trasformarlo in un'estensione Firebase distribuibile
  • Come impostare un file extension.yaml
  • Come archiviare stringhe sensibili (chiavi API) in un'estensione
  • Come consentire agli sviluppatori dell'estensione di configurarla in base alle proprie esigenze
  • Come testare e distribuire l'estensione

Di cosa avrai bisogno

  • CLI Firebase (installazione e accesso)
  • Un account Google, come un account Gmail
  • Node.js e npm
  • Il tuo ambiente di sviluppo preferito

2. Preparati

Ottieni il codice

Tutto ciò di cui hai bisogno per questa estensione è in un repository GitHub. Per iniziare, prendi il codice e aprilo nel tuo ambiente di sviluppo preferito.

  1. Decomprimi il file zip scaricato.
  2. Per installare le dipendenze richieste, aprire il terminale nella directory functions ed eseguire il comando npm install .

Configura Firebase

Questo codelab incoraggia fortemente l'uso degli emulatori Firebase. Se vuoi provare lo sviluppo di estensioni con un vero progetto Firebase, consulta creare un progetto Firebase . Questo codelab utilizza Cloud Functions, quindi se utilizzi un vero progetto Firebase anziché gli emulatori, devi eseguire l'upgrade al piano tariffario Blaze .

Vuoi saltare avanti?

È possibile scaricare una versione completa del codelab. Se rimani bloccato lungo il percorso o se vuoi vedere come appare un'estensione completata, controlla il ramo codelab-end del repository GitHub o scarica lo zip completato.

3. Esaminare il codice

  • Apri il file index.ts dal file zip. Tieni presente che contiene due dichiarazioni Cloud Functions al suo interno.

Cosa fanno queste funzioni?

Queste funzioni demo vengono utilizzate per il geohashing. Prendono una coppia di coordinate e le trasformano in un formato ottimizzato per le query geografiche in Firestore. Le funzioni simulano l'uso di una chiamata API in modo che tu possa saperne di più sulla gestione dei tipi di dati sensibili nelle estensioni. Per ulteriori informazioni, consulta la documentazione sull'esecuzione di query geografiche sui dati in Firestore .

Costanti di funzione

Le costanti vengono dichiarate all'inizio, nella parte superiore del file index.ts . Ad alcune di queste costanti viene fatto riferimento nei trigger definiti dell'estensione.

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

Attivatore Firestore

La prima funzione nel file index.ts è simile alla seguente:

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

Questa funzione è un trigger di Firestore . Quando si verifica un evento di scrittura nel database, la funzione reagisce a tale evento cercando un campo xv e un campo yv e, se esistono entrambi i campi, calcola il geohash e scrive l'output in una posizione di output del documento specificata. Il documento di input è definito dalla costante users/{uid} , il che significa che la funzione legge ogni documento scritto nella raccolta users/ e quindi elabora un geohash per tali documenti. Quindi restituisce l'hash a un campo hash nello stesso documento.

Funzioni richiamabili

La funzione successiva nel file index.ts è simile alla seguente:

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

Notare la funzione onCall . Indica che questa funzione è una funzione richiamabile , che può essere chiamata dal codice dell'applicazione client. Questa funzione richiamabile accetta y x restituisce un geohash. Anche se questa funzione non verrà chiamata direttamente in questo codelab, è inclusa qui come esempio di qualcosa da configurare nell'estensione Firebase.

4. Configura un file extension.yaml

Ora che sai cosa fa il codice Cloud Functions nella tua estensione, sei pronto per impacchettarlo per la distribuzione. Ogni estensione Firebase viene fornita con un file extension.yaml che descrive cosa fa l'estensione e come si comporta.

Un file extension.yaml richiede alcuni metadati iniziali sulla tua estensione. Ciascuno dei passaggi seguenti ti aiuta a capire il significato di tutti i campi e il motivo per cui ne hai bisogno.

  1. Crea un file extension.yaml nella directory root del progetto scaricato in precedenza. Inizia aggiungendo quanto segue:
name: geohash-ext
version: 0.0.1
specVersion: v1beta  # Firebase Extensions specification version (do not edit)

Il nome dell'estensione viene utilizzato come base dell'ID istanza dell'estensione (gli utenti possono installare più istanze di un'estensione, ciascuna con il proprio ID). Firebase genera quindi il nome degli account di servizio dell'estensione e delle risorse specifiche dell'estensione utilizzando tale ID istanza. Il numero di versione indica la versione della tua estensione. Deve seguire il controllo delle versioni semantico ed è necessario aggiornarlo ogni volta che apporti modifiche alla funzionalità dell'estensione. La versione delle specifiche dell'estensione viene utilizzata per determinare quale specifica delle estensioni Firebase seguire, in questo caso viene utilizzata v1beta .

  1. Aggiungi alcuni dettagli intuitivi al file YAML:
...

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

Il nome visualizzato è una rappresentazione descrittiva del nome della tua estensione quando gli sviluppatori interagiscono con la tua estensione. La descrizione fornisce una breve panoramica di ciò che fa l'estensione. Quando l'estensione viene distribuita su extensions.dev , assomiglia a questo:

Estensione Geohash Converter come vista su extensions.dev

  1. Specifica la licenza per il codice nella tua estensione.
...

license: Apache-2.0  # The license you want for the extension
  1. Indica chi ha scritto l'estensione e se è necessaria o meno la fatturazione per installarla:
...

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

billingRequired: true

La sezione author viene utilizzata per far sapere ai tuoi utenti a chi rivolgersi nel caso in cui abbiano problemi con l'estensione o desiderino maggiori informazioni al riguardo. billingRequired è un parametro obbligatorio e deve essere impostato su true poiché tutte le estensioni si basano su Cloud Functions, che richiede il piano Blaze.

Questo copre il numero minimo di campi richiesti nel file extension.yaml per identificare questa estensione. Per ulteriori dettagli su altre informazioni identificative che è possibile specificare in un'estensione, consultare la documentazione .

5. Converti il ​​codice Cloud Functions in una risorsa Extensions

Una risorsa di estensione è un elemento che Firebase crea nel progetto durante l'installazione di un'estensione. L'interno possiede quindi tali risorse e dispone di un account di servizio specifico che opera su di esse. In questo progetto, tali risorse sono Cloud Functions, che devono essere definite nel file extension.yaml perché l'estensione non creerà automaticamente le risorse dal codice nella cartella Functions. Se le funzioni Cloud non sono dichiarate esplicitamente come risorsa, non possono essere distribuite quando viene distribuita l'estensione.

Posizione di distribuzione definita dall'utente

  1. Consenti all'utente di specificare la posizione in cui desidera distribuire questa estensione e decidere se sarebbe meglio ospitare l'estensione più vicino agli utenti finali o più vicino al database. Nel file extension.yaml , includi l'opzione per scegliere una posizione.

estensione.yaml

Ora sei pronto per scrivere la configurazione per la risorsa funzione.

  1. Nel file extension.yaml , crea un oggetto risorsa per la funzione locationUpdate . Aggiungi quanto segue al file 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}

Il name viene definito come il nome della funzione definita nel file index.ts del progetto. Specifica il type di funzione che viene distribuita, che per ora dovrebbe essere sempre firebaseextensions.v1beta.function . Quindi, definisci le properties di questa funzione. la prima proprietà che definisci è eventTrigger associato a questa funzione. Per rispecchiare ciò che l'estensione attualmente supporta, utilizza eventType di providers/cloud.firestore/eventTypes/document.write , che si trova in Write Cloud Functions per la documentazione dell'estensione . La resource viene definita come l'ubicazione dei documenti. Poiché il tuo obiettivo attuale è rispecchiare ciò che esiste nel codice, il percorso del documento ascolta users/{uid} , con la posizione del database predefinita che lo precede.

  1. L'estensione necessita di autorizzazioni di lettura e scrittura per il database Firestore. Alla fine del file extension.yaml , specifica i ruoli IAM a cui l'estensione dovrebbe avere accesso per poter lavorare con il database nel progetto Firebase dello sviluppatore.
roles:
  - role: datastore.user
    reason: Allows the extension to read / write to your Firestore instance.

Il ruolo datastore.user proviene dall'elenco dei ruoli IAM supportati per le estensioni . Poiché l'estensione sarà in lettura e scrittura, il ruolo datastore.user è adatto qui.

  1. È necessario aggiungere anche la funzione richiamabile. Nel file extension.yaml , crea una nuova risorsa nella proprietà resources. Queste proprietà sono specifiche per una funzione richiamabile:
  - name: callableHash
    type: firebaseextensions.v1beta.function
    properties:
      httpsTrigger: {}

Sebbene la risorsa precedente utilizzasse un eventTrigger , qui utilizzi un httpsTrigger , che copre sia le funzioni richiamabili che le funzioni HTTPS.

Controllo del codice

È stata necessaria molta configurazione per far sì che extension.yaml corrispondesse a tutto ciò che è stato fatto dal codice nel tuo file index.ts . Questo è come dovrebbe apparire il file extension.yaml completato in questo momento:

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

Controllo dello stato

A questo punto, hai configurato le parti funzionali iniziali dell'estensione, quindi puoi provarla effettivamente utilizzando gli emulatori Firebase!

  1. Se non lo hai già fatto, chiama npm run build nella cartella Functions del progetto delle estensioni scaricate.
  2. Crea una nuova directory sul tuo sistema host e collega quella directory al tuo progetto Firebase utilizzando 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. Dalla stessa directory, esegui firebase ext:install . Sostituisci /path/to/extension con il percorso assoluto della directory che contiene il file extension.yaml .
firebase ext:install /path/to/extension
    This command does two things:
  • Ti viene richiesto di specificare la configurazione per l'istanza dell'estensione e crea un file *.env che contiene le informazioni di configurazione per l'istanza.
  • Aggiunge l'istanza dell'estensione alla sezione delle extensions di firebase.json . Funziona come una mappa dell'ID istanza rispetto alla versione dell'estensione.
  • Poiché stai distribuendo il progetto localmente, puoi specificare che desideri utilizzare un file locale anziché Google Cloud Secret Manager.

Screenshot del processo di installazione dell'estensione che mostra che il file locale viene utilizzato per i segreti durante l'installazione di questa estensione

  1. Avvia gli emulatori Firebase con la nuova configurazione:
firebase emulators:start
  1. Dopo aver eseguito emulators:start , vai alla scheda Firestore nella visualizzazione web degli emulatori.
  2. Aggiungi un documento alla raccolta users con un campo numerico xv e un campo numerico yv .

Una finestra di dialogo visualizzata negli emulatori Firebase per avviare una raccolta con l'ID raccolta contenente la frase

  1. Se sei riuscito a installare l'estensione, l'estensione crea un nuovo campo chiamato hash nel documento.

La raccolta users con un documento utente con un campo xv, yv e hash.

Ripulire per evitare conflitti

  • Una volta terminato il test, disinstalla l'estensione: aggiornerai il codice dell'estensione e non vorrai entrare in conflitto con l'estensione corrente in seguito.

Le estensioni consentono di installare più versioni della stessa estensione contemporaneamente, quindi disinstallandole ti assicuri che non ci siano conflitti con un'estensione installata in precedenza.

firebase ext:uninstall geohash-ext

La soluzione attuale funziona, ma, come accennato all'inizio del progetto, esiste una chiave API codificata per simulare la comunicazione con un servizio. Come puoi utilizzare la chiave API dell'utente finale invece di quella fornita originariamente? Continuate a leggere per scoprirlo.

6. Rendere configurabile l'utente dell'interno

A questo punto nel codelab, hai un'estensione configurata per l'uso con l'impostazione supponente delle funzioni che hai già scritto, ma cosa succede se il tuo utente desidera utilizzare latitudine e longitudine invece di y e x per i campi che indicano il posizione su un piano cartesiano? Inoltre, come puoi convincere l'utente finale a fornire la propria chiave API, anziché lasciare che utilizzi la chiave API fornita? Potresti superare rapidamente la quota per quell'API. In questo caso, imposti e utilizzi i parametri.

Definire i parametri di base nel file extension.yaml

Inizia convertendo gli elementi per i quali gli sviluppatori potrebbero potenzialmente avere una configurazione personalizzata. Il primo sarebbero i parametri XFIELD e YFIELD .

  1. Nel file extension.yaml , aggiungi il codice seguente, che utilizza i parametri dei campi XFIELD e YFIELD . Questi parametri risiedono all'interno della proprietà YAML params precedentemente definita:

estensione.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 nomina il parametro in un modo che sia visibile a te, il produttore dell'estensione. Utilizzare questo valore in seguito quando si specificano i valori dei parametri.
  • label è un identificatore leggibile dall'uomo per lo sviluppatore per fargli sapere cosa fa il parametro.
  • description fornisce una descrizione dettagliata del valore. Poiché supporta il markdown, può collegarsi a documentazione aggiuntiva o evidenziare parole che potrebbero essere importanti per lo sviluppatore.
  • type definisce il meccanismo di input per il modo in cui un utente imposterebbe il valore del parametro. Esistono molti tipi, inclusi string , select , multiSelect , selectResource e secret . Per ulteriori informazioni su ciascuna di queste opzioni, consultare la documentazione .
  • validationRegex vincola la voce dello sviluppatore a un determinato valore regex (nell'esempio si basa sulle semplici linee guida per il nome del campo trovate qui ); e se fallisce...
  • validationErrorMessage avvisa lo sviluppatore del valore di errore.
  • default è quale sarebbe il valore se lo sviluppatore non avesse inserito alcun testo.
  • obbligatorio significa che allo sviluppatore non è richiesto di inserire alcun testo.
  • immutabile consente allo sviluppatore di aggiornare questa estensione e modificare questo valore. In questo caso, lo sviluppatore dovrebbe essere in grado di modificare i nomi dei campi man mano che cambiano i requisiti.
  • L'esempio fornisce un'idea di come potrebbe apparire un input valido.

C'era molto da capire!

  1. Hai altri tre parametri da aggiungere al file extension.yaml prima di aggiungere un parametro speciale.
  - 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

Definire i parametri sensibili

Ora devi gestire la chiave API specificata dall'utente. Si tratta di una stringa sensibile che non deve essere archiviata in testo normale nella funzione. Archivia invece questo valore nel gestore dei segreti cloud . Si tratta di una posizione speciale nel cloud che archivia i segreti crittografati e impedisce che vengano divulgati accidentalmente. Ciò richiede che lo sviluppatore paghi per l'utilizzo di questo servizio, ma aggiunge un ulteriore livello di sicurezza sulle chiavi API e limita potenzialmente le attività fraudolente. La documentazione per l'utente avverte lo sviluppatore che si tratta di un servizio a pagamento, in modo che non ci siano sorprese nella fatturazione. Nel complesso, l'utilizzo è simile a quello delle altre risorse stringa sopra menzionate. L'unica differenza è il tipo chiamato secret .

  • Nel file extension.yaml , aggiungi il seguente codice:

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

Aggiorna gli attributi resource per utilizzare i parametri

Come accennato in precedenza, la risorsa (non la funzione) definisce il modo in cui viene osservata la risorsa, quindi la risorsa locationUpdate deve essere aggiornata per poter utilizzare il nuovo parametro.

  • Nel file extension.yaml , aggiungi il seguente codice:

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

Controlla il file extension.yaml

  • Esamina il file extension.yaml . Dovrebbe assomigliare a qualcosa di simile a questo:

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

Accedere ai parametri nel codice

Ora che tutti i parametri sono configurati nel file extension.yaml , aggiungili al file index.ts .

  • Nel file index.ts , sostituisci i valori predefiniti con process.env.PARAMETER_NAME , che recupera i valori dei parametri appropriati e li popola nel codice della funzione distribuito sul progetto Firebase dello sviluppatore.

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

Normalmente, si desidera eseguire controlli nulli con i valori delle variabili di ambiente, ma in questo caso si ha fiducia che i valori dei parametri vengano copiati correttamente. Il codice è ora configurato per funzionare con i parametri dell'estensione.

7. Creare la documentazione per l'utente

Prima di testare il codice sugli emulatori o nel marketplace delle estensioni Firebase, l'estensione deve essere documentata in modo che gli sviluppatori sappiano cosa ottengono quando utilizzano l'estensione.

  1. Inizia creando il file PREINSTALL.md , utilizzato per descrivere la funzionalità, eventuali prerequisiti per l'installazione e potenziali implicazioni per la fatturazione.

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. Per risparmiare tempo nella scrittura del README.md per questo progetto, utilizzare il metodo pratico:
firebase ext:info . --markdown > README.md

Questo combina il contenuto del tuo file PREINSTALL.md e ulteriori dettagli sulla tua estensione dal tuo file extension.yaml .

Infine, informa lo sviluppatore dell'estensione su alcuni dettagli aggiuntivi riguardanti l'estensione appena installata. Lo sviluppatore potrebbe ottenere alcune istruzioni e informazioni aggiuntive dopo aver completato l'installazione e alcune attività post-installazione dettagliate come l'impostazione del codice client qui.

  1. Crea un file POSTINSTALL.md e includi le seguenti informazioni post-installazione:

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

Monitoraggio

Come best practice, puoi monitorare l'attività dell'estensione installata, inclusi i controlli sulla sua integrità, utilizzo e registri.

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

Ciò ricompila le funzioni in modo che il codice sorgente più recente sia pronto per la distribuzione insieme all'estensione quando viene distribuito direttamente su un emulatore o su Firebase.

Successivamente, crea una nuova directory da cui testare l'estensione. Poiché l'estensione è stata sviluppata da funzioni esistenti, non eseguire il test dalla cartella in cui è stata configurata l'estensione poiché tenta anche di distribuire le funzioni e le regole Firebase insieme ad essa.

Installa e testa con gli emulatori Firebase

  1. Crea una nuova directory sul tuo sistema host e collega quella directory al tuo progetto Firebase utilizzando firebase init .
mkdir sample-proj
cd sample-proj
firebase init --project=projectID-or-alias
  1. Da quella directory, esegui firebase ext:install per installare l'estensione. Sostituisci /path/to/extension con il percorso assoluto della directory che contiene il file extension.yaml . Questo avvia il processo di installazione per la tua estensione e crea un file .env che contiene le tue configurazioni prima di inviare la configurazione a Firebase o agli emulatori.
firebase ext:install /path/to/extension
  • Poiché stai distribuendo il progetto localmente, specifica che desideri utilizzare un file locale anziché Google Cloud Secret Manager.

da928c65ffa8ce15.png

  1. Avvia la suite dell'emulatore locale:
firebase emulators:start

Installa e testa con un vero progetto Firebase

Puoi installare la tua estensione in un progetto Firebase reale. Si consiglia di utilizzare un progetto di test per i test. Utilizza questo flusso di lavoro di test se desideri testare il flusso end-to-end della tua estensione o se il trigger della tua estensione non è ancora supportato dalla suite di emulazione Firebase (vedi l' opzione Emulatore di estensioni ). Gli emulatori attualmente supportano funzioni attivate da richieste HTTP e funzioni attivate da eventi in background per Cloud Firestore, Realtime Database e Pub/Sub.

  1. Crea una nuova directory sul tuo sistema host e collega quella directory al tuo progetto Firebase utilizzando firebase init .
cd ..
mkdir sample-proj
cd sample-proj
firebase init --project=projectID-or-alias
  1. Quindi, da quella directory, esegui firebase ext:install per installare l'estensione. Sostituisci /path/to/extension con il percorso assoluto della directory che contiene il file extension.yaml . Questo avvia il processo di installazione per la tua estensione e crea un file .env che contiene le tue configurazioni prima di inviare la configurazione a Firebase o agli emulatori.
firebase ext:install /path/to/extension
  • Poiché desideri eseguire la distribuzione direttamente su Firebase e utilizzare Google Cloud Secret Manager, devi attivare l'API Secret Manager prima di installare l'estensione.
  1. Distribuisci al tuo progetto Firebase.
firebase deploy

Prova l'estensione

  1. Dopo aver eseguito firebase deploy o firebase emulators:start , vai alla scheda Firestore della console Firebase o della visualizzazione Web degli emulatori, a seconda dei casi.
  2. Aggiungi un documento alla raccolta specificata dai campi x e y . In questo caso, i documenti aggiornati si trovano in u/{uid} con un campo x di xv e un campo y di yv .

Schermata degli emulatori Firebase per aggiungere un record Firestore

  1. Se l'installazione dell'estensione è riuscita, l'estensione crea un nuovo campo chiamato hash nel documento dopo aver salvato i due campi.

Schermata del database Firestore da un emulatore che mostra l'hash aggiunto

8. Congratulazioni!

Hai convertito con successo la tua prima Cloud Function in un'estensione Firebase!

Hai aggiunto un file extension.yaml e lo hai configurato in modo che gli sviluppatori possano selezionare come desiderano che venga distribuita la tua estensione. Hai quindi creato la documentazione per l'utente che fornisce indicazioni su cosa dovrebbero fare gli sviluppatori dell'estensione prima di configurare l'estensione e quali passaggi potrebbero dover eseguire dopo aver installato correttamente l'estensione.

Ora conosci i passaggi chiave necessari per convertire una funzione Firebase in un'estensione Firebase distribuibile.

Qual è il prossimo?