Réutilisez votre code Cloud Functions en tant qu'extension Firebase

1. Avant de commencer

Une extension Firebase effectue une tâche spécifique ou un ensemble de tâches en réponse à des requêtes HTTP ou au déclenchement d'événements provenant d'autres produits Firebase et Google tels que Firebase Cloud Messaging, Cloud Firestore ou Pub/Sub.

Ce que vous construirez

Dans cet atelier de programmation, vous allez créer une extension Firebase pour le géohashing . Une fois déployée, votre extension convertit ensuite les coordonnées X et Y en géohashes en réponse aux événements Firestore ou via des invocations de fonctions appelables. Cela peut être utilisé comme alternative à la mise en œuvre de la bibliothèque Geofire sur toutes vos plates-formes cibles pour stocker des données, ce qui vous fait gagner du temps.

L'extension geohash affichée dans la console Firebase

Ce que vous apprendrez

  • Comment prendre le code Cloud Functions existant et le transformer en une extension Firebase distribuable
  • Comment configurer un fichier extension.yaml
  • Comment stocker des chaînes sensibles (clés API) dans une extension
  • Comment permettre aux développeurs de l'extension de la configurer en fonction de leurs besoins
  • Comment tester et déployer l'extension

Ce dont vous aurez besoin

  • Firebase CLI (installation et connexion)
  • Un compte Google, comme un compte Gmail
  • Node.js et npm
  • Votre environnement de développement préféré

2. Préparez-vous

Obtenez le code

Tout ce dont vous avez besoin pour cette extension se trouve dans un dépôt GitHub. Pour commencer, récupérez le code et ouvrez-le dans votre environnement de développement préféré.

  1. Décompressez le fichier zip téléchargé.
  2. Pour installer les dépendances requises, ouvrez le terminal dans le répertoire functions et exécutez la commande npm install .

Configurer Firebase

Cet atelier de programmation encourage fortement l'utilisation d'émulateurs Firebase. Si vous souhaitez essayer le développement d'extensions avec un vrai projet Firebase, consultez créer un projet Firebase . Cet atelier de programmation utilise Cloud Functions. Par conséquent, si vous utilisez un véritable projet Firebase au lieu des émulateurs, vous devez passer au plan tarifaire Blaze .

Voulez-vous aller de l'avant ?

Vous pouvez télécharger une version complète de l'atelier de programmation. Si vous êtes bloqué en cours de route ou si vous voulez voir à quoi ressemble une extension terminée, consultez la branche codelab-end du référentiel GitHub ou téléchargez le zip terminé.

3. Vérifiez le code

  • Ouvrez le fichier index.ts à partir du fichier zip. Notez qu'il contient deux déclarations Cloud Functions.

A quoi servent ces fonctions ?

Ces fonctions de démonstration sont utilisées pour le géohashing. Ils prennent une paire de coordonnées et les transforment dans un format optimisé pour les requêtes géographiques dans Firestore. Les fonctions simulent l'utilisation d'un appel API afin que vous puissiez en savoir plus sur la gestion des types de données sensibles dans les extensions. Pour plus d'informations, consultez la documentation sur l'exécution de requêtes géographiques sur les données dans Firestore .

Constantes de fonction

Les constantes sont déclarées dès le début, en haut du fichier index.ts . Certaines de ces constantes sont référencées dans les déclencheurs définis de l'extension.

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

Déclencheur Firestore

La première fonction du fichier index.ts ressemble à ceci :

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

Cette fonction est un déclencheur Firestore . Lorsqu'un événement d'écriture se produit dans la base de données, la fonction réagit à cet événement en recherchant un champ xv et un champ yv et, si ces deux champs existent, elle calcule le géohash et écrit la sortie dans un emplacement de sortie de document spécifié. Le document d'entrée est défini par la constante users/{uid} , ce qui signifie que la fonction lit chaque document écrit dans la collection users/ , puis traite un geohash pour ces documents. Il envoie ensuite le hachage dans un champ de hachage dans le même document.

