Skip to content

Commit

Permalink
Merge branch 'master' into fredzqm-patch-2
Browse files Browse the repository at this point in the history
  • Loading branch information
fredzqm authored Dec 12, 2024
2 parents 53b1ab0 + 55a70ab commit c91a23a
Show file tree
Hide file tree
Showing 18 changed files with 227 additions and 64 deletions.
3 changes: 0 additions & 3 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +0,0 @@
- Changes default CF3 runtime to nodejs22 (#8037)
- Fixed an issue where `--import` would error for the Data Connect emulator if `dataDir` was also set.
- Fixed an issue where `firebase init dataconnect` errored when importing a schema with no GQL files.
51 changes: 30 additions & 21 deletions firebase-vscode/src/logger-wrapper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,28 +44,37 @@ export function logSetup() {

// Log to file
// Only log to file if firebase.debug extension setting is true.
// Re-implement file logger call from ../../src/bin/firebase.ts to not bring
// in the entire firebase.ts file
const rootFolders = getRootFolders();
// Default to a central path, but write files to a local path if we're in a Firebase directory.
let filePath = path.join(os.homedir(), ".cache", "firebase", "logs", "vsce-debug.log");
if (fs.existsSync(path.join(rootFolders[0], "firebase.json"))) {
filePath = path.join(rootFolders[0], ".firebase", "logs", "vsce-debug.log");
}
pluginLogger.info("Logging to path", filePath);
cliLogger.add(
new transports.File({
level: "debug",
filename: filePath,
format: format.printf((info) => {
const segments = [info.message, ...(info[SPLAT] || [])].map(
tryStringify,
);
return `[${info.level}] ${stripVTControlCharacters(segments.join(" "))}`;
}),
// Re-implement file logger call from ../../src/bin/firebase.ts to not bring
// in the entire firebase.ts file
const rootFolders = getRootFolders();
// Default to a central path, but write files to a local path if we're in a Firebase directory.
let filePath = path.join(
os.homedir(),
".cache",
"firebase",
"logs",
"vsce-debug.log",
);
if (
rootFolders.length > 0 &&
fs.existsSync(path.join(rootFolders[0], "firebase.json"))
) {
filePath = path.join(rootFolders[0], ".firebase", "logs", "vsce-debug.log");
}
pluginLogger.info("Logging to path", filePath);
cliLogger.add(
new transports.File({
level: "debug",
filename: filePath,
format: format.printf((info) => {
const segments = [info.message, ...(info[SPLAT] || [])].map(
tryStringify,
);
return `[${info.level}] ${stripVTControlCharacters(segments.join(" "))}`;
}),
);
cliLogger.add(new VSCodeOutputTransport({ level: "info" }));
}),
);
cliLogger.add(new VSCodeOutputTransport({ level: "info" }));
}

/**
Expand Down
4 changes: 2 additions & 2 deletions npm-shrinkwrap.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "firebase-tools",
"version": "13.28.0",
"version": "13.29.0",
"description": "Command-Line Interface for Firebase",
"main": "./lib/index.js",
"bin": {
Expand Down
10 changes: 6 additions & 4 deletions src/deploy/functions/backend.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import * as utils from "../../utils";
import { Runtime } from "./runtimes/supported";
import { FirebaseError } from "../../error";
import { Context } from "./args";
import { flattenArray } from "../../functional";
import { assertExhaustive, flattenArray } from "../../functional";

/** Retry settings for a ScheduleSpec. */
export interface ScheduleRetryConfig {
Expand Down Expand Up @@ -41,7 +41,9 @@ export interface HttpsTriggered {
}

/** API agnostic version of a Firebase callable function. */
export type CallableTrigger = Record<string, never>;
export type CallableTrigger = {
genkitAction?: string;
};

/** Something that has a callable trigger */
export interface CallableTriggered {
Expand Down Expand Up @@ -135,6 +137,7 @@ export interface BlockingTrigger {
eventType: string;
options?: Record<string, unknown>;
}

export interface BlockingTriggered {
blockingTrigger: BlockingTrigger;
}
Expand All @@ -153,9 +156,8 @@ export function endpointTriggerType(endpoint: Endpoint): string {
return "taskQueue";
} else if (isBlockingTriggered(endpoint)) {
return endpoint.blockingTrigger.eventType;
} else {
throw new Error("Unexpected trigger type for endpoint " + JSON.stringify(endpoint));
}
assertExhaustive(endpoint);
}

// TODO(inlined): Enum types should be singularly named
Expand Down
9 changes: 6 additions & 3 deletions src/deploy/functions/build.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,8 +74,9 @@ export interface HttpsTrigger {

// Trigger definitions for RPCs servers using the HTTP protocol defined at
// https://firebase.google.com/docs/functions/callable-reference
// eslint-disable-next-line
interface CallableTrigger {}
interface CallableTrigger {
genkitAction?: string;
}

// Trigger definitions for endpoints that should be called as a delegate for other operations.
// For example, before user login.
Expand Down Expand Up @@ -568,7 +569,9 @@ function discoverTrigger(endpoint: Endpoint, region: string, r: Resolver): backe
}
return { httpsTrigger };
} else if (isCallableTriggered(endpoint)) {
return { callableTrigger: {} };
const trigger: CallableTriggered = { callableTrigger: {} };
proto.copyIfPresent(trigger.callableTrigger, endpoint.callableTrigger, "genkitAction");
return trigger;
} else if (isBlockingTriggered(endpoint)) {
return { blockingTrigger: endpoint.blockingTrigger };
} else if (isEventTriggered(endpoint)) {
Expand Down
10 changes: 10 additions & 0 deletions src/deploy/functions/release/planner.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,16 @@ describe("planner", () => {
expect(() => planner.calculateUpdate(httpsFunc, scheduleFunc)).to.throw();
});

it("allows upgrades of genkit functions from the genkit plugin to firebase-functions SDK", () => {
const httpsFunc = func("a", "b", { httpsTrigger: {} });
const genkitFunc = func("a", "b", { callableTrigger: { genkitAction: "flows/flow" } });
expect(planner.calculateUpdate(genkitFunc, httpsFunc)).to.deep.equal({
// Missing: deleteAndRecreate
endpoint: genkitFunc,
unsafe: false,
});
});

it("knows to delete & recreate for v2 topic changes", () => {
const original: backend.Endpoint = {
...func("a", "b", {
Expand Down
15 changes: 8 additions & 7 deletions src/deploy/functions/release/planner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,6 @@ import { FirebaseError } from "../../../error";
import * as utils from "../../../utils";
import * as backend from "../backend";
import * as v2events from "../../../functions/events/v2";
import {
FIRESTORE_EVENT_REGEX,
FIRESTORE_EVENT_WITH_AUTH_CONTEXT_REGEX,
} from "../../../functions/events/v2";

export interface EndpointUpdate {
endpoint: backend.Endpoint;
Expand Down Expand Up @@ -261,9 +257,9 @@ export function upgradedScheduleFromV1ToV2(
export function checkForUnsafeUpdate(want: backend.Endpoint, have: backend.Endpoint): boolean {
return (
backend.isEventTriggered(want) &&
FIRESTORE_EVENT_WITH_AUTH_CONTEXT_REGEX.test(want.eventTrigger.eventType) &&
backend.isEventTriggered(have) &&
FIRESTORE_EVENT_REGEX.test(have.eventTrigger.eventType)
want.eventTrigger.eventType ===
v2events.CONVERTABLE_EVENTS[have.eventTrigger.eventType as v2events.Event]
);
}

Expand All @@ -289,7 +285,12 @@ export function checkForIllegalUpdate(want: backend.Endpoint, have: backend.Endp
};
const wantType = triggerType(want);
const haveType = triggerType(have);
if (wantType !== haveType) {

// Originally, @genkit-ai/firebase/functions defined onFlow which created an HTTPS trigger that implemented the streaming callable protocol for the Flow.
// The new version is firebase-functions/https which defines onCallFlow
const upgradingHttpsFunction =
backend.isHttpsTriggered(have) && backend.isCallableTriggered(want);
if (wantType !== haveType && !upgradingHttpsFunction) {
throw new FirebaseError(
`[${getFunctionLabel(
want,
Expand Down
29 changes: 29 additions & 0 deletions src/deploy/functions/runtimes/discovery/v1alpha1.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,35 @@ describe("buildFromV1Alpha", () => {
});
});

describe("genkitTriggers", () => {
it("fails with invalid fields", () => {
assertParserError({
endpoints: {
func: {
...MIN_ENDPOINT,
genkitTrigger: {
tool: "tools are not supported",
},
},
},
});
});

it("cannot be used with 1st gen", () => {
assertParserError({
endpoints: {
func: {
...MIN_ENDPOINT,
platform: "gcfv1",
genkitTrigger: {
flow: "agent",
},
},
},
});
});
});

describe("scheduleTriggers", () => {
const validTrigger: build.ScheduleTrigger = {
schedule: "every 5 minutes",
Expand Down
6 changes: 5 additions & 1 deletion src/deploy/functions/runtimes/discovery/v1alpha1.ts
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,9 @@ function assertBuildEndpoint(ep: WireEndpoint, id: string): void {
invoker: "array?",
});
} else if (build.isCallableTriggered(ep)) {
// no-op
assertKeyTypes(prefix + ".callableTrigger", ep.callableTrigger, {
genkitAction: "string?",
});
} else if (build.isScheduleTriggered(ep)) {
assertKeyTypes(prefix + ".scheduleTrigger", ep.scheduleTrigger, {
schedule: "Field<string>",
Expand Down Expand Up @@ -263,6 +265,7 @@ function assertBuildEndpoint(ep: WireEndpoint, id: string): void {
options: "object",
});
} else {
// TODO: Replace with assertExhaustive, which needs some type magic here because we have an any
throw new FirebaseError(
`Do not recognize trigger type for endpoint ${id}. Try upgrading ` +
"firebase-tools with npm install -g firebase-tools@latest",
Expand Down Expand Up @@ -310,6 +313,7 @@ function parseEndpointForBuild(
copyIfPresent(triggered.httpsTrigger, ep.httpsTrigger, "invoker");
} else if (build.isCallableTriggered(ep)) {
triggered = { callableTrigger: {} };
copyIfPresent(triggered.callableTrigger, ep.callableTrigger, "genkitAction");
} else if (build.isScheduleTriggered(ep)) {
const st: build.ScheduleTrigger = {
// TODO: consider adding validation for fields like this that reject
Expand Down
36 changes: 29 additions & 7 deletions src/emulator/dataconnect/pgliteServer.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// https://github.com/supabase-community/pg-gateway

import { PGlite, PGliteOptions } from "@electric-sql/pglite";
import { DebugLevel, PGlite, PGliteOptions } from "@electric-sql/pglite";
// Unfortunately, we need to dynamically import the Postgres extensions.
// They are only available as ESM, and if we import them normally,
// our tsconfig will convert them to requires, which will cause errors
Expand All @@ -18,6 +18,7 @@ import {
import { fromNodeSocket } from "./pg-gateway/platforms/node";
import { logger } from "../../logger";
import { hasMessage } from "../../error";

export const TRUNCATE_TABLES_SQL = `
DO $do$
BEGIN
Expand All @@ -35,8 +36,11 @@ export class PostgresServer {
private database: string;
private dataDirectory?: string;
private importPath?: string;
private debug: DebugLevel;

public db: PGlite | undefined = undefined;
private server: net.Server | undefined = undefined;

public async createPGServer(host: string = "127.0.0.1", port: number): Promise<net.Server> {
const getDb = this.getDb.bind(this);

Expand Down Expand Up @@ -67,6 +71,7 @@ export class PostgresServer {
server.emit("error", err);
});
});
this.server = server;

const listeningPromise = new Promise<void>((resolve) => {
server.listen(port, host, () => {
Expand All @@ -86,7 +91,7 @@ export class PostgresServer {
const pgliteArgs: PGliteOptions = {
username: this.username,
database: this.database,
debug: 0,
debug: this.debug,
extensions: {
vector,
uuidOssp,
Expand Down Expand Up @@ -132,11 +137,28 @@ export class PostgresServer {
}
}

constructor(database: string, username: string, dataDirectory?: string, importPath?: string) {
this.username = username;
this.database = database;
this.dataDirectory = dataDirectory;
this.importPath = importPath;
public async stop(): Promise<void> {
if (this.db) {
await this.db.close();
}
if (this.server) {
this.server.close();
}
return;
}

constructor(args: {
database: string;
username: string;
dataDirectory?: string;
importPath?: string;
debug?: boolean;
}) {
this.username = args.username;
this.database = args.database;
this.dataDirectory = args.dataDirectory;
this.importPath = args.importPath;
this.debug = args.debug ? 5 : 0;
}
}

Expand Down
12 changes: 11 additions & 1 deletion src/emulator/dataconnectEmulator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ export interface DataConnectEmulatorArgs {
enable_output_schema_extensions: boolean;
enable_output_generated_sdk: boolean;
importPath?: string;
debug?: boolean;
}

export interface DataConnectGenerateArgs {
Expand Down Expand Up @@ -116,7 +117,13 @@ export class DataConnectEmulator implements EmulatorInstance {
const postgresDumpPath = this.args.importPath
? path.join(this.args.importPath, "postgres.tar.gz")
: undefined;
this.postgresServer = new PostgresServer(dbId, "postgres", dataDirectory, postgresDumpPath);
this.postgresServer = new PostgresServer({
database: dbId,
username: "fdc",
dataDirectory,
importPath: postgresDumpPath,
debug: this.args.debug,
});
const server = await this.postgresServer.createPGServer(pgHost, pgPort);
const connectableHost = connectableHostname(pgHost);
connStr = `postgres://${connectableHost}:${pgPort}/${dbId}?sslmode=disable`;
Expand Down Expand Up @@ -166,6 +173,9 @@ export class DataConnectEmulator implements EmulatorInstance {
);
return;
}
if (this.postgresServer) {
await this.postgresServer.stop();
}
return stop(Emulators.DATACONNECT);
}

Expand Down
Loading

0 comments on commit c91a23a

Please sign in to comment.