Skip to content

Commit 0f86418

Browse files
author
Jacob Wenger
authored
Cleaned up auth user types for easier debugging (#1)
1 parent d56ff4f commit 0f86418

File tree

4 files changed

+99
-118
lines changed

4 files changed

+99
-118
lines changed

src/auth/user-record.ts

Lines changed: 45 additions & 116 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import * as utils from '../utils';
12
import {AuthClientErrorCode, FirebaseAuthError} from '../utils/error';
23

34
/**
@@ -28,26 +29,16 @@ function parseDate(time: any): Date {
2829
* @constructor
2930
*/
3031
export class UserMetadata {
31-
private lastSignedInAtInternal: Date;
32-
private createdAtInternal: Date;
32+
public readonly createdAt: Date;
33+
public readonly lastSignedInAt: Date;
3334

3435
constructor(response: any) {
3536
// Creation date should always be available but due to some backend bugs there
3637
// were cases in the past where users did not have creation date properly set.
3738
// This included legacy Firebase migrating project users and some anonymous users.
3839
// These bugs have already been addressed since then.
39-
this.createdAtInternal = parseDate(response.createdAt);
40-
this.lastSignedInAtInternal = parseDate(response.lastLoginAt);
41-
}
42-
43-
/** @return {Date} The user's last sign-in date. */
44-
public get lastSignedInAt(): Date {
45-
return this.lastSignedInAtInternal;
46-
}
47-
48-
/** @return {Date} The user's account creation date. */
49-
public get createdAt(): Date {
50-
return this.createdAtInternal;
40+
utils.addReadonlyGetter(this, 'createdAt', parseDate(response.createdAt));
41+
utils.addReadonlyGetter(this, 'lastSignedInAt', parseDate(response.lastLoginAt));
5142
}
5243

5344
/** @return {Object} The plain object representation of the user's metadata. */
@@ -68,11 +59,11 @@ export class UserMetadata {
6859
* @constructor
6960
*/
7061
export class UserInfo {
71-
private uidInternal: string;
72-
private displayNameInternal: string;
73-
private emailInternal: string;
74-
private photoURLInternal: string;
75-
private providerIdInternal: string;
62+
public readonly uid: string;
63+
public readonly displayName: string;
64+
public readonly email: string;
65+
public readonly photoURL: string;
66+
public readonly providerId: string;
7667

7768
constructor(response: any) {
7869
// Provider user id and provider id are required.
@@ -81,36 +72,12 @@ export class UserInfo {
8172
AuthClientErrorCode.INTERNAL_ERROR,
8273
'INTERNAL ASSERT FAILED: Invalid user info response');
8374
}
84-
this.uidInternal = response.rawId;
85-
this.displayNameInternal = response.displayName;
86-
this.emailInternal = response.email;
87-
this.photoURLInternal = response.photoUrl;
88-
this.providerIdInternal = response.providerId;
89-
}
90-
91-
/** @return {string} The provider user id. */
92-
public get uid(): string {
93-
return this.uidInternal;
94-
}
95-
96-
/** @return {string} The provider display name. */
97-
public get displayName(): string {
98-
return this.displayNameInternal;
99-
}
10075

101-
/** @return {string} The provider email. */
102-
public get email(): string {
103-
return this.emailInternal;
104-
}
105-
106-
/** @return {string} The provider photo URL. */
107-
public get photoURL(): string {
108-
return this.photoURLInternal;
109-
}
110-
111-
/** @return {string} The provider Firebase ID. */
112-
public get providerId(): string {
113-
return this.providerIdInternal;
76+
utils.addReadonlyGetter(this, 'uid', response.rawId);
77+
utils.addReadonlyGetter(this, 'displayName', response.displayName);
78+
utils.addReadonlyGetter(this, 'email', response.email);
79+
utils.addReadonlyGetter(this, 'photoURL', response.photoUrl);
80+
utils.addReadonlyGetter(this, 'providerId', response.providerId);
11481
}
11582

11683
/** @return {Object} The plain object representation of the current provider data. */
@@ -134,14 +101,14 @@ export class UserInfo {
134101
* @constructor
135102
*/
136103
export class UserRecord {
137-
private uidInternal: string;
138-
private emailInternal: string;
139-
private emailVerifiedInternal: boolean;
140-
private displayNameInternal: string;
141-
private photoURLInternal: string;
142-
private disabledInternal: boolean;
143-
private metadataInternal: UserMetadata;
144-
private providerDataInternal: UserInfo[];
104+
public readonly uid: string;
105+
public readonly email: string;
106+
public readonly emailVerified: boolean;
107+
public readonly displayName: string;
108+
public readonly photoURL: string;
109+
public readonly disabled: boolean;
110+
public readonly metadata: UserMetadata;
111+
public readonly providerData: UserInfo[];
145112

146113
constructor(response: any) {
147114
// The Firebase user id is required.
@@ -150,72 +117,34 @@ export class UserRecord {
150117
AuthClientErrorCode.INTERNAL_ERROR,
151118
'INTERNAL ASSERT FAILED: Invalid user response');
152119
}
153-
this.uidInternal = response.localId;
154-
this.emailInternal = response.email;
155-
this.emailVerifiedInternal = !!response.emailVerified;
156-
this.displayNameInternal = response.displayName;
157-
this.photoURLInternal = response.photoUrl;
120+
121+
utils.addReadonlyGetter(this, 'uid', response.localId);
122+
utils.addReadonlyGetter(this, 'email', response.email);
123+
utils.addReadonlyGetter(this, 'emailVerified', !!response.emailVerified);
124+
utils.addReadonlyGetter(this, 'displayName', response.displayName);
125+
utils.addReadonlyGetter(this, 'photoURL', response.photoUrl);
158126
// If disabled is not provided, the account is enabled by default.
159-
this.disabledInternal = response.disabled || false;
160-
this.metadataInternal = new UserMetadata(response);
161-
let providerData: UserInfo[] = response.providerUserInfo || [];
162-
this.providerDataInternal = [];
163-
for (let entry of providerData) {
164-
this.providerData.push(new UserInfo(entry));
127+
utils.addReadonlyGetter(this, 'disabled', response.disabled || false);
128+
utils.addReadonlyGetter(this, 'metadata', new UserMetadata(response));
129+
const providerData: UserInfo[] = [];
130+
for (let entry of (response.providerUserInfo || [])) {
131+
providerData.push(new UserInfo(entry));
165132
}
166-
}
167-
168-
/** @return {string} The Firebase user id corresponding to the current user record. */
169-
public get uid(): string {
170-
return this.uidInternal;
171-
}
172-
173-
/** @return {string} The primary email corresponding to the current user record. */
174-
public get email(): string {
175-
return this.emailInternal;
176-
}
177-
178-
/** @return {boolean} Whether the primary email is verified. */
179-
public get emailVerified(): boolean {
180-
return this.emailVerifiedInternal;
181-
}
182-
183-
/** @return {string} The display name corresponding to the current user record. */
184-
public get displayName(): string {
185-
return this.displayNameInternal;
186-
}
187-
188-
/** @return {string} The photo URL corresponding to the current user record. */
189-
public get photoURL(): string {
190-
return this.photoURLInternal;
191-
}
192-
193-
/** @return {boolean} Whether the current user is disabled or not. */
194-
public get disabled(): boolean {
195-
return this.disabledInternal;
196-
}
197-
198-
/** @return {UserMetadata} The user record's metadata. */
199-
public get metadata(): UserMetadata {
200-
return this.metadataInternal;
201-
}
202-
203-
/** @return {UserInfo[]} The list of providers linked to the current record. */
204-
public get providerData(): UserInfo[] {
205-
return this.providerDataInternal;
133+
utils.addReadonlyGetter(this, 'providerData', providerData);
206134
}
207135

208136
/** @return {Object} The plain object representation of the user record. */
209137
public toJSON(): Object {
210-
let json: any = {};
211-
json.uid = this.uid;
212-
json.email = this.email;
213-
json.emailVerified = this.emailVerified;
214-
json.displayName = this.displayName;
215-
json.photoURL = this.photoURL;
216-
json.disabled = this.disabled;
217-
// Convert metadata to json.
218-
json.metadata = this.metadata.toJSON();
138+
let json: any = {
139+
uid: this.uid,
140+
email: this.email,
141+
emailVerified: this.emailVerified,
142+
displayName: this.displayName,
143+
photoURL: this.photoURL,
144+
disabled: this.disabled,
145+
// Convert metadata to json.
146+
metadata: this.metadata.toJSON(),
147+
};
219148
json.providerData = [];
220149
for (let entry of this.providerData) {
221150
// Convert each provider data to json.

src/utils/index.ts

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@
33
*
44
* For example, this can be used to map underscore_cased properties to camelCase.
55
*
6-
* @param {obj} Object The object whose properties to rename.
7-
* @param {keyMap} Object The mapping from old to new property names.
6+
* @param {Object} obj The object whose properties to rename.
7+
* @param {Object} keyMap The mapping from old to new property names.
88
*/
99
export function renameProperties(obj: Object, keyMap: { [key: string]: string }): void {
1010
Object.keys(keyMap).forEach((oldKey) => {
@@ -16,3 +16,22 @@ export function renameProperties(obj: Object, keyMap: { [key: string]: string })
1616
}
1717
});
1818
}
19+
20+
/**
21+
* Defines a new read-only property directly on an object and returns the object.
22+
*
23+
* @param {Object} obj The object on which to define the property.
24+
* @param {string} prop The name of the property to be defined or modified.
25+
* @param {any} value The value associated with the property.
26+
*
27+
* @return {Object} The object that was passed to the function.
28+
*/
29+
export function addReadonlyGetter(obj: Object, prop: string, value: any): void {
30+
Object.defineProperty(obj, prop, {
31+
value,
32+
// Make this property read-only.
33+
writable: false,
34+
// Include this property during enumeration of obj's properties.
35+
enumerable: true,
36+
});
37+
}

test/unit/index.spec.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import './firebase-app.spec';
44
import './firebase-namespace.spec';
55

66
// Utilities
7+
import './utils/index.spec';
78
import './utils/error.spec';
89
import './utils/validator.spec';
910
import './utils/api-request.spec';

test/unit/utils/index.spec.ts

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import {expect} from 'chai';
2+
3+
import {addReadonlyGetter} from '../../../src/utils/index';
4+
5+
type Obj = {
6+
[key: string]: any;
7+
};
8+
9+
describe('addReadonlyGetter()', () => {
10+
it('should add a new property to the provided object', () => {
11+
const obj: Obj = {};
12+
addReadonlyGetter(obj, 'foo', true);
13+
14+
expect(obj.foo).to.be.true;
15+
});
16+
17+
it('should make the new property read-only', () => {
18+
const obj: Obj = {};
19+
addReadonlyGetter(obj, 'foo', true);
20+
21+
expect(() => {
22+
obj.foo = false;
23+
}).to.throw('Cannot assign to read only property \'foo\' of object \'#<Object>\'');
24+
});
25+
26+
it('should make the new property enumerable', () => {
27+
const obj: Obj = {};
28+
addReadonlyGetter(obj, 'foo', true);
29+
30+
expect(obj).to.have.keys(['foo']);
31+
});
32+
});

0 commit comments

Comments
 (0)