Skip to content

Commit

Permalink
squashed commits from aws-amplify#12907
Browse files Browse the repository at this point in the history
  • Loading branch information
Samaritan1011001 authored and Ashwin Kumar committed Feb 8, 2024
1 parent 4840d0a commit 9c32e1b
Show file tree
Hide file tree
Showing 28 changed files with 837 additions and 21 deletions.
18 changes: 9 additions & 9 deletions packages/aws-amplify/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -317,13 +317,13 @@
"name": "[Analytics] record (Kinesis)",
"path": "./dist/esm/analytics/kinesis/index.mjs",
"import": "{ record }",
"limit": "44.50 kB"
"limit": "44.503 kB"
},
{
"name": "[Analytics] record (Kinesis Firehose)",
"path": "./dist/esm/analytics/kinesis-firehose/index.mjs",
"import": "{ record }",
"limit": "41.55 kB"
"limit": "41.555 kB"
},
{
"name": "[Analytics] record (Personalize)",
Expand Down Expand Up @@ -401,7 +401,7 @@
"name": "[Auth] confirmSignIn (Cognito)",
"path": "./dist/esm/auth/index.mjs",
"import": "{ confirmSignIn }",
"limit": "25.95 kB"
"limit": "26.005 kB"
},
{
"name": "[Auth] updateMFAPreference (Cognito)",
Expand All @@ -419,13 +419,13 @@
"name": "[Auth] verifyTOTPSetup (Cognito)",
"path": "./dist/esm/auth/index.mjs",
"import": "{ verifyTOTPSetup }",
"limit": "9.19 kB"
"limit": "9.192 kB"
},
{
"name": "[Auth] updatePassword (Cognito)",
"path": "./dist/esm/auth/index.mjs",
"import": "{ updatePassword }",
"limit": "9.20 kB"
"limit": "9.21 kB"
},
{
"name": "[Auth] setUpTOTP (Cognito)",
Expand All @@ -449,7 +449,7 @@
"name": "[Auth] confirmUserAttribute (Cognito)",
"path": "./dist/esm/auth/index.mjs",
"import": "{ confirmUserAttribute }",
"limit": "9.20 kB"
"limit": "9.204 kB"
},
{
"name": "[Auth] signInWithRedirect (Cognito)",
Expand All @@ -461,13 +461,13 @@
"name": "[Auth] fetchUserAttributes (Cognito)",
"path": "./dist/esm/auth/index.mjs",
"import": "{ fetchUserAttributes }",
"limit": "8.29 kB"
"limit": "8.295 kB"
},
{
"name": "[Auth] Basic Auth Flow (Cognito)",
"path": "./dist/esm/auth/index.mjs",
"import": "{ signIn, signOut, fetchAuthSession, confirmSignIn }",
"limit": "28.05 kB"
"limit": "28.08 kB"
},
{
"name": "[Auth] OAuth Auth Flow (Cognito)",
Expand All @@ -485,7 +485,7 @@
"name": "[Storage] downloadData (S3)",
"path": "./dist/esm/storage/index.mjs",
"import": "{ downloadData }",
"limit": "14.05 kB"
"limit": "14.06 kB"
},
{
"name": "[Storage] getProperties (S3)",
Expand Down
74 changes: 74 additions & 0 deletions packages/core/__tests__/NetworkConnectionMonitor.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

import { Observable, Observer } from 'rxjs';
import { NetworkConnectionMonitor, Reachability } from '../src/Reachability';

describe('NetworkConnectionMonitor', () => {
let reachabilityObserver: Observer<{ online: boolean }>;
const eventHandler = jest.fn();

beforeEach(() => {
jest
.spyOn(Reachability.prototype, 'networkMonitor')
.mockImplementationOnce(() => {
return new Observable(observer => {
reachabilityObserver = observer;
});
})
// Twice because we subscribe to get the initial state then again to monitor reachability
.mockImplementationOnce(() => {
return new Observable(observer => {
reachabilityObserver = observer;
});
});
});

afterEach(() => {
eventHandler.mockReset();
});

it('should execute event handler exactly once if device is already online', () => {
const netMon = new NetworkConnectionMonitor();
netMon.enableNetworkMonitoringFor(eventHandler);
expect(eventHandler).toHaveBeenCalledTimes(1);
});

it('should not execute event handler if device is already offline', () => {
jest
.spyOn(Reachability.prototype, 'isOnline')
.mockImplementationOnce(() => {
return false;
});
const netMon = new NetworkConnectionMonitor();
netMon.enableNetworkMonitoringFor(eventHandler);
expect(eventHandler).toHaveBeenCalledTimes(0);
});

it('should subscribe to network event when device is offline, execute the event handler when device comes online then unsubscribe', () => {
jest
.spyOn(Reachability.prototype, 'isOnline')
.mockImplementationOnce(() => {
return false;
});

const netMon = new NetworkConnectionMonitor();
netMon.enableNetworkMonitoringFor(eventHandler);

// Should have been called 0 times at this stage since device is offline
expect(eventHandler).toHaveBeenCalledTimes(0);

// Replicating device coming online the first time
reachabilityObserver?.next?.({ online: true });

// Should be called exactly one time when the device comes online
expect(eventHandler).toHaveBeenCalledTimes(1);
eventHandler.mockReset();

// Replicating device coming online the second time
reachabilityObserver?.next?.({ online: true });

// Should not be called since it should have unsubscribed after executing the event handler once
expect(eventHandler).toHaveBeenCalledTimes(0);
});
});
8 changes: 8 additions & 0 deletions packages/core/__tests__/utils.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -114,4 +114,12 @@ describe('Util', () => {

expect(subscribe).not.toThrow();
});
test('Web Reachability isOnline method should return true when device is online', () => {
jest.spyOn(window.navigator, 'onLine', 'get').mockReturnValue(true);
expect(new Reachability().isOnline()).toBe(true);
});
test('Web Reachability isOnline method should return false when device is offline', () => {
jest.spyOn(window.navigator, 'onLine', 'get').mockReturnValue(false);
expect(new Reachability().isOnline()).toBe(false);
});
});
22 changes: 22 additions & 0 deletions packages/core/__tests__/utils/deviceId/getDeviceId.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { Cache } from '../../../src';
import { getDeviceId } from '../../../src/utils/deviceId/getDeviceId';

describe('getDeviceId: ', () => {
const testDeviceId = 'test-device-id';
it('should return the device id if already present in Cache', async () => {
jest.spyOn(Cache, 'getItem').mockImplementationOnce(key => {
return Promise.resolve(testDeviceId);
});
const setItemSpy = jest.spyOn(Cache, 'setItem');
expect(await getDeviceId()).toBe(testDeviceId);
expect(setItemSpy).toHaveBeenCalledTimes(0);
});
it('should create a new device id if not available and store it in Cache', async () => {
jest.spyOn(Cache, 'getItem').mockImplementationOnce(key => {
return Promise.resolve(undefined);
});
const setItemSpy = jest.spyOn(Cache, 'setItem');
await getDeviceId();
expect(setItemSpy).toHaveBeenCalledTimes(1);
});
});
21 changes: 16 additions & 5 deletions packages/core/src/Reachability/Reachability.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,19 @@ import { isWebWorker } from '../utils';
export class Reachability {
private static _observers: Array<CompletionObserver<NetworkStatus>> = [];

networkMonitor(_?: unknown): Observable<NetworkStatus> {
const globalObj = isWebWorker()
? self
: typeof window !== 'undefined' && window;
isOnline(): boolean {
const globalObj = this._retreiveGlobalObject();
if (!globalObj) {
return false;
}
return globalObj.navigator.onLine;
}

networkMonitor(_?: unknown): Observable<NetworkStatus> {
const globalObj = this._retreiveGlobalObject();
if (!globalObj) {
return from([{ online: true }]);
}

return new Observable(observer => {
observer.next({ online: globalObj.navigator.onLine });

Expand Down Expand Up @@ -52,4 +56,11 @@ export class Reachability {
observer?.next && observer.next(status);
}
}

private _retreiveGlobalObject() {
const globalObj = isWebWorker()
? self
: typeof window !== 'undefined' && window;
return globalObj;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

import { Observable, Subscription } from 'rxjs';
import { Reachability } from '..';
import { AsyncReturnType, NetworkStatus } from '../types';

export class NetworkConnectionMonitor {
/**
* @private
*/
private _networkMonitoringSubscriptions?: Subscription;
private _networkMonitor: Observable<NetworkStatus>;
private _reachability: Reachability;

constructor() {
this._reachability = new Reachability();
this._networkMonitor = this._reachability.networkMonitor();
}

/**
* Turn network state monitoring on if it isn't on already
*/
public enableNetworkMonitoringFor(
eventHandler: (...args: any) => Promise<any>
): AsyncReturnType<typeof eventHandler> {
if (this._reachability.isOnline()) {
return eventHandler();
} else {
return new Promise((resolve, _) => {
const subscription = this._networkMonitor.subscribe(({ online }) => {
if (online) {
const eventHandlerResult = eventHandler();
subscription.unsubscribe();
resolve(eventHandlerResult);
}
});
if (!this._networkMonitoringSubscriptions) {
this._networkMonitoringSubscriptions = subscription;
return;
}
this._networkMonitoringSubscriptions.add(subscription);
});
}
}

/**
* Turn network state monitoring off if it isn't off already
*/
public disableNetworkMonitoring() {
this._networkMonitoringSubscriptions?.unsubscribe();
this._networkMonitoringSubscriptions = undefined;
}
}
1 change: 1 addition & 0 deletions packages/core/src/Reachability/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@
// SPDX-License-Identifier: Apache-2.0

export { Reachability } from './Reachability';
export { NetworkConnectionMonitor } from '../Reachability/ReachabilityMonitor/NetworkConnectionMonitor';
4 changes: 4 additions & 0 deletions packages/core/src/Reachability/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,7 @@
export type NetworkStatus = {
online: boolean;
};

// TODO: can reuse the type defined at packages/core/src/utils/deDupeAsyncFunction.ts#L5
export type AsyncReturnType<T extends (...args: any) => Promise<any>> =
T extends (...args: any) => Promise<infer R> ? R : any;
18 changes: 17 additions & 1 deletion packages/core/src/libraryUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ export { LegacyConfig } from './singleton/types';
export { ADD_OAUTH_LISTENER } from './singleton/constants';
export { amplifyUuid } from './utils/amplifyUuid';
export { AmplifyUrl, AmplifyUrlSearchParams } from './utils/amplifyUrl';
export { getDeviceId } from './utils/deviceId';

// Auth utilities
export {
Expand Down Expand Up @@ -105,7 +106,7 @@ export {
// Other utilities & constants
export { BackgroundProcessManager } from './BackgroundProcessManager';
export { Mutex } from './Mutex';
export { Reachability } from './Reachability';
export { Reachability, NetworkConnectionMonitor } from './Reachability';
export { USER_AGENT_HEADER } from './constants';
export { fetchAuthSession } from './singleton/apis/internal/fetchAuthSession';
export { AMPLIFY_SYMBOL } from './Hub';
Expand All @@ -124,3 +125,18 @@ export {
SESSION_START_EVENT,
SESSION_STOP_EVENT,
} from './utils/sessionListener';

// Queued storage utilities
export {
createQueuedStorage,
QueuedStorage,
QueuedItem,
} from './utils/queuedStorage';

// Logging utilities
export {
LogLevel,
LogParams,
LoggingProvider,
LoggingCategory,
} from './logging';
2 changes: 1 addition & 1 deletion packages/core/src/logging/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@
// SPDX-License-Identifier: Apache-2.0

export { ConsoleLogger } from './legacy/ConsoleLogger';
export { LoggingProvider } from './types';
export { LoggingProvider, LogLevel, LogParams, LoggingCategory } from './types';
export { createLogger } from './createLogger';
24 changes: 24 additions & 0 deletions packages/core/src/utils/deviceId/getDeviceId.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

// TODO: add native impl of this
import { Cache } from '../../Cache';
import { amplifyUuid } from '../amplifyUuid';

/**
* Local storage key to store the device id
*/
const _localStorageKey = 'amplify-device-id';

/**
* Utility to generate or return cached deviceId
*/
export async function getDeviceId(): Promise<string | undefined> {
let deviceId = (await Cache.getItem(_localStorageKey)) as string | undefined;
if (!!deviceId) {
return deviceId;
}
deviceId = amplifyUuid();
await Cache.setItem(_localStorageKey, deviceId);
return deviceId;
}
4 changes: 4 additions & 0 deletions packages/core/src/utils/deviceId/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

export { getDeviceId } from './getDeviceId';
1 change: 1 addition & 0 deletions packages/core/src/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,4 @@ export { urlSafeDecode } from './urlSafeDecode';
export { urlSafeEncode } from './urlSafeEncode';
export { deepFreeze } from './deepFreeze';
export { deDupeAsyncFunction } from './deDupeAsyncFunction';
export { createQueuedStorage, QueuedStorage } from './queuedStorage';
1 change: 1 addition & 0 deletions packages/core/src/utils/queuedStorage/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@
// SPDX-License-Identifier: Apache-2.0

export { createQueuedStorage } from './createQueuedStorage';
export { QueuedStorage, QueuedItem } from './types';
Loading

0 comments on commit 9c32e1b

Please sign in to comment.