Skip to content
Permalink
Browse files

Merge 0ef1ab4 into b2adea0

  • Loading branch information...
petercrona committed Apr 29, 2017
2 parents b2adea0 + 0ef1ab4 commit 3d665fa667cc08828609a694c33abe631b44c8a2

This file was deleted.

@@ -0,0 +1,47 @@
import {curry} from 'ladda-fp';
import * as QueryCache from './query-cache';
import * as EntityStore from './entity-store';

export const createCache = (entityConfigs, onChange) => {
const entityStore = EntityStore.createEntityStore(entityConfigs, onChange);
const queryCache = QueryCache.createQueryCache(entityStore, onChange);
return {entityStore, queryCache};
};

export const storeQueryResponse = ({queryCache}, ...args) => {
return QueryCache.put(queryCache, ...args);
};

export const getQueryResponse = QueryCache.getValue;

export const getQueryResponseWithMeta = ({queryCache}, ...args) => {
return QueryCache.get(queryCache, ...args);
};

export const containsQueryResponse = ({queryCache}, ...args) => {
return QueryCache.contains(queryCache, ...args);
};

export const invalidateQuery = ({queryCache}, ...args) => {
return QueryCache.invalidate(queryCache, ...args);
};

export const storeEntity = curry(({entityStore}, ...args) => {
return EntityStore.put(entityStore, ...args);
});

export const storeEntities = ({entityStore}, ...args) => {
return EntityStore.mPut(entityStore, ...args);
};

export const getEntity = curry(({entityStore}, ...args) => {
return EntityStore.get(entityStore, ...args);
});

export const removeEntity = ({entityStore}, ...args) => {
return EntityStore.remove(entityStore, ...args);
};

export const containsEntity = ({entityStore}, ...args) => {
return EntityStore.contains(entityStore, ...args);
};
@@ -1,6 +1,5 @@
import {compose, values} from 'ladda-fp';
import {createEntityStore} from './entity-store';
import {createQueryCache} from './query-cache';
import {values} from 'ladda-fp';
import {createCache} from './cache';
import {decorateCreate} from './operations/create';
import {decorateRead} from './operations/read';
import {decorateUpdate} from './operations/update';
@@ -16,10 +15,9 @@ const HANDLERS = {
};

export const cachePlugin = (onChange) => ({ config, entityConfigs }) => {
const entityStore = compose((c) => createEntityStore(c, onChange), values)(entityConfigs);
const queryCache = createQueryCache(entityStore, onChange);
const cache = createCache(values(entityConfigs), onChange);
return ({ entity, fn }) => {
const handler = HANDLERS[fn.operation];
return handler(config, entityStore, queryCache, entity, fn);
return handler(config, cache, entity, fn);
};
};

This file was deleted.

@@ -1,12 +1,11 @@
import {passThrough, compose} from 'ladda-fp';
import {put} from '../entity-store';
import {invalidate} from '../query-cache';
import {storeEntity, invalidateQuery} from '../cache';
import {addId} from '../id-helper';

