Skip to content
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions firebase-vscode/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
## NEXT

- Fixed the projectless developer experience. There are "error linter", "run (local)" buttons.

## 1.6.1

- Update internal `firebase-tools` dependency to 14.13.0
Expand Down
5 changes: 2 additions & 3 deletions firebase-vscode/src/core/emulators.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@

// Subscription to trigger emulator exports when button is clicked.
this.subscriptions.push(broker.on("runEmulatorsExport", () => {
vscode.commands.executeCommand("firebase.emulators.exportData")

Check warning on line 45 in firebase-vscode/src/core/emulators.ts

View workflow job for this annotation

GitHub Actions / lint (20)

Missing semicolon

Check warning on line 45 in firebase-vscode/src/core/emulators.ts

View workflow job for this annotation

GitHub Actions / lint (20)

Missing semicolon
}));
}

Expand Down Expand Up @@ -179,9 +179,8 @@
}

private getHubClient(): EmulatorHubClient | undefined {
const projectId = firebaseRC.value?.tryReadValue?.projects?.default;
// TODO: think about what to without projectID, in potentially a logged out mode
const hubClient = new EmulatorHubClient(projectId!);
const projectId = firebaseRC.value?.tryReadValue?.projects?.default || "";
Comment thread
yuchenshi marked this conversation as resolved.
Outdated
const hubClient = new EmulatorHubClient(projectId);
if (hubClient.foundHub()) {
return hubClient;
} else {
Expand Down
25 changes: 13 additions & 12 deletions firebase-vscode/src/data-connect/code-lens-provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,8 +71,8 @@ export class OperationCodeLensProvider extends ComputedCodeLensProvider {
): vscode.CodeLens[] {
// Wait for configs to be loaded and emulator to be running
const fdcConfigs = this.watch(dataConnectConfigs)?.tryReadValue;
const rc = this.watch(firebaseRC)?.tryReadValue;
if (!fdcConfigs || !rc) {
const projectId = this.watch(firebaseRC)?.tryReadValue?.projects.default;
if (!fdcConfigs) {
return [];
}

Expand Down Expand Up @@ -117,14 +117,16 @@ export class OperationCodeLensProvider extends ComputedCodeLensProvider {
}),
);

codeLenses.push(
new vscode.CodeLens(range, {
title: `$(play) Run (Production – Project: ${rc.projects.default})`,
command: "firebase.dataConnect.executeOperation",
tooltip: "Execute the operation (⌘+enter or Ctrl+Enter)",
arguments: [x, operationLocation, InstanceType.PRODUCTION],
}),
);
if (projectId) {
codeLenses.push(
new vscode.CodeLens(range, {
title: `$(play) Run (Production – Project: ${projectId})`,
command: "firebase.dataConnect.executeOperation",
tooltip: "Execute the operation (⌘+enter or Ctrl+Enter)",
arguments: [x, operationLocation, InstanceType.PRODUCTION],
}),
);
}
}
}
}
Expand Down Expand Up @@ -201,8 +203,7 @@ export class ConfigureSdkCodeLensProvider extends ComputedCodeLensProvider {
): vscode.CodeLens[] {
// Wait for configs to be loaded
const fdcConfigs = this.watch(dataConnectConfigs)?.tryReadValue;
const rc = this.watch(firebaseRC)?.tryReadValue;
if (!fdcConfigs || !rc) {
if (!fdcConfigs) {
return [];
}

Expand Down
24 changes: 16 additions & 8 deletions firebase-vscode/src/data-connect/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -305,27 +305,35 @@ export class ResolvedDataConnectConfig {
export class ResolvedDataConnectConfigs {
constructor(readonly values: DeepReadOnly<ResolvedDataConnectConfig[]>) {}

get serviceIds() {
get serviceIds(): string[] {
return this.values.map((config) => config.value.serviceId);
}

get allConnectors() {
get allConnectors(): ResolvedConnectorYaml[] {
return this.values.flatMap((dc) => dc.resolvedConnectors);
}

findById(serviceId: string) {
return this.values.find((dc) => dc.value.serviceId === serviceId);
findById(serviceId: string): ResolvedDataConnectConfig {
const dc = this.values.find((dc) => dc.value.serviceId === serviceId);
if (!dc) {
throw new Error(`No dataconnect.yaml with serviceId ${serviceId}. Available: ${this.serviceIds.join(", ")}`);
}
return dc;
}

findEnclosingServiceForPath(filePath: string) {
return this.values.find((dc) => dc.containsPath(filePath));
findEnclosingServiceForPath(filePath: string): ResolvedDataConnectConfig {
const dc = this.values.find((dc) => dc.containsPath(filePath));
if (!dc) {
throw new Error(`No enclosing dataconnect.yaml found for path ${filePath}. Available Paths: ${this.values.map((dc) => dc.path).join(", ")}`);
}
return dc;
}

getApiServicePathByPath(projectId: string, path: string) {
getApiServicePathByPath(projectId: string | undefined, path: string): string {
const dataConnectConfig = this.findEnclosingServiceForPath(path);
const serviceId = dataConnectConfig?.value.serviceId;
const locationId = dataConnectConfig?.dataConnectLocation;
Comment thread
fredzqm marked this conversation as resolved.
return `projects/${projectId}/locations/${locationId}/services/${serviceId}`;
return `projects/${projectId || "p"}/locations/${locationId}/services/${serviceId}`;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we still need this? If so, I'd suggest

Suggested change
return `projects/${projectId || "p"}/locations/${locationId}/services/${serviceId}`;
return `projects/${projectId || "demo-ignored"}/locations/${locationId}/services/${serviceId}`;

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, when .firebaserc is missing. project is undefined here.

Sure, I will use the no-project placeholder.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just add a comment that the Data Connect emulator ignores the project ID and will return the right response pretending that there's only one project

}
}

Expand Down
1 change: 0 additions & 1 deletion firebase-vscode/src/data-connect/execution/execution.ts
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,6 @@ export function registerExecution(

const alwaysExecuteMutationsInProduction =
"alwaysAllowMutationsInProduction";
const alwaysStartEmulator = "alwaysStartEmulator";

// notify users that emulator is starting
if (
Expand Down
20 changes: 7 additions & 13 deletions firebase-vscode/src/data-connect/service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,23 +52,17 @@ export class DataConnectService {
private context: ExtensionContext,
) {}

async servicePath(path: string): Promise<string | undefined> {
async servicePath(path: string): Promise<string> {
const dataConnectConfigsValue = await firstWhereDefined(dataConnectConfigs);
// TODO: avoid calling this here and in getApiServicePathByPath
const serviceId =
dataConnectConfigsValue?.tryReadValue?.findEnclosingServiceForPath(path)
?.value.serviceId;
const projectId = firebaseRC.value?.tryReadValue?.projects?.default;

if (serviceId === undefined || projectId === undefined) {
return undefined;
const dcs = dataConnectConfigsValue?.tryReadValue;
if (!dcs) {
throw new Error("cannot find dataconnect.yaml in the project");
}

return (
dataConnectConfigsValue?.tryReadValue?.getApiServicePathByPath(
projectId,
return ( dcs?.getApiServicePathByPath(
firebaseRC.value?.tryReadValue?.projects?.default,
path,
) || `projects/p/locations/l/services/${serviceId}`
)
);
Comment thread
fredzqm marked this conversation as resolved.
Outdated
}

Expand Down
6 changes: 4 additions & 2 deletions firebase-vscode/src/data-connect/terminal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,8 +92,10 @@ export function registerTerminalTasks(
const startEmulatorsTask = () => {
analyticsLogger.logger.logUsage(DATA_CONNECT_EVENT_NAME.START_EMULATORS);

let cmd = `${settings.firebasePath} emulators:start --project ${currentProjectId.value}`;

let cmd = `${settings.firebasePath} emulators:start`;
if (currentProjectId.value) {
cmd += ` --project ${currentProjectId.value}`;
}
if (settings.importPath) {
cmd += ` --import ${settings.importPath}`;
}
Expand Down
9 changes: 3 additions & 6 deletions firebase-vscode/src/data-connect/toolkit.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import * as vscode from "vscode";
import { ExtensionBrokerImpl } from "../extension-broker";
import { effect } from "@preact/signals-core";
import { firebaseRC } from "../core/config";
import { dataConnectConfigs, firebaseConfig } from "./config";
import { runDataConnectCompiler } from "./core-compiler";
import { DataConnectToolkitController } from "../../../src/emulator/dataconnectToolkitController";
Expand All @@ -19,10 +18,9 @@ export class DataConnectToolkit implements vscode.Disposable {
this.subs.push(
effect(() => {
if (!this.isFDCToolkitRunning()) {
const rc = firebaseRC.value?.tryReadValue;
const config = firebaseConfig.value?.tryReadValue;
if (rc && config) {
this.startFDCToolkit("./dataconnect", config, rc).then(() => {
if (config) {
this.startFDCToolkit("./dataconnect", config).then(() => {
this.connectToToolkit();
});
}
Expand All @@ -35,7 +33,7 @@ export class DataConnectToolkit implements vscode.Disposable {
}

// special function to start FDC emulator with special flags & port
async startFDCToolkit(configDir: string, config: Config, RC: RC) {
async startFDCToolkit(configDir: string, config: Config) {
const port = await findOpenPort(DEFAULT_PORT);
const settings = getSettings();

Expand All @@ -44,7 +42,6 @@ export class DataConnectToolkit implements vscode.Disposable {
listen: [{ address: "localhost", port, family: "IPv4" }],
config,
configDir,
rc: RC,
autoconnectToPostgres: false,
enable_output_generated_sdk: true,
enable_output_schema_extensions: true,
Expand Down
4 changes: 2 additions & 2 deletions src/emulator/controller.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,21 +7,21 @@

function createMockOptions(
only: string | undefined,
configValues: { [key: string]: any },

Check warning on line 10 in src/emulator/controller.spec.ts

View workflow job for this annotation

GitHub Actions / lint (20)

Unexpected any. Specify a different type
): Options {
const config = {
get: (key: string) => configValues[key],

Check warning on line 13 in src/emulator/controller.spec.ts

View workflow job for this annotation

GitHub Actions / lint (20)

Unsafe return of an `any` typed value
has: (key: string) => !!configValues[key],
src: {
emulators: configValues,
functions: configValues.functions,

Check warning on line 17 in src/emulator/controller.spec.ts

View workflow job for this annotation

GitHub Actions / lint (20)

Unsafe assignment of an `any` value
},
};
return {

Check warning on line 20 in src/emulator/controller.spec.ts

View workflow job for this annotation

GitHub Actions / lint (20)

Unsafe return of an `any` typed value
only,
config,
project: "test-project",
} as any;

Check warning on line 24 in src/emulator/controller.spec.ts

View workflow job for this annotation

GitHub Actions / lint (20)

Unexpected any. Specify a different type
}

describe("EmulatorController", () => {
Expand All @@ -38,7 +38,7 @@
await EmulatorRegistry.start(fake);

expect(EmulatorRegistry.isRunning(name)).to.be.true;
expect(EmulatorRegistry.getInfo(name)!.port).to.eql(fake.getInfo().port);

Check warning on line 41 in src/emulator/controller.spec.ts

View workflow job for this annotation

GitHub Actions / lint (20)

Forbidden non-null assertion
});

describe("shouldStart", () => {
Expand All @@ -47,9 +47,9 @@
expect(shouldStart(options, Emulators.HUB)).to.be.true;
});

it("should not start the hub if no project is specified", () => {
it("should start the hub even if no project is specified", () => {
const options = {} as Options;
expect(shouldStart(options, Emulators.HUB)).to.be.false;
expect(shouldStart(options, Emulators.HUB)).to.be.true;
});

it("should start the UI if options.ui is true", () => {
Expand Down
16 changes: 4 additions & 12 deletions src/emulator/controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@

/**
* Exports emulator data on clean exit (SIGINT or process end)
* @param options

Check warning on line 76 in src/emulator/controller.ts

View workflow job for this annotation

GitHub Actions / lint (20)

Missing JSDoc @param "options" description
*/
export async function exportOnExit(options: Options): Promise<void> {
// Note: options.exportOnExit is coerced to a string before this point in commandUtils.ts#setExportOnExitOptions
Expand All @@ -86,7 +86,7 @@
);
await exportEmulatorData(exportOnExitDir, options, /* initiatedBy= */ "exit");
} catch (e: unknown) {
utils.logWarning(`${e}`);

Check warning on line 89 in src/emulator/controller.ts

View workflow job for this annotation

GitHub Actions / lint (20)

Invalid type "unknown" of template literal expression
utils.logWarning(`Automatic export to "${exportOnExitDir}" failed, going to exit now...`);
}
}
Expand All @@ -94,7 +94,7 @@

/**
* Hook to do things when we're exiting cleanly (this does not include errors). Will be skipped on a second SIGINT
* @param options

Check warning on line 97 in src/emulator/controller.ts

View workflow job for this annotation

GitHub Actions / lint (20)

Missing JSDoc @param "options" description
*/
export async function onExit(options: any) {
await exportOnExit(options);
Expand Down Expand Up @@ -148,8 +148,8 @@
*/
export function shouldStart(options: Options, name: Emulators): boolean {
if (name === Emulators.HUB) {
// The hub only starts if we know the project ID.
return !!options.project;
// The emulator hub always starts.
return true;
}
const targets = filterEmulatorTargets(options);
const emulatorInTargets = targets.includes(name);
Expand Down Expand Up @@ -310,7 +310,7 @@
const hubLogger = EmulatorLogger.forEmulator(Emulators.HUB);
hubLogger.logLabeled("BULLET", "emulators", `Starting emulators: ${targets.join(", ")}`);

const projectId: string = getProjectId(options) || ""; // TODO: Next breaking change, consider making this fall back to demo project.
const projectId: string = getProjectId(options) || "";
const isDemoProject = Constants.isDemoProject(projectId);
if (isDemoProject) {
hubLogger.logLabeled(
Expand Down Expand Up @@ -882,7 +882,6 @@
projectId,
auto_download: true,
configDir: config[0].source,
rc: options.rc,
config: options.config,
autoconnectToPostgres: true,
postgresListen: listenForEmulator["dataconnect.postgres"],
Expand Down Expand Up @@ -1104,14 +1103,7 @@
*/
export async function exportEmulatorData(exportPath: string, options: any, initiatedBy: string) {
const projectId = options.project;
if (!projectId) {
throw new FirebaseError(
"Could not determine project ID, make sure you're running in a Firebase project directory or add the --project flag.",
{ exit: 1 },
);
}

const hubClient = new EmulatorHubClient(projectId);
const hubClient = new EmulatorHubClient(projectId || "");
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
const hubClient = new EmulatorHubClient(projectId || "");
const hubClient = new EmulatorHubClient(projectId);

if (!hubClient.foundHub()) {
throw new FirebaseError(
`Did not find any running emulators for project ${clc.bold(projectId)}.`,
Expand Down
2 changes: 0 additions & 2 deletions src/emulator/dataconnectEmulator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ import {
import { EmulatorInfo, EmulatorInstance, Emulators, ListenSpec } from "./types";
import { FirebaseError } from "../error";
import { EmulatorLogger } from "./emulatorLogger";
import { RC } from "../rc";
import { BuildResult, requiresVector } from "../dataconnect/types";
import { listenSpecsToString } from "./portUtils";
import { Client, ClientResponse } from "../apiv2";
Expand All @@ -36,7 +35,6 @@ export interface DataConnectEmulatorArgs {
listen: ListenSpec[];
configDir: string;
auto_download?: boolean;
rc: RC;
config: Config;
autoconnectToPostgres: boolean;
postgresListen?: ListenSpec[];
Expand Down
13 changes: 9 additions & 4 deletions src/emulator/hub.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ export class EmulatorHub extends ExpressBasedEmulator {
* This is useful so that multiple copies of the Firebase CLI can discover
* each other.
*/
static readLocatorFile(projectId: string): Locator | undefined {
static readLocatorFile(projectId: string | undefined): Locator | undefined {
const locatorPath = this.getLocatorFilePath(projectId);
if (!fs.existsSync(locatorPath)) {
return undefined;
Expand All @@ -61,10 +61,15 @@ export class EmulatorHub extends ExpressBasedEmulator {
return locator;
}

static getLocatorFilePath(projectId: string): string {
static getLocatorFilePath(projectId: string | undefined): string {
const dir = os.tmpdir();
const filename = `hub-${projectId}.json`;
return path.join(dir, filename);
let filename = `hub_no_project.json`;
if (projectId) {
filename = `hub-${projectId}.json`;
}
const locatorPath = path.join(dir, filename);
logger.debug(`Emulator locator file path: ${locatorPath}`);
return locatorPath;
}

constructor(private args: EmulatorHubArgs) {
Expand Down
2 changes: 1 addition & 1 deletion src/functionsShellCommandAction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ export const actionFunction = async (options: Options) => {
}

needProjectId(options);
const hubClient = new EmulatorHubClient(options.project!);
const hubClient = new EmulatorHubClient(options.project || "");

let remoteEmulators: Record<string, EmulatorInfo> = {};
if (hubClient.foundHub()) {
Expand Down
5 changes: 1 addition & 4 deletions src/mcp/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -188,10 +188,7 @@ export class FirebaseMcpServer {
return this.emulatorHubClient;
}
const projectId = await this.getProjectId();
if (!projectId) {
return;
}
this.emulatorHubClient = new EmulatorHubClient(projectId);
this.emulatorHubClient = new EmulatorHubClient(projectId || "");
return this.emulatorHubClient;
}

Expand Down
Loading