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 Dec 20, 2024
1 parent ed34bd6 commit 5a9311b
Show file tree
Hide file tree
Showing 3 changed files with 202 additions and 78 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
132 changes: 77 additions & 55 deletions firestore-bigquery-export/functions/__tests__/functions.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,73 +2,69 @@ 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";

// 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,
CREATE: 0,
},
}));

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

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

// 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")),
}));

// 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 = functionsTestInit();

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

afterEach(() => {
restoreEnv();
jest.clearAllMocks();
});

test("functions are exported", () => {
Expand All @@ -79,21 +75,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 and publishes both old and new events", async () => {
const beforeSnapshot = functionsTest.firestore.makeDocumentSnapshot(
{ foo: "bar" },
"document/path"
{}, // Empty data 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 +95,45 @@ describe("extension", () => {
);

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

expect(callResult).toBeUndefined();

// Verify Logs
expect(mockConsoleLog).toBeCalledWith(
"Started execution of extension with configuration",
functionsConfig
);
expect(mockConsoleLog).toBeCalledWith("Completed execution of extension");

// sleep for 10 seconds
await new Promise((resolve) => setTimeout(resolve, 10000));
// Verify Event Publishing
const eventarcMock = require("firebase-admin/eventarc").getEventarc;
const channelMock = eventarcMock().channel();

expect(mockConsoleLog).toBeCalledWith("Completed execution of extension");
}, 20000);
expect(channelMock.publish).toHaveBeenCalledTimes(2);
expect(channelMock.publish).toHaveBeenCalledWith(
expect.objectContaining({
type: "firebase.extensions.firestore-counter.v1.onStart",
data: expect.any(Object),
})
);
expect(channelMock.publish).toHaveBeenCalledWith(
expect.objectContaining({
type: "firebase.extensions.firestore-bigquery-export.v1.onStart",
data: expect.any(Object),
})
);
});

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 data to simulate no document
"example/doc1"
);

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

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

expect(callResult).toBeUndefined();

// Verify Logs
expect(mockConsoleLog).toBeCalledWith(
"Started execution of extension with configuration",
functionsConfig
);

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

// Verify Event Publishing for both old and new event types
const eventarcMock = require("firebase-admin/eventarc").getEventarc;
const channelMock = eventarcMock().channel();

expect(channelMock.publish).toHaveBeenCalledTimes(2);
expect(channelMock.publish).toHaveBeenCalledWith(
expect.objectContaining({
type: "firebase.extensions.firestore-counter.v1.onCompletion",
data: expect.any(Object),
})
);
expect(channelMock.publish).toHaveBeenCalledWith(
expect.objectContaining({
type: "firebase.extensions.firestore-bigquery-export.v1.onCompletion",
data: expect.any(Object),
})
);
});
});
});
Loading

0 comments on commit 5a9311b

Please sign in to comment.