Skip to content

Commit

Permalink
refactor(dedup) Update to simplified plugin api
Browse files Browse the repository at this point in the history
  • Loading branch information
LFDM committed Mar 16, 2017
1 parent 0256572 commit b0255c3
Show file tree
Hide file tree
Showing 2 changed files with 51 additions and 51 deletions.
10 changes: 5 additions & 5 deletions src/dedup/index.js
Expand Up @@ -4,20 +4,20 @@ const toKey = (args) => JSON.stringify(args);

const isActive = reduce((active, conf = {}) => active && !conf.noDedup, true);

export const dedup = ({ config }) => ({ entity, apiFn }) => {
if (apiFn.operation !== 'READ') { return apiFn; }
export const dedup = ({ config }) => ({ entity, fn }) => {
if (fn.operation !== 'READ') { return fn; }
const cache = {};

return (...args) => {
if (!isActive([config, entity, apiFn])) {
return apiFn(...args);
if (!isActive([config, entity, fn])) {
return fn(...args);
}

const key = toKey(args);
const cached = cache[key];
if (cached) { return cached; }

const promise = apiFn(...args);
const promise = fn(...args);
cache[key] = promise;
const cleanup = () => delete cache[key];

Expand Down
92 changes: 46 additions & 46 deletions src/dedup/index.spec.js
Expand Up @@ -11,66 +11,66 @@ describe('dedup', () => {
describe('for operations other than READ', () => {
it('just returns the original apiFn', () => {
const user = { id: 'x' };
const apiFn = sinon.spy(() => Promise.resolve({ ...user }));
apiFn.operation = 'UPDATE';
const wrappedApiFn = dedup({})({ apiFn });
expect(wrappedApiFn).to.equal(apiFn);
const fn = sinon.spy(() => Promise.resolve({ ...user }));
fn.operation = 'UPDATE';
const wrappedApiFn = dedup({})({ fn });
expect(wrappedApiFn).to.equal(fn);
});
});

describe('for READ operations', () => {
it('wraps the function', () => {
const user = { id: 'x' };
const apiFn = sinon.spy(() => Promise.resolve(user));
apiFn.operation = 'READ';
const wrappedApiFn = dedup({})({ apiFn });
expect(wrappedApiFn).not.to.equal(apiFn);
const fn = sinon.spy(() => Promise.resolve(user));
fn.operation = 'READ';
const wrappedApiFn = dedup({})({ fn });
expect(wrappedApiFn).not.to.equal(fn);
});

it('makes several calls when apiFn is called with different args', () => {
const user = { id: 'x' };
const apiFn = sinon.spy(() => Promise.resolve({ ...user }));
apiFn.operation = 'READ';
const wrappedApiFn = dedup({})({ apiFn });
const fn = sinon.spy(() => Promise.resolve({ ...user }));
fn.operation = 'READ';
const wrappedApiFn = dedup({})({ fn });
return Promise.all([
wrappedApiFn('x'),
wrappedApiFn('y')
]).then(() => {
expect(apiFn).to.have.been.calledTwice;
expect(fn).to.have.been.calledTwice;
});
});

it('only makes one call when apiFn is called in identical args', () => {
const user = { id: 'x' };
const apiFn = sinon.spy(() => delay(() => Promise.resolve({ ...user })));
apiFn.operation = 'READ';
const wrappedApiFn = dedup({})({ apiFn });
const fn = sinon.spy(() => delay(() => Promise.resolve({ ...user })));
fn.operation = 'READ';
const wrappedApiFn = dedup({})({ fn });
return Promise.all([
wrappedApiFn('x'),
wrappedApiFn('x')
]).then(() => {
expect(apiFn).to.have.been.calledOnce;
expect(fn).to.have.been.calledOnce;
});
});

it('detects complex arguments properly', () => {
const user = { id: 'x' };
const apiFn = sinon.spy(() => delay(() => Promise.resolve({ ...user })));
apiFn.operation = 'READ';
const wrappedApiFn = dedup({})({ apiFn });
const fn = sinon.spy(() => delay(() => Promise.resolve({ ...user })));
fn.operation = 'READ';
const wrappedApiFn = dedup({})({ fn });
return Promise.all([
wrappedApiFn({ a: 1, b: [2, 3] }, 'a'),
wrappedApiFn({ a: 1, b: [2, 3] }, 'a')
]).then(() => {
expect(apiFn).to.have.been.calledOnce;
expect(fn).to.have.been.calledOnce;
});
});

it('passes the result of the single call to all callees', () => {
const user = { id: 'x' };
const apiFn = sinon.spy(() => delay(() => Promise.resolve({ ...user })));
apiFn.operation = 'READ';
const wrappedApiFn = dedup({})({ apiFn });
const fn = sinon.spy(() => delay(() => Promise.resolve({ ...user })));
fn.operation = 'READ';
const wrappedApiFn = dedup({})({ fn });
return Promise.all([
wrappedApiFn(),
wrappedApiFn()
Expand All @@ -83,34 +83,34 @@ describe('dedup', () => {

it('makes subsequent calls if another calls is made after the first one is resolved', () => {
const user = { id: 'x' };
const apiFn = sinon.spy(() => delay(() => Promise.resolve({ ...user })));
apiFn.operation = 'READ';
const wrappedApiFn = dedup({})({ apiFn });
const fn = sinon.spy(() => delay(() => Promise.resolve({ ...user })));
fn.operation = 'READ';
const wrappedApiFn = dedup({})({ fn });

return wrappedApiFn().then(() => {
return wrappedApiFn().then(() => {
expect(apiFn).to.have.been.calledTwice;
expect(fn).to.have.been.calledTwice;
});
});
});

it('also makes subsequent calls after the first one is rejected', () => {
const user = { id: 'x' };
const apiFn = sinon.spy(() => delay(() => Promise.reject({ ...user })));
apiFn.operation = 'READ';
const wrappedApiFn = dedup({})({ apiFn });
const fn = sinon.spy(() => delay(() => Promise.reject({ ...user })));
fn.operation = 'READ';
const wrappedApiFn = dedup({})({ fn });

return wrappedApiFn().catch(() => {
return wrappedApiFn().catch(() => {
expect(apiFn).to.have.been.calledTwice;
expect(fn).to.have.been.calledTwice;
});
});
});

it('propagates errors to all callees', () => {
const error = { error: 'ERROR' };
const apiFn = sinon.spy(() => delay(() => Promise.reject(error)));
const wrappedApiFn = dedup({})({ apiFn });
const fn = sinon.spy(() => delay(() => Promise.reject(error)));
const wrappedApiFn = dedup({})({ fn });
return Promise.all([
wrappedApiFn().catch((err) => err),
wrappedApiFn().catch((err) => err)
Expand All @@ -122,46 +122,46 @@ describe('dedup', () => {

it('can be disabled on a global level', () => {
const user = { id: 'x' };
const apiFn = sinon.spy(() => delay(() => Promise.resolve({ ...user })));
apiFn.operation = 'READ';
const fn = sinon.spy(() => delay(() => Promise.resolve({ ...user })));
fn.operation = 'READ';
const config = { noDedup: true };
const wrappedApiFn = dedup({ config })({ apiFn });
const wrappedApiFn = dedup({ config })({ fn });

return Promise.all([
wrappedApiFn(),
wrappedApiFn()
]).then(() => {
expect(apiFn).to.have.been.calledTwice;
expect(fn).to.have.been.calledTwice;
});
});

it('can be disabled on an entity level', () => {
const user = { id: 'x' };
const apiFn = sinon.spy(() => delay(() => Promise.resolve({ ...user })));
apiFn.operation = 'READ';
const fn = sinon.spy(() => delay(() => Promise.resolve({ ...user })));
fn.operation = 'READ';
const entity = { noDedup: true };
const wrappedApiFn = dedup({})({ apiFn, entity });
const wrappedApiFn = dedup({})({ fn, entity });

return Promise.all([
wrappedApiFn(),
wrappedApiFn()
]).then(() => {
expect(apiFn).to.have.been.calledTwice;
expect(fn).to.have.been.calledTwice;
});
});

it('can be disabled on a function level', () => {
const user = { id: 'x' };
const apiFn = sinon.spy(() => delay(() => Promise.resolve({ ...user })));
apiFn.operation = 'READ';
apiFn.noDedup = true;
const wrappedApiFn = dedup({})({ apiFn });
const fn = sinon.spy(() => delay(() => Promise.resolve({ ...user })));
fn.operation = 'READ';
fn.noDedup = true;
const wrappedApiFn = dedup({})({ fn });

return Promise.all([
wrappedApiFn(),
wrappedApiFn()
]).then(() => {
expect(apiFn).to.have.been.calledTwice;
expect(fn).to.have.been.calledTwice;
});
});
});
Expand Down

0 comments on commit b0255c3

Please sign in to comment.