Skip to content

Commit a66acec

Browse files
authored
Update peer dependency to firebase-admin v5.0.0 (#66)
1 parent b2ec1b3 commit a66acec

File tree

4 files changed

+94
-83
lines changed

4 files changed

+94
-83
lines changed

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@
3838
"@types/sinon": "^1.16.29",
3939
"chai": "^3.5.0",
4040
"chai-as-promised": "^5.2.0",
41-
"firebase-admin": "^4.2.1",
41+
"firebase-admin": "^5.0.0",
4242
"istanbul": "^0.4.2",
4343
"mocha": "^2.4.5",
4444
"mock-require": "^2.0.1",
@@ -48,7 +48,7 @@
4848
"typescript": "^2.0.3"
4949
},
5050
"peerDependencies": {
51-
"firebase-admin": "~4.2.1"
51+
"firebase-admin": "~5.0.0"
5252
},
5353
"dependencies": {
5454
"@types/express": "^4.0.33",

spec/providers/auth.spec.ts

Lines changed: 51 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -61,90 +61,76 @@ describe('AuthBuilder', () => {
6161
});
6262

6363
describe('#_dataConstructor', () => {
64-
it('should handle an event with the appropriate fields', () => {
65-
const cloudFunctionCreate = auth.user().onCreate((ev: Event<firebase.auth.UserRecord>) => ev.data);
66-
const cloudFunctionDelete = auth.user().onDelete((ev: Event<firebase.auth.UserRecord>) => ev.data);
64+
let cloudFunctionCreate;
65+
let cloudFunctionDelete;
66+
let event;
6767

68-
// The event data delivered over the wire will be the JSON for a UserRecord:
69-
// https://firebase.google.com/docs/auth/admin/manage-users#retrieve_user_data
70-
let event = {
71-
eventId: 'f2e2f0bf-2e47-4d92-b009-e7a375ecbd3e',
72-
eventType: 'providers/firebase.auth/eventTypes/user.create',
73-
resource: 'projects/myUnitTestProject',
74-
notSupported: {
75-
},
68+
before(() => {
69+
cloudFunctionCreate = auth.user().onCreate((ev: Event<firebase.auth.UserRecord>) => ev.data);
70+
cloudFunctionDelete = auth.user().onDelete((ev: Event<firebase.auth.UserRecord>) => ev.data);
71+
event = {
7672
data: {
77-
uid: 'abcde12345',
78-
email: 'foo@bar.baz',
79-
emailVerified: false,
80-
displayName: 'My Display Name',
81-
photoURL: 'bar.baz/foo.jpg',
82-
disabled: false,
8373
metadata: {
8474
createdAt: '2016-12-15T19:37:37.059Z',
8575
lastSignedInAt: '2017-01-01T00:00:00.000Z',
8676
},
87-
providerData: [{
88-
uid: 'g-abcde12345',
89-
email: 'foo@gmail.com',
90-
displayName: 'My Google Provider Display Name',
91-
photoURL: 'googleusercontent.com/foo.jpg',
92-
providerId: 'google.com',
93-
}],
94-
},
95-
};
96-
97-
const expectedData = {
98-
uid: 'abcde12345',
99-
email: 'foo@bar.baz',
100-
emailVerified: false,
101-
displayName: 'My Display Name',
102-
photoURL: 'bar.baz/foo.jpg',
103-
disabled: false,
104-
metadata: {
105-
// Gotcha's:
106-
// - JS Date is, by default, local-time based, not UTC-based.
107-
// - JS Date's month is zero-based.
108-
createdAt: new Date(Date.UTC(2016, 11, 15, 19, 37, 37, 59)),
109-
lastSignedInAt: new Date(Date.UTC(2017, 0, 1)),
11077
},
111-
providerData: [{
112-
uid: 'g-abcde12345',
113-
email: 'foo@gmail.com',
114-
displayName: 'My Google Provider Display Name',
115-
photoURL: 'googleusercontent.com/foo.jpg',
116-
providerId: 'google.com',
117-
}],
11878
};
79+
});
11980

81+
it('should transform old wire format for UserRecord into v5.0.0 format', () => {
12082
return Promise.all([
121-
expect(cloudFunctionCreate(event)).to.eventually.deep.equal(expectedData),
122-
expect(cloudFunctionDelete(event)).to.eventually.deep.equal(expectedData),
83+
cloudFunctionCreate(event).then(data => {
84+
expect(data.metadata.creationTime).to.equal('2016-12-15T19:37:37.059Z');
85+
expect(data.metadata.lastSignInTime).to.equal('2017-01-01T00:00:00.000Z');
86+
}),
87+
cloudFunctionDelete(event).then(data => {
88+
expect(data.metadata.creationTime).to.equal('2016-12-15T19:37:37.059Z');
89+
expect(data.metadata.lastSignInTime).to.equal('2017-01-01T00:00:00.000Z');
90+
}),
12391
]);
12492
});
12593

126-
// This isn't expected to happen in production, but if it does we should
127-
// handle it gracefully.
128-
it('should tolerate missing fields in the payload', () => {
129-
const cloudFunction = auth.user().onCreate((ev: Event<firebase.auth.UserRecord>) => ev.data);
94+
// createdAt and lastSignedIn are fields of admin.auth.UserMetadata below v5.0.0
95+
// We want to add shims to still expose these fields so that user's code do not break
96+
// The shim and this test should be removed in v1.0.0 of firebase-functions
97+
it('should still retain createdAt and lastSignedIn', () => {
98+
return Promise.all([
99+
cloudFunctionCreate(event).then(data => {
100+
expect(data.metadata.createdAt).to.deep.equal(new Date('2016-12-15T19:37:37.059Z'));
101+
expect(data.metadata.lastSignedInAt).to.deep.equal(new Date('2017-01-01T00:00:00.000Z'));
102+
}),
103+
cloudFunctionDelete(event).then(data => {
104+
expect(data.metadata.createdAt).to.deep.equal(new Date('2016-12-15T19:37:37.059Z'));
105+
expect(data.metadata.lastSignedInAt).to.deep.equal(new Date('2017-01-01T00:00:00.000Z'));
106+
}),
107+
]);
108+
});
130109

131-
let event: Event<firebase.auth.UserRecord> = {
110+
it('should handle new wire format if/when there is a change', () => {
111+
const newEvent = {
132112
data: {
133-
uid: 'abcde12345',
134113
metadata: {
135-
// TODO(inlined) We'll need to manually parse these!
136-
createdAt: new Date(),
137-
lastSignedInAt: new Date(),
114+
creationTime: '2016-12-15T19:37:37.059Z',
115+
lastSignInTime: '2017-01-01T00:00:00.000Z',
138116
},
139-
email: 'nobody@google.com',
140-
emailVerified: false,
141-
displayName: 'sample user',
142-
photoURL: '',
143-
disabled: false,
144117
},
145-
} as any;
118+
};
146119

147-
return expect(cloudFunction(event)).to.eventually.deep.equal(event.data);
120+
return Promise.all([
121+
cloudFunctionCreate(newEvent).then(data => {
122+
expect(data.metadata.creationTime).to.equal('2016-12-15T19:37:37.059Z');
123+
expect(data.metadata.lastSignInTime).to.equal('2017-01-01T00:00:00.000Z');
124+
expect(data.metadata.createdAt).to.deep.equal(new Date('2016-12-15T19:37:37.059Z'));
125+
expect(data.metadata.lastSignedInAt).to.deep.equal(new Date('2017-01-01T00:00:00.000Z'));
126+
}),
127+
cloudFunctionDelete(newEvent).then(data => {
128+
expect(data.metadata.creationTime).to.equal('2016-12-15T19:37:37.059Z');
129+
expect(data.metadata.lastSignInTime).to.equal('2017-01-01T00:00:00.000Z');
130+
expect(data.metadata.createdAt).to.deep.equal(new Date('2016-12-15T19:37:37.059Z'));
131+
expect(data.metadata.lastSignedInAt).to.deep.equal(new Date('2017-01-01T00:00:00.000Z'));
132+
}),
133+
]);
148134
});
149135
});
150136
});

src/providers/auth.ts

Lines changed: 40 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222

2323
import { makeCloudFunction, CloudFunction, Event } from '../cloud-functions';
2424
import * as firebase from 'firebase-admin';
25+
import * as _ from 'lodash';
2526

2627
/** @internal */
2728
export const provider = 'firebase.auth';
@@ -31,27 +32,51 @@ export function user() {
3132
return new UserBuilder('projects/' + process.env.GCLOUD_PROJECT);
3233
}
3334

35+
export class UserRecordMetadata implements firebase.auth.UserMetadata {
36+
37+
constructor(public creationTime: string, public lastSignInTime: string) { };
38+
39+
// Remove in v1.0.0
40+
/** @internal */
41+
get lastSignedInAt() {
42+
console.warn('WARNING: "lastSignedInAt" will be removed in firebase-functions v1.0.0. ' +
43+
'Please start using "lastSignInTime", which is an ISO string.');
44+
return new Date(this.lastSignInTime);
45+
}
46+
47+
// Remove in v1.0.0
48+
/** @internal */
49+
get createdAt() {
50+
console.warn('WARNING: "createdAt" will be removed in firebase-functions v1.0.0. ' +
51+
'Please start using "creationTime", which is an ISO string.');
52+
return new Date(this.creationTime);
53+
}
54+
55+
toJSON() {
56+
return {
57+
creationTime: this.creationTime,
58+
lastSignInTime: this.lastSignInTime,
59+
};
60+
}
61+
}
62+
3463
/** Builder used to create Cloud Functions for Firebase Auth user lifecycle events. */
3564
export class UserBuilder {
3665
private static dataConstructor(raw: any): firebase.auth.UserRecord {
3766
// The UserRecord returned here is an interface. The firebase-admin/auth/user-record module
38-
// also has a class of the same name, which is one implementation of the interface. Here,
39-
// because our wire format already almost matches the UserRecord interface, we only use the
40-
// interface, no need to use the class.
41-
//
42-
// The one change we need to make to match the interface is to our incoming timestamps. The
43-
// interface requires them to be Date objects, while they raw payload has them as strings.
44-
if (raw.data.metadata) {
45-
let metadata = raw.data.metadata;
46-
if (metadata.lastSignedInAt && typeof metadata.lastSignedInAt === 'string') {
47-
metadata.lastSignedInAt = new Date(metadata.lastSignedInAt);
48-
}
49-
if (metadata.createdAt && typeof metadata.createdAt === 'string') {
50-
metadata.createdAt = new Date(metadata.createdAt);
51-
}
67+
// also has a class of the same name, which is one implementation of the interface.
68+
69+
// Transform payload to firebase-admin v5.0.0 format
70+
let data = _.clone(raw.data);
71+
if (data.metadata) {
72+
let meta = data.metadata;
73+
data.metadata = new UserRecordMetadata(
74+
meta.createdAt || meta.creationTime,
75+
meta.lastSignedInAt || meta.lastSignInTime,
76+
);
5277
}
5378

54-
return raw.data;
79+
return data;
5580
}
5681

5782
/** @internal */

src/providers/datastore.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ export class DeltaDocumentSnapshot {
141141
result = new Buffer(fieldValue, 'base64');
142142
}
143143
} else if (fieldType === 'referenceValue') {
144-
console.log('WARNING: you have a data field which is a datastore reference. ' +
144+
console.warn('WARNING: you have a data field which is a datastore reference. ' +
145145
'There will be a breaking change later which will change it from a string to a reference object.');
146146
result = fieldValue;
147147
} else {

0 commit comments

Comments
 (0)