Skip to content

Commit

Permalink
Adds RTDB triggers for v2 functions (#1127)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
colerogers committed Jun 9, 2022
1 parent 40f0b43 commit e42fe3a
Show file tree
Hide file tree
Showing 11 changed files with 1,816 additions and 303 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
- Adds RTDB Triggers for v2 functions (#1127)
6 changes: 5 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,8 @@
"./v2/alerts/billing": "./lib/v2/providers/alerts/billing.js",
"./v2/alerts/crashlytics": "./lib/v2/providers/alerts/crashlytics.js",
"./v2/eventarc": "./lib/v2/providers/eventarc.js",
"./v2/identity": "./lib/v2/providers/identity.js"
"./v2/identity": "./lib/v2/providers/identity.js",
"./v2/database": "./lib/v2/providers/database.js"
},
"typesVersions": {
"*": {
Expand Down Expand Up @@ -126,6 +127,9 @@
"v2/base": [
"lib/v2/base"
],
"v2/database": [
"lib/v2/providers/database"
],
"v2/eventarc": [
"lib/v2/providers/eventarc"
],
Expand Down
145 changes: 145 additions & 0 deletions spec/utilities/path-pattern.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
// The MIT License (MIT)
//
// Copyright (c) 2022 Firebase
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
import { expect } from 'chai';
import * as pathPattern from '../../src/utilities/path-pattern';

describe('path-pattern', () => {
describe('trimParam', () => {
it('should trim a capture param without equals', () => {
expect(pathPattern.trimParam('{something}')).to.equal('something');
});

it('should trim a capture param with equals', () => {
expect(pathPattern.trimParam('{something=*}')).to.equal('something');
});
});

describe('extractMatches', () => {
it('should parse without multi segment', () => {
const pp = new pathPattern.PathPattern('{a}/something/else/{b}/end/{c}');

expect(
pp.extractMatches('match_a/something/else/match_b/end/match_c')
).to.deep.equal({
a: 'match_a',
b: 'match_b',
c: 'match_c',
});
});

it('should parse multi segment with params after', () => {
const pp = new pathPattern.PathPattern(
'something/**/else/{a}/hello/{b}/world'
);

expect(
pp.extractMatches('something/is/a/thing/else/nothing/hello/user/world')
).to.deep.equal({
a: 'nothing',
b: 'user',
});
});

it('should parse multi segment param with params after', () => {
const pp = new pathPattern.PathPattern(
'something/{path=**}/else/{a}/hello/{b}/world'
);

expect(
pp.extractMatches('something/is/a/thing/else/nothing/hello/user/world')
).to.deep.equal({
path: 'is/a/thing',
a: 'nothing',
b: 'user',
});
});

it('should parse multi segment with params before', () => {
const pp = new pathPattern.PathPattern('{a}/something/{b}/**/end');

expect(
pp.extractMatches(
'match_a/something/match_b/thing/else/nothing/hello/user/end'
)
).to.deep.equal({
a: 'match_a',
b: 'match_b',
});
});

it('should parse multi segment param with params before', () => {
const pp = new pathPattern.PathPattern('{a}/something/{b}/{path=**}/end');

expect(
pp.extractMatches(
'match_a/something/match_b/thing/else/nothing/hello/user/end'
)
).to.deep.equal({
a: 'match_a',
b: 'match_b',
path: 'thing/else/nothing/hello/user',
});
});

it('should parse multi segment with params before and after', () => {
const pp = new pathPattern.PathPattern('{a}/something/**/{b}/end');

expect(
pp.extractMatches(
'match_a/something/thing/else/nothing/hello/user/match_b/end'
)
).to.deep.equal({
a: 'match_a',
b: 'match_b',
});
});

it('should parse multi segment param with params before', () => {
const pp = new pathPattern.PathPattern('{a}/something/{path=**}/{b}/end');

expect(
pp.extractMatches(
'match_a/something/thing/else/nothing/hello/user/match_b/end'
)
).to.deep.equal({
a: 'match_a',
b: 'match_b',
path: 'thing/else/nothing/hello/user',
});
});

// handle an instance param
it('should parse an instance', () => {
const pp = new pathPattern.PathPattern('{a}-something-{b}-else-{c}');

expect(
pp.extractMatches('match_a-something-match_b-else-match_c')
).to.deep.equal({});

const anotherPP = new pathPattern.PathPattern('{a}');

expect(anotherPP.extractMatches('match_a')).to.deep.equal({
a: 'match_a',
});
});
});
});
143 changes: 134 additions & 9 deletions spec/v1/providers/database.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -639,17 +639,52 @@ describe('Database Functions', () => {
expect(subject.val()).to.equal(0);
populate({ myKey: 0 });
expect(subject.val()).to.deep.equal({ myKey: 0 });

// Null values are still reported as null.
populate({ myKey: null });
expect(subject.val()).to.deep.equal({ myKey: null });
});

// Regression test: .val() was returning array of nulls when there's a property called length (BUG#37683995)
it('should return correct values when data has "length" property', () => {
populate({ length: 3, foo: 'bar' });
expect(subject.val()).to.deep.equal({ length: 3, foo: 'bar' });
});

it('should deal with null-values appropriately', () => {
populate(null);
expect(subject.val()).to.be.null;

populate({ myKey: null });
expect(subject.val()).to.be.null;
});

it('should deal with empty object values appropriately', () => {
populate({});
expect(subject.val()).to.be.null;

populate({ myKey: {} });
expect(subject.val()).to.be.null;

populate({ myKey: { child: null } });
expect(subject.val()).to.be.null;
});

it('should deal with empty array values appropriately', () => {
populate([]);
expect(subject.val()).to.be.null;

populate({ myKey: [] });
expect(subject.val()).to.be.null;

populate({ myKey: [null] });
expect(subject.val()).to.be.null;

populate({ myKey: [{}] });
expect(subject.val()).to.be.null;

populate({ myKey: [{ myKey: null }] });
expect(subject.val()).to.be.null;

populate({ myKey: [{ myKey: {} }] });
expect(subject.val()).to.be.null;
});
});

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

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

it('should be false for a value pathed beyond a leaf', () => {
populate({ a: { b: 'c' } });
expect(subject.child('a/b/c').exists()).to.be.false;
});

it('should be false for an empty object value', () => {
populate({ a: {} });
expect(subject.child('a').exists()).to.be.false;

populate({ a: { child: null } });
expect(subject.child('a').exists()).to.be.false;

populate({ a: { child: {} } });
expect(subject.child('a').exists()).to.be.false;
});

it('should be false for an empty array value', () => {
populate({ a: [] });
expect(subject.child('a').exists()).to.be.false;

populate({ a: [null] });
expect(subject.child('a').exists()).to.be.false;

populate({ a: [{}] });
expect(subject.child('a').exists()).to.be.false;
});
});

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

