Skip to content

Commit 914843b

Browse files
authored
Add changes for v1.0.0 (#192)
1 parent 8f2d0ab commit 914843b

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+2295
-1624
lines changed

.npmignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ coverage
55
tsconfig.*
66
tslint.*
77
.travis.yml
8+
.github
89

910
# Don't include the raw typescript
1011
src

.travis.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
language: node_js
22
node_js:
3-
- '6.11.1'
3+
- '6.11.5'
44
- stable
55
sudo: false

README.md

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,27 +4,26 @@ The `firebase-functions` package provides an SDK for defining Cloud Functions fo
44

55
Cloud Functions is a hosted, private, and scalable Node.js environment where you can run JavaScript code. The Firebase SDK for Cloud Functions integrates the Firebase platform by letting you write code that responds to events and invokes functionality exposed by other Firebase features.
66

7-
_This is a Beta release of Google Cloud Functions. This API might be changed in backward-incompatible ways and is not subject to any SLA or deprecation policy._
8-
9-
107
## Learn more
118

129
Learn more about the Firebase SDK for Cloud Functions in the [Firebase documentation](https://firebase.google.com/docs/functions/) or [check out our samples](https://github.com/firebase/functions-samples).
1310

11+
## Migrating to v1
12+
13+
To migrate from a beta version of firebase-functions to v1, please refer to the [migration guide](https://firebase.google.com/docs/functions/beta-v1-diff).
14+
1415
## Usage
1516

1617
```js
1718
// functions/index.js
18-
var functions = require('firebase-functions');
19-
var notifyUsers = require('./notify-users');
19+
const functions = require('firebase-functions');
20+
const notifyUsers = require('./notify-users');
2021

2122
exports.newPost = functions.database
2223
.ref('/posts/{postId}')
23-
.onWrite(function(event) {
24-
// only execute function on creation
25-
if (!event.data.previous.exists()) {
26-
notifyUsers(event.data.val());
27-
}
24+
.onCreate((snapshot, context) => {
25+
console.log('Received new post with ID:', context.params.postId);
26+
return notifyUsers(snapshot.val());
2827
});
2928
```
3029

integration_test/functions/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@
77
"dependencies": {
88
"@google-cloud/pubsub": "^0.6.0",
99
"@types/lodash": "^4.14.41",
10-
"firebase": ">3.6.9",
11-
"firebase-admin": ">=5.4.2",
10+
"firebase": "^4.9.1",
11+
"firebase-admin": "~5.10.0",
1212
"firebase-functions": "./firebase-functions.tgz",
1313
"lodash": "^4.17.2"
1414
},
Lines changed: 28 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,52 +1,52 @@
11
import * as functions from 'firebase-functions';
22
import { TestSuite, expectEq } from './testing';
3+
import * as admin from 'firebase-admin';
4+
import UserMetadata = admin.auth.UserRecord;
35

4-
export const createUserTests: any = functions.auth.user().onCreate(receivedEvent => {
5-
let user = receivedEvent.data;
6-
let testId: string = user.displayName;
6+
export const createUserTests: any = functions.auth.user().onCreate((u, c) => {
7+
let testId: string = u.displayName;
78
console.log(`testId is ${testId}`);
89

9-
return new TestSuite('auth user onCreate')
10-
.it('should have a project as resource', event => expectEq(
11-
event.resource, `projects/${process.env.GCLOUD_PROJECT}`))
10+
return new TestSuite<UserMetadata>('auth user onCreate')
11+
.it('should have a project as resource', (user, context) => expectEq(
12+
context.resource, `projects/${process.env.GCLOUD_PROJECT}`))
1213

13-
.it('should not have a path', event => expectEq(event.path, undefined))
14+
.it('should not have a path', (user, context) => expectEq((context as any).path, undefined))
1415

15-
.it('should have the correct eventType', event => expectEq(
16-
event.eventType, 'providers/firebase.auth/eventTypes/user.create'))
16+
.it('should have the correct eventType', (user, context) => expectEq(
17+
context.eventType, 'providers/firebase.auth/eventTypes/user.create'))
1718

18-
.it('should have an eventId', event => event.eventId)
19+
.it('should have an eventId', (user, context)=> context.eventId)
1920

20-
.it('should have a timestamp', event => event.timestamp)
21+
.it('should have a timestamp', (user, context) => context.timestamp)
2122

22-
.it('should not have auth', event => expectEq(event.auth, undefined))
23+
.it('should not have auth', (user, context) => expectEq((context as any).auth, undefined))
2324

24-
.it('should not have action', event => expectEq(event.action, undefined))
25+
.it('should not have action', (user, context) => expectEq((context as any).action, undefined))
2526

26-
.run(testId, receivedEvent);
27+
.run(testId, u, c);
2728
});
2829

29-
export const deleteUserTests: any = functions.auth.user().onDelete(receivedEvent => {
30-
let user = receivedEvent.data;
31-
let testId: string = user.displayName;
30+
export const deleteUserTests: any = functions.auth.user().onDelete((u, c) => {
31+
let testId: string = u.displayName;
3232
console.log(`testId is ${testId}`);
3333

34-
return new TestSuite('auth user onDelete')
35-
.it('should have a project as resource', event => expectEq(
36-
event.resource, `projects/${process.env.GCLOUD_PROJECT}`))
34+
return new TestSuite<UserMetadata>('auth user onDelete')
35+
.it('should have a project as resource', (user, context) => expectEq(
36+
context.resource, `projects/${process.env.GCLOUD_PROJECT}`))
3737

38-
.it('should not have a path', event => expectEq(event.path, undefined))
38+
.it('should not have a path', (user, context) => expectEq((context as any).path, undefined))
3939

40-
.it('should have the correct eventType', event => expectEq(
41-
event.eventType, 'providers/firebase.auth/eventTypes/user.delete'))
40+
.it('should have the correct eventType', (user, context) => expectEq(
41+
context.eventType, 'providers/firebase.auth/eventTypes/user.delete'))
4242

43-
.it('should have an eventId', event => event.eventId)
43+
.it('should have an eventId', (user, context) => context.eventId)
4444

45-
.it('should have a timestamp', event => event.timestamp)
45+
.it('should have a timestamp', (user, context) => context.timestamp)
4646

47-
.it('should not have auth', event => expectEq(event.auth, undefined))
47+
.it('should not have auth', (user, context) => expectEq((context as any).auth, undefined))
4848

49-
.it('should not have action', event => expectEq(event.action, undefined))
49+
.it('should not have action', (user, context) => expectEq((context as any).action, undefined))
5050

51-
.run(testId, receivedEvent);
51+
.run(testId, u, c);
5252
});
Lines changed: 23 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,51 +1,49 @@
11
import * as functions from 'firebase-functions';
2-
import { TestSuite, expectReject, expectEq, expectMatches } from './testing';
2+
import { TestSuite, expectEq, expectMatches } from './testing';
3+
import * as admin from 'firebase-admin';
4+
import DataSnapshot = admin.database.DataSnapshot;
5+
import { Change } from '../../../src/cloud-functions';
36

47
const testIdFieldName = 'testId';
58

6-
export const databaseTests: any = functions.database.ref('dbTests/{testId}/start').onWrite(receivedEvent => {
7-
if (receivedEvent.data.val() === null) {
9+
export const databaseTests: any = functions.database.ref('dbTests/{testId}/start').onWrite((ch, ctx) => {
10+
if (ch.after.val() === null) {
811
console.log(
9-
'Event for ' + receivedEvent.params[testIdFieldName]
12+
'Event for ' + ctx.params[testIdFieldName]
1013
+ ' is null; presuming data cleanup, so skipping.');
1114
return;
1215
}
1316

14-
return new TestSuite('database ref onWrite')
17+
return new TestSuite<Change<DataSnapshot>>('database ref onWrite')
1518

16-
.it('should not have event.app', event => !event.app)
19+
.it('should not have event.app', (change, context) => !(context as any).app)
1720

18-
.it('should not give user refs access to admin data', expectReject(event =>
19-
event.data.ref.parent.child('adminOnly').update({ disallowed: 0 })))
21+
.it('should give refs access to admin data', (change) =>
22+
change.after.ref.parent.child('adminOnly').update({ allowed: 1 }).then(() => true))
2023

21-
.it('should give admin refs access to admin data', event =>
22-
event.data.adminRef.parent.child('adminOnly').update({ allowed: 1 }).then(() => true))
23-
24-
.it('should have a correct ref url', event => {
25-
const url = event.data.ref.toString();
24+
.it('should have a correct ref url', (change) => {
25+
const url = change.after.ref.toString();
2626
return Promise.resolve().then(() => {
2727
return expectMatches(url, new RegExp(`^https://${process.env.GCLOUD_PROJECT}.firebaseio.com/dbTests`));
2828
}).then(() => {
2929
return expectMatches(url, /\/start$/);
3030
});
3131
})
3232

33-
.it('should have refs resources', event => expectEq(
34-
event.resource,
35-
`projects/_/instances/${process.env.GCLOUD_PROJECT}/refs/dbTests/${event.params.testId}/start`))
36-
37-
.it('should not include path', event => expectEq(event.path, undefined))
33+
.it('should have refs resources', (change, context) => expectEq(
34+
context.resource.name,
35+
`projects/_/instances/${process.env.GCLOUD_PROJECT}/refs/dbTests/${context.params.testId}/start`))
3836

39-
.it('should have the right eventType', event => expectEq(
40-
event.eventType, 'providers/google.firebase.database/eventTypes/ref.write'))
37+
.it('should not include path', (change, context) => expectEq((context as any).path, undefined))
4138

42-
.it('should have eventId', event => event.eventId)
39+
.it('should have the right eventType', (change, context) => expectEq(
40+
context.eventType, 'google.firebase.database.ref.write'))
4341

44-
.it('should have timestamp', event => event.timestamp)
42+
.it('should have eventId', (change, context) => context.eventId)
4543

46-
.it('should not be admin-authenticated', event => expectEq(event.auth.admin, false))
44+
.it('should have timestamp', (change, context) => context.timestamp)
4745

48-
.it('should not have action', event => expectEq(event.action, undefined))
46+
.it('should not have action', (change, context) => expectEq((context as any).action, undefined))
4947

50-
.run(receivedEvent.params[testIdFieldName], receivedEvent);
48+
.run(ctx.params[testIdFieldName], ch, ctx);
5149
});
Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,31 @@
11
import * as functions from 'firebase-functions';
22
import { TestSuite, expectEq, expectDeepEq } from './testing';
3+
import * as admin from 'firebase-admin';
4+
import DocumentSnapshot = admin.firestore.DocumentSnapshot;
35

46
const testIdFieldName = 'documentId';
57

6-
export const firestoreTests: any = functions.firestore.document('tests/{documentId}').onCreate(receivedEvent => {
7-
return new TestSuite('firestore document onWrite')
8+
export const firestoreTests: any = functions.firestore.document('tests/{documentId}').onCreate((s, c) => {
9+
return new TestSuite<DocumentSnapshot>('firestore document onWrite')
810

9-
.it('should not have event.app', event => !event.app)
11+
.it('should not have event.app', (snap, context) => !(context as any).app)
1012

11-
.it('should give refs write access', event =>
12-
event.data.ref.set({ allowed: 1 }, {merge: true}).then(() => true))
13+
.it('should give refs write access', (snap) =>
14+
snap.ref.set({ allowed: 1 }, {merge: true}).then(() => true))
1315

14-
.it('should have well-formatted resource', event => expectEq(
15-
event.resource,
16-
`projects/${process.env.GCLOUD_PROJECT}/databases/(default)/documents/tests/${event.params.documentId}`)
16+
.it('should have well-formatted resource', (snap, context) => expectEq(
17+
context.resource.name,
18+
`projects/${process.env.GCLOUD_PROJECT}/databases/(default)/documents/tests/${context.params.documentId}`)
1719
)
1820

19-
.it('should have the right eventType', event => expectEq(
20-
event.eventType, 'providers/cloud.firestore/eventTypes/document.create'))
21+
.it('should have the right eventType', (snap, context) => expectEq(
22+
context.eventType, 'providers/cloud.firestore/eventTypes/document.create'))
2123

22-
.it('should have eventId', event => event.eventId)
24+
.it('should have eventId', (snap, context) => context.eventId)
2325

24-
.it('should have timestamp', event => event.timestamp)
26+
.it('should have timestamp', (snap, context) => context.timestamp)
2527

26-
.it('should have the correct data', event => expectDeepEq(event.data.data(), {test: event.params.documentId}))
28+
.it('should have the correct data', (snap, context) => expectDeepEq(snap.data(), {test: context.params.documentId}))
2729

28-
.it('previous.exists should be false', event => expectEq(event.data.previous.exists, false))
29-
30-
.run(receivedEvent.params[testIdFieldName], receivedEvent);
30+
.run(c.params[testIdFieldName], s, c);
3131
});
Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
import * as functions from 'firebase-functions';
2+
import * as _ from 'lodash';
23
import { TestSuite, expectEq } from './testing';
34

4-
export const callableTests: any = functions.https.onCall((data, context) => {
5+
export const callableTests: any = functions.https.onCall(d => {
56
return new TestSuite('https onCall')
6-
.it('should have the correct data', event => expectEq(event.data.foo, 'bar'))
7-
.run(data.testId, { data: data, context: context });
7+
.it('should have the correct data', data => expectEq(_.get(data, 'foo'), 'bar'))
8+
.run(d.testId, d);
89
});

integration_test/functions/src/index.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ import * as functions from 'firebase-functions';
22
import * as firebase from 'firebase';
33
import * as https from 'https';
44
import * as admin from 'firebase-admin';
5-
import * as _ from 'lodash';
65
import { Request, Response } from 'express';
76

87
export * from './pubsub-tests';
@@ -12,8 +11,10 @@ export * from './firestore-tests';
1211
export * from './https-tests';
1312
const numTests = Object.keys(exports).length; // Assumption: every exported function is its own test.
1413

15-
firebase.initializeApp(_.omit(functions.config().firebase, 'credential')); // Explicitly decline admin privileges.
16-
admin.initializeApp(functions.config().firebase);
14+
// Client SDK doesn't support auto initialization:
15+
firebase.initializeApp(JSON.parse(process.env.FIREBASE_CONFIG));
16+
console.log('initializing admin');
17+
admin.initializeApp();
1718

1819
// TODO(klimt): Get rid of this once the JS client SDK supports callable triggers.
1920
function callHttpsTrigger(name: string, data: any) {
Lines changed: 19 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,42 @@
11
import * as functions from 'firebase-functions';
22
import { TestSuite, expectEq, evaluate } from './testing';
3+
import PubsubMessage = functions.pubsub.Message;
34

45
// TODO(inlined) use multiple queues to run inline.
56
// Expected message data: {"hello": "world"}
6-
export const pubsubTests: any = functions.pubsub.topic('pubsubTests').onPublish(receivedEvent => {
7+
export const pubsubTests: any = functions.pubsub.topic('pubsubTests').onPublish((m, c) => {
78
let testId: string;
89
try {
9-
testId = receivedEvent.data.json.testId;
10+
testId = m.json.testId;
1011
} catch (e) {
1112
/* Ignored. Covered in another test case that `event.data.json` works. */
1213
}
1314

14-
return new TestSuite('pubsub onPublish')
15-
.it('should have a topic as resource', event => expectEq(
16-
event.resource, `projects/${process.env.GCLOUD_PROJECT}/topics/pubsubTests`))
15+
return new TestSuite<PubsubMessage>('pubsub onPublish')
16+
.it('should have a topic as resource', (message, context) => expectEq(
17+
context.resource.name, `projects/${process.env.GCLOUD_PROJECT}/topics/pubsubTests`))
1718

18-
.it('should not have a path', event => expectEq(event.path, undefined))
19+
.it('should not have a path', (message, context) => expectEq((context as any).path, undefined))
1920

20-
.it('should have the correct eventType', event => expectEq(
21-
event.eventType, 'providers/cloud.pubsub/eventTypes/topic.publish'))
21+
.it('should have the correct eventType', (message, context) => expectEq(
22+
context.eventType, 'providers/cloud.pubsub/eventTypes/topic.publish'))
2223

23-
.it('should have an eventId', event => event.eventId)
24+
.it('should have an eventId', (message, context) => context.eventId)
2425

25-
.it('should have a timestamp', event => event.timestamp)
26+
.it('should have a timestamp', (message, context) => context.timestamp)
2627

27-
.it('should not have auth', event => expectEq(event.auth, undefined))
28+
.it('should not have auth', (message, context) => expectEq((context as any).auth, undefined))
2829

29-
.it('should not have action', event => expectEq(event.action, undefined))
30+
.it('should not have action', (message, context) => expectEq((context as any).action, undefined))
3031

31-
.it('should have pubsub data', event => {
32-
const decoded = (new Buffer(event.data.data, 'base64')).toString();
32+
.it('should have pubsub data', (message) => {
33+
const decoded = (new Buffer(message.data, 'base64')).toString();
3334
const parsed = JSON.parse(decoded);
34-
return evaluate(parsed.hasOwnProperty('testId'), 'Raw data was: ' + event.data.data);
35+
return evaluate(parsed.hasOwnProperty('testId'), 'Raw data was: ' + message.data);
3536
})
3637

37-
.it('should decode JSON payloads with the json helper', event =>
38-
evaluate(event.data.json.hasOwnProperty('testId'), event.data.json))
38+
.it('should decode JSON payloads with the json helper', (message) =>
39+
evaluate(message.json.hasOwnProperty('testId'), message.json))
3940

40-
.run(testId, receivedEvent);
41+
.run(testId, m, c);
4142
});

0 commit comments

Comments
 (0)