export function decorateCreate(c, es, qc, e, aFn) {
export function decorateCreate(c, cache, e, aFn) {
return (...args) => {
return aFn(...args)
.then(passThrough(() => invalidate(qc, e, aFn)))
.then(passThrough(compose(put(es, e), addId(c, aFn, args))));
.then(passThrough(() => invalidateQuery(cache, e, aFn)))
.then(passThrough(compose(storeEntity(cache, e), addId(c, aFn, args))));
};
}
@@ -1,7 +1,6 @@
import sinon from 'sinon';
import {decorateCreate} from './create';
import {createEntityStore, get} from '../entity-store';
import {createQueryCache} from '../query-cache';
import {createCache, getEntity} from '../cache';
import {createApiFunction} from '../test-helper';

const config = [
@@ -41,17 +40,16 @@ const config = [
describe('Create', () => {
describe('decorateCreate', () => {
it('Adds value to entity store', (done) => {
const es = createEntityStore(config);
const qc = createQueryCache(es);
const cache = createCache(config);
const e = config[0];
const xOrg = {name: 'Kalle'};
const response = {...xOrg, id: 1};
const aFnWithoutSpy = createApiFunction(() => Promise.resolve(response));
const aFn = sinon.spy(aFnWithoutSpy);
const res = decorateCreate({}, es, qc, e, aFn);
const res = decorateCreate({}, cache, e, aFn);
res(xOrg).then((newX) => {
expect(newX).to.equal(response);
expect(get(es, e, 1).value).to.deep.equal({...response, __ladda__id: 1});
expect(getEntity(cache, e, 1).value).to.deep.equal({...response, __ladda__id: 1});
done();
});
});
@@ -1,12 +1,11 @@
import {passThrough} from 'ladda-fp';
import {remove} from '../entity-store';
import {invalidate} from '../query-cache';
import * as Cache from '../cache';
import {serialize} from '../serializer';

export function decorateDelete(c, es, qc, e, aFn) {
export function decorateDelete(c, cache, e, aFn) {
return (...args) => {
return aFn(...args)
.then(passThrough(() => invalidate(qc, e, aFn)))
.then(() => remove(es, e, serialize(args)));
.then(passThrough(() => Cache.invalidateQuery(cache, e, aFn)))
.then(() => Cache.removeEntity(cache, e, serialize(args)));
};
}
@@ -1,7 +1,6 @@
import sinon from 'sinon';
import {decorateDelete} from './delete';
import {createEntityStore, get, put} from '../entity-store';
import {createQueryCache} from '../query-cache';
import * as Cache from '../cache';
import {addId} from '../id-helper';
import {createApiFunction} from '../test-helper';

@@ -42,16 +41,15 @@ const config = [
describe('Delete', () => {
describe('decorateDelete', () => {
it('Removes cache', (done) => {
const es = createEntityStore(config);
const qc = createQueryCache(es);
const cache = Cache.createCache(config);
const e = config[0];
const xOrg = {id: 1, name: 'Kalle'};
const aFnWithoutSpy = createApiFunction(() => Promise.resolve({}));
const aFn = sinon.spy(aFnWithoutSpy);
put(es, e, addId({}, undefined, undefined, xOrg));
const res = decorateDelete({}, es, qc, e, aFn);
Cache.storeEntity(cache, e, addId({}, undefined, undefined, xOrg));
const res = decorateDelete({}, cache, e, aFn);
res(1).then(() => {
expect(get(es, e, 1)).to.equal(undefined);
expect(Cache.getEntity(cache, e, 1)).to.equal(undefined);
done();
});
});
@@ -1,9 +1,9 @@
import {passThrough} from 'ladda-fp';
import {invalidate} from '../query-cache';
import {invalidateQuery} from '../cache';

export function decorateNoOperation(c, es, qc, e, aFn) {
export function decorateNoOperation(c, cache, e, aFn) {
return (...args) => {
return aFn(...args)
.then(passThrough(() => invalidate(qc, e, aFn)));
.then(passThrough(() => invalidateQuery(cache, e, aFn)));
};
}
@@ -2,52 +2,48 @@

import sinon from 'sinon';
import {decorateNoOperation} from './no-operation';
import {createEntityStore} from '../entity-store';
import {createQueryCache, contains, put} from '../query-cache';
import * as Cache from '../cache';
import {createSampleConfig, createApiFunction} from '../test-helper';

const config = createSampleConfig();

describe('DecorateNoOperation', () => {
it('Invalidates based on what is specified in the original function', (done) => {
const es = createEntityStore(config);
const qc = createQueryCache(es);
const cache = Cache.createCache(config);
const e = config[0];
const xOrg = {__ladda__id: 1, name: 'Kalle'};
const aFn = sinon.spy(() => Promise.resolve({}));
const getUsers = () => Promise.resolve(xOrg);
aFn.invalidates = ['getUsers'];
put(qc, e, getUsers, ['args'], xOrg);
const res = decorateNoOperation({}, es, qc, e, aFn);
Cache.storeQueryResponse(cache, e, getUsers, ['args'], xOrg);
const res = decorateNoOperation({}, cache, e, aFn);
res(xOrg).then(() => {
const killedCache = !contains(qc, e, getUsers, ['args']);
const killedCache = !Cache.containsQueryResponse(cache, e, getUsers, ['args']);
expect(killedCache).to.be.true;
done();
});
});
it('Does not change original function', () => {
const es = createEntityStore(config);
const qc = createQueryCache(es);
const cache = Cache.createCache(config);
const e = config[0];
const aFn = sinon.spy(() => {
return Promise.resolve({});
});
decorateNoOperation({}, es, qc, e, aFn);
decorateNoOperation({}, cache, e, aFn);
expect(aFn.operation).to.be.undefined;
});
it('Ignored inherited invalidation config', (done) => {
const es = createEntityStore(config);
const qc = createQueryCache(es);
const cache = Cache.createCache(config);
const e = config[0];
const xOrg = {__ladda__id: 1, name: 'Kalle'};
const aFnWithoutSpy = createApiFunction(() => Promise.resolve({}), {invalidates: ['user']});
const aFn = sinon.spy(aFnWithoutSpy);
const getUsers = createApiFunction(() => Promise.resolve(xOrg));
aFn.hasOwnProperty = () => false;
put(qc, e, getUsers, ['args'], xOrg);
const res = decorateNoOperation({}, es, qc, e, aFn);
Cache.storeQueryResponse(cache, e, getUsers, ['args'], xOrg);
const res = decorateNoOperation({}, cache, e, aFn);
res(xOrg).then(() => {
const killedCache = !contains(qc, e, getUsers, ['args']);
const killedCache = !Cache.containsQueryResponse(cache, e, getUsers, ['args']);
expect(killedCache).to.be.false;
done();
});
@@ -1,13 +1,5 @@
import {passThrough, compose, curry, reduce, toIdMap, map, concat, zip} from 'ladda-fp';
import {get as getFromEs,
put as putInEs,
mPut as mPutInEs,
contains as inEs} from '../entity-store';
import {get as getFromQc,
invalidate,
put as putInQc,
contains as inQc,
getValue} from '../query-cache';
import * as Cache from '../cache';
import {addId, removeId} from '../id-helper';

const getTtl = e => e.ttl * 1000;
@@ -17,32 +9,32 @@ const hasExpired = (e, timestamp) => {
return (Date.now() - timestamp) > getTtl(e);
};

const readFromCache = curry((es, e, aFn, id) => {
if (inEs(es, e, id) && !aFn.alwaysGetFreshData) {
const v = getFromEs(es, e, id);
const readFromCache = curry((cache, e, aFn, id) => {
if (Cache.containsEntity(cache, e, id) && !aFn.alwaysGetFreshData) {
const v = Cache.getEntity(cache, e, id);
if (!hasExpired(e, v.timestamp)) {
return removeId(v.value);
}
}
return undefined;
});

const decorateReadSingle = (c, es, qc, e, aFn) => {
const decorateReadSingle = (c, cache, e, aFn) => {
return (id) => {
const fromCache = readFromCache(es, e, aFn, id);
const fromCache = readFromCache(cache, e, aFn, id);
if (fromCache) {
return Promise.resolve(fromCache);
}

return aFn(id)
.then(passThrough(compose(putInEs(es, e), addId(c, aFn, id))))
.then(passThrough(() => invalidate(qc, e, aFn)));
.then(passThrough(compose(Cache.storeEntity(cache, e), addId(c, aFn, id))))
.then(passThrough(() => Cache.invalidateQuery(cache, e, aFn)));
};
};

const decorateReadSome = (c, es, qc, e, aFn) => {
const decorateReadSome = (c, cache, e, aFn) => {
return (ids) => {
const readFromCache_ = readFromCache(es, e, aFn);
const readFromCache_ = readFromCache(cache, e, aFn);
const [cached, remaining] = reduce(([c_, r], id) => {
const fromCache = readFromCache_(id);
if (fromCache) {
@@ -60,36 +52,38 @@ const decorateReadSome = (c, es, qc, e, aFn) => {
const addIds = map(([id, item]) => addId(c, aFn, id, item));

return aFn(remaining)
.then(passThrough(compose(mPutInEs(es, e), addIds, zip(remaining))))
.then(passThrough(() => invalidate(qc, e, aFn)))
.then(passThrough(compose(Cache.storeEntities(cache, e), addIds, zip(remaining))))
.then(passThrough(() => Cache.invalidateQuery(cache, e, aFn)))
.then((other) => {
const asMap = compose(toIdMap, concat)(cached, other);
return map((id) => asMap[id], ids);
});
};
};

const decorateReadQuery = (c, es, qc, e, aFn) => {
const decorateReadQuery = (c, cache, e, aFn) => {
return (...args) => {
if (inQc(qc, e, aFn, args) && !aFn.alwaysGetFreshData) {
const v = getFromQc(qc, e, aFn, args);
if (Cache.containsQueryResponse(cache, e, aFn, args) && !aFn.alwaysGetFreshData) {
const v = Cache.getQueryResponseWithMeta(cache, e, aFn, args);
if (!hasExpired(e, v.timestamp)) {
return Promise.resolve(removeId(getValue(v.value)));
return Promise.resolve(removeId(Cache.getQueryResponse(v.value)));
}
}

return aFn(...args)
.then(passThrough(compose(putInQc(qc, e, aFn, args), addId(c, aFn, args))))
.then(passThrough(() => invalidate(qc, e, aFn)));
.then(passThrough(
compose(Cache.storeQueryResponse(cache, e, aFn, args),
addId(c, aFn, args))))
.then(passThrough(() => Cache.invalidateQuery(cache, e, aFn)));
};
};

export function decorateRead(c, es, qc, e, aFn) {
export function decorateRead(c, cache, e, aFn) {
if (aFn.byId) {
return decorateReadSingle(c, es, qc, e, aFn);
return decorateReadSingle(c, cache, e, aFn);
}
if (aFn.byIds) {
return decorateReadSome(c, es, qc, e, aFn);
return decorateReadSome(c, cache, e, aFn);
}
return decorateReadQuery(c, es, qc, e, aFn);
return decorateReadQuery(c, cache, e, aFn);
}

0 comments on commit 3d665fa

Please sign in to comment.
You can’t perform that action at this time.