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 l'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 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 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à una base di codice di base per una nuova estensione, da cui potrai iniziare a svilupparla.

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 quando l'hai avviato.

    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 di 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. Scrivi una funzione Cloud Functions e dichiarala 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. Sostituisci i contenuti con quanto segue:

    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 da eventi del database in tempo reale: cerca nuovi elementi in un determinato percorso e, quando ne viene rilevato uno, scrive 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 nell'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 ti viene chiesto, salta la configurazione di un progetto predefinito, quindi seleziona gli emulatori Functions e Database. Accetta le porte predefinite e consenti allo strumento di configurazione di scaricare i file richiesti.

    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 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 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. Dopodiché, potrai distribuire l'estensione ad altri utenti e infine scoprire 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. Ogni account dispone dell'insieme minimo di autorizzazioni necessarie per il funzionamento. 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, monitorare una località specifica è proprio ciò che vuoi, ad esempio quando la tua estensione opera su una struttura di database che utilizzi esclusivamente per la tua 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 ovunque in extension.yaml (e in POSTINSTALL.md; di seguito sono riportate ulteriori informazioni).

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

    Nella funzione che hai scritto nell'ultima sezione, hai inserito in modo hardcoded il percorso da monitorare per rilevare 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 dispiattata 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. Potresti trovare deludente apportare una modifica al codice che non ha alcun effetto in fase di runtime, ma la lezione importante da ricordare è che puoi accedere a qualsiasi parametro definito dall'utente nel codice della funzione e utilizzarlo come un valore normale 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 torna all'interfaccia utente dell'emulatore del 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 estensioni, 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 la 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. 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 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 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 attivato almeno un tipo di evento. Se EVENTARC_CHANNEL non è definito, il codice non tenta di pubblicare eventi.

    Puoi allegare informazioni aggiuntive a un evento Eventarc. Nell'esempio riportato sopra, 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 attivano l'evento possono utilizzare queste informazioni.

  3. In genere, 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 evento asincrono alla tua 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 la pubblicazione di un evento complete da parte dell'estensione e, quando viene attivata, aggiunge tre punti esclamativi al messaggio appena impostato 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. 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 dispone di una funzionalità chiamata hook per eventi di 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 delle 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.");
      }
    });
    

    Tieni presente che la funzione elabora solo alcuni record prima di aggiungersi nuovamente 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
    

    Poi, 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 di estensioni sia un ottimo strumento per eseguire rapidamente l'iterazione di un'estensione durante lo sviluppo, a un certo punto ti consigliamo di provarlo 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 tuo progetto al piano Blaze con pagamento a consumo. Cloud Functions per Firebase richiede che il 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 di base del DBRT.
    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 come funziona l'esperienza utente durante l'installazione di 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, 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 manifest per salvare la configurazione delle estensioni e implementarla in progetti diversi.

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

    firebase deploy --only extensions
    

Se tutto va bene, 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 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 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.

extension.yaml

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

Tuttavia, non trascurare l'importanza della documentazione contenuta in questo file. Oltre alle informazioni di identificazione fondamentali 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 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. 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 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 per gli utenti che visitano il repository dell'estensione. Puoi scrivere questo file manualmente o generare un file readme 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 consentirne agli utenti un utilizzo corretto. In questo caso, devi scrivere ulteriore documentazione e ospitarla in un luogo a cui puoi 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 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 codice è disponibile da una fonte verificabile, Firebase può pubblicare l'estensione direttamente da questa posizione. In questo modo, puoi assicurarti di pubblicare la versione attualmente rilasciata dell'estensione e aiutare 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 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.