expect(subject.forEach(counter)).to.equal(false);
expect(count).to.eq(0);

populate({
a: 'foo',
nullChild: null,
emptyObjectChild: {},
emptyArrayChild: [],
});
count = 0;

expect(subject.forEach(counter)).to.equal(false);
expect(count).to.eq(1);
});

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

describe('#numChildren()', () => {
it('should be key count for objects', () => {
populate({ a: 'b', c: 'd' });
populate({
a: 'b',
c: 'd',
nullChild: null,
emptyObjectChild: {},
emptyArrayChild: [],
});
expect(subject.numChildren()).to.eq(2);
});

it('should be 0 for non-objects', () => {
populate(23);
expect(subject.numChildren()).to.eq(0);

populate({
nullChild: null,
emptyObjectChild: {},
emptyArrayChild: [],
});
expect(subject.numChildren()).to.eq(0);
});
});

describe('#hasChildren()', () => {
it('should true for objects', () => {
populate({
a: 'b',
c: 'd',
nullChild: null,
emptyObjectChild: {},
emptyArrayChild: [],
});
expect(subject.hasChildren()).to.be.true;
});

it('should be false for non-objects', () => {
populate(23);
expect(subject.hasChildren()).to.be.false;

populate({
nullChild: null,
emptyObjectChild: {},
emptyArrayChild: [],
});
expect(subject.hasChildren()).to.be.false;
});
});

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

it('should return false if a child is missing', () => {
populate({ a: 'b' });
populate({
a: 'b',
nullChild: null,
emptyObjectChild: {},
emptyArrayChild: [],
});
expect(subject.hasChild('c')).to.be.false;
expect(subject.hasChild('a/b')).to.be.false;
expect(subject.hasChild('nullChild')).to.be.false;
expect(subject.hasChild('emptyObjectChild')).to.be.false;
expect(subject.hasChild('emptyArrayChild')).to.be.false;
});
});

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

describe('#toJSON(): Object', () => {
it('should return the current value', () => {
populate({ a: 'b' });
populate({
a: 'b',
nullChild: null,
emptyObjectChild: {},
emptyArrayChild: [],
});
expect(subject.toJSON()).to.deep.equal(subject.val());
});
it('should be stringifyable', () => {
populate({ a: 'b' });
populate({
a: 'b',
nullChild: null,
emptyObjectChild: {},
emptyArrayChild: [],
});
expect(JSON.stringify(subject)).to.deep.equal('{"a":"b"}');
});
});
Expand Down

0 comments on commit e42fe3a

Please sign in to comment.