-
-
Save culttm/d4746510c4ba3573e9b4f232b950aa09 to your computer and use it in GitHub Desktop.
#vuejs : vue-resource cache plugin with PouchDB
This file contains 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
/** | |
* 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