Fonctions appelables

La fonction suivante dans le fichier index.ts ressemble à ceci :

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

Notez la fonction onCall . Cela indique que cette fonction est une fonction appelable , qui peut être appelée depuis le code de votre application client. Cette fonction appelable prend les paramètres x et y et renvoie un geohash. Bien que cette fonction ne soit pas appelée directement dans cet atelier de programmation, elle est incluse ici à titre d'exemple de élément à configurer dans l'extension Firebase.

4. Configurez un fichier extension.yaml

Maintenant que vous savez ce que fait le code Cloud Functions de votre extension, vous êtes prêt à le conditionner pour la distribution. Chaque extension Firebase est livrée avec un fichier extension.yaml qui décrit ce que fait l'extension et comment elle se comporte.

Un fichier extension.yaml nécessite des métadonnées initiales sur votre extension. Chacune des étapes suivantes vous aide à comprendre la signification de tous les champs et pourquoi vous en avez besoin.

  1. Créez un fichier extension.yaml dans le répertoire racine du projet que vous avez téléchargé précédemment. Commencez par ajouter ce qui suit :
name: geohash-ext
version: 0.0.1
specVersion: v1beta  # Firebase Extensions specification version (do not edit)

Le nom de l'extension est utilisé comme base de l'ID d'instance de l'extension (les utilisateurs peuvent installer plusieurs instances d'une extension, chacune avec son propre ID). Firebase génère ensuite le nom des comptes de service de l'extension et des ressources spécifiques à l'extension à l'aide de cet ID d'instance. Le numéro de version indique la version de votre extension. Il doit suivre le versioning sémantique et vous devez le mettre à jour chaque fois que vous apportez des modifications aux fonctionnalités de l'extension. La version de la spécification d'extension est utilisée pour déterminer quelle spécification d'extension Firebase suivre. Dans ce cas, v1beta est utilisée.

  1. Ajoutez quelques détails conviviaux au fichier YAML :
...

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

Le nom d'affichage est une représentation conviviale du nom de votre extension lorsque les développeurs interagissent avec votre extension. La description donne un bref aperçu de ce que fait l'extension. Lorsque l'extension est déployée sur extensions.dev , elle ressemble à ceci :

Extension Geohash Converter comme on le voit sur extensions.dev

  1. Spécifiez la licence pour le code dans votre extension.
...

license: Apache-2.0  # The license you want for the extension
  1. Indiquez qui a écrit l'extension et si une facturation est requise ou non pour l'installer :
...

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

billingRequired: true

La section author est utilisée pour indiquer à vos utilisateurs à qui s'adresser s'ils rencontrent des problèmes avec l'extension ou souhaitent plus d'informations à ce sujet. billingRequired est un paramètre obligatoire et doit être défini sur true car toutes les extensions reposent sur Cloud Functions, qui nécessite le plan Blaze.

Cela couvre le nombre minimum de champs requis dans le fichier extension.yaml pour identifier cette extension. Pour plus de détails sur les autres informations d'identification que vous pouvez spécifier dans une extension, consultez la documentation .

5. Convertissez le code Cloud Functions en une ressource Extensions

Une ressource d'extension est un élément créé par Firebase dans le projet lors de l'installation d'une extension. L'extension est alors propriétaire de ces ressources et dispose d'un compte de service spécifique qui les exploite. Dans ce projet, ces ressources sont des Cloud Functions, qui doivent être définies dans le fichier extension.yaml car l'extension ne créera pas automatiquement de ressources à partir du code dans le dossier des fonctions. Si vos fonctions Cloud ne sont pas explicitement déclarées en tant que ressource, elles ne peuvent pas être déployées lorsque l'extension est déployée.

Emplacement de déploiement défini par l'utilisateur

  1. Permettez à l'utilisateur de spécifier l'emplacement où il souhaite déployer cette extension et de décider s'il serait préférable d'héberger l'extension plus près de ses utilisateurs finaux ou plus près de sa base de données. Dans le fichier extension.yaml , incluez l'option permettant de choisir un emplacement.

extension.yaml

Vous êtes maintenant prêt à écrire la configuration de la ressource de fonction.

  1. Dans le fichier extension.yaml , créez un objet ressource pour la fonction locationUpdate . Ajoutez ce qui suit au fichier 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}

