Skip to content

Commit 27c6c13

Browse files
tinaliangjoehan
authored andcommitted
firebase-functions/handler SDK (#219)
* crashlytics * remove unnecessary import * throwing error if context.params accessed * reworded and removed unnecessary tests * initial commit * firestore unit tests * remove unnecessary imports * reworded tests * https and database providers for firebase-functions/handler sdk (#222) https and database providers and tests for firebase-functions/handler sdk * auth, pubsub, storage, and analytics providers for firebase-functions/handler sdk (#224) auth, pubsub, storage, and analytics providers * remote config provider for firebase-functions/handler SDK (#223) * remote config provider and unit tests
1 parent 1ad8b38 commit 27c6c13

14 files changed

+929
-53
lines changed

spec/cloud-functions.spec.ts

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,39 @@ describe('makeCloudFunction', () => {
158158
},
159159
});
160160
});
161+
162+
it('should throw error when context.params accessed in handler environment', () => {
163+
let args: any = _.assign({}, cloudFunctionArgs, {
164+
handler: (data: any, context: EventContext) => context,
165+
triggerResource: () => null,
166+
});
167+
let cf = makeCloudFunction(args);
168+
let test: Event = {
169+
context: {
170+
eventId: '00000',
171+
timestamp: '2016-11-04T21:29:03.496Z',
172+
eventType: 'provider.event',
173+
resource: {
174+
service: 'provider',
175+
name: 'resource',
176+
},
177+
},
178+
data: 'test data',
179+
};
180+
181+
return cf(test).then(result => {
182+
expect(result).to.deep.equal({
183+
eventId: '00000',
184+
timestamp: '2016-11-04T21:29:03.496Z',
185+
eventType: 'provider.event',
186+
resource: {
187+
service: 'provider',
188+
name: 'resource',
189+
},
190+
});
191+
expect(() => result.params).to.throw(Error);
192+
});
193+
});
161194
});
162195

163196
describe('makeParams', () => {

spec/providers/analytics.spec.ts

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -249,6 +249,44 @@ describe('Analytics Functions', () => {
249249
});
250250
});
251251

