Skip to content

Commit e42fe3a

Browse files
authored
Adds RTDB triggers for v2 functions (#1127)
* moving DataSnapshot to common, removing lodash, and adding in initial v2 interface and logic * fixing tests * normalize path & remove commented structure * changing default rtdb url * defualt to * instances and export everything correctly * linter * fixed parsing for multi segments and adding in tests * update tsdoc comments and add changelog * fixing export path * cleaning up params code * ref is always a path pattern * addressing comments and creating path pattern class * linter * adding describe block * adding in changes from @rhodgkins to match the admin SDK * linter
1 parent 40f0b43 commit e42fe3a

File tree

11 files changed

+1816
-303
lines changed

11 files changed

+1816
-303
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
- Adds RTDB Triggers for v2 functions (#1127)

package.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,8 @@
6565
"./v2/alerts/billing": "./lib/v2/providers/alerts/billing.js",
6666
"./v2/alerts/crashlytics": "./lib/v2/providers/alerts/crashlytics.js",
6767
"./v2/eventarc": "./lib/v2/providers/eventarc.js",
68-
"./v2/identity": "./lib/v2/providers/identity.js"
68+
"./v2/identity": "./lib/v2/providers/identity.js",
69+
"./v2/database": "./lib/v2/providers/database.js"
6970
},
7071
"typesVersions": {
7172
"*": {
@@ -126,6 +127,9 @@
126127
"v2/base": [
127128
"lib/v2/base"
128129
],
130+
"v2/database": [
131+
"lib/v2/providers/database"
132+
],
129133
"v2/eventarc": [
130134
"lib/v2/providers/eventarc"
131135
],

spec/utilities/path-pattern.spec.ts

Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
// The MIT License (MIT)
2+
//
3+
// Copyright (c) 2022 Firebase
4+
//
5+
// Permission is hereby granted, free of charge, to any person obtaining a copy
6+
// of this software and associated documentation files (the "Software"), to deal
7+
// in the Software without restriction, including without limitation the rights
8+
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
// copies of the Software, and to permit persons to whom the Software is
10+
// furnished to do so, subject to the following conditions:
11+
//
12+
// The above copyright notice and this permission notice shall be included in all
13+
// copies or substantial portions of the Software.
14+
//
15+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
// SOFTWARE.
22+
import { expect } from 'chai';
23+
import * as pathPattern from '../../src/utilities/path-pattern';
24+
25+
describe('path-pattern', () => {
26+
describe('trimParam', () => {
27+
it('should trim a capture param without equals', () => {
28+
expect(pathPattern.trimParam('{something}')).to.equal('something');
29+
});
30+
31+
it('should trim a capture param with equals', () => {
32+
expect(pathPattern.trimParam('{something=*}')).to.equal('something');
33+
});
34+
});
35+
36+
describe('extractMatches', () => {
37+
it('should parse without multi segment', () => {
38+
const pp = new pathPattern.PathPattern('{a}/something/else/{b}/end/{c}');
39+
40+
expect(
41+
pp.extractMatches('match_a/something/else/match_b/end/match_c')
42+
).to.deep.equal({
43+
a: 'match_a',
44+
b: 'match_b',
45+
c: 'match_c',
46+
});
47+
});
48+
49+
it('should parse multi segment with params after', () => {
50+
const pp = new pathPattern.PathPattern(
51+
'something/**/else/{a}/hello/{b}/world'
52+
);
53+
54+
expect(
55+
pp.extractMatches('something/is/a/thing/else/nothing/hello/user/world')
56+
).to.deep.equal({
57+
a: 'nothing',
58+
b: 'user',
59+
});
60+
});
61+
62+
it('should parse multi segment param with params after', () => {
63+
const pp = new pathPattern.PathPattern(
64+
'something/{path=**}/else/{a}/hello/{b}/world'
65+
);
66+
67+
expect(
68+
pp.extractMatches('something/is/a/thing/else/nothing/hello/user/world')
69+
).to.deep.equal({
70+
path: 'is/a/thing',
71+
a: 'nothing',
72+
b: 'user',
73+
});
74+
});
75+
76+
it('should parse multi segment with params before', () => {
77+
const pp = new pathPattern.PathPattern('{a}/something/{b}/**/end');
78+
79+
expect(
80+
pp.extractMatches(
81+
'match_a/something/match_b/thing/else/nothing/hello/user/end'
82+
)
83+
).to.deep.equal({
84+
a: 'match_a',
85+
b: 'match_b',
86+
});
87+
});
88+
89+
it('should parse multi segment param with params before', () => {
90+
const pp = new pathPattern.PathPattern('{a}/something/{b}/{path=**}/end');
91+
92+
expect(
93+
pp.extractMatches(
94+
'match_a/something/match_b/thing/else/nothing/hello/user/end'
95+
)
96+
).to.deep.equal({
97+
a: 'match_a',
98+
b: 'match_b',
99+
path: 'thing/else/nothing/hello/user',
100+
});
101+
});
102+
103+
it('should parse multi segment with params before and after', () => {
104+
const pp = new pathPattern.PathPattern('{a}/something/**/{b}/end');
105+
106+
expect(
107+
pp.extractMatches(
108+
'match_a/something/thing/else/nothing/hello/user/match_b/end'
109+
)
110+
).to.deep.equal({
111+
a: 'match_a',
112+
b: 'match_b',
113+
});
114+
});
115+
116+
it('should parse multi segment param with params before', () => {
117+
const pp = new pathPattern.PathPattern('{a}/something/{path=**}/{b}/end');
118+
119+
expect(
120+
pp.extractMatches(
121+
'match_a/something/thing/else/nothing/hello/user/match_b/end'
122+
)
123+
).to.deep.equal({
124+
a: 'match_a',
125+
b: 'match_b',
126+
path: 'thing/else/nothing/hello/user',
127+
});
128+
});
129+
130+
// handle an instance param
131+
it('should parse an instance', () => {
132+
const pp = new pathPattern.PathPattern('{a}-something-{b}-else-{c}');
133+
134+
expect(
135+
pp.extractMatches('match_a-something-match_b-else-match_c')
136+
).to.deep.equal({});
137+
138+
const anotherPP = new pathPattern.PathPattern('{a}');
139+
140+
expect(anotherPP.extractMatches('match_a')).to.deep.equal({
141+
a: 'match_a',
142+
});
143+
});
144+
});
145+
});

spec/v1/providers/database.spec.ts

Lines changed: 134 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -639,17 +639,52 @@ describe('Database Functions', () => {
639639
expect(subject.val()).to.equal(0);
640640
populate({ myKey: 0 });
641641
expect(subject.val()).to.deep.equal({ myKey: 0 });
642-
643-
// Null values are still reported as null.
644-
populate({ myKey: null });
645-
expect(subject.val()).to.deep.equal({ myKey: null });
646642
});
647643

648644
// Regression test: .val() was returning array of nulls when there's a property called length (BUG#37683995)
649645
it('should return correct values when data has "length" property', () => {
650646
populate({ length: 3, foo: 'bar' });
651647
expect(subject.val()).to.deep.equal({ length: 3, foo: 'bar' });
652648
});
649+
650+
it('should deal with null-values appropriately', () => {
651+
populate(null);
652+
expect(subject.val()).to.be.null;
653+
654+
populate({ myKey: null });
655+
expect(subject.val()).to.be.null;
656+
});
657+
658+
it('should deal with empty object values appropriately', () => {
659+
populate({});
660+
expect(subject.val()).to.be.null;
661+
662+
populate({ myKey: {} });
663+
expect(subject.val()).to.be.null;
664+
665+
populate({ myKey: { child: null } });
666+
expect(subject.val()).to.be.null;
667+
});
668+
669+
it('should deal with empty array values appropriately', () => {
670+
populate([]);
671+
expect(subject.val()).to.be.null;
672+
673+
populate({ myKey: [] });
674+
expect(subject.val()).to.be.null;
675+
676+
populate({ myKey: [null] });
677+
expect(subject.val()).to.be.null;
678+
679+
populate({ myKey: [{}] });
680+
expect(subject.val()).to.be.null;
681+
682+
populate({ myKey: [{ myKey: null }] });
683+
expect(subject.val()).to.be.null;
684+
685+
populate({ myKey: [{ myKey: {} }] });
686+
expect(subject.val()).to.be.null;
687+
});
653688
});
654689

655690
describe('#child(): DataSnapshot', () => {
@@ -676,14 +711,37 @@ describe('Database Functions', () => {
676711
});
677712

678713
it('should be false for a non-existent value', () => {
679-
populate({ a: { b: 'c' } });
714+
populate({ a: { b: 'c', nullChild: null } });
680715
expect(subject.child('d').exists()).to.be.false;
716+
expect(subject.child('nullChild').exists()).to.be.false;
681717
});
682718

683719
it('should be false for a value pathed beyond a leaf', () => {
684720
populate({ a: { b: 'c' } });
685721
expect(subject.child('a/b/c').exists()).to.be.false;
686722
});
723+
724+
it('should be false for an empty object value', () => {
725+
populate({ a: {} });
726+
expect(subject.child('a').exists()).to.be.false;
727+
728+
populate({ a: { child: null } });
729+
expect(subject.child('a').exists()).to.be.false;
730+
731+
populate({ a: { child: {} } });
732+
expect(subject.child('a').exists()).to.be.false;
733+
});
734+
735+
it('should be false for an empty array value', () => {
736+
populate({ a: [] });
737+
expect(subject.child('a').exists()).to.be.false;
738+
739+
populate({ a: [null] });
740+
expect(subject.child('a').exists()).to.be.false;
741+
742+
populate({ a: [{}] });
743+
expect(subject.child('a').exists()).to.be.false;
744+
});
687745
});
688746

689747
describe('#forEach(action: (a: DataSnapshot) => boolean): boolean', () => {
@@ -712,6 +770,17 @@ describe('Database Functions', () => {
712770

713771
expect(subject.forEach(counter)).to.equal(false);
714772
expect(count).to.eq(0);
773+
774+
populate({
775+
a: 'foo',
776+
nullChild: null,
777+
emptyObjectChild: {},
778+
emptyArrayChild: [],
779+
});
780+
count = 0;
781+
782+
expect(subject.forEach(counter)).to.equal(false);
783+
expect(count).to.eq(1);
715784
});
716785

717786
it('should cancel further enumeration if callback returns true', () => {
@@ -751,13 +820,51 @@ describe('Database Functions', () => {
751820

752821
describe('#numChildren()', () => {
753822
it('should be key count for objects', () => {
754-
populate({ a: 'b', c: 'd' });
823+
populate({
824+
a: 'b',
825+
c: 'd',
826+
nullChild: null,
827+
emptyObjectChild: {},
828+
emptyArrayChild: [],
829+
});
755830
expect(subject.numChildren()).to.eq(2);
756831
});
757832

758833
it('should be 0 for non-objects', () => {
759834
populate(23);
760835
expect(subject.numChildren()).to.eq(0);
836+
837+
populate({
838+
nullChild: null,
839+
emptyObjectChild: {},
840+
emptyArrayChild: [],
841+
});
842+
expect(subject.numChildren()).to.eq(0);
843+
});
844+
});
845+
846+
describe('#hasChildren()', () => {
847+
it('should true for objects', () => {
848+
populate({
849+
a: 'b',
850+
c: 'd',
851+
nullChild: null,
852+
emptyObjectChild: {},
853+
emptyArrayChild: [],
854+
});
855+
expect(subject.hasChildren()).to.be.true;
856+
});
857+
858+
it('should be false for non-objects', () => {
859+
populate(23);
860+
expect(subject.hasChildren()).to.be.false;
861+
862+
populate({
863+
nullChild: null,
864+
emptyObjectChild: {},
865+
emptyArrayChild: [],
866+
});
867+
expect(subject.hasChildren()).to.be.false;
761868
});
762869
});
763870

@@ -769,9 +876,17 @@ describe('Database Functions', () => {
769876
});
770877

771878
it('should return false if a child is missing', () => {
772-
populate({ a: 'b' });
879+
populate({
880+
a: 'b',
881+
nullChild: null,
882+
emptyObjectChild: {},
883+
emptyArrayChild: [],
884+
});
773885
expect(subject.hasChild('c')).to.be.false;
774886
expect(subject.hasChild('a/b')).to.be.false;
887+
expect(subject.hasChild('nullChild')).to.be.false;
888+
expect(subject.hasChild('emptyObjectChild')).to.be.false;
889+
expect(subject.hasChild('emptyArrayChild')).to.be.false;
775890
});
776891
});
777892

@@ -801,11 +916,21 @@ describe('Database Functions', () => {
801916

802917
describe('#toJSON(): Object', () => {
803918
it('should return the current value', () => {
804-
populate({ a: 'b' });
919+
populate({
920+
a: 'b',
921+
nullChild: null,
922+
emptyObjectChild: {},
923+
emptyArrayChild: [],
924+
});
805925
expect(subject.toJSON()).to.deep.equal(subject.val());
806926
});
807927
it('should be stringifyable', () => {
808-
populate({ a: 'b' });
928+
populate({
929+
a: 'b',
930+
nullChild: null,
931+
emptyObjectChild: {},
932+
emptyArrayChild: [],
933+
});
809934
expect(JSON.stringify(subject)).to.deep.equal('{"a":"b"}');
810935
});
811936
});

0 commit comments

Comments
 (0)