Vous définissez le name comme le nom de la fonction défini dans le fichier index.ts du projet. Vous spécifiez le type de fonction en cours de déploiement, qui doit toujours être firebaseextensions.v1beta.function , pour l'instant. Ensuite, vous définissez les properties de cette fonction. la première propriété que vous définissez est le eventTrigger associé à cette fonction. Pour refléter ce que l'extension prend actuellement en charge, vous utilisez le eventType de providers/cloud.firestore/eventTypes/document.write , qui se trouve dans la documentation Write Cloud Functions de votre extension . Vous définissez la resource comme l'emplacement des documents. Puisque votre objectif actuel est de refléter ce qui existe dans le code, le chemin du document écoute users/{uid} , précédé de l'emplacement par défaut de la base de données.

  1. L'extension nécessite des autorisations de lecture et d'écriture pour la base de données Firestore. À la toute fin du fichier extension.yaml , spécifiez les rôles IAM auxquels l'extension doit avoir accès afin de travailler avec la base de données dans le projet Firebase du développeur.
roles:
  - role: datastore.user
    reason: Allows the extension to read / write to your Firestore instance.

Le rôle datastore.user provient de la liste des rôles IAM pris en charge pour les extensions . Étant donné que l'extension va lire et écrire, le rôle datastore.user convient ici.

  1. La fonction appelable doit également être ajoutée. Dans le fichier extension.yaml , créez une nouvelle ressource sous la propriété resources. Ces propriétés sont spécifiques à une fonction appelable :
  - name: callableHash
    type: firebaseextensions.v1beta.function
    properties:
      httpsTrigger: {}

Bien que la ressource précédente utilisait un eventTrigger , vous utilisez ici un httpsTrigger , qui couvre à la fois les fonctions appelables et les fonctions HTTPS.

Vérification des codes

Cela a demandé beaucoup de configuration pour que votre extension.yaml corresponde à tout ce qui est fait par le code dans votre fichier index.ts . Voici à quoi devrait ressembler le fichier extension.yaml terminé à ce moment :

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.

Vérification de l'état

À ce stade, vous avez configuré les éléments fonctionnels initiaux de l'extension, vous pouvez donc réellement l'essayer à l'aide des émulateurs Firebase !

  1. Si vous ne l'avez pas déjà fait, appelez npm run build dans le dossier fonctions du projet d'extensions téléchargé.
  2. Créez un nouveau répertoire sur votre système hôte et connectez ce répertoire à votre projet Firebase à l'aide 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. À partir du même répertoire, exécutez firebase ext:install . Remplacez /path/to/extension par le chemin absolu du répertoire qui contient votre fichier extension.yaml .
firebase ext:install /path/to/extension
    This command does two things:
  • Il vous invite à spécifier la configuration de l'instance d'extension et crée un fichier *.env contenant les informations de configuration de l'instance.
  • Il ajoute l'instance d'extension à la section extensions de votre firebase.json . Cela agit comme une carte de l’ID d’instance à la version de l’extension.
  • Puisque vous déployez le projet localement, vous pouvez spécifier que vous souhaitez utiliser un fichier local plutôt que Google Cloud Secret Manager.

Capture d'écran du processus d'installation de l'extension montrant que le fichier local est utilisé pour les secrets lors de l'installation de cette extension

  1. Démarrez les émulateurs Firebase avec la nouvelle configuration :