252+
describe('handler namespace', () => {
253+
describe('#onLog', () => {
254+
it('should return an empty trigger', () => {
255+
const cloudFunction = functions.handler.analytics.event.onLog(
256+
() => null
257+
);
258+
expect(cloudFunction.__trigger).to.deep.equal({});
259+
});
260+
261+
it('should handle an event with the appropriate fields', () => {
262+
const cloudFunction = functions.handler.analytics.event.onLog(
263+
(data: analytics.AnalyticsEvent) => data
264+
);
265+
266+
// The event data delivered over the wire will be the JSON for an AnalyticsEvent:
267+
// https://firebase.google.com/docs/auth/admin/manage-users#retrieve_user_data
268+
let event: LegacyEvent = {
269+
eventId: 'f2e2f0bf-2e47-4d92-b009-e7a375ecbd3e',
270+
eventType: 'providers/google.firebase.analytics/eventTypes/event.log',
271+
resource: 'projects/myUnitTestProject/events/first_open',
272+
data: {
273+
userDim: {
274+
userId: 'hi!',
275+
},
276+
},
277+
};
278+
279+
return expect(cloudFunction(event)).to.eventually.deep.equal({
280+
params: {},
281+
user: {
282+
userId: 'hi!',
283+
userProperties: {},
284+
},
285+
});
286+
});
287+
});
288+
});
289+
252290
describe('process.env.GCLOUD_PROJECT not set', () => {
253291
it('should not throw if __trigger is not accessed', () => {
254292
expect(() => analytics.event('event').onLog(() => null)).to.not.throw(

spec/providers/auth.spec.ts

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,115 @@ describe('Auth Functions', () => {
201201
});
202202
});
203203

204+
describe('handler namespace', () => {
205+
describe('#onCreate', () => {
206+
let cloudFunctionCreate: CloudFunction<
207+
firebase.auth.UserRecord
208+
> = functions.handler.auth.user.onCreate(
209+
(data: firebase.auth.UserRecord) => data
210+
);
211+
212+
it('should return an empty trigger', () => {
213+
const cloudFunction = functions.handler.auth.user.onCreate(() => null);
214+
expect(cloudFunction.__trigger).to.deep.equal({});
215+
});
216+
217+
it('should transform wire format for UserRecord into v5.0.0 format', () => {
218+
const event = {
219+
data: {
220+
metadata: {
221+
createdAt: '2016-12-15T19:37:37.059Z',
222+
lastSignedInAt: '2017-01-01T00:00:00.000Z',
223+
},
224+
},
225+
};
226+
227+
return cloudFunctionCreate(event).then((data: any) => {
228+
expect(data.metadata.creationTime).to.equal(
229+
'2016-12-15T19:37:37.059Z'
230+
);
231+
expect(data.metadata.lastSignInTime).to.equal(
232+
'2017-01-01T00:00:00.000Z'
233+
);
234+
});
235+
});
236+
237+
it('should handle new wire format if/when there is a change', () => {
238+
const newEvent = {
239+
data: {
240+
metadata: {
241+
creationTime: '2016-12-15T19:37:37.059Z',
242+
lastSignInTime: '2017-01-01T00:00:00.000Z',
243+
},
244+
},
245+
};
246+
247+
return cloudFunctionCreate(newEvent).then((data: any) => {
248+
expect(data.metadata.creationTime).to.equal(
249+
'2016-12-15T19:37:37.059Z'
250+
);
251+
expect(data.metadata.lastSignInTime).to.equal(
252+
'2017-01-01T00:00:00.000Z'
253+
);
254+
});
255+
});
256+
});
257+
258+
describe('#onDelete', () => {
259+
let cloudFunctionDelete: CloudFunction<
260+
firebase.auth.UserRecord
261+
> = functions.handler.auth.user.onDelete(
262+
(data: firebase.auth.UserRecord) => data
263+
);
264+
265+
it('should return an empty trigger', () => {
266+
let handler: (user: firebase.auth.UserRecord) => PromiseLike<any> | any;
267+
const cloudFunction = functions.handler.auth.user.onDelete(handler);
268+
expect(cloudFunction.__trigger).to.deep.equal({});
269+
});
270+
271+
it('should transform wire format for UserRecord into v5.0.0 format', () => {
272+
const event = {
273+
data: {
274+
metadata: {
275+
createdAt: '2016-12-15T19:37:37.059Z',
276+
lastSignedInAt: '2017-01-01T00:00:00.000Z',
277+
},
278+
},
279+
};
280+
281+
return cloudFunctionDelete(event).then((data: any) => {
282+
expect(data.metadata.creationTime).to.equal(
283+
'2016-12-15T19:37:37.059Z'
284+
);
285+
expect(data.metadata.lastSignInTime).to.equal(
286+
'2017-01-01T00:00:00.000Z'
287+
);
288+
});
289+
});
290+
291+
it('should handle new wire format if/when there is a change', () => {
292+
const newEvent = {
293+
data: {
294+
metadata: {
295+
creationTime: '2016-12-15T19:37:37.059Z',
296+
lastSignInTime: '2017-01-01T00:00:00.000Z',
297+
},
298+
},
299+
};
300+
301+
return cloudFunctionDelete(newEvent).then((data: any) => {
302+
expect(data.metadata.creationTime).to.equal(
303+
'2016-12-15T19:37:37.059Z'
304+
);
305+
expect(data.metadata.lastSignInTime).to.equal(
306+
'2017-01-01T00:00:00.000Z'
307+
);
308+
});
309+
});
310+
});
311+
});
312+
204313
describe('process.env.GCLOUD_PROJECT not set', () => {
205314
it('should not throw if __trigger is not accessed', () => {
206315
expect(() => auth.user().onCreate(() => null)).to.not.throw(Error);

spec/providers/crashlytics.spec.ts

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,59 @@ describe('Crashlytics Functions', () => {
9393
});
9494
});
9595
});
96+
97+
describe('HandlerBuilder', () => {
98+
const testIssue = {
99+
issueId: '1234',
100+
issueTitle: 'testIssue',
101+
appInfo: {
102+
appName: 'My Awesome Test App',
103+
appPlatform: 'ios',
104+
appId: '9876',
105+
latestAppVersion: '1.2.3.4',
106+
},
107+
createTime: '2018-12-18T23:05:55+00:00',
108+
};
109+
110+
describe('#onNew', () => {
111+
it('should return a CloudFunction with appropriate values', () => {
112+
const cloudFunction = functions.handler.crashlytics.issue.onNew(
113+
testIssue => {
114+
return (
115+
testIssue.issueId + testIssue.issueTitle + testIssue.createTime
116+
);
117+
}
118+
);
119+
expect(cloudFunction.__trigger).to.deep.equal({});
120+
});
121+
});
122+
123+
describe('#onRegressed', () => {
124+
it('should return a CloudFunction with appropriate values', () => {
125+
const cloudFunction = functions.handler.crashlytics.issue.onRegressed(
126+
testIssue => {
127+
return (
128+
testIssue.issueId + testIssue.issueTitle + testIssue.createTime
129+
);
130+
}
131+
);
132+
expect(cloudFunction.__trigger).to.deep.equal({});
133+
});
134+
});
135+
136+
describe('#onVelocityAlert', () => {
137+
it('should return a CloudFunction with appropriate values', () => {
138+
const cloudFunction = functions.handler.crashlytics.issue.onVelocityAlert(
139+
testIssue => {
140+
return (
141+
testIssue.issueId + testIssue.issueTitle + testIssue.createTime
142+
);
143+
}
144+
);
145+
expect(cloudFunction.__trigger).to.deep.equal({});
146+
});
147+
});
148+
});
96149
});
97150

98151
describe('process.env.GCLOUD_PROJECT not set', () => {

spec/providers/database.spec.ts

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,116 @@ describe('Database Functions', () => {
218218
});
219219
});
220220

221+
describe('handler namespace', () => {
222+
describe('#onWrite()', () => {
223+
it('correctly sets trigger to {}', () => {
224+
let cf = functions.handler.database.ref.onWrite(() => null);
225+
expect(cf.__trigger).to.deep.equal({});
226+
});
227+
228+
it('should be able to use the instance entry point', () => {
229+
let func = functions.handler.database.instance.ref.onWrite(() => null);
230+
expect(func.__trigger).to.deep.equal({});
231+
});
232+
233+
it('should return a handler that emits events with a proper DataSnapshot', () => {
234+
let handler = functions.handler.database.ref.onWrite(change => {
235+
return expect(change.after.val()).to.deep.equal({ foo: 'bar' });
236+
});
237+
238+
return handler({
239+
data: {
240+
data: null,
241+
delta: { foo: 'bar' },
242+
},
243+
resource: 'projects/_/instances/subdomains/refs/users',
244+
eventType: 'providers/google.firebase.database/eventTypes/ref.write',
245+
});
246+
});
247+
});
248+
249+
describe('#onCreate()', () => {
250+
it('correctly sets trigger to {}', () => {
251+
let cf = functions.handler.database.ref.onCreate(() => null);
252+
return expect(cf.__trigger).to.deep.equal({});
253+
});
254+
255+
it('should be able to use the instance entry point', () => {
256+
let func = functions.handler.database.instance.ref.onCreate(() => null);
257+
expect(func.__trigger).to.deep.equal({});
258+
});
259+
260+
it('should return a handler that emits events with a proper DataSnapshot', () => {
261+
let handler = functions.handler.database.ref.onCreate(data => {
262+
return expect(data.val()).to.deep.equal({ foo: 'bar' });
263+
});
264+
265+
return handler({
266+
data: {
267+
data: null,
268+
delta: { foo: 'bar' },
269+
},
270+
resource: 'projects/_/instances/subdomains/refs/users',
271+
eventType: 'providers/google.firebase.database/eventTypes/ref.create',
272+
});
273+
});
274+
});
275+
276+
describe('#onUpdate()', () => {
277+
it('correctly sets trigger to {}', () => {
278+
let cf = functions.handler.database.ref.onUpdate(() => null);
279+
return expect(cf.__trigger).to.deep.equal({});
280+
});
281+
282+
it('should be able to use the instance entry point', () => {
283+
let func = functions.handler.database.instance.ref.onUpdate(() => null);
284+
expect(func.__trigger).to.deep.equal({});
285+
});
286+
287+
it('should return a handler that emits events with a proper DataSnapshot', () => {
288+
let handler = functions.handler.database.ref.onUpdate(change => {
289+
return expect(change.after.val()).to.deep.equal({ foo: 'bar' });
290+
});
291+
292+
return handler({
293+
data: {
294+
data: null,
295+
delta: { foo: 'bar' },
296+
},
297+
resource: 'projects/_/instances/subdomains/refs/users',
298+
eventType: 'providers/google.firebase.database/eventTypes/ref.update',
299+
});
300+
});
301+
});
302+
303+
describe('#onDelete()', () => {
304+
it('correctly sets trigger to {}', () => {
305+
let cf = functions.handler.database.ref.onDelete(() => null);
306+
return expect(cf.__trigger).to.deep.equal({});
307+
});
308+
309+
it('should be able to use the instance entry point', () => {
310+
let func = functions.handler.database.instance.ref.onDelete(() => null);
311+
expect(func.__trigger).to.deep.equal({});
312+
});
313+
314+
it('should return a handler that emits events with a proper DataSnapshot', () => {
315+
let handler = functions.handler.database.ref.onDelete(data => {
316+
return expect(data.val()).to.deep.equal({ foo: 'bar' });
317+
});
318+
319+
return handler({
320+
data: {
321+
data: { foo: 'bar' },
322+
delta: null,
323+
},
324+
resource: 'projects/_/instances/subdomains/refs/users',
325+
eventType: 'providers/google.firebase.database/eventTypes/ref.delete',
326+
});
327+
});
328+
});
329+
});
330+
221331
describe('process.env.FIREBASE_CONFIG not set', () => {
222332
it('should not throw if __trigger is not accessed', () => {
223333
expect(() => database.ref('/path').onWrite(() => null)).to.not.throw(

0 commit comments

Comments
 (0)