Last active
July 1, 2021 11:46
-
-
Save kibolho/17d6c0013d299532654ff128e2c2de18 to your computer and use it in GitHub Desktop.
Cache Handler
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // TO USE: | |
| // Add a interceptor to the request: | |
| // api.interceptors.request.use((request) => cacheHandler.requestHandler(request)); | |
| // api.interceptors.response.use( | |
| // (response) => cacheHandler.responseHandler(response), | |
| // (error) => cacheHandler.errorHandler(error), | |
| // ); | |
| import { AxiosRequestConfig, AxiosResponse } from 'axios'; | |
| import Storage from './Storage'; | |
| import checkResponseErrors from './errors/checkForResponseErrors'; | |
| import normalizeApiResponse from './normalizeApiResponse'; | |
| interface GetValidCacheResponse { | |
| isValid: boolean; | |
| value?: string; | |
| cacheDate?: Date; | |
| } | |
| export interface ResquestCache { | |
| isIgnoreCache?: boolean; | |
| } | |
| const SEPARATOR = '//**//'; | |
| const CACHE_INTERVAL = 60 * 60 * 1000; | |
| const storage = new Storage('cacheStorage'); | |
| async function storeInCache( | |
| key: string, | |
| value: string, | |
| timestamp: string, | |
| ): Promise<void> { | |
| const finalValue = `${value}${SEPARATOR}${timestamp}`; | |
| await storage.set(key, finalValue); | |
| }; | |
| async function getValidCache(key: string): Promise<GetValidCacheResponse> { | |
| const value = await storage.get(key); | |
| if (value === null) { | |
| return { | |
| isValid: false, | |
| }; | |
| } | |
| const values = value.split(SEPARATOR); | |
| const timestamp = Number(values[1]); | |
| if (Number.isNaN(timestamp)) { | |
| return { | |
| isValid: false, | |
| }; | |
| } | |
| const date = new Date(timestamp); | |
| if (date.toString() === 'Invalid Date') { | |
| return { | |
| isValid: false, | |
| }; | |
| } | |
| if (Date.now() - date.getTime() < CACHE_INTERVAL) { | |
| return { | |
| isValid: true, | |
| value: values[0], | |
| cacheDate: date, | |
| }; | |
| } | |
| await storage.del(key); | |
| return { | |
| isValid: false, | |
| }; | |
| } | |
| async function responseHandler( | |
| response: AxiosResponse<any>, | |
| ): Promise<AxiosResponse<any>> { | |
| const responseChecked = await checkResponseErrors(response); | |
| const responseNormalized = await normalizeApiResponse(responseChecked); | |
| if (response.config.method === 'GET' || 'get') { | |
| const key = getKey({ response: response }); | |
| if (key) { | |
| const timestamp = Date.now().toString(); | |
| const value = JSON.stringify(responseNormalized.data); | |
| await storeInCache(key, value, timestamp); | |
| const cacheDate = new Date(Number(timestamp)); | |
| responseNormalized.cacheDate = cacheDate; | |
| } | |
| } | |
| return { ...response, ...responseNormalized }; | |
| } | |
| function getKey({ | |
| response, | |
| request, | |
| }: { | |
| response?: AxiosResponse<any>; | |
| request?: AxiosRequestConfig; | |
| }): string | undefined { | |
| let key = request?.baseURL ? request?.baseURL + request?.url : request?.url; | |
| let data = request?.data; | |
| if (response) { | |
| key = response.config.baseURL | |
| ? response.config.baseURL + response.config.url | |
| : response.config.url; | |
| data = response?.config.data; | |
| } | |
| if (data) { | |
| key += JSON.stringify(data); | |
| } | |
| return key; | |
| } | |
| async function errorHandler(error: any): Promise<any> { | |
| return new Promise((resolve, reject) => { | |
| if (error?.headers?.cached === true) { | |
| resolve(error); | |
| } | |
| reject(error); | |
| }); | |
| } | |
| async function requestHandler( | |
| request: AxiosRequestConfig, | |
| ): Promise<AxiosRequestConfig> { | |
| if (request.method === 'GET' || 'get') { | |
| const checkIsValidResponse = await getValidCache(getKey({ request }) || ''); | |
| if (checkIsValidResponse.isValid && !request?.headers?.isIgnoreCache) { | |
| request.headers.cached = true; | |
| request.cacheDate = checkIsValidResponse.cacheDate; | |
| request.data = JSON.parse(checkIsValidResponse.value || '{}'); | |
| return Promise.reject(request); | |
| } | |
| } | |
| return Promise.resolve(request); | |
| } | |
| const cache = { | |
| responseHandler, | |
| errorHandler, | |
| requestHandler, | |
| }; | |
| export default cache; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import AsyncStorage from '@react-native-community/async-storage'; | |
| export default class Storage { | |
| private name: string; | |
| constructor(name: string) { | |
| this.name = name; | |
| } | |
| async set( | |
| key: string, | |
| value: string | Date | Record<string, unknown>, | |
| ): Promise<any> { | |
| return await AsyncStorage.setItem( | |
| `${this.name}-${key}`, | |
| JSON.stringify(value), | |
| ); | |
| } | |
| async get(key: string): Promise<any> { | |
| const value = await AsyncStorage.getItem(`${this.name}-${key}`); | |
| if (value) { | |
| return JSON.parse(value); | |
| } | |
| return null; | |
| } | |
| async del(key: string): Promise<any> { | |
| return await AsyncStorage.removeItem(`${this.name}-${key}`); | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment