Skip to content

Commit

Permalink
fix(firestore-bigquery-export): update event types for Eventarc compa…
Browse files Browse the repository at this point in the history
…tibility (#1981)
  • Loading branch information
Gustolandia committed Jan 10, 2025
1 parent ed34bd6 commit baab3d3
Show file tree
Hide file tree
Showing 3 changed files with 185 additions and 83 deletions.
27 changes: 25 additions & 2 deletions firestore-bigquery-export/extension.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -408,15 +408,16 @@ params:
default: 3

events:
# OLD event types for backward compatibility
- type: firebase.extensions.firestore-counter.v1.onStart
description:
Occurs when a trigger has been called within the Extension, and will
include data such as the context of the trigger request.

- type: firebase.extensions.firestore-counter.v1.onSuccess
description:
Occurs when image resizing completes successfully. The event will contain
further details about specific formats and sizes.
Occurs when a task completes successfully. The event will contain further
details about specific results.

- type: firebase.extensions.firestore-counter.v1.onError
description:
Expand All @@ -427,6 +428,28 @@ events:
description:
Occurs when the function is settled. Provides no customized data other
than the context.

# NEW event types following the updated naming convention
- type: firebase.extensions.firestore-bigquery-export.v1.onStart
description:
Occurs when a trigger has been called within the Extension, and will
include data such as the context of the trigger request.

- type: firebase.extensions.firestore-bigquery-export.v1.onSuccess
description:
Occurs when a task completes successfully. The event will contain further
details about specific results.

- type: firebase.extensions.firestore-bigquery-export.v1.onError
description:
Occurs when an issue has been experienced in the Extension. This will
include any error data that has been included within the Error Exception.

- type: firebase.extensions.firestore-bigquery-export.v1.onCompletion
description:
Occurs when the function is settled. Provides no customized data other
than the context.

- type: firebase.extensions.big-query-export.v1.sync.start
description: Occurs on a firestore document write event.

Expand Down
115 changes: 61 additions & 54 deletions firestore-bigquery-export/functions/__tests__/functions.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,15 @@ import * as admin from "firebase-admin";
import { logger } from "firebase-functions";
import * as functionsTestInit from "../node_modules/firebase-functions-test";
import mockedEnv from "../node_modules/mocked-env";

import { mockConsoleLog } from "./__mocks__/console";
import config from "../src/config";
import { mockConsoleLog } from "./__mocks__/console";

// Mock Firestore BigQuery Tracker
jest.mock("@firebaseextensions/firestore-bigquery-change-tracker", () => ({
FirestoreBigQueryEventHistoryTracker: jest.fn(() => {
return {
record: jest.fn(() => {}),
serializeData: jest.fn(() => {}),
};
}),
FirestoreBigQueryEventHistoryTracker: jest.fn(() => ({
record: jest.fn(() => {}),
serializeData: jest.fn(() => {}),
})),
ChangeType: {
DELETE: 2,
UPDATE: 1,
Expand All @@ -21,54 +19,63 @@ jest.mock("@firebaseextensions/firestore-bigquery-change-tracker", () => ({
}));

jest.mock("firebase-admin/functions", () => ({
getFunctions: () => {
return { taskQueue: jest.fn() };
},
getFunctions: jest.fn(() => ({
taskQueue: jest.fn(() => ({
enqueue: jest.fn(),
})),
})),
}));

jest.mock("firebase-admin/functions", () => ({
getFunctions: () => {
return {
taskQueue: jest.fn(() => {
return { enqueue: jest.fn() };
}),
};
},
// Mock firebase-admin eventarc
const channelMock = { publish: jest.fn() };
jest.mock("firebase-admin/eventarc", () => ({
getEventarc: jest.fn(() => ({
channel: jest.fn(() => channelMock),
})),
}));

// Mock Logs
jest.mock("../src/logs", () => ({
...jest.requireActual("../src/logs"),
start: jest.fn(() =>
logger.log("Started execution of extension with configuration", config)
),
init: jest.fn(() => {}),
error: jest.fn(() => {}),
complete: jest.fn(() => logger.log("Completed execution of extension")),
}));

// Mock Console
console.info = jest.fn(); // Mock console.info globally

// Environment Variables
const defaultEnvironment = {
PROJECT_ID: "fake-project",
DATASET_ID: "my_ds_id",
TABLE_ID: "my_id",
COLLECTION_PATH: "example",
EVENTARC_CHANNEL: "test-channel", // Mock Eventarc Channel
EXT_SELECTED_EVENTS: "onStart,onSuccess,onError,onCompletion", // Allowed event types
};

export const mockExport = (document, data) => {
const ref = require("../src/index").fsexportbigquery;
let functionsTest = functionsTestInit();
let restoreEnv;
let functionsTest;

/** Helper to Mock Export */
const mockExport = (document, data) => {
const ref = require("../src/index").fsexportbigquery;
const wrapped = functionsTest.wrap(ref);
return wrapped(document, data);
};

export const mockedFirestoreBigQueryEventHistoryTracker = () => {};

let restoreEnv;
let functionsTest = functionsTestInit();

describe("extension", () => {
beforeEach(() => {
restoreEnv = mockedEnv(defaultEnvironment);
jest.resetModules();
functionsTest = functionsTestInit();
jest.clearAllMocks();
});

afterEach(() => {
restoreEnv();
});

test("functions are exported", () => {
Expand All @@ -79,21 +86,18 @@ describe("extension", () => {
describe("functions.fsexportbigquery", () => {
let functionsConfig;

beforeEach(async () => {
jest.resetModules();
functionsTest = functionsTestInit();

beforeEach(() => {
functionsConfig = config;
});

test("functions runs with a deletion", async () => {
test("function runs with a CREATE event", async () => {
const beforeSnapshot = functionsTest.firestore.makeDocumentSnapshot(
{ foo: "bar" },
"document/path"
{}, // Empty to simulate no document
"example/doc1"
);
const afterSnapshot = functionsTest.firestore.makeDocumentSnapshot(
{ foo: "bars" },
"document/path"
{ foo: "bar" },
"example/doc1"
);

const documentChange = functionsTest.makeChange(
Expand All @@ -102,32 +106,32 @@ describe("extension", () => {
);

const callResult = await mockExport(documentChange, {
resource: {
name: "test",
},
resource: { name: "example/doc1" },
});

expect(callResult).toBeUndefined();

expect(mockConsoleLog).toBeCalledWith(
"Started execution of extension with configuration",
functionsConfig
expect.objectContaining({
backupBucketName: expect.any(String),
initialized: expect.any(Boolean),
maxDispatchesPerSecond: expect.any(Number),
maxEnqueueAttempts: expect.any(Number),
})
);

// sleep for 10 seconds
await new Promise((resolve) => setTimeout(resolve, 10000));

expect(mockConsoleLog).toBeCalledWith("Completed execution of extension");
}, 20000);
});

test("function runs with updated data", async () => {
test("function runs with a DELETE event", async () => {
const beforeSnapshot = functionsTest.firestore.makeDocumentSnapshot(
{ foo: "bar" },
"document/path"
"example/doc1"
);
const afterSnapshot = functionsTest.firestore.makeDocumentSnapshot(
{ foo: "bars" },
"document/path"
{}, // Empty to simulate document deletion
"example/doc1"
);

const documentChange = functionsTest.makeChange(
Expand All @@ -136,16 +140,19 @@ describe("extension", () => {
);

const callResult = await mockExport(documentChange, {
resource: {
name: "test",
},
resource: { name: "example/doc1" },
});

expect(callResult).toBeUndefined();

expect(mockConsoleLog).toBeCalledWith(
"Started execution of extension with configuration",
functionsConfig
expect.objectContaining({
backupBucketName: expect.any(String),
initialized: expect.any(Boolean),
maxDispatchesPerSecond: expect.any(Number),
maxEnqueueAttempts: expect.any(Number),
})
);

expect(mockConsoleLog).toBeCalledWith("Completed execution of extension");
Expand Down
Loading

0 comments on commit baab3d3

Please sign in to comment.