Extender con Cloud Functions (2nd gen)

Con Cloud Functions, puedes implementar código para controlar eventos que se activan debido a los cambios en la base de datos de Firestore. Esto te permite agregar del servidor, sin tener que ejecutar tus propios servidores.

Extiende Firestore con Cloud Functions (2nd gen)

Cloud Functions (2nd gen) admite el siguiente evento de Firestore para permitirte crear controladores vinculados a eventos de Firestore:

Tipo de evento Activador
google.cloud.firestore.document.v1.created Se activa cuando se escribe en un documento por primera vez.
google.cloud.firestore.document.v1.updated Se activa cuando un documento ya existe y se cambia uno de sus valores.
google.cloud.firestore.document.v1.deleted Se activa cuando se borra un documento.
google.cloud.firestore.document.v1.written Se activa con created, updated o deleted.
google.cloud.firestore.document.v1.created.withAuthContext Es igual que created, pero agrega información de autenticación.
google.cloud.firestore.document.v1.updated.withAuthContext Es igual que updated, pero agrega información de autenticación.
google.cloud.firestore.document.v1.deleted.withAuthContext Es igual que deleted, pero agrega información de autenticación.
google.cloud.firestore.document.v1.written.withAuthContext Es igual que written, pero agrega información de autenticación.

Solo se activan los eventos de Firestore sobre cambios en los documentos. Es una actualización de un documento de Firestore en el que los datos no cambia (una escritura no-op) no genera un evento de actualización ni de escritura. No es posible agregar eventos a campos específicos.

Incluye el contexto de autenticación en el evento

Para incluir información de autenticación adicional sobre el evento, usa un activador con la extensión withAuthContext. Esta extensión agrega funciones adicionales información sobre la principal que activó el evento. Agrega la los atributos authtype y authid, además de la información que se muestra en el evento base. Consulta la Consulta la referencia de authcontext para obtener más información sobre los valores de los atributos.

Escribe una función activada por Firestore

Para escribir una función que responda a los eventos de Firestore, sigue estos pasos: prepara para especificar lo siguiente durante la implementación:

  • un tipo de evento activador
  • Un filtro de evento activador para seleccionar los documentos asociados con la función
  • el código de la función para ejecutar

Filtros de eventos del activador

Cuando especificas un filtro de eventos, puedes indicar un documento exacto coincidencia o un patrón de ruta de acceso. Usa un patrón de ruta de acceso para hacer coincidir varios documentos con comodines, * o **.

Por ejemplo, puedes responder a los cambios del siguiente documento:

users/marie

Usa comodines, * o **, para responder a los cambios en los documentos que coinciden con un patrón. Un comodín * coincide con un solo segmento y el comodín de varios segmentos ** coincide con cero o más segmentos del patrón.

Para las coincidencias de un solo segmento (*), también puedes usar un grupo de captura con nombre. Por ejemplo, users/{userId}.

Por ejemplo:

Patrón Descripción
/users/* o /users/{userId} Coincide con todos los documentos de la colección /users. No coincide con los documentos de las subcolecciones como /users/marie/messages/33e2IxYBD9enzS50SJ68
/users/** Coincide con todos los documentos de la colección /users y los documentos de las subcolecciones como /users/marie/messages/33e2IxYBD9enzS50SJ68

Para obtener más información sobre los patrones de ruta, consulta Patrones de ruta de Eventarc.

Los activadores siempre deben apuntar a un documento, incluso si usas un comodín. Por ejemplo, users/{userId=*}/{messageCollectionId=*} no es válido porque {messageCollectionId=*} es una colección. Sin embargo, users/{userId=*}/{messageCollectionId}/{messageId=*} es válido porque {messageId=*} siempre apunta a un documento.

Funciones de ejemplo

En el siguiente ejemplo, se muestra cómo recibir eventos de Firestore. Para trabajar con los datos de documentos involucrados en un evento, consulta value y old_value campos.

  • value: Es un objeto Document que contiene una instantánea de documento posterior a la operación. Este campo no se completa para los eventos de eliminación.
  • old_value: Es un objeto Document que contiene un documento de operación previa. instantánea. Este campo solo se propaga para los eventos de actualización y eliminación.

Go

Para autenticarte en Firestore, configura las credenciales predeterminadas de la aplicación. Si deseas obtener más información, consulta Configura la autenticación para un entorno de desarrollo local.


// 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 err := proto.Unmarshal(event.Data(), &data); 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

Para autenticarte en Firestore, configura las credenciales predeterminadas de la aplicación. Si deseas obtener más información, consulta Configura la autenticación para un entorno de desarrollo local.

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 firestorEventData = 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(firestorEventData.getOldValue().toString());

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

Node.js

Para autenticarte en Firestore, configura las credenciales predeterminadas de la aplicación. Si deseas obtener más información, consulta Configura la autenticación para un entorno de desarrollo local.

Usa protobufjs para decodificar los datos del evento. Incluye google.events.cloud.firestore.v1 data.proto en la fuente.
/**
 * 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

Para autenticarte en Firestore, configura las credenciales predeterminadas de la aplicación. Si deseas obtener más información, consulta Configura la autenticación para un entorno de desarrollo local.

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)

C#

Para autenticarte en Firestore, configura las credenciales predeterminadas de la aplicación. Si deseas obtener más información, consulta Configura la autenticación para un entorno de desarrollo local.

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

En el siguiente ejemplo, se convierten las cadenas agregadas al campo original de un el documento afectado en mayúscula y escribe el nuevo valor en el mismo documento:

Go

Para autenticarte en Firestore, configura las credenciales predeterminadas de la aplicación. Si deseas obtener más información, consulta Configura la autenticación para un entorno de desarrollo local.


// 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 err := proto.Unmarshal(e.Data(), &data); 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

Para autenticarte en Firestore, configura las credenciales predeterminadas de la aplicación. Si deseas obtener más información, consulta Configura la autenticación para un entorno de desarrollo local.

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();
  }
}

Node.js

Para autenticarte en Firestore, configura las credenciales predeterminadas de la aplicación. Si deseas obtener más información, consulta Configura la autenticación para un entorno de desarrollo local.

Usa protobufjs para decodificar los datos del evento. Incluye google.events.cloud.firestore.v1 data.proto en la fuente.
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

Para autenticarte en Firestore, configura las credenciales predeterminadas de la aplicación. Si deseas obtener más información, consulta Configura la autenticación para un entorno de desarrollo local.

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

C#

Para autenticarte en Firestore, configura las credenciales predeterminadas de la aplicación. Si deseas obtener más información, consulta Configura la autenticación para un entorno de desarrollo local.

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

Incluye las dependencias proto en tu código fuente

Debes incluir el data.proto de Firestore en el directorio del código fuente de tu función. Este archivo importa lo siguiente .protos que también debes incluir en tu directorio del código fuente:

Usa la misma estructura de directorio para las dependencias. Por ejemplo, coloca struct.proto en google/protobuf.

Estos archivos son necesarios para decodificar los datos de eventos. Si la fuente de tu función no si no incluyes estos archivos, se muestra un error cuando se ejecuta.

Atributos del evento

Cada evento incluye atributos de datos. que incluyen información sobre el evento, como la hora en que se activó. Firestore agrega datos adicionales sobre la base de datos y el documento participan en el evento. Puedes acceder a estos atributos de la siguiente manera:

Java
logger.info("Function triggered by event on: " + event.getSource());
logger.info("Event type: " + event.getType());
logger.info("Event time " + event.getTime());
logger.info("Event project: " + event.getExtension("project"));
logger.info("Event location: " + event.getExtension("location"));
logger.info("Database name: " + event.getExtension("database"));
logger.info("Database document: " + event.getExtension("document"));
// For withAuthContext events
logger.info("Auth information: " + event.getExtension("authid"));
logger.info("Auth information: " + event.getExtension("authtype"));
Node.js
console.log(`Function triggered by event on: ${cloudEvent.source}`);
console.log(`Event type: ${cloudEvent.type}`);
console.log(`Event time: ${cloudEvent.time}`);
console.log(`Event project: ${cloudEvent.project}`);
console.log(`Event location: ${cloudEvent.location}`);
console.log(`Database name: ${cloudEvent.database}`);
console.log(`Document name: ${cloudEvent.document}`);
// For withAuthContext events
console.log(`Auth information: ${cloudEvent.authid}`);
console.log(`Auth information: ${cloudEvent.authtype}`);
Python
print(f"Function triggered by change to: {cloud_event['source']}")
print(f"Event type: {cloud_event['type']}")
print(f"Event time: {cloud_event['time']}")
print(f"Event project: {cloud_event['project']}")
print(f"Location: {cloud_event['location']}")
print(f"Database name: {cloud_event['database']}")
print(f"Document: {cloud_event['document']}")
// For withAuthContext events
print(f"Auth information: {cloud_event['authid']}")
print(f"Auth information: {cloud_event['authtype']}")

Implementa una función

Los usuarios que implementan Cloud Functions deben tener el rol de IAM Cloud Functions Developer o una función que incluya los mismos permisos. Consulta también Configuración adicional para la implementación.

Puedes implementar una función con gcloud CLI o la consola de Google Cloud. En el siguiente ejemplo, se muestra la implementación con con gcloud CLI. Para más detalles sobre la implementación con la consola de Google Cloud, consulta Implementa Cloud Functions

gcloud

  1. En la consola de Google Cloud, activa Cloud Shell.

    Activar Cloud Shell

    En la parte inferior de la consola de Google Cloud, se inicia una sesión de Cloud Shell en la que se muestra una ventana de línea de comandos. Cloud Shell es un entorno de shell con Google Cloud CLI ya instalada y con valores ya establecidos para el proyecto actual. La sesión puede tardar unos segundos en inicializarse.

  2. Usa el comando gcloud functions deploy para implementar una función:

    gcloud functions deploy YOUR_FUNCTION_NAME \
    --gen2 \
    --region=FUNCTION_LOCATION \
    --trigger-location=TRIGGER_LOCATION \
    --runtime=YOUR_RUNTIME \
    --source=YOUR_SOURCE_LOCATION \
    --entry-point=YOUR_CODE_ENTRYPOINT \
    --trigger-event-filters="type=EVENT_FILTER_TYPE" \
    --trigger-event-filters="database=DATABASE" \
    --trigger-event-filters-path-pattern="document=DOCUMENT" \
    

    El primer argumento, YOUR_FUNCTION_NAME, es un nombre para tu función implementada. El nombre de la función debe comenzar con una letra seguida de un máximo de 62 letras, números, guiones o guiones bajos, y debe terminar con una letra o un número.

    • La marca --gen2 especifica que deseas implementar en Cloud Functions (2nd gen). Omitir esta marca da como resultado la implementación en Cloud Functions (1st gen).

    • La marca --region especifica la región en la que se implementará la función.

      Para maximizar la proximidad, establece una región cercana a Firestore en la base de datos. Si tu base de datos de Firestore está en una multirregión ubicación, establecida en us-central1 para las bases de datos en nam5 y en europe-west4 para bases de datos en eur3. Para regiones Ubicaciones de Firestore, configuradas en la misma región.

    • La --trigger-location marca especifica la ubicación del activador. Debes establecer esta marca la ubicación de la base de datos de Firestore.

    • La marca --runtime especifica el entorno de ejecución de lenguaje que usa tu función. Cloud Functions admite varios entornos de ejecución. Consulta Entornos de ejecución para obtener más información.

    • La marca --source especifica la ubicación del código fuente de tu función. Consulta lo siguiente para conocer los detalles:

    • La marca --entry-point especifica el punto de entrada a tu función en tu código fuente. Este es el código que se ejecutará cuando se ejecute tu función. El valor de esta marca debe ser un nombre de función o un nombre de clase completamente calificado que exista en tu código fuente. Consulta Punto de entrada de la función para obtener más información.

    • EVENT_FILTER_TYPE: Firestore admite los siguientes tipos de eventos.

      • google.cloud.firestore.document.v1.created: El evento se envía cuando un en el que se escribe este documento por primera vez.
      • google.cloud.firestore.document.v1.updated: El evento se envía cuando un el documento ya existe y su valor cambió.
      • google.cloud.firestore.document.v1.deleted: El evento se envía cuando un el documento.
      • google.cloud.firestore.document.v1.written: El evento se envía cuando un se crea, actualiza o borra el documento.
      • google.cloud.firestore.document.v1.created.withAuthContext: El evento se envía cuando un se escribe el documento por primera vez y el evento incluye información de autenticación adicional
      • google.cloud.firestore.document.v1.updated.withAuthContext: El evento se envía cuando un el documento ya existe y su valor cambió. Incluye información de autenticación adicional
      • google.cloud.firestore.document.v1.deleted.withAuthContext: El evento se envía cuando un el documento. Incluye información de autenticación adicional
      • google.cloud.firestore.document.v1.written.withAuthContext: El evento se envía cuando un se crea, actualiza o borra un evento. Incluye información de autenticación adicional
    • DATABASE: la base de datos de Firestore. Para ver el nombre de la base de datos predeterminada, usa (default).

    • DOCUMENT: Es la ruta de acceso de la base de datos que activa eventos cuando se crean, actualizan o borrar. El operador puede ser uno de los siguientes:

      • Iguales; por ejemplo, --trigger-event-filters=document='users/marie'
      • Patrón de ruta de acceso; por ejemplo, --trigger-event-filters-path-pattern=document='users/*' Para obtener más información, consulta Información sobre los patrones de ruta de acceso.

    De manera opcional, puedes especificar opciones adicionales de configuración, herramientas de redes y seguridad cuando implementes una función.

    Para obtener una referencia completa del comando de implementación y sus marcas, consulta la documentación de gcloud functions deploy.

Implementaciones de ejemplo

En los siguientes ejemplos, se muestran implementaciones con Google Cloud CLI.

Implementa una función para una base de datos en la región us-west2:

gcloud functions deploy gcfv2-trigger-firestore-node \
--gen2 \
--region=us-west2 \
--trigger-location=us-west2 \
--runtime=nodejs18 \
--source=gs://CLOUD_STORAGE_BUCKET/firestoreEventFunction.zip \
--entry-point=makeUpperCase \
--trigger-event-filters=type=google.cloud.firestore.document.v1.written \
--trigger-event-filters=database='(default)' \
--trigger-event-filters-path-pattern=document='messages/{pushId}'

Implementa una función para una base de datos en la multirregión nam5:

gcloud functions deploy gcfv2-trigger-firestore-python \
--gen2 \
--region=us-central1 \
--trigger-location=nam5 \
--runtime=python311 \
--source=gs://CLOUD_STORAGE_BUCKET/firestoreEventFunction.zip \
--entry-point=make_upper_case \
--trigger-event-filters=type=google.cloud.firestore.document.v1.written.withAuthContext \
--trigger-event-filters=database='(default)' \
--trigger-event-filters-path-pattern=document='messages/{pushId}'

Limitaciones

Ten en cuenta las siguientes limitaciones para los activadores de Firestore para Cloud Functions:

  • Cloud Functions (1ª gen.) es un requisito de una base de datos “(predeterminada)” existente en modo nativo de Firestore. No admite bases de datos con nombre de Firestore ni modo Datastore. Usa Cloud Functions (2nd gen) para configurar eventos en esos casos.
  • No se garantiza el ordenamiento. Los cambios rápidos pueden activar invocaciones de funciones en un orden inesperado.
  • Los eventos se entregan al menos una vez, pero un solo evento puede dar lugar a varias invocaciones de funciones. Evita depender de mecanismos solo una vez exactos y escribe funciones idempotentes.
  • Firestore en modo Datastore requiere Cloud Functions (2nd gen). Cloud Functions (1ª gen.) no es compatible con el modo Datastore.
  • Un activador se asocia con una sola base de datos. No puedes crear un activador que coincida con varias bases de datos.
  • Cuando se borra una base de datos, no se borra automáticamente ningún activador de la base de datos. El activador deja de entregar eventos, pero sigue existiendo hasta que lo borras.
  • Si un evento coincidente excede el tamaño máximo de la solicitud, es posible que el evento no se entregue a Cloud Functions (1st gen).
    • Los eventos que no se entregaron debido al tamaño de la solicitud se registran en los registros de la plataforma y se consideran en el uso de registros del proyecto.
    • Puedes encontrar estos registros en el Explorador de registros con el mensaje “El evento no se puede entregar a Cloud Function debido a que el tamaño supera el límite de 1ª gen... de gravedad error”. Puedes encontrar el nombre de la función en el campo functionName. Si el campo receiveTimestamp todavía está dentro de una hora a partir de ahora, puedes inferir el contenido real del evento si lees el documento en cuestión con una instantánea antes y después de la marca de tiempo.
    • Para evitar esa cadencia, puedes hacer lo siguiente:
      • Migra y actualiza a Cloud Functions (2nd gen)
      • Reducir el tamaño del documento
      • Borra la función de Cloud Functions en cuestión
    • Puedes desactivar el registro con exclusiones, pero ten en cuenta que los eventos problemáticos aún no se entregarán.

Ubicaciones de Eventarc y Firestore

Eventarc no es compatible con multirregiones para eventos de Firestore pero aún puedes crear activadores para las bases de datos de Firestore en ubicaciones multirregionales. Eventarc asigna Firestore ubicaciones multirregionales a las siguientes regiones de Eventarc:

Firestore multirregional Región de Eventarc
nam5 us-central1
eur3 europe-west4

Diferencias entre Cloud Functions (2ª gen.) y 1a gen.

Cloud Functions (2nd gen) usa eventos de Eventarc para todos los entornos de ejecución. Anteriormente, Cloud Functions (1a gen.) usaba eventos de Eventarc para solo algunos entornos de ejecución. Los eventos de Eventarc presentan las siguientes diferencias de Cloud Functions (1a gen.).

  • Los activadores de Firestore para la compatibilidad con Eventarc destinos adicionales además de Cloud Functions. Puedes enrutar CloudEvents a varios destinos, entre ellos, entre otras, Cloud Run, GKE y Workflows

  • Los activadores de Firestore para Eventarc recuperan el definición de activador al inicio de una operación de escritura de la base de datos y usa esa definición para decidir si Firestore debe emitir un evento. El La operación de escritura no considera ningún cambio para activar la definición. que puede ocurrir mientras se ejecuta.

    Cloud Functions (1a gen.) recupera la definición del activador durante el evaluación de la escritura de la base de datos y los cambios en el activador durante puede afectar si Firestore emite un evento o no.

Para obtener más detalles, consulta la comparación de versiones de Cloud Functions.