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

1. Avant de commencer

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

Objectifs de l'atelier

Dans cet atelier de programmation, vous allez créer une extension Firebase pour le géohachage. Une fois déployée, votre extension convertit les coordonnées X et Y en géocodes en réponse aux événements Firestore ou via des appels de fonction appelables. Vous pouvez utiliser cette méthode au lieu d'implémenter la bibliothèque Geofire sur toutes vos plates-formes cibles pour stocker des données, ce qui vous fait gagner du temps.

Extension geohash affichée dans la console Firebase

Points abordés

  • Convertir un code Cloud Functions existant en extension Firebase distribuable
  • Configurer un fichier extension.yaml
  • Stocker des chaînes sensibles (clés API) dans une extension
  • Permettre aux développeurs de l'extension de la configurer en fonction de leurs besoins
  • Tester et déployer l'extension

Prérequis

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

2. Configuration

Obtenir 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 l'environnement de développement de votre choix.

  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 vous encourage vivement à utiliser les émulateurs Firebase. Si vous souhaitez essayer de développer des extensions avec un véritable projet Firebase, consultez Créer un projet Firebase. Cet atelier de programmation utilise Cloud Functions. Si vous utilisez un véritable projet Firebase au lieu des émulateurs, vous devez passer à la formule Blaze.

Vous souhaitez passer à l'étape suivante ?

Vous pouvez télécharger une version finalisée de l'atelier de programmation. Si vous rencontrez des difficultés ou si vous souhaitez voir à quoi ressemble une extension terminée, consultez la branche codelab-end du dépôt GitHub ou téléchargez le fichier ZIP finalisé.

3. Examiner le code

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

À quoi servent ces fonctions ?

Ces fonctions de démonstration sont utilisées pour le géocodage. Ils prennent une paire de coordonnées et la convertissent dans un format optimisé pour les requêtes géographiques dans Firestore. Les fonctions simulent l'utilisation d'un appel d'API afin que vous puissiez en savoir plus sur la gestion des types de données sensibles dans les extensions. Pour en savoir plus, 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 tôt, 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 se présente comme suit:

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. Si ces deux champs existent, elle calcule le géohachage et écrit la sortie à 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 géohachage pour ces documents. Il génère ensuite le hachage dans un champ de hachage du même document.

Fonctions appelables

La fonction suivante du fichier index.ts se présente comme suit:

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. Il indique que cette fonction est une fonction appelable, qui peut être appelée à partir du code de votre application cliente. Cette fonction appelable utilise les paramètres x et y et renvoie un géocode. Bien que cette fonction ne soit pas appelée directement dans cet atelier de programmation, elle est incluse ici à titre d'exemple de configuration dans l'extension Firebase.

4. Configurer un fichier extension.yaml

Maintenant que vous savez ce que fait le code Cloud Functions de votre extension, vous pouvez le empaqueter pour le distribuer. Chaque extension Firebase est fournie avec un fichier extension.yaml qui décrit son fonctionnement et son comportement.

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 les éléments suivants:
name: geohash-ext
version: 0.0.1
specVersion: v1beta  # Firebase Extensions specification version (do not edit)

Le nom de l'extension sert de base à 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 respecter le gestion sémantique des versions, et vous devez le mettre à jour chaque fois que vous modifiez les fonctionnalités de l'extension. La version de la spécification de l'extension permet de déterminer la spécification d'extension Firebase à suivre. Dans ce cas, v1beta est utilisé.

  1. Ajoutez des informations conviviales au fichier YAML:
...

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

Le nom à afficher est une représentation conviviale du nom de votre extension lorsque les développeurs interagissent avec elle. La description donne un bref aperçu de l'extension. Lorsque l'extension est déployée sur extensions.dev, elle se présente comme suit:

Extension Geohash Converter sur extensions.dev

  1. Spécifiez la licence du code dans votre extension.
...

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

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

billingRequired: true

La section author permet d'indiquer à vos utilisateurs à qui s'adresser s'ils rencontrent des problèmes avec l'extension ou souhaitent en savoir plus à son 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 forfait Blaze.

Il s'agit du nombre minimal de champs requis dans le fichier extension.yaml pour identifier cette extension. Pour en savoir plus sur les autres informations d'identification que vous pouvez spécifier dans une extension, consultez la documentation.