firebase emulators:start
  1. Après avoir exécuté emulators:start , accédez à l'onglet Firestore dans la vue Web des émulateurs.
  2. Ajoutez un document dans la collection users avec un champ numéro xv et un champ numéro yv .

Une boîte de dialogue qui s'affiche dans les émulateurs Firebase pour démarrer une collection avec l'ID de collection contenant la phrase

  1. Si vous avez réussi à installer l'extension, celle-ci crée un nouveau champ appelé hash dans le document.

La collection des utilisateurs avec un document utilisateur ayant un champ xv, yv et hash.

Faire le ménage pour éviter les conflits

  • Une fois les tests terminés, désinstallez l'extension. Vous allez mettre à jour le code de l'extension et vous ne souhaitez pas entrer en conflit avec l'extension actuelle ultérieurement.

Les extensions permettent d'installer plusieurs versions de la même extension à la fois, donc en désinstallant, vous vous assurez qu'il n'y a pas de conflits avec une extension précédemment installée.

firebase ext:uninstall geohash-ext

La solution actuelle fonctionne, mais comme mentionné au début du projet, il existe une clé API codée en dur pour simuler la communication avec un service. Comment utiliser la clé API de l'utilisateur final au lieu de celle fournie initialement ? Continuez à lire pour le découvrir.

6. Rendre l'extension configurable par l'utilisateur

À ce stade de l'atelier de programmation, vous disposez d'une extension configurée pour être utilisée avec la configuration avisée des fonctions que vous avez déjà écrites, mais que se passe-t-il si votre utilisateur souhaite utiliser la latitude et la longitude au lieu de y et x pour les champs indiquant le localisation sur un plan cartésien ? De plus, comment pouvez-vous amener l'utilisateur final à fournir sa propre clé API, plutôt que de le laisser consommer la clé API fournie ? Vous pourriez rapidement dépasser le quota de cette API. Dans ce cas, vous configurez et utilisez des paramètres.

Définir les paramètres de base dans le fichier extension.yaml

Commencez par convertir les éléments pour lesquels les développeurs peuvent potentiellement avoir une configuration personnalisée. Le premier serait les paramètres XFIELD et YFIELD .

  1. Dans le fichier extension.yaml , ajoutez le code suivant, qui utilise les paramètres de champ XFIELD et YFIELD . Ces paramètres se trouvent dans la propriété YAML params précédemment définie :

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
  • param nomme le paramètre d'une manière qui est visible pour vous, le producteur de l'extension. Utilisez cette valeur ultérieurement lors de la spécification des valeurs des paramètres.
  • label est un identifiant lisible par l'homme pour le développeur pour lui faire savoir ce que fait le paramètre.
  • description donne une description détaillée de la valeur. Étant donné que cela prend en charge le markdown, il peut créer un lien vers une documentation supplémentaire ou mettre en évidence des mots qui pourraient être importants pour le développeur.
  • type définit le mécanisme de saisie permettant à un utilisateur de définir la valeur du paramètre. Il existe de nombreux types, notamment string , select , multiSelect , selectResource et secret . Pour en savoir plus sur chacune de ces options, consultez la documentation .
  • validationRegex contraint l'entrée du développeur à une certaine valeur regex (dans l'exemple, elle est basée sur les directives simples de nom de champ trouvées ici ) ; et si cela échoue...
  • validationErrorMessage alerte le développeur de la valeur d'échec.
  • La valeur par défaut correspond à ce que serait la valeur si le développeur n'avait saisi aucun texte.
  • requis signifie que le développeur n’est pas tenu de saisir de texte.
  • immuable permet au développeur de mettre à jour cette extension et de modifier cette valeur. Dans ce cas, le développeur devrait pouvoir modifier les noms des champs à mesure que ses besoins changent.
  • L'exemple donne une idée de ce à quoi peut ressembler une entrée valide.

