Ampliar com o Cloud Run functions (2ª geração)

Com o Cloud Run functions, é possível implantar o código para processar eventos acionados por alterações no banco de dados do Firestore. Isso permite que você adicione funcionalidade do lado do servidor sem executar seus próprios servidores.

Ampliar o Firestore com o Cloud Run (2ª geração)

O Cloud Run functions (2nd gen) oferece suporte aos seguintes gatilhos de evento do Firestore para permitir que você crie gerenciadores vinculados a eventos do Firestore:

Tipo de evento Gatilho
google.cloud.firestore.document.v1.created Acionado quando um documento é gravado pela primeira vez.
google.cloud.firestore.document.v1.updated Acionado quando um documento já existe e tem algum valor alterado.
google.cloud.firestore.document.v1.deleted Acionado quando um documento é excluído.
google.cloud.firestore.document.v1.written Acionado quando created, updated ou deleted é acionado.
google.cloud.firestore.document.v1.created.withAuthContext Igual a created, mas adiciona informações de autenticação.
google.cloud.firestore.document.v1.updated.withAuthContext Igual a updated, mas adiciona informações de autenticação.
google.cloud.firestore.document.v1.deleted.withAuthContext Igual a deleted, mas adiciona informações de autenticação.
google.cloud.firestore.document.v1.written.withAuthContext Igual a written, mas adiciona informações de autenticação.

Os eventos do Firestore são acionados somente se houver mudanças nos documentos. Uma atualização em um documento do Firestore, em que os dados permanecem inalterados (uma gravação em ambiente autônomo), não gera um evento de atualização ou gravação. Não é possível adicionar eventos a campos específicos.

Incluir o contexto de autenticação no evento

Para incluir outras informações de autenticação sobre o evento, use um acionador de evento com a extensão withAuthContext. Essa extensão adiciona mais informações sobre o principal que acionou o evento. Ele adiciona os atributos authtype e authid, além das informações retornadas no evento base. Consulte a referência authcontext para mais informações sobre os valores de atributo.

Gravar uma função acionada pelo Firestore

Para escrever uma função que responda a eventos do Firestore, prepare-se para especificar o seguinte durante a implantação:

  • um tipo de evento acionador
  • um filtro de evento acionador para selecionar os documentos associados à função
  • o código da função a ser executado

Acionar filtros de eventos

Ao especificar um filtro de evento, é possível especificar uma correspondência de documento exata ou um padrão de caminho. Use um padrão de caminho para corresponder a vários documentos com caracteres curinga, * ou **.

Por exemplo, você pode responder a mudanças no seguinte documento:

users/marie

Use caracteres curinga, * ou **, para responder a mudanças em documentos que correspondam a um padrão. Um caractere curinga * corresponde a um único segmento, e um caractere curinga de vários segmentos ** corresponde a zero ou mais segmentos no padrão.

Para correspondências de segmento único (*), você também pode usar um grupo de captura nomeado. Por exemplo, users/{userId}.

Exemplo:

Padrão Descrição
/users/* ou /users/{userId} Corresponde a todos os documentos na coleção /users. Não corresponde a documentos em subcoleções como /users/marie/messages/33e2IxYBD9enzS50SJ68
/users/** Corresponde a todos os documentos na coleção /users e em subcoleções como /users/marie/messages/33e2IxYBD9enzS50SJ68

Para saber mais sobre os padrões de caminho, consulte Padrões de caminho do Eventarc.

É preciso que seu gatilho aponte sempre para um documento, mesmo que você esteja usando um caractere curinga. Por exemplo, users/{userId=*}/{messageCollectionId=*} não é válido porque {messageCollectionId=*} é uma coleção. No entanto, users/{userId=*}/{messageCollectionId}/{messageId=*} é válido porque {messageId=*} sempre vai apontar para um documento.

Exemplos de funções

O exemplo a seguir demonstra como receber eventos do Firestore. Para trabalhar com os dados do documento envolvidos em um evento, consulte os campos value e old_value.

  • value: um objeto Document que contém um snapshot de documento pós-operação. Esse campo não é preenchido para eventos de exclusão.
  • old_value: um objeto Document que contém um instantâneo de documento de pré-operação. Esse campo é preenchido apenas para eventos de atualização e exclusão.

Go

Para autenticar no Firestore, configure o Application Default Credentials. Para mais informações, consulte Configurar a autenticação para um ambiente de desenvolvimento 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 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

Para autenticar no Firestore, configure o Application Default Credentials. Para mais informações, consulte Configurar a autenticação para um ambiente de desenvolvimento 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 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());
  }
}

Node.js

Para autenticar no Firestore, configure o Application Default Credentials. Para mais informações, consulte Configurar a autenticação para um ambiente de desenvolvimento local.

Use protobufjs para decodificar os dados do evento. Inclua google.events.cloud.firestore.v1 data.proto na sua origem.
/**
 * 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 autenticar no Firestore, configure o Application Default Credentials. Para mais informações, consulte Configurar a autenticação para um ambiente de desenvolvimento 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 autenticar no Firestore, configure o Application Default Credentials. Para mais informações, consulte Configurar a autenticação para um ambiente de desenvolvimento 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));
    }
}

Os exemplos a seguir convertem strings adicionadas ao campo original de um documento afetado para letras maiúsculas e gravam o novo valor no mesmo documento:

Go

Para autenticar no Firestore, configure o Application Default Credentials. Para mais informações, consulte Configurar a autenticação para um ambiente de desenvolvimento 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 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

Para autenticar no Firestore, configure o Application Default Credentials. Para mais informações, consulte Configurar a autenticação para um ambiente de desenvolvimento 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 autenticar no Firestore, configure o Application Default Credentials. Para mais informações, consulte Configurar a autenticação para um ambiente de desenvolvimento local.

Use protobufjs para decodificar os dados do evento. Inclua google.events.cloud.firestore.v1 data.proto na sua origem.
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 autenticar no Firestore, configure o Application Default Credentials. Para mais informações, consulte Configurar a autenticação para um ambiente de desenvolvimento 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 autenticar no Firestore, configure o Application Default Credentials. Para mais informações, consulte Configurar a autenticação para um ambiente de desenvolvimento 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);
    }
}

Inclua as dependências de proto na sua origem

Inclua o arquivo data.proto do Firestore no diretório de origem da função. Esse arquivo importa os seguintes protos, que também precisam ser incluídos no diretório de origem:

Use a mesma estrutura de diretório para as dependências. Por exemplo, coloque struct.proto dentro de google/protobuf.

Esses arquivos são necessários para decodificar os dados do evento. Se a origem da função não incluir esses arquivos, ela vai retornar um erro quando for executada.

Atributos do evento

Cada evento inclui atributos de dados que contêm informações sobre o evento, como o horário em que ele foi acionado. O Firestore adiciona mais dados sobre o banco de dados e o documento envolvidos no evento. É possível acessar esses atributos da seguinte maneira:

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']}")

Implantar uma função

Os usuários que implantam funções do Cloud Run precisam ter o papel do IAM de Desenvolvedor de funções do Cloud Run ou um papel que inclua as mesmas permissões. Consulte também Outras configurações para implantação.

É possível implantar uma função usando a CLI gcloud ou o console do Google Cloud. O exemplo abaixo demonstra a implantação com a CLI gcloud. Para saber mais sobre a implantação com o console do Google Cloud, consulte Implantar funções do Cloud Run.

gcloud

  1. In the Google Cloud console, activate Cloud Shell.

    Activate Cloud Shell

    At the bottom of the Google Cloud console, a Cloud Shell session starts and displays a command-line prompt. Cloud Shell is a shell environment with the Google Cloud CLI already installed and with values already set for your current project. It can take a few seconds for the session to initialize.

  2. Use o comando gcloud functions deploy para implantar uma função:

    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" \
    

    O primeiro argumento, YOUR_FUNCTION_NAME, é um nome para a função implantada. O nome da função deve começar com uma letra, seguida por até 62 letras, números, hífens e sublinhados, e terminar com uma letra ou um número

    • A flag --gen2 especifica que você quer implantar nas funções do Cloud Run (2ª geração). A omissão dessa flag resulta na implantação no Cloud Run functions (1ª geração).

    • A sinalização --region especifica a região em que a função será implantada.

      Para maximizar a proximidade, defina uma região perto do seu banco de dados do Firestore. Se o banco de dados do Firestore estiver em um local multirregional, defina como us-central1 para bancos de dados em nam5 e como europe-west4 para bancos de dados em eur3. Para locais regionais do Firestore, defina a mesma região.

    • A flag --trigger-location especifica o local do acionador. Defina essa flag como o local do seu banco de dados do Firestore.

    • A sinalização --runtime especifica o ambiente de execução da linguagem que a função usa. O Cloud Run functions aceita vários ambientes de execução. Consulte Tempos de execução para mais informações.

    • A sinalização --source especifica o local do código-fonte da função. Confira mais detalhes neste link:

    • A sinalização --entry-point especifica o ponto de entrada da função no código-fonte. Este é o código que será executado quando a função for executada. O valor dessa sinalização precisa ser um nome de função ou de classe totalmente qualificada no código-fonte. Consulte Ponto de entrada de função para mais informações.

    • EVENT_FILTER_TYPE: o Firestore oferece suporte aos seguintes tipos de evento.

      • google.cloud.firestore.document.v1.created: o evento é enviado quando um documento é gravado pela primeira vez.
      • google.cloud.firestore.document.v1.updated: o evento é enviado quando um documento já existe e algum valor é alterado.
      • google.cloud.firestore.document.v1.deleted: o evento é enviado quando um documento é excluído.
      • google.cloud.firestore.document.v1.written: o evento é enviado quando um documento é criado, atualizado ou excluído.
      • google.cloud.firestore.document.v1.created.withAuthContext: o evento é enviado quando um documento é gravado pela primeira vez e inclui mais informações de autenticação.
      • google.cloud.firestore.document.v1.updated.withAuthContext: o evento é enviado quando um documento já existe e algum valor é alterado. Inclui outras informações de autenticação
      • google.cloud.firestore.document.v1.deleted.withAuthContext: o evento é enviado quando um documento é excluído. Inclui outras informações de autenticação
      • google.cloud.firestore.document.v1.written.withAuthContext: o evento é enviado quando um documento é criado, atualizado ou excluído e evento. Inclui outras informações de autenticação
    • DATABASE: o banco de dados do Firestore. Para o nome padrão do banco de dados, use (default).

    • DOCUMENT: o caminho do banco de dados que aciona eventos quando os dados são criados, atualizados ou excluídos. O operador pode ser um dos seguintes:

      • Igual por exemplo, --trigger-event-filters=document='users/marie'
      • Padrão do caminho por exemplo, --trigger-event-filters-path-pattern=document='users/*'. Para mais informações, consulte Entender os padrões de caminho.

    Também é possível especificar opções adicionais de configuração, rede e segurança ao implantar uma função.

    Para ver uma referência completa sobre o comando de implantação e as sinalizações dele, consulte a documentação gcloud functions deploy.

Exemplos de implantações

Os exemplos a seguir demonstram implantações com a Google Cloud CLI.

Implante uma função para um banco de dados na região 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}'

Implante uma função para um banco de dados na multirregião 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}'

Limitações

As seguintes limitações para gatilhos do Firestore do Cloud Run functions:

  • O pré-requisito do Cloud Run functions (1ª geração) é um banco de dados "(padrão)" no modo nativo do Firestore. Ele não é compatível com bancos de dados nomeados do Firestore ou com o modo Datastore. Use o Cloud Run functions (2ª geração) para configurar eventos nesses casos.
  • Não garantimos acionamentos em ordem. Alterações rápidas podem acionar invocações de função em uma ordem inesperada.
  • Os eventos são entregues pelo menos uma vez, mas um único evento pode resultar em invocações de várias funções. Evite depender de mecanismos do tipo "apenas uma vez" e escreva funções idempotentes.
  • O Firestore no modo Datastore requer o Cloud Run functions (2ª geração). O Cloud Run functions (1ª geração) não é compatível com o modo Datastore.
  • Um gatilho está associado a um único banco de dados. Não é possível criar um gatilho que corresponda a vários bancos de dados.
  • A exclusão de um banco de dados não remove automaticamente nenhum gatilho dele. O gatilho deixa de entregar eventos, mas continua existindo até que você o exclua.
  • Se um evento correspondente exceder o tamanho máximo da solicitação, ele pode não ser entregue ao Cloud Run functions (1ª geração).
    • Os eventos não entregues devido ao tamanho da solicitação são registrados nos registros da plataforma e contabilizados no uso de registros do projeto.
    • É possível encontrar esses registros na Análise de registros com a mensagem "O evento não pode ser entregue à função do Cloud devido ao tamanho excedido em relação ao limite para a 1ª geração..." da gravidade de error. Encontre o nome da função no campo functionName. Se o campo receiveTimestamp ainda estiver dentro de uma hora, será possível inferir o conteúdo real do evento lendo o documento em questão com um snapshot antes e depois do carimbo de data/hora.
    • Para evitar isso, faça o seguinte:
      • Migre e faça upgrade para o Cloud Run functions (2ª geração)
      • Reduza o tamanho do documento
      • Exclua o Cloud Run functions em questão
    • É possível desativar a geração de registros usando exclusões, mas os eventos ofensivos ainda não serão entregues.

Locais do Eventarc e do Firestore

O Eventarc não oferece suporte a várias regiões para gatilhos de evento do Firestore, mas você ainda pode criar gatilhos para bancos de dados do Firestore em locais multirregionais. O Eventarc mapeia os locais de várias regiões do Firestore para as seguintes regiões do Eventarc:

Multirregião do Firestore Região do Eventarc
nam5 us-central1
eur3 europe-west4

Diferenças entre as funções do Cloud Run de 1ª e 2ª geração

O Cloud Run Functions (2nd gen) usa eventos do Eventarc para todos os ambientes de execução. Anteriormente, as funções do Cloud Run (1ª geração) usavam eventos do Eventarc para apenas alguns ambientes de execução. Os eventos do Eventarc apresentam as seguintes diferenças em relação às funções do Cloud Run (1ª geração).

  • Os gatilhos do Firestore para o Eventarc oferecem suporte a outros destinos além das funções do Cloud Run. É possível encaminhar CloudEvents para vários destinos, incluindo, mas não se limitando a Cloud Run, GKE e Workflows.

  • Os acionadores do Firestore para o Eventarc recuperam a definição do acionador no início de uma operação de gravação de banco de dados e usam essa definição para decidir se o Firestore precisa emitir um evento. A operação de gravação não considera as mudanças na definição do acionador que podem acontecer durante a execução.

    O Cloud Run functions (1ª geração) recupera a definição do acionador durante a avaliação da gravação do banco de dados, e as alterações no acionador durante a avaliação podem afetar se o Firestore emite um evento ou não.

Para mais detalhes, consulte Comparação de versões das funções do Cloud Run.