From f708efe52c9d5e47a4d909505643f8defe172393 Mon Sep 17 00:00:00 2001 From: naser rasouli Date: Mon, 9 Dec 2024 14:39:36 +0330 Subject: [PATCH] Define cache management Install idb package for control indexedDB Define indexedDBManager.js localStorageManager.js cacheStorageManager.js Each of them for control cache driver Define cacheManager.js for control drivers Define strategiesManager.js for control strategies for caching Add cache cacheType strategy params in get and request in api.service.js Add cache cacheType strategy params in get and request in crud.service.js And just add option params to useFetchUser and useFetchTodos --- package-lock.json | 12 +++ package.json | 1 + src/composables/todo.composable.js | 20 +++- src/composables/user.composable.js | 26 ++++- src/enums/CacheDriverType.js | 6 ++ src/enums/CacheStrategies.js | 8 ++ src/services/api.service.js | 61 +++++++++++- src/services/crud.service.js | 14 ++- src/utils/cache/cacheManager.js | 70 ++++++++++++++ src/utils/cache/cacheStorageManager.js | 101 ++++++++++++++++++++ src/utils/cache/indexedDBManager.js | 125 +++++++++++++++++++++++++ src/utils/cache/localStorageManager.js | 94 +++++++++++++++++++ src/utils/cache/strategiesManager.js | 78 +++++++++++++++ src/views/TodosView.vue | 8 +- 14 files changed, 607 insertions(+), 17 deletions(-) create mode 100644 src/enums/CacheDriverType.js create mode 100644 src/enums/CacheStrategies.js create mode 100644 src/utils/cache/cacheManager.js create mode 100644 src/utils/cache/cacheStorageManager.js create mode 100644 src/utils/cache/indexedDBManager.js create mode 100644 src/utils/cache/localStorageManager.js create mode 100644 src/utils/cache/strategiesManager.js diff --git a/package-lock.json b/package-lock.json index 6c9e001..491e5bd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,6 +12,7 @@ "axios": "^1.3.2", "bootstrap": "^5.3.1", "bootstrap-icons": "^1.11.2", + "idb": "^8.0.0", "pasoonate": "^1.2.5", "pinia": "^2.0.28", "vue": "^3.3.4", @@ -1528,6 +1529,12 @@ "node": ">=8" } }, + "node_modules/idb": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/idb/-/idb-8.0.0.tgz", + "integrity": "sha512-l//qvlAKGmQO31Qn7xdzagVPPaHTxXx199MhrAFuVBTPqydcPYBWjkrbv4Y0ktB+GmWOiwHl237UUOrLmQxLvw==", + "license": "ISC" + }, "node_modules/ignore": { "version": "5.2.4", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", @@ -3548,6 +3555,11 @@ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true }, + "idb": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/idb/-/idb-8.0.0.tgz", + "integrity": "sha512-l//qvlAKGmQO31Qn7xdzagVPPaHTxXx199MhrAFuVBTPqydcPYBWjkrbv4Y0ktB+GmWOiwHl237UUOrLmQxLvw==" + }, "ignore": { "version": "5.2.4", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", diff --git a/package.json b/package.json index 65ebf18..95c4792 100644 --- a/package.json +++ b/package.json @@ -13,6 +13,7 @@ "axios": "^1.3.2", "bootstrap": "^5.3.1", "bootstrap-icons": "^1.11.2", + "idb": "^8.0.0", "pasoonate": "^1.2.5", "pinia": "^2.0.28", "vue": "^3.3.4", diff --git a/src/composables/todo.composable.js b/src/composables/todo.composable.js index d4fbcff..0d5111b 100644 --- a/src/composables/todo.composable.js +++ b/src/composables/todo.composable.js @@ -8,8 +8,22 @@ import { useLoading } from '@/composables/loading.composable'; // Utils import { keyBy } from '@/utils'; - -export function useFetchTodos() { +import StrategiesManager from "@/utils/cache/strategiesManager"; +import CacheStrategies from "@/enums/CacheStrategies"; +import CacheDriverType from "@/enums/CacheDriverType"; +/** + * Hook to fetch all todos with caching options. + * @param {Object} options - Options for caching. + * @param {boolean} [options.cache] - Enable or disable caching. + * @param {"LocalStorage"|"CacheStorage"|"IndexedDB"} [options.cacheType] - Cache type. + * @param {"justCache"|"cacheFirstThenUpdate"} [options.strategy] - Caching strategy. + */ +export function useFetchTodos(options = {}) { + const { + cache = false, + cacheType = CacheDriverType.LOCAL_STORAGE, + strategy = CacheStrategies.CACHE_FIRST_THEN_UPDATE, + } = options; const { isLoading, startLoading, endLoading } = useLoading(); const todos = ref([]); @@ -22,7 +36,7 @@ export function useFetchTodos() { function fetchTodos(config) { startLoading(); - return TodoService.getAll(config) + return TodoService.getAll(config,cache, cacheType, strategy) .then(function (response) { todos.value = response.data; return response; diff --git a/src/composables/user.composable.js b/src/composables/user.composable.js index 309f1df..33cca58 100644 --- a/src/composables/user.composable.js +++ b/src/composables/user.composable.js @@ -8,18 +8,34 @@ import { useLoading } from '@/composables/loading.composable'; // Utils import { keyBy } from '@/utils'; - -export function useFetchUsers() { +import StrategiesManager from "@/utils/cache/strategiesManager"; +import CacheDriverType from "@/enums/CacheDriverType"; +import CacheStrategies from "@/enums/CacheStrategies"; +/** + * Hook to fetch all todos with caching options. + * @param {Object} options - Options for caching. + * @param {boolean} [options.cache=false] - Enable or disable caching. + * @param {"LocalStorage"|"CacheStorage"|"IndexedDB"} [options.cacheType=CacheDriverType.LOCAL_STORAGE] - Cache type. + * @param {"justCache"|"cacheFirstThenUpdate"} [options.strategy=CacheStrategies.CACHE_FIRST_THEN_UPDATE] - Caching strategy. + */ +export function useFetchUsers(options = {}) { + const { + cache = false, + cacheType = CacheDriverType.INDEXED_DB, + strategy = CacheStrategies.CACHE_FIRST_THEN_UPDATE, + } = options; const { isLoading, startLoading, endLoading } = useLoading(); const users = ref([]); const usersKeyById = computed(() => keyBy(users.value, 'id')); - - function fetchUsers() { + /** + * @param {AxiosRequestConfig} [config] + */ + function fetchUsers(config) { startLoading(); - return UserService.getAll() + return UserService.getAll(config, cache, cacheType, strategy) .then(function (response) { users.value = response.data; return response; diff --git a/src/enums/CacheDriverType.js b/src/enums/CacheDriverType.js new file mode 100644 index 0000000..2d4bcf3 --- /dev/null +++ b/src/enums/CacheDriverType.js @@ -0,0 +1,6 @@ +const driverType={ + LOCAL_STORAGE:"LocalStorage", + CACHE_STORAGE:"CacheStorage", + INDEXED_DB:"IndexedDB" +} +export default Object.freeze(driverType); diff --git a/src/enums/CacheStrategies.js b/src/enums/CacheStrategies.js new file mode 100644 index 0000000..5b7ba10 --- /dev/null +++ b/src/enums/CacheStrategies.js @@ -0,0 +1,8 @@ +const strategies = { + JUST_CACHE: 'justCache', + CACHE_FIRST_THEN_UPDATE: 'cacheFirstThenUpdate', +}; + + + +export default Object.freeze(strategies); diff --git a/src/services/api.service.js b/src/services/api.service.js index 047b07a..7cc055b 100644 --- a/src/services/api.service.js +++ b/src/services/api.service.js @@ -2,6 +2,9 @@ import axios from 'axios'; import HttpMethod from '@/enums/HttpMethod'; +import StrategiesManager from "@/utils/cache/strategiesManager" +import CacheManager from "@/utils/cache/cacheManager"; + /** * @callback onFulfilledRequest * @param {AxiosRequestConfig} config @@ -18,6 +21,17 @@ const instance = axios.create({ }); class ApiService { + + /** + * Generate cache key based on URL and query params. + * @param {string} url - The URL. + * @param {Object} [params] - The query parameters. + * @returns {string} Cache key. + */ + static generateCacheKey(url, params = {}) { + const queryParams = new URLSearchParams(params).toString(); + return queryParams ? `${url}?${queryParams}` : url; + } /** * Set header for all or specific http method * @@ -89,10 +103,30 @@ class ApiService { * Custom request * * @param {AxiosRequestConfig} config + * @param {Boolean} cache + * @param {"LocalStorage"|"CacheStorage"|"IndexedDB"} cacheType + * @param {"justCache"|"cacheFirstThenUpdate"} strategy - Caching strategy. * @returns {Promise} */ - static request(config) { - return instance.request(config); + static async request(config,cache,cacheType,strategy) { + const url = config.url + const key = this.generateCacheKey(url, config?.params ?config.params : {}); + if (cache && config.method==="get") { + return StrategiesManager.executeStrategy(strategy, { + key, + cacheType, + url, + config, + axiosInstance: instance, + }); + } + + + const response = await instance.get(url, config); + if (cache && config.method==="get") { + await CacheManager.set(key, response.data, cacheType); + } + return response; } /** @@ -100,10 +134,29 @@ class ApiService { * * @param {String} url * @param {AxiosRequestConfig} [config] + * @param {Boolean} cache + * @param {"LocalStorage"|"CacheStorage"|"IndexedDB"} cacheType + * @param {"justCache"|"cacheFirstThenUpdate"} strategy - Caching strategy. * @returns {Promise} */ - static get(url, config) { - return instance.get(url, config); + static async get(url, config,cache,cacheType,strategy = StrategiesManager.strategies.CACHE_FIRST_THEN_UPDATE) { + const key = this.generateCacheKey(url, config?.params ?config.params : {}); + if (cache) { + return StrategiesManager.executeStrategy(strategy, { + key, + cacheType, + url, + config, + axiosInstance: instance, + }); + } + + + const response = await instance.get(url, config); + if (cache) { + await CacheManager.set(key, response.data, cacheType); + } + return response; } /** diff --git a/src/services/crud.service.js b/src/services/crud.service.js index d3e062b..dbd5ecd 100644 --- a/src/services/crud.service.js +++ b/src/services/crud.service.js @@ -17,10 +17,13 @@ class CrudService { * Get items * * @param {AxiosRequestConfig} [config] + * @param {Boolean} cache + * @param {"LocalStorage"|"CacheStorage"|"IndexedDB"} cacheType + * @param {"justCache"|"cacheFirstThenUpdate"} strategy - Caching strategy. * @returns {Promise} */ - static getAll(config) { - return ApiService.get(this.URL, config); + static getAll(config,cache, cacheType, strategy) { + return ApiService.get(this.URL, config,cache, cacheType, strategy); } /** @@ -28,10 +31,13 @@ class CrudService { * * @param {Number|String} id * @param {AxiosRequestConfig} [config] + * @param {Boolean} cache + * @param {"LocalStorage"|"CacheStorage"|"IndexedDB"} cacheType + * @param {"justCache"|"cacheFirstThenUpdate"} strategy - Caching strategy. * @returns {Promise} */ - static getOneById(id, config) { - return ApiService.get(`${this.URL}/${id}`, config); + static getOneById(id, config,cache, cacheType, strategy) { + return ApiService.get(`${this.URL}/${id}`, config,cache, cacheType, strategy); } /** diff --git a/src/utils/cache/cacheManager.js b/src/utils/cache/cacheManager.js new file mode 100644 index 0000000..b2d7f26 --- /dev/null +++ b/src/utils/cache/cacheManager.js @@ -0,0 +1,70 @@ +import {openDB} from "idb"; +import CacheDriverType from "@/enums/CacheDriverType"; + +class CacheManager { + + + /** + * Get data from the cache. + * @param {string} key - The cache key. + * @param {"LocalStorage"|"CacheStorage"|"IndexedDB"} cacheType - The cache type. + * @returns {Promise} Cached data or null if not found. + */ + static async get(key, cacheType) { + + if (cacheType === CacheDriverType.LOCAL_STORAGE) { + return JSON.parse(localStorage.getItem(key)) || null; + } else if (cacheType === CacheDriverType.CACHE_STORAGE) { + const cache = await caches.open('api-cache'); + const response = await cache.match(key); + return response ? await response.json() : null; + } else if (cacheType === CacheDriverType.INDEXED_DB) { + const db = await this._getIndexedDB(); + const data = await db.get('api-cache', key); + return data || null; + } else { + throw new Error(`Unsupported cache type: ${cacheType}`); + } + } + + /** + * Set data to the cache. + * @param {string} key - The cache key. + * @param {any} data - The data to cache. + * @param {"LocalStorage"|"CacheStorage"|"IndexedDB"} cacheType - The cache type. + * @returns {Promise} + */ + static async set(key, data, cacheType) { + if (cacheType === CacheDriverType.LOCAL_STORAGE) { + localStorage.setItem(key, JSON.stringify(data)); + } else if (cacheType === CacheDriverType.CACHE_STORAGE) { + const cache = await caches.open('api-cache'); + const response = new Response(JSON.stringify(data)); + await cache.put(key, response); + } else if (cacheType === CacheDriverType.INDEXED_DB) { + const db = await this._getIndexedDB(); + await db.put('api-cache', data, key); + } else { + throw new Error(`Unsupported cache type: ${cacheType}`); + } + } + + /** + * Internal method to get IndexedDB instance. + * @returns {Promise} + * @private + */ + static async _getIndexedDB() { + if (!this.dbPromise) { + this.dbPromise = openDB('cache-db', 1, { + upgrade(db) { + if (!db.objectStoreNames.contains('api-cache')) { + db.createObjectStore('api-cache'); + } + }, + }); + } + return this.dbPromise; + } +} + export default CacheManager; diff --git a/src/utils/cache/cacheStorageManager.js b/src/utils/cache/cacheStorageManager.js new file mode 100644 index 0000000..10286c7 --- /dev/null +++ b/src/utils/cache/cacheStorageManager.js @@ -0,0 +1,101 @@ +/** + * CacheStorageManager for managing browser Cache Storage operations. + */ +class CacheStorageManager { + /** @type {string} */ + static cacheName = 'default-cache'; + + /** + * Configures the cache name. + * @param {string} cacheName - The name of the cache. + * @example + * CacheStorageManager.configure('custom-cache'); + */ + static configure(cacheName) { + this.cacheName = cacheName; + } + + /** + * Retrieves the cache instance. + * @returns {Promise} A promise that resolves to the cache instance. + * @private + */ + static async getCache() { + return await caches.open(this.cacheName); + } + + /** + * Sets data in the cache storage. + * @param {string} key - The key to identify the data. + * @param {*} data - The data to be stored (automatically stringified). + * @returns {Promise} A promise that resolves when the data is saved. + * @example + * await CacheStorageManager.set('exampleKey', { value: 42 }); + */ + static async set(key, data) { + const cache = await this.getCache(); + const request = new Request(key); + const response = new Response(JSON.stringify(data), { + headers: { 'Content-Type': 'application/json' }, + }); + await cache.put(request, response); + } + + /** + * Gets data from the cache storage. + * @param {string} key - The key to identify the data. + * @returns {Promise<*>} A promise that resolves to the retrieved data, or null if not found. + * @example + * const data = await CacheStorageManager.get('exampleKey'); + * (data); + */ + static async get(key) { + const cache = await this.getCache(); + const request = new Request(key); + const response = await cache.match(request); + if (!response) return null; + return await response.json(); + } + + /** + * Deletes data from the cache storage. + * @param {string} key - The key to identify the data to be deleted. + * @returns {Promise} A promise that resolves when the data is deleted. + * @example + * await CacheStorageManager.delete('exampleKey'); + */ + static async delete(key) { + const cache = await this.getCache(); + const request = new Request(key); + await cache.delete(request); + } + + /** + * Clears all data from the cache storage. + * @returns {Promise} A promise that resolves when the cache is cleared. + * @example + * await CacheStorageManager.clear(); + */ + static async clear() { + await caches.delete(this.cacheName); + } + + /** + * Retrieves all keys from the cache storage. + * @returns {Promise} A promise that resolves to an array of keys. + * @example + * const keys = await CacheStorageManager.getAllKeys(); + * console.log(keys); + */ + static async getAllKeys() { + const cache = await this.getCache(); + const keys = []; + const requests = await cache.keys(); + for (const request of requests) { + keys.push(new URL(request.url).pathname); // Extracts the key from the URL + } + return keys; + } +} + +export default CacheStorageManager; diff --git a/src/utils/cache/indexedDBManager.js b/src/utils/cache/indexedDBManager.js new file mode 100644 index 0000000..3d93b07 --- /dev/null +++ b/src/utils/cache/indexedDBManager.js @@ -0,0 +1,125 @@ +import { openDB } from 'idb'; + +/** + * IndexedDBManager for managing IndexedDB operations. + */ +class IndexedDBManager { + /** @type {string} */ + static dbName = 'my-app-db'; + + /** @type {string} */ + static storeName = 'api-cache'; + + /** @type {Promise | null} */ + static dbPromise = null; + + /** + * Configures the database and object store names. + * @param {string} dbName - The name of the database. + * @param {string} storeName - The name of the object store. + * @example + * IndexedDBManager.configure('custom-db', 'custom-store'); + */ + static configure(dbName, storeName) { + this.dbName = dbName; + this.storeName = storeName; + } + + /** + * Initializes the IndexedDB database. + * @returns {Promise} A promise that resolves to the database instance. + * @private + */ + static async initDB() { + if (!this.dbPromise) { + this.dbPromise = openDB(this.dbName, 1, { + upgrade(db) { + if (!db.objectStoreNames.contains(IndexedDBManager.storeName)) { + db.createObjectStore(IndexedDBManager.storeName, { keyPath: 'key' }); + } + }, + }); + } + return this.dbPromise; + } + + /** + * Saves data to IndexedDB. + * @param {string} key - The key to identify the data. + * @param {*} data - The data to be saved. + * @returns {Promise} A promise that resolves when the data is saved. + * @example + * await IndexedDBManager.save('exampleKey', { value: 42 }); + */ + static async save(key, data) { + const db = await this.initDB(); + const tx = db.transaction(this.storeName, 'readwrite'); + const store = tx.objectStore(this.storeName); + await store.put({ key, data }); + await tx.done; + } + + /** + * Loads data from IndexedDB. + * @param {string} key - The key to identify the data. + * @returns {Promise<*>} A promise that resolves to the loaded data, or null if not found. + * @example + * const data = await IndexedDBManager.load('exampleKey'); + * console.log(data); + */ + static async load(key) { + const db = await this.initDB(); + const tx = db.transaction(this.storeName, 'readonly'); + const store = tx.objectStore(this.storeName); + const result = await store.get(key); + await tx.done; + return result ? result.data : null; + } + + /** + * Deletes data from IndexedDB. + * @param {string} key - The key to identify the data to be deleted. + * @returns {Promise} A promise that resolves when the data is deleted. + * @example + * await IndexedDBManager.delete('exampleKey'); + */ + static async delete(key) { + const db = await this.initDB(); + const tx = db.transaction(this.storeName, 'readwrite'); + const store = tx.objectStore(this.storeName); + await store.delete(key); + await tx.done; + } + + /** + * Clears all data from the object store. + * @returns {Promise} A promise that resolves when the store is cleared. + * @example + * await IndexedDBManager.clear(); + */ + static async clear() { + const db = await this.initDB(); + const tx = db.transaction(this.storeName, 'readwrite'); + const store = tx.objectStore(this.storeName); + await store.clear(); + await tx.done; + } + + /** + * Retrieves all keys from the object store. + * @returns {Promise} A promise that resolves to an array of keys. + * @example + * const keys = await IndexedDBManager.getAllKeys(); + * console.log(keys); + */ + static async getAllKeys() { + const db = await this.initDB(); + const tx = db.transaction(this.storeName, 'readonly'); + const store = tx.objectStore(this.storeName); + const keys = await store.getAllKeys(); + await tx.done; + return keys; + } +} + +export default IndexedDBManager; diff --git a/src/utils/cache/localStorageManager.js b/src/utils/cache/localStorageManager.js new file mode 100644 index 0000000..5e0222c --- /dev/null +++ b/src/utils/cache/localStorageManager.js @@ -0,0 +1,94 @@ +/** + * LocalStorageManager for managing browser localStorage operations. + */ +class LocalStorageManager { + /** @type {string} */ + static prefix = 'default-'; + + /** + * Configures a prefix for all keys to avoid conflicts. + * @param {string} prefix - The prefix to use for all keys. + * @example + * LocalStorageManager.configure('custom-'); + */ + static configure(prefix) { + this.prefix = prefix; + } + + /** + * Constructs the full key with the prefix. + * @param {string} key - The key to construct. + * @returns {string} The full key with the prefix. + * @private + */ + static constructKey(key) { + return `${this.prefix}${key}`; + } + + /** + * Sets data in localStorage. + * @param {string} key - The key to identify the data. + * @param {*} data - The data to be stored (automatically stringified). + * @example + * LocalStorageManager.set('exampleKey', { value: 42 }); + */ + static set(key, data) { + const fullKey = this.constructKey(key); + localStorage.setItem(fullKey, JSON.stringify(data)); + } + + /** + * Gets data from localStorage. + * @param {string} key - The key to identify the data. + * @returns {*} The retrieved data, or null if not found. + * @example + * const data = LocalStorageManager.get('exampleKey'); + * (data); + */ + static get(key) { + const fullKey = this.constructKey(key); + const storedData = localStorage.getItem(fullKey); + return storedData ? JSON.parse(storedData) : null; + } + + /** + * Deletes data from localStorage. + * @param {string} key - The key to identify the data to be deleted. + * @example + * LocalStorageManager.delete('exampleKey'); + */ + static delete(key) { + const fullKey = this.constructKey(key); + localStorage.removeItem(fullKey); + } + + /** + * Clears all keys with the configured prefix from localStorage. + * @example + * LocalStorageManager.clear(); + */ + static clear() { + const keys = Object.keys(localStorage); + keys.forEach((storedKey) => { + if (storedKey.startsWith(this.prefix)) { + localStorage.removeItem(storedKey); + } + }); + } + + /** + * Retrieves all keys with the configured prefix from localStorage. + * @returns {string[]} An array of keys without the prefix. + * @example + * const keys = LocalStorageManager.getAllKeys(); + * console.log(keys); + */ + static getAllKeys() { + const keys = Object.keys(localStorage); + return keys + .filter((storedKey) => storedKey.startsWith(this.prefix)) + .map((storedKey) => storedKey.replace(this.prefix, '')); + } +} + +export default LocalStorageManager; diff --git a/src/utils/cache/strategiesManager.js b/src/utils/cache/strategiesManager.js new file mode 100644 index 0000000..b6cfa4f --- /dev/null +++ b/src/utils/cache/strategiesManager.js @@ -0,0 +1,78 @@ +import CacheManager from "@/utils/cache/cacheManager"; +import CacheStrategies from "@/enums/CacheStrategies"; +import CacheDriverType from "@/enums/CacheDriverType"; + +class StrategiesManager { + + /** + * Handle the `justCache` strategy. + * @param {string} key - The cache key. + * @param {"LocalStorage"|"CacheStorage"|"IndexedDB"} cacheType - The cache type. + * @returns {Promise} Cached data. + * @throws {Error} If data is not found in the cache. + */ + static async justCache(key, cacheType) { + const cachedData = await CacheManager.get(key, cacheType); + if (cachedData) { + return { data: cachedData, fromCache: true }; + } + throw new Error('Data not found in cache'); + } + + /** + * Handle the `cacheFirstThenUpdate` strategy. + * @param {string} key - The cache key. + * @param {"LocalStorage"|"CacheStorage"|"IndexedDB"} cacheType - The cache type. + * @param {string} url - The URL to fetch data from. + * @param {AxiosRequestConfig} config - Axios config. + * @param {AxiosInstance} axiosInstance - The Axios instance. + * @returns {Promise} Cached data if available, otherwise fetched data. + */ + static async cacheFirstThenUpdate(key, cacheType, url, config, axiosInstance) { + const cachedData = await CacheManager.get(key, cacheType); + if (cachedData) { + // Return cached data immediately + setTimeout(async () => { + try { + const response = await axiosInstance.get(url, config); + await CacheManager.set(key, response.data, cacheType); + } catch (err) { + console.error('Failed to update cache:', err); + } + }, 0); + return { data: cachedData, fromCache: true }; + } + // If no cached data, fetch from API + const response = await axiosInstance.get(url, config); + await CacheManager.set(key, response.data, cacheType); + return response; + } + + /** + * Execute the specified caching strategy. + * @param {string} strategy - The strategy to execute. + * @param {Object} options - The options required for the strategy. + * @returns {Promise} The response data. + */ + static async executeStrategy(strategy, options) { + + switch (strategy) { + case CacheStrategies.JUST_CACHE: + return this.justCache(options.key, options.cacheType); + + case CacheStrategies.CACHE_FIRST_THEN_UPDATE: + return this.cacheFirstThenUpdate( + options.key, + options.cacheType, + options.url, + options.config, + options.axiosInstance + ); + + default: + throw new Error(`Unsupported caching strategy: ${strategy}`); + } + } +} + +export default StrategiesManager diff --git a/src/views/TodosView.vue b/src/views/TodosView.vue index 2124989..eae016d 100644 --- a/src/views/TodosView.vue +++ b/src/views/TodosView.vue @@ -59,6 +59,8 @@ // Stores import { useUserStore } from '@/stores/user.store'; + import CacheDriverType from "@/enums/CacheDriverType"; + import CacheStrategies from "@/enums/CacheStrategies"; export default { name: 'TodosView', @@ -70,7 +72,11 @@ }, setup() { - const { todosLoading, todos, fetchTodos } = useFetchTodos(); + const { todosLoading, todos, fetchTodos } = useFetchTodos({ + cache:true, + cacheType:CacheDriverType.INDEXED_DB, + strategy:CacheStrategies.CACHE_FIRST_THEN_UPDATE + }); const { page, itemsPerPage, paginationParams } = useRoutePagination(); const filters = useApplyFilters({