Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(core): Check for storage initialization errors #13938

Merged
merged 7 commits into from
Nov 8, 2024
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions packages/aws-amplify/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -293,7 +293,7 @@
"name": "[Analytics] record (Pinpoint)",
"path": "./dist/esm/analytics/index.mjs",
"import": "{ record }",
"limit": "17.41 kB"
"limit": "17.5 kB"
},
{
"name": "[Analytics] record (Kinesis)",
Expand All @@ -317,7 +317,7 @@
"name": "[Analytics] identifyUser (Pinpoint)",
"path": "./dist/esm/analytics/index.mjs",
"import": "{ identifyUser }",
"limit": "15.91 kB"
"limit": "15.95 kB"
},
{
"name": "[Analytics] enable",
Expand Down Expand Up @@ -497,7 +497,7 @@
"name": "[Storage] uploadData (S3)",
"path": "./dist/esm/storage/index.mjs",
"import": "{ uploadData }",
"limit": "20.08 kB"
"limit": "20.15 kB"
}
]
}
22 changes: 22 additions & 0 deletions packages/core/__tests__/storage/DefaultStorage.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { DefaultStorage } from '../../src/storage/DefaultStorage';
import { InMemoryStorage } from '../../src/storage/InMemoryStorage';

const key = 'k';
const value = 'value';
Expand Down Expand Up @@ -35,4 +36,25 @@ describe('DefaultStorage', () => {
await defaultStorage.clear();
expect(defaultStorage.getItem(key)).resolves.toBeNull();
});

it('should fall back to alternative storage when localStorage is not accessible', async () => {
// Mock window.localStorage to throw an error
const originalLocalStorage = window.localStorage;

Object.defineProperty(window, 'localStorage', {
value: undefined,
writable: true,
});

// Create a new DefaultStorage instance to trigger the fallback
const fallbackStorage = new DefaultStorage();

// Verify that the storage still works as expected
expect(fallbackStorage.storage instanceof InMemoryStorage).toEqual(true);

// Restore the original localStorage
Object.defineProperty(window, 'localStorage', {
value: originalLocalStorage,
});
});
});
24 changes: 23 additions & 1 deletion packages/core/__tests__/storage/SessionStorage.test.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { InMemoryStorage } from '../../src/storage/InMemoryStorage';
import { SessionStorage } from '../../src/storage/SessionStorage';

const key = 'k';
const value = 'value';

describe('sessionStorage', () => {
describe('SessionStorage', () => {
let sessionStorage: SessionStorage;

beforeEach(() => {
Expand Down Expand Up @@ -37,4 +38,25 @@ describe('sessionStorage', () => {
await sessionStorage.clear();
expect(await sessionStorage.getItem(key)).toBeNull();
});

it('should fall back to alternative storage when sessionStorage is not accessible', async () => {
// Mock window.sessionStorage to throw an error
const originalSessionStorage = window.sessionStorage;

Object.defineProperty(window, 'sessionStorage', {
value: undefined,
writable: true,
});

// Create a new SessionStorage instance to trigger the fallback
const fallbackStorage = new SessionStorage();

// Verify that the storage still works as expected
expect(fallbackStorage.storage instanceof InMemoryStorage).toEqual(true);

// Restore the original sessionStorage
Object.defineProperty(window, 'sessionStorage', {
value: originalSessionStorage,
});
});
});
2 changes: 1 addition & 1 deletion packages/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@
"name": "Cache (default browser storage)",
"path": "./dist/esm/index.mjs",
"import": "{ Cache }",
"limit": "3.3 kB"
"limit": "3.4 kB"
}
],
"exports": {
Expand Down
45 changes: 37 additions & 8 deletions packages/core/src/storage/utils.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,51 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

import { ConsoleLogger } from '../Logger';

import { InMemoryStorage } from './InMemoryStorage';

/**
* @internal
* @returns Either a reference to window.localStorage or an in-memory storage as fallback
*/
export const getLocalStorageWithFallback = (): Storage =>
typeof window !== 'undefined' && window.localStorage
? window.localStorage
: new InMemoryStorage();

const logger = new ConsoleLogger('CoreStorageUtils');
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I dont' think we have a convention for logger name but this looks good to me!


export const getLocalStorageWithFallback = (): Storage => {
try {
// Attempt to use localStorage directly
if (typeof window !== 'undefined' && window.localStorage) {
return window.localStorage;
cwomack marked this conversation as resolved.
Show resolved Hide resolved
}
} catch (e) {
// Handle any errors related to localStorage access
console.error('LocalStorage access failed:', e);
cwomack marked this conversation as resolved.
Show resolved Hide resolved
}

// Return in-memory storage as a fallback if localStorage is not accessible
return new InMemoryStorage();
};

/**
* @internal
* @returns Either a reference to window.sessionStorage or an in-memory storage as fallback
*/
export const getSessionStorageWithFallback = (): Storage =>
typeof window !== 'undefined' && window.sessionStorage
? window.sessionStorage
: new InMemoryStorage();
export const getSessionStorageWithFallback = (): Storage => {
try {
// Attempt to use sessionStorage directly
if (typeof window !== 'undefined' && window.sessionStorage) {
// Verify we can actually use it by testing access
window.sessionStorage.getItem('test');

return window.sessionStorage;
}

throw new Error('sessionStorage is not defined');
} catch (e) {
// Handle any errors related to sessionStorage access
logger.error('SessionStorage access failed:', e);

return new InMemoryStorage();
}
};
Loading