5. Convertir le code Cloud Functions en 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 devient alors propriétaire de ces ressources et dispose d'un compte de service spécifique qui les gère. Dans ce projet, ces ressources sont des fonctions Cloud, qui doivent être définies dans le fichier extension.yaml, car l'extension ne crée pas automatiquement de ressources à partir du code dans le dossier "functions". Si vos fonctions Cloud Functions ne sont pas explicitement déclarées comme une 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. Autorisez l'utilisateur à spécifier l'emplacement où il souhaite déployer cette extension et à décider s'il est préférable d'héberger l'extension plus près de ses utilisateurs finaux ou 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 de 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 la fonction qui est déployée, qui doit toujours être firebaseextensions.v1beta.function pour le moment. Vous définissez ensuite le 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 devez utiliser le eventType de providers/cloud.firestore/eventTypes/document.write, qui se trouve dans la documentation Écrire des fonctions Cloud pour votre extension. Vous définissez resource comme emplacement des documents. Étant donné que votre objectif actuel est de refléter ce qui existe dans le code, le chemin d'accès au document écoute users/{uid}, avec l'emplacement par défaut de la base de données qui le précède.

  1. L'extension a besoin d'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 pour utiliser 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 compatibles avec les extensions. Étant donné que l'extension va lire et écrire, le rôle datastore.user est adapté.

  1. La fonction appelable doit également être ajoutée. Dans le fichier extension.yaml, créez une 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 du code

Cela a nécessité beaucoup de configuration pour que votre extension.yaml corresponde à tout ce qui est effectué par le code de votre fichier index.ts. Le fichier extension.yaml finalisé doit se présenter comme suit:

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 la tester à l'aide des émulateurs Firebase.

  1. Si vous ne l'avez pas déjà fait, appelez npm run build dans le dossier "functions" du projet d'extensions téléchargé.
  2. Créez un répertoire sur votre système hôte et associez-le à votre projet Firebase à l'aide de 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. Dans le même répertoire, exécutez firebase ext:install. Remplacez /path/to/extension par le chemin absolu du répertoire contenant votre fichier extension.yaml.
firebase ext:install /path/to/extension
    This command does two things:
  • Vous êtes invité à spécifier la configuration de l'instance de l'extension, et un fichier *.env contenant les informations de configuration de l'instance est créé.
  • Il ajoute l'instance d'extension à la section extensions de votre firebase.json. Il s'agit d'une mise en correspondance de l'ID d'instance avec la version de l'extension.
  • Comme vous déployez le projet localement, vous pouvez spécifier que vous souhaitez utiliser un fichier local plutôt que Secret Manager de Google Cloud.

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 de l'émulateur.
  2. Ajoutez un document à la collection users avec un champ de numéro xv et un champ de numéro yv.

Boîte de dialogue affichée 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, elle crée un champ nommé hash dans le document.

Collection "users" avec un document utilisateur contenant des champs xv, yv et hash.

Effectuer un nettoyage 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 voulez pas qu'il entre en conflit avec l'extension actuelle plus tard.

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

firebase ext:uninstall geohash-ext

La solution actuelle fonctionne, mais comme indiqué au début du projet, une clé API codée en dur est utilisée pour simuler la communication avec un service. Comment utiliser la clé API de l'utilisateur final au lieu de celle fournie à l'origine ? Poursuivez votre lecture pour le savoir.

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 orienté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 l'emplacement sur un plan cartésien ? De plus, comment pouvez-vous demander à l'utilisateur final de fournir sa propre clé API plutôt que de le laisser utiliser la clé API fournie ? Vous risqueriez rapidement de 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 avoir une configuration personnalisée. Le premier est 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 définie précédemment:

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 de manière visible pour vous, le producteur de l'extension. Utilisez cette valeur plus tard lorsque vous spécifiez les valeurs de paramètre.
  • label est un identifiant lisible par l'utilisateur qui permet au développeur de savoir à quoi sert le paramètre.
  • description fournit une description détaillée de la valeur. Étant donné que le format Markdown est pris en charge, il peut créer des liens vers des documents supplémentaires ou mettre en évidence des mots qui pourraient être importants pour le développeur.
  • type définit le mécanisme d'entrée permettant à un utilisateur de définir la valeur du paramètre. Il existe de nombreux types, y compris string, select, multiSelect, selectResource et secret. Pour en savoir plus sur chacune de ces options, consultez la documentation.
  • validationRegex limite la saisie du développeur à une certaine valeur d'expression régulière (dans l'exemple, elle est basée sur les consignes de dénomination de champ simple disponibles ici). Si cela échoue…
  • validationErrorMessage avertit le développeur de la valeur d'échec.
  • default correspond à la valeur qui serait utilisée si le développeur n'avait saisi aucun texte.
  • obligatoire signifie que le développeur n'est pas obligé de saisir de texte.
  • immutable permet au développeur de mettre à jour cette extension et de modifier cette valeur. Dans ce cas, le développeur doit pouvoir modifier les noms des champs à mesure que ses exigences évoluent.
  • example donne une idée de ce à quoi peut ressembler une entrée valide.

C'était beaucoup à comprendre !

  1. Vous devez ajouter trois autres paramètres 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 des paramètres sensibles

