Memperluas dengan fungsi Cloud Run (generasi ke-2)

Dengan fungsi Cloud Run, Anda dapat men-deploy kode untuk menangani peristiwa yang dipicu oleh perubahan pada database Firestore. Dengan begitu, Anda dapat menambahkan fungsi sisi server tanpa menjalankan server Anda sendiri.

Memperluas Firestore dengan fungsi Cloud Run (generasi ke-2)

Fungsi Cloud Run (generasi ke-2) mendukung pemicu peristiwa Firestore berikut untuk memungkinkan Anda membuat pengendali yang terkait dengan peristiwa Firestore:

Jenis Peristiwa Pemicu
google.cloud.firestore.document.v1.created Dipicu saat dokumen ditulisi untuk pertama kalinya.
google.cloud.firestore.document.v1.updated Dipicu saat dokumen sudah ada dan nilainya berubah.
google.cloud.firestore.document.v1.deleted Dipicu saat dokumen dihapus.
google.cloud.firestore.document.v1.written Dipicu saat created, updated, atau deleted dipicu.
google.cloud.firestore.document.v1.created.withAuthContext Sama seperti created, tetapi menambahkan informasi autentikasi.
google.cloud.firestore.document.v1.updated.withAuthContext Sama seperti updated, tetapi menambahkan informasi autentikasi.
google.cloud.firestore.document.v1.deleted.withAuthContext Sama seperti deleted, tetapi menambahkan informasi autentikasi.
google.cloud.firestore.document.v1.written.withAuthContext Sama seperti written, tetapi menambahkan informasi autentikasi.

Peristiwa Firestore hanya dipicu jika ada perubahan dokumen. Pembaruan terhadap dokumen Firestore yang tidak mengubah data (penulisan tanpa pengoperasian), tidak menghasilkan peristiwa penulisan atau pembaruan. Peristiwa tidak dapat ditambahkan ke kolom tertentu.

Menyertakan konteks autentikasi dalam peristiwa

Untuk menyertakan informasi autentikasi tambahan tentang peristiwa, gunakan pemicu peristiwa dengan ekstensi withAuthContext. Ekstensi ini menambahkan informasi tambahan tentang akun utama yang memicu peristiwa. Tindakan ini menambahkan atribut authtype dan authid selain informasi yang ditampilkan dalam peristiwa dasar. Lihat referensi authcontext untuk mengetahui informasi selengkapnya tentang nilai atribut.

Menulis fungsi yang dipicu oleh Firestore

Untuk menulis fungsi yang merespons peristiwa Firestore, siapkan untuk menentukan hal berikut selama deployment:

  • jenis peristiwa pemicu
  • filter peristiwa pemicu untuk memilih dokumen yang terkait dengan fungsi
  • kode fungsi yang akan dijalankan

Filter peristiwa pemicu

Saat menentukan filter peristiwa, Anda dapat menentukan kecocokan dokumen yang sama persis atau pola jalur. Gunakan pola jalur untuk mencocokkan beberapa dokumen dengan karakter pengganti, *, atau **.

Misalnya, Anda dapat merespons perubahan pada dokumen berikut:

users/marie

Gunakan karakter pengganti, * atau **, untuk merespons perubahan dalam dokumen yang cocok dengan pola. Karakter pengganti * cocok dengan satu segmen dan karakter pengganti multi-segmen ** cocok dengan nol atau beberapa segmen dalam pola.

Untuk pencocokan segmen tunggal (*), Anda juga dapat menggunakan grup tangkapan bernama. Contoh, users/{userId}.

Contoh:

Pola Deskripsi
/users/* atau /users/{userId} Mencocokkan semua dokumen dalam koleksi /users. Tidak cocok dengan dokumen di subkoleksi seperti /users/marie/messages/33e2IxYBD9enzS50SJ68
/users/** Mencocokkan semua dokumen dalam koleksi /users dan dokumen dalam subkoleksi seperti /users/marie/messages/33e2IxYBD9enzS50SJ68

Untuk mempelajari pola jalur lebih lanjut, lihat Pola jalur Eventarc.

Pemicu Anda harus selalu menunjuk ke sebuah dokumen, meskipun Anda menggunakan karakter pengganti. Misalnya, users/{userId=*}/{messageCollectionId=*} tidak valid karena {messageCollectionId=*} adalah sebuah koleksi. Namun, users/{userId=*}/{messageCollectionId}/{messageId=*} valid karena {messageId=*} akan selalu mengarah ke dokumen.

Contoh fungsi

Contoh berikut menunjukkan cara menerima peristiwa Firestore. Untuk menggunakan data dokumen yang terlibat dalam peristiwa, lihat kolom value dan old_value.

  • value: Objek Document yang berisi snapshot dokumen pasca-operasi. Kolom ini tidak diisi untuk peristiwa penghapusan.
  • old_value: Objek Document yang berisi snapshot dokumen pra-operasi. Kolom ini hanya diisi untuk peristiwa pembaruan dan penghapusan.

Go

Untuk melakukan autentikasi ke Firestore, siapkan Kredensial Default Aplikasi. Untuk mengetahui informasi selengkapnya, lihat Menyiapkan autentikasi untuk lingkungan pengembangan lokal.


// 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

Untuk melakukan autentikasi ke Firestore, siapkan Kredensial Default Aplikasi. Untuk mengetahui informasi selengkapnya, lihat Menyiapkan autentikasi untuk lingkungan pengembangan lokal.

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

Untuk melakukan autentikasi ke Firestore, siapkan Kredensial Default Aplikasi. Untuk mengetahui informasi selengkapnya, lihat Menyiapkan autentikasi untuk lingkungan pengembangan lokal.

Gunakan protobufjs untuk mendekode data peristiwa. Sertakan google.events.cloud.firestore.v1 data.proto dalam sumber Anda.
/**
 * 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

Untuk melakukan autentikasi ke Firestore, siapkan Kredensial Default Aplikasi. Untuk mengetahui informasi selengkapnya, lihat Menyiapkan autentikasi untuk lingkungan pengembangan lokal.

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#

Untuk melakukan autentikasi ke Firestore, siapkan Kredensial Default Aplikasi. Untuk mengetahui informasi selengkapnya, lihat Menyiapkan autentikasi untuk lingkungan pengembangan lokal.

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

Contoh berikut mengonversi string yang ditambahkan ke kolom original dokumen yang terpengaruh menjadi huruf besar dan menulis nilai baru ke dokumen yang sama:

Go

Untuk melakukan autentikasi ke Firestore, siapkan Kredensial Default Aplikasi. Untuk mengetahui informasi selengkapnya, lihat Menyiapkan autentikasi untuk lingkungan pengembangan lokal.


// 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

Untuk melakukan autentikasi ke Firestore, siapkan Kredensial Default Aplikasi. Untuk mengetahui informasi selengkapnya, lihat Menyiapkan autentikasi untuk lingkungan pengembangan lokal.

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

Untuk melakukan autentikasi ke Firestore, siapkan Kredensial Default Aplikasi. Untuk mengetahui informasi selengkapnya, lihat Menyiapkan autentikasi untuk lingkungan pengembangan lokal.

Gunakan protobufjs untuk mendekode data peristiwa. Sertakan google.events.cloud.firestore.v1 data.proto dalam sumber Anda.
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

Untuk melakukan autentikasi ke Firestore, siapkan Kredensial Default Aplikasi. Untuk mengetahui informasi selengkapnya, lihat Menyiapkan autentikasi untuk lingkungan pengembangan lokal.

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#

Untuk melakukan autentikasi ke Firestore, siapkan Kredensial Default Aplikasi. Untuk mengetahui informasi selengkapnya, lihat Menyiapkan autentikasi untuk lingkungan pengembangan lokal.

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

Menyertakan dependensi proto dalam sumber Anda

Anda harus menyertakan file Firestore data.proto di direktori sumber untuk fungsi Anda. File ini mengimpor proto berikut yang juga harus Anda sertakan dalam direktori sumber:

Gunakan struktur direktori yang sama untuk dependensi. Misalnya, tempatkan struct.proto dalam google/protobuf.

File ini diperlukan untuk mendekode data peristiwa. Jika sumber fungsi Anda tidak menyertakan file ini, sumber tersebut akan menampilkan error saat dijalankan.

Atribut peristiwa

Setiap peristiwa menyertakan atribut data yang menyertakan informasi tentang peristiwa tersebut, seperti waktu peristiwa dipicu. Firestore menambahkan data tambahan tentang database dan dokumen yang terlibat dalam peristiwa. Anda dapat mengakses atribut ini sebagai berikut:

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

Men-deploy fungsi

Pengguna yang men-deploy fungsi Cloud Run harus memiliki peran IAM Developer fungsi Cloud Run atau peran yang mencakup izin yang sama. Lihat juga Konfigurasi tambahan untuk deployment.

Anda dapat men-deploy fungsi menggunakan gcloud CLI atau Konsol Google Cloud. Contoh di bawah menunjukkan deployment dengan gcloud CLI. Untuk mengetahui detail tentang deployment dengan konsol Google Cloud, lihat Men-deploy fungsi 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. Gunakan perintah gcloud functions deploy untuk men-deploy fungsi:

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

    Argumen pertama, YOUR_FUNCTION_NAME, adalah nama untuk fungsi yang di-deploy. Nama fungsi harus dimulai dengan huruf yang diikuti dengan maksimal 62 huruf, angka, tanda hubung, atau garis bawah, dan harus diakhiri dengan huruf atau angka.

    • Flag --gen2 menentukan bahwa Anda ingin men-deploy ke fungsi Cloud Run (generasi ke-2). Menghapus flag ini akan menghasilkan deployment ke fungsi Cloud Run (generasi ke-1).

    • Flag --region menentukan region tempat men-deploy fungsi Anda.

      Untuk memaksimalkan kedekatan, tetapkan ke region di dekat database Firestore Anda. Jika database Firestore Anda berada di lokasi multi-region, tetapkan ke us-central1 untuk database di nam5 dan ke europe-west4 untuk database di eur3. Untuk lokasi Firestore regional, tetapkan ke region yang sama.

    • Flag --trigger-location menentukan lokasi pemicu. Anda harus menetapkan tanda ini ke lokasi database Firestore.

    • Flag --runtime menentukan runtime bahasa yang digunakan fungsi Anda. Fungsi Cloud Run mendukung beberapa runtime. Lihat Runtime untuk mengetahui informasi selengkapnya.

    • Flag --source menentukan lokasi kode sumber fungsi Anda. Lihat hal berikut untuk mengetahui detailnya:

    • Flag --entry-point menentukan titik masuk ke fungsi Anda dalam kode sumber. Ini adalah kode yang akan dijalankan saat fungsi Anda berjalan. Nilai flag ini harus berupa nama fungsi atau nama class yang sepenuhnya memenuhi syarat yang ada dalam kode sumber Anda. Lihat Titik entri fungsi untuk informasi selengkapnya.

    • EVENT_FILTER_TYPE: Firestore mendukung jenis peristiwa berikut.

      • google.cloud.firestore.document.v1.created: peristiwa dikirim saat dokumen ditulis untuk pertama kalinya.
      • google.cloud.firestore.document.v1.updated: peristiwa dikirim saat dokumen sudah ada dan nilainya berubah.
      • google.cloud.firestore.document.v1.deleted: peristiwa dikirim saat dokumen dihapus.
      • google.cloud.firestore.document.v1.written: peristiwa dikirim saat dokumen dibuat, diperbarui, atau dihapus.
      • google.cloud.firestore.document.v1.created.withAuthContext: peristiwa dikirim saat dokumen ditulis untuk pertama kalinya dan peristiwa menyertakan informasi autentikasi tambahan
      • google.cloud.firestore.document.v1.updated.withAuthContext: peristiwa dikirim saat dokumen sudah ada dan nilainya berubah. Menyertakan informasi autentikasi tambahan
      • google.cloud.firestore.document.v1.deleted.withAuthContext: peristiwa dikirim saat dokumen dihapus. Menyertakan informasi autentikasi tambahan
      • google.cloud.firestore.document.v1.written.withAuthContext: peristiwa dikirim saat dokumen dibuat, diperbarui, atau dihapus dan peristiwa. Menyertakan informasi autentikasi tambahan
    • DATABASE: database Firestore. Untuk nama database default, gunakan (default).

    • DOCUMENT: jalur database yang memicu peristiwa saat data dibuat, diperbarui, atau dihapus. Operator dapat berupa salah satu dari berikut:

      • Sama; misalnya, --trigger-event-filters=document='users/marie'
      • Pola jalur; misalnya, --trigger-event-filters-path-pattern=document='users/*'. Untuk mengetahui informasi selengkapnya, lihat Memahami pola jalur.

    Secara opsional, Anda dapat menentukan opsi konfigurasi, jaringan, dan keamanan tambahan saat men-deploy fungsi.

    Untuk referensi lengkap tentang perintah deployment dan flag-nya, lihat dokumentasi gcloud functions deploy.

Contoh deployment

Contoh berikut menunjukkan deployment dengan Google Cloud CLI.

Men-deploy fungsi untuk database di region 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}'

Men-deploy fungsi untuk database di multi-region 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}'

Batasan

Perhatikan batasan berikut untuk pemicu Firestore untuk fungsi Cloud Run:

  • Fungsi Cloud Run (generasi ke-1) menjadi prasyarat database "(default)" yang ada dalam mode native Firestore. Lingkungan ini tidak mendukung mode Datastore atau database bernama Firestore. Gunakan fungsi Cloud Run (generasi ke-2) untuk mengonfigurasi peristiwa dalam kasus tersebut.
  • Pengurutan tidak dijamin. Perubahan cepat dapat memicu pemanggilan fungsi dalam urutan yang tidak terduga.
  • Peristiwa dikirim setidaknya satu kali, tetapi satu peristiwa dapat menghasilkan beberapa pemanggilan fungsi. Hindari mengandalkan mekanisme tepat satu kali, dan tulis fungsi idempoten.
  • Firestore dalam mode Datastore memerlukan fungsi Cloud Run (generasi ke-2). Fungsi Cloud Run (generasi ke-1) tidak mendukung mode Datastore.
  • Pemicu dikaitkan dengan satu database. Anda tidak dapat membuat pemicu yang cocok dengan beberapa database.
  • Menghapus database tidak secara otomatis menghapus pemicu untuk database tersebut. Pemicu berhenti mengirim peristiwa, tetapi akan tetap ada sampai Anda menghapus pemicu.
  • Jika peristiwa yang cocok melebihi ukuran permintaan maksimum, peristiwa tersebut mungkin tidak akan dikirim ke fungsi Cloud Run (generasi ke-1).
    • Peristiwa yang tidak terkirim karena ukuran permintaan akan dicatat dalam log platform dan diperhitungkan terhadap penggunaan log untuk project.
    • Anda dapat menemukan log ini di Logs Explorer dengan pesan "Event dapat mengirim ke Cloud function karena ukuran melebihi batas untuk generasi ke-1..." dengan tingkat keparahan error. Anda dapat menemukan nama fungsi di bawah kolom functionName. Jika kolom receiveTimestamp masih berada dalam waktu satu jam dari sekarang, Anda dapat menyimpulkan konten peristiwa yang sebenarnya dengan membaca dokumen yang dimaksud menggunakan snapshot sebelum dan setelah stempel waktu.
    • Untuk menghindari peristiwa seperti ini, Anda dapat:
      • Melakukan migrasi dan upgrade ke fungsi Cloud Run (generasi ke-2)
      • Memperkecil dokumen
      • Menghapus fungsi Cloud Run yang dimaksud
    • Anda dapat menonaktifkan logging itu sendiri menggunakan pengecualian, tetapi perhatikan bahwa peristiwa yang melanggar tidak akan dikirim.

Lokasi Eventarc dan Firestore

Eventarc tidak mendukung multi-region untuk pemicu peristiwa Firestore, tetapi Anda tetap dapat membuat pemicu untuk database Firestore di lokasi multi-region. Eventarc memetakan lokasi multi-region Firestore ke region Eventarc berikut:

Firestore multi-region Wilayah Eventarc
nam5 us-central1
eur3 europe-west4

Perbedaan antara fungsi Cloud Run generasi ke-2 dan generasi ke-1

Fungsi Cloud Run (generasi ke-2) menggunakan peristiwa Eventarc untuk semua runtime. Sebelumnya, fungsi Cloud Run (generasi ke-1) menggunakan peristiwa Eventarc untuk hanya beberapa runtime. Peristiwa Eventarc memperkenalkan perbedaan berikut dari fungsi Cloud Run (generasi ke-1).

  • Pemicu Firestore untuk Eventarc mendukung tujuan tambahan selain fungsi Cloud Run. Anda dapat merutekan CloudEvents ke sejumlah tujuan, termasuk, tetapi tidak terbatas pada Cloud Run, GKE, dan Workflow.

  • Pemicu Firestore untuk Eventarc mengambil definisi pemicu di awal operasi tulis database dan menggunakan definisi tersebut untuk memutuskan apakah Firestore harus memunculkan peristiwa. Operasi menulis tidak memperhitungkan perubahan apa pun pada definisi pemicu yang mungkin terjadi saat berjalan.

    Fungsi Cloud Run (generasi ke-1) mengambil definisi pemicu selama evaluasi penulisan database, dan perubahan pada pemicu selama evaluasi dapat memengaruhi apakah Firestore memunculkan peristiwa atau tidak.

Untuk mengetahui detail selengkapnya, lihat Perbandingan versi fungsi Cloud Run.