From a40aacb9b202e800d8441b5b8c329691b7914f32 Mon Sep 17 00:00:00 2001 From: Jim Blanchard Date: Thu, 17 Aug 2023 09:58:44 -0500 Subject: [PATCH] feature: Integrate Cache utilities with v6 (#11806) --- .../__tests__/Cache/StorageCache-unit-test.ts | 6 +- packages/core/src/Cache/AsyncStorageCache.ts | 65 +++++----- .../core/src/Cache/BrowserStorageCache.ts | 97 +++++++-------- packages/core/src/Cache/InMemoryCache.ts | 34 +++-- packages/core/src/Cache/StorageCache.ts | 117 +++++++++--------- packages/core/src/Cache/Utils/CacheUtils.ts | 2 + packages/core/src/index.ts | 6 +- packages/core/src/singleton/types.ts | 4 + 8 files changed, 164 insertions(+), 167 deletions(-) diff --git a/packages/core/__tests__/Cache/StorageCache-unit-test.ts b/packages/core/__tests__/Cache/StorageCache-unit-test.ts index 93947b92abf..ec44e407871 100644 --- a/packages/core/__tests__/Cache/StorageCache-unit-test.ts +++ b/packages/core/__tests__/Cache/StorageCache-unit-test.ts @@ -121,7 +121,7 @@ describe('StorageCache', () => { defaultTTL: 3000000, itemMaxSize: 1000, keyPrefix: 'aws-amplify#$#', - storage: undefined, + storage: expect.any(Storage), warningThreshold: 0.8, }); }); @@ -130,9 +130,9 @@ describe('StorageCache', () => { const spyon = jest.spyOn(Logger.prototype, 'warn'); const storage: StorageCache = new StorageCache(config); - const customizedConfig: CacheConfig = { + const customizedConfig = { keyPrefix: 'abcc', - }; + } as Omit; const new_config = storage.configure(customizedConfig); expect(spyon).toBeCalled(); diff --git a/packages/core/src/Cache/AsyncStorageCache.ts b/packages/core/src/Cache/AsyncStorageCache.ts index 15e2255a686..3ac6e5fba0a 100644 --- a/packages/core/src/Cache/AsyncStorageCache.ts +++ b/packages/core/src/Cache/AsyncStorageCache.ts @@ -2,11 +2,11 @@ // SPDX-License-Identifier: Apache-2.0 import AsyncStorage from '@react-native-async-storage/async-storage'; -import { Amplify } from '../Amplify'; import { ConsoleLogger as Logger } from '../Logger'; import { StorageCache } from './StorageCache'; import { defaultConfig, getCurrTime } from './Utils'; -import { ICache } from './types'; +import { CacheConfig, ICache } from './types'; +import { getCurrSizeKey } from './Utils/CacheUtils'; const logger = new Logger('AsyncStorageCache'); @@ -19,14 +19,13 @@ export class AsyncStorageCache extends StorageCache implements ICache { * * @param {Object} config - the configuration of the cache */ - constructor(config?) { - const cache_config = config - ? Object.assign({}, defaultConfig, config) - : defaultConfig; - super(cache_config); + constructor(config?: CacheConfig) { + super(config); + this.getItem = this.getItem.bind(this); this.setItem = this.setItem.bind(this); this.removeItem = this.removeItem.bind(this); + logger.debug('Using AsyncStorageCache'); } @@ -38,7 +37,7 @@ export class AsyncStorageCache extends StorageCache implements ICache { async _decreaseCurSizeInBytes(amount) { const curSize = await this.getCacheCurSize(); await AsyncStorage.setItem( - this.cacheCurSizeKey, + getCurrSizeKey(this.cacheConfig.keyPrefix), (curSize - amount).toString() ); } @@ -51,7 +50,7 @@ export class AsyncStorageCache extends StorageCache implements ICache { async _increaseCurSizeInBytes(amount) { const curSize = await this.getCacheCurSize(); await AsyncStorage.setItem( - this.cacheCurSizeKey, + getCurrSizeKey(this.cacheConfig.keyPrefix), (curSize + amount).toString() ); } @@ -139,9 +138,9 @@ export class AsyncStorageCache extends StorageCache implements ICache { */ async _sizeToPop(itemSize) { const spaceItemNeed = - (await this.getCacheCurSize()) + itemSize - this.config.capacityInBytes; + (await this.getCacheCurSize()) + itemSize - this.cacheConfig.capacityInBytes; const cacheThresholdSpace = - (1 - this.config.warningThreshold) * this.config.capacityInBytes; + (1 - this.cacheConfig.warningThreshold) * this.cacheConfig.capacityInBytes; return spaceItemNeed > cacheThresholdSpace ? spaceItemNeed : cacheThresholdSpace; @@ -156,7 +155,7 @@ export class AsyncStorageCache extends StorageCache implements ICache { */ async _isCacheFull(itemSize) { return ( - itemSize + (await this.getCacheCurSize()) > this.config.capacityInBytes + itemSize + (await this.getCacheCurSize()) > this.cacheConfig.capacityInBytes ); } @@ -173,8 +172,8 @@ export class AsyncStorageCache extends StorageCache implements ICache { for (let i = 0; i < keyInCache.length; i += 1) { const key = keyInCache[i]; if ( - key.indexOf(this.config.keyPrefix) === 0 && - key !== this.cacheCurSizeKey + key.indexOf(this.cacheConfig.keyPrefix) === 0 && + key !== getCurrSizeKey(this.cacheConfig.keyPrefix) ) { if (await this._isExpired(key)) { await this._removeItem(key); @@ -250,11 +249,11 @@ export class AsyncStorageCache extends StorageCache implements ICache { logger.debug( `Set item: key is ${key}, value is ${value} with options: ${options}` ); - const prefixedKey = this.config.keyPrefix + key; + const prefixedKey = this.cacheConfig.keyPrefix + key; // invalid keys if ( - prefixedKey === this.config.keyPrefix || - prefixedKey === this.cacheCurSizeKey + prefixedKey === this.cacheConfig.keyPrefix || + prefixedKey === getCurrSizeKey(this.cacheConfig.keyPrefix) ) { logger.warn(`Invalid key: should not be empty or 'CurSize'`); return; @@ -269,11 +268,11 @@ export class AsyncStorageCache extends StorageCache implements ICache { priority: options && options.priority !== undefined ? options.priority - : this.config.defaultPriority, + : this.cacheConfig.defaultPriority, expires: options && options.expires !== undefined ? options.expires - : this.config.defaultTTL + getCurrTime(), + : this.cacheConfig.defaultTTL + getCurrTime(), }; if (cacheItemOptions.priority < 1 || cacheItemOptions.priority > 5) { @@ -286,7 +285,7 @@ export class AsyncStorageCache extends StorageCache implements ICache { const item = this.fillCacheItem(prefixedKey, value, cacheItemOptions); // check wether this item is too big; - if (item.byteSize > this.config.itemMaxSize) { + if (item.byteSize > this.cacheConfig.itemMaxSize) { logger.warn( `Item with key: ${key} you are trying to put into is too big!` ); @@ -333,11 +332,11 @@ export class AsyncStorageCache extends StorageCache implements ICache { async getItem(key, options) { logger.debug(`Get item: key is ${key} with options ${options}`); let ret = null; - const prefixedKey = this.config.keyPrefix + key; + const prefixedKey = this.cacheConfig.keyPrefix + key; if ( - prefixedKey === this.config.keyPrefix || - prefixedKey === this.cacheCurSizeKey + prefixedKey === this.cacheConfig.keyPrefix || + prefixedKey === getCurrSizeKey(this.cacheConfig.keyPrefix) ) { logger.warn(`Invalid key: should not be empty or 'CurSize'`); return null; @@ -380,11 +379,11 @@ export class AsyncStorageCache extends StorageCache implements ICache { */ async removeItem(key) { logger.debug(`Remove item: key is ${key}`); - const prefixedKey = this.config.keyPrefix + key; + const prefixedKey = this.cacheConfig.keyPrefix + key; if ( - prefixedKey === this.config.keyPrefix || - prefixedKey === this.cacheCurSizeKey + prefixedKey === this.cacheConfig.keyPrefix || + prefixedKey === getCurrSizeKey(this.cacheConfig.keyPrefix) ) { return; } @@ -412,7 +411,7 @@ export class AsyncStorageCache extends StorageCache implements ICache { const keysToRemove = []; for (let i = 0; i < keys.length; i += 1) { - if (keys[i].indexOf(this.config.keyPrefix) === 0) { + if (keys[i].indexOf(this.cacheConfig.keyPrefix) === 0) { keysToRemove.push(keys[i]); } } @@ -431,9 +430,9 @@ export class AsyncStorageCache extends StorageCache implements ICache { * @return {Promise} */ async getCacheCurSize() { - let ret = await AsyncStorage.getItem(this.cacheCurSizeKey); + let ret = await AsyncStorage.getItem(getCurrSizeKey(this.cacheConfig.keyPrefix)); if (!ret) { - await AsyncStorage.setItem(this.cacheCurSizeKey, '0'); + await AsyncStorage.setItem(getCurrSizeKey(this.cacheConfig.keyPrefix), '0'); ret = '0'; } return Number(ret); @@ -451,10 +450,10 @@ export class AsyncStorageCache extends StorageCache implements ICache { const retKeys = []; for (let i = 0; i < keys.length; i += 1) { if ( - keys[i].indexOf(this.config.keyPrefix) === 0 && - keys[i] !== this.cacheCurSizeKey + keys[i].indexOf(this.cacheConfig.keyPrefix) === 0 && + keys[i] !== getCurrSizeKey(this.cacheConfig.keyPrefix) ) { - retKeys.push(keys[i].substring(this.config.keyPrefix.length)); + retKeys.push(keys[i].substring(this.cacheConfig.keyPrefix.length)); } } return retKeys; @@ -480,5 +479,3 @@ export class AsyncStorageCache extends StorageCache implements ICache { const instance: ICache = new AsyncStorageCache(); export { AsyncStorage, instance as Cache }; - -Amplify.register(instance); diff --git a/packages/core/src/Cache/BrowserStorageCache.ts b/packages/core/src/Cache/BrowserStorageCache.ts index 391f6df84cf..20ff8dd4d87 100644 --- a/packages/core/src/Cache/BrowserStorageCache.ts +++ b/packages/core/src/Cache/BrowserStorageCache.ts @@ -1,11 +1,11 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { Amplify } from '../Amplify'; import { ConsoleLogger as Logger } from '../Logger'; import { defaultConfig, getCurrTime } from './Utils'; import { StorageCache } from './StorageCache'; import { ICache, CacheConfig, CacheItem, CacheItemOptions } from './types'; +import { getCurrSizeKey } from './Utils/CacheUtils'; const logger = new Logger('Cache'); @@ -18,11 +18,8 @@ export class BrowserStorageCacheClass extends StorageCache implements ICache { * @param config - the configuration of the cache */ constructor(config?: CacheConfig) { - const cacheConfig = config - ? Object.assign({}, defaultConfig, config) - : defaultConfig; - super(cacheConfig); - this.config.storage = cacheConfig.storage; + super(config); + this.getItem = this.getItem.bind(this); this.setItem = this.setItem.bind(this); this.removeItem = this.removeItem.bind(this); @@ -36,8 +33,8 @@ export class BrowserStorageCacheClass extends StorageCache implements ICache { */ private _decreaseCurSizeInBytes(amount: number): void { const curSize: number = this.getCacheCurSize(); - this.config.storage.setItem( - this.cacheCurSizeKey, + this.cacheConfig.storage.setItem( + getCurrSizeKey(this.cacheConfig.keyPrefix), (curSize - amount).toString() ); } @@ -50,8 +47,8 @@ export class BrowserStorageCacheClass extends StorageCache implements ICache { */ private _increaseCurSizeInBytes(amount: number): void { const curSize: number = this.getCacheCurSize(); - this.config.storage.setItem( - this.cacheCurSizeKey, + this.cacheConfig.storage.setItem( + getCurrSizeKey(this.cacheConfig.keyPrefix), (curSize + amount).toString() ); } @@ -67,7 +64,7 @@ export class BrowserStorageCacheClass extends StorageCache implements ICache { */ private _refreshItem(item: CacheItem, prefixedKey: string): CacheItem { item.visitedTime = getCurrTime(); - this.config.storage.setItem(prefixedKey, JSON.stringify(item)); + this.cacheConfig.storage.setItem(prefixedKey, JSON.stringify(item)); return item; } @@ -80,7 +77,7 @@ export class BrowserStorageCacheClass extends StorageCache implements ICache { * @return true if the item is expired. */ private _isExpired(key: string): boolean { - const text: string | null = this.config.storage.getItem(key); + const text: string | null = this.cacheConfig.storage.getItem(key); const item: CacheItem = JSON.parse(text); if (getCurrTime() >= item.expires) { return true; @@ -98,10 +95,10 @@ export class BrowserStorageCacheClass extends StorageCache implements ICache { private _removeItem(prefixedKey: string, size?: number): void { const itemSize: number = size ? size - : JSON.parse(this.config.storage.getItem(prefixedKey)).byteSize; + : JSON.parse(this.cacheConfig.storage.getItem(prefixedKey)).byteSize; this._decreaseCurSizeInBytes(itemSize); // remove the cache item - this.config.storage.removeItem(prefixedKey); + this.cacheConfig.storage.removeItem(prefixedKey); } /** @@ -117,7 +114,7 @@ export class BrowserStorageCacheClass extends StorageCache implements ICache { this._increaseCurSizeInBytes(item.byteSize); try { - this.config.storage.setItem(prefixedKey, JSON.stringify(item)); + this.cacheConfig.storage.setItem(prefixedKey, JSON.stringify(item)); } catch (setItemErr) { // if failed, we need to rollback the cache size this._decreaseCurSizeInBytes(item.byteSize); @@ -135,9 +132,9 @@ export class BrowserStorageCacheClass extends StorageCache implements ICache { */ private _sizeToPop(itemSize: number): number { const spaceItemNeed = - this.getCacheCurSize() + itemSize - this.config.capacityInBytes; + this.getCacheCurSize() + itemSize - this.cacheConfig.capacityInBytes; const cacheThresholdSpace = - (1 - this.config.warningThreshold) * this.config.capacityInBytes; + (1 - this.cacheConfig.warningThreshold) * this.cacheConfig.capacityInBytes; return spaceItemNeed > cacheThresholdSpace ? spaceItemNeed : cacheThresholdSpace; @@ -152,7 +149,7 @@ export class BrowserStorageCacheClass extends StorageCache implements ICache { * @return true if cache is full */ private _isCacheFull(itemSize: number): boolean { - return itemSize + this.getCacheCurSize() > this.config.capacityInBytes; + return itemSize + this.getCacheCurSize() > this.cacheConfig.capacityInBytes; } /** @@ -167,16 +164,16 @@ export class BrowserStorageCacheClass extends StorageCache implements ICache { const keys: string[] = []; const keyInCache: string[] = []; // get all keys in Storage - for (let i = 0; i < this.config.storage.length; i += 1) { - keyInCache.push(this.config.storage.key(i)); + for (let i = 0; i < this.cacheConfig.storage.length; i += 1) { + keyInCache.push(this.cacheConfig.storage.key(i)); } // find those items which belong to our cache and also clean those expired items for (let i = 0; i < keyInCache.length; i += 1) { const key: string = keyInCache[i]; if ( - key.indexOf(this.config.keyPrefix) === 0 && - key !== this.cacheCurSizeKey + key.indexOf(this.cacheConfig.keyPrefix) === 0 && + key !== getCurrSizeKey(this.cacheConfig.keyPrefix) ) { if (this._isExpired(key)) { this._removeItem(key); @@ -202,7 +199,7 @@ export class BrowserStorageCacheClass extends StorageCache implements ICache { let remainedSize: number = sizeToPop; // get the items from Storage for (let i = 0; i < keys.length; i += 1) { - const val: string | null = this.config.storage.getItem(keys[i]); + const val: string | null = this.cacheConfig.storage.getItem(keys[i]); if (val != null) { const item: CacheItem = JSON.parse(val); items.push(item); @@ -257,11 +254,11 @@ export class BrowserStorageCacheClass extends StorageCache implements ICache { logger.log( `Set item: key is ${key}, value is ${value} with options: ${options}` ); - const prefixedKey: string = this.config.keyPrefix + key; + const prefixedKey: string = this.cacheConfig.keyPrefix + key; // invalid keys if ( - prefixedKey === this.config.keyPrefix || - prefixedKey === this.cacheCurSizeKey + prefixedKey === this.cacheConfig.keyPrefix || + prefixedKey === getCurrSizeKey(this.cacheConfig.keyPrefix) ) { logger.warn(`Invalid key: should not be empty or 'CurSize'`); return; @@ -276,11 +273,11 @@ export class BrowserStorageCacheClass extends StorageCache implements ICache { priority: options && options.priority !== undefined ? options.priority - : this.config.defaultPriority, + : this.cacheConfig.defaultPriority, expires: options && options.expires !== undefined ? options.expires - : this.config.defaultTTL + getCurrTime(), + : this.cacheConfig.defaultTTL + getCurrTime(), }; if (cacheItemOptions.priority < 1 || cacheItemOptions.priority > 5) { @@ -297,7 +294,7 @@ export class BrowserStorageCacheClass extends StorageCache implements ICache { ); // check wether this item is too big; - if (item.byteSize > this.config.itemMaxSize) { + if (item.byteSize > this.cacheConfig.itemMaxSize) { logger.warn( `Item with key: ${key} you are trying to put into is too big!` ); @@ -306,7 +303,7 @@ export class BrowserStorageCacheClass extends StorageCache implements ICache { try { // first look into the storage, if it exists, delete it. - const val: string | null = this.config.storage.getItem(prefixedKey); + const val: string | null = this.cacheConfig.storage.getItem(prefixedKey); if (val) { this._removeItem(prefixedKey, JSON.parse(val).byteSize); } @@ -347,18 +344,18 @@ export class BrowserStorageCacheClass extends StorageCache implements ICache { public getItem(key: string, options?: CacheItemOptions): any { logger.log(`Get item: key is ${key} with options ${options}`); let ret: string | null = null; - const prefixedKey: string = this.config.keyPrefix + key; + const prefixedKey: string = this.cacheConfig.keyPrefix + key; if ( - prefixedKey === this.config.keyPrefix || - prefixedKey === this.cacheCurSizeKey + prefixedKey === this.cacheConfig.keyPrefix || + prefixedKey === getCurrSizeKey(this.cacheConfig.keyPrefix) ) { logger.warn(`Invalid key: should not be empty or 'CurSize'`); return null; } try { - ret = this.config.storage.getItem(prefixedKey); + ret = this.cacheConfig.storage.getItem(prefixedKey); if (ret != null) { if (this._isExpired(prefixedKey)) { // if expired, remove that item and return null @@ -394,17 +391,17 @@ export class BrowserStorageCacheClass extends StorageCache implements ICache { */ public removeItem(key: string): void { logger.log(`Remove item: key is ${key}`); - const prefixedKey: string = this.config.keyPrefix + key; + const prefixedKey: string = this.cacheConfig.keyPrefix + key; if ( - prefixedKey === this.config.keyPrefix || - prefixedKey === this.cacheCurSizeKey + prefixedKey === this.cacheConfig.keyPrefix || + prefixedKey === getCurrSizeKey(this.cacheConfig.keyPrefix) ) { return; } try { - const val: string | null = this.config.storage.getItem(prefixedKey); + const val: string | null = this.cacheConfig.storage.getItem(prefixedKey); if (val) { this._removeItem(prefixedKey, JSON.parse(val).byteSize); } @@ -422,16 +419,16 @@ export class BrowserStorageCacheClass extends StorageCache implements ICache { logger.log(`Clear Cache`); const keysToRemove: string[] = []; - for (let i = 0; i < this.config.storage.length; i += 1) { - const key = this.config.storage.key(i); - if (key.indexOf(this.config.keyPrefix) === 0) { + for (let i = 0; i < this.cacheConfig.storage.length; i += 1) { + const key = this.cacheConfig.storage.key(i); + if (key.indexOf(this.cacheConfig.keyPrefix) === 0) { keysToRemove.push(key); } } try { for (let i = 0; i < keysToRemove.length; i += 1) { - this.config.storage.removeItem(keysToRemove[i]); + this.cacheConfig.storage.removeItem(keysToRemove[i]); } } catch (e) { logger.warn(`clear failed! ${e}`); @@ -445,13 +442,13 @@ export class BrowserStorageCacheClass extends StorageCache implements ICache { */ public getAllKeys(): string[] { const keys: string[] = []; - for (let i = 0; i < this.config.storage.length; i += 1) { - const key = this.config.storage.key(i); + for (let i = 0; i < this.cacheConfig.storage.length; i += 1) { + const key = this.cacheConfig.storage.key(i); if ( - key.indexOf(this.config.keyPrefix) === 0 && - key !== this.cacheCurSizeKey + key.indexOf(this.cacheConfig.keyPrefix) === 0 && + key !== getCurrSizeKey(this.cacheConfig.keyPrefix) ) { - keys.push(key.substring(this.config.keyPrefix.length)); + keys.push(key.substring(this.cacheConfig.keyPrefix.length)); } } return keys; @@ -463,9 +460,9 @@ export class BrowserStorageCacheClass extends StorageCache implements ICache { * @return - current size of the cache */ public getCacheCurSize(): number { - let ret: string | null = this.config.storage.getItem(this.cacheCurSizeKey); + let ret: string | null = this.cacheConfig.storage.getItem(getCurrSizeKey(this.cacheConfig.keyPrefix)); if (!ret) { - this.config.storage.setItem(this.cacheCurSizeKey, '0'); + this.cacheConfig.storage.setItem(getCurrSizeKey(this.cacheConfig.keyPrefix), '0'); ret = '0'; } return Number(ret); @@ -488,5 +485,3 @@ export class BrowserStorageCacheClass extends StorageCache implements ICache { } export const BrowserStorageCache: ICache = new BrowserStorageCacheClass(); - -Amplify.register(BrowserStorageCache); diff --git a/packages/core/src/Cache/InMemoryCache.ts b/packages/core/src/Cache/InMemoryCache.ts index 8f9f0d732b7..c00bbab6c73 100644 --- a/packages/core/src/Cache/InMemoryCache.ts +++ b/packages/core/src/Cache/InMemoryCache.ts @@ -6,6 +6,7 @@ import { CacheList, defaultConfig, getCurrTime, CacheObject } from './Utils'; import { StorageCache } from './StorageCache'; import { ICache, CacheConfig, CacheItem, CacheItemOptions } from './types'; import { ConsoleLogger as Logger } from '../Logger'; +import { getCurrSizeKey } from './Utils/CacheUtils'; const logger = new Logger('InMemoryCache'); @@ -29,11 +30,8 @@ export class InMemoryCacheClass extends StorageCache implements ICache { * @param config - the configuration of the cache */ constructor(config?: CacheConfig) { - const cacheConfig = config - ? Object.assign({}, defaultConfig, config) - : defaultConfig; - super(cacheConfig); - logger.debug('now we start!'); + super(config); + this.cacheList = []; this.curSizeInBytes = 0; this.maxPriority = 5; @@ -128,7 +126,7 @@ export class InMemoryCacheClass extends StorageCache implements ICache { * @return true if cache is full */ private _isCacheFull(itemSize: number): boolean { - return this.curSizeInBytes + itemSize > this.config.capacityInBytes; + return this.curSizeInBytes + itemSize > this.cacheConfig.capacityInBytes; } /** @@ -137,7 +135,7 @@ export class InMemoryCacheClass extends StorageCache implements ICache { * @param key */ private containsKey(key: string): number { - const prefixedKey: string = this.config.keyPrefix + key; + const prefixedKey: string = this.cacheConfig.keyPrefix + key; for (let i = 0; i < this.maxPriority; i += 1) { if (this.cacheList[i].containsKey(prefixedKey)) { return i + 1; @@ -170,11 +168,11 @@ export class InMemoryCacheClass extends StorageCache implements ICache { value: object | string | number | boolean, options?: CacheItemOptions ): void { - const prefixedKey: string = this.config.keyPrefix + key; + const prefixedKey: string = this.cacheConfig.keyPrefix + key; // invalid keys if ( - prefixedKey === this.config.keyPrefix || - prefixedKey === this.cacheCurSizeKey + prefixedKey === this.cacheConfig.keyPrefix || + prefixedKey === getCurrSizeKey(this.cacheConfig.keyPrefix) ) { logger.warn(`Invalid key: should not be empty or 'CurSize'`); return; @@ -189,11 +187,11 @@ export class InMemoryCacheClass extends StorageCache implements ICache { priority: options && options.priority !== undefined ? options.priority - : this.config.defaultPriority, + : this.cacheConfig.defaultPriority, expires: options && options.expires !== undefined ? options.expires - : this.config.defaultTTL + getCurrTime(), + : this.cacheConfig.defaultTTL + getCurrTime(), }; if (cacheItemOptions.priority < 1 || cacheItemOptions.priority > 5) { @@ -210,7 +208,7 @@ export class InMemoryCacheClass extends StorageCache implements ICache { ); // check wether this item is too big; - if (item.byteSize > this.config.itemMaxSize) { + if (item.byteSize > this.cacheConfig.itemMaxSize) { logger.warn( `Item with key: ${key} you are trying to put into is too big!` ); @@ -252,11 +250,11 @@ export class InMemoryCacheClass extends StorageCache implements ICache { */ public getItem(key: string, options?: CacheItemOptions): any { let ret: string | null = null; - const prefixedKey: string = this.config.keyPrefix + key; + const prefixedKey: string = this.cacheConfig.keyPrefix + key; if ( - prefixedKey === this.config.keyPrefix || - prefixedKey === this.cacheCurSizeKey + prefixedKey === this.cacheConfig.keyPrefix || + prefixedKey === getCurrSizeKey(this.cacheConfig.keyPrefix) ) { logger.warn(`Invalid key: should not be empty or 'CurSize'`); return null; @@ -293,7 +291,7 @@ export class InMemoryCacheClass extends StorageCache implements ICache { * @param key - the key of the item */ public removeItem(key: string): void { - const prefixedKey: string = this.config.keyPrefix + key; + const prefixedKey: string = this.cacheConfig.keyPrefix + key; // check if the key is in the cache const presentKeyPrio: number = this.containsKey(key); @@ -320,7 +318,7 @@ export class InMemoryCacheClass extends StorageCache implements ICache { const keys: string[] = []; for (let i = 0; i < this.maxPriority; i += 1) { for (const key of this.cacheList[i].getKeys()) { - keys.push(key.substring(this.config.keyPrefix.length)); + keys.push(key.substring(this.cacheConfig.keyPrefix.length)); } } diff --git a/packages/core/src/Cache/StorageCache.ts b/packages/core/src/Cache/StorageCache.ts index 33deaca501b..71c274a9037 100644 --- a/packages/core/src/Cache/StorageCache.ts +++ b/packages/core/src/Cache/StorageCache.ts @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 import { getCurrTime, getByteLength, defaultConfig, isInteger } from './Utils'; - +import { AmplifyV6 } from '../singleton'; import { CacheConfig, CacheItem, CacheItemOptions } from './types'; import { ConsoleLogger as Logger } from '../Logger'; @@ -13,83 +13,66 @@ const logger = new Logger('StorageCache'); * */ export class StorageCache { - protected cacheCurSizeKey: string; - protected config: CacheConfig; + // Contains any fields that have been customized for this Cache instance (i.e. without default values) + private instanceConfig: CacheConfig; /** * Initialize the cache - * @param config - the configuration of the cache + * + * @param config - Custom configuration for this instance. */ - constructor(config: CacheConfig) { - this.config = Object.assign({}, config); - this.cacheCurSizeKey = this.config.keyPrefix + 'CurSize'; - this.checkConfig(); + constructor(config?: CacheConfig) { + if (config) { + // A configuration was specified for this specific instance + this.instanceConfig = config; + } + + this.sanitizeConfig(); } public getModuleName() { return 'Cache'; } - private checkConfig(): void { - // check configuration - if (!isInteger(this.config.capacityInBytes)) { - logger.error( - 'Invalid parameter: capacityInBytes. It should be an Integer. Setting back to default.' - ); - this.config.capacityInBytes = defaultConfig.capacityInBytes; - } - - if (!isInteger(this.config.itemMaxSize)) { - logger.error( - 'Invalid parameter: itemMaxSize. It should be an Integer. Setting back to default.' - ); - this.config.itemMaxSize = defaultConfig.itemMaxSize; - } - - if (!isInteger(this.config.defaultTTL)) { - logger.error( - 'Invalid parameter: defaultTTL. It should be an Integer. Setting back to default.' - ); - this.config.defaultTTL = defaultConfig.defaultTTL; - } - - if (!isInteger(this.config.defaultPriority)) { - logger.error( - 'Invalid parameter: defaultPriority. It should be an Integer. Setting back to default.' - ); - this.config.defaultPriority = defaultConfig.defaultPriority; - } + private sanitizeConfig(): void { + const tempInstanceConfig = this.instanceConfig || {}; - if (this.config.itemMaxSize > this.config.capacityInBytes) { + if (this.cacheConfig.itemMaxSize > this.cacheConfig.capacityInBytes) { logger.error( 'Invalid parameter: itemMaxSize. It should be smaller than capacityInBytes. Setting back to default.' ); - this.config.itemMaxSize = defaultConfig.itemMaxSize; + tempInstanceConfig.itemMaxSize = defaultConfig.itemMaxSize; } - if (this.config.defaultPriority > 5 || this.config.defaultPriority < 1) { + if (this.cacheConfig.defaultPriority > 5 || this.cacheConfig.defaultPriority < 1) { logger.error( 'Invalid parameter: defaultPriority. It should be between 1 and 5. Setting back to default.' ); - this.config.defaultPriority = defaultConfig.defaultPriority; + tempInstanceConfig.defaultPriority = defaultConfig.defaultPriority; } if ( - Number(this.config.warningThreshold) > 1 || - Number(this.config.warningThreshold) < 0 + Number(this.cacheConfig.warningThreshold) > 1 || + Number(this.cacheConfig.warningThreshold) < 0 ) { logger.error( 'Invalid parameter: warningThreshold. It should be between 0 and 1. Setting back to default.' ); - this.config.warningThreshold = defaultConfig.warningThreshold; + tempInstanceConfig.warningThreshold = defaultConfig.warningThreshold; } - // set 5MB limit + + // Set 5MB limit const cacheLimit: number = 5 * 1024 * 1024; - if (this.config.capacityInBytes > cacheLimit) { + if (this.cacheConfig.capacityInBytes > cacheLimit) { logger.error( 'Cache Capacity should be less than 5MB. Setting back to default. Setting back to default.' ); - this.config.capacityInBytes = defaultConfig.capacityInBytes; + tempInstanceConfig.capacityInBytes = defaultConfig.capacityInBytes; + } + + // Apply sanitized values to the instance config + if (Object.keys(tempInstanceConfig).length > 0) { + this.instanceConfig = tempInstanceConfig; } } @@ -124,21 +107,39 @@ export class StorageCache { } /** - * set cache with customized configuration - * @param config - customized configuration + * Set custom configuration for the cache instance. + * + * @param config - customized configuration (without keyPrefix, which can't be changed) * * @return - the current configuration */ - public configure(config?: CacheConfig): CacheConfig { - if (!config) { - return this.config; - } - if (config.keyPrefix) { - logger.warn(`Don't try to configure keyPrefix!`); + public configure(config?: Omit): CacheConfig { + if (config) { + if ((config as CacheConfig).keyPrefix) { + logger.warn('keyPrefix can not be re-configured on an existing Cache instance.'); + } + + this.instanceConfig = this.instanceConfig ? Object.assign({}, this.instanceConfig, config) : config; } - this.config = Object.assign({}, this.config, config, config.Cache); - this.checkConfig(); - return this.config; + this.sanitizeConfig(); + + return this.cacheConfig; + } + + /** + * Returns an appropriate configuration for the Cache instance. Will apply any custom configuration for this + * instance on top of the global configuration. Default configuration will be applied in all cases. + * + * @internal + */ + protected get cacheConfig(): CacheConfig { + const globalCacheConfig = AmplifyV6.getConfig().Cache || {}; + + if (this.instanceConfig) { + return Object.assign({}, defaultConfig, globalCacheConfig, this.instanceConfig); + } else { + return Object.assign({}, defaultConfig, globalCacheConfig); + } } } diff --git a/packages/core/src/Cache/Utils/CacheUtils.ts b/packages/core/src/Cache/Utils/CacheUtils.ts index 8ec6d44f751..80d6371ecb3 100644 --- a/packages/core/src/Cache/Utils/CacheUtils.ts +++ b/packages/core/src/Cache/Utils/CacheUtils.ts @@ -88,3 +88,5 @@ export class CacheObject { delete store[key]; } } + +export const getCurrSizeKey = (keyPrefix: string) => keyPrefix + 'CurSize'; diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 57f6b49fdfc..c9cb8a79e74 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -114,6 +114,9 @@ export { InMemoryCache } from './Cache/InMemoryCache'; export { CacheConfig } from './Cache/types'; export { ICache } from './Cache/types'; export { BrowserStorageCache }; +export { BrowserStorageCache as Cache }; // Maintain interoperability with React Native + +// Singleton exports export { decodeJWT, assertTokenProviderConfig, @@ -147,9 +150,6 @@ export { export { AmplifyV6, fetchAuthSession } from './singleton'; export { LibraryOptions, ResourcesConfig } from './singleton/types'; -// Standard `Cache` export to maintain interoperability with React Native -export { BrowserStorageCache as Cache }; - /** * @deprecated use named import */ diff --git a/packages/core/src/singleton/types.ts b/packages/core/src/singleton/types.ts index a2129e4de7b..9ebb4042bc8 100644 --- a/packages/core/src/singleton/types.ts +++ b/packages/core/src/singleton/types.ts @@ -14,12 +14,16 @@ import { StorageAccessLevel, StorageConfig, } from './Storage/types'; +import { + CacheConfig +} from '../Cache/types'; import { I18nOptions } from '../I18n/types'; export type ResourcesConfig = { API?: {}; Analytics?: {}; Auth?: AuthConfig; + Cache?: CacheConfig; DataStore?: {}; I18n?: I18nOptions; Interactions?: {};