Source

api/api-cache.js

/**
 * Client side cache for API requests (get/post)
 * @namespace api
 * @category api
 * @module api-wrapper-cache
 * @requires vue-local-storage.js
 * */
import storage from '@/plugins/vue-local-storage.js'
import store from '@/store'
import { getQueryStringValue } from '@/utils/querystring'
const shouldLog =
  (getQueryStringValue('verbose') || '').includes('1') ||
  (getQueryStringValue('verbose') || '').includes('api')
const apiStorage = storage.fromNamespace('api')

export const apiCacheStorage = apiStorage

/**
 * Tries to grab JWT from cache (two tries with 1s difference)
 * @returns
 */
export function getCachedJWT(url) {
  return new Promise(async (resolve, reject) => {
    let tries = 0
    let start = Date.now()
    tryGrabJWT()
    async function tryGrabJWT() {
      let userInfos = (await apiStorage.localforage.getItem('user_infos')) || {}
      let jwt =
        userInfos.toClientToken ||
        userInfos.token ||
        store.state.auth.user_infos.toClientToken ||
        store.state.auth.user_infos.token ||
        null
      if (jwt) {
        resolve(jwt)
      } else {
        if (tries >= 5) {
          console.warn(`api-cache: fail to get JWT (${url})`)
          resolve(null)
        } else {
          //Hot-fix: Abort if logout occurs during the try period
          if (window._logoutAt && window._logoutAt > start) {
            reject(
              new Error('LOGOUT_DETECTED_WHILE_RETRIEVING_JWT_FOR_API_REQUEST')
            )
            return
          }

          tries = tries + 1
          console.warn(`api-cache: Waiting for JWT (${url})...`, tries)
          setTimeout(() => tryGrabJWT(), 1000)
        }
      }
    }
  })
}

/**
 * Will cache API response per URL+JWT if a cache time was specified for that URL (cache-times.json)
 *
 * @param {*} res
 * @todo Cache also null/empty responses? (History APIs)
 */
export async function setCacheFromAPIResponse(url, res, options = {}) {
  if (apiStorage.cache && !!res && apiStorage.cache.shouldCache(url)) {
    let jwt =
      options.jwt || (await getCachedJWT(url + ' (setCacheFromAPIResponse)'))
    let isInvalidResponse =
      !res.data ||
      res.data.error !== undefined ||
      res.data === null ||
      (res.data instanceof Array && res.data.length === 0)
    if (jwt && !isInvalidResponse) {
      await apiStorage.cache.set(`${url}__uniq__${jwt}`, {
        data: res.data,
        status: res.status,
        apiHeaders: res.apiHeaders,
      })
      shouldLog && console.log('setCacheFromAPIResponse:cache:set(done)')
    } else {
      shouldLog &&
        console.log(
          'setCacheFromAPIResponse:cache:set(skip-invalid-response)',
          {
            res,
            url,
          }
        )
    }
  } else {
    shouldLog &&
      console.log('setCacheFromAPIResponse:cache:set(skip)', {
        url,
        hasRes: !!res,
        cacheEnabled: !!apiStorage.cache,
      })
  }
}

/**
 * Will retrieve a cached API response per URL+JWT if available (not expired)
 * @param {*} url
 * @param {Function} options.beforeCacheFetch Will skip cache if false (Boolean)
 * @return {Object}
 */
export async function getCacheFromAPIRequestURL(
  url,
  options = {},
  ajaxOptions = {}
) {
  if (apiStorage.cache && apiStorage.cache.shouldCache(url)) {
    if (options.beforeCacheFetch && options.beforeCacheFetch(url) == false) {
      return null
    }

    let jwt =
      ajaxOptions.jwt ||
      (await getCachedJWT(url + ' (getCacheFromAPIRequestURL)'))
    if (jwt) {
      let cached = await apiStorage.cache.get(`${url}__uniq__${jwt}`)
      if (cached) {
        return cached
      }
    }
  }
  return null
}