Commencez à créer une extension

Cette page vous guide à travers les étapes nécessaires pour créer une extension Firebase simple, que vous pouvez installer dans vos projets ou partager avec d'autres. Cet exemple simple d'extension Firebase surveillera les messages de votre base de données en temps réel et les convertira en majuscules.

1. Configurez votre environnement et initialisez un projet

Avant de pouvoir commencer à créer une extension, vous devrez configurer un environnement de construction avec les outils requis.

  1. Installez Node.js 16 ou version ultérieure. Une façon d'installer Node consiste à utiliser nvm (ou nvm-windows ).

  2. Installez ou mettez à jour vers la dernière version de Firebase CLI . Pour installer ou mettre à jour à l'aide de npm , exécutez cette commande :

    npm install -g firebase-tools
    

Utilisez maintenant la CLI Firebase pour initialiser un nouveau projet d'extension :

  1. Créez un répertoire pour votre extension et cd -y :

    mkdir rtdb-uppercase-messages && cd rtdb-uppercase-messages
    
  2. Exécutez la commande ext:dev:init de la CLI Firebase :

    firebase ext:dev:init
    

    Lorsque vous y êtes invité, choisissez JavaScript comme langage pour les fonctions (mais notez que vous pouvez également utiliser TypeScript lorsque vous développez votre propre extension) et, lorsqu'on vous demande d'installer des dépendances, répondez « oui ». (Acceptez les valeurs par défaut pour toutes les autres options.) Cette commande configurera une base de code squelette pour une nouvelle extension, à partir de laquelle vous pourrez commencer à développer votre extension.

2. Essayez l'exemple d'extension en utilisant l'émulateur

Lorsque la CLI Firebase a initialisé le nouveau répertoire d'extensions, elle a créé un exemple de fonction simple et un répertoire de integration-tests contenant les fichiers nécessaires pour exécuter une extension à l'aide de la suite d'émulateurs Firebase.

Essayez d'exécuter l'exemple d'extension dans l'émulateur :

  1. Accédez au répertoire integration-tests :

    cd functions/integration-tests
    
  2. Démarrez l'émulateur avec un projet de démonstration :

    firebase emulators:start --project=demo-test
    

    L'émulateur charge l'extension dans un projet "factice" prédéfini ( demo-test ). Jusqu'à présent, l'extension consiste en une seule fonction déclenchée par HTTP, greetTheWorld , qui renvoie un message "hello world" lors de l'accès.

  3. L'émulateur étant toujours en cours d'exécution, essayez la fonction greetTheWorld de l'extension en visitant l'URL imprimée lorsque vous l'avez démarré.

    Votre navigateur affiche le message « Hello World from greet-the-world ».

  4. Le code source de cette fonction se trouve dans le répertoire functions de l'extension. Ouvrez le source dans l'éditeur ou l'IDE de votre choix :

    fonctions/index.js

    const functions = require("firebase-functions");
    
    exports.greetTheWorld = functions.https.onRequest((req, res) => {
      // Here we reference a user-provided parameter
      // (its value is provided by the user during installation)
      const consumerProvidedGreeting = process.env.GREETING;
    
      // And here we reference an auto-populated parameter
      // (its value is provided by Firebase after installation)
      const instanceId = process.env.EXT_INSTANCE_ID;
    
      const greeting = `${consumerProvidedGreeting} World from ${instanceId}`;
    
      res.send(greeting);
    });
    
  5. Pendant que l'émulateur est en cours d'exécution, il rechargera automatiquement toutes les modifications que vous apportez à votre code de fonctions. Essayez d'apporter une petite modification à la fonction greetTheWorld :

    fonctions/index.js

    const greeting = `${consumerProvidedGreeting} everyone, from ${instanceId}`;
    

    Enregistrez vos modifications. L'émulateur rechargera votre code et désormais, lorsque vous visiterez l'URL de la fonction, vous verrez le message d'accueil mis à jour.

3. Ajoutez des informations de base à extension.yaml

Maintenant que vous disposez d’un environnement de développement configuré et que vous exécutez l’émulateur d’extensions, vous pouvez commencer à écrire votre propre extension.

Dans un premier temps modeste, modifiez les métadonnées d'extension prédéfinies pour refléter l'extension que vous souhaitez écrire au lieu de greet-the-world . Ces métadonnées sont stockées dans le fichier extension.yaml .

  1. Ouvrez extension.yaml dans votre éditeur et remplacez tout le contenu du fichier par ce qui suit :

    name: rtdb-uppercase-messages
    version: 0.0.1
    specVersion: v1beta  # Firebase Extensions specification version; don't change
    
    # Friendly display name for your extension (~3-5 words)
    displayName: Convert messages to upper case
    
    # Brief description of the task your extension performs (~1 sentence)
    description: >-
      Converts messages in RTDB to upper case
    
    author:
      authorName: Your Name
      url: https://your-site.example.com
    
    license: Apache-2.0  # Required license
    
    # Public URL for the source code of your extension
    sourceUrl: https://github.com/your-name/your-repo
    

    Notez la convention de dénomination utilisée dans le champ name : les extensions Firebase officielles sont nommées avec un préfixe indiquant le produit Firebase principal sur lequel l'extension fonctionne, suivi d'une description de ce que fait l'extension. Vous devez utiliser la même convention dans vos propres extensions.

  2. Puisque vous avez modifié le nom de votre extension, vous devez également mettre à jour la configuration de votre émulateur avec le nouveau nom :

    1. Dans functions/integration-tests/firebase.json , remplacez greet-the-world par rtdb-uppercase-messages .
    2. Renommez functions/integration-tests/extensions/greet-the-world.env en functions/integration-tests/extensions/rtdb-uppercase-messages.env .

Il reste encore quelques restes de l'extension greet-the-world dans votre code d'extension, mais laissez-les pour le moment. Vous les mettrez à jour dans les prochaines sections.

4. Écrivez une fonction Cloud et déclarez-la comme ressource d'extension

Vous pouvez maintenant commencer à écrire du code. Au cours de cette étape, vous écrirez une fonction Cloud qui effectue la tâche principale de votre extension, qui consiste à surveiller les messages de votre base de données en temps réel et à les convertir en majuscules.

  1. Ouvrez le source des fonctions de l'extension (dans le répertoire functions de l'extension) dans l'éditeur ou l'IDE de votre choix. Remplacez son contenu par ce qui suit :

    fonctions/index.js

    import { database, logger } from "firebase-functions/v1";
    
    const app = initializeApp();
    
    // Listens for new messages added to /messages/{pushId}/original and creates an
    // uppercase version of the message to /messages/{pushId}/uppercase
    // for all databases in 'us-central1'
    export const makeuppercase = database
      .ref("/messages/{pushId}/uppercase")
      .onCreate(async (snapshot, context) => {
        // Grab the current value of what was written to the Realtime Database.
        const original = snapshot.val();
    
        // Convert it to upper case.
        logger.log("Uppercasing", context.params.pushId, original);
        const uppercase = original.toUpperCase();
    
        // Setting an "uppercase" sibling in the Realtime Database.
        const upperRef = snapshot.ref.parent.child("upper");
        await upperRef.set(uppercase);
    });
    

    L'ancienne fonction, que vous avez remplacée, était une fonction déclenchée par HTTP, qui s'exécutait lors de l'accès à un point de terminaison HTTP. La nouvelle fonction est déclenchée par des événements de base de données en temps réel : elle surveille les nouveaux éléments sur un chemin particulier et, lorsqu'un élément est détecté, elle réécrit la version majuscule de la valeur dans la base de données.

    D'ailleurs, ce nouveau fichier utilise la syntaxe du module ECMAScript ( import et export ) au lieu de CommonJS ( require ). Pour utiliser les modules ES dans Node, spécifiez "type": "module" dans functions/package.json :

    {
      "name": "rtdb-uppercase-messages",
      "main": "index.js",
      "type": "module",
      …
    }
    
  2. Chaque fonction de votre extension doit être déclarée dans le fichier extension.yaml . L'exemple d'extension a déclaré greetTheWorld comme seule fonction Cloud de l'extension ; maintenant que vous l'avez remplacé par makeuppercase , vous devez également mettre à jour sa déclaration.

    Ouvrez extension.yaml et ajoutez un champ resources :

    resources:
      - name: makeuppercase
        type: firebaseextensions.v1beta.function
        properties:
          eventTrigger:
            eventType: providers/google.firebase.database/eventTypes/ref.create
            # DATABASE_INSTANCE (project's default instance) is an auto-populated
            # parameter value. You can also specify an instance.
            resource: projects/_/instances/${DATABASE_INSTANCE}/refs/messages/{pushId}/original
          runtime: "nodejs18"
    
  3. Étant donné que votre extension utilise désormais Realtime Database comme déclencheur, vous devez mettre à jour la configuration de votre émulateur pour exécuter l'émulateur RTDB avec l'émulateur Cloud Functions :

    1. Si l'émulateur est toujours en cours d'exécution, arrêtez-le en appuyant sur Ctrl-C.

    2. Depuis le répertoire functions/integration-tests , exécutez la commande suivante :

      firebase init emulators
      

      Lorsque vous y êtes invité, ignorez la configuration d'un projet par défaut, puis sélectionnez les émulateurs de fonctions et de base de données. Acceptez les ports par défaut et autorisez l'outil de configuration à télécharger tous les fichiers requis.

    3. Redémarrez l'émulateur :

      firebase emulators:start --project=demo-test
      
  4. Essayez votre extension mise à jour :

    1. Ouvrez l'interface utilisateur de l'émulateur de base de données à l'aide du lien imprimé par l'émulateur lorsque vous l'avez démarré.

    2. Modifiez le nœud racine de la base de données :

      • Champ : messages
      • Tapez : json
      • Valeur : {"11": {"original": "recipe"}}

      Si tout est configuré correctement, lorsque vous enregistrez les modifications de votre base de données, la fonction makeuppercase de l'extension doit se déclencher et ajouter un enregistrement enfant au message 11 avec le contenu "upper": "RECIPE" . Jetez un œil aux journaux et aux onglets de base de données de l’interface utilisateur de l’émulateur pour confirmer les résultats attendus.

    3. Essayez d'ajouter quelques enfants supplémentaires au nœud messages ( {"original":"any text"} ). Chaque fois que vous ajoutez un nouvel enregistrement, l'extension doit ajouter un champ uppercase contenant le contenu en majuscule du champ original .

Vous disposez désormais d’une extension complète, bien que simple, qui fonctionne sur une instance RTDB. Dans les sections qui suivent, vous affinerez cette extension avec quelques fonctionnalités supplémentaires. Ensuite, vous préparerez l’extension à être distribuée à d’autres et enfin, vous apprendrez comment publier votre extension sur Extensions Hub.

5. Déclarez les API et les rôles

Firebase accorde à chaque instance d'une extension installée un accès limité au projet et à ses données à l'aide d'un compte de service par instance. Chaque compte dispose de l'ensemble minimum d'autorisations nécessaires pour fonctionner. Pour cette raison, vous devez déclarer explicitement tous les rôles IAM requis par votre extension ; lorsque les utilisateurs installent votre extension, Firebase crée un compte de service avec ces rôles accordés et l'utilise pour exécuter l'extension.

Vous n'avez pas besoin de déclarer des rôles pour déclencher les événements d'un produit, mais vous devez déclarer un rôle pour interagir autrement avec celui-ci. Étant donné que la fonction que vous avez ajoutée à la dernière étape écrit dans la base de données en temps réel, vous devez ajouter la déclaration suivante à extension.yaml :

roles:
  - role: firebasedatabase.admin
    reason: Allows the extension to write to RTDB.

De même, vous déclarez les API Google qu'une extension utilise dans le champ apis . Lorsque les utilisateurs installent votre extension, il leur sera demandé s'ils souhaitent activer automatiquement ces API pour leur projet. Cela n'est généralement nécessaire que pour les API Google non Firebase et n'est pas nécessaire pour ce guide.

6. Définir les paramètres configurables par l'utilisateur

La fonction que vous avez créée au cours des deux dernières étapes surveillait un emplacement RTDB spécifique pour les messages entrants. Parfois, vous souhaitez réellement surveiller un emplacement spécifique, par exemple lorsque votre extension fonctionne sur une structure de base de données que vous utilisez exclusivement pour votre extension. Cependant, la plupart du temps, vous souhaiterez rendre ces valeurs configurables par les utilisateurs qui installent votre extension dans leurs projets. De cette façon, les utilisateurs peuvent utiliser votre extension pour travailler avec leur configuration de base de données existante.

