From e57eb7af29e47e34e2c2186d28510a3210cbe233 Mon Sep 17 00:00:00 2001 From: Chris Thompson Date: Fri, 1 Nov 2024 15:13:50 -0700 Subject: [PATCH 01/13] Moving the emulator UI server to firebase-tools. --- src/emulator/ui.ts | 44 ++++++++++++-- src/emulator/ui/server.ts | 120 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 160 insertions(+), 4 deletions(-) create mode 100644 src/emulator/ui/server.ts diff --git a/src/emulator/ui.ts b/src/emulator/ui.ts index 387f4be9765..df6460b2a58 100644 --- a/src/emulator/ui.ts +++ b/src/emulator/ui.ts @@ -2,9 +2,11 @@ import { EmulatorInstance, EmulatorInfo, Emulators, ListenSpec } from "./types"; import * as downloadableEmulators from "./downloadableEmulators"; import { EmulatorRegistry } from "./registry"; import { FirebaseError } from "../error"; +import { EmulatorLogger } from "./emulatorLogger"; import { Constants } from "./constants"; import { emulatorSession } from "../track"; import { ExpressBasedEmulator } from "./ExpressBasedEmulator"; +import { createApp } from "./ui/server"; import { ALL_EXPERIMENTS, ExperimentName, isEnabled } from "../experiments"; export interface EmulatorUIOptions { @@ -16,7 +18,7 @@ export interface EmulatorUIOptions { export class EmulatorUI implements EmulatorInstance { constructor(private args: EmulatorUIOptions) {} - start(): Promise { + async start(): Promise { if (!EmulatorRegistry.isRunning(Emulators.HUB)) { throw new FirebaseError( `Cannot start ${Constants.description(Emulators.UI)} without ${Constants.description( @@ -39,9 +41,43 @@ export class EmulatorUI implements EmulatorInstance { const enabledExperiments = (Object.keys(ALL_EXPERIMENTS) as Array).filter( (experimentName) => isEnabled(experimentName), ); - env[Constants.FIREBASE_ENABLED_EXPERIMENTS] = JSON.stringify(enabledExperiments); + env[Constants.FIREBASE_ENABLED_EXPERIMENTS] = JSON.stringify(enabledExperiments); // FIXME pass in GA info - return downloadableEmulators.start(Emulators.UI, { auto_download: autoDownload }, env); + // return downloadableEmulators.start(Emulators.UI, { auto_download: autoDownload }, env); + const downloadDetails = downloadableEmulators.DownloadDetails[Emulators.UI]; + const logger = EmulatorLogger.forEmulator(Emulators.UI); + await downloadableEmulators.downloadIfNecessary(Emulators.UI); + + // FIXME check CI + // const hasEmulator = fs.existsSync(getExecPath(Emulators.UI)); + // if (!hasEmulator || downloadDetails.opts.skipCache) { + // if (args.auto_download) { + // if (process.env.CI) { + // utils.logWarning( + // `It appears you are running in a CI environment. You can avoid downloading the ${Constants.description( + // targetName, + // )} repeatedly by caching the ${downloadDetails.opts.cacheDir} directory.`, + // ); + // } + + // await downloadEmulator(targetName); + // } else { + // utils.logWarning("Setup required, please run: firebase setup:emulators:" + targetName); + // throw new FirebaseError("emulator not found"); + // } + // } + + // const command = _getCommand(targetName, args); + // logger.log( + // "DEBUG", + // `Starting ${Constants.description(targetName)} with command ${JSON.stringify(command)}`, + // ); + // return _runBinary(emulator, command, extraEnv); + // FIXME do some logging probs + + // FIXME consider the host may be different? Should take it from the config methinks +//export function createApp(zipDirPath: string, env : ("DEV" | "PROD"), projectId : string, host : string | undefined, port: string | undefined, hubHost: string ) : Promise { + await createApp("path", "PROD", projectId, "127.0.0.1", Constants.getDefaultPort(Emulators.UI), EmulatorRegistry.url(Emulators.HUB).host); } connect(): Promise { @@ -49,7 +85,7 @@ export class EmulatorUI implements EmulatorInstance { } stop(): Promise { - return downloadableEmulators.stop(Emulators.UI); + return downloadableEmulators.stop(Emulators.UI); // FIXME consider stop } getInfo(): EmulatorInfo { diff --git a/src/emulator/ui/server.ts b/src/emulator/ui/server.ts new file mode 100644 index 00000000000..14dfce7df9e --- /dev/null +++ b/src/emulator/ui/server.ts @@ -0,0 +1,120 @@ +/** + * Copyright 2022 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { createServer } from 'http'; +import type { ListenOptions } from 'net'; +import * as path from 'path'; + +import express from 'express'; +import fetch from 'node-fetch'; + +/* + This file defines Node.js server-side logic for the Emulator UI. + + During development, the express app is loaded into the Vite dev server + (configured via ./vite.config.ts) and exposes the /api/* endpoints below. + + For production, this file serves as an entry point and runs additional logic + (see `import.meta.env.PROD` below) to start the server on a port which serves + static assets in addition to APIs. + + This file may NOT import any front-end code or types from src/. +*/ +// FIXME note to self zipdirpath is - check server.ts in firebase-tools-ui +export function createApp(zipDirPath: string, env : ("DEV" | "PROD"), projectId : string, host : string | undefined, port: number, hubHost: string ) : Promise { + const app = express(); + + // Exposes the host and port of various emulators to facilitate accessing + // them using client SDKs. For features that involve multiple emulators or + // hard to accomplish using client SDKs, consider adding an API below. + app.get( + '/api/config', + jsonHandler(async () => { + const hubDiscoveryUrl = new URL(`http://${hubHost}/emulators`); + const emulatorsRes = await fetch(hubDiscoveryUrl.toString()); + const emulators = (await emulatorsRes.json()) as any; + + const json = { projectId, experiments: [], ...emulators }; + + // Googlers: see go/firebase-emulator-ui-usage-collection-design?pli=1#heading=h.jwz7lj6r67z8 + // for more detail + if (process.env.FIREBASE_GA_SESSION) { + json.analytics = JSON.parse(process.env.FIREBASE_GA_SESSION); + } + + // pick up any experiments enabled with `firebase experiment:enable` + if (process.env.FIREBASE_ENABLED_EXPERIMENTS) { + json.experiments = JSON.parse(process.env.FIREBASE_ENABLED_EXPERIMENTS); + } + + return json; + }) + ); + + if (env == "PROD") { + const webDir = path.join(path.dirname(zipDirPath), '..', 'client'); + app.use(express.static(webDir)); + // Required for the router to work properly. + app.get('*', function (_, res) { + res.sendFile(path.join(webDir, 'index.html')); + }); + + let listen: ListenOptions[]; + if (process.env.LISTEN) { // FIXME what is this + listen = JSON.parse(process.env.LISTEN); + } else { + // Mainly used when starting in dev mode (without CLI). + host = host || '127.0.0.1'; + const portValue = Number(port) || 5173; + listen = [{ host, port: portValue }]; + } + for (const opts of listen) { + const server = createServer(app).listen(opts); + server.once('listening', () => { + console.log(`Web / API server started at ${opts.host}:${opts.port}`); + }); + server.once('error', (err) => { + console.error(`Failed to start server at ${opts.host}:${opts.port}`); + console.error(err); + if (opts === listen[0]) { + // If we failed to listen on the primary address, surface the error. + process.exit(1); + } + }); + } + } + + return Promise.resolve(app) +} +function jsonHandler( + handler: (req: express.Request) => Promise +): express.Handler { + return (req, res) => { + handler(req).then( + (body) => { + res.status(200).json(body); + }, + (err) => { + console.error(err); + res.status(500).json({ + message: err.message, + stack: err.stack, + raw: err, + }); + } + ); + }; +} From bf59e014ba9ff61efff1b7e6564cff7ad83d6a04 Mon Sep 17 00:00:00 2001 From: Chris Thompson Date: Wed, 6 Nov 2024 17:00:20 -0800 Subject: [PATCH 02/13] Should be good --- src/emulator/controller.ts | 2 +- src/emulator/downloadableEmulators.ts | 4 +- src/emulator/ui.ts | 70 +++++----------- src/emulator/ui/server.ts | 114 +++++++++++++------------- 4 files changed, 83 insertions(+), 107 deletions(-) diff --git a/src/emulator/controller.ts b/src/emulator/controller.ts index 5708097f97d..8491079d669 100755 --- a/src/emulator/controller.ts +++ b/src/emulator/controller.ts @@ -305,7 +305,7 @@ export async function startAll( } const hubLogger = EmulatorLogger.forEmulator(Emulators.HUB); - hubLogger.logLabeled("BULLET", "emulators", `Starting emulators: ${targets.join(", ")}`); + hubLogger.logLabeled("BULLET", "emulators", `LINKED: Starting emulators: ${targets.join(", ")}`); const projectId: string = getProjectId(options) || ""; // TODO: Next breaking change, consider making this fall back to demo project. const isDemoProject = Constants.isDemoProject(projectId); diff --git a/src/emulator/downloadableEmulators.ts b/src/emulator/downloadableEmulators.ts index ab8c906f305..995651ca904 100755 --- a/src/emulator/downloadableEmulators.ts +++ b/src/emulator/downloadableEmulators.ts @@ -280,7 +280,7 @@ const Commands: { [s in DownloadableEmulators]: DownloadableEmulatorCommand } = joinArgs: true, shell: true, }, - ui: { + ui: { // FIXME binary: "node", args: [getExecPath(Emulators.UI)], optionalArgs: [], @@ -527,7 +527,7 @@ async function _runBinary( */ export function getDownloadDetails(emulator: DownloadableEmulators): EmulatorDownloadDetails { const details = DownloadDetails[emulator]; - const pathOverride = process.env[`${emulator.toUpperCase()}_EMULATOR_BINARY_PATH`]; + const pathOverride = process.env[`${emulator.toUpperCase()}_EMULATOR_BINARY_PATH`]; // FIXME care about this for UI? if (pathOverride) { const logger = EmulatorLogger.forEmulator(emulator); logger.logLabeled( diff --git a/src/emulator/ui.ts b/src/emulator/ui.ts index df6460b2a58..6c94c8643bb 100644 --- a/src/emulator/ui.ts +++ b/src/emulator/ui.ts @@ -8,6 +8,7 @@ import { emulatorSession } from "../track"; import { ExpressBasedEmulator } from "./ExpressBasedEmulator"; import { createApp } from "./ui/server"; import { ALL_EXPERIMENTS, ExperimentName, isEnabled } from "../experiments"; +import { createDestroyer } from "../utils"; export interface EmulatorUIOptions { listen: ListenSpec[]; @@ -16,7 +17,9 @@ export interface EmulatorUIOptions { } export class EmulatorUI implements EmulatorInstance { - constructor(private args: EmulatorUIOptions) {} + + private destroyServer?: () => Promise; + constructor(private args: EmulatorUIOptions) { } async start(): Promise { if (!EmulatorRegistry.isRunning(Emulators.HUB)) { @@ -26,58 +29,24 @@ export class EmulatorUI implements EmulatorInstance { )}!`, ); } - const { auto_download: autoDownload, projectId } = this.args; - const env: Partial = { - LISTEN: JSON.stringify(ExpressBasedEmulator.listenOptionsFromSpecs(this.args.listen)), - GCLOUD_PROJECT: projectId, - [Constants.FIREBASE_EMULATOR_HUB]: EmulatorRegistry.url(Emulators.HUB).host, - }; + const { projectId } = this.args; - const session = emulatorSession(); - if (session) { - env[Constants.FIREBASE_GA_SESSION] = JSON.stringify(session); - } - - const enabledExperiments = (Object.keys(ALL_EXPERIMENTS) as Array).filter( + // FIXME Question: do we need GCLOUD_PROJECT: projectId, for anything? + const enabledExperiments: Array = (Object.keys(ALL_EXPERIMENTS) as Array).filter( (experimentName) => isEnabled(experimentName), ); - env[Constants.FIREBASE_ENABLED_EXPERIMENTS] = JSON.stringify(enabledExperiments); // FIXME pass in GA info - - // return downloadableEmulators.start(Emulators.UI, { auto_download: autoDownload }, env); - const downloadDetails = downloadableEmulators.DownloadDetails[Emulators.UI]; - const logger = EmulatorLogger.forEmulator(Emulators.UI); - await downloadableEmulators.downloadIfNecessary(Emulators.UI); + const downloadDetails = downloadableEmulators.DownloadDetails[Emulators.UI]; // FIXME Question: use getDownloadDetails to re-use path override? idk + await downloadableEmulators.downloadIfNecessary(Emulators.UI); - // FIXME check CI - // const hasEmulator = fs.existsSync(getExecPath(Emulators.UI)); - // if (!hasEmulator || downloadDetails.opts.skipCache) { - // if (args.auto_download) { - // if (process.env.CI) { - // utils.logWarning( - // `It appears you are running in a CI environment. You can avoid downloading the ${Constants.description( - // targetName, - // )} repeatedly by caching the ${downloadDetails.opts.cacheDir} directory.`, - // ); - // } + const server = await createApp( + downloadDetails.unzipDir!!, + projectId, + EmulatorRegistry.url(Emulators.HUB).host, + emulatorSession(), + ExpressBasedEmulator.listenOptionsFromSpecs(this.args.listen), + enabledExperiments); + this.destroyServer = createDestroyer(server); - // await downloadEmulator(targetName); - // } else { - // utils.logWarning("Setup required, please run: firebase setup:emulators:" + targetName); - // throw new FirebaseError("emulator not found"); - // } - // } - - // const command = _getCommand(targetName, args); - // logger.log( - // "DEBUG", - // `Starting ${Constants.description(targetName)} with command ${JSON.stringify(command)}`, - // ); - // return _runBinary(emulator, command, extraEnv); - // FIXME do some logging probs - - // FIXME consider the host may be different? Should take it from the config methinks -//export function createApp(zipDirPath: string, env : ("DEV" | "PROD"), projectId : string, host : string | undefined, port: string | undefined, hubHost: string ) : Promise { - await createApp("path", "PROD", projectId, "127.0.0.1", Constants.getDefaultPort(Emulators.UI), EmulatorRegistry.url(Emulators.HUB).host); } connect(): Promise { @@ -85,7 +54,10 @@ export class EmulatorUI implements EmulatorInstance { } stop(): Promise { - return downloadableEmulators.stop(Emulators.UI); // FIXME consider stop + if (this.destroyServer) { + return this.destroyServer(); + } + return Promise.resolve(); } getInfo(): EmulatorInfo { diff --git a/src/emulator/ui/server.ts b/src/emulator/ui/server.ts index 14dfce7df9e..11af3efb5b5 100644 --- a/src/emulator/ui/server.ts +++ b/src/emulator/ui/server.ts @@ -18,8 +18,11 @@ import { createServer } from 'http'; import type { ListenOptions } from 'net'; import * as path from 'path'; -import express from 'express'; +import * as express from "express"; import fetch from 'node-fetch'; +import { AnalyticsSession } from "../../track"; +import { ExperimentName } from "../../experiments"; +import * as http from "http"; /* This file defines Node.js server-side logic for the Emulator UI. @@ -33,72 +36,73 @@ import fetch from 'node-fetch'; This file may NOT import any front-end code or types from src/. */ -// FIXME note to self zipdirpath is - check server.ts in firebase-tools-ui -export function createApp(zipDirPath: string, env : ("DEV" | "PROD"), projectId : string, host : string | undefined, port: number, hubHost: string ) : Promise { - const app = express(); +// FIXME note that this is a facsimile class, and some items are passed in rather than fetched on purpose to keep logic similar to the local instance. +export async function createApp( + zipDirPath: string, + projectId: string, + hubHost: string, + emulatorGaSession: AnalyticsSession | undefined, + listenOptions: ListenOptions[], + experiments: Array): Promise { - // Exposes the host and port of various emulators to facilitate accessing - // them using client SDKs. For features that involve multiple emulators or - // hard to accomplish using client SDKs, consider adding an API below. - app.get( + const app = express(); + // Exposes the host and port of various emulators to facilitate accessing + // them using client SDKs. For features that involve multiple emulators or + // hard to accomplish using client SDKs, consider adding an API below. + app.get( '/api/config', jsonHandler(async () => { - const hubDiscoveryUrl = new URL(`http://${hubHost}/emulators`); - const emulatorsRes = await fetch(hubDiscoveryUrl.toString()); - const emulators = (await emulatorsRes.json()) as any; + const hubDiscoveryUrl = new URL(`http://${hubHost}/emulators`); + const emulatorsRes = await fetch(hubDiscoveryUrl.toString()); + const emulators = (await emulatorsRes.json()) as any; - const json = { projectId, experiments: [], ...emulators }; + const json = { projectId, experiments: [], ...emulators }; - // Googlers: see go/firebase-emulator-ui-usage-collection-design?pli=1#heading=h.jwz7lj6r67z8 - // for more detail - if (process.env.FIREBASE_GA_SESSION) { - json.analytics = JSON.parse(process.env.FIREBASE_GA_SESSION); - } + // Googlers: see go/firebase-emulator-ui-usage-collection-design?pli=1#heading=h.jwz7lj6r67z8 + // for more detail + if (emulatorGaSession) { + json.analytics = emulatorGaSession; + } - // pick up any experiments enabled with `firebase experiment:enable` - if (process.env.FIREBASE_ENABLED_EXPERIMENTS) { - json.experiments = JSON.parse(process.env.FIREBASE_ENABLED_EXPERIMENTS); - } + // pick up any experiments enabled with `firebase experiment:enable` + if (experiments) { + json.experiments = experiments; + } - return json; + return json; }) - ); + ); - if (env == "PROD") { - const webDir = path.join(path.dirname(zipDirPath), '..', 'client'); - app.use(express.static(webDir)); - // Required for the router to work properly. - app.get('*', function (_, res) { - res.sendFile(path.join(webDir, 'index.html')); - }); + const webDir = path.join(zipDirPath, 'client'); + app.use(express.static(webDir)); + // Required for the router to work properly. + app.get('*', function (_, res) { + res.sendFile(path.join(webDir, 'index.html')); + }); - let listen: ListenOptions[]; - if (process.env.LISTEN) { // FIXME what is this - listen = JSON.parse(process.env.LISTEN); - } else { - // Mainly used when starting in dev mode (without CLI). - host = host || '127.0.0.1'; - const portValue = Number(port) || 5173; - listen = [{ host, port: portValue }]; - } - for (const opts of listen) { - const server = createServer(app).listen(opts); - server.once('listening', () => { - console.log(`Web / API server started at ${opts.host}:${opts.port}`); - }); - server.once('error', (err) => { - console.error(`Failed to start server at ${opts.host}:${opts.port}`); - console.error(err); - if (opts === listen[0]) { - // If we failed to listen on the primary address, surface the error. - process.exit(1); - } - }); - } - } + if(listenOptions.length == 0) { + console.error(`Failed to start UI server, listenOptions empty`); + process.exit(1); + } + var server = null; + for (const opts of listenOptions) { + server = createServer(app).listen(opts); + server.once('listening', () => { + console.log(`Web / API server started at ${opts.host}:${opts.port}`); + }); + server.once('error', (err) => { + console.error(`Failed to start server at ${opts.host}:${opts.port}`); + console.error(err); + if (opts === listenOptions[0]) { + // If we failed to listen on the primary address, surface the error. + process.exit(1); + } + }); + } - return Promise.resolve(app) + return server!! } + function jsonHandler( handler: (req: express.Request) => Promise ): express.Handler { From b05a567447ab8911f2e06c95dd0102100e6ad2d6 Mon Sep 17 00:00:00 2001 From: Chris Thompson Date: Wed, 6 Nov 2024 17:11:10 -0800 Subject: [PATCH 03/13] More --- src/emulator/controller.ts | 2 +- src/emulator/downloadableEmulators.ts | 6 +++--- src/emulator/types.ts | 1 + src/emulator/ui/server.ts | 10 +--------- 4 files changed, 6 insertions(+), 13 deletions(-) diff --git a/src/emulator/controller.ts b/src/emulator/controller.ts index 8491079d669..5708097f97d 100755 --- a/src/emulator/controller.ts +++ b/src/emulator/controller.ts @@ -305,7 +305,7 @@ export async function startAll( } const hubLogger = EmulatorLogger.forEmulator(Emulators.HUB); - hubLogger.logLabeled("BULLET", "emulators", `LINKED: Starting emulators: ${targets.join(", ")}`); + 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 isDemoProject = Constants.isDemoProject(projectId); diff --git a/src/emulator/downloadableEmulators.ts b/src/emulator/downloadableEmulators.ts index 995651ca904..36895bb72fa 100755 --- a/src/emulator/downloadableEmulators.ts +++ b/src/emulator/downloadableEmulators.ts @@ -280,9 +280,9 @@ const Commands: { [s in DownloadableEmulators]: DownloadableEmulatorCommand } = joinArgs: true, shell: true, }, - ui: { // FIXME - binary: "node", - args: [getExecPath(Emulators.UI)], + ui: { + binary: "", + args: [], optionalArgs: [], joinArgs: false, shell: false, diff --git a/src/emulator/types.ts b/src/emulator/types.ts index ddea1aa82b1..e8ae15ba5bc 100644 --- a/src/emulator/types.ts +++ b/src/emulator/types.ts @@ -27,6 +27,7 @@ export type DownloadableEmulators = | Emulators.UI | Emulators.STORAGE | Emulators.DATACONNECT; + export const DOWNLOADABLE_EMULATORS = [ Emulators.FIRESTORE, Emulators.DATABASE, diff --git a/src/emulator/ui/server.ts b/src/emulator/ui/server.ts index 11af3efb5b5..458afe31b4c 100644 --- a/src/emulator/ui/server.ts +++ b/src/emulator/ui/server.ts @@ -27,16 +27,8 @@ import * as http from "http"; /* This file defines Node.js server-side logic for the Emulator UI. - During development, the express app is loaded into the Vite dev server - (configured via ./vite.config.ts) and exposes the /api/* endpoints below. - - For production, this file serves as an entry point and runs additional logic - (see `import.meta.env.PROD` below) to start the server on a port which serves - static assets in addition to APIs. - - This file may NOT import any front-end code or types from src/. + It is a facsimile class of the local server in firebase-tools-ui/server.ts. */ -// FIXME note that this is a facsimile class, and some items are passed in rather than fetched on purpose to keep logic similar to the local instance. export async function createApp( zipDirPath: string, projectId: string, From 5e53d7d0d8dc3623e5191fa0a348075c6ba771fa Mon Sep 17 00:00:00 2001 From: Chris Thompson Date: Wed, 6 Nov 2024 17:17:06 -0800 Subject: [PATCH 04/13] ok --- src/emulator/ui/server.ts | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/emulator/ui/server.ts b/src/emulator/ui/server.ts index 458afe31b4c..af547cfd0ce 100644 --- a/src/emulator/ui/server.ts +++ b/src/emulator/ui/server.ts @@ -1,5 +1,5 @@ /** - * Copyright 2022 Google LLC + * Copyright 2024 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -27,7 +27,9 @@ import * as http from "http"; /* This file defines Node.js server-side logic for the Emulator UI. - It is a facsimile class of the local server in firebase-tools-ui/server.ts. + It is a facsimile class of the local server in firebase-tools-ui/server.ts. For that reason, + values that were previously environment variables are passed in rather than fetched/derived + in the class itself. */ export async function createApp( zipDirPath: string, @@ -72,11 +74,11 @@ export async function createApp( res.sendFile(path.join(webDir, 'index.html')); }); - if(listenOptions.length == 0) { + if (listenOptions.length == 0) { console.error(`Failed to start UI server, listenOptions empty`); process.exit(1); } - var server = null; + var server = null; for (const opts of listenOptions) { server = createServer(app).listen(opts); server.once('listening', () => { From b94a3d6416f1554dda90f49b84267490b21c7f89 Mon Sep 17 00:00:00 2001 From: Chris Thompson Date: Thu, 7 Nov 2024 15:25:19 -0800 Subject: [PATCH 05/13] All destroyers --- src/emulator/ui.ts | 18 ++++++++++-------- src/emulator/ui/server.ts | 33 +++++++++++++++++---------------- 2 files changed, 27 insertions(+), 24 deletions(-) diff --git a/src/emulator/ui.ts b/src/emulator/ui.ts index 6c94c8643bb..1cd259efa38 100644 --- a/src/emulator/ui.ts +++ b/src/emulator/ui.ts @@ -18,7 +18,7 @@ export interface EmulatorUIOptions { export class EmulatorUI implements EmulatorInstance { - private destroyServer?: () => Promise; + private destroyers = new Set<() => Promise>(); constructor(private args: EmulatorUIOptions) { } async start(): Promise { @@ -38,26 +38,28 @@ export class EmulatorUI implements EmulatorInstance { const downloadDetails = downloadableEmulators.DownloadDetails[Emulators.UI]; // FIXME Question: use getDownloadDetails to re-use path override? idk await downloadableEmulators.downloadIfNecessary(Emulators.UI); - const server = await createApp( + const servers = await createApp( downloadDetails.unzipDir!!, projectId, EmulatorRegistry.url(Emulators.HUB).host, emulatorSession(), ExpressBasedEmulator.listenOptionsFromSpecs(this.args.listen), enabledExperiments); - this.destroyServer = createDestroyer(server); - + servers.forEach((server) => { + this.destroyers.add(createDestroyer(server)); + }); } connect(): Promise { return Promise.resolve(); } - stop(): Promise { - if (this.destroyServer) { - return this.destroyServer(); + async stop(): Promise { + const promises = []; + for (const destroyer of this.destroyers) { + promises.push(destroyer().then(() => this.destroyers.delete(destroyer))); } - return Promise.resolve(); + await Promise.all(promises); } getInfo(): EmulatorInfo { diff --git a/src/emulator/ui/server.ts b/src/emulator/ui/server.ts index af547cfd0ce..5689ff039d5 100644 --- a/src/emulator/ui/server.ts +++ b/src/emulator/ui/server.ts @@ -14,15 +14,17 @@ * limitations under the License. */ -import { createServer } from 'http'; -import type { ListenOptions } from 'net'; -import * as path from 'path'; +import * as http from "http"; +import type { ListenOptions } from "net"; +import * as path from "path"; import * as express from "express"; -import fetch from 'node-fetch'; +import fetch from "node-fetch"; import { AnalyticsSession } from "../../track"; import { ExperimentName } from "../../experiments"; -import * as http from "http"; +import { FirebaseError } from "../../error"; +import { EmulatorLogger } from "../emulatorLogger"; +import { Emulators } from "../types"; /* This file defines Node.js server-side logic for the Emulator UI. @@ -37,7 +39,7 @@ export async function createApp( hubHost: string, emulatorGaSession: AnalyticsSession | undefined, listenOptions: ListenOptions[], - experiments: Array): Promise { + experiments: Array): Promise { const app = express(); // Exposes the host and port of various emulators to facilitate accessing @@ -75,26 +77,25 @@ export async function createApp( }); if (listenOptions.length == 0) { - console.error(`Failed to start UI server, listenOptions empty`); - process.exit(1); + throw new FirebaseError("Failed to start UI server, listenOptions empty"); } - var server = null; + var servers : http.Server[] = []; for (const opts of listenOptions) { - server = createServer(app).listen(opts); + var server = http.createServer(app).listen(opts) + servers.push(server); server.once('listening', () => { - console.log(`Web / API server started at ${opts.host}:${opts.port}`); + EmulatorLogger.forEmulator(Emulators.UI).log("DEBUG", `Web / API server started at ${opts.host}:${opts.port}`); }); server.once('error', (err) => { - console.error(`Failed to start server at ${opts.host}:${opts.port}`); - console.error(err); + EmulatorLogger.forEmulator(Emulators.UI).log("ERROR", `Failed to start server at ${opts.host}:${opts.port}. ${err}`); if (opts === listenOptions[0]) { // If we failed to listen on the primary address, surface the error. - process.exit(1); + throw new FirebaseError(`Failed to start server for the primary address at ${opts.host}:${opts.port}`); } }); } - return server!! + return servers } function jsonHandler( @@ -106,7 +107,7 @@ function jsonHandler( res.status(200).json(body); }, (err) => { - console.error(err); + EmulatorLogger.forEmulator(Emulators.UI).log("ERROR", err); res.status(500).json({ message: err.message, stack: err.stack, From 316d1e0c99015c685b6962eeb5404cba579d5dab Mon Sep 17 00:00:00 2001 From: Chris Thompson Date: Tue, 12 Nov 2024 00:48:32 -0800 Subject: [PATCH 06/13] Expressbasedemulator --- src/emulator/ui.ts | 109 ++++++++++++++++++++++------------ src/emulator/ui/server.ts | 119 -------------------------------------- 2 files changed, 73 insertions(+), 155 deletions(-) delete mode 100644 src/emulator/ui/server.ts diff --git a/src/emulator/ui.ts b/src/emulator/ui.ts index 1cd259efa38..e5729f4f74e 100644 --- a/src/emulator/ui.ts +++ b/src/emulator/ui.ts @@ -1,4 +1,7 @@ -import { EmulatorInstance, EmulatorInfo, Emulators, ListenSpec } from "./types"; +import * as express from "express"; +import * as path from "path"; +import fetch from "node-fetch"; +import { Emulators, ListenSpec } from "./types"; import * as downloadableEmulators from "./downloadableEmulators"; import { EmulatorRegistry } from "./registry"; import { FirebaseError } from "../error"; @@ -6,9 +9,7 @@ import { EmulatorLogger } from "./emulatorLogger"; import { Constants } from "./constants"; import { emulatorSession } from "../track"; import { ExpressBasedEmulator } from "./ExpressBasedEmulator"; -import { createApp } from "./ui/server"; import { ALL_EXPERIMENTS, ExperimentName, isEnabled } from "../experiments"; -import { createDestroyer } from "../utils"; export interface EmulatorUIOptions { listen: ListenSpec[]; @@ -16,12 +17,19 @@ export interface EmulatorUIOptions { auto_download?: boolean; } -export class EmulatorUI implements EmulatorInstance { +export class EmulatorUI extends ExpressBasedEmulator { - private destroyers = new Set<() => Promise>(); - constructor(private args: EmulatorUIOptions) { } + constructor(private args: EmulatorUIOptions) { + super({ + listen: args.listen, + }); + } + + override async start(): Promise { + await super.start(); + } - async start(): Promise { + protected override async createExpressApp(): Promise { if (!EmulatorRegistry.isRunning(Emulators.HUB)) { throw new FirebaseError( `Cannot start ${Constants.description(Emulators.UI)} without ${Constants.description( @@ -29,49 +37,78 @@ export class EmulatorUI implements EmulatorInstance { )}!`, ); } + const app = await super.createExpressApp(); const { projectId } = this.args; - - // FIXME Question: do we need GCLOUD_PROJECT: projectId, for anything? const enabledExperiments: Array = (Object.keys(ALL_EXPERIMENTS) as Array).filter( (experimentName) => isEnabled(experimentName), ); - const downloadDetails = downloadableEmulators.DownloadDetails[Emulators.UI]; // FIXME Question: use getDownloadDetails to re-use path override? idk + const emulatorGaSession = emulatorSession(); + await downloadableEmulators.downloadIfNecessary(Emulators.UI); + const downloadDetails = downloadableEmulators.getDownloadDetails(Emulators.UI); + const webDir = path.join(downloadDetails.unzipDir!!, 'client'); + + // Exposes the host and port of various emulators to facilitate accessing + // them using client SDKs. For features that involve multiple emulators or + // hard to accomplish using client SDKs, consider adding an API below. + app.get( + '/api/config', + this.jsonHandler(async () => { + const hubDiscoveryUrl = new URL(`http://${EmulatorRegistry.url(Emulators.HUB).host}/emulators`); + const emulatorsRes = await fetch(hubDiscoveryUrl.toString()); + const emulators = (await emulatorsRes.json()) as any; + + const json = { projectId, experiments: [], ...emulators }; + + // Googlers: see go/firebase-emulator-ui-usage-collection-design?pli=1#heading=h.jwz7lj6r67z8 + // for more detail + if (emulatorGaSession) { + json.analytics = emulatorGaSession; + } + + // pick up any experiments enabled with `firebase experiment:enable` + if (enabledExperiments) { + json.experiments = enabledExperiments; + } + + return json; + }) + ); + + app.use(express.static(webDir)); + // Required for the router to work properly. + app.get('*', function (_, res) { + res.sendFile(path.join(webDir, 'index.html')); + }); - const servers = await createApp( - downloadDetails.unzipDir!!, - projectId, - EmulatorRegistry.url(Emulators.HUB).host, - emulatorSession(), - ExpressBasedEmulator.listenOptionsFromSpecs(this.args.listen), - enabledExperiments); - servers.forEach((server) => { - this.destroyers.add(createDestroyer(server)); - }); + return app } connect(): Promise { return Promise.resolve(); } - async stop(): Promise { - const promises = []; - for (const destroyer of this.destroyers) { - promises.push(destroyer().then(() => this.destroyers.delete(destroyer))); - } - await Promise.all(promises); + getName(): Emulators { + return Emulators.UI; } - getInfo(): EmulatorInfo { - return { - name: this.getName(), - host: this.args.listen[0].address, - port: this.args.listen[0].port, - pid: downloadableEmulators.getPID(Emulators.UI), + jsonHandler( + handler: (req: express.Request) => Promise + ): express.Handler { + return (req, res) => { + handler(req).then( + (body) => { + res.status(200).json(body); + }, + (err) => { + EmulatorLogger.forEmulator(Emulators.UI).log("ERROR", err); + res.status(500).json({ + message: err.message, + stack: err.stack, + raw: err, + }); + } + ); }; } - - getName(): Emulators { - return Emulators.UI; - } } diff --git a/src/emulator/ui/server.ts b/src/emulator/ui/server.ts deleted file mode 100644 index 5689ff039d5..00000000000 --- a/src/emulator/ui/server.ts +++ /dev/null @@ -1,119 +0,0 @@ -/** - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import * as http from "http"; -import type { ListenOptions } from "net"; -import * as path from "path"; - -import * as express from "express"; -import fetch from "node-fetch"; -import { AnalyticsSession } from "../../track"; -import { ExperimentName } from "../../experiments"; -import { FirebaseError } from "../../error"; -import { EmulatorLogger } from "../emulatorLogger"; -import { Emulators } from "../types"; - -/* - This file defines Node.js server-side logic for the Emulator UI. - - It is a facsimile class of the local server in firebase-tools-ui/server.ts. For that reason, - values that were previously environment variables are passed in rather than fetched/derived - in the class itself. -*/ -export async function createApp( - zipDirPath: string, - projectId: string, - hubHost: string, - emulatorGaSession: AnalyticsSession | undefined, - listenOptions: ListenOptions[], - experiments: Array): Promise { - - const app = express(); - // Exposes the host and port of various emulators to facilitate accessing - // them using client SDKs. For features that involve multiple emulators or - // hard to accomplish using client SDKs, consider adding an API below. - app.get( - '/api/config', - jsonHandler(async () => { - const hubDiscoveryUrl = new URL(`http://${hubHost}/emulators`); - const emulatorsRes = await fetch(hubDiscoveryUrl.toString()); - const emulators = (await emulatorsRes.json()) as any; - - const json = { projectId, experiments: [], ...emulators }; - - // Googlers: see go/firebase-emulator-ui-usage-collection-design?pli=1#heading=h.jwz7lj6r67z8 - // for more detail - if (emulatorGaSession) { - json.analytics = emulatorGaSession; - } - - // pick up any experiments enabled with `firebase experiment:enable` - if (experiments) { - json.experiments = experiments; - } - - return json; - }) - ); - - const webDir = path.join(zipDirPath, 'client'); - app.use(express.static(webDir)); - // Required for the router to work properly. - app.get('*', function (_, res) { - res.sendFile(path.join(webDir, 'index.html')); - }); - - if (listenOptions.length == 0) { - throw new FirebaseError("Failed to start UI server, listenOptions empty"); - } - var servers : http.Server[] = []; - for (const opts of listenOptions) { - var server = http.createServer(app).listen(opts) - servers.push(server); - server.once('listening', () => { - EmulatorLogger.forEmulator(Emulators.UI).log("DEBUG", `Web / API server started at ${opts.host}:${opts.port}`); - }); - server.once('error', (err) => { - EmulatorLogger.forEmulator(Emulators.UI).log("ERROR", `Failed to start server at ${opts.host}:${opts.port}. ${err}`); - if (opts === listenOptions[0]) { - // If we failed to listen on the primary address, surface the error. - throw new FirebaseError(`Failed to start server for the primary address at ${opts.host}:${opts.port}`); - } - }); - } - - return servers -} - -function jsonHandler( - handler: (req: express.Request) => Promise -): express.Handler { - return (req, res) => { - handler(req).then( - (body) => { - res.status(200).json(body); - }, - (err) => { - EmulatorLogger.forEmulator(Emulators.UI).log("ERROR", err); - res.status(500).json({ - message: err.message, - stack: err.stack, - raw: err, - }); - } - ); - }; -} From 858492d909a5cadfa3c5a936d5c47f722b2b88d6 Mon Sep 17 00:00:00 2001 From: Chris Thompson Date: Tue, 12 Nov 2024 00:52:10 -0800 Subject: [PATCH 07/13] ok --- src/emulator/downloadableEmulators.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/emulator/downloadableEmulators.ts b/src/emulator/downloadableEmulators.ts index 36895bb72fa..ef6ceb4468b 100755 --- a/src/emulator/downloadableEmulators.ts +++ b/src/emulator/downloadableEmulators.ts @@ -527,7 +527,7 @@ async function _runBinary( */ export function getDownloadDetails(emulator: DownloadableEmulators): EmulatorDownloadDetails { const details = DownloadDetails[emulator]; - const pathOverride = process.env[`${emulator.toUpperCase()}_EMULATOR_BINARY_PATH`]; // FIXME care about this for UI? + const pathOverride = process.env[`${emulator.toUpperCase()}_EMULATOR_BINARY_PATH`]; if (pathOverride) { const logger = EmulatorLogger.forEmulator(emulator); logger.logLabeled( From 2d8685ede5733849ab031f069311ec8a83f69693 Mon Sep 17 00:00:00 2001 From: Chris Thompson Date: Tue, 12 Nov 2024 00:57:02 -0800 Subject: [PATCH 08/13] Formatting --- src/emulator/ui.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/emulator/ui.ts b/src/emulator/ui.ts index e5729f4f74e..0a112c9a63d 100644 --- a/src/emulator/ui.ts +++ b/src/emulator/ui.ts @@ -39,9 +39,11 @@ export class EmulatorUI extends ExpressBasedEmulator { } const app = await super.createExpressApp(); const { projectId } = this.args; - const enabledExperiments: Array = (Object.keys(ALL_EXPERIMENTS) as Array).filter( - (experimentName) => isEnabled(experimentName), - ); + const enabledExperiments: Array = + (Object.keys(ALL_EXPERIMENTS) as Array) + .filter( + (experimentName) => isEnabled(experimentName), + ); const emulatorGaSession = emulatorSession(); await downloadableEmulators.downloadIfNecessary(Emulators.UI); From 2200e4e66f3beb8d1078b6ba75de5110888b3b60 Mon Sep 17 00:00:00 2001 From: Chris Thompson Date: Tue, 12 Nov 2024 11:47:55 -0800 Subject: [PATCH 09/13] lint --- src/emulator/ui.ts | 31 ++++++++++++++----------------- 1 file changed, 14 insertions(+), 17 deletions(-) diff --git a/src/emulator/ui.ts b/src/emulator/ui.ts index 0a112c9a63d..03474f24b39 100644 --- a/src/emulator/ui.ts +++ b/src/emulator/ui.ts @@ -18,7 +18,6 @@ export interface EmulatorUIOptions { } export class EmulatorUI extends ExpressBasedEmulator { - constructor(private args: EmulatorUIOptions) { super({ listen: args.listen, @@ -39,24 +38,24 @@ export class EmulatorUI extends ExpressBasedEmulator { } const app = await super.createExpressApp(); const { projectId } = this.args; - const enabledExperiments: Array = - (Object.keys(ALL_EXPERIMENTS) as Array) - .filter( - (experimentName) => isEnabled(experimentName), - ); + const enabledExperiments: Array = ( + Object.keys(ALL_EXPERIMENTS) as Array + ).filter((experimentName) => isEnabled(experimentName)); const emulatorGaSession = emulatorSession(); await downloadableEmulators.downloadIfNecessary(Emulators.UI); const downloadDetails = downloadableEmulators.getDownloadDetails(Emulators.UI); - const webDir = path.join(downloadDetails.unzipDir!!, 'client'); + const webDir = path.join(downloadDetails.unzipDir!!, "client"); // Exposes the host and port of various emulators to facilitate accessing // them using client SDKs. For features that involve multiple emulators or // hard to accomplish using client SDKs, consider adding an API below. app.get( - '/api/config', + "/api/config", this.jsonHandler(async () => { - const hubDiscoveryUrl = new URL(`http://${EmulatorRegistry.url(Emulators.HUB).host}/emulators`); + const hubDiscoveryUrl = new URL( + `http://${EmulatorRegistry.url(Emulators.HUB).host}/emulators`, + ); const emulatorsRes = await fetch(hubDiscoveryUrl.toString()); const emulators = (await emulatorsRes.json()) as any; @@ -74,16 +73,16 @@ export class EmulatorUI extends ExpressBasedEmulator { } return json; - }) + }), ); app.use(express.static(webDir)); // Required for the router to work properly. - app.get('*', function (_, res) { - res.sendFile(path.join(webDir, 'index.html')); + app.get("*", (_, res) => { + res.sendFile(path.join(webDir, "index.html")); }); - return app + return app; } connect(): Promise { @@ -94,9 +93,7 @@ export class EmulatorUI extends ExpressBasedEmulator { return Emulators.UI; } - jsonHandler( - handler: (req: express.Request) => Promise - ): express.Handler { + jsonHandler(handler: (req: express.Request) => Promise): express.Handler { return (req, res) => { handler(req).then( (body) => { @@ -109,7 +106,7 @@ export class EmulatorUI extends ExpressBasedEmulator { stack: err.stack, raw: err, }); - } + }, ); }; } From f6cea112ac256e41282e8c4c3cb0b67508ccd645 Mon Sep 17 00:00:00 2001 From: Chris Thompson Date: Tue, 12 Nov 2024 11:57:23 -0800 Subject: [PATCH 10/13] Remove autodownload from emulator UI options --- src/emulator/controller.ts | 1 - src/emulator/ui.ts | 1 - 2 files changed, 2 deletions(-) diff --git a/src/emulator/controller.ts b/src/emulator/controller.ts index 5708097f97d..107eccbda83 100755 --- a/src/emulator/controller.ts +++ b/src/emulator/controller.ts @@ -963,7 +963,6 @@ export async function startAll( if (listenForEmulator.ui) { const ui = new EmulatorUI({ projectId: projectId, - auto_download: true, listen: listenForEmulator[Emulators.UI], }); await startEmulator(ui); diff --git a/src/emulator/ui.ts b/src/emulator/ui.ts index 03474f24b39..01cf452cd01 100644 --- a/src/emulator/ui.ts +++ b/src/emulator/ui.ts @@ -14,7 +14,6 @@ import { ALL_EXPERIMENTS, ExperimentName, isEnabled } from "../experiments"; export interface EmulatorUIOptions { listen: ListenSpec[]; projectId: string; - auto_download?: boolean; } export class EmulatorUI extends ExpressBasedEmulator { From df93243c76010591f57964140263eb2d06b84be3 Mon Sep 17 00:00:00 2001 From: Chris Thompson Date: Tue, 12 Nov 2024 18:12:11 -0800 Subject: [PATCH 11/13] Get emulators from registry/hub isntead of http --- src/emulator/hub.ts | 13 ++++++++++++- src/emulator/ui.ts | 6 ++++-- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/src/emulator/hub.ts b/src/emulator/hub.ts index 792aee63a1b..7c5d18ab78f 100644 --- a/src/emulator/hub.ts +++ b/src/emulator/hub.ts @@ -77,6 +77,17 @@ export class EmulatorHub extends ExpressBasedEmulator { await this.writeLocatorFile(); } + getRunningEmulatorsMapping(): any { + const emulators: GetEmulatorsResponse = {}; + for (const info of EmulatorRegistry.listRunningWithInfo()) { + emulators[info.name] = { + listen: this.args.listenForEmulator[info.name], + ...info, + }; + } + return emulators; + } + protected override async createExpressApp(): Promise { const app = await super.createExpressApp(); app.get("/", (req, res) => { @@ -96,7 +107,7 @@ export class EmulatorHub extends ExpressBasedEmulator { ...info, }; } - res.json(body); + res.json(this.getRunningEmulatorsMapping()); }); app.post(EmulatorHub.PATH_EXPORT, async (req, res) => { diff --git a/src/emulator/ui.ts b/src/emulator/ui.ts index 01cf452cd01..3485cea205e 100644 --- a/src/emulator/ui.ts +++ b/src/emulator/ui.ts @@ -10,6 +10,7 @@ import { Constants } from "./constants"; import { emulatorSession } from "../track"; import { ExpressBasedEmulator } from "./ExpressBasedEmulator"; import { ALL_EXPERIMENTS, ExperimentName, isEnabled } from "../experiments"; +import { EmulatorHub} from "./hub"; export interface EmulatorUIOptions { listen: ListenSpec[]; @@ -35,6 +36,7 @@ export class EmulatorUI extends ExpressBasedEmulator { )}!`, ); } + const hub = EmulatorRegistry.get(Emulators.HUB); const app = await super.createExpressApp(); const { projectId } = this.args; const enabledExperiments: Array = ( @@ -44,7 +46,7 @@ export class EmulatorUI extends ExpressBasedEmulator { await downloadableEmulators.downloadIfNecessary(Emulators.UI); const downloadDetails = downloadableEmulators.getDownloadDetails(Emulators.UI); - const webDir = path.join(downloadDetails.unzipDir!!, "client"); + const webDir = path.join(downloadDetails.unzipDir!, "client"); // Exposes the host and port of various emulators to facilitate accessing // them using client SDKs. For features that involve multiple emulators or @@ -58,7 +60,7 @@ export class EmulatorUI extends ExpressBasedEmulator { const emulatorsRes = await fetch(hubDiscoveryUrl.toString()); const emulators = (await emulatorsRes.json()) as any; - const json = { projectId, experiments: [], ...emulators }; + const json = { projectId, experiments: [], ... (hub! as EmulatorHub).getRunningEmulatorsMapping() }; // Googlers: see go/firebase-emulator-ui-usage-collection-design?pli=1#heading=h.jwz7lj6r67z8 // for more detail From 84cf0e66f93e0fa2c01ef59fef157aa1153e6180 Mon Sep 17 00:00:00 2001 From: Chris Thompson Date: Tue, 12 Nov 2024 18:19:23 -0800 Subject: [PATCH 12/13] changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e69de29bb2d..3af1a42e7ad 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -0,0 +1 @@ +- Moved firebase-tools-ui server.js logic to fireabse-tools to run it in-memory. (#7897) From dcd8aa69f444f110a311dfb0139c3603e47502c4 Mon Sep 17 00:00:00 2001 From: Chris Thompson Date: Wed, 13 Nov 2024 15:34:55 -0800 Subject: [PATCH 13/13] Cleanup --- src/emulator/hub.ts | 7 ------- src/emulator/ui.ts | 17 +++++++---------- 2 files changed, 7 insertions(+), 17 deletions(-) diff --git a/src/emulator/hub.ts b/src/emulator/hub.ts index 7c5d18ab78f..436c5436170 100644 --- a/src/emulator/hub.ts +++ b/src/emulator/hub.ts @@ -100,13 +100,6 @@ export class EmulatorHub extends ExpressBasedEmulator { }); app.get(EmulatorHub.PATH_EMULATORS, (req, res) => { - const body: GetEmulatorsResponse = {}; - for (const info of EmulatorRegistry.listRunningWithInfo()) { - body[info.name] = { - listen: this.args.listenForEmulator[info.name], - ...info, - }; - } res.json(this.getRunningEmulatorsMapping()); }); diff --git a/src/emulator/ui.ts b/src/emulator/ui.ts index 3485cea205e..b3b33586ffe 100644 --- a/src/emulator/ui.ts +++ b/src/emulator/ui.ts @@ -1,6 +1,5 @@ import * as express from "express"; import * as path from "path"; -import fetch from "node-fetch"; import { Emulators, ListenSpec } from "./types"; import * as downloadableEmulators from "./downloadableEmulators"; import { EmulatorRegistry } from "./registry"; @@ -10,7 +9,7 @@ import { Constants } from "./constants"; import { emulatorSession } from "../track"; import { ExpressBasedEmulator } from "./ExpressBasedEmulator"; import { ALL_EXPERIMENTS, ExperimentName, isEnabled } from "../experiments"; -import { EmulatorHub} from "./hub"; +import { EmulatorHub } from "./hub"; export interface EmulatorUIOptions { listen: ListenSpec[]; @@ -53,14 +52,12 @@ export class EmulatorUI extends ExpressBasedEmulator { // hard to accomplish using client SDKs, consider adding an API below. app.get( "/api/config", - this.jsonHandler(async () => { - const hubDiscoveryUrl = new URL( - `http://${EmulatorRegistry.url(Emulators.HUB).host}/emulators`, - ); - const emulatorsRes = await fetch(hubDiscoveryUrl.toString()); - const emulators = (await emulatorsRes.json()) as any; - - const json = { projectId, experiments: [], ... (hub! as EmulatorHub).getRunningEmulatorsMapping() }; + this.jsonHandler(() => { + const json = { + projectId, + experiments: [], + ...(hub! as EmulatorHub).getRunningEmulatorsMapping(), + }; // Googlers: see go/firebase-emulator-ui-usage-collection-design?pli=1#heading=h.jwz7lj6r67z8 // for more detail