From 7d122503ef37428deb1ba7d2438b6b386d81906d Mon Sep 17 00:00:00 2001 From: Fred Zhang Date: Fri, 8 May 2020 11:29:02 -0700 Subject: [PATCH 01/11] use domain specified in context --- src/providers/database.ts | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/src/providers/database.ts b/src/providers/database.ts index e648be20e..78f41d454 100644 --- a/src/providers/database.ts +++ b/src/providers/database.ts @@ -215,9 +215,7 @@ export class RefBuilder { ) => PromiseLike | any ): CloudFunction { const dataConstructor = (raw: Event) => { - const [dbInstance, path] = resourceToInstanceAndPath( - raw.context.resource.name - ); + const [dbInstance, path] = extractInstanceAndPath(raw); return new DataSnapshot( raw.data.delta, path, @@ -243,9 +241,7 @@ export class RefBuilder { ) => PromiseLike | any ): CloudFunction { const dataConstructor = (raw: Event) => { - const [dbInstance, path] = resourceToInstanceAndPath( - raw.context.resource.name - ); + const [dbInstance, path] = extractInstanceAndPath(raw); return new DataSnapshot(raw.data.data, path, this.apps.admin, dbInstance); }; return this.onOperation(handler, 'ref.delete', dataConstructor); @@ -271,9 +267,7 @@ export class RefBuilder { } private changeConstructor = (raw: Event): Change => { - const [dbInstance, path] = resourceToInstanceAndPath( - raw.context.resource.name - ); + const [dbInstance, path] = extractInstanceAndPath(raw); const before = new DataSnapshot( raw.data.data, path, @@ -295,7 +289,8 @@ export class RefBuilder { /* Utility function to extract database reference from resource string */ /** @hidden */ -export function resourceToInstanceAndPath(resource: string) { +export function extractInstanceAndPath(raw: Event) { + const resource = raw.context.resource.name const resourceRegex = `projects/([^/]+)/instances/([a-zA-Z0-9\-^/]+)/refs(/.+)?`; const match = resource.match(new RegExp(resourceRegex)); if (!match) { @@ -310,7 +305,11 @@ export function resourceToInstanceAndPath(resource: string) { `Expect project to be '_' in a Firebase Realtime Database event` ); } - const dbInstance = 'https://' + dbInstanceName + '.firebaseio.com'; + let domain = "firebaseio.com"; + if (raw.context.domain) { + domain = raw.context.domain; + } + const dbInstance = 'https://' + dbInstanceName + '.' + domain; return [dbInstance, path]; } From 1ba15d4640e2b771b55797691b8bf748cb389733 Mon Sep 17 00:00:00 2001 From: Fred Zhang Date: Fri, 8 May 2020 11:35:54 -0700 Subject: [PATCH 02/11] doc --- src/providers/database.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/providers/database.ts b/src/providers/database.ts index 78f41d454..5250f64cf 100644 --- a/src/providers/database.ts +++ b/src/providers/database.ts @@ -307,6 +307,9 @@ export function extractInstanceAndPath(raw: Event) { } let domain = "firebaseio.com"; if (raw.context.domain) { + // See go/rtdb-multi-region-function-sdk. + // Multi-region RTDB are served from different domains. + // Since region information is not part of resource name, it is provided through context. domain = raw.context.domain; } const dbInstance = 'https://' + dbInstanceName + '.' + domain; From 1f2c3b38fe3829cfb3372bf2c97f538cab9fd59a Mon Sep 17 00:00:00 2001 From: Fred Zhang Date: Fri, 8 May 2020 11:39:29 -0700 Subject: [PATCH 03/11] d --- src/providers/database.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/providers/database.ts b/src/providers/database.ts index 5250f64cf..c387a0f26 100644 --- a/src/providers/database.ts +++ b/src/providers/database.ts @@ -309,7 +309,7 @@ export function extractInstanceAndPath(raw: Event) { if (raw.context.domain) { // See go/rtdb-multi-region-function-sdk. // Multi-region RTDB are served from different domains. - // Since region information is not part of resource name, it is provided through context. + // Since region is not part of the resource name, it is provided through context. domain = raw.context.domain; } const dbInstance = 'https://' + dbInstanceName + '.' + domain; From 264faf0ef7375318cf3bf0f4ff3bf01763744f3a Mon Sep 17 00:00:00 2001 From: Fred Zhang Date: Fri, 8 May 2020 12:02:02 -0700 Subject: [PATCH 04/11] use lodash --- src/providers/database.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/providers/database.ts b/src/providers/database.ts index c387a0f26..3a420571f 100644 --- a/src/providers/database.ts +++ b/src/providers/database.ts @@ -289,8 +289,8 @@ export class RefBuilder { /* Utility function to extract database reference from resource string */ /** @hidden */ -export function extractInstanceAndPath(raw: Event) { - const resource = raw.context.resource.name +export function extractInstanceAndPath(event: Event) { + const resource = event.context.resource.name const resourceRegex = `projects/([^/]+)/instances/([a-zA-Z0-9\-^/]+)/refs(/.+)?`; const match = resource.match(new RegExp(resourceRegex)); if (!match) { @@ -306,11 +306,11 @@ export function extractInstanceAndPath(raw: Event) { ); } let domain = "firebaseio.com"; - if (raw.context.domain) { + if (_.has(event, 'context.domain')) { // See go/rtdb-multi-region-function-sdk. // Multi-region RTDB are served from different domains. // Since region is not part of the resource name, it is provided through context. - domain = raw.context.domain; + domain = _.get(event, 'context.domain'); } const dbInstance = 'https://' + dbInstanceName + '.' + domain; return [dbInstance, path]; From 0cdc378b42fc56261f4021e4d0afc0355ccdb761 Mon Sep 17 00:00:00 2001 From: Fred Zhang Date: Fri, 8 May 2020 13:46:55 -0700 Subject: [PATCH 05/11] t --- spec/providers/database.spec.ts | 62 ++++++++++++++++++++++++++------- src/providers/database.ts | 2 +- 2 files changed, 50 insertions(+), 14 deletions(-) diff --git a/spec/providers/database.spec.ts b/spec/providers/database.spec.ts index 25293ac11..07da5fa8b 100644 --- a/spec/providers/database.spec.ts +++ b/spec/providers/database.spec.ts @@ -424,10 +424,16 @@ describe('Database Functions', () => { }); }); - describe('resourceToInstanceAndPath', () => { + describe('extractInstanceAndPath', () => { it('should return the correct instance and path strings', () => { - const [instance, path] = database.resourceToInstanceAndPath( - 'projects/_/instances/foo/refs/bar' + const [instance, path] = database.extractInstanceAndPath( + { + context: { + resource: { + name: 'projects/_/instances/foo/refs/bar' + } + } + } ); expect(instance).to.equal('https://foo.firebaseio.com'); expect(path).to.equal('/bar'); @@ -435,18 +441,36 @@ describe('Database Functions', () => { it('should throw an error if the given instance name contains anything except alphanumerics and dashes', () => { expect(() => { - return database.resourceToInstanceAndPath( - 'projects/_/instances/a.bad.name/refs/bar' + return database.extractInstanceAndPath( + { + context: { + resource: { + name: 'projects/_/instances/a.bad.name/refs/bar' + } + } + } ); }).to.throw(Error); expect(() => { - return database.resourceToInstanceAndPath( - 'projects/_/instances/a_different_bad_name/refs/bar' + return database.extractInstanceAndPath( + { + context: { + resource: { + name: 'projects/_/instances/a_different_bad_name/refs/bar' + } + } + } ); }).to.throw(Error); expect(() => { - return database.resourceToInstanceAndPath( - 'projects/_/instances/BAD!!!!/refs/bar' + return database.extractInstanceAndPath( + { + context: { + resource: { + name: 'projects/_/instances/BAD!!!!/refs/bar' + } + } + } ); }).to.throw(Error); }); @@ -457,8 +481,14 @@ describe('Database Functions', () => { const apps = new appsNamespace.Apps(); const populate = (data: any) => { - const [instance, path] = database.resourceToInstanceAndPath( - 'projects/_/instances/other-subdomain/refs/foo' + const [instance, path] = database.extractInstanceAndPath( + { + context: { + resource: { + name: 'projects/_/instances/other-subdomain/refs/foo' + } + } + } ); subject = new database.DataSnapshot(data, path, apps.admin, instance); }; @@ -648,8 +678,14 @@ describe('Database Functions', () => { }); it('should return null for the root', () => { - const [instance, path] = database.resourceToInstanceAndPath( - 'projects/_/instances/foo/refs/' + const [instance, path] = database.extractInstanceAndPath( + { + context: { + resource: { + name: 'projects/_/instances/foo/refs/' + } + } + } ); const snapshot = new database.DataSnapshot( null, diff --git a/src/providers/database.ts b/src/providers/database.ts index 3a420571f..c3e71336e 100644 --- a/src/providers/database.ts +++ b/src/providers/database.ts @@ -289,7 +289,7 @@ export class RefBuilder { /* Utility function to extract database reference from resource string */ /** @hidden */ -export function extractInstanceAndPath(event: Event) { +export function extractInstanceAndPath(event: any) { const resource = event.context.resource.name const resourceRegex = `projects/([^/]+)/instances/([a-zA-Z0-9\-^/]+)/refs(/.+)?`; const match = resource.match(new RegExp(resourceRegex)); From 87ca663fbbc6f9b0b06eae12449f5341c33e7e99 Mon Sep 17 00:00:00 2001 From: Fred Zhang Date: Fri, 8 May 2020 14:12:21 -0700 Subject: [PATCH 06/11] tests --- package.json | 4 ++-- spec/providers/database.spec.ts | 37 ++++++++++++++++++++++++++++++--- src/providers/database.ts | 13 +++++++----- 3 files changed, 44 insertions(+), 10 deletions(-) diff --git a/package.json b/package.json index 3fab11e33..744965837 100644 --- a/package.json +++ b/package.json @@ -58,7 +58,7 @@ "chai": "^4.2.0", "chai-as-promised": "^7.1.1", "child-process-promise": "^2.2.1", - "firebase-admin": "^8.2.0", + "firebase-admin": "^8.12.1", "istanbul": "^0.4.5", "js-yaml": "^3.13.1", "jsdom": "^16.2.1", @@ -78,7 +78,7 @@ "yargs": "^15.3.1" }, "peerDependencies": { - "firebase-admin": "^8.0.0" + "firebase-admin": "^8.12.1" }, "engines": { "node": "^8.13.0 || >=10.10.0" diff --git a/spec/providers/database.spec.ts b/spec/providers/database.spec.ts index 07da5fa8b..8996e1510 100644 --- a/spec/providers/database.spec.ts +++ b/spec/providers/database.spec.ts @@ -425,7 +425,7 @@ describe('Database Functions', () => { }); describe('extractInstanceAndPath', () => { - it('should return the correct instance and path strings', () => { + it('should return the correct instance with default domain and path strings', () => { const [instance, path] = database.extractInstanceAndPath( { context: { @@ -439,6 +439,36 @@ describe('Database Functions', () => { expect(path).to.equal('/bar'); }); + it('should return the correct staging instance and path strings', () => { + const [instance, path] = database.extractInstanceAndPath( + { + context: { + resource: { + name: 'projects/_/instances/foo/refs/bar' + }, + domain: 'firebaseio.com' + }, + } + ); + expect(instance).to.equal('https://foo.firebaseio.com'); + expect(path).to.equal('/bar'); + }); + + it('should return the correct multi-region instance and path strings', () => { + const [instance, path] = database.extractInstanceAndPath( + { + context: { + resource: { + name: 'projects/_/instances/foo/refs/bar' + }, + domain: 'euw1.firebasedatabase.app' + } + } + ); + expect(instance).to.equal('https://foo.euw1.firebasedatabase.app'); + expect(path).to.equal('/bar'); + }); + it('should throw an error if the given instance name contains anything except alphanumerics and dashes', () => { expect(() => { return database.extractInstanceAndPath( @@ -486,7 +516,8 @@ describe('Database Functions', () => { context: { resource: { name: 'projects/_/instances/other-subdomain/refs/foo' - } + }, + domain: 'firebaseio-staging.com' } } ); @@ -497,7 +528,7 @@ describe('Database Functions', () => { it('should return a ref for correct instance, not the default instance', () => { populate({}); expect(subject.ref.toJSON()).to.equal( - 'https://other-subdomain.firebaseio.com/foo' + 'https://other-subdomain.firebaseio-staging.com/foo' ); }); }); diff --git a/src/providers/database.ts b/src/providers/database.ts index c3e71336e..2d37d7fd9 100644 --- a/src/providers/database.ts +++ b/src/providers/database.ts @@ -287,10 +287,13 @@ export class RefBuilder { }; } -/* Utility function to extract database reference from resource string */ +/** + * Utility function to extract database reference from resource string. + * The raw event may contain fields not part of the public API. + */ /** @hidden */ -export function extractInstanceAndPath(event: any) { - const resource = event.context.resource.name +export function extractInstanceAndPath(rawEvent: any) { + const resource = rawEvent.context.resource.name const resourceRegex = `projects/([^/]+)/instances/([a-zA-Z0-9\-^/]+)/refs(/.+)?`; const match = resource.match(new RegExp(resourceRegex)); if (!match) { @@ -306,11 +309,11 @@ export function extractInstanceAndPath(event: any) { ); } let domain = "firebaseio.com"; - if (_.has(event, 'context.domain')) { + if (_.has(rawEvent, 'context.domain')) { // See go/rtdb-multi-region-function-sdk. // Multi-region RTDB are served from different domains. // Since region is not part of the resource name, it is provided through context. - domain = _.get(event, 'context.domain'); + domain = _.get(rawEvent, 'context.domain'); } const dbInstance = 'https://' + dbInstanceName + '.' + domain; return [dbInstance, path]; From 920898f89e71ddf184c7795612cf58be809b1033 Mon Sep 17 00:00:00 2001 From: Fred Zhang Date: Fri, 8 May 2020 14:13:37 -0700 Subject: [PATCH 07/11] tests --- spec/providers/database.spec.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/providers/database.spec.ts b/spec/providers/database.spec.ts index 8996e1510..cc0e2214a 100644 --- a/spec/providers/database.spec.ts +++ b/spec/providers/database.spec.ts @@ -446,11 +446,11 @@ describe('Database Functions', () => { resource: { name: 'projects/_/instances/foo/refs/bar' }, - domain: 'firebaseio.com' + domain: 'firebaseio-staging.com' }, } ); - expect(instance).to.equal('https://foo.firebaseio.com'); + expect(instance).to.equal('https://foo.firebaseio-staging.com'); expect(path).to.equal('/bar'); }); From f460e4c5edbe95110d9d5d4fea084274fe162ea5 Mon Sep 17 00:00:00 2001 From: Fred Zhang Date: Fri, 8 May 2020 14:14:59 -0700 Subject: [PATCH 08/11] doc --- spec/providers/database.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/providers/database.spec.ts b/spec/providers/database.spec.ts index cc0e2214a..9f847bf07 100644 --- a/spec/providers/database.spec.ts +++ b/spec/providers/database.spec.ts @@ -425,7 +425,7 @@ describe('Database Functions', () => { }); describe('extractInstanceAndPath', () => { - it('should return the correct instance with default domain and path strings', () => { + it('should return the correct us-central prod instance and path strings', () => { const [instance, path] = database.extractInstanceAndPath( { context: { From dd2d9eff38e853876fb49e618def66ae4414317d Mon Sep 17 00:00:00 2001 From: Fred Zhang Date: Fri, 8 May 2020 14:15:27 -0700 Subject: [PATCH 09/11] revert --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 744965837..3fab11e33 100644 --- a/package.json +++ b/package.json @@ -58,7 +58,7 @@ "chai": "^4.2.0", "chai-as-promised": "^7.1.1", "child-process-promise": "^2.2.1", - "firebase-admin": "^8.12.1", + "firebase-admin": "^8.2.0", "istanbul": "^0.4.5", "js-yaml": "^3.13.1", "jsdom": "^16.2.1", @@ -78,7 +78,7 @@ "yargs": "^15.3.1" }, "peerDependencies": { - "firebase-admin": "^8.12.1" + "firebase-admin": "^8.0.0" }, "engines": { "node": "^8.13.0 || >=10.10.0" From 685f62fb1f9e85d0e582c5f913d87686cea216ea Mon Sep 17 00:00:00 2001 From: Fred Zhang Date: Fri, 8 May 2020 14:19:20 -0700 Subject: [PATCH 10/11] format --- spec/providers/database.spec.ts | 132 ++++++++++++++------------------ src/providers/database.ts | 4 +- 2 files changed, 60 insertions(+), 76 deletions(-) diff --git a/spec/providers/database.spec.ts b/spec/providers/database.spec.ts index 9f847bf07..469dc4551 100644 --- a/spec/providers/database.spec.ts +++ b/spec/providers/database.spec.ts @@ -426,82 +426,70 @@ describe('Database Functions', () => { describe('extractInstanceAndPath', () => { it('should return the correct us-central prod instance and path strings', () => { - const [instance, path] = database.extractInstanceAndPath( - { - context: { - resource: { - name: 'projects/_/instances/foo/refs/bar' - } - } - } - ); + const [instance, path] = database.extractInstanceAndPath({ + context: { + resource: { + name: 'projects/_/instances/foo/refs/bar', + }, + }, + }); expect(instance).to.equal('https://foo.firebaseio.com'); expect(path).to.equal('/bar'); }); it('should return the correct staging instance and path strings', () => { - const [instance, path] = database.extractInstanceAndPath( - { - context: { - resource: { - name: 'projects/_/instances/foo/refs/bar' - }, - domain: 'firebaseio-staging.com' + const [instance, path] = database.extractInstanceAndPath({ + context: { + resource: { + name: 'projects/_/instances/foo/refs/bar', }, - } - ); + domain: 'firebaseio-staging.com', + }, + }); expect(instance).to.equal('https://foo.firebaseio-staging.com'); expect(path).to.equal('/bar'); }); it('should return the correct multi-region instance and path strings', () => { - const [instance, path] = database.extractInstanceAndPath( - { - context: { - resource: { - name: 'projects/_/instances/foo/refs/bar' - }, - domain: 'euw1.firebasedatabase.app' - } - } - ); + const [instance, path] = database.extractInstanceAndPath({ + context: { + resource: { + name: 'projects/_/instances/foo/refs/bar', + }, + domain: 'euw1.firebasedatabase.app', + }, + }); expect(instance).to.equal('https://foo.euw1.firebasedatabase.app'); expect(path).to.equal('/bar'); }); it('should throw an error if the given instance name contains anything except alphanumerics and dashes', () => { expect(() => { - return database.extractInstanceAndPath( - { - context: { - resource: { - name: 'projects/_/instances/a.bad.name/refs/bar' - } - } - } - ); + return database.extractInstanceAndPath({ + context: { + resource: { + name: 'projects/_/instances/a.bad.name/refs/bar', + }, + }, + }); }).to.throw(Error); expect(() => { - return database.extractInstanceAndPath( - { - context: { - resource: { - name: 'projects/_/instances/a_different_bad_name/refs/bar' - } - } - } - ); + return database.extractInstanceAndPath({ + context: { + resource: { + name: 'projects/_/instances/a_different_bad_name/refs/bar', + }, + }, + }); }).to.throw(Error); expect(() => { - return database.extractInstanceAndPath( - { - context: { - resource: { - name: 'projects/_/instances/BAD!!!!/refs/bar' - } - } - } - ); + return database.extractInstanceAndPath({ + context: { + resource: { + name: 'projects/_/instances/BAD!!!!/refs/bar', + }, + }, + }); }).to.throw(Error); }); }); @@ -511,16 +499,14 @@ describe('Database Functions', () => { const apps = new appsNamespace.Apps(); const populate = (data: any) => { - const [instance, path] = database.extractInstanceAndPath( - { - context: { - resource: { - name: 'projects/_/instances/other-subdomain/refs/foo' - }, - domain: 'firebaseio-staging.com' - } - } - ); + const [instance, path] = database.extractInstanceAndPath({ + context: { + resource: { + name: 'projects/_/instances/other-subdomain/refs/foo', + }, + domain: 'firebaseio-staging.com', + }, + }); subject = new database.DataSnapshot(data, path, apps.admin, instance); }; @@ -709,15 +695,13 @@ describe('Database Functions', () => { }); it('should return null for the root', () => { - const [instance, path] = database.extractInstanceAndPath( - { - context: { - resource: { - name: 'projects/_/instances/foo/refs/' - } - } - } - ); + const [instance, path] = database.extractInstanceAndPath({ + context: { + resource: { + name: 'projects/_/instances/foo/refs/', + }, + }, + }); const snapshot = new database.DataSnapshot( null, path, diff --git a/src/providers/database.ts b/src/providers/database.ts index 2d37d7fd9..16d6b3736 100644 --- a/src/providers/database.ts +++ b/src/providers/database.ts @@ -293,7 +293,7 @@ export class RefBuilder { */ /** @hidden */ export function extractInstanceAndPath(rawEvent: any) { - const resource = rawEvent.context.resource.name + const resource = rawEvent.context.resource.name; const resourceRegex = `projects/([^/]+)/instances/([a-zA-Z0-9\-^/]+)/refs(/.+)?`; const match = resource.match(new RegExp(resourceRegex)); if (!match) { @@ -308,7 +308,7 @@ export function extractInstanceAndPath(rawEvent: any) { `Expect project to be '_' in a Firebase Realtime Database event` ); } - let domain = "firebaseio.com"; + let domain = 'firebaseio.com'; if (_.has(rawEvent, 'context.domain')) { // See go/rtdb-multi-region-function-sdk. // Multi-region RTDB are served from different domains. From f181a4ef91e60370ca6b1afc21d0c033a4498944 Mon Sep 17 00:00:00 2001 From: Fred Zhang Date: Fri, 8 May 2020 16:29:32 -0700 Subject: [PATCH 11/11] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e69de29bb..7f3b10154 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -0,0 +1 @@ +- Adds support for multi-region RTDB.