Acionadores do Firebase Realtime Database

Com as funções do Cloud Run, pode processar eventos na Firebase Realtime Database no mesmo Google Cloud projeto que a função. As funções do Cloud Run permitem-lhe executar operações de base de dados com privilégios administrativos completos e garantem que cada alteração à base de dados é processada individualmente. Pode fazer alterações à base de dados em tempo real do Firebase através do SDK Firebase Admin.

Num ciclo de vida típico, uma função do Firebase Realtime Database faz o seguinte:

  1. Aguarda alterações a uma localização específica da base de dados.

  2. É acionado quando ocorre um evento e executa as respetivas tarefas.

  3. Recebe um objeto de dados que contém um instantâneo dos dados armazenados no documento especificado.

Tipos de eventos

As funções permitem-lhe processar eventos da base de dados em dois níveis de especificidade. Pode ouvir especificamente apenas eventos de criação, atualização ou eliminação, ou pode ouvir qualquer alteração de qualquer tipo a um caminho. As funções do Cloud Run suportam os seguintes tipos de eventos para a Realtime Database:

Tipo de evento Acionador
providers/google.firebase.database/eventTypes/ref.write Acionado em qualquer evento de mutação: quando os dados são criados, atualizados ou eliminados na Realtime Database.
providers/google.firebase.database/eventTypes/ref.create (predefinição) Acionada quando são criados novos dados na Realtime Database.
providers/google.firebase.database/eventTypes/ref.update Acionado quando os dados são atualizados na Realtime Database.
providers/google.firebase.database/eventTypes/ref.delete Acionado quando os dados são eliminados da Realtime Database.

Especificar o caminho e a instância da base de dados

Para controlar quando e onde a sua função deve ser acionada, tem de especificar um caminho e, opcionalmente, especificar uma instância da base de dados.

Caminho

As especificações de caminho correspondem a todas as escritas que tocam num caminho, incluindo as escritas que ocorrem em qualquer lugar abaixo do mesmo. Se definir o caminho da sua função como /foo/bar, os eventos correspondem a ambas as localizações:

 /foo/bar
 /foo/bar/baz/really/deep/path

Em qualquer dos casos, o Firebase interpreta que o evento ocorre às /foo/bar e os dados do evento incluem os dados antigos e novos às /foo/bar. Se os dados de eventos puderem ser grandes, considere usar várias funções em caminhos mais profundos em vez de uma única função perto da raiz da base de dados. Para o melhor desempenho, peça apenas dados no nível mais detalhado possível.

Pode especificar um componente do caminho como um caráter universal ao colocá-lo entre chavetas; foo/{bar} corresponde a qualquer filho de /foo. Os valores destes componentes do caminho com carateres universais estão disponíveis no objeto event.params da sua função. Neste exemplo, o valor está disponível como event.params.bar.

Os caminhos com carateres universais podem corresponder a vários eventos de uma única gravação. Uma inserção de:

{
  "foo": {
    "hello": "world",
    "firebase": "functions"
  }
}

corresponde ao caminho /foo/{bar} duas vezes: uma com "hello": "world" e outra com "firebase": "functions".

Instância

Quando usar a Google Cloud consola, tem de especificar a instância da base de dados.

Quando usar a Google Cloud CLI, a instância tem de ser especificada como parte da string --trigger-resource.

Por exemplo, o seguinte usaria o seguinte na sua string --trigger-resource:

--trigger-resource projects/_/instances/DATABASE_INSTANCE/refs/PATH

Estrutura de eventos

Quando processa um evento da Realtime Database, o objeto data contém duas propriedades que são fornecidas no formato de objeto JSON:

  • data: uma imagem instantânea dos dados obtidos antes do evento que acionou a função.

  • delta: uma imagem instantânea dos dados obtidos após o evento que acionou a função.

Exemplo de código

Node.js

/**
 * Background Function triggered by a change to a Firebase RTDB reference.
 *
 * @param {!Object} event The Cloud Functions event.
 * @param {!Object} context The Cloud Functions event context.
 */
exports.helloRTDB = (event, context) => {
  const triggerResource = context.resource;

  console.log(`Function triggered by change to: ${triggerResource}`);
  console.log(`Admin?: ${!!context.auth.admin}`);
  console.log('Delta:');
  console.log(JSON.stringify(event.delta, null, 2));
};

Python

import json

def hello_rtdb(data, context):
    """Triggered by a change to a Firebase RTDB reference.
    Args:
        data (dict): The event payload.
        context (google.cloud.functions.Context): Metadata for the event.
    """
    trigger_resource = context.resource

    print("Function triggered by change to: %s" % trigger_resource)
    print("Admin?: %s" % data.get("admin", False))
    print("Delta:")
    print(json.dumps(data["delta"]))

Go


// Package p contains a Cloud Function triggered by a Firebase Realtime Database
// event.
package p

import (
	"context"
	"fmt"
	"log"

	"cloud.google.com/go/functions/metadata"
)

// RTDBEvent is the payload of a RTDB event.
type RTDBEvent struct {
	Data  interface{} `json:"data"`
	Delta interface{} `json:"delta"`
}

// HelloRTDB handles changes to a Firebase RTDB.
func HelloRTDB(ctx context.Context, e RTDBEvent) error {
	meta, err := metadata.FromContext(ctx)
	if err != nil {
		return fmt.Errorf("metadata.FromContext: %w", err)
	}
	log.Printf("Function triggered by change to: %v", meta.Resource)
	log.Printf("%+v", e)
	return nil
}

Java

import com.google.cloud.functions.Context;
import com.google.cloud.functions.RawBackgroundFunction;
import com.google.gson.Gson;
import com.google.gson.JsonObject;
import java.util.logging.Logger;

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

  // Use GSON (https://github.com/google/gson) to parse JSON content.
  private static final Gson gson = new Gson();

  @Override
  public void accept(String json, Context context) {
    logger.info("Function triggered by change to: " + context.resource());

    JsonObject body = gson.fromJson(json, JsonObject.class);

    boolean isAdmin = false;
    if (body != null && body.has("auth")) {
      JsonObject authObj = body.getAsJsonObject("auth");
      isAdmin = authObj.has("admin") && authObj.get("admin").getAsBoolean();
    }

    logger.info("Admin?: " + isAdmin);

    if (body != null && body.has("delta")) {
      logger.info("Delta:");
      logger.info(body.get("delta").toString());
    }
  }
}

C#

using CloudNative.CloudEvents;
using Google.Cloud.Functions.Framework;
using Google.Events.Protobuf.Firebase.Database.V1;
using Microsoft.Extensions.Logging;
using System.Threading;
using System.Threading.Tasks;

namespace FirebaseRtdb;

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

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

    public Task HandleAsync(CloudEvent cloudEvent, ReferenceEventData data, CancellationToken cancellationToken)
    {
        _logger.LogInformation("Function triggered by change to {subject}", cloudEvent.Subject);
        _logger.LogInformation("Delta: {delta}", data.Delta);

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

Ruby

require "functions_framework"

# Triggered by a change to a Firebase RTDB document.
FunctionsFramework.cloud_event "hello_rtdb" do |event|
  # Event-triggered Ruby functions receive a CloudEvents::Event::V1 object.
  # See https://cloudevents.github.io/sdk-ruby/latest/CloudEvents/Event/V1.html
  # The Firebase event payload can be obtained from the `data` field.
  payload = event.data

  logger.info "Function triggered by change to: #{event.source}"
  logger.info "Admin?: #{payload.fetch 'admin', false}"
  logger.info "Delta: #{payload['delta']}"
end

PHP

use Google\CloudFunctions\CloudEvent;

function firebaseRTDB(CloudEvent $cloudevent)
{
    $log = fopen(getenv('LOGGER_OUTPUT') ?: 'php://stderr', 'wb');

    fwrite($log, 'Event: ' . $cloudevent->getId() . PHP_EOL);

    $data = $cloudevent->getData();
    $resource = $data['resource'] ?? '<null>';

    fwrite($log, 'Function triggered by change to: ' . $resource . PHP_EOL);

    $isAdmin = isset($data['auth']['admin']) && $data['auth']['admin'] == true;

    fwrite($log, 'Admin?: ' . var_export($isAdmin, true) . PHP_EOL);
    fwrite($log, 'Delta: ' . json_encode($data['delta'] ?? '') . PHP_EOL);
}

Implementar a sua função

O seguinte comando gcloud implementa uma função que é acionada por eventos create no caminho /messages/{pushId}/original:

gcloud functions deploy FUNCTION_NAME \
  --no-gen2 \
  --entry-point ENTRY_POINT \
  --trigger-event providers/google.firebase.database/eventTypes/ref.create \
  --trigger-resource projects/_/instances/DATABASE_INSTANCE/refs/messages/{pushId}/original \
  --runtime RUNTIME
Argumento Descrição
FUNCTION_NAME O nome registado da função do Cloud Run que está a implementar. Pode ser o nome de uma função no seu código-fonte ou uma string arbitrária. Se FUNCTION_NAME for uma string arbitrária, tem de incluir a flag --entry-point.
--entry-point ENTRY_POINT O nome de uma função ou classe no seu código-fonte. Opcional, a menos que não tenha usado FUNCTION_NAME para especificar a função no código fonte a ser executada durante a implementação. Nesse caso, tem de usar --entry-point para indicar o nome da função executável.
--trigger-event NAME O nome do tipo de evento que a função quer receber. Neste caso, será uma das seguintes opções: escrever, criar, atualizar ou eliminar.
--trigger-resource NAME O caminho da base de dados totalmente qualificado que a função vai ouvir. Este deve estar em conformidade com o seguinte formato: projects/_/instances/DATABASE_INSTANCE/refs/PATH.
--runtime RUNTIME O nome do tempo de execução que está a usar. Para ver uma lista completa, consulte a gcloud referência.