Déclencheurs Firestore

Vous pouvez configurer vos fonctions Cloud Run afin qu'elles soient déclenchées par des événements dans une base de données Firestore. Une fois déclenchée, votre fonction peut lire et mettre à jour une base de données Firestore en réponse à ces événements via les API et bibliothèques clientes Firestore.

Dans un cycle de vie typique, une fonction Firestore effectue les opérations suivantes :

  1. Attend les modifications apportées à un document donné.

  2. Se déclenche lorsqu'un événement se produit et exécute ses tâches.

  3. Reçoit un objet de données contenant un instantané du document affecté. Pour les événements write ou update, l'objet de données contient des instantanés représentant l'état du document avant et après l'événement déclencheur.

Types d'événement

Firestore accepte les événements create, update, delete et write. L'événement write englobe toutes les modifications apportées à un document.

Type d'événement Déclencheur
google.cloud.firestore.document.v1.created (par défaut) Déclenché lorsqu'un document est écrit pour la première fois.
google.cloud.firestore.document.v1.updated Déclenché lorsqu'un document existe déjà et qu'une valeur y a été modifiée.
google.cloud.firestore.document.v1.deleted Déclenché lorsqu'un document contenant des données est supprimé.
google.cloud.firestore.document.v1.written Déclenché lorsqu'un document est créé, mis à jour ou supprimé.

Les caractères génériques sont écrits dans les déclencheurs à l'aide d'accolades, comme suit : "projects/YOUR_PROJECT_ID/databases/(default)/documents/collection/{document_wildcard}"

Spécifier le chemin d'accès aux documents

Pour déclencher votre fonction, spécifiez un chemin d'accès de document à écouter. Le chemin d'accès aux documents doit se trouver dans le même projet Google Cloud que la fonction.

Voici quelques exemples de chemins d'accès aux documents valides :

  • users/marie : déclencheur valide. Surveille un seul document, /users/marie.

  • users/{username} : déclencheur valide. Surveille tous les documents d'utilisateur. Les caractères génériques permettent de surveiller tous les documents de la collection.

  • users/{username}/addresses : déclencheur non valide. Renvoie à la sous-collection addresses, et non à un document.

  • users/{username}/addresses/home : déclencheur valide. Surveille le document d'adresses personnelles de tous les utilisateurs.

  • users/{username}/addresses/{addressId} : déclencheur valide. Surveille tous les documents d'adresses.

  • users/{user=**} : déclencheur valide. Surveille tous les documents d'utilisateur et tous les documents des sous-collections associées à chaque document d'utilisateur, telles que /users/userID/address/home ou /users/userID/phone/work.

Caractères génériques et paramètres

Si vous ne connaissez pas le nom du document spécifique que vous souhaitez surveiller, utilisez un caractère générique ({wildcard}) à la place de l'ID du document :

  • users/{username} : écoute les modifications apportées à tous les documents d'utilisateur.

Dans cet exemple, lorsqu'un champ de n'importe quel document de la collection users est modifié, il correspond au caractère générique {username}.

Si un document de la collection users comporte des sous-collections et qu'un champ de l'une d'entre elles est modifié, le caractère générique {username} n'est pas déclenché. Si votre objectif est de répondre aux événements dans les sous-collections, utilisez le caractère générique multisegment {username=**}.

Les correspondances de caractères génériques sont extraites du chemin du document. Vous pouvez définir autant de caractères génériques que vous le souhaitez pour remplacer les ID explicites de collection ou de document. Vous ne pouvez utiliser qu'un seul caractère générique multisegment, tel que {username=**}.

Structures d'événements

Ce déclencheur appelle votre fonction avec un événement semblable à celui-ci :

{
    "oldValue": { // Update and Delete operations only
        A Document object containing a pre-operation document snapshot
    },
    "updateMask": { // Update operations only
        A DocumentMask object that lists changed fields.
    },
    "value": {
        // A Document object containing a post-operation document snapshot
    }
}

Chaque objet Document contient un ou plusieurs objets Value. Consultez la documentation de référence de l'objet Value pour obtenir plus d'informations sur les types de valeurs. C'est particulièrement utile si vous utilisez un langage saisi, tel que Go, pour écrire vos fonctions.

Exemples

