Skip to content

Commit f5a0696

Browse files
committed
Add conditional delays to auth-next
1 parent b792887 commit f5a0696

File tree

5 files changed

+169
-3
lines changed

5 files changed

+169
-3
lines changed

packages-exp/auth-exp/src/api/index.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,7 @@ describe('performApiRequest', () => {
175175
Endpoint.SIGN_UP,
176176
request
177177
);
178-
clock.tick(DEFAULT_API_TIMEOUT_MS + 1);
178+
clock.tick(DEFAULT_API_TIMEOUT_MS.get() + 1);
179179
await expect(promise).to.be.rejectedWith(
180180
FirebaseError,
181181
'Firebase: The operation has timed out. (auth/timeout).'

packages-exp/auth-exp/src/api/index.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import {
2525
ServerErrorMap,
2626
SERVER_ERROR_MAP
2727
} from './errors';
28+
import { Delay } from '../core/util/delay';
2829

2930
export enum HttpMethod {
3031
POST = 'POST',
@@ -53,7 +54,7 @@ export enum Endpoint {
5354
WITHDRAW_MFA = '/v2/accounts/mfaEnrollment:withdraw'
5455
}
5556

56-
export const DEFAULT_API_TIMEOUT_MS = 30_000;
57+
export const DEFAULT_API_TIMEOUT_MS = new Delay(30_000, 60_000);
5758

5859
export async function performApiRequest<T, V>(
5960
auth: Auth,
@@ -101,7 +102,7 @@ export async function performApiRequest<T, V>(
101102
appName: auth.name
102103
})
103104
);
104-
}, DEFAULT_API_TIMEOUT_MS)
105+
}, DEFAULT_API_TIMEOUT_MS.get())
105106
)
106107
]);
107108
if (response.ok) {
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
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 * as util from '@firebase/util';
19+
import { expect } from 'chai';
20+
import { restore, stub } from 'sinon';
21+
import { Delay, _OFFLINE_DELAY_MS } from './delay';
22+
import * as navigator from './navigator';
23+
24+
describe('Delay.get()', () => {
25+
const SHORT_DELAY = 30_000;
26+
const LONG_DELAY = 60_000;
27+
28+
afterEach(restore);
29+
30+
it('should return the short delay in browser environments', () => {
31+
const delay = new Delay(SHORT_DELAY, LONG_DELAY);
32+
expect(delay.get()).to.eq(SHORT_DELAY);
33+
});
34+
35+
it('should return the long delay in Cordova environments', () => {
36+
const mock = stub(util, 'isMobileCordova');
37+
mock.callsFake(() => true);
38+
const delay = new Delay(SHORT_DELAY, LONG_DELAY);
39+
expect(delay.get()).to.eq(LONG_DELAY);
40+
});
41+
42+
it('should return the long delay in React Native environments', () => {
43+
const mock = stub(util, 'isReactNative');
44+
mock.callsFake(() => true);
45+
const delay = new Delay(SHORT_DELAY, LONG_DELAY);
46+
expect(delay.get()).to.eq(LONG_DELAY);
47+
});
48+
49+
it('should return quicker when offline', () => {
50+
const mock = stub(navigator, 'isOnline');
51+
mock.callsFake(() => false);
52+
const delay = new Delay(SHORT_DELAY, LONG_DELAY);
53+
expect(delay.get()).to.eq(_OFFLINE_DELAY_MS);
54+
});
55+
});
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
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 { isMobileCordova, isReactNative } from '@firebase/util';
19+
import { isOnline } from './navigator';
20+
21+
export const _OFFLINE_DELAY_MS = 5000;
22+
23+
/**
24+
* A structure to help pick between a range of long and short delay durations
25+
* depending on the current environment. In general, the long delay is used for
26+
* mobile environments whereas short delays are used for desktop environments.
27+
*/
28+
export class Delay {
29+
// The default value for the offline delay timeout in ms.
30+
31+
private readonly isMobile: boolean;
32+
constructor(
33+
private readonly shortDelay: number,
34+
private readonly longDelay: number
35+
) {
36+
// Internal error when improperly initialized.
37+
if (shortDelay > longDelay) {
38+
throw new Error('Short delay should be less than long delay!');
39+
}
40+
this.isMobile = isMobileCordova() || isReactNative();
41+
}
42+
43+
get(): number {
44+
if (!isOnline()) {
45+
// Pick the shorter timeout.
46+
return Math.min(_OFFLINE_DELAY_MS, this.shortDelay);
47+
}
48+
// If running in a mobile environment, return the long delay, otherwise
49+
// return the short delay.
50+
// This could be improved in the future to dynamically change based on other
51+
// variables instead of just reading the current environment.
52+
return this.isMobile ? this.longDelay : this.shortDelay;
53+
}
54+
}
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
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 { isBrowserExtension } from '@firebase/util';
19+
import { isHttpOrHttps } from './location';
20+
21+
/**
22+
* For some reason the TS library doesn't know about NetworkInformation
23+
*/
24+
interface NetworkInformation {
25+
downlink?: number;
26+
downlinkMax?: number;
27+
effectiveType?: string;
28+
rtt?: number;
29+
saveData?: boolean;
30+
type?: string;
31+
}
32+
interface StandardNavigator extends Navigator {
33+
connection: NetworkInformation;
34+
}
35+
36+
/**
37+
* Determine whether the browser is working online
38+
*/
39+
export function isOnline(): boolean {
40+
if (
41+
navigator &&
42+
typeof navigator.onLine === 'boolean' &&
43+
// Apply only for traditional web apps and Chrome extensions.
44+
// This is especially true for Cordova apps which have unreliable
45+
// navigator.onLine behavior unless cordova-plugin-network-information is
46+
// installed which overwrites the native navigator.onLine value and
47+
// defines navigator.connection.
48+
(isHttpOrHttps() ||
49+
isBrowserExtension() ||
50+
typeof (navigator as StandardNavigator).connection !== 'undefined')
51+
) {
52+
return navigator.onLine;
53+
}
54+
// If we can't determine the state, assume it is online.
55+
return true;
56+
}

0 commit comments

Comments
 (0)