Skip to content

Define cache management #51

New issue

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

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

Already on GitHub? # to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all 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
12 changes: 12 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
20 changes: 17 additions & 3 deletions src/composables/todo.composable.js
Original file line number Diff line number Diff line change
Expand Up @@ -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([]);
Expand All @@ -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;
Expand Down
26 changes: 21 additions & 5 deletions src/composables/user.composable.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
6 changes: 6 additions & 0 deletions src/enums/CacheDriverType.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
const driverType={
LOCAL_STORAGE:"LocalStorage",
CACHE_STORAGE:"CacheStorage",
INDEXED_DB:"IndexedDB"
}
export default Object.freeze(driverType);
8 changes: 8 additions & 0 deletions src/enums/CacheStrategies.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
const strategies = {
JUST_CACHE: 'justCache',
CACHE_FIRST_THEN_UPDATE: 'cacheFirstThenUpdate',
};



export default Object.freeze(strategies);
61 changes: 57 additions & 4 deletions src/services/api.service.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
*
Expand Down Expand Up @@ -89,21 +103,60 @@ class ApiService {
* Custom request
*
* @param {AxiosRequestConfig} config
* @param {Boolean} cache
* @param {"LocalStorage"|"CacheStorage"|"IndexedDB"} cacheType
* @param {"justCache"|"cacheFirstThenUpdate"} strategy - Caching strategy.
* @returns {Promise<AxiosResponse>}
*/
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;
}

/**
* Get request
*
* @param {String} url
* @param {AxiosRequestConfig} [config]
* @param {Boolean} cache
* @param {"LocalStorage"|"CacheStorage"|"IndexedDB"} cacheType
* @param {"justCache"|"cacheFirstThenUpdate"} strategy - Caching strategy.
* @returns {Promise<AxiosResponse>}
*/
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;
}

/**
Expand Down
14 changes: 10 additions & 4 deletions src/services/crud.service.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,21 +17,27 @@ class CrudService {
* Get items
*
* @param {AxiosRequestConfig} [config]
* @param {Boolean} cache
* @param {"LocalStorage"|"CacheStorage"|"IndexedDB"} cacheType
* @param {"justCache"|"cacheFirstThenUpdate"} strategy - Caching strategy.
* @returns {Promise<AxiosResponse>}
*/
static getAll(config) {
return ApiService.get(this.URL, config);
static getAll(config,cache, cacheType, strategy) {
return ApiService.get(this.URL, config,cache, cacheType, strategy);
}

/**
* Get item by id
*
* @param {Number|String} id
* @param {AxiosRequestConfig} [config]
* @param {Boolean} cache
* @param {"LocalStorage"|"CacheStorage"|"IndexedDB"} cacheType
* @param {"justCache"|"cacheFirstThenUpdate"} strategy - Caching strategy.
* @returns {Promise<AxiosResponse>}
*/
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);
}

/**
Expand Down
70 changes: 70 additions & 0 deletions src/utils/cache/cacheManager.js
Original file line number Diff line number Diff line change
@@ -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<any>} 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<void>}
*/
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<IDBPDatabase>}
* @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;
Loading