Iniziare a creare un'estensione

Questa pagina descrive i passaggi necessari per creare una semplice estensione Firebase, che puoi installare nei tuoi progetti o condividere con altri. Questo semplice esempio di estensione Firebase monitora il tuo Realtime Database per i messaggi e li converte in maiuscolo.

1. Configurare l'ambiente e inizializzare un progetto

Prima di poter iniziare a creare un'estensione, devi configurare un ambiente di build con gli strumenti richiesti.

  1. Installa Node.js 16 o versioni successive. Un modo per installare Node è utilizzare nvm (o nvm-windows).

  2. Installa o esegui l'aggiornamento all'ultima versione dell'interfaccia a riga di comando di Firebase. Per installare o aggiornare utilizzando npm, esegui questo comando:

    npm install -g firebase-tools

Ora utilizza l'interfaccia a riga di comando di Firebase per inizializzare un nuovo progetto di estensione:

  1. Crea una directory per l'estensione e cd al suo interno:

    mkdir rtdb-uppercase-messages && cd rtdb-uppercase-messages
  2. Esegui il comando ext:dev:init dell'interfaccia a riga di comando di Firebase:

    firebase ext:dev:init

    Quando richiesto, scegli JavaScript come linguaggio per le funzioni (ma tieni presente che puoi utilizzare anche TypeScript quando sviluppi la tua estensione) e, quando ti viene chiesto di installare le dipendenze, rispondi "sì". (Accetta i valori predefiniti per qualsiasi altra opzione.) Questo comando configurerà una base di codice scheletrica per una nuova estensione, da cui potrai iniziare a sviluppare la tua estensione.

2. Prova l'estensione di esempio utilizzando l'emulatore

Quando l'interfaccia a riga di comando di Firebase ha inizializzato la nuova directory delle estensioni, ha creato una semplice funzione di esempio e una directory integration-tests che contiene i file necessari per eseguire un'estensione utilizzando la suite di emulatori Firebase.

Prova a eseguire l'estensione di esempio nell'emulatore:

  1. Passa alla directory integration-tests:

    cd functions/integration-tests
  2. Avvia l'emulatore con un progetto demo:

    firebase emulators:start --project=demo-test

    L'emulatore carica l'estensione in un progetto "fittizio" predefinito (demo-test). Finora l'estensione è costituita da una singola funzione attivata da HTTP, greetTheWorld, che restituisce un messaggio "Hello world" quando viene eseguita.

  3. Con l'emulatore ancora in esecuzione, prova la funzione greetTheWorld dell'estensione visitando l'URL stampato all'avvio.

    Il browser visualizza il messaggio "Hello World from greet-the-world".

  4. Il codice sorgente di questa funzione si trova nella directory functions dell'estensione. Apri l'origine nell'editor o nell'IDE che preferisci:

    functions/index.js

    const functions = require("firebase-functions/v1");
    
    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. Mentre l'emulatore è in esecuzione, ricarica automaticamente le modifiche apportate al codice delle funzioni. Prova ad apportare una piccola modifica alla funzione greetTheWorld:

    functions/index.js

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

    Salva le modifiche. L'emulatore ricaricherà il codice e ora, quando visiti l'URL della funzione, vedrai il saluto aggiornato.

3. Aggiungi informazioni di base a extension.yaml

Ora che hai configurato un ambiente di sviluppo ed esegui l'emulatore di estensioni, puoi iniziare a scrivere la tua estensione.

Come primo passo, modifica i metadati dell'estensione predefinita in modo che riflettano l'estensione che vuoi scrivere anziché greet-the-world. Questi metadati vengono memorizzati nel file extension.yaml.

  1. Apri extension.yaml nell'editor e sostituisci l'intero contenuto del file con quanto segue:

    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
    

    Tieni presente la convenzione di denominazione utilizzata nel campo name: le estensioni Firebase ufficiali hanno un prefisso che indica il prodotto Firebase principale su cui opera l'estensione, seguito da una descrizione della funzione dell'estensione. Ti consigliamo di utilizzare la stessa convenzione nelle tue estensioni.

  2. Poiché hai modificato il nome dell'estensione, devi aggiornare anche la configurazione dell'emulatore con il nuovo nome:

    1. In functions/integration-tests/firebase.json, modifica greet-the-world in rtdb-uppercase-messages.
    2. Rinomina functions/integration-tests/extensions/greet-the-world.env in functions/integration-tests/extensions/rtdb-uppercase-messages.env.

Nel codice dell'estensione sono ancora presenti alcuni resti dell'estensione greet-the-world, ma lasciali per ora. Aggiornerai questi dati nelle sezioni successive.

4. Scrivere una funzione Cloud Functions e dichiararla come risorsa di estensione

Ora puoi iniziare a scrivere del codice. In questo passaggio, scriverai una funzione Cloud che esegue l'attività principale della tua estensione, ovvero monitorare Realtime Database per i messaggi e convertirli in maiuscolo.

  1. Apri l'origine delle funzioni dell'estensione (nella directory functions dell'estensione) nell'editor o nell'IDE che preferisci. Sostituisci i suoi contenuti con i seguenti:

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

    La vecchia funzione, che hai sostituito, era una funzione attivata da HTTP, che veniva eseguita quando si accedeva a un endpoint HTTP. La nuova funzione viene attivata da eventi del database in tempo reale: monitora i nuovi elementi in un percorso specifico e, quando ne viene rilevato uno, scrive di nuovo la versione in maiuscolo del valore nel database.

    A proposito, questo nuovo file utilizza la sintassi dei moduli ECMAScript (import e export) anziché CommonJS (require). Per utilizzare i moduli ES in Node, specifica "type": "module" in functions/package.json:

    {
      "name": "rtdb-uppercase-messages",
      "main": "index.js",
      "type": "module",
      
    }
    
  2. Ogni funzione dell'estensione deve essere dichiarata nel file extension.yaml. L'estensione di esempio ha dichiarato greetTheWorld come unica funzione Cloud dell'estensione. Ora che l'hai sostituita con makeuppercase, devi anche aggiornarne la dichiarazione.

    Apri extension.yaml e aggiungi un campo 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. Poiché ora l'estensione utilizza Realtime Database come trigger, devi aggiornare la configurazione dell'emulatore per eseguire l'emulatore RTDB insieme all'emulatore Cloud Functions:

    1. Se l'emulatore è ancora in esecuzione, arrestalo premendo Ctrl+C.

    2. Dalla directory functions/integration-tests, esegui questo comando:

      firebase init emulators

      Quando ti viene chiesto, salta la configurazione di un progetto predefinito, quindi seleziona gli emulatori di Functions e Database. Accetta le porte predefinite e consenti allo strumento di configurazione di scaricare i file necessari.

    3. Riavvia l'emulatore:

      firebase emulators:start --project=demo-test
  4. Prova l'estensione aggiornata:

    1. Apri la UI dell'emulatore di database utilizzando il link stampato dall'emulatore quando l'hai avviato.

    2. Modifica il nodo principale del database:

      • Campo: messages
      • Tipo: json
      • Valore:{"11": {"original": "recipe"}}

      Se tutto è configurato correttamente, quando salvi le modifiche al database, la funzione makeuppercase dell'estensione dovrebbe attivarsi e aggiungere un record secondario al messaggio 11 con i contenuti "upper": "RECIPE". Dai un'occhiata ai log e alle schede del database dell'interfaccia utente dell'emulatore per confermare i risultati previsti.

    3. Prova ad aggiungere altri figli al nodo messages ({"original":"any text"}). Ogni volta che aggiungi un nuovo record, l'estensione dovrebbe aggiungere un campo uppercase contenente i contenuti in maiuscolo del campo original.

Ora hai un'estensione completa, anche se semplice, che opera su un'istanza RTDB. Nelle sezioni successive perfezionerai questa estensione con alcune funzionalità aggiuntive. Poi, preparerai l'estensione per la distribuzione ad altri e, infine, imparerai a pubblicarla nell'hub delle estensioni.

5. Dichiarare API e ruoli

Firebase concede a ogni istanza di un'estensione installata un accesso limitato al progetto e ai relativi dati utilizzando un service account per istanza. Ogni account dispone dell'insieme minimo di autorizzazioni necessario per funzionare. Per questo motivo, devi dichiarare esplicitamente tutti i ruoli IAM richiesti dalla tua estensione. Quando gli utenti installano la tua estensione, Firebase crea un service account con questi ruoli concessi e lo utilizza per eseguire l'estensione.

Non è necessario dichiarare i ruoli per attivare gli eventi di un prodotto, ma è necessario dichiarare un ruolo per interagire con il prodotto. Poiché la funzione che hai aggiunto nell'ultimo passaggio scrive nel database in tempo reale, devi aggiungere la seguente dichiarazione a extension.yaml:

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

Allo stesso modo, dichiari le API di Google utilizzate da un'estensione nel campo apis. Quando gli utenti installano l'estensione, viene chiesto loro se vogliono abilitare automaticamente queste API per il loro progetto. In genere, questa operazione è necessaria solo per le API di Google non Firebase e non è richiesta per questa guida.

6. Definisci i parametri configurabili dall'utente

La funzione che hai creato negli ultimi due passaggi ha monitorato una posizione RTDB specifica per i messaggi in arrivo. A volte, guardare una posizione specifica è davvero ciò che vuoi, ad esempio quando l'estensione opera su una struttura di database che utilizzi esclusivamente per la tua estensione. Tuttavia, la maggior parte delle volte vorrai rendere questi valori configurabili dagli utenti che installano l'estensione nei loro progetti. In questo modo, gli utenti possono utilizzare la tua estensione per lavorare con la configurazione del database esistente.

Rendi configurabile dall'utente il percorso in cui l'estensione cerca nuovi messaggi:

  1. Nel file extension.yaml, aggiungi una sezione 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
    

    Definisce un nuovo parametro stringa che verrà richiesto agli utenti di impostare quando installano l'estensione.

  2. Sempre nel file extension.yaml, torna alla dichiarazione makeuppercase e modifica il campo resource come segue:

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

    Il token ${param:MESSAGE_PATH} è un riferimento al parametro che hai appena definito. Quando l'estensione viene eseguita, questo token verrà sostituito dal valore che l'utente ha configurato per quel parametro, con il risultato che la funzione makeuppercase ascolterà il percorso specificato dall'utente. Puoi utilizzare questa sintassi per fare riferimento a qualsiasi parametro definito dall'utente in extension.yaml (e in POSTINSTALL.md, di cui parleremo più avanti).

  3. Puoi anche accedere ai parametri definiti dall'utente dal codice delle funzioni.

    Nella funzione che hai scritto nella sezione precedente, hai codificato il percorso da monitorare per rilevare le modifiche. Modifica la definizione del trigger in modo che faccia riferimento al valore definito dall'utente:

    functions/index.js

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

    Tieni presente che in Firebase Extensions questa modifica è puramente a scopo di documentazione: quando una funzione Cloud Functions viene implementata come parte di un'estensione, utilizza la definizione del trigger dal file extension.yaml e ignora il valore specificato nella definizione della funzione. Tuttavia, è consigliabile documentare nel codice la provenienza di questo valore.

  4. Potresti rimanere deluso da una modifica del codice che non ha alcun effetto in fase di runtime, ma la lezione importante da imparare è che puoi accedere a qualsiasi parametro definito dall'utente nel codice della funzione e utilizzarlo come valore ordinario nella logica della funzione. Per dimostrare questa funzionalità, aggiungi la seguente istruzione di log per dimostrare che stai effettivamente accedendo al valore definito dall'utente:

    functions/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. Di norma, agli utenti viene chiesto di fornire i valori dei parametri quando installano un'estensione. Quando utilizzi l'emulatore per test e sviluppo, tuttavia, salti la procedura di installazione, quindi fornisci i valori per i parametri definiti dall'utente utilizzando un file env.

    Apri functions/integration-tests/extensions/rtdb-uppercase-messages.env e sostituisci la definizione di GREETING con la seguente:

    MESSAGE_PATH=/msgs/{pushId}/original
    

    Tieni presente che il percorso precedente è diverso dal percorso predefinito e dal percorso definito in precedenza. Questo serve solo a dimostrare, quando provi l'estensione aggiornata, che la definizione ha effetto.

  6. Ora riavvia l'emulatore e visita di nuovo la relativa UI.

    Modifica il nodo radice del database utilizzando il percorso definito sopra:

    • Campo: msgs
    • Tipo: json
    • Valore:{"11": {"original": "recipe"}}

    Quando salvi le modifiche al database, la funzione makeuppercase dell'estensione dovrebbe attivarsi come prima, ma ora dovrebbe anche stampare il parametro definito dall'utente nel log della console.

7. Fornire hook di eventi per la logica definita dall'utente

In qualità di autore di un'estensione, hai già visto come un prodotto Firebase può attivare la logica fornita dall'estensione: la creazione di nuovi record in Realtime Database attiva la funzione makeuppercase. La tua estensione può avere un rapporto analogo con gli utenti che la installano: la tua estensione può attivare una logica definita dall'utente.

Un'estensione può fornire hook sincroni, hook asincroni o entrambi. Gli hook sincroni consentono agli utenti di eseguire attività che bloccano il completamento di una delle funzioni dell'estensione. Questo può essere utile, ad esempio, per consentire agli utenti di eseguire un pre-elaborazione personalizzata prima che un'estensione svolga il suo lavoro.

In questa guida, aggiungerai un hook asincrono alla tua estensione, che consentirà agli utenti di definire i propri passaggi di elaborazione da eseguire dopo che l'estensione scrive il messaggio in maiuscolo in Realtime Database. Gli hook asincroni utilizzano Eventarc per attivare le funzioni definite dall'utente. Le estensioni dichiarano i tipi di eventi che emettono e, quando gli utenti installano l'estensione, scelgono i tipi di eventi che interessano loro. Se sceglie almeno un evento, Firebase eseguirà il provisioning di un canale Eventarc per l'estensione nell'ambito della procedura di installazione. Gli utenti possono quindi eseguire il deployment delle proprie funzioni cloud che ascoltano quel canale e si attivano quando l'estensione pubblica nuovi eventi.

Per aggiungere un hook asincrono:

  1. Nel file extension.yaml, aggiungi la seguente sezione, che dichiara l'unico tipo di evento emesso dall'estensione:

    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.
    

    I tipi di eventi devono essere univoci a livello universale; per garantire l'univocità, assegna sempre un nome ai tuoi eventi utilizzando il seguente formato: <publisher-id>.<extension-id>.<version>.<description>. (Non hai ancora un ID editore, quindi per ora usa test-publisher.)

  2. Alla fine della funzione makeuppercase, aggiungi un codice che pubblica un evento del tipo che hai appena dichiarato:

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

    Questo codice di esempio sfrutta il fatto che la variabile di ambiente EVENTARC_CHANNEL è definita solo quando l'utente ha attivato almeno un tipo di evento. Se EVENTARC_CHANNEL non è definita, il codice non tenta di pubblicare alcun evento.

    Puoi allegare informazioni aggiuntive a un evento Eventarc. Nell'esempio precedente, l'evento ha un campo subject che contiene un riferimento al valore appena creato e un payload data che contiene i messaggi originali e in maiuscolo. Le funzioni definite dall'utente che vengono attivate dall'evento possono utilizzare queste informazioni.

  3. Normalmente, le variabili di ambiente EVENTARC_CHANNEL e EXT_SELECTED_EVENTS vengono definite in base alle opzioni selezionate dall'utente durante l'installazione. Per i test con l'emulatore, definisci manualmente queste variabili nel file rtdb-uppercase-messages.env:

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

A questo punto, hai completato i passaggi necessari per aggiungere un hook di eventi asincroni all'estensione.

Per provare questa nuova funzionalità che hai appena implementato, nei passaggi successivi, assumi il ruolo di un utente che sta installando l'estensione:

  1. Dalla directory functions/integration-tests, inizializza un nuovo progetto Firebase:

    firebase init functions

    Quando richiesto, rifiuta di configurare un progetto predefinito, seleziona JavaScript come linguaggio di Cloud Functions e installa le dipendenze richieste. Questo progetto rappresenta il progetto di un utente in cui è installata la tua estensione.

  2. Modifica integration-tests/functions/index.js e incolla il seguente codice:

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

    Questo è un esempio di funzione di post-elaborazione che un utente potrebbe scrivere. In questo caso, la funzione attende che l'estensione pubblichi un evento complete e, quando viene attivata, aggiunge tre punti esclamativi al messaggio convertito in maiuscolo.

  3. Riavvia l'emulatore. L'emulatore caricherà le funzioni dell'estensione e la funzione di post-elaborazione definita dall'utente.

  4. Visita l'interfaccia utente dell'emulatore di database e modifica il nodo radice del database utilizzando il percorso definito sopra:

    • Campo:msgs
    • Tipo: json
    • Valore:{"11": {"original": "recipe"}}

    Quando salvi le modifiche al database, la funzione makeuppercase dell'estensione e la funzione extraemphasis dell'utente devono attivarsi in sequenza, in modo che il campo upper riceva il valore RECIPE!!!.

8. Aggiungere gestori di eventi del ciclo di vita

L'estensione che hai scritto finora elabora i messaggi man mano che vengono creati. Ma cosa succede se i tuoi utenti hanno già un database di messaggi quando installano l'estensione? Firebase Extensions ha una funzionalità chiamata hook degli eventi del ciclo di vita che puoi utilizzare per attivare azioni quando l'estensione viene installata, aggiornata o riconfigurata. In questa sezione, utilizzerai gli hook degli eventi del ciclo di vita per eseguire il backfill di un database di messaggi esistente di un progetto con messaggi in maiuscolo quando un utente installa la tua estensione.

Firebase Extensions utilizza Cloud Tasks per eseguire i gestori di eventi del ciclo di vita. Definisci i gestori di eventi utilizzando Cloud Functions. Ogni volta che un'istanza della tua estensione raggiunge uno degli eventi del ciclo di vita supportati, se hai definito un gestore, questo verrà aggiunto a una coda di Cloud Tasks. Cloud Tasks eseguirà quindi il gestore in modo asincrono. Mentre è in esecuzione un gestore di eventi del ciclo di vita, la console Firebase segnala all'utente che l'istanza dell'estensione ha un'attività di elaborazione in corso. Spetta alla funzione di gestione comunicare all'utente lo stato e il completamento dell'attività.

Per aggiungere un gestore di eventi del ciclo di vita che esegue il backfill dei messaggi esistenti, procedi nel seguente modo:

  1. Definisci una nuova funzione Cloud Functions attivata dagli eventi della coda di attività:

    functions/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.");
      }
    });
    

    Tieni presente che la funzione elabora solo alcuni record prima di aggiungersi di nuovo alla coda delle attività. Si tratta di una strategia comunemente utilizzata per gestire le attività di elaborazione che non possono essere completate entro la finestra di timeout di una funzione Cloud. Poiché non puoi prevedere quanti messaggi un utente potrebbe già avere nel suo database quando installa la tua estensione, questa strategia è adatta.

  2. Nel file extension.yaml, dichiara la funzione di backfill come risorsa di estensione con la proprietà taskQueueTrigger:

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

    Dopodiché, dichiara la funzione come gestore dell'evento del ciclo di vita onInstall:

    lifecycleEvents:
      onInstall:
        function: backfilldata
        processingMessage: Uppercasing existing messages
    
  3. Anche se il riempimento dei messaggi esistenti è utile, l'estensione potrebbe funzionare comunque senza. In situazioni come questa, devi rendere facoltativa l'esecuzione dei gestori di eventi del ciclo di vita.

    Per farlo, aggiungi un nuovo parametro a 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
    

    Poi, all'inizio della funzione di backfill, controlla il valore del parametro DO_BACKFILL ed esci in anticipo se non è impostato:

    functions/index.js

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

Con le modifiche riportate sopra, l'estensione ora convertirà i messaggi esistenti in maiuscolo al momento dell'installazione.

Fino a questo punto, hai utilizzato l'emulatore di estensioni per sviluppare l'estensione e testare le modifiche in corso. Tuttavia, l'emulatore di estensioni salta la procedura di installazione, quindi per testare il gestore di eventi onInstall, devi installare l'estensione in un progetto reale. È meglio così, perché con l'aggiunta di questa funzionalità di riempimento automatico, l'estensione del tutorial è ora completa.

9. Esegui il deployment in un progetto Firebase reale

Sebbene l'emulatore di estensioni sia un ottimo strumento per iterare rapidamente un'estensione durante lo sviluppo, a un certo punto vorrai provarla in un progetto reale.

Per farlo, configura prima un nuovo progetto con alcuni servizi abilitati:

  1. Nella console Firebase, aggiungi un nuovo progetto.
  2. Esegui l'upgrade del tuo progetto al piano Blaze con pagamento a consumo. Cloud Functions for Firebase richiede che il tuo progetto disponga di un account di fatturazione, quindi ne hai bisogno anche per installare un'estensione.
  3. Nel nuovo progetto, attiva Realtime Database.
  4. Poiché vuoi testare la capacità dell'estensione di eseguire il backfill dei dati esistenti durante l'installazione, importa alcuni dati di esempio nell'istanza del database in tempo reale:
    1. Scarica alcuni dati RTDB iniziali.
    2. Nella pagina Realtime Database della console Firebase, fai clic su (altro) > Importa JSON e seleziona il file che hai appena scaricato.
  5. Per consentire alla funzione di backfill di utilizzare il metodo orderByChild, configura il database in modo da indicizzare i messaggi in base al valore di upper:

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

Ora installa l'estensione dalla sorgente locale nel nuovo progetto:

  1. Crea una nuova directory per il tuo progetto Firebase:

    mkdir ~/extensions-live-test && cd ~/extensions-live-test
    
  2. Inizializza un progetto Firebase nella directory di lavoro:

    firebase init database

    Quando richiesto, seleziona il progetto che hai appena creato.

  3. Installa l'estensione nel progetto Firebase locale:

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

    Qui puoi vedere com'è l'esperienza utente quando si installa un'estensione utilizzando lo strumento Firebase CLI. Assicurati di selezionare "Sì" quando lo strumento di configurazione ti chiede se vuoi eseguire il backfill del database esistente.

    Dopo aver selezionato le opzioni di configurazione, la CLI Firebase salverà la configurazione nella directory extensions e registrerà la posizione dell'origine dell'estensione nel file firebase.json. Questi due record insieme sono chiamati manifesto delle estensioni. Gli utenti possono utilizzare il manifest per salvare la configurazione delle estensioni e distribuirla a progetti diversi.

  4. Esegui il deployment della configurazione dell'estensione nel progetto live:

    firebase deploy --only extensions

Se tutto va bene, l'interfaccia a riga di comando di Firebase dovrebbe caricare l'estensione nel tuo progetto e installarla. Al termine dell'installazione, verrà eseguita l'attività di backfill e, in pochi minuti, il database verrà aggiornato con i messaggi in maiuscolo. Aggiungi alcuni nuovi nodi al database dei messaggi e assicurati che l'estensione funzioni anche per i nuovi messaggi.

10. Scrivere la documentazione

Prima di condividere l'estensione con gli utenti, assicurati di fornire una documentazione sufficiente per consentire loro di utilizzarla correttamente.

Quando hai inizializzato il progetto di estensione, la CLI Firebase ha creato versioni stub della documentazione minima richiesta. Aggiorna questi file per riflettere con precisione l'estensione che hai creato.

extension.yaml

Hai già aggiornato questo file durante lo sviluppo dell'estensione, quindi non devi apportare ulteriori aggiornamenti al momento.

Tuttavia, non trascurare l'importanza della documentazione contenuta in questo file. Oltre alle informazioni di identificazione cruciali di un'estensione (nome, descrizione, autore, posizione del repository ufficiale), il file extension.yaml contiene la documentazione rivolta agli utenti per ogni risorsa e parametro configurabile dall'utente. Queste informazioni vengono mostrate agli utenti nella console Firebase, nell'hub delle estensioni e nella CLI Firebase.

PREINSTALL.md

In questo file, fornisci le informazioni di cui l'utente ha bisogno prima di installare la tua estensione: descrivi brevemente cosa fa l'estensione, spiega eventuali prerequisiti e fornisci all'utente informazioni sulle implicazioni di fatturazione dell'installazione dell'estensione. Se hai un sito web con informazioni aggiuntive, questo è anche un buon posto per inserire il link.

Il testo di questo file viene visualizzato all'utente nell'hub delle estensioni e dal comando firebase ext:info.

Ecco un esempio di file 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

Questo file contiene informazioni utili per gli utenti dopo l'installazione dell'estensione, ad esempio passaggi di configurazione successivi, un esempio di estensione in azione e così via.

I contenuti di POSTINSTALL.md vengono visualizzati nella console Firebase dopo la configurazione e l'installazione di un'estensione. Puoi fare riferimento ai parametri utente in questo file e verranno sostituiti dai valori configurati.

Ecco un esempio di file post-installazione per l'estensione tutorial:

### 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

Devi anche documentare le modifiche apportate tra le release di un'estensione nel file CHANGELOG.md.

Poiché l'estensione di esempio non è mai stata pubblicata prima, il log delle modifiche ha una sola voce:

## Version 0.0.1

Initial release of the _Convert messages to upper case_ extension.

README.md

La maggior parte delle estensioni fornisce anche un file Leggimi a vantaggio degli utenti che visitano il repository dell'estensione. Puoi scrivere questo file manualmente o generare un Leggimi utilizzando il comando.

Ai fini di questa guida, salta la scrittura di un file Leggimi.

Documentazione aggiuntiva

La documentazione descritta sopra è il set minimo di documentazione che devi fornire agli utenti. Molte estensioni richiedono una documentazione più dettagliata per consentire agli utenti di utilizzarle correttamente. In questo caso, devi scrivere ulteriore documentazione e ospitarla in un punto a cui puoi indirizzare gli utenti.

Ai fini di questa guida, salta la stesura di una documentazione più dettagliata.

11. Pubblica nell'hub delle estensioni

Ora che l'estensione è completa e documentata, puoi condividerla con il mondo nell'hub delle estensioni. Ma poiché si tratta solo di un tutorial, non farlo davvero. Inizia a scrivere la tua estensione utilizzando ciò che hai appreso qui e nel resto della documentazione per i publisher di Firebase Extensions, e esaminando il codice sorgente delle estensioni ufficiali scritte da Firebase.

Quando tutto è pronto per pubblicare il tuo lavoro su Extensions Hub, ecco come fare:

  1. Se stai pubblicando la tua prima estensione, registrati come publisher di estensioni. Quando ti registri come publisher di estensioni, crei un ID publisher che consente agli utenti di identificarti rapidamente come autore delle tue estensioni.
  2. Ospita il codice sorgente dell'estensione in una posizione verificabile pubblicamente. Quando il tuo codice è disponibile da una fonte verificabile, Firebase può pubblicare la tua estensione direttamente da questa posizione. In questo modo, ti assicuri di pubblicare la versione attualmente rilasciata della tua estensione e aiuti gli utenti consentendo loro di esaminare il codice che stanno installando nei loro progetti.

    Al momento, ciò significa rendere disponibile l'estensione in un repository GitHub pubblico.

  3. Carica l'estensione nell'hub delle estensioni utilizzando il comando firebase ext:dev:upload.

  4. Vai alla dashboard del publisher nella console Firebase, trova l'estensione che hai appena caricato e fai clic su "Pubblica nell'hub delle estensioni". In questo modo viene richiesta una revisione da parte del nostro personale, che può richiedere alcuni giorni. In caso di approvazione, l'estensione verrà pubblicata nell'hub delle estensioni. In caso di rifiuto, riceverai un messaggio che spiega il motivo. Potrai quindi risolvere i problemi segnalati e inviare di nuovo la richiesta per la revisione.