Skip to content

Commit 90a907e

Browse files
committed
Use typescript asserts keyword for typesafe assertions
1 parent 95d8d32 commit 90a907e

File tree

5 files changed

+119
-31
lines changed

5 files changed

+119
-31
lines changed

packages-exp/auth-exp/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
"@firebase/app-types-exp": "0.x"
3030
},
3131
"dependencies": {
32+
"@firebase/logger": "^0.2.2",
3233
"@firebase/util": "^0.2.44",
3334
"tslib": "1.11.1"
3435
},

packages-exp/auth-exp/src/core/user/token_manager.ts

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717

1818
import { IdTokenResponse } from '../../model/id_token';
1919
import { PersistedBlob } from '../persistence';
20-
import { assertType } from '../util/assert';
20+
import { assert } from '../util/assert';
2121

2222
/**
2323
* The number of milliseconds before the official expiration time of a token
@@ -79,13 +79,16 @@ export class StsTokenManager {
7979

8080
const manager = new StsTokenManager();
8181
if (refreshToken) {
82-
manager.refreshToken = assertType(refreshToken, 'string', appName);
82+
assert(typeof refreshToken === 'string', appName);
83+
manager.refreshToken = refreshToken;
8384
}
8485
if (accessToken) {
85-
manager.accessToken = assertType(accessToken, 'string', appName);
86+
assert(typeof accessToken === 'string', appName);
87+
manager.accessToken = accessToken;
8688
}
8789
if (expirationTime) {
88-
manager.expirationTime = assertType(expirationTime, 'number', appName);
90+
assert(typeof expirationTime === 'number', appName);
91+
manager.expirationTime = expirationTime;
8992
}
9093
return manager;
9194
}

packages-exp/auth-exp/src/core/user/user_impl.ts

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ import { IdTokenResult } from '../../model/id_token';
2020
import { User } from '../../model/user';
2121
import { PersistedBlob } from '../persistence';
2222
import { ProviderId } from '../providers';
23-
import { assert, assertType } from '../util/assert';
23+
import { assert, assertStringOrUndefined} from '../util/assert';
2424
import { StsTokenManager } from './token_manager';
2525

2626
export interface UserParameters {
@@ -104,23 +104,26 @@ export class UserImpl implements User {
104104
photoURL
105105
} = object;
106106

107-
assert(uid && plainObjectTokenManager, auth.name);
107+
assert(uid && !!plainObjectTokenManager, auth.name);
108108

109109
const stsTokenManager = StsTokenManager.fromPlainObject(
110110
auth.name,
111111
plainObjectTokenManager as PersistedBlob
112112
);
113113

114-
const stringOrUndef = ['string', 'undefined'];
115-
114+
assert(typeof uid === 'string', auth.name);
115+
assertStringOrUndefined(displayName, auth.name);
116+
assertStringOrUndefined(email, auth.name);
117+
assertStringOrUndefined(phoneNumber, auth.name);
118+
assertStringOrUndefined(photoURL, auth.name);
116119
return new UserImpl({
117-
uid: assertType(uid, 'string', auth.name),
120+
uid,
118121
auth,
119122
stsTokenManager,
120-
displayName: assertType(displayName, stringOrUndef, auth.name),
121-
email: assertType(email, stringOrUndef, auth.name),
122-
phoneNumber: assertType(phoneNumber, stringOrUndef, auth.name),
123-
photoURL: assertType(photoURL, stringOrUndef, auth.name)
123+
displayName,
124+
email,
125+
phoneNumber,
126+
photoURL
124127
});
125128
}
126129
}

packages-exp/auth-exp/src/core/util/assert.ts

Lines changed: 55 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -15,29 +15,66 @@
1515
* limitations under the License.
1616
*/
1717

18+
import { SDK_VERSION } from '@firebase/app-exp';
1819
import { AUTH_ERROR_FACTORY, AuthErrorCode } from '../errors';
20+
import { logError } from './log';
1921

20-
export function assert<T>(
21-
expression: T | null | undefined,
22-
appName: string,
23-
code = AuthErrorCode.INTERNAL_ERROR
24-
): T {
25-
if (!expression) {
26-
throw AUTH_ERROR_FACTORY.create(code, { appName });
22+
/**
23+
* Unconditionally fails, throwing a developer facing INTERNAL_ERROR
24+
*
25+
* @param appName App name for tagging the error
26+
* @throws FirebaseError
27+
*/
28+
export function fail(
29+
appName: string
30+
): never {
31+
throw AUTH_ERROR_FACTORY.create(AuthErrorCode.INTERNAL_ERROR, { appName });
32+
}
33+
34+
/**
35+
* Verifies the given condition and fails if false, throwing a developer facing error
36+
*
37+
* @param assertion
38+
* @param appName
39+
*/
40+
export function assert(assertion: boolean, appName: string): asserts assertion {
41+
if (!assertion) {
42+
fail(appName);
2743
}
44+
}
2845

29-
return expression;
46+
export function assertStringOrUndefined(assertion: unknown, appName: string): asserts assertion is string | undefined {
47+
assert(typeof assertion === 'string' || typeof assertion === 'undefined', appName);
3048
}
3149

32-
export function assertType<T>(
33-
expression: unknown,
34-
expected: string | string[],
35-
appName: string
36-
): T {
37-
if (typeof expected === 'string') {
38-
expected = [expected];
39-
}
50+
/**
51+
* Unconditionally fails, throwing an internal error with the given message.
52+
*
53+
* @param failure type of failure encountered
54+
* @throws Error
55+
*/
56+
export function debugFail(failure: string): never {
57+
// Log the failure in addition to throw an exception, just in case the
58+
// exception is swallowed.
59+
const message =
60+
`AUTH (${SDK_VERSION}) INTERNAL ASSERTION FAILED: ` + failure;
61+
logError(message);
4062

41-
assert(expected.includes(typeof expression), appName);
42-
return expression as T;
63+
// NOTE: We don't use FirestoreError here because these are internal failures
64+
// that cannot be handled by the user. (Also it would create a circular
65+
// dependency between the error and assert modules which doesn't work.)
66+
throw new Error(message);
4367
}
68+
69+
/**
70+
* Fails if the given assertion condition is false, throwing an Error with the
71+
* given message if it did.
72+
*
73+
* @param assertion
74+
* @param message
75+
*/
76+
export function debugAssert(assertion: boolean, message: string): asserts assertion {
77+
if (!assertion) {
78+
debugFail(message);
79+
}
80+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
/**
2+
* @license
3+
* Copyright 2020 Google LLC
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
import { Logger, LogLevel } from '@firebase/logger';
19+
import { SDK_VERSION } from '@firebase/app-exp';
20+
21+
export { LogLevel };
22+
23+
const logClient = new Logger('@firebase/auth-exp');
24+
25+
// Helper methods are needed because variables can't be exported as read/write
26+
export function getLogLevel(): LogLevel {
27+
return logClient.logLevel;
28+
}
29+
30+
export function setLogLevel(newLevel: LogLevel): void {
31+
logClient.logLevel = newLevel;
32+
}
33+
34+
export function logDebug(msg: string, ...args: string[]): void {
35+
if (logClient.logLevel <= LogLevel.DEBUG) {
36+
logClient.debug(`Auth (${SDK_VERSION}): ${msg}`, ...args);
37+
}
38+
}
39+
40+
export function logError(msg: string, ...args: string[]): void {
41+
if (logClient.logLevel <= LogLevel.ERROR) {
42+
logClient.error(`Auth (${SDK_VERSION}): ${msg}`, ...args);
43+
}
44+
}

0 commit comments

Comments
 (0)