Skip to content

Instantly share code, notes, and snippets.

@culttm
Forked from Rebolon/interceptors.js
Created September 11, 2017 06:27
Show Gist options
  • Save culttm/d4746510c4ba3573e9b4f232b950aa09 to your computer and use it in GitHub Desktop.
Save culttm/d4746510c4ba3573e9b4f232b950aa09 to your computer and use it in GitHub Desktop.
#vuejs : vue-resource cache plugin with PouchDB
/**
* allow to cache response and/or to listen on newRequest event
*
* for cache system, it's freely adapted from https://github.com/pagekit/vue-resource/issues/252 @airtonix sample
*/
export class HttpInterceptors {
_cache
constructor (Vue, ttlInHours) {
if (Vue.http === undefined) {
throw new Error('you have to add the vue-resource plugin')
}
if (!Vue.DI || !Vue.DI.Bus) {
throw new Error('you have to add the Vue.DI.Bus system')
}
this.vueJS = Vue
this.ttlInHours = ttlInHours
}
/**
*
* @param IBus {$emit: function}
* @returns {HttpInterceptors}
*/
addNewRequestListener (bus) {
if (typeof bus.$emit !== 'function') {
throw new Error('bus must have an $emit method')
}
this.vueJS.http.interceptors.push((request, next) => {
bus.$emit('newRequest', request)
// continue to next interceptor
next()
})
return this
}
/**
* @params ICache {get: function, put: function, remove: function}
* @returns {HttpInterceptors}
*/
addHttpCache (cache) {
['get', 'put', 'remove'].forEach(method => {
if (typeof cache[method] !== 'function') {
throw new Error(`cache must have an ${method} method`)
}
})
// this is to be able to test the doCache method lonely
this._cache = cache
this.vueJS.http.interceptors.push(this._doCache.bind(this))
return this
}
/**
*
* @param request
* @param next
* @private
*/
_doCache (request, next) {
if (!this._cache) {
throw new Error(`you forgot the cache system`)
}
let id = this._getId(request)
if (request.method.toLowerCase() === 'get') {
this._cache.get(this._getId(request))
.then(doc => {
if (this._checkTTL(doc)) {
console.log('cache hit', id)
next(request.respondWith(doc.body, {status: 200, statusText: 'Ok'}))
} else {
console.log(doc.ttl, this._getTTL(), this._checkTTL(doc))
this._cache.remove(doc)
throw Object.create({code: 1000})
}
})
.catch(err => {
if (err.status === 404) {
console.info('cache miss, not in db (catch)', id)
next((response) => {
let {status, statusText, body} = response
if (status === 200 && request.method.toLowerCase() === 'get') {
response._id = id
response.ttl = this._getTTL()
// if Conflict we have to get the current object, merge the response with the object Object.assign(docFound, response) and then put it in db
this._cache
.put(response)
.catch(err => {
if (err.status === 409) {
console.warn('Conflict error in insert')
return
}
console.error('error during put db', err, response)
})
}
request.respondWith(body, {status, statusText})
})
return
} else if (err.code === 1000) {
console.info('cache miss, ttl delayed (catch)', id)
next()
return
}
console.error('error during get db', err)
})
} else {
next()
}
}
/**
*
* @param request
* @returns {string}
*/
_getId (request) {
let qs = []
Object.keys(request.params).forEach(key => {
if (key === 'apikey') return
qs.push(`${key}=${request.params[key]}`)
})
if (qs.length) {
qs = `?${qs.join('&')}`
}
const id = `CACHE_${request.url}${qs}`
return id
}
/**
*
*/
_getTTL () {
const now = new Date()
return now.setHours(now.getHours() + this.ttlInHours)
}
/**
*
* @param data
* @returns {boolean}
*/
_checkTTL (data) {
return data.ttl > new Date()
}
}
/**
* to use it with a 60 minutes of TTL on each http call:
*
* import Vue from 'vue'
* import PouchDB from 'pouchdb'
* import { HttpInterceptors } from './interceptors'
* const VueResource = require('vue-resource')
*
* Vue.use(VueResource)
* const interceptors = new HttpInterceptors(Vue, 1)
* interceptors
* .addNewRequestListener(new Vue)
* .addHttpCache(new PouchDB('your-channel'))
*/
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment