Iniziare a creare un'estensione

Questa pagina illustra 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 alla ricerca di messaggi e li converte in maiuscolo.

1. Configura il tuo ambiente e inizializza un progetto

Prima di poter iniziare a creare un'estensione, devi configurare un ambiente di compilazione 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'upgrade alla versione più recente dell'interfaccia a riga di comando di Firebase. Per eseguire l'installazione o l'aggiornamento 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 estensioni:

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

    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 anche utilizzare TypeScript quando sviluppi la tua estensione) e, quando ti viene chiesto di installare le dipendenze, rispondi "Sì". Accetta i valori predefiniti per tutte le altre opzioni. Questo comando configurerà uno scheletro di codebase per una nuova estensione, da cui puoi iniziare a sviluppare l'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 contenente 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 dimostrativo:

    firebase emulators:start --project=demo-test

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

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

    Il browser mostra il messaggio "Hello World da greet-the-world".

  4. Il codice sorgente di questa funzione si trova nella directory functions dell'estensione. Apri il codice sorgente 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 di Functions. 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 visiterai l'URL della funzione, vedrai il messaggio di benvenuto aggiornato.

3. Aggiungi informazioni di base a extension.yaml

Ora che hai configurato un ambiente di sviluppo e stai eseguendo l'emulatore delle estensioni, puoi iniziare a scrivere la tua estensione.

Come primo passaggio, 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 sono denominate con un prefisso che indica il prodotto Firebase principale su cui operano, seguito da una descrizione della funzionalità dell'estensione. Devi 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, cambia 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 per il momento non farne caso. Le aggiornerai nelle sezioni seguenti.

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 dell'estensione, ovvero monitorare il database in tempo reale per rilevare i messaggi e convertirli in maiuscolo.

  1. Apri il codice sorgente delle funzioni dell'estensione (nella directory functions dell'estensione) nell'editor o nell'IDE che preferisci. Sostituiscine i 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 tramite HTTP, che veniva eseguita quando si accedeva a un endpoint HTTP. La nuova funzione viene attivata dagli eventi del database in tempo reale: rileva i nuovi elementi in un determinato percorso e, quando ne viene rilevato uno, scrive la versione maiuscola del valore nel database.

    A proposito, questo nuovo file utilizza la sintassi del modulo 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 attivatore, devi aggiornare la configurazione dell'emulatore per eseguire l'emulatore RTDB insieme all'emulatore Cloud Functions:

    1. Se l'emulatore è ancora in esecuzione, interrompilo premendo Ctrl-C.

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

      firebase init emulators

      Quando richiesto, salta la configurazione di un progetto predefinito, poi seleziona gli emulatori di Functions e Database. Accetta le porte predefinite e consenti allo strumento di configurazione di scaricare i file.

    3. Riavvia l'emulatore:

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

    1. Apri l'interfaccia utente dell'emulatore di database utilizzando il link stampato dall'emulatore quando lo hai avviato.

    2. Modifica il nodo radice 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 figlio al messaggio 11 con il contenuto "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 elementi secondari al nodo messages ({"original":"any text"}). Ogni volta che aggiungi un nuovo record, l'estensione deve 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 che seguono, perfezionerai questa estensione con alcune funzionalità aggiuntive. Poi, prepara l'estensione da distribuire ad altri e, infine, scopri come pubblicarla nell'hub delle estensioni.

5. Dichiarare API e ruoli

Firebase concede a ogni istanza di un'estensione installata l'accesso limitato al progetto e ai relativi dati utilizzando un account di servizio per istanza. Ciascun account dispone dell'insieme minimo di autorizzazioni necessarie per operare. Per questo motivo, devi dichiarare esplicitamente i ruoli IAM richiesti dalla tua estensione. Quando gli utenti installano la tua estensione, Firebase crea un account di servizio a cui sono stati concessi questi ruoli 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 in altro modo. 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.

Analogamente, dichiari le API Google utilizzate da un'estensione nel campo apis. Quando gli utenti installano l'estensione, viene chiesto loro se vogliono attivare automaticamente queste API per il loro progetto. In genere, questo passaggio è necessario solo per le API di Google non Firebase e non è necessario per questa guida.

6. Definire i parametri configurabili dall'utente

La funzione creata negli ultimi due passaggi ha monitorato una posizione RTDB specifica per i messaggi in arrivo. A volte, è davvero opportuno monitorare una località specifica, ad esempio se l'estensione opera su una struttura di database che utilizzi esclusivamente per l'estensione. Tuttavia, nella maggior parte dei casi, ti consigliamo di far sì che questi valori siano configurabili dagli utenti che installano la tua 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 i 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
    

    Viene definito un nuovo parametro di stringa che verrà chiesto agli utenti di impostare quando installano l'estensione.

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

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

    Il token ${param:MESSAGE_PATH} è un riferimento al parametro 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 qualsiasi punto di extension.yaml (e in POSTINSTALL.md, più avanti).

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

    Nella funzione scritta nell'ultima sezione, hai impostato come hardcoded il percorso per controllare le modifiche. Modifica la definizione dell'attivatore 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 per motivi di documentazione: quando una funzione Cloud viene dispiata all'interno di un'estensione, utilizza la definizione dell'attivatore dal file extension.yaml e ignora il valore specificato nella definizione della funzione. Tuttavia, è buona norma documentare nel codice la provenienza di questo valore.

  4. Potrebbe essere deludente apportare una modifica al codice senza effetto sul runtime, ma la lezione importante da imparare è che è possibile accedere a qualsiasi parametro definito dall'utente nel codice della funzione e utilizzarlo come valore ordinario nella logica della funzione. Come riferimento a questa funzionalità, aggiungi la seguente dichiarazione 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 solito, agli utenti viene chiesto di fornire i valori per i parametri quando installano un'estensione. Tuttavia, quando utilizzi l'emulatore per i test e lo sviluppo, 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 riportato sopra è diverso da quello predefinito e da quello che hai definito in precedenza. Questo è solo per verificare, quando provi l'estensione aggiornata, che la definizione venga applicata.

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

    Modifica il nodo principale 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 evento per la logica definita dall'utente

In qualità di autore di un'estensione, hai già visto in che modo 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 la logica definita dall'utente.

Un'estensione può fornire hook sincroni, hook asincroni o entrambi. Gli hook sincronizzati offrono agli utenti un modo per eseguire attività che bloccano il completamento di una delle funzioni dell'estensione. Ciò può essere utile, ad esempio, per offrire agli utenti un modo per eseguire la preelaborazione personalizzata prima che un'estensione esegua la sua operazione.

In questa guida aggiungerai un hook asincrono all'estensione, che consentirà agli utenti di definire i propri passaggi di elaborazione da eseguire dopo che l'estensione avrà scritto il messaggio in maiuscolo nel database in tempo reale. Gli hook asincroni utilizzano Eventarc per attivare funzioni definite dall'utente. Le estensioni dichiarano i tipi di eventi che emettono e, quando gli utenti le installano, scelgono i tipi di eventi che li interessano. 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 Functions che restano in ascolto su 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 il 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 universalmente univoci. Per garantire l'unicità, assegna sempre ai tuoi eventi un nome utilizzando il seguente formato:<publisher-id>.<extension-id>.<version>.<description>. Non hai ancora un ID editore, quindi per il momento utilizza test-publisher.

  2. Alla fine della funzione makeuppercase, aggiungi del codice che pubblichi un evento del tipo 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 viene definita solo quando l'utente ha abilitato almeno un tipo di evento. Se EVENTARC_CHANNEL non è definito, il codice non tenta di pubblicare nessun evento.

    Puoi collegare 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. Queste informazioni possono essere usate da funzioni definite dall'utente che attivano l'evento.

  3. Di solito, le variabili di ambiente EVENTARC_CHANNEL e EXT_SELECTED_EVENTS sono definite in base alle opzioni selezionate dall'utente durante l'installazione. Per eseguire il 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 evento asincrono all'estensione.

Per provare questa nuova funzionalità che hai appena implementato, nei prossimi passaggi assumere 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 Cloud Functions e installa le dipendenze richieste. Questo progetto rappresenta un 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 ascolta l'evento complete pubblicato dall'estensione e, quando viene attivata, aggiunge tre punti esclamativi al messaggio in maiuscolo.

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

  4. Visita l'interfaccia utente dell'emulatore del database e modifica il nodo principale 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 essere attivate in sequenza, in modo che il campo upper riceva il valore RECIPE!!!.

8. Aggiungi 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 include 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 per gli eventi del ciclo di vita per eseguire il backfill del database dei messaggi esistente di un progetto con messaggi in maiuscolo quando un utente installa la tua estensione.

Le Estensioni Firebase utilizzano Cloud Tasks per eseguire i gestori degli eventi del ciclo di vita. Puoi definire 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 nell'istanza dell'estensione è in corso un'attività di elaborazione. Spetta alla funzione di gestore segnalare all'utente lo stato in corso e il completamento dell'attività.

Per aggiungere un gestore degli eventi del ciclo di vita che esegue il backfill dei messaggi esistenti:

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

    Nota che la funzione elabora solo alcuni record prima di essere aggiunta di nuovo alla coda delle attività. Si tratta di una strategia di uso comune per gestire le attività di elaborazione che non possono essere completate entro il periodo di tempo di una funzione Cloud. Poiché non puoi prevedere quanti messaggi un utente potrebbe già avere nel proprio 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: {}
    

    Dichiara poi la funzione come gestore per l'evento di ciclo di vita onInstall:

    lifecycleEvents:
      onInstall:
        function: backfilldata
        processingMessage: Uppercasing existing messages
    
  3. Sebbene il backfill dei messaggi esistenti sia un'opzione utile, l'estensione potrebbe funzionare anche senza. In situazioni come questa, devi rendere facoltativo l'esecuzione degli eventi di gestione 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
    

    All'inizio della funzione di backfill, controlla il valore del parametro DO_BACKFILL ed esci anticipatamente se non è impostato:

    functions/index.js

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

Con le modifiche precedenti, l'estensione ora convertirà i messaggi esistenti in lettere maiuscole quando è installata.

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 il processo di installazione, quindi per testare il gestore eventi onInstall devi installare l'estensione in un progetto reale. Per fortuna, con l'aggiunta di questa funzionalità di backfill automatico, l'estensione del tutorial ora è completa di codice.

9. Esegui il deployment in un progetto Firebase reale

Sebbene l'emulatore delle estensioni sia un ottimo strumento per eseguire rapidamente l'iterazione di un'estensione durante lo sviluppo, prima o poi vorrai provare in un progetto reale.

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

  1. Nella Console Firebase, aggiungi un nuovo progetto.
  2. Esegui l'upgrade del progetto al piano Blaze con pagamento a consumo. Cloud Functions per Firebase richiede che il tuo progetto abbia un account di fatturazione, quindi devi disporre di un account di fatturazione anche per installare un'estensione.
  3. Nel nuovo progetto, abilita Realtime Database.
  4. Poiché vuoi testare la capacità dell'estensione di eseguire il backfill dei dati esistenti al momento dell'installazione, importa alcuni dati di esempio nell'istanza del database in tempo reale:
    1. Scarica alcuni dati RTDB iniziali.
    2. Nella pagina Database in tempo reale 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 dall'origine locale nel nuovo progetto:

  1. Crea una nuova directory per il 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 appena creato.

  3. Installa l'estensione nel tuo progetto Firebase locale:

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

    Qui puoi vedere com'è l'esperienza utente quando installi un'estensione utilizzando lo strumento dell'interfaccia a riga di comando di Firebase. 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, Firebase CLI salverà la configurazione nella directory extensions e registrerà la posizione della fonte dell'estensione nel file firebase.json. Insieme, questi due record sono chiamati manifest delle estensioni. Gli utenti possono utilizzare il file manifest per salvare la configurazione delle estensioni e implementarla in diversi progetti.

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

    firebase deploy --only extensions

Se tutto funziona correttamente, l'interfaccia a riga di comando di Firebase dovrebbe caricare l'estensione nel 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 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 documentazione sufficiente per consentirne l'utilizzo.

Quando hai inizializzato il progetto di estensione, l'interfaccia a riga di comando di Firebase ha creato versioni stub della documentazione minima richiesta. Aggiorna questi file in modo che riflettano con precisione l'estensione che hai creato.

estensione.yaml

Stai già aggiornando questo file perché hai sviluppato l'estensione, quindi non è necessario apportare altri aggiornamenti adesso.

Tuttavia, non trascurare l'importanza della documentazione contenuta in questo file. Oltre alle informazioni di identificazione cruciali di un'estensione (nome, descrizione, autore, posizione ufficiale del repository), il file extension.yaml contiene 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 di Firebase.

PREINSTALL.md

In questo file, fornisci le informazioni di cui l'utente ha bisogno prima di installare la tua estensione: descrivi brevemente la funzionalità dell'estensione, spiega gli 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 collegarlo.

Il testo di questo file viene mostrato all'utente in Hub 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 che hanno installato correttamente la tua estensione, ad esempio i passaggi di configurazione successivi, un esempio dell'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. In questo file puoi fare riferimento ai parametri utente, che verranno sostituiti dai valori configurati.

Ecco un esempio di file post-installazione per l'estensione del 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 contiene 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 readme a vantaggio degli utenti che visitano il repository dell'estensione. Puoi scrivere questo file a mano o generare un comando di lettura utilizzando il comando.

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

Documentazione aggiuntiva

La documentazione discussa sopra è l'insieme 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 documentazione aggiuntiva e ospitarla in un luogo a cui indirizzare gli utenti.

Ai fini di questa guida, salta la scrittura di una documentazione più completa.

11. Pubblicare su Extensions Hub

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

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

  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 codice è disponibile da un'origine verificabile, Firebase può pubblicare l'estensione direttamente da questa posizione. In questo modo puoi assicurarti di pubblicare la versione attualmente rilasciata dell'estensione e consentire agli utenti di esaminare il codice che stanno installando nei loro progetti.

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

  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 appena caricata e fai clic su "Pubblica nell'hub delle estensioni". Questa operazione richiede una revisione da parte del nostro personale addetto alle revisioni, che può richiedere alcuni giorni. Se approvata, 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 nuovamente la richiesta di revisione.