C'était beaucoup à comprendre !

  1. Vous disposez de trois paramètres supplémentaires à ajouter au fichier extension.yaml avant d'ajouter un paramètre spécial.
  - 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

Définir les paramètres sensibles

Maintenant, vous devez gérer la clé API spécifiée par l'utilisateur. Il s'agit d'une chaîne sensible qui ne doit pas être stockée en texte brut dans la fonction. Stockez plutôt cette valeur dans le gestionnaire de secrets Cloud . Il s'agit d'un emplacement spécial dans le cloud qui stocke les secrets cryptés et empêche leur fuite accidentelle. Cela oblige le développeur à payer pour l'utilisation de ce service, mais cela ajoute une couche de sécurité supplémentaire sur ses clés API et limite potentiellement les activités frauduleuses. La documentation utilisateur prévient le développeur qu'il s'agit d'un service payant, afin qu'il n'y ait pas de surprise au niveau de la facturation. Dans l’ensemble, l’utilisation est similaire aux autres ressources de chaînes mentionnées ci-dessus. La seule différence est le type appelé secret .

  • Dans le fichier extension.yaml , ajoutez le code suivant :

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

Mettre à jour les attributs resource pour utiliser des paramètres

Comme mentionné précédemment, la ressource (et non la fonction) définit la manière dont la ressource est observée. La ressource locationUpdate doit donc être mise à jour afin d'utiliser le nouveau paramètre.

  • Dans le fichier extension.yaml , ajoutez le code suivant :

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}

Vérifiez le fichier extension.yaml

  • Examinez le fichier extension.yaml . Ça devrait ressembler a quelque chose comme ca:

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.

Accéder aux paramètres dans le code

Maintenant que tous les paramètres sont configurés dans le fichier extension.yaml , ajoutez-les au fichier index.ts .

  • Dans le fichier index.ts , remplacez les valeurs par défaut par process.env.PARAMETER_NAME , qui récupère les valeurs de paramètres appropriées et les remplit dans le code de fonction déployé sur le projet Firebase du développeur.

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

Normalement, vous souhaitez effectuer des vérifications nulles avec les valeurs des variables d'environnement, mais dans ce cas, vous êtes sûr que les valeurs des paramètres sont correctement copiées. Le code est maintenant configuré pour fonctionner avec les paramètres d'extension.

7. Créer une documentation utilisateur

Avant de tester le code sur des émulateurs ou sur le marché des extensions Firebase, l'extension doit être documentée afin que les développeurs sachent ce qu'ils obtiennent lorsqu'ils utilisent l'extension.

  1. Commencez par créer le fichier PREINSTALL.md , qui est utilisé pour décrire la fonctionnalité, les conditions préalables à l'installation et les implications potentielles en matière de facturation.

PRÉINSTALLER.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. Pour gagner du temps lors de l'écriture du README.md pour ce projet, utilisez la méthode pratique :
firebase ext:info . --markdown > README.md

Cela combine le contenu de votre fichier PREINSTALL.md et des détails supplémentaires sur votre extension à partir de votre fichier extension.yaml .

Enfin, informez le développeur de l'extension de quelques détails supplémentaires concernant l'extension qui vient d'être installée. Le développeur peut obtenir des instructions et des informations supplémentaires après avoir terminé l'installation et peut effectuer des tâches post-installation détaillées, telles que la configuration du code client ici.

  1. Créez un fichier POSTINSTALL.md , puis incluez les informations de post-installation suivantes :

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

Surveillance

À titre de bonne pratique, vous pouvez surveiller l'activité de votre extension installée, notamment en vérifiant son état de santé, son utilisation et ses journaux.

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

Cela recompile les fonctions afin que le dernier code source soit prêt à être déployé avec l'extension lorsqu'il est déployé sur un émulateur ou directement sur Firebase.

Ensuite, créez un nouveau répertoire à partir duquel tester l’extension. Étant donné que l'extension a été développée à partir de fonctions existantes, ne testez pas à partir du dossier dans lequel l'extension a été configurée, car celui-ci tente également de déployer les fonctions et les règles Firebase à côté.

