Skip to content

Commit bbed1b5

Browse files
authored
[VS Code] Fix the no project emulator experience (#9072)
1 parent c3c1c53 commit bbed1b5

File tree

16 files changed

+75
-104
lines changed

16 files changed

+75
-104
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
- `firebase emulator:start` use a default project if no project can be found. (#9072)

firebase-vscode/CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
## NEXT
22

3+
- Fixed the projectless developer experience. There are "error linter", "run (local)" buttons.
4+
35
## 1.6.1
46

57
- Update internal `firebase-tools` dependency to 14.13.0

firebase-vscode/src/core/emulators.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -180,8 +180,7 @@ export class EmulatorsController implements Disposable {
180180

181181
private getHubClient(): EmulatorHubClient | undefined {
182182
const projectId = firebaseRC.value?.tryReadValue?.projects?.default;
183-
// TODO: think about what to without projectID, in potentially a logged out mode
184-
const hubClient = new EmulatorHubClient(projectId!);
183+
const hubClient = new EmulatorHubClient(projectId);
185184
if (hubClient.foundHub()) {
186185
return hubClient;
187186
} else {

firebase-vscode/src/data-connect/code-lens-provider.ts

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -71,8 +71,8 @@ export class OperationCodeLensProvider extends ComputedCodeLensProvider {
7171
): vscode.CodeLens[] {
7272
// Wait for configs to be loaded and emulator to be running
7373
const fdcConfigs = this.watch(dataConnectConfigs)?.tryReadValue;
74-
const rc = this.watch(firebaseRC)?.tryReadValue;
75-
if (!fdcConfigs || !rc) {
74+
const projectId = this.watch(firebaseRC)?.tryReadValue?.projects.default;
75+
if (!fdcConfigs) {
7676
return [];
7777
}
7878

@@ -117,14 +117,16 @@ export class OperationCodeLensProvider extends ComputedCodeLensProvider {
117117
}),
118118
);
119119

120-
codeLenses.push(
121-
new vscode.CodeLens(range, {
122-
title: `$(play) Run (Production – Project: ${rc.projects.default})`,
123-
command: "firebase.dataConnect.executeOperation",
124-
tooltip: "Execute the operation (⌘+enter or Ctrl+Enter)",
125-
arguments: [x, operationLocation, InstanceType.PRODUCTION],
126-
}),
127-
);
120+
if (projectId) {
121+
codeLenses.push(
122+
new vscode.CodeLens(range, {
123+
title: `$(play) Run (Production – Project: ${projectId})`,
124+
command: "firebase.dataConnect.executeOperation",
125+
tooltip: "Execute the operation (⌘+enter or Ctrl+Enter)",
126+
arguments: [x, operationLocation, InstanceType.PRODUCTION],
127+
}),
128+
);
129+
}
128130
}
129131
}
130132
}
@@ -201,8 +203,7 @@ export class ConfigureSdkCodeLensProvider extends ComputedCodeLensProvider {
201203
): vscode.CodeLens[] {
202204
// Wait for configs to be loaded
203205
const fdcConfigs = this.watch(dataConnectConfigs)?.tryReadValue;
204-
const rc = this.watch(firebaseRC)?.tryReadValue;
205-
if (!fdcConfigs || !rc) {
206+
if (!fdcConfigs) {
206207
return [];
207208
}
208209

firebase-vscode/src/data-connect/config.ts

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import { DataConnectMultiple } from "../firebaseConfig";
2020
import path from "path";
2121
import { ExtensionBrokerImpl } from "../extension-broker";
2222
import * as fs from "fs";
23+
import { EmulatorHub } from "../emulator/hub";
2324

2425
export * from "../core/config";
2526

@@ -305,26 +306,37 @@ export class ResolvedDataConnectConfig {
305306
export class ResolvedDataConnectConfigs {
306307
constructor(readonly values: DeepReadOnly<ResolvedDataConnectConfig[]>) {}
307308

308-
get serviceIds() {
309+
get serviceIds(): string[] {
309310
return this.values.map((config) => config.value.serviceId);
310311
}
311312

312-
get allConnectors() {
313+
get allConnectors(): ResolvedConnectorYaml[] {
313314
return this.values.flatMap((dc) => dc.resolvedConnectors);
314315
}
315316

316-
findById(serviceId: string) {
317-
return this.values.find((dc) => dc.value.serviceId === serviceId);
317+
findById(serviceId: string): ResolvedDataConnectConfig {
318+
const dc = this.values.find((dc) => dc.value.serviceId === serviceId);
319+
if (!dc) {
320+
throw new Error(`No dataconnect.yaml with serviceId ${serviceId}. Available: ${this.serviceIds.join(", ")}`);
321+
}
322+
return dc;
318323
}
319324

320-
findEnclosingServiceForPath(filePath: string) {
321-
return this.values.find((dc) => dc.containsPath(filePath));
325+
findEnclosingServiceForPath(filePath: string): ResolvedDataConnectConfig {
326+
const dc = this.values.find((dc) => dc.containsPath(filePath));
327+
if (!dc) {
328+
throw new Error(`No enclosing dataconnect.yaml found for path ${filePath}. Available Paths: ${this.values.map((dc) => dc.path).join(", ")}`);
329+
}
330+
return dc;
322331
}
323332

324-
getApiServicePathByPath(projectId: string, path: string) {
333+
getApiServicePathByPath(projectId: string | undefined, path: string): string {
325334
const dataConnectConfig = this.findEnclosingServiceForPath(path);
326335
const serviceId = dataConnectConfig?.value.serviceId;
327336
const locationId = dataConnectConfig?.dataConnectLocation;
337+
// FDC emulator can service multiple services keyed by serviceId.
338+
// ${projectId} and ${locationId} aren't used to resolve emulator service.
339+
projectId = projectId || EmulatorHub.MISSING_PROJECT_PLACEHOLDER;
328340
return `projects/${projectId}/locations/${locationId}/services/${serviceId}`;
329341
}
330342
}

firebase-vscode/src/data-connect/execution/execution.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,6 @@ export function registerExecution(
136136

137137
const alwaysExecuteMutationsInProduction =
138138
"alwaysAllowMutationsInProduction";
139-
const alwaysStartEmulator = "alwaysStartEmulator";
140139

141140
// notify users that emulator is starting
142141
if (

firebase-vscode/src/data-connect/service.ts

Lines changed: 6 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -52,24 +52,15 @@ export class DataConnectService {
5252
private context: ExtensionContext,
5353
) {}
5454

55-
async servicePath(path: string): Promise<string | undefined> {
55+
async servicePath(path: string): Promise<string> {
5656
const dataConnectConfigsValue = await firstWhereDefined(dataConnectConfigs);
5757
// TODO: avoid calling this here and in getApiServicePathByPath
58-
const serviceId =
59-
dataConnectConfigsValue?.tryReadValue?.findEnclosingServiceForPath(path)
60-
?.value.serviceId;
61-
const projectId = firebaseRC.value?.tryReadValue?.projects?.default;
62-
63-
if (serviceId === undefined || projectId === undefined) {
64-
return undefined;
58+
const dcs = dataConnectConfigsValue?.tryReadValue;
59+
if (!dcs) {
60+
throw new Error("cannot find dataconnect.yaml in the project");
6561
}
66-
67-
return (
68-
dataConnectConfigsValue?.tryReadValue?.getApiServicePathByPath(
69-
projectId,
70-
path,
71-
) || `projects/p/locations/l/services/${serviceId}`
72-
);
62+
const projectId = firebaseRC.value?.tryReadValue?.projects?.default;
63+
return dcs?.getApiServicePathByPath(projectId, path);
7364
}
7465

7566
private async decodeResponse(

firebase-vscode/src/data-connect/terminal.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -92,8 +92,10 @@ export function registerTerminalTasks(
9292
const startEmulatorsTask = () => {
9393
analyticsLogger.logger.logUsage(DATA_CONNECT_EVENT_NAME.START_EMULATORS);
9494

95-
let cmd = `${settings.firebasePath} emulators:start --project ${currentProjectId.value}`;
96-
95+
let cmd = `${settings.firebasePath} emulators:start`;
96+
if (currentProjectId.value) {
97+
cmd += ` --project ${currentProjectId.value}`;
98+
}
9799
if (settings.importPath) {
98100
cmd += ` --import ${settings.importPath}`;
99101
}

firebase-vscode/src/data-connect/toolkit.ts

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import * as vscode from "vscode";
22
import { ExtensionBrokerImpl } from "../extension-broker";
33
import { effect } from "@preact/signals-core";
4-
import { firebaseRC } from "../core/config";
54
import { dataConnectConfigs, firebaseConfig } from "./config";
65
import { runDataConnectCompiler } from "./core-compiler";
76
import { DataConnectToolkitController } from "../../../src/emulator/dataconnectToolkitController";
@@ -19,10 +18,9 @@ export class DataConnectToolkit implements vscode.Disposable {
1918
this.subs.push(
2019
effect(() => {
2120
if (!this.isFDCToolkitRunning()) {
22-
const rc = firebaseRC.value?.tryReadValue;
2321
const config = firebaseConfig.value?.tryReadValue;
24-
if (rc && config) {
25-
this.startFDCToolkit("./dataconnect", config, rc).then(() => {
22+
if (config) {
23+
this.startFDCToolkit("./dataconnect", config).then(() => {
2624
this.connectToToolkit();
2725
});
2826
}
@@ -35,7 +33,7 @@ export class DataConnectToolkit implements vscode.Disposable {
3533
}
3634

3735
// special function to start FDC emulator with special flags & port
38-
async startFDCToolkit(configDir: string, config: Config, RC: RC) {
36+
async startFDCToolkit(configDir: string, config: Config) {
3937
const port = await findOpenPort(DEFAULT_PORT);
4038
const settings = getSettings();
4139

@@ -44,7 +42,6 @@ export class DataConnectToolkit implements vscode.Disposable {
4442
listen: [{ address: "localhost", port, family: "IPv4" }],
4543
config,
4644
configDir,
47-
rc: RC,
4845
autoconnectToPostgres: false,
4946
enable_output_generated_sdk: true,
5047
enable_output_schema_extensions: true,

src/emulator/controller.spec.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -47,9 +47,9 @@ describe("EmulatorController", () => {
4747
expect(shouldStart(options, Emulators.HUB)).to.be.true;
4848
});
4949

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

5555
it("should start the UI if options.ui is true", () => {
@@ -63,10 +63,10 @@ describe("EmulatorController", () => {
6363
expect(shouldStart(options, Emulators.UI)).to.be.true;
6464
});
6565

66-
it("should not start the UI if no project is specified", () => {
66+
it("should start the UI even if no project is specified", () => {
6767
const options = createMockOptions("firestore", { firestore: {} });
6868
delete options.project;
69-
expect(shouldStart(options, Emulators.UI)).to.be.false;
69+
expect(shouldStart(options, Emulators.UI)).to.be.true;
7070
});
7171

7272
it("should not start the UI if no UI-supported emulator is running", () => {

0 commit comments

Comments
 (0)