From edb03ac28f80b896803336ff0b3b231b012bd9b9 Mon Sep 17 00:00:00 2001 From: Jan Wyszynski Date: Tue, 28 May 2019 10:15:07 -0700 Subject: [PATCH 01/18] Add RTDB emulator support to the functions emulator --- src/emulator/constants.ts | 1 + src/emulator/functionsEmulator.ts | 51 +++++++++++++++++++++++++++++++ 2 files changed, 52 insertions(+) diff --git a/src/emulator/constants.ts b/src/emulator/constants.ts index 459e5712469..78ec9ec85ac 100644 --- a/src/emulator/constants.ts +++ b/src/emulator/constants.ts @@ -13,6 +13,7 @@ const DEFAULT_HOST = "localhost"; export class Constants { static SERVICE_FIRESTORE = "firestore.googleapis.com"; + static SERVICE_REALTIME_DATABASE = "firebaseio.com"; static getDefaultHost(emulator: Emulators): string { return DEFAULT_HOST; diff --git a/src/emulator/functionsEmulator.ts b/src/emulator/functionsEmulator.ts index 41ff442fb6d..031f27d9602 100644 --- a/src/emulator/functionsEmulator.ts +++ b/src/emulator/functionsEmulator.ts @@ -481,6 +481,7 @@ You can probably fix this by running "npm install ${ this.triggers = triggerDefinitions; for (const definition of toSetup) { + console.log(require("util").inspect(definition)); if (definition.httpsTrigger) { // TODO(samstern): Right now we only emulate each function in one region, but it's possible // that a developer is running the same function in multiple regions. @@ -503,6 +504,9 @@ You can probably fix this by running "npm install ${ case Constants.SERVICE_FIRESTORE: await this.addFirestoreTrigger(this.projectId, definition); break; + case Constants.SERVICE_REALTIME_DATABASE: + await this.addRealtimeDatabaseTrigger(this.projectId, definition); + break; default: EmulatorLogger.log("DEBUG", `Unsupported trigger: ${JSON.stringify(definition)}`); EmulatorLogger.log( @@ -527,6 +531,53 @@ You can probably fix this by running "npm install ${ return loadTriggers(); } + addRealtimeDatabaseTrigger(projectId: string, definition: EmulatedTriggerDefinition): Promise { + const databasePort = 9000; //EmulatorRegistry.getPort(Emulators.DATABASE); + if (!databasePort) { + EmulatorLogger.log( + "INFO", + `Ignoring trigger "${definition.name}" because the Realtime Database emulator is not running` + ); + return Promise.resolve(); + } + if (definition.eventTrigger === undefined) { + EmulatorLogger.log( + "WARN", + `Event trigger "${definition.name}" has undefined "eventTrigger" member` + ); + return Promise.reject(); + } + console.log("Trigger definition " + require('util').inspect(definition)); + const bundle = JSON.stringify([{ + name: definition.name, + path: definition.eventTrigger.resource, + event: definition.eventTrigger.eventType, + topic: "" + }]); + EmulatorLogger.logLabeled( + "BULLET", + "functions", + `Setting up Realtime Database trigger "${definition.name}"` + ); + logger.debug(`addDatabaseTrigger`, JSON.stringify(bundle)); + return new Promise((resolve, reject) => { + request.put(`http://localhost:${databasePort}/.settings/functionTriggers.json`, + { + body: bundle + }, + (err, res, body) => { + if (err) { + EmulatorLogger.log("WARN", "Error adding trigger: " + err); + reject(); + return; + } + console.log("RTDB Trigger add result: " + require('util').inspect(body)); + resolve(); + } + ) + }); + } + addFirestoreTrigger(projectId: string, definition: EmulatedTriggerDefinition): Promise { const firestorePort = EmulatorRegistry.getPort(Emulators.FIRESTORE); if (!firestorePort) { From ae0ffe4b637de9a1b5f48172429fd4ef256f66c6 Mon Sep 17 00:00:00 2001 From: Jan Wyszynski Date: Wed, 29 May 2019 10:29:18 -0700 Subject: [PATCH 02/18] Add auth header to function trigger setup request --- src/emulator/functionsEmulator.ts | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/src/emulator/functionsEmulator.ts b/src/emulator/functionsEmulator.ts index 031f27d9602..e04a5bce642 100644 --- a/src/emulator/functionsEmulator.ts +++ b/src/emulator/functionsEmulator.ts @@ -481,7 +481,6 @@ You can probably fix this by running "npm install ${ this.triggers = triggerDefinitions; for (const definition of toSetup) { - console.log(require("util").inspect(definition)); if (definition.httpsTrigger) { // TODO(samstern): Right now we only emulate each function in one region, but it's possible // that a developer is running the same function in multiple regions. @@ -531,7 +530,10 @@ You can probably fix this by running "npm install ${ return loadTriggers(); } - addRealtimeDatabaseTrigger(projectId: string, definition: EmulatedTriggerDefinition): Promise { + addRealtimeDatabaseTrigger( + projectId: string, + definition: EmulatedTriggerDefinition + ): Promise { const databasePort = 9000; //EmulatorRegistry.getPort(Emulators.DATABASE); if (!databasePort) { EmulatorLogger.log( @@ -547,13 +549,13 @@ You can probably fix this by running "npm install ${ ); return Promise.reject(); } - console.log("Trigger definition " + require('util').inspect(definition)); const bundle = JSON.stringify([{ - name: definition.name, - path: definition.eventTrigger.resource, + name: `projects/${projectId}/locations/_/functions/${definition.name}`, + path: definition.eventTrigger.resource.split("refs")[1], event: definition.eventTrigger.eventType, - topic: "" + topic: `projects/${projectId}/topics/_`, }]); + EmulatorLogger.logLabeled( "BULLET", "functions", @@ -563,6 +565,9 @@ You can probably fix this by running "npm install ${ return new Promise((resolve, reject) => { request.put(`http://localhost:${databasePort}/.settings/functionTriggers.json`, { + auth: { + bearer: "owner" + }, body: bundle }, (err, res, body) => { @@ -571,7 +576,6 @@ You can probably fix this by running "npm install ${ reject(); return; } - console.log("RTDB Trigger add result: " + require('util').inspect(body)); resolve(); } ) From 783d764a23ad189cb5bba197137150ae938b7b87 Mon Sep 17 00:00:00 2001 From: Jan Wyszynski Date: Wed, 29 May 2019 18:38:26 -0700 Subject: [PATCH 03/18] Fix lint errors --- src/emulator/functionsEmulator.ts | 29 +++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/src/emulator/functionsEmulator.ts b/src/emulator/functionsEmulator.ts index e04a5bce642..8119ce2f575 100644 --- a/src/emulator/functionsEmulator.ts +++ b/src/emulator/functionsEmulator.ts @@ -534,11 +534,13 @@ You can probably fix this by running "npm install ${ projectId: string, definition: EmulatedTriggerDefinition ): Promise { - const databasePort = 9000; //EmulatorRegistry.getPort(Emulators.DATABASE); + const databasePort = EmulatorRegistry.getPort(Emulators.DATABASE); if (!databasePort) { EmulatorLogger.log( "INFO", - `Ignoring trigger "${definition.name}" because the Realtime Database emulator is not running` + `Ignoring trigger "${ + definition.name + }" because the Realtime Database emulator is not running` ); return Promise.resolve(); } @@ -549,12 +551,14 @@ You can probably fix this by running "npm install ${ ); return Promise.reject(); } - const bundle = JSON.stringify([{ - name: `projects/${projectId}/locations/_/functions/${definition.name}`, - path: definition.eventTrigger.resource.split("refs")[1], - event: definition.eventTrigger.eventType, - topic: `projects/${projectId}/topics/_`, - }]); + const bundle = JSON.stringify([ + { + name: `projects/${projectId}/locations/_/functions/${definition.name}`, + path: definition.eventTrigger.resource.split("refs")[1], + event: definition.eventTrigger.eventType, + topic: `projects/${projectId}/topics/_`, + }, + ]); EmulatorLogger.logLabeled( "BULLET", @@ -563,12 +567,13 @@ You can probably fix this by running "npm install ${ ); logger.debug(`addDatabaseTrigger`, JSON.stringify(bundle)); return new Promise((resolve, reject) => { - request.put(`http://localhost:${databasePort}/.settings/functionTriggers.json`, + request.put( + `http://localhost:${databasePort}/.settings/functionTriggers.json`, { auth: { - bearer: "owner" + bearer: "owner", }, - body: bundle + body: bundle, }, (err, res, body) => { if (err) { @@ -578,7 +583,7 @@ You can probably fix this by running "npm install ${ } resolve(); } - ) + ); }); } From 17edb2dd49335b2cca21d29ddc773424a9aed307 Mon Sep 17 00:00:00 2001 From: Jan Wyszynski Date: Thu, 30 May 2019 11:57:08 -0700 Subject: [PATCH 04/18] Use regex to extract database-root-relative paths for trigger creation --- src/emulator/functionsEmulator.ts | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/src/emulator/functionsEmulator.ts b/src/emulator/functionsEmulator.ts index 8119ce2f575..819afa2956e 100644 --- a/src/emulator/functionsEmulator.ts +++ b/src/emulator/functionsEmulator.ts @@ -540,7 +540,7 @@ You can probably fix this by running "npm install ${ "INFO", `Ignoring trigger "${ definition.name - }" because the Realtime Database emulator is not running` + }" because the Realtime Database emulator is not running.` ); return Promise.resolve(); } @@ -551,10 +551,26 @@ You can probably fix this by running "npm install ${ ); return Promise.reject(); } + /* + * The Realtime Database emulator expects the `path` field in its trigger + * definition to be relative to the database root. + */ + const pathPattern = new RegExp("^projects/[^/]*/instances/[^/]*/refs(/.*)$"); + const result: string[] | null = pathPattern.exec(definition.eventTrigger.resource); + + if (result === null || result.length !== 2) { + EmulatorLogger.log( + "WARN", + `Event trigger "${definition.name}" has malformed "resource" member. ` + + `${definition.eventTrigger.resource}` + ); + return Promise.reject(); + } + const bundle = JSON.stringify([ { name: `projects/${projectId}/locations/_/functions/${definition.name}`, - path: definition.eventTrigger.resource.split("refs")[1], + path: result[1], // path stored in the first capture group event: definition.eventTrigger.eventType, topic: `projects/${projectId}/topics/_`, }, From fceb7f232e0e614d5823e4f1062a3848de0791b3 Mon Sep 17 00:00:00 2001 From: Jan Wyszynski Date: Thu, 30 May 2019 12:34:13 -0700 Subject: [PATCH 05/18] Make path extractor regex a constant, enforce non-empty instance and project --- src/emulator/functionsEmulator.ts | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/emulator/functionsEmulator.ts b/src/emulator/functionsEmulator.ts index 1ec81bc5981..b5fcdd94545 100644 --- a/src/emulator/functionsEmulator.ts +++ b/src/emulator/functionsEmulator.ts @@ -32,6 +32,14 @@ import { EmulatorLogger, Verbosity } from "./emulatorLogger"; const EVENT_INVOKE = "functions:invoke"; +/* + * The Realtime Database emulator expects the `path` field in its trigger + * definition to be relative to the database root. This regex is used to extract + * that path from the `resource` member in the trigger definition used by the + * functions emulator. + */ +const DATABASE_PATH_PATTERN = new RegExp("^projects/[^/]+/instances/[^/]+/refs(/.*)$"); + export interface FunctionsEmulatorArgs { port?: number; host?: string; @@ -538,13 +546,8 @@ You can probably fix this by running "npm install ${ ); return Promise.reject(); } - /* - * The Realtime Database emulator expects the `path` field in its trigger - * definition to be relative to the database root. - */ - const pathPattern = new RegExp("^projects/[^/]*/instances/[^/]*/refs(/.*)$"); - const result: string[] | null = pathPattern.exec(definition.eventTrigger.resource); + const result: string[] | null = DATABASE_PATH_PATTERN.exec(definition.eventTrigger.resource); if (result === null || result.length !== 2) { EmulatorLogger.log( "WARN", From 0485229dc53b25e2ceea2fd9e5a41c4a063c7c6f Mon Sep 17 00:00:00 2001 From: Jan Wyszynski Date: Thu, 30 May 2019 13:03:06 -0700 Subject: [PATCH 06/18] Pass functions emulator arguments to database emulator when present --- src/emulator/controller.ts | 25 +++++++++++++++---------- src/emulator/databaseEmulator.ts | 2 ++ 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/src/emulator/controller.ts b/src/emulator/controller.ts index e1bb2500f9c..b7f101c8c66 100644 --- a/src/emulator/controller.ts +++ b/src/emulator/controller.ts @@ -154,17 +154,22 @@ export async function startAll(options: any): Promise { if (targets.indexOf(Emulators.DATABASE) > -1) { const databaseAddr = Constants.getAddress(Emulators.DATABASE, options); - const databaseEmulator = new DatabaseEmulator({ - host: databaseAddr.host, - port: databaseAddr.port, - }); + let databaseEmulator; + if (targets.indexOf(Emulators.FUNCTIONS) > -1) { + const functionsAddr = Constants.getAddress(Emulators.FUNCTIONS, options); + databaseEmulator = new DatabaseEmulator({ + host: databaseAddr.host, + port: databaseAddr.port, + functionsEmulatorHost: functionsAddr.host, + functionsEmulatorPort: functionsAddr.port, + }); + } else { + databaseEmulator = new DatabaseEmulator({ + host: databaseAddr.host, + port: databaseAddr.port, + }); + } await startEmulator(databaseEmulator); - - // TODO: When the database emulator is integrated with the Functions - // emulator, we will need to pass the port in and remove this warning - utils.logWarning( - `Note: the database emulator is not currently integrated with the functions emulator.` - ); } if (targets.indexOf(Emulators.HOSTING) > -1) { diff --git a/src/emulator/databaseEmulator.ts b/src/emulator/databaseEmulator.ts index b9b9b8b4f43..0b50d4f61ce 100644 --- a/src/emulator/databaseEmulator.ts +++ b/src/emulator/databaseEmulator.ts @@ -5,6 +5,8 @@ import { Constants } from "./constants"; interface DatabaseEmulatorArgs { port?: number; host?: string; + functionsEmulatorPort?: number; + functionsEmulatorHost?: string; } export class DatabaseEmulator implements EmulatorInstance { From bbcf0ddec7da1651b7144c1770fdb242f4927d90 Mon Sep 17 00:00:00 2001 From: Bryan Kendall Date: Thu, 30 May 2019 15:46:36 -0700 Subject: [PATCH 07/18] Print error and exit early with Node 6 (#1349) * increase engines to node >= 8; engineStrict is deprecated * add error if node version does not satisfy engine version * drop testing on 6, test hosting on 8 * update message for error * add note to changelog about breaking CLI * put breaking change at the top of the changelog --- .travis.yml | 3 +-- changelog.txt | 1 + package.json | 3 +-- src/bin/firebase.js | 15 +++++++++++++++ 4 files changed, 18 insertions(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index 76e46caf1cb..378e72b409c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,5 @@ language: node_js node_js: - - "6" - "8" - "10" after_script: @@ -9,7 +8,7 @@ jobs: include: - stage: hosting functional test if: repo == head_repo OR type == push - node_js: "6" + node_js: "8" before_script: ./scripts/decrypt-app-credentials.sh script: ./scripts/test-hosting.sh after_script: skip diff --git a/changelog.txt b/changelog.txt index 6b472790409..e42703a4247 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,3 +1,4 @@ +* **BREAKING** The CLI no longer suppport being run in Node 6 environments. * Fixed bug in Firestore emulator where some FieldTransforms were sending back incorrect results. * Fixed bug where read-only transactions would cause errors in the Firestore emulator. * Fixed bug where collection group query listeners would cause errors in the Firestore emulator. diff --git a/package.json b/package.json index f8f4b777670..565b42804dd 100644 --- a/package.json +++ b/package.json @@ -40,9 +40,8 @@ ], "preferGlobal": true, "engines": { - "node": ">= 6.0.0" + "node": ">= 8.0.0" }, - "engineStrict": true, "author": "Firebase (https://firebase.google.com/)", "license": "MIT", "bugs": { diff --git a/src/bin/firebase.js b/src/bin/firebase.js index 848278f66e9..3b89a7f5b40 100755 --- a/src/bin/firebase.js +++ b/src/bin/firebase.js @@ -1,7 +1,22 @@ #!/usr/bin/env node "use strict"; +// Make check for Node 6, which is no longer supported by the CLI. +var semver = require("semver"); var pkg = require("../../package.json"); +var nodeVersion = process.version; +if (!semver.satisfies(nodeVersion, pkg.engines.node)) { + console.error( + "Firebase CLI v" + + pkg.version + + " is incompatible with Node.js " + + nodeVersion + + " Please upgrade Node.js to version " + + pkg.engines.node + ); + process.exit(1); +} + var updateNotifier = require("update-notifier")({ pkg: pkg }); updateNotifier.notify({ defer: true, isGlobal: true }); From 324f0732203ac0964fd34e61eaa55449b0a07395 Mon Sep 17 00:00:00 2001 From: Jan Wyszynski Date: Thu, 30 May 2019 18:16:29 -0700 Subject: [PATCH 08/18] Add trigger definition name to invocation payload topicName --- src/emulator/functionsEmulator.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/emulator/functionsEmulator.ts b/src/emulator/functionsEmulator.ts index b5fcdd94545..3de608b1072 100644 --- a/src/emulator/functionsEmulator.ts +++ b/src/emulator/functionsEmulator.ts @@ -562,7 +562,7 @@ You can probably fix this by running "npm install ${ name: `projects/${projectId}/locations/_/functions/${definition.name}`, path: result[1], // path stored in the first capture group event: definition.eventTrigger.eventType, - topic: `projects/${projectId}/topics/_`, + topic: `projects/${projectId}/topics/${definition.name}`, }, ]); From 425b37375249ea968220ee9e3e92bca2cbb519c4 Mon Sep 17 00:00:00 2001 From: Jan Wyszynski Date: Fri, 31 May 2019 10:58:44 -0700 Subject: [PATCH 09/18] Use snake-case in database emulator args --- src/emulator/controller.ts | 4 ++-- src/emulator/databaseEmulator.ts | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/emulator/controller.ts b/src/emulator/controller.ts index b7f101c8c66..cec97b9ccf4 100644 --- a/src/emulator/controller.ts +++ b/src/emulator/controller.ts @@ -160,8 +160,8 @@ export async function startAll(options: any): Promise { databaseEmulator = new DatabaseEmulator({ host: databaseAddr.host, port: databaseAddr.port, - functionsEmulatorHost: functionsAddr.host, - functionsEmulatorPort: functionsAddr.port, + functions_emulator_host: functionsAddr.host, + functions_emulator_port: functionsAddr.port, }); } else { databaseEmulator = new DatabaseEmulator({ diff --git a/src/emulator/databaseEmulator.ts b/src/emulator/databaseEmulator.ts index 0b50d4f61ce..1449191e938 100644 --- a/src/emulator/databaseEmulator.ts +++ b/src/emulator/databaseEmulator.ts @@ -5,8 +5,8 @@ import { Constants } from "./constants"; interface DatabaseEmulatorArgs { port?: number; host?: string; - functionsEmulatorPort?: number; - functionsEmulatorHost?: string; + functions_emulator_port?: number; + functions_emulator_host?: string; } export class DatabaseEmulator implements EmulatorInstance { From 70a60f7f70d9411a29f6e2996dc9cf74e1fc4f7b Mon Sep 17 00:00:00 2001 From: Bryan Kendall Date: Fri, 31 May 2019 15:20:18 -0700 Subject: [PATCH 10/18] Remove prepublish script (and typo) (#1355) * remove prepublish script - it's deprecated * fix spelling mistake * Update changelog.txt --- changelog.txt | 2 +- package.json | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/changelog.txt b/changelog.txt index e42703a4247..f32367ffc93 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,4 +1,4 @@ -* **BREAKING** The CLI no longer suppport being run in Node 6 environments. +* **BREAKING** The CLI no longer supports being run in Node 6 environments. * Fixed bug in Firestore emulator where some FieldTransforms were sending back incorrect results. * Fixed bug where read-only transactions would cause errors in the Firestore emulator. * Fixed bug where collection group query listeners would cause errors in the Firestore emulator. diff --git a/package.json b/package.json index 565b42804dd..067e371a924 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,6 @@ "lint:js": "eslint 'src/**/*.js'", "lint:ts": "tslint --project tsconfig.json --config tslint.json", "mocha": "nyc mocha --opts mocha.opts", - "prepublish": "npm run clean && npm run build", "prepare": "npm run clean && npm run build", "test": "npm run lint && npm run mocha" }, From 2646aa1b340c5480454e9ffcb8d83ac2b04959ba Mon Sep 17 00:00:00 2001 From: Jan Wyszynski Date: Tue, 28 May 2019 10:15:07 -0700 Subject: [PATCH 11/18] Add RTDB emulator support to the functions emulator --- src/emulator/constants.ts | 1 + src/emulator/functionsEmulator.ts | 51 +++++++++++++++++++++++++++++++ 2 files changed, 52 insertions(+) diff --git a/src/emulator/constants.ts b/src/emulator/constants.ts index 459e5712469..78ec9ec85ac 100644 --- a/src/emulator/constants.ts +++ b/src/emulator/constants.ts @@ -13,6 +13,7 @@ const DEFAULT_HOST = "localhost"; export class Constants { static SERVICE_FIRESTORE = "firestore.googleapis.com"; + static SERVICE_REALTIME_DATABASE = "firebaseio.com"; static getDefaultHost(emulator: Emulators): string { return DEFAULT_HOST; diff --git a/src/emulator/functionsEmulator.ts b/src/emulator/functionsEmulator.ts index 42c6f9296ab..791c51cb5db 100644 --- a/src/emulator/functionsEmulator.ts +++ b/src/emulator/functionsEmulator.ts @@ -468,6 +468,7 @@ You can probably fix this by running "npm install ${ this.triggers = triggerDefinitions; for (const definition of toSetup) { + console.log(require("util").inspect(definition)); if (definition.httpsTrigger) { // TODO(samstern): Right now we only emulate each function in one region, but it's possible // that a developer is running the same function in multiple regions. @@ -490,6 +491,9 @@ You can probably fix this by running "npm install ${ case Constants.SERVICE_FIRESTORE: await this.addFirestoreTrigger(this.projectId, definition); break; + case Constants.SERVICE_REALTIME_DATABASE: + await this.addRealtimeDatabaseTrigger(this.projectId, definition); + break; default: EmulatorLogger.log("DEBUG", `Unsupported trigger: ${JSON.stringify(definition)}`); EmulatorLogger.log( @@ -514,6 +518,53 @@ You can probably fix this by running "npm install ${ return loadTriggers(); } + addRealtimeDatabaseTrigger(projectId: string, definition: EmulatedTriggerDefinition): Promise { + const databasePort = 9000; //EmulatorRegistry.getPort(Emulators.DATABASE); + if (!databasePort) { + EmulatorLogger.log( + "INFO", + `Ignoring trigger "${definition.name}" because the Realtime Database emulator is not running` + ); + return Promise.resolve(); + } + if (definition.eventTrigger === undefined) { + EmulatorLogger.log( + "WARN", + `Event trigger "${definition.name}" has undefined "eventTrigger" member` + ); + return Promise.reject(); + } + console.log("Trigger definition " + require('util').inspect(definition)); + const bundle = JSON.stringify([{ + name: definition.name, + path: definition.eventTrigger.resource, + event: definition.eventTrigger.eventType, + topic: "" + }]); + EmulatorLogger.logLabeled( + "BULLET", + "functions", + `Setting up Realtime Database trigger "${definition.name}"` + ); + logger.debug(`addDatabaseTrigger`, JSON.stringify(bundle)); + return new Promise((resolve, reject) => { + request.put(`http://localhost:${databasePort}/.settings/functionTriggers.json`, + { + body: bundle + }, + (err, res, body) => { + if (err) { + EmulatorLogger.log("WARN", "Error adding trigger: " + err); + reject(); + return; + } + console.log("RTDB Trigger add result: " + require('util').inspect(body)); + resolve(); + } + ) + }); + } + addFirestoreTrigger(projectId: string, definition: EmulatedTriggerDefinition): Promise { const firestorePort = EmulatorRegistry.getPort(Emulators.FIRESTORE); if (!firestorePort) { From 70b443cf6ae3ec971031406ee291dfc90ee4f23d Mon Sep 17 00:00:00 2001 From: Jan Wyszynski Date: Wed, 29 May 2019 10:29:18 -0700 Subject: [PATCH 12/18] Add auth header to function trigger setup request --- src/emulator/functionsEmulator.ts | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/src/emulator/functionsEmulator.ts b/src/emulator/functionsEmulator.ts index 791c51cb5db..178690491a8 100644 --- a/src/emulator/functionsEmulator.ts +++ b/src/emulator/functionsEmulator.ts @@ -468,7 +468,6 @@ You can probably fix this by running "npm install ${ this.triggers = triggerDefinitions; for (const definition of toSetup) { - console.log(require("util").inspect(definition)); if (definition.httpsTrigger) { // TODO(samstern): Right now we only emulate each function in one region, but it's possible // that a developer is running the same function in multiple regions. @@ -518,7 +517,10 @@ You can probably fix this by running "npm install ${ return loadTriggers(); } - addRealtimeDatabaseTrigger(projectId: string, definition: EmulatedTriggerDefinition): Promise { + addRealtimeDatabaseTrigger( + projectId: string, + definition: EmulatedTriggerDefinition + ): Promise { const databasePort = 9000; //EmulatorRegistry.getPort(Emulators.DATABASE); if (!databasePort) { EmulatorLogger.log( @@ -534,13 +536,13 @@ You can probably fix this by running "npm install ${ ); return Promise.reject(); } - console.log("Trigger definition " + require('util').inspect(definition)); const bundle = JSON.stringify([{ - name: definition.name, - path: definition.eventTrigger.resource, + name: `projects/${projectId}/locations/_/functions/${definition.name}`, + path: definition.eventTrigger.resource.split("refs")[1], event: definition.eventTrigger.eventType, - topic: "" + topic: `projects/${projectId}/topics/_`, }]); + EmulatorLogger.logLabeled( "BULLET", "functions", @@ -550,6 +552,9 @@ You can probably fix this by running "npm install ${ return new Promise((resolve, reject) => { request.put(`http://localhost:${databasePort}/.settings/functionTriggers.json`, { + auth: { + bearer: "owner" + }, body: bundle }, (err, res, body) => { @@ -558,7 +563,6 @@ You can probably fix this by running "npm install ${ reject(); return; } - console.log("RTDB Trigger add result: " + require('util').inspect(body)); resolve(); } ) From 4ca8b4d3ec5c43ebdd91b34d3b7ef021c2ae84a0 Mon Sep 17 00:00:00 2001 From: Jan Wyszynski Date: Wed, 29 May 2019 18:38:26 -0700 Subject: [PATCH 13/18] Fix lint errors --- src/emulator/functionsEmulator.ts | 29 +++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/src/emulator/functionsEmulator.ts b/src/emulator/functionsEmulator.ts index 178690491a8..df11d41effe 100644 --- a/src/emulator/functionsEmulator.ts +++ b/src/emulator/functionsEmulator.ts @@ -521,11 +521,13 @@ You can probably fix this by running "npm install ${ projectId: string, definition: EmulatedTriggerDefinition ): Promise { - const databasePort = 9000; //EmulatorRegistry.getPort(Emulators.DATABASE); + const databasePort = EmulatorRegistry.getPort(Emulators.DATABASE); if (!databasePort) { EmulatorLogger.log( "INFO", - `Ignoring trigger "${definition.name}" because the Realtime Database emulator is not running` + `Ignoring trigger "${ + definition.name + }" because the Realtime Database emulator is not running` ); return Promise.resolve(); } @@ -536,12 +538,14 @@ You can probably fix this by running "npm install ${ ); return Promise.reject(); } - const bundle = JSON.stringify([{ - name: `projects/${projectId}/locations/_/functions/${definition.name}`, - path: definition.eventTrigger.resource.split("refs")[1], - event: definition.eventTrigger.eventType, - topic: `projects/${projectId}/topics/_`, - }]); + const bundle = JSON.stringify([ + { + name: `projects/${projectId}/locations/_/functions/${definition.name}`, + path: definition.eventTrigger.resource.split("refs")[1], + event: definition.eventTrigger.eventType, + topic: `projects/${projectId}/topics/_`, + }, + ]); EmulatorLogger.logLabeled( "BULLET", @@ -550,12 +554,13 @@ You can probably fix this by running "npm install ${ ); logger.debug(`addDatabaseTrigger`, JSON.stringify(bundle)); return new Promise((resolve, reject) => { - request.put(`http://localhost:${databasePort}/.settings/functionTriggers.json`, + request.put( + `http://localhost:${databasePort}/.settings/functionTriggers.json`, { auth: { - bearer: "owner" + bearer: "owner", }, - body: bundle + body: bundle, }, (err, res, body) => { if (err) { @@ -565,7 +570,7 @@ You can probably fix this by running "npm install ${ } resolve(); } - ) + ); }); } From a830f41a5302f79e11d088cf587c15d89558943e Mon Sep 17 00:00:00 2001 From: Jan Wyszynski Date: Thu, 30 May 2019 11:57:08 -0700 Subject: [PATCH 14/18] Use regex to extract database-root-relative paths for trigger creation --- src/emulator/functionsEmulator.ts | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/src/emulator/functionsEmulator.ts b/src/emulator/functionsEmulator.ts index df11d41effe..1ec81bc5981 100644 --- a/src/emulator/functionsEmulator.ts +++ b/src/emulator/functionsEmulator.ts @@ -527,7 +527,7 @@ You can probably fix this by running "npm install ${ "INFO", `Ignoring trigger "${ definition.name - }" because the Realtime Database emulator is not running` + }" because the Realtime Database emulator is not running.` ); return Promise.resolve(); } @@ -538,10 +538,26 @@ You can probably fix this by running "npm install ${ ); return Promise.reject(); } + /* + * The Realtime Database emulator expects the `path` field in its trigger + * definition to be relative to the database root. + */ + const pathPattern = new RegExp("^projects/[^/]*/instances/[^/]*/refs(/.*)$"); + const result: string[] | null = pathPattern.exec(definition.eventTrigger.resource); + + if (result === null || result.length !== 2) { + EmulatorLogger.log( + "WARN", + `Event trigger "${definition.name}" has malformed "resource" member. ` + + `${definition.eventTrigger.resource}` + ); + return Promise.reject(); + } + const bundle = JSON.stringify([ { name: `projects/${projectId}/locations/_/functions/${definition.name}`, - path: definition.eventTrigger.resource.split("refs")[1], + path: result[1], // path stored in the first capture group event: definition.eventTrigger.eventType, topic: `projects/${projectId}/topics/_`, }, From 2cdc215c19e67b60071e81627c3f9567043c8d44 Mon Sep 17 00:00:00 2001 From: Jan Wyszynski Date: Thu, 30 May 2019 12:34:13 -0700 Subject: [PATCH 15/18] Make path extractor regex a constant, enforce non-empty instance and project --- src/emulator/functionsEmulator.ts | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/emulator/functionsEmulator.ts b/src/emulator/functionsEmulator.ts index 1ec81bc5981..b5fcdd94545 100644 --- a/src/emulator/functionsEmulator.ts +++ b/src/emulator/functionsEmulator.ts @@ -32,6 +32,14 @@ import { EmulatorLogger, Verbosity } from "./emulatorLogger"; const EVENT_INVOKE = "functions:invoke"; +/* + * The Realtime Database emulator expects the `path` field in its trigger + * definition to be relative to the database root. This regex is used to extract + * that path from the `resource` member in the trigger definition used by the + * functions emulator. + */ +const DATABASE_PATH_PATTERN = new RegExp("^projects/[^/]+/instances/[^/]+/refs(/.*)$"); + export interface FunctionsEmulatorArgs { port?: number; host?: string; @@ -538,13 +546,8 @@ You can probably fix this by running "npm install ${ ); return Promise.reject(); } - /* - * The Realtime Database emulator expects the `path` field in its trigger - * definition to be relative to the database root. - */ - const pathPattern = new RegExp("^projects/[^/]*/instances/[^/]*/refs(/.*)$"); - const result: string[] | null = pathPattern.exec(definition.eventTrigger.resource); + const result: string[] | null = DATABASE_PATH_PATTERN.exec(definition.eventTrigger.resource); if (result === null || result.length !== 2) { EmulatorLogger.log( "WARN", From 2cbd0ebc11e96bb45d3b557fa08abeb7cdfc98c3 Mon Sep 17 00:00:00 2001 From: Jan Wyszynski Date: Thu, 30 May 2019 13:03:06 -0700 Subject: [PATCH 16/18] Pass functions emulator arguments to database emulator when present --- src/emulator/controller.ts | 25 +++++++++++++++---------- src/emulator/databaseEmulator.ts | 2 ++ 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/src/emulator/controller.ts b/src/emulator/controller.ts index e1bb2500f9c..b7f101c8c66 100644 --- a/src/emulator/controller.ts +++ b/src/emulator/controller.ts @@ -154,17 +154,22 @@ export async function startAll(options: any): Promise { if (targets.indexOf(Emulators.DATABASE) > -1) { const databaseAddr = Constants.getAddress(Emulators.DATABASE, options); - const databaseEmulator = new DatabaseEmulator({ - host: databaseAddr.host, - port: databaseAddr.port, - }); + let databaseEmulator; + if (targets.indexOf(Emulators.FUNCTIONS) > -1) { + const functionsAddr = Constants.getAddress(Emulators.FUNCTIONS, options); + databaseEmulator = new DatabaseEmulator({ + host: databaseAddr.host, + port: databaseAddr.port, + functionsEmulatorHost: functionsAddr.host, + functionsEmulatorPort: functionsAddr.port, + }); + } else { + databaseEmulator = new DatabaseEmulator({ + host: databaseAddr.host, + port: databaseAddr.port, + }); + } await startEmulator(databaseEmulator); - - // TODO: When the database emulator is integrated with the Functions - // emulator, we will need to pass the port in and remove this warning - utils.logWarning( - `Note: the database emulator is not currently integrated with the functions emulator.` - ); } if (targets.indexOf(Emulators.HOSTING) > -1) { diff --git a/src/emulator/databaseEmulator.ts b/src/emulator/databaseEmulator.ts index b9b9b8b4f43..0b50d4f61ce 100644 --- a/src/emulator/databaseEmulator.ts +++ b/src/emulator/databaseEmulator.ts @@ -5,6 +5,8 @@ import { Constants } from "./constants"; interface DatabaseEmulatorArgs { port?: number; host?: string; + functionsEmulatorPort?: number; + functionsEmulatorHost?: string; } export class DatabaseEmulator implements EmulatorInstance { From cba83e2c6c08b43ce1a6763353edf7d037e0eeb1 Mon Sep 17 00:00:00 2001 From: Jan Wyszynski Date: Thu, 30 May 2019 18:16:29 -0700 Subject: [PATCH 17/18] Add trigger definition name to invocation payload topicName --- src/emulator/functionsEmulator.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/emulator/functionsEmulator.ts b/src/emulator/functionsEmulator.ts index b5fcdd94545..3de608b1072 100644 --- a/src/emulator/functionsEmulator.ts +++ b/src/emulator/functionsEmulator.ts @@ -562,7 +562,7 @@ You can probably fix this by running "npm install ${ name: `projects/${projectId}/locations/_/functions/${definition.name}`, path: result[1], // path stored in the first capture group event: definition.eventTrigger.eventType, - topic: `projects/${projectId}/topics/_`, + topic: `projects/${projectId}/topics/${definition.name}`, }, ]); From db05600d224b69ebdd7bd2c0c92b1cc3304789cb Mon Sep 17 00:00:00 2001 From: Jan Wyszynski Date: Fri, 31 May 2019 10:58:44 -0700 Subject: [PATCH 18/18] Use snake-case in database emulator args --- src/emulator/controller.ts | 4 ++-- src/emulator/databaseEmulator.ts | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/emulator/controller.ts b/src/emulator/controller.ts index b7f101c8c66..cec97b9ccf4 100644 --- a/src/emulator/controller.ts +++ b/src/emulator/controller.ts @@ -160,8 +160,8 @@ export async function startAll(options: any): Promise { databaseEmulator = new DatabaseEmulator({ host: databaseAddr.host, port: databaseAddr.port, - functionsEmulatorHost: functionsAddr.host, - functionsEmulatorPort: functionsAddr.port, + functions_emulator_host: functionsAddr.host, + functions_emulator_port: functionsAddr.port, }); } else { databaseEmulator = new DatabaseEmulator({ diff --git a/src/emulator/databaseEmulator.ts b/src/emulator/databaseEmulator.ts index 0b50d4f61ce..1449191e938 100644 --- a/src/emulator/databaseEmulator.ts +++ b/src/emulator/databaseEmulator.ts @@ -5,8 +5,8 @@ import { Constants } from "./constants"; interface DatabaseEmulatorArgs { port?: number; host?: string; - functionsEmulatorPort?: number; - functionsEmulatorHost?: string; + functions_emulator_port?: number; + functions_emulator_host?: string; } export class DatabaseEmulator implements EmulatorInstance {