Vous devez maintenant 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 des secrets chiffrés et empêche leur fuite accidentelle. Le développeur doit donc payer pour utiliser ce service, mais il ajoute une couche de sécurité supplémentaire à ses clés API et limite potentiellement les activités frauduleuses. La documentation utilisateur avertit le développeur qu'il s'agit d'un service payant, afin qu'il n'y ait pas de surprise lors de la facturation. Dans l'ensemble, l'utilisation est semblable à celle des autres ressources de chaîne mentionnées ci-dessus. La seule différence réside dans le type, qui est appelé secret.

  • Dans votre 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

Modifier les attributs resource pour utiliser des paramètres

Comme indiqué précédemment, la ressource (et non la fonction) définit la façon dont la ressource est observée. Par conséquent, la ressource locationUpdate doit être mise à jour pour utiliser le nouveau paramètre.

  • Dans votre 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érifier le fichier extension.yaml

  • Examinez le fichier extension.yaml. Voici un exemple :

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ètre appropriées et les renseigne 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 devez effectuer des vérifications de valeurs nulles avec les valeurs des variables d'environnement. Toutefois, dans ce cas, vous pouvez vous fier à la copie correcte des valeurs des paramètres. Le code est maintenant configuré pour fonctionner avec les paramètres de l'extension.

7. Créer une documentation utilisateur

Avant de tester le code sur des émulateurs ou sur la place de marché des extensions Firebase, vous devez documenter l'extension afin que les développeurs sachent ce qu'ils obtiennent lorsqu'ils l'utilisent.

  1. Commencez par créer le fichier PREINSTALL.md, qui permet de décrire la fonctionnalité, les conditions préalables à l'installation et les conséquences potentielles sur la facturation.

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. Pour gagner du temps lors de la rédaction de l'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 informations supplémentaires sur votre extension à partir de votre fichier extension.yaml.

Enfin, informez le développeur de l'extension de quelques informations supplémentaires sur l'extension qui vient d'être installée. Le développeur peut recevoir des instructions et des informations supplémentaires après avoir terminé l'installation. Il peut également recevoir des tâches détaillées post-installation, comme la configuration du code client.

  1. Créez un fichier POSTINSTALL.md, puis incluez les informations 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

Nous vous recommandons de surveiller l'activité de votre extension installée, y compris en vérifiant son état, 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

Les fonctions sont recompilées afin que le dernier code source soit prêt à être déployé avec l'extension lorsqu'elle est déployée sur un émulateur ou directement sur Firebase.

Ensuite, créez un répertoire à partir duquel vous pourrez tester l'extension. Étant donné que l'extension a été développée à partir de fonctions existantes, ne la testez pas à partir du dossier dans lequel elle a été configurée, car cela tente également de déployer les fonctions et les règles Firebase en même temps.

Installer et tester avec les émulateurs Firebase

  1. Créez un répertoire sur votre système hôte et associez-le à votre projet Firebase à l'aide de 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 contenant votre fichier extension.yaml. Le processus d'installation de votre extension démarre et un fichier .env contenant vos configurations est créé avant d'être transféré vers Firebase ou les émulateurs.
firebase ext:install /path/to/extension
  • Étant donné que vous déployez le projet en local, indiquez que vous souhaitez utiliser un fichier local plutôt que Secret Manager de Google Cloud.

da928c65ffa8ce15.png

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

Installer et tester avec un projet Firebase réel

Vous pouvez installer votre extension dans un projet Firebase réel. Nous vous recommandons d'utiliser un projet de test pour vos tests. Suivez 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 compatible avec la suite d'émulateurs Firebase (voir l'option d'émulateur pour les extensions). Les émulateurs sont actuellement compatibles avec 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 répertoire sur votre système hôte et associez-le à votre projet Firebase à l'aide de 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 contenant votre fichier extension.yaml. Le processus d'installation de votre extension démarre et un fichier .env contenant vos configurations est créé avant d'être envoyé à Firebase ou aux émulateurs.
firebase ext:install /path/to/extension
  • Étant donné que 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-le dans votre projet Firebase.
firebase deploy

Tester l'extension

  1. Après avoir exécuté firebase deploy ou firebase emulators:start, accédez à l'onglet Firestore de la console Firebase ou de la vue Web des émulateurs, selon le cas.
  2. Ajoutez un document à la collection spécifiée par les champs x et y. Dans ce cas, les documents mis à jour se trouvent dans 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, elle crée un champ appelé hash dans le document une fois que vous avez enregistré les deux champs.

Écran de la base de données Firestore d&#39;un émulateur affichant le hachage ajouté

8. Félicitations !

Vous avez bien converti votre première fonction Cloud en extension Firebase.

Vous avez ajouté un fichier extension.yaml et l'avez configuré pour que les développeurs puissent choisir comment déployer votre extension. Vous avez ensuite créé une documentation utilisateur qui indique ce que les développeurs de l'extension doivent faire avant de la configurer et les étapes qu'ils peuvent être amenés à suivre après avoir installé l'extension.

Vous savez maintenant quelles sont les étapes clés à suivre pour convertir une fonction Firebase en extension Firebase distribuable.

Étapes suivantes