Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
- `firebase emulator:start` use a default project if no project can be found. (#9072)
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
- `firebase emulator:start` use a default project if no project can be found. (#9072)
- Changed `firebase emulator:start` to use a default project `demo-no-project` if no project can be found. (#9072)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Err, my bad. Shouldn't have enable auto-merge.

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
3 changes: 1 addition & 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 @@ -180,8 +180,7 @@

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 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
26 changes: 19 additions & 7 deletions firebase-vscode/src/data-connect/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import { DataConnectMultiple } from "../firebaseConfig";
import path from "path";
import { ExtensionBrokerImpl } from "../extension-broker";
import * as fs from "fs";
import { EmulatorHub } from "../emulator/hub";

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

Expand Down Expand Up @@ -305,26 +306,37 @@ 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;
// FDC emulator can service multiple services keyed by serviceId.
// ${projectId} and ${locationId} aren't used to resolve emulator service.
projectId = projectId || EmulatorHub.MISSING_PROJECT_PLACEHOLDER;
return `projects/${projectId}/locations/${locationId}/services/${serviceId}`;
}
}
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
21 changes: 6 additions & 15 deletions firebase-vscode/src/data-connect/service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,24 +52,15 @@ 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,
path,
) || `projects/p/locations/l/services/${serviceId}`
);
const projectId = firebaseRC.value?.tryReadValue?.projects?.default;
return dcs?.getApiServicePathByPath(projectId, path);
}

private async decodeResponse(
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
8 changes: 4 additions & 4 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 All @@ -63,10 +63,10 @@
expect(shouldStart(options, Emulators.UI)).to.be.true;
});

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

it("should not start the UI if no UI-supported emulator is running", () => {
Expand Down
56 changes: 11 additions & 45 deletions src/emulator/controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
import { Constants, FIND_AVAILBLE_PORT_BY_DEFAULT } from "./constants";
import { EmulatableBackend, FunctionsEmulator } from "./functionsEmulator";
import { FirebaseError } from "../error";
import { getProjectId, needProjectId, getAliases, needProjectNumber } from "../projectUtils";
import { getProjectId, getAliases, needProjectNumber } from "../projectUtils";
import * as commandUtils from "./commandUtils";
import { EmulatorHub } from "./hub";
import { ExportMetadata, HubExport } from "./hubExport";
Expand Down 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 All @@ -166,9 +166,7 @@
}
// Emulator UI only starts if we know the project ID AND at least one
// emulator supported by Emulator UI is launching.
return (
!!options.project && targets.some((target) => EMULATORS_SUPPORTED_BY_UI.includes(target))
);
return targets.some((target) => EMULATORS_SUPPORTED_BY_UI.includes(target));
}

// Don't start the functions emulator if we can't validate the functions config
Expand Down Expand Up @@ -310,7 +308,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 = getProjectId(options) || EmulatorHub.MISSING_PROJECT_PLACEHOLDER;
const isDemoProject = Constants.isDemoProject(projectId);
if (isDemoProject) {
hubLogger.logLabeled(
Expand Down Expand Up @@ -579,7 +577,6 @@
}
const functionsLogger = EmulatorLogger.forEmulator(Emulators.FUNCTIONS);
const functionsAddr = legacyGetFirstAddr(Emulators.FUNCTIONS);
const projectId = needProjectId(options);

const inspectFunctions = commandUtils.parseInspectionPort(options);
if (inspectFunctions) {
Expand Down Expand Up @@ -723,16 +720,8 @@

// undefined in the config defaults to setting single_project_mode.
if (singleProjectModeEnabled) {
if (projectId) {
args.single_project_mode = true;
args.single_project_mode_error = false;
} else {
firestoreLogger.logLabeled(
"DEBUG",
"firestore",
"Could not enable single_project_mode: missing projectId.",
);
}
args.single_project_mode = true;
args.single_project_mode_error = false;
}

const firestoreEmulator = new FirestoreEmulator(args);
Expand Down Expand Up @@ -822,14 +811,6 @@
}

if (listenForEmulator.auth) {
if (!projectId) {
throw new FirebaseError(
`Cannot start the ${Constants.description(
Emulators.AUTH,
)} without a project: run 'firebase init' or provide the --project flag`,
);
}

const authAddr = legacyGetFirstAddr(Emulators.AUTH);
const authEmulator = new AuthEmulator({
host: authAddr.host,
Expand All @@ -851,12 +832,6 @@
}

if (listenForEmulator.pubsub) {
if (!projectId) {
throw new FirebaseError(
"Cannot start the Pub/Sub emulator without a project: run 'firebase init' or provide the --project flag",
);
}

const pubsubAddr = legacyGetFirstAddr(Emulators.PUBSUB);
const pubsubEmulator = new PubsubEmulator({
host: pubsubAddr.host,
Expand All @@ -882,7 +857,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 @@ -941,7 +915,7 @@
const storageEmulator = new StorageEmulator({
host: storageAddr.host,
port: storageAddr.port,
projectId: projectId,
projectId,
rules: getStorageRulesConfig(projectId, options),
});
await startEmulator(storageEmulator);
Expand Down Expand Up @@ -1030,15 +1004,14 @@
hubLogger.logLabeled(
"WARN",
"emulators",
"The Emulator UI is not starting, either because none of the running " +
"emulators have a UI component or the Emulator UI cannot " +
"determine the Project ID. Pass the --project flag to specify a project.",
"The Emulator UI is not starting because none of the running " +
"emulators have a UI component.",
);
}

if (listenForEmulator.ui) {
const ui = new EmulatorUI({
projectId: projectId,
projectId,
listen: listenForEmulator[Emulators.UI],
});
await startEmulator(ui);
Expand Down Expand Up @@ -1104,13 +1077,6 @@
*/
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);
if (!hubClient.foundHub()) {
throw new FirebaseError(
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
Loading
Loading