Rendre le chemin par lequel l'extension surveille les nouveaux messages est configurable par l'utilisateur :

  1. Dans le fichier extension.yaml , ajoutez une section params :

    - param: MESSAGE_PATH
      label: Message path
      description: >-
        What is the path at which the original text of a message can be found?
      type: string
      default: /messages/{pushId}/original
      required: true
      immutable: false
    

    Cela définit un nouveau paramètre de chaîne que les utilisateurs seront invités à définir lors de l'installation de votre extension.

  2. Toujours dans le fichier extension.yaml , revenez à votre déclaration makeuppercase et modifiez le champ resource comme suit :

    resource: projects/_/instances/${DATABASE_INSTANCE}/refs/${param:MESSAGE_PATH}
    

    Le jeton ${param:MESSAGE_PATH} est une référence au paramètre que vous venez de définir. Lorsque votre extension s'exécute, ce jeton sera remplacé par la valeur configurée par l'utilisateur pour ce paramètre, avec pour résultat que la fonction makeuppercase écoutera le chemin spécifié par l'utilisateur. Vous pouvez utiliser cette syntaxe pour référencer n'importe quel paramètre défini par l'utilisateur n'importe où dans extension.yaml (et dans POSTINSTALL.md - nous en parlerons plus tard).

  3. Vous pouvez également accéder aux paramètres définis par l'utilisateur à partir de votre code de fonctions.

    Dans la fonction que vous avez écrite dans la dernière section, vous avez codé en dur le chemin pour surveiller les changements. Modifiez la définition du déclencheur pour faire référence à la valeur définie par l'utilisateur :

    fonctions/index.js

    export const makeuppercase = database.ref(process.env.MESSAGE_PATH).onCreate
    

    Notez que dans les extensions Firebase, cette modification est uniquement à des fins de documentation : lorsqu'une fonction Cloud est déployée dans le cadre d'une extension, elle utilise la définition du déclencheur du fichier extension.yaml et ignore la valeur spécifiée dans la définition de la fonction. Néanmoins, c'est une bonne idée de documenter dans votre code d'où vient cette valeur.

  4. Vous pourriez trouver décevant d'effectuer une modification de code qui n'a aucun effet à l'exécution, mais la leçon importante à retenir est que vous pouvez accéder à n'importe quel paramètre défini par l'utilisateur dans votre code de fonction et l'utiliser comme valeur ordinaire dans la logique de la fonction. En guise de clin d'œil à cette fonctionnalité, ajoutez l'instruction de journal suivante pour démontrer que vous accédez effectivement à la valeur définie par l'utilisateur :

    fonctions/index.js

    export const makeuppercase = database.ref(process.env.MESSAGE_PATH).onCreate(
      async (snapshot, context) => {
        logger.log("Found new message at ", snapshot.ref);
    
        // Grab the current value of what was written to the Realtime Database.
        ...
    
  5. Normalement, les utilisateurs sont invités à fournir des valeurs pour les paramètres lorsqu'ils installent une extension. Cependant, lorsque vous utilisez l'émulateur à des fins de test et de développement, vous ignorez le processus d'installation et fournissez donc des valeurs pour les paramètres définis par l'utilisateur à l'aide d'un fichier env .

    Ouvrez functions/integration-tests/extensions/rtdb-uppercase-messages.env et remplacez la définition GREETING par ce qui suit :

    MESSAGE_PATH=/msgs/{pushId}/original
    

    Notez que le chemin ci-dessus est différent du chemin par défaut et du chemin que vous avez défini précédemment ; c'est juste pour vous prouver, lorsque vous essayez votre extension mise à jour, que votre définition prend effet.

  6. Maintenant, redémarrez l’émulateur et visitez à nouveau l’interface utilisateur de l’émulateur de base de données.

    Modifiez le nœud racine de la base de données en utilisant le chemin que vous avez défini ci-dessus :

    • Champ : msgs
    • Tapez : json
    • Valeur : {"11": {"original": "recipe"}}

    Lorsque vous enregistrez les modifications de votre base de données, la fonction makeuppercase de l'extension devrait se déclencher comme avant, mais elle devrait désormais également imprimer le paramètre défini par l'utilisateur dans le journal de la console.

7. Fournir des hooks d'événement pour la logique définie par l'utilisateur

Vous avez déjà vu, en tant qu'auteur d'extension, comment un produit Firebase peut déclencher la logique fournie par votre extension : la création de nouveaux enregistrements dans Realtime Database déclenche votre fonction makeuppercase . Votre extension peut avoir une relation analogue avec les utilisateurs qui installent votre extension : votre extension peut déclencher une logique définie par l' utilisateur .

Une extension peut fournir des hooks synchrones , des hooks asynchrones ou les deux. Les hooks synchrones permettent aux utilisateurs d'effectuer des tâches qui bloquent l'achèvement de l'une des fonctions de l'extension. Cela peut être utile, par exemple, pour donner aux utilisateurs un moyen d'effectuer un prétraitement personnalisé avant qu'une extension ne fasse son travail.

Dans ce guide, vous allez ajouter un hook asynchrone à votre extension, qui permettra aux utilisateurs de définir leurs propres étapes de traitement à exécuter après que votre extension ait écrit le message en majuscule dans la base de données en temps réel. Les hooks asynchrones utilisent Eventarc pour déclencher des fonctions définies par l'utilisateur. Les extensions déclarent les types d'événements qu'elles émettent et lorsque les utilisateurs installent l'extension, ils choisissent les types d'événements qui les intéressent. S'ils choisissent au moins un événement, Firebase fournira un canal Eventarc pour l'extension dans le cadre du processus d'installation. . Les utilisateurs peuvent ensuite déployer leurs propres fonctions cloud qui écoutent sur ce canal et se déclenchent lorsque l'extension publie de nouveaux événements.

Suivez ces étapes pour ajouter un hook asynchrone :

  1. Dans le fichier extension.yaml , ajoutez la section suivante, qui déclare le type d'événement émis par l'extension :

    events:
      - type: test-publisher.rtdb-uppercase-messages.v1.complete
        description: >-
          Occurs when message uppercasing completes. The event subject will contain
          the RTDB URL of the uppercase message.
    

    Les types d'événements doivent être universellement uniques ; pour garantir l'unicité, nommez toujours vos événements en utilisant le format suivant : <publisher-id>.<extension-id>.<version>.<description> . (Vous n'avez pas encore d'identifiant d'éditeur, alors utilisez simplement test-publisher pour le moment.)

  2. A la fin de la fonction makeuppercase , ajoutez du code qui publie un événement du type que vous venez de déclarer :

    fonctions/index.js

    // Import the Eventarc library:
    import { initializeApp } from "firebase-admin/app";
    import { getEventarc } from "firebase-admin/eventarc";
    
    const app = initializeApp();
    
    // In makeuppercase, after upperRef.set(uppercase), add:
    
    // Set eventChannel to a newly-initialized channel, or `undefined` if events
    // aren't enabled.
    const eventChannel =
      process.env.EVENTARC_CHANNEL &&
      getEventarc().channel(process.env.EVENTARC_CHANNEL, {
        allowedEventTypes: process.env.EXT_SELECTED_EVENTS,
      });
    
    // If events are enabled, publish a `complete` event to the configured
    // channel.
    eventChannel &&
      eventChannel.publish({
        type: "test-publisher.rtdb-uppercase-messages.v1.complete",
        subject: upperRef.toString(),
        data: {
          "original": original,
          "uppercase": uppercase,
        },
      });
    

    Cet exemple de code tire parti du fait que la variable d'environnement EVENTARC_CHANNEL est définie uniquement lorsque l'utilisateur a activé au moins un type d'événement. si EVENTARC_CHANNEL n'est pas défini, le code ne tente de publier aucun événement.

    Vous pouvez joindre des informations supplémentaires à un événement Eventarc. Dans l'exemple ci-dessus, l'événement comporte un champ subject qui contient une référence à la valeur nouvellement créée et une charge utile data contenant les messages d'origine et en majuscules. Les fonctions définies par l'utilisateur qui déclenchent l'événement peuvent utiliser ces informations.

  3. Normalement, les variables d'environnement EVENTARC_CHANNEL et EXT_SELECTED_EVENTS sont définies en fonction des options sélectionnées par l'utilisateur lors de l'installation. Pour tester avec l'émulateur, définissez manuellement ces variables dans le fichier rtdb-uppercase-messages.env :

    EVENTARC_CHANNEL=locations/us-central1/channels/firebase
    EXT_SELECTED_EVENTS=test-publisher.rtdb-uppercase-messages.v1.complete
    

À ce stade, vous avez terminé les étapes nécessaires pour ajouter un hook d’événement asynchrone à votre extension.

Pour tester cette nouvelle fonctionnalité que vous venez d'implémenter, dans les prochaines étapes, incarnez un utilisateur qui installe l'extension :

  1. Depuis le répertoire functions/integration-tests , initialisez un nouveau projet Firebase :

    firebase init functions
    

    Lorsque vous y êtes invité, refusez de configurer un projet par défaut, sélectionnez JavaScript comme langage Cloud Functions et installez les dépendances requises. Ce projet représente le projet d'un utilisateur sur lequel votre extension est installée.

  2. Modifiez integration-tests/functions/index.js et collez le code suivant :

    import { logger } from "firebase-functions/v1";
    import { onCustomEventPublished } from "firebase-functions/v2/eventarc";
    
    import { initializeApp } from "firebase-admin/app";
    import { getDatabase } from "firebase-admin/database";
    
    const app = initializeApp();
    
    export const extraemphasis = onCustomEventPublished(
      "test-publisher.rtdb-uppercase-messages.v1.complete",
      async (event) => {
        logger.info("Received makeuppercase completed event", event);
    
        const refUrl = event.subject;
        const ref = getDatabase().refFromURL(refUrl);
        const upper = (await ref.get()).val();
        return ref.set(`${upper}!!!`);
      }
    );
    

    Ceci est un exemple de fonction de post-traitement qu'un utilisateur pourrait écrire. Dans ce cas, la fonction écoute l'extension pour publier un événement complete et, lorsqu'elle est déclenchée, ajoute trois points d'exclamation au message nouvellement mis en majuscule.

  3. Redémarrez l'émulateur. L'émulateur chargera les fonctions de l'extension ainsi que la fonction de post-traitement définie par "l'utilisateur".

  4. Visitez l'interface utilisateur de l'émulateur de base de données et modifiez le nœud racine de la base de données en utilisant le chemin que vous avez défini ci-dessus :

    • Champ : msgs
    • Tapez : json
    • Valeur : {"11": {"original": "recipe"}}

    Lorsque vous enregistrez les modifications de votre base de données, la fonction makeuppercase de l'extension et la fonction extraemphasis de l'utilisateur doivent se déclencher en séquence, ce qui fait que le champ upper obtient la valeur RECIPE!!! .

8. Ajouter des gestionnaires d'événements de cycle de vie

L'extension que vous avez écrite jusqu'à présent traite les messages au fur et à mesure de leur création. Mais que se passe-t-il si vos utilisateurs disposent déjà d’une base de données de messages lorsqu’ils installent l’extension ? Les extensions Firebase disposent d'une fonctionnalité appelée hooks d'événements de cycle de vie que vous pouvez utiliser pour déclencher des actions lorsque votre extension est installée, mise à jour ou reconfigurée. Dans cette section, vous utiliserez les hooks d'événements de cycle de vie pour remplir la base de données de messages existante d'un projet avec des messages en majuscules lorsqu'un utilisateur installe votre extension.

Les extensions Firebase utilisent Cloud Tasks pour exécuter vos gestionnaires d'événements de cycle de vie. Vous définissez des gestionnaires d'événements à l'aide de Cloud Functions ; chaque fois qu'une instance de votre extension atteint l'un des événements du cycle de vie pris en charge, si vous avez défini un gestionnaire, le gestionnaire sera ajouté à une file d'attente Cloud Tasks. Cloud Tasks exécutera ensuite le gestionnaire de manière asynchrone. Pendant qu'un gestionnaire d'événements de cycle de vie est en cours d'exécution, la console Firebase signalera à l'utilisateur que l'instance d'extension a une tâche de traitement en cours. C'est à votre fonction de gestionnaire de signaler l'état en cours et l'achèvement des tâches à l'utilisateur.

Pour ajouter un gestionnaire d'événements de cycle de vie qui remplit les messages existants, procédez comme suit :

  1. Définissez une nouvelle fonction Cloud déclenchée par des événements de file d'attente de tâches :

    fonctions/index.js

    import { tasks } from "firebase-functions/v1";
    
    import { getDatabase } from "firebase-admin/database";
    import { getExtensions } from "firebase-admin/extensions";
    import { getFunctions } from "firebase-admin/functions";
    
    export const backfilldata = tasks.taskQueue().onDispatch(async () => {
      const batch = await getDatabase()
        .ref(process.env.MESSAGE_PATH)
        .parent.parent.orderByChild("upper")
        .limitToFirst(20)
        .get();
    
      const promises = [];
      for (const key in batch.val()) {
        const msg = batch.child(key);
        if (msg.hasChild("original") && !msg.hasChild("upper")) {
          const upper = msg.child("original").val().toUpperCase();
          promises.push(msg.child("upper").ref.set(upper));
        }
      }
      await Promise.all(promises);
    
      if (promises.length > 0) {
        const queue = getFunctions().taskQueue(
          "backfilldata",
          process.env.EXT_INSTANCE_ID
        );
        return queue.enqueue({});
      } else {
        return getExtensions()
          .runtime()
          .setProcessingState("PROCESSING_COMPLETE", "Backfill complete.");
      }
    });
    

    Notez que la fonction ne traite que quelques enregistrements avant de se rajouter à la file d'attente des tâches. Il s'agit d'une stratégie couramment utilisée pour gérer les tâches de traitement qui ne peuvent pas être terminées dans le délai d'expiration d'une fonction Cloud. Étant donné que vous ne pouvez pas prédire combien de messages un utilisateur pourrait déjà avoir dans sa base de données lorsqu'il installe votre extension, cette stratégie est une bonne solution.

  2. Dans le fichier extension.yaml , déclarez votre fonction de remplissage en tant que ressource d'extension possédant la propriété taskQueueTrigger :

    resources:
      - name: makeuppercase
        ...
      - name: backfilldata
        type: firebaseextensions.v1beta.function
        description: >-
          Backfill existing messages with uppercase versions
        properties:
          runtime: "nodejs18"
          taskQueueTrigger: {}
    

    Déclarez ensuite la fonction comme gestionnaire de l'événement de cycle de vie onInstall :

    lifecycleEvents:
      onInstall:
        function: backfilldata
        processingMessage: Uppercasing existing messages
    
  3. Bien que le remplissage des messages existants soit agréable, l'extension pourrait toujours fonctionner sans cela. Dans des situations comme celle-ci, vous devez rendre facultative l’exécution des gestionnaires d’événements de cycle de vie.

    Pour ce faire, ajoutez un nouveau paramètre à extension.yaml :

    - param: DO_BACKFILL
      label: Backfill existing messages
      description: >-
        Generate uppercase versions of existing messages?
      type: select
      required: true
      options:
        - label: Yes
          value: true
        - label: No
          value: false
    

    Ensuite, au début de la fonction de remplissage, vérifiez la valeur du paramètre DO_BACKFILL et quittez plus tôt s'il n'est pas défini :

    fonctions/index.js

    if (!process.env.DO_BACKFILL) {
      return getExtensions()
        .runtime()
        .setProcessingState("PROCESSING_COMPLETE", "Backfill skipped.");
    }
    

Avec les modifications ci-dessus, l'extension convertira désormais les messages existants en majuscules lors de son installation.

Jusqu'à présent, vous utilisiez l'émulateur d'extension pour développer votre extension et tester les modifications en cours. Cependant, l'émulateur d'extension ignore le processus d'installation. Par conséquent, pour tester votre gestionnaire d'événements onInstall , vous devrez installer l'extension dans un projet réel. C'est aussi bien, car avec l'ajout de cette fonctionnalité de remplissage automatique, l'extension du didacticiel est désormais complète en code !

9. Déployer dans un vrai projet Firebase

Bien que l'émulateur d'extensions soit un excellent outil pour itérer rapidement sur une extension pendant le développement, vous voudrez à un moment donné l'essayer dans un projet réel.

Pour ce faire, configurez d'abord un nouveau projet avec certains services activés :

  1. Dans la console Firebase , ajoutez un nouveau projet.
  2. Mettez à niveau votre projet vers le plan Blaze par répartition. Cloud Functions pour Firebase nécessite que votre projet dispose d'un compte de facturation. Vous avez donc également besoin d'un compte de facturation pour installer une extension.
  3. Dans votre nouveau projet, activez Real-time Database .
  4. Puisque vous souhaitez tester la capacité de votre extension à remplir les données existantes lors de l'installation, importez des exemples de données dans votre instance de base de données en temps réel :
    1. Téléchargez des données RTDB de départ .
    2. Sur la page Base de données en temps réel de la console Firebase, cliquez sur (plus) > Importer JSON et sélectionnez le fichier que vous venez de télécharger.
  5. Pour permettre à la fonction de remplissage d'utiliser la méthode orderByChild , configurez la base de données pour indexer les messages sur la valeur de upper :

    {
      "rules": {
        ".read": false,
        ".write": false,
        "messages": {
          ".indexOn": "upper"
        }
      }
    }
    

Installez maintenant votre extension à partir d'une source locale dans le nouveau projet :

  1. Créez un nouveau répertoire pour votre projet Firebase :

    mkdir ~/extensions-live-test && cd ~/extensions-live-test
    
  2. Initialisez un projet Firebase dans le répertoire de travail :

    firebase init database
    

    Lorsque vous y êtes invité, sélectionnez le projet que vous venez de créer.

  3. Installez l'extension dans votre projet Firebase local :

    firebase ext:install /path/to/rtdb-uppercase-messages
    

    Ici, vous pouvez voir à quoi ressemble l'expérience utilisateur lors de l'installation d'une extension à l'aide de l'outil Firebase CLI. Assurez-vous de sélectionner « oui » lorsque l'outil de configuration vous demande si vous souhaitez remplir votre base de données existante.

    Après avoir sélectionné les options de configuration, la CLI Firebase enregistrera votre configuration dans le répertoire extensions et enregistrera l'emplacement source de l'extension dans le fichier firebase.json . Collectivement, ces deux enregistrements sont appelés les extensions manifest . Les utilisateurs peuvent utiliser le manifeste pour enregistrer la configuration de leurs extensions et la déployer sur différents projets.

  4. Déployez votre configuration d'extension sur votre projet en direct :

    firebase deploy --only extensions
    

Si tout se passe bien, la CLI Firebase devrait télécharger votre extension sur votre projet et l'installer. Une fois l'installation terminée, la tâche de remplissage s'exécutera et, dans quelques minutes, votre base de données sera mise à jour avec des messages en majuscules. Ajoutez de nouveaux nœuds à la base de données de messages et assurez-vous que l'extension fonctionne également pour les nouveaux messages.

10. Rédiger la documentation

Avant de partager votre extension avec les utilisateurs, assurez-vous de fournir suffisamment de documentation pour qu'ils réussissent.

Lorsque vous avez initialisé le projet d'extension, la CLI Firebase a créé des versions stub de la documentation minimale requise. Mettez à jour ces fichiers pour refléter avec précision l'extension que vous avez créée.

extension.yaml

Vous avez déjà mis à jour ce fichier au fur et à mesure que vous développiez cette extension, vous n'avez donc pas besoin d'effectuer d'autres mises à jour pour le moment.

Cependant, ne négligez pas l'importance de la documentation contenue dans ce fichier. En plus des informations d'identification cruciales d'une extension (nom, description, auteur, emplacement du référentiel officiel), le fichier extension.yaml contient une documentation destinée à l'utilisateur pour chaque ressource et paramètre configurable par l'utilisateur. Ces informations sont présentées aux utilisateurs dans la console Firebase, Extensions Hub et Firebase CLI.

PRÉINSTALLER.md

Dans ce fichier, fournissez les informations dont l'utilisateur a besoin avant d'installer votre extension : décrivez brièvement ce que fait l'extension, expliquez les conditions préalables et donnez à l'utilisateur des informations sur les implications de facturation liées à l'installation de l'extension. Si vous disposez d’un site Web contenant des informations supplémentaires, c’est également un bon endroit pour créer un lien.

Le texte de ce fichier est affiché à l'utilisateur dans Extensions Hub et par la commande firebase ext:info .

Voici un exemple de fichier PREINSTALL :

Use this extension to automatically convert strings to upper case when added to
a specified Realtime Database path.

This extension expects a database layout like the following example:

    "messages": {
      MESSAGE_ID: {
        "original": MESSAGE_TEXT
      },
      MESSAGE_ID: {
        "original": MESSAGE_TEXT
      },
    }

When you create new string records, this extension creates a new sibling record
with upper-cased text:

    MESSAGE_ID: {
      "original": MESSAGE_TEXT,
      "upper": UPPERCASE_MESSAGE_TEXT,
    }

#### Additional setup

Before installing this extension, make sure that you've
[set up Realtime Database](https://firebase.google.com/docs/database/quickstart)
in your Firebase project.

#### 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:
  - Realtime Database
  - Cloud Functions (Node.js 10+ runtime)
    [See FAQs](https://firebase.google.com/support/faq#extensions-pricing)
- If you enable events,
  [Eventarc fees apply](https://cloud.google.com/eventarc/pricing).

POSTINSTALL.md

Ce fichier contient des informations utiles aux utilisateurs une fois qu'ils ont installé avec succès votre extension : par exemple, les étapes de configuration de suivi, un exemple de l'extension en action, etc.

Le contenu de POSTINSTALL.md est affiché dans la console Firebase une fois qu'une extension est configurée et installée. Vous pouvez référencer les paramètres utilisateur dans ce fichier et ils seront remplacés par les valeurs configurées.

Voici un exemple de fichier de post-installation pour l'extension du didacticiel :

### See it in action

You can test out this extension right away!

1.  Go to your
    [Realtime Database dashboard](https://console.firebase.google.com/project/${param:PROJECT_ID}/database/${param:PROJECT_ID}/data) in the Firebase console.

1.  Add a message string to a path that matches the pattern `${param:MESSAGE_PATH}`.

1.  In a few seconds, you'll see a sibling node named `upper` that contains the
    message in upper case.

### Using the extension

We recommend adding data by pushing -- for example,
`firebase.database().ref().push()` -- because pushing assigns an automatically
generated ID to the node in the database. During retrieval, these nodes are
guaranteed to be ordered by the time they were added. Learn more about reading
and writing data for your platform (iOS, Android, or Web) in the
[Realtime Database documentation](https://firebase.google.com/docs/database/).

### Monitoring

As a best practice, you can
[monitor the activity](https://firebase.google.com/docs/extensions/manage-installed-extensions#monitor)
of your installed extension, including checks on its health, usage, and logs.

CHANGELOG.md

Vous devez également documenter les modifications que vous apportez entre les versions d'une extension dans le fichier CHANGELOG.md .

Étant donné que l'exemple d'extension n'a jamais été publié auparavant, le journal des modifications ne contient qu'une seule entrée :

## Version 0.0.1

Initial release of the _Convert messages to upper case_ extension.

LISEZMOI.md

La plupart des extensions fournissent également un fichier Lisez-moi à l'intention des utilisateurs visitant le référentiel de l'extension. vous pouvez écrire ce fichier à la main ou générer un read me en utilisant la commande.

Pour les besoins de ce guide, évitez d'écrire un fichier Lisez-moi.

Documentation supplémentaire

La documentation décrite ci-dessus constitue l'ensemble minimum de documentation que vous devez fournir aux utilisateurs. De nombreuses extensions nécessitent une documentation plus détaillée pour que les utilisateurs puissent les utiliser avec succès. Lorsque tel est le cas, vous devez rédiger une documentation supplémentaire et l’héberger dans un endroit vers lequel vous pouvez diriger les utilisateurs.

Pour les besoins de ce guide, évitez de rédiger une documentation plus complète.

11. Publier sur Extensions Hub

Maintenant que le code de votre extension est complet et documenté, vous êtes prêt à la partager avec le monde entier sur Extensions Hub. Mais comme il ne s’agit que d’un tutoriel, ne le faites pas réellement. Allez commencer à écrire votre propre extension en utilisant ce que vous avez appris ici et dans le reste de la documentation de l'éditeur des extensions Firebase, et en examinant la source des extensions officielles écrites par Firebase.

Lorsque vous êtes prêt à publier votre travail sur Extensions Hub, voici comment procéder :

  1. Si vous publiez votre première extension, inscrivez-vous en tant qu'éditeur d'extensions . Lorsque vous vous inscrivez en tant qu'éditeur d'extensions, vous créez un identifiant d'éditeur qui permet aux utilisateurs de vous identifier rapidement en tant qu'auteur de vos extensions.
  2. Hébergez le code source de votre extension dans un emplacement publiquement vérifiable. Lorsque votre code est disponible à partir d'une source vérifiable, Firebase peut publier votre extension directement à partir de cet emplacement. Cela permet de garantir que vous publiez la version actuellement publiée de votre extension et aide les utilisateurs en leur permettant d'examiner le code qu'ils installent dans leurs projets.

    Actuellement, cela signifie rendre votre extension disponible dans un référentiel GitHub public.

  3. Téléchargez votre extension sur Extensions Hub à l'aide de la commande firebase ext:dev:upload .

  4. Accédez au tableau de bord de votre éditeur dans la console Firebase, recherchez l'extension que vous venez de télécharger, puis cliquez sur « Publier sur Extensions Hub ». Cela nécessite un examen de notre équipe d’examen, ce qui peut prendre quelques jours. Si elle est approuvée, l’extension sera publiée sur Extensions Hub. En cas de refus, vous recevrez un message expliquant la raison ; vous pouvez ensuite résoudre les problèmes signalés et soumettre à nouveau pour examen.