Skip to content
This repository has been archived by the owner on Sep 28, 2022. It is now read-only.

Commit

Permalink
Merge pull request #76 from makerdao/create-maker-async-plugins
Browse files Browse the repository at this point in the history
Create maker async plugins
  • Loading branch information
b-pmcg authored Jan 17, 2019
2 parents 56177df + 4e0492f commit aa6a1ff
Show file tree
Hide file tree
Showing 7 changed files with 180 additions and 54 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@makerdao/dai",
"version": "0.9.10",
"version": "0.10.0",
"contributors": [
"Wouter Kampmann <[email protected]>",
"Sean Brennan <[email protected]>",
Expand Down
41 changes: 32 additions & 9 deletions src/Maker.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,20 +8,25 @@ export default class Maker {
constructor(preset, options = {}) {
const { plugins = [], ...otherOptions } = options;

for (let plugin of plugins) {
for (let pluginTuple of plugins) {
const [plugin, pluginOptions] = pluginTuple;
if (plugin.addConfig) {
mergeOptions(otherOptions, plugin.addConfig(otherOptions));
mergeOptions(
otherOptions,
plugin.addConfig(otherOptions, pluginOptions)
);
}
}

const config = ConfigFactory.create(preset, otherOptions, resolver);
this._container = new DefaultServiceProvider(config).buildContainer();

for (let plugin of plugins) {
for (let pluginTuple of plugins) {
const [plugin, pluginOptions] = pluginTuple;
if (typeof plugin === 'function') {
plugin(this, config);
plugin(this, config, pluginOptions);
} else if (plugin.afterCreate) {
plugin.afterCreate(this, config);
plugin.afterCreate(this, config, pluginOptions);
}
}

Expand Down Expand Up @@ -97,8 +102,26 @@ function mergeOptions(object, source) {
});
}

// This factory function doesn't do much at the moment, but it will give us
// more flexibility for plugins and extensions in the future.
Maker.create = function(...args) {
return new Maker(...args);
Maker.create = async function(...args) {
const [preset, options] = args;
const { plugins } = options;

if (plugins) {
// If its not already, format the plugin to be a tuple
// of type [plugin, options object].
const pluginTuples = plugins.map(
plugin => (!Array.isArray(plugin) ? [plugin, {}] : plugin)
);
for (let pluginTuple of pluginTuples) {
const [plugin, pluginOptions] = pluginTuple;
if (plugin.beforeCreate) {
const resultOptions = await plugin.beforeCreate(pluginOptions);
Object.assign(options, resultOptions);
}
}
// reassign the plugins array in the options
options.plugins = pluginTuples;
}

return new Maker(preset, options);
};
12 changes: 6 additions & 6 deletions test/Maker.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ import Maker, { ETH, LocalService } from '../src/index';
import TestAccountProvider from './helpers/TestAccountProvider';
const Maker2 = require('../src/index');

function createMaker(privateKey) {
return Maker.create('test', { privateKey, log: false });
async function createMaker(privateKey) {
return await Maker.create('test', { privateKey, log: false });
}

test('import vs require', () => {
Expand All @@ -13,7 +13,7 @@ test('import vs require', () => {
});

test('openCdp', async () => {
const maker = createMaker();
const maker = await createMaker();
await maker.authenticate();
const cdp = await maker.openCdp();
const id = cdp.id;
Expand All @@ -26,7 +26,7 @@ test(
async () => {
const testAccount = TestAccountProvider.nextAccount();

const maker = createMaker(testAccount.key);
const maker = await createMaker(testAccount.key);
await maker.authenticate();
const cdp = await maker.openCdp();
const id = cdp.id;
Expand All @@ -39,7 +39,7 @@ test(
);

test('creates a new CDP object for existing CDPs', async () => {
const maker = createMaker();
const maker = await createMaker();
await maker.authenticate();
const cdp = await maker.openCdp();
const id = cdp.id;
Expand All @@ -50,7 +50,7 @@ test('creates a new CDP object for existing CDPs', async () => {
});

test('throws an error for an invalid id', async () => {
const maker = createMaker();
const maker = await createMaker();
await maker.authenticate();
expect.assertions(1);
try {
Expand Down
2 changes: 1 addition & 1 deletion test/eth/DSProxyService.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ describe('execute', () => {
let maker, tubContract;

beforeAll(async () => {
maker = Maker.create('test', {
maker = await Maker.create('test', {
web3: { confirmedBlockCount: '0' },
log: false
});
Expand Down
2 changes: 1 addition & 1 deletion test/infura.spec.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import Maker from '../src/index';

test('use Kovan via Infura without API key', async () => {
const maker = Maker.create('kovan', { log: false });
const maker = await Maker.create('kovan', { log: false });
await maker.authenticate();
});
2 changes: 1 addition & 1 deletion test/integration/dai.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ beforeAll(async () => {
}
};

maker = Maker.create(process.env.NETWORK, settings);
maker = await Maker.create(process.env.NETWORK, settings);

await maker.authenticate();
tokenService = maker.service('token');
Expand Down
173 changes: 138 additions & 35 deletions test/plugins.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,19 @@ beforeEach(() => {
jest.spyOn(ConfigFactory, 'create');
});

test('function plugin', () => {
test('function plugin', async () => {
const testPlugin = jest.fn(maker => {
expect(maker).toBeInstanceOf(Maker);
});

Maker.create('test', { plugins: [testPlugin], autoAuthenticate: false });
await Maker.create('test', {
plugins: [testPlugin],
autoAuthenticate: false
});
expect(testPlugin).toBeCalled();
});

test('object plugin with addConfig and afterCreate', () => {
test('object plugin with addConfig and afterCreate', async () => {
const testPlugin = {
addConfig: jest.fn(config => {
expect(config.autoAuthenticate).toBe(false);
Expand All @@ -27,7 +30,7 @@ test('object plugin with addConfig and afterCreate', () => {
})
};

Maker.create('test', {
await Maker.create('test', {
plugins: [testPlugin],
autoAuthenticate: false,
log: false
Expand All @@ -51,7 +54,7 @@ const makeMockService = role => {
return MockService;
};

test('add options, merging correctly', () => {
test('add options, merging correctly', async () => {
const MockService1 = makeMockService('mock1');
const MockService2 = makeMockService('mock2');

Expand All @@ -72,7 +75,7 @@ test('add options, merging correctly', () => {
})
};

Maker.create('test', {
await Maker.create('test', {
plugins: [testPlugin],
additionalServices: ['mock1'],
mock1: MockService1,
Expand Down Expand Up @@ -110,7 +113,7 @@ test('add options, merging correctly', () => {
});
});

test('do not allow collisions in smartContract.addContracts', () => {
test('do not allow collisions in smartContract.addContracts', async () => {
const exampleAbiItem = {
constant: true,
inputs: [],
Expand Down Expand Up @@ -147,35 +150,40 @@ test('do not allow collisions in smartContract.addContracts', () => {
})
};

expect(() => {
Maker.create('test', {
autoAuthenticate: false,
plugins: [testPlugin],
smartContract: {
addContracts: {
// different address
foo: {
abi: [],
address: '0xbeefed1bedded2dabbed3defaced4decade5bead'
},
// different ABI
bar: {
abi: [{ ...exampleAbiItem, name: 'zest' }],
address: '0xbeefed1bedded2dabbed3defaced4decade5bade'
},
// same address and ABI -- will not cause error
baz: {
abi: [],
address: '0xbeefed1bedded2dabbed3defaced4decade5abed'
}
expect.assertions(1);

await Maker.create('test', {
autoAuthenticate: false,
plugins: [testPlugin],
smartContract: {
addContracts: {
// different address
foo: {
abi: [],
address: '0xbeefed1bedded2dabbed3defaced4decade5bead'
},
// different ABI
bar: {
abi: [{ ...exampleAbiItem, name: 'zest' }],
address: '0xbeefed1bedded2dabbed3defaced4decade5bade'
},
// same address and ABI -- will not cause error
baz: {
abi: [],
address: '0xbeefed1bedded2dabbed3defaced4decade5abed'
}
}
});
}).toThrowError(/Contracts "foo", "bar" cannot be defined more than once/);
// note that "baz" is not in this list
}
}).catch(
err =>
expect(err).toEqual(
new Error('Contracts "foo", "bar" cannot be defined more than once')
)
// note that "baz" is not in this list
);
});

test('add options when smartContract.addContracts is not set on target', () => {
test('add options when smartContract.addContracts is not set on target', async () => {
const testPlugin = {
addConfig: jest.fn(() => {
return {
Expand All @@ -191,14 +199,14 @@ test('add options when smartContract.addContracts is not set on target', () => {
})
};

Maker.create('test', {
await Maker.create('test', {
plugins: [testPlugin],
autoAuthenticate: false,
smartContract: {}
});
});

test('add options when smartContract.addContracts is not set on source', () => {
test('add options when smartContract.addContracts is not set on source', async () => {
const testPlugin = {
addConfig: jest.fn(() => {
return {
Expand All @@ -207,7 +215,7 @@ test('add options when smartContract.addContracts is not set on source', () => {
})
};

Maker.create('test', {
await Maker.create('test', {
plugins: [testPlugin],
additionalServices: ['mock1'],
autoAuthenticate: false,
Expand All @@ -221,3 +229,98 @@ test('add options when smartContract.addContracts is not set on source', () => {
}
});
});

test('each plugin type can be created with an optional options object for additional processing', async () => {
const MockService1 = makeMockService('mock1');
const MockService2 = makeMockService('mock2');

const beforeCreatePlugin = {
beforeCreate: jest.fn(options => {
expect(options.testOption1).toBe('myOption1');
return {
additionalServices: ['mock1'],
mock1: MockService1,
testOption1: options.testOption1
};
})
};

const addConfigPlugin = {
addConfig: jest.fn((config, options) => {
expect(options.testOption2).toBe('myOption2');
return {
additionalServices: ['mock2'],
mock2: MockService2,
testOption2: options.testOption2
};
})
};

const afterCreatePlugin = {
afterCreate: jest.fn((maker, config, options) => {
expect(options.testOption3).toBe('myOption3');
})
};

const functionPlugin = jest.fn((maker, config, options) => {
const gasEstimatorService = maker.service('gasEstimator', true);
gasEstimatorService.setPercentage(options.percentage);
const percentage = gasEstimatorService.getPercentage();
expect(percentage).toBe(options.percentage);
});

await Maker.create('test', {
plugins: [
[addConfigPlugin, { testOption2: 'myOption2' }],
[beforeCreatePlugin, { testOption1: 'myOption1' }],
[functionPlugin, { percentage: 10 }],
[afterCreatePlugin, { testOption3: 'myOption3' }]
],
autoAuthenticate: false
});

expect(addConfigPlugin.addConfig).toBeCalled();
expect(beforeCreatePlugin.beforeCreate).toBeCalled();
expect(afterCreatePlugin.afterCreate).toBeCalled();
expect(functionPlugin).toBeCalled();
expect(ConfigFactory.create).toBeCalled();
const last = ConfigFactory.create.mock.calls.length - 1;

//beforeCreate and addConfig plugins can modify the maker config
expect(ConfigFactory.create.mock.calls[last][1]).toEqual({
additionalServices: ['mock1', 'mock2'],
mock1: MockService1,
mock2: MockService2,
autoAuthenticate: false,
testOption1: 'myOption1',
testOption2: 'myOption2'
});
});

test('object plugin with beforeCreate can make an async call', async () => {
const mockAsyncCall = async () => {
return new Promise(resolve => {
setTimeout(50);
resolve({ testOption: 'myOption' });
});
};

const testPlugin = {
beforeCreate: jest.fn(async () => {
return await mockAsyncCall();
})
};

await Maker.create('test', {
plugins: [testPlugin],
autoAuthenticate: false
});

expect(testPlugin.beforeCreate).toBeCalled();
expect(ConfigFactory.create).toBeCalled();
const last = ConfigFactory.create.mock.calls.length - 1;
expect(ConfigFactory.create.mock.calls[last][1]).toEqual({
autoAuthenticate: false,
testOption: 'myOption'
});
});

0 comments on commit aa6a1ff

Please sign in to comment.