-
Notifications
You must be signed in to change notification settings - Fork 1.8k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
91a069b
commit 6222886
Showing
11 changed files
with
181 additions
and
72 deletions.
There are no files selected for viewing
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
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
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
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
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
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,130 @@ | ||
/* eslint-disable no-console, prefer-rest-params */ | ||
import Service, { inject as service } from '@ember/service'; | ||
import DS from 'ember-data'; | ||
|
||
function pushToStore(store: DS.Store, data: any): any[] | any { | ||
const parsed = data?.value; | ||
if (Array.isArray(parsed)) { | ||
const items = [] | ||
for (const item of parsed) { | ||
store.pushPayload(item); | ||
items.push(store.peekRecord(item.data.type, item.data.id)); | ||
} | ||
return items; | ||
} else { | ||
store.pushPayload(parsed); | ||
|
||
return store.peekRecord(parsed.data.type, parsed.data.id); | ||
} | ||
} | ||
|
||
function saveToStorage(key: string, value: any | null) { | ||
if (!value) {return} | ||
let serialized = null; | ||
if (Array.isArray(value.content)) { | ||
serialized = value.map((v: any) => v.serialize({ includeId: true })); | ||
} else { | ||
serialized = value.serialize({ includeId: true }); | ||
} | ||
|
||
localStorage.setItem(key, JSON.stringify({ | ||
time : Date.now(), | ||
value : serialized | ||
})); | ||
} | ||
|
||
export default class Cache extends Service.extend({ | ||
// anything which *must* be merged to prototype here | ||
}) { | ||
version = 'v1'; | ||
|
||
@service store!: DS.Store; | ||
|
||
get prefix(): string { | ||
return 'cache:' + this.version + ':'; | ||
} | ||
|
||
isExpired(data: { time: number, value: any} | null): boolean { | ||
// Item expired after 15 seconds | ||
return Boolean(data?.time && (Date.now() - data?.time) > 60 * 1000) | ||
} | ||
|
||
async passThrough(key: string, callable: () => any): Promise<any> { | ||
const value = await callable(); | ||
saveToStorage(key, value); | ||
|
||
return value; | ||
} | ||
|
||
async cacheData(key: string, callable: () => any): Promise<any | null> { | ||
key = this.prefix + key; | ||
const stored = localStorage.getItem(key); | ||
try { | ||
if (stored) { | ||
const data = JSON.parse(stored); | ||
|
||
if (!data.time) { | ||
// Invalid data structure | ||
return this.passThrough(key, callable); | ||
} | ||
|
||
const expired = this.isExpired(data); | ||
const item = pushToStore(this.store, data); | ||
|
||
if (expired) { | ||
// Revalidate resource while serving stale | ||
console.info('Item expired. Revalidating...', key); | ||
this.passThrough(key, callable); | ||
} | ||
|
||
return item; | ||
} else { | ||
return this.passThrough(key, callable); | ||
} | ||
} catch (e) { | ||
console.error('Error while loading value from cache using key: ' + key, e); | ||
|
||
return callable(); | ||
} | ||
} | ||
|
||
async findAll(model: string, options: any | null): Promise<any> { | ||
const saved = await this.cacheData(model, () => this.store.findAll(model, options)); | ||
if (saved) {return saved;} | ||
return this.store.peekAll(model); | ||
} | ||
|
||
async queryRecord(key: string, model: string, options: any | null): Promise<any> { | ||
const saved = await this.cacheData(key, () => this.store.queryRecord(model, options)); | ||
if (saved) {return saved;} | ||
return this.store.peekRecord(model, 1); | ||
} | ||
|
||
clear(): void { | ||
for (const key of Object.keys(localStorage)) { | ||
if (key.startsWith(this.prefix)) { | ||
console.info('Clearing cache entry:', key); | ||
localStorage.removeItem(key); | ||
} | ||
} | ||
} | ||
|
||
constructor() { | ||
super(...arguments); | ||
for (const key of Object.keys(localStorage)) { | ||
if (key.startsWith('cache:')) { | ||
if (!key.startsWith(this.prefix)) { | ||
console.info('Removing previous cache entry:', key); | ||
localStorage.removeItem(key); | ||
} | ||
} | ||
} | ||
} | ||
} | ||
|
||
// DO NOT DELETE: this is how TypeScript knows how to look up your services. | ||
declare module '@ember/service' { | ||
interface Registry { | ||
'cache': Cache; | ||
} | ||
} |
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
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
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
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1003,4 +1003,4 @@ export const countries = [ | |
name : 'Zimbabwe', | ||
code : 'ZW' | ||
} | ||
]; | ||
]; |
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
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
import { module, test } from 'qunit'; | ||
import { setupTest } from 'ember-qunit'; | ||
|
||
module('Unit | Service | cache', function(hooks) { | ||
setupTest(hooks); | ||
|
||
// Replace this with your real tests. | ||
test('it exists', function(assert) { | ||
const service = this.owner.lookup('service:cache'); | ||
assert.ok(service); | ||
}); | ||
}); | ||
|