Skip to content
Merged
Show file tree
Hide file tree
Changes from 8 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
4 changes: 2 additions & 2 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,9 @@
}

private getHubClient(): EmulatorHubClient | undefined {
const projectId = firebaseRC.value?.tryReadValue?.projects?.default;
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 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;
return `projects/${projectId}/locations/${locationId}/services/${serviceId}`;
return `projects/${projectId || "p"}/locations/${locationId}/services/${serviceId}`;
Copy link
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
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
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}`
)
);
}

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
5 changes: 2 additions & 3 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 @@ -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
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
10 changes: 6 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,12 @@ 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);
const filename = `hub-${projectId || "local-demo"}.json`;
const locatorPath = path.join(dir, filename);
logger.debug(`Emulator locator file path: ${locatorPath}`);
return locatorPath;
}

constructor(private args: EmulatorHubArgs) {
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