Installer et tester avec les émulateurs Firebase

  1. Créez un nouveau répertoire sur votre système hôte et connectez ce répertoire à votre projet Firebase à l'aide firebase init .
mkdir sample-proj
cd sample-proj
firebase init --project=projectID-or-alias
  1. À partir de ce répertoire, exécutez firebase ext:install pour installer l'extension. Remplacez /path/to/extension par le chemin absolu du répertoire qui contient votre fichier extension.yaml . Cela démarre le processus d'installation de votre extension et crée un fichier .env contenant vos configurations avant de transmettre la configuration à Firebase ou aux émulateurs.
firebase ext:install /path/to/extension
  • Puisque vous déployez le projet localement, précisez que vous souhaitez utiliser un fichier local plutôt que Google Cloud Secret Manager.

da928c65ffa8ce15.png

  1. Démarrez la suite d'émulateurs locaux :
firebase emulators:start

Installer et tester avec un vrai projet Firebase

Vous pouvez installer votre extension dans un projet Firebase réel. Il est recommandé d'utiliser un projet de test pour vos tests. Utilisez ce workflow de test si vous souhaitez tester le flux de bout en bout de votre extension ou si le déclencheur de votre extension n'est pas encore pris en charge par la suite d'émulateurs Firebase (voir l' option d'émulateur Extensions ). Les émulateurs prennent actuellement en charge les fonctions déclenchées par des requêtes HTTP et les fonctions déclenchées par des événements en arrière-plan pour Cloud Firestore, Realtime Database et Pub/Sub.

  1. Créez un nouveau répertoire sur votre système hôte et connectez ce répertoire à votre projet Firebase à l'aide firebase init .
cd ..
mkdir sample-proj
cd sample-proj
firebase init --project=projectID-or-alias
  1. Ensuite, à partir de ce répertoire, exécutez firebase ext:install pour installer l'extension. Remplacez /path/to/extension par le chemin absolu du répertoire qui contient votre fichier extension.yaml . Cela démarre le processus d'installation de votre extension et crée un fichier .env contenant vos configurations avant de transmettre la configuration à Firebase ou aux émulateurs.
firebase ext:install /path/to/extension
  • Puisque vous souhaitez déployer directement sur Firebase et utiliser Google Cloud Secret Manager, vous devez activer l'API Secret Manager avant d'installer l'extension.
  1. Déployez sur votre projet Firebase.
firebase deploy

Testez l'extension

  1. Après avoir exécuté firebase deploy ou firebase emulators:start , accédez à l'onglet Firestore de la console Firebase ou à la vue Web des émulateurs, selon le cas.
  2. Ajoutez un document dans la collection spécifiée par le champ x et le champ y . Dans ce cas, les documents mis à jour sont situés à u/{uid} avec un champ x de xv et un champ y de yv .

Écran des émulateurs Firebase pour ajouter un enregistrement Firestore

  1. Si vous avez réussi à installer l'extension, celle-ci crée un nouveau champ appelé hash dans le document après avoir enregistré les deux champs.

Écran de base de données Firestore d'un émulateur affichant le hachage ajouté

8. Félicitations !

Vous avez converti avec succès votre première fonction Cloud en une extension Firebase !

Vous avez ajouté un fichier extension.yaml et l'avez configuré afin que les développeurs puissent sélectionner la manière dont ils souhaitent que votre extension soit déployée. Vous avez ensuite créé une documentation utilisateur qui fournit des conseils sur ce que les développeurs de l'extension doivent faire avant de configurer l'extension et sur les étapes qu'ils pourraient devoir suivre après avoir installé l'extension avec succès.

Vous connaissez maintenant les étapes clés nécessaires pour convertir une fonction Firebase en une extension Firebase distribuable.

Et après?