Les exemples suivants montrent comment écrire des fonctions qui répondent à un déclencheur Firestore.

Avant de commencer

  1. Sign in to your Google Cloud account. If you're new to Google Cloud, create an account to evaluate how our products perform in real-world scenarios. New customers also get $300 in free credits to run, test, and deploy workloads.
  2. In the Google Cloud console, on the project selector page, select or create a Google Cloud project.

    Go to project selector

  3. Make sure that billing is enabled for your Google Cloud project.

  4. Enable the Cloud Functions, Cloud Build, Artifact Registry, Eventarc, Logging, Pub/Sub, and Cloud Run APIs.

    Enable the APIs

  5. Install the Google Cloud CLI.
  6. To initialize the gcloud CLI, run the following command:

    gcloud init
  7. In the Google Cloud console, on the project selector page, select or create a Google Cloud project.

    Go to project selector

  8. Make sure that billing is enabled for your Google Cloud project.

  9. Enable the Cloud Functions, Cloud Build, Artifact Registry, Eventarc, Logging, Pub/Sub, and Cloud Run APIs.

    Enable the APIs

  10. Install the Google Cloud CLI.
  11. To initialize the gcloud CLI, run the following command:

    gcloud init
  12. Si la gcloud CLI est déjà installée, mettez-le à jour en exécutant la commande suivante :

    gcloud components update
  13. Préparez votre environnement de développement.

Configurer votre base de données Cloud Firestore

Vous devez disposer d'une base de données Firestore pour tester les exemples de ce document. Celle-ci doit être en place avant de déployer vos fonctions. Si vous ne disposez pas encore d'une base de données Firestore, créez-en une comme suit :

  1. Accédez à la page Données Firestore.

  2. Cliquez sur Sélectionner le mode natif.

  3. Choisissez la région (emplacement) où doit être située votre base de données. Ce choix est définitif.

  4. Cliquez sur Créer une base de données.

Le modèle de données Firestore est constitué de collections contenant des documents. Chaque document contient un ensemble de paires clé/valeur.

Les fonctions que vous créez dans ce tutoriel sont déclenchées lorsque vous apportez des modifications à un document au sein d'une collection spécifiée.

Exemple 1 : Fonction Hello Firestore

L'exemple de fonction Cloud Run suivant imprime les champs d'un événement déclencheur Firestore :

Node.js

Utilisez protobufjs pour décoder les données d'événement. Incluez la propriété google.events.cloud.firestore.v1data.proto dans votre source.

/**
 * Cloud Event Function triggered by a change to a Firestore document.
 */
const functions = require('@google-cloud/functions-framework');
const protobuf = require('protobufjs');

functions.cloudEvent('helloFirestore', async cloudEvent => {
  console.log(`Function triggered by event on: ${cloudEvent.source}`);
  console.log(`Event type: ${cloudEvent.type}`);

  console.log('Loading protos...');
  const root = await protobuf.load('data.proto');
  const DocumentEventData = root.lookupType(
    'google.events.cloud.firestore.v1.DocumentEventData'
  );

  console.log('Decoding data...');
  const firestoreReceived = DocumentEventData.decode(cloudEvent.data);

  console.log('\nOld value:');
  console.log(JSON.stringify(firestoreReceived.oldValue, null, 2));

  console.log('\nNew value:');
  console.log(JSON.stringify(firestoreReceived.value, null, 2));
});

Python

from cloudevents.http import CloudEvent
import functions_framework
from google.events.cloud import firestore


@functions_framework.cloud_event
def hello_firestore(cloud_event: CloudEvent) -> None:
    """Triggers by a change to a Firestore document.

    Args:
        cloud_event: cloud event with information on the firestore event trigger
    """
    firestore_payload = firestore.DocumentEventData()
    firestore_payload._pb.ParseFromString(cloud_event.data)

    print(f"Function triggered by change to: {cloud_event['source']}")

    print("\nOld value:")
    print(firestore_payload.old_value)

    print("\nNew value:")
    print(firestore_payload.value)

Go


// Package hellofirestore contains a Cloud Event Function triggered by a Cloud Firestore event.
package hellofirestore

import (
	"context"
	"fmt"

	"github.com/GoogleCloudPlatform/functions-framework-go/functions"
	"github.com/cloudevents/sdk-go/v2/event"
	"github.com/googleapis/google-cloudevents-go/cloud/firestoredata"
	"google.golang.org/protobuf/proto"
)

func init() {
	functions.CloudEvent("helloFirestore", HelloFirestore)
}

// HelloFirestore is triggered by a change to a Firestore document.
func HelloFirestore(ctx context.Context, event event.Event) error {
	var data firestoredata.DocumentEventData

	// If you omit `DiscardUnknown`, protojson.Unmarshal returns an error
	// when encountering a new or unknown field.
	options := proto.UnmarshalOptions{
		DiscardUnknown: true,
	}
	err := options.Unmarshal(event.Data(), &data)

	if err != nil {
		return fmt.Errorf("proto.Unmarshal: %w", err)
	}

	fmt.Printf("Function triggered by change to: %v\n", event.Source())
	fmt.Printf("Old value: %+v\n", data.GetOldValue())
	fmt.Printf("New value: %+v\n", data.GetValue())
	return nil
}

Java

import com.google.cloud.functions.CloudEventsFunction;
import com.google.events.cloud.firestore.v1.DocumentEventData;
import com.google.protobuf.InvalidProtocolBufferException;
import io.cloudevents.CloudEvent;
import java.util.logging.Logger;

public class FirebaseFirestore implements CloudEventsFunction {
  private static final Logger logger = Logger.getLogger(FirebaseFirestore.class.getName());

  @Override
  public void accept(CloudEvent event) throws InvalidProtocolBufferException {
    DocumentEventData firestoreEventData = DocumentEventData
        .parseFrom(event.getData().toBytes());

    logger.info("Function triggered by event on: " + event.getSource());
    logger.info("Event type: " + event.getType());

    logger.info("Old value:");
    logger.info(firestoreEventData.getOldValue().toString());

    logger.info("New value:");
    logger.info(firestoreEventData.getValue().toString());
  }
}

C#

using CloudNative.CloudEvents;
using Google.Cloud.Functions.Framework;
using Google.Events.Protobuf.Cloud.Firestore.V1;
using Microsoft.Extensions.Logging;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;

namespace FirebaseFirestore;

public class Function : ICloudEventFunction<DocumentEventData>
{
    private readonly ILogger _logger;

    public Function(ILogger<Function> logger) =>
        _logger = logger;

    public Task HandleAsync(CloudEvent cloudEvent, DocumentEventData data, CancellationToken cancellationToken)
    {
        _logger.LogInformation("Function triggered by event on {subject}", cloudEvent.Subject);
        _logger.LogInformation("Event type: {type}", cloudEvent.Type);
        MaybeLogDocument("Old value", data.OldValue);
        MaybeLogDocument("New value", data.Value);

        // In this example, we don't need to perform any asynchronous operations, so the
        // method doesn't need to be declared async.
        return Task.CompletedTask;
    }

    /// <summary>
    /// Logs the names and values of the fields in a document in a very simplistic way.
    /// </summary>
    private void MaybeLogDocument(string message, Document document)
    {
        if (document is null)
        {
            return;
        }

        // ConvertFields converts the Firestore representation into a .NET-friendly
        // representation.
        IReadOnlyDictionary<string, object> fields = document.ConvertFields();
        var fieldNamesAndTypes = fields
            .OrderBy(pair => pair.Key)
            .Select(pair => $"{pair.Key}: {pair.Value}");
        _logger.LogInformation(message + ": {fields}", string.Join(", ", fieldNamesAndTypes));
    }
}

Déployer la fonction Hello Firestore

  1. Si ce n'est pas déjà fait, configurez votre base de données Firestore.

  2. Pour déployer la fonction Hello Firestore avec un déclencheur Firestore, exécutez la commande suivante dans le répertoire contenant l'exemple de code (ou, dans le cas de Java, le fichier pom.xml) :

    gcloud functions deploy FUNCTION_NAME \
    --gen2 \
    --runtime=RUNTIME \
    --region=REGION \
    --trigger-location=TRIGGER REGION \
    --source=. \
    --entry-point=ENTRY_POINT \
    --trigger-event-filters=type=google.cloud.firestore.document.v1.written \
    --trigger-event-filters=database='(default)' \
    --trigger-event-filters-path-pattern=document='users/{username}'

    Remplacez les éléments suivants :

    • FUNCTION_NAME : nom de votre fonction déployée
    • RUNTIME : environnement d'exécution de langage utilisé par votre fonction
    • REGION : région dans laquelle déployer votre fonction
    • TRIGGER_REGION : emplacement du déclencheur, qui doit correspondre à la région de la base de données Firestore.
    • ENTRY_POINT : point d'entrée de votre fonction dans votre code source. Il s'agit du code qui est exécuté lors de l'exécution de votre fonction.

    Laissez les autres champs inchangés :

    • --trigger-event-filters=type=google.cloud.firestore.document.v1.written spécifie que la fonction est déclenchée lors de la création, de la mise à jour ou de la suppression d'un document, selon le type d'événement google.cloud.firestore.document.v1.written.
    • --trigger-event-filters=database='(default)' spécifie la base de données Firebase. Pour spécifier le nom de la base de données par défaut, utilisez (default).
    • --trigger-event-filters-path-pattern=document='users/{username}' fournit le format de chemin d'accès des documents à surveiller pour y détecter des modifications pertinentes. Ce format de chemin d'accès indique que tous les documents de la collection users doivent être surveillés. Pour en savoir plus, consultez la page Comprendre les formats de chemin d'accès.

Tester la fonction Hello Firestore

Pour tester la fonction Hello Firestore, configurez une collection appelée users dans votre base de données Firestore :

  1. Sur la page des données Firestore, cliquez sur Commencer une collection.

  2. Spécifiez users comme ID de collection.

  3. Pour commencer à ajouter le premier document de la collection, sous Ajouter le premier document, acceptez l'ID de document généré automatiquement.

  4. Ajoutez au moins un champ pour le document, en spécifiant un nom et une valeur. Dans cet exemple, nous ajoutons un champ nommé "username" dont la valeur est "rowan".

    Capture d&#39;écran illustrant la création d&#39;une collection Firestore

  5. Lorsque vous avez terminé, cliquez sur Save (Enregistrer).

    Cette action crée un document, ce qui va donc déclencher votre fonction.

  6. Pour vérifier que votre fonction a été déclenchée, cliquez sur le nom de la fonction associée dans la page de présentation de Cloud Run Functions dans la console Google Cloud, pour afficher la page Détails de la fonction.

  7. Accédez à l'onglet Journaux et recherchez cette chaîne :

Function triggered by change to: //firestore.googleapis.com/projects/your-project-id/databases/(default)'

Exemple 2 : Fonction de conversion en majuscules

Cet exemple récupère la valeur ajoutée par l'utilisateur, convertit la chaîne à cet emplacement en majuscules, et remplace la valeur par la chaîne en majuscules :

Node.js

Utilisez protobufjs pour décoder les données d'événement. Incluez la propriété google.events.cloud.firestore.v1data.proto dans votre source.

const functions = require('@google-cloud/functions-framework');
const Firestore = require('@google-cloud/firestore');
const protobuf = require('protobufjs');

const firestore = new Firestore({
  projectId: process.env.GOOGLE_CLOUD_PROJECT,
});

// Converts strings added to /messages/{pushId}/original to uppercase
functions.cloudEvent('makeUpperCase', async cloudEvent => {
  console.log('Loading protos...');
  const root = await protobuf.load('data.proto');
  const DocumentEventData = root.lookupType(
    'google.events.cloud.firestore.v1.DocumentEventData'
  );

  console.log('Decoding data...');
  const firestoreReceived = DocumentEventData.decode(cloudEvent.data);

  const resource = firestoreReceived.value.name;
  const affectedDoc = firestore.doc(resource.split('/documents/')[1]);

  const curValue = firestoreReceived.value.fields.original.stringValue;
  const newValue = curValue.toUpperCase();

  if (curValue === newValue) {
    // Value is already upper-case
    // Don't perform a(nother) write to avoid infinite loops
    console.log('Value is already upper-case.');
    return;
  }

  console.log(`Replacing value: ${curValue} --> ${newValue}`);
  affectedDoc.set({
    original: newValue,
  });
});

Python

from cloudevents.http import CloudEvent
import functions_framework
from google.cloud import firestore
from google.events.cloud import firestore as firestoredata

client = firestore.Client()


# Converts strings added to /messages/{pushId}/original to uppercase
@functions_framework.cloud_event
def make_upper_case(cloud_event: CloudEvent) -> None:
    firestore_payload = firestoredata.DocumentEventData()
    firestore_payload._pb.ParseFromString(cloud_event.data)

    path_parts = firestore_payload.value.name.split("/")
    separator_idx = path_parts.index("documents")
    collection_path = path_parts[separator_idx + 1]
    document_path = "/".join(path_parts[(separator_idx + 2) :])

    print(f"Collection path: {collection_path}")
    print(f"Document path: {document_path}")

    affected_doc = client.collection(collection_path).document(document_path)

    cur_value = firestore_payload.value.fields["original"].string_value
    new_value = cur_value.upper()

    if cur_value != new_value:
        print(f"Replacing value: {cur_value} --> {new_value}")
        affected_doc.set({"original": new_value})
    else:
        # Value is already upper-case
        # Don't perform a second write (which can trigger an infinite loop)
        print("Value is already upper-case.")

Go


// Package upper contains a Firestore Cloud Function.
package upper

import (
	"context"
	"errors"
	"fmt"
	"log"
	"os"
	"strings"

	"cloud.google.com/go/firestore"
	firebase "firebase.google.com/go/v4"
	"github.com/GoogleCloudPlatform/functions-framework-go/functions"
	"github.com/cloudevents/sdk-go/v2/event"
	"github.com/googleapis/google-cloudevents-go/cloud/firestoredata"
	"google.golang.org/protobuf/proto"
)

// set the GOOGLE_CLOUD_PROJECT environment variable when deploying.
var projectID = os.Getenv("GOOGLE_CLOUD_PROJECT")

// client is a Firestore client, reused between function invocations.
var client *firestore.Client

func init() {
	// Use the application default credentials.
	conf := &firebase.Config{ProjectID: projectID}

	// Use context.Background() because the app/client should persist across
	// invocations.
	ctx := context.Background()

	app, err := firebase.NewApp(ctx, conf)
	if err != nil {
		log.Fatalf("firebase.NewApp: %v", err)
	}

	client, err = app.Firestore(ctx)
	if err != nil {
		log.Fatalf("app.Firestore: %v", err)
	}

	// Register cloud event function
	functions.CloudEvent("MakeUpperCase", MakeUpperCase)
}

// MakeUpperCase is triggered by a change to a Firestore document. It updates
// the `original` value of the document to upper case.
func MakeUpperCase(ctx context.Context, e event.Event) error {
	var data firestoredata.DocumentEventData

	// If you omit `DiscardUnknown`, protojson.Unmarshal returns an error
	// when encountering a new or unknown field.
	options := proto.UnmarshalOptions{
		DiscardUnknown: true,
	}
	err := options.Unmarshal(e.Data(), &data)

	if err != nil {
		return fmt.Errorf("proto.Unmarshal: %w", err)
	}

	if data.GetValue() == nil {
		return errors.New("Invalid message: 'Value' not present")
	}

	fullPath := strings.Split(data.GetValue().GetName(), "/documents/")[1]
	pathParts := strings.Split(fullPath, "/")
	collection := pathParts[0]
	doc := strings.Join(pathParts[1:], "/")

	var originalStringValue string
	if v, ok := data.GetValue().GetFields()["original"]; ok {
		originalStringValue = v.GetStringValue()
	} else {
		return errors.New("Document did not contain field \"original\"")
	}

	newValue := strings.ToUpper(originalStringValue)
	if originalStringValue == newValue {
		log.Printf("%q is already upper case: skipping", originalStringValue)
		return nil
	}
	log.Printf("Replacing value: %q -> %q", originalStringValue, newValue)

	newDocumentEntry := map[string]string{"original": newValue}
	_, err = client.Collection(collection).Doc(doc).Set(ctx, newDocumentEntry)
	if err != nil {
		return fmt.Errorf("Set: %w", err)
	}
	return nil
}

Java

import com.google.cloud.firestore.Firestore;
import com.google.cloud.firestore.FirestoreOptions;
import com.google.cloud.firestore.SetOptions;
import com.google.cloud.functions.CloudEventsFunction;
import com.google.events.cloud.firestore.v1.DocumentEventData;
import com.google.events.cloud.firestore.v1.Value;
import com.google.protobuf.InvalidProtocolBufferException;
import io.cloudevents.CloudEvent;
import java.util.Map;
import java.util.concurrent.ExecutionException;
import java.util.logging.Logger;

public class FirebaseFirestoreReactive implements CloudEventsFunction {
  private static final Logger logger = Logger.getLogger(FirebaseFirestoreReactive.class.getName());
  private final Firestore firestore;

  private static final String FIELD_KEY = "original";
  private static final String APPLICATION_PROTOBUF = "application/protobuf";

  public FirebaseFirestoreReactive() {
    this(FirestoreOptions.getDefaultInstance().getService());
  }

  public FirebaseFirestoreReactive(Firestore firestore) {
    this.firestore = firestore;
  }

  @Override
  public void accept(CloudEvent event)
      throws InvalidProtocolBufferException, InterruptedException, ExecutionException {
    if (event.getData() == null) {
      logger.warning("No data found in event!");
      return;
    }

    if (!event.getDataContentType().equals(APPLICATION_PROTOBUF)) {
      logger.warning(String.format("Found unexpected content type %s, expected %s",
          event.getDataContentType(),
          APPLICATION_PROTOBUF));
      return;
    }

    DocumentEventData firestoreEventData = DocumentEventData
        .parseFrom(event.getData().toBytes());

    // Get the fields from the post-operation document snapshot
    // https://firebase.google.com/docs/firestore/reference/rest/v1/projects.databases.documents#Document
    Map<String, Value> fields = firestoreEventData.getValue().getFieldsMap();
    if (!fields.containsKey(FIELD_KEY)) {
      logger.warning("Document does not contain original field");
      return;
    }
    String currValue = fields.get(FIELD_KEY).getStringValue();
    String newValue = currValue.toUpperCase();

    if (currValue.equals(newValue)) {
      logger.info("Value is already upper-case");
      return;
    }

    // Retrieve the document name from the resource path:
    // projects/{project_id}/databases/{database_id}/documents/{document_path}
    String affectedDoc = firestoreEventData.getValue()
        .getName()
        .split("/documents/")[1]
        .replace("\"", "");

    logger.info(String.format("Replacing values: %s --> %s", currValue, newValue));

    // Wait for the async call to complete
    this.firestore
        .document(affectedDoc)
        .set(Map.of(FIELD_KEY, newValue), SetOptions.merge())
        .get();
  }
}

C#

using CloudNative.CloudEvents;
using Google.Cloud.Firestore;
using Google.Cloud.Functions.Framework;
using Google.Cloud.Functions.Hosting;
using Google.Events.Protobuf.Cloud.Firestore.V1;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;

namespace FirestoreReactive;

public class Startup : FunctionsStartup
{
    public override void ConfigureServices(WebHostBuilderContext context, IServiceCollection services) =>
        services.AddSingleton(FirestoreDb.Create());
}

// Register the startup class to provide the Firestore dependency.
[FunctionsStartup(typeof(Startup))]
public class Function : ICloudEventFunction<DocumentEventData>
{
    private readonly ILogger _logger;
    private readonly FirestoreDb _firestoreDb;

    public Function(ILogger<Function> logger, FirestoreDb firestoreDb) =>
        (_logger, _firestoreDb) = (logger, firestoreDb);

    public async Task HandleAsync(CloudEvent cloudEvent, DocumentEventData data, CancellationToken cancellationToken)
    {
        // Get the recently-written value. This expression will result in a null value
        // if any of the following is true:
        // - The event doesn't contain a "new" document
        // - The value doesn't contain a field called "original"
        // - The "original" field isn't a string
        string currentValue = data.Value?.ConvertFields().GetValueOrDefault("original") as string;
        if (currentValue is null)
        {
            _logger.LogWarning($"Event did not contain a suitable document");
            return;
        }

        string newValue = currentValue.ToUpperInvariant();
        if (newValue == currentValue)
        {
            _logger.LogInformation("Value is already upper-cased; no replacement necessary");
            return;
        }

        // The CloudEvent subject is "documents/x/y/...".
        // The Firestore SDK FirestoreDb.Document method expects a reference relative to
        // "documents" (so just the "x/y/..." part). This may be simplified over time.
        if (cloudEvent.Subject is null || !cloudEvent.Subject.StartsWith("documents/"))
        {
            _logger.LogWarning("CloudEvent subject is not a document reference.");
            return;
        }
        string documentPath = cloudEvent.Subject.Substring("documents/".Length);

        _logger.LogInformation("Replacing '{current}' with '{new}' in '{path}'", currentValue, newValue, documentPath);
        await _firestoreDb.Document(documentPath).UpdateAsync("original", newValue, cancellationToken: cancellationToken);
    }
}

Déployer la fonction de conversion en majuscules

  1. Si ce n'est pas déjà fait, configurez votre base de données Firestore.

  2. Utilisez la commande suivante pour déployer une fonction qui est déclenchée par des événements d'écriture sur le document companies/{CompanyId} :

    gcloud functions deploy FUNCTION_NAME \
    --gen2 \
    --runtime=RUNTIME \
    --trigger-location=TRIGGER REGION \
    --region=REGION \
    --source=. \
    --entry-point=ENTRY_POINT \
    --set-env-vars GOOGLE_CLOUD_PROJECT=PROJECT_ID \
    --trigger-event-filters=type=google.cloud.firestore.document.v1.written \
    --trigger-event-filters=database='(default)' \
    --trigger-event-filters-path-pattern=document='messages/{pushId}'

    Remplacez les éléments suivants :

    • FUNCTION_NAME : nom de votre fonction déployée
    • RUNTIME : environnement d'exécution de langage utilisé par votre fonction
    • REGION : région dans laquelle déployer votre fonction
    • TRIGGER_REGION : emplacement du déclencheur, qui doit correspondre à la région de la base de données Firestore.
    • ENTRY_POINT : point d'entrée de votre fonction dans votre code source. Il s'agit du code qui est exécuté lors de l'exécution de votre fonction.
    • PROJECT_ID : identifiant unique du projet.

    Laissez les autres champs inchangés :

    • --trigger-event-filters=type=google.cloud.firestore.document.v1.written spécifie que la fonction est déclenchée lors de la création, de la mise à jour ou de la suppression d'un document, selon le type d'événement google.cloud.firestore.document.v1.written.
    • --trigger-event-filters=database='(default)' spécifie la base de données Firestore. Pour spécifier le nom de la base de données par défaut, utilisez (default).
    • --trigger-event-filters-path-pattern=document='messages/{pushId}' fournit le format de chemin d'accès des documents à surveiller pour y détecter des modifications pertinentes. Ce format de chemin d'accès indique que tous les documents de la collection messages doivent être surveillés. Pour en savoir plus, consultez la page Comprendre les formats de chemin d'accès.

Tester la fonction de conversion en majuscules

Pour tester la fonction de conversion en majuscules que vous venez de déployer, configurez une collection appelée messages dans votre base de données Firestore :

  1. Accédez à la page des données Firestore.

  2. Cliquez sur Commencer une collection.

  3. Spécifiez messages comme ID de collection.

  4. Pour commencer à ajouter le premier document de la collection, sous Ajouter le premier document, acceptez l'ID de document généré automatiquement.

  5. Pour déclencher votre fonction déployée, ajoutez un document dont le nom de champ est "original", la valeur de ce champ étant un mot minuscule, par exemple :

    Capture d&#39;écran illustrant la création d&#39;une collection Firestore

  6. Lorsque vous enregistrez le document, le mot en minuscules du champ de valeur est converti en majuscules.

    Si vous modifiez ensuite la valeur du champ pour y faire figurer des lettres minuscules, la fonction va à nouveau être déclenchée, ce qui va convertir toutes les lettres minuscules en majuscules.

Limites

  • L'ordre n'est pas garanti. Les modifications rapides peuvent déclencher des appels de fonctions dans un ordre inattendu.
  • Bien que les événements soient diffusés une fois au moins, un même événement peut produire plusieurs appels de fonction. Évitez de dépendre de procédés dits "exactement une fois" et écrivez des fonctions idempotentes.
  • Un déclencheur est associé à une seule base de données. Vous ne pouvez pas créer un déclencheur qui correspond à plusieurs bases de données.
  • La suppression d'une base de données ne supprime pas automatiquement les déclencheurs de cette base de données. Le déclencheur cesse de diffuser des événements, mais continue d'exister jusqu'à ce que vous le supprimiez.