diff --git a/dapps/tests/app/config/namesystem.js b/dapps/tests/app/config/namesystem.js index bb7297b75..764487bde 100644 --- a/dapps/tests/app/config/namesystem.js +++ b/dapps/tests/app/config/namesystem.js @@ -2,7 +2,9 @@ module.exports = { "default": { "enabled": true, "available_providers": ["ens"], - "provider": "ens", + "provider": "ens" + }, + development: { "register": { "rootDomain": "embark.eth", "subdomains": { @@ -11,8 +13,5 @@ module.exports = { "MyToken2": "$MyToken2" } } - }, - test: { - enabled: true } }; diff --git a/dapps/tests/app/test/embarkJS_spec.js b/dapps/tests/app/test/embarkJS_spec.js index b25cd2797..2e76268dc 100644 --- a/dapps/tests/app/test/embarkJS_spec.js +++ b/dapps/tests/app/test/embarkJS_spec.js @@ -4,6 +4,12 @@ const SimpleStorage = require('Embark/contracts/SimpleStorage'); const EmbarkJS = require('Embark/EmbarkJS'); config({ + namesystem: { + enabled: true, + register: { + "rootDomain": "test.eth" + } + }, contracts: { deploy: { "SimpleStorage": { @@ -14,12 +20,12 @@ config({ }); describe("EmbarkJS functions", function() { - it('should have access to ENS functions', async function() { - const rootAddress = await EmbarkJS.Names.resolve('embark.eth'); + it('should have access to ENS functions and registered test.eth', async function() { + const rootAddress = await EmbarkJS.Names.resolve('test.eth'); assert.strictEqual(rootAddress, web3.eth.defaultAccount); const rootName = await EmbarkJS.Names.lookup(rootAddress); - assert.strictEqual(rootName, 'embark.eth'); + assert.strictEqual(rootName, 'test.eth'); }); it('should have access to Storage functions', async function() { diff --git a/dapps/tests/app/test/namesystem_spec.js b/dapps/tests/app/test/namesystem_spec.js index 839de2ab6..d0a2e968e 100644 --- a/dapps/tests/app/test/namesystem_spec.js +++ b/dapps/tests/app/test/namesystem_spec.js @@ -5,6 +5,15 @@ const MyToken2 = require('Embark/contracts/MyToken2'); const EmbarkJS = require('Embark/EmbarkJS'); config({ + namesystem: { + "register": { + "rootDomain": "embark.eth", + "subdomains": { + "mytoken": "$MyToken", + "MyToken2": "$MyToken2" + } + } + }, contracts: { deploy: { "Token": { diff --git a/packages/embark-ens/src/index.js b/packages/embark-ens/src/index.js index 10c0e4fd7..77f526f58 100644 --- a/packages/embark-ens/src/index.js +++ b/packages/embark-ens/src/index.js @@ -67,28 +67,39 @@ class ENS { this.logger = embark.logger; this.events = embark.events; this.fs = embark.fs; - this.namesConfig = embark.config.namesystemConfig; + this.config = embark.config; this.enabled = false; - this.registration = this.namesConfig.register || {}; this.embark = embark; this.ensConfig = ensConfig; this.configured = false; this.modulesPath = dappPath(embark.config.embarkConfig.generationDir, dappArtifacts.symlinkDir); + this.consoleCmdsRegistered = false; + this.eventsRegistered = false; this.events.setCommandHandler("ens:resolve", this.ensResolve.bind(this)); this.events.setCommandHandler("ens:isENSName", this.isENSName.bind(this)); - if (this.namesConfig === {} || - this.namesConfig.enabled !== true || - this.namesConfig.available_providers.indexOf('ens') < 0) { - return; + this.embark.events.setCommandHandler("module:namesystem:reset", (cb) => { + this.reset(); + this.init(cb); + }); + + this.init(); + } + + init(cb = () => {}) { + if (this.config.namesystemConfig === {} || + this.config.namesystemConfig.enabled !== true || + !this.config.namesystemConfig.available_providers || + this.config.namesystemConfig.available_providers.indexOf('ens') < 0) { + return cb(); } this.enabled = true; - this.doSetENSProvider = this.namesConfig.provider === 'ens'; + this.doSetENSProvider = this.config.namesystemConfig.provider === 'ens'; - this.addENSToEmbarkJS(); this.registerEvents(); this.registerConsoleCommands(); + this.addENSToEmbarkJS(cb); } reset() { @@ -96,6 +107,10 @@ class ENS { } registerConsoleCommands() { + if (this.consoleCmdsRegistered) { + return; + } + this.consoleCmdsRegistered = true; this.embark.registerConsoleCommand({ usage: 'resolve [name]', description: __('Resolves an ENS name'), @@ -138,6 +153,10 @@ class ENS { } registerEvents() { + if (this.eventsRegistered) { + return; + } + this.eventsRegistered = true; this.embark.registerActionForEvent("deploy:beforeAll", this.configureContractsAndRegister.bind(this)); this.events.on('blockchain:reseted', this.reset.bind(this)); this.events.setCommandHandler("storage:ens:associate", this.associateStorageToEns.bind(this)); @@ -147,7 +166,7 @@ class ENS { getEnsConfig(cb) { cb({ env: this.env, - registration: this.registration, + registration: this.config.namesystemConfig.register, registryAbi: this.ensConfig.ENSRegistry.abiDefinition, registryAddress: this.ensConfig.ENSRegistry.deployedAddress, registrarAbi: this.ensConfig.FIFSRegistrar.abiDefinition, @@ -166,7 +185,7 @@ class ENS { self.events.request('blockchain:networkId', (networkId) => { const isKnownNetwork = Boolean(ENS_CONTRACTS_CONFIG[networkId]); - const shouldRegisterSubdomain = self.registration && self.registration.subdomains && Object.keys(self.registration.subdomains).length; + const shouldRegisterSubdomain = self.config.namesystemConfig.register && self.config.namesystemConfig.register.subdomains && Object.keys(self.config.namesystemConfig.register.subdomains).length; if (isKnownNetwork || !shouldRegisterSubdomain) { return cb(); } @@ -248,8 +267,8 @@ class ENS { registerConfigDomains(config, cb) { this.events.request("blockchain:defaultAccount:get", (defaultAccount) => { - async.each(Object.keys(this.registration.subdomains), (subDomainName, eachCb) => { - const address = this.registration.subdomains[subDomainName]; + async.each(Object.keys(this.config.namesystemConfig.register.subdomains), (subDomainName, eachCb) => { + const address = this.config.namesystemConfig.register.subdomains[subDomainName]; const directivesRegExp = new RegExp(/\$(\w+\[?\d?\]?)/g); @@ -281,13 +300,13 @@ class ENS { } safeRegisterSubDomain(subDomainName, address, defaultAccount, callback) { - this.ensResolve(`${subDomainName}.${this.registration.rootDomain}`, (error, currentAddress) => { + this.ensResolve(`${subDomainName}.${this.config.namesystemConfig.register.rootDomain}`, (error, currentAddress) => { if (currentAddress && currentAddress.toLowerCase() === address.toLowerCase()) { return callback(); } if (error && error !== NOT_REGISTERED_ERROR) { - this.logger.error(__('Error resolving %s', `${subDomainName}.${this.registration.rootDomain}`)); + this.logger.error(__('Error resolving %s', `${subDomainName}.${this.config.namesystemConfig.register.rootDomain}`)); return callback(error); } @@ -299,7 +318,7 @@ class ENS { registerSubDomain(defaultAccount, subDomainName, reverseNode, address, secureSend, cb) { this.events.request("blockchain:get", (web3) => { ENSFunctions.registerSubDomain(web3, this.ensContract, this.registrarContract, this.resolverContract, defaultAccount, - subDomainName, this.registration.rootDomain, reverseNode, address, this.logger, secureSend, cb, namehash); + subDomainName, this.config.namesystemConfig.register.rootDomain, reverseNode, address, this.logger, secureSend, cb, namehash); }); } @@ -363,27 +382,30 @@ class ENS { if (error) { return res.send({error: error.message || error}); } - res.send({name: `${req.body.subdomain}.${self.registration.rootDomain}`, address: req.body.address}); + res.send({name: `${req.body.subdomain}.${self.config.namesystemConfig.register.rootDomain}`, address: req.body.address}); }); }); } ); } - addENSToEmbarkJS() { + addENSToEmbarkJS(cb) { this.events.request('version:downloadIfNeeded', 'eth-ens-namehash', (err, location) => { if (err) { this.logger.error(__('Error downloading NameHash')); - return this.logger.error(err.message || err); + this.logger.error(err.message || err); + return cb(); } this.events.request('code-generator:ready', () => { this.events.request('code-generator:symlink:generate', location, 'eth-ens-namehash', (err) => { if (err) { this.logger.error(__('Error creating a symlink to eth-ens-namehash')); - return this.logger.error(err.message || err); + this.logger.error(err.message || err); + return cb(); } this.events.emit('runcode:register', 'namehash', require('eth-ens-namehash'), () => { + cb(); }); }); }); @@ -430,6 +452,7 @@ class ENS { if (self.configured) { return cb(); } + const registration = this.config.namesystemConfig.register; async.waterfall([ function getNetworkId(next) { @@ -454,19 +477,19 @@ class ENS { }); }, function checkRootNode(next) { - if (!self.registration || !self.registration.rootDomain) { + if (!registration || !registration.rootDomain) { return next(NO_REGISTRATION); } - if (!self.isENSName(self.registration.rootDomain)) { + if (!self.isENSName(registration.rootDomain)) { return next(__('Invalid domain name: {{name}}\nValid extensions are: {{extenstions}}', - {name: self.registration.rootDomain, extenstions: ENS_WHITELIST.join(', ')})); + {name: registration.rootDomain, extenstions: ENS_WHITELIST.join(', ')})); } next(); }, function registrar(next) { const registryAddress = self.ensConfig.ENSRegistry.deployedAddress; - const rootNode = namehash.hash(self.registration.rootDomain); + const rootNode = namehash.hash(registration.rootDomain); const contract = self.ensConfig.FIFSRegistrar; contract.args = [registryAddress, rootNode]; @@ -518,7 +541,7 @@ class ENS { self.resolverContract = result[2]; const web3 = result[3]; - const rootNode = namehash.hash(self.registration.rootDomain); + const rootNode = namehash.hash(registration.rootDomain); var reverseNode = namehash.hash(web3.eth.defaultAccount.toLowerCase().substr(2) + reverseAddrSuffix); const owner = await self.ensContract.methods.owner(rootNode).call(); @@ -550,7 +573,7 @@ class ENS { }, false); }).then(() => { // Set name of the reverse node to the root domain - return secureSend(web3, self.resolverContract.methods.setName(reverseNode, self.registration.rootDomain), { + return secureSend(web3, self.resolverContract.methods.setName(reverseNode, registration.rootDomain), { from: web3.eth.defaultAccount, gas: ENS_GAS_PRICE }, false); diff --git a/packages/embark-storage/src/index.js b/packages/embark-storage/src/index.js index 6606f789a..7316f3d26 100644 --- a/packages/embark-storage/src/index.js +++ b/packages/embark-storage/src/index.js @@ -4,7 +4,6 @@ import * as async from 'async'; class Storage { constructor(embark, options){ this.embark = embark; - this.storageConfig = embark.config.storageConfig; this.plugins = options.plugins; this.ready = false; @@ -15,22 +14,24 @@ class Storage { this.embark.events.once("module:storage:ready", cb); }); - if (!this.storageConfig.enabled) { + this.embark.events.setCommandHandler("module:storage:reset", (cb) => { + this.ready = false; + this.addSetProviders(cb); + }); + + if (!embark.config.storageConfig.enabled) { this.ready = true; return; } this.handleUploadCommand(); - this.addSetProviders(() => { - this.ready = true; - this.embark.events.emit("module:storage:ready"); - }); + this.addSetProviders(() => {}); } handleUploadCommand() { const self = this; this.embark.events.setCommandHandler('storage:upload', (cb) => { - let platform = self.storageConfig.upload.provider; + let platform = this.embark.config.storageConfig.upload.provider; let uploadCmds = self.plugins.getPluginsProperty('uploadCmds', 'uploadCmds'); for (let uploadCmd of uploadCmds) { @@ -44,7 +45,7 @@ class Storage { } addSetProviders(cb) { - let code = `\nEmbarkJS.Storage.setProviders(${JSON.stringify(this.storageConfig.dappConnection || [])}, {web3});`; + let code = `\nEmbarkJS.Storage.setProviders(${JSON.stringify(this.embark.config.storageConfig.dappConnection || [])}, {web3});`; let shouldInit = (storageConfig) => { return storageConfig.enabled; @@ -75,7 +76,10 @@ class Storage { // in the case where the storage process is too slow when starting up we // execute ourselves the setProviders because the console provider init // was already executed - this.embark.events.request('runcode:eval', `if (Object.keys(EmbarkJS.Storage.Providers).length) { ${code} }`, cb, true); + this.embark.events.request('runcode:eval', `if (Object.keys(EmbarkJS.Storage.Providers).length) { ${code} }`, () => { + this.ready = true; + this.embark.events.emit("module:storage:ready"); + }, true); }); } diff --git a/packages/embark-test-runner/src/test.js b/packages/embark-test-runner/src/test.js index f312628f2..915767f32 100644 --- a/packages/embark-test-runner/src/test.js +++ b/packages/embark-test-runner/src/test.js @@ -1,5 +1,6 @@ import { __ } from 'embark-i18n'; import { deconstructUrl, prepareContractsConfig, buildUrl } from 'embark-utils'; +import deepEqual from 'deep-equal'; const async = require('async'); const web3Utils = require('web3-utils'); @@ -24,7 +25,11 @@ class Test { this.accounts = []; this.embarkjs = {}; this.dappPath = options.dappPath; - this.accounts = []; + this.moduleConfigs = { + namesystem: {}, + storage: {}, + communication: {} + }; this.events.setCommandHandler("blockchain:provider:contract:accounts:get", cb => { this.events.request("blockchain:getAccounts", cb); @@ -168,6 +173,26 @@ class Test { }); } + checkModuleConfigs(options, callback) { + const self = this; + const restartModules = []; + + Object.keys(this.moduleConfigs).forEach(moduleName => { + options[moduleName] = options[moduleName] || {}; + if (!deepEqual(options[moduleName], this.moduleConfigs[moduleName])) { + restartModules.push(function (paraCb) { + self.events.request(`config:${moduleName}Config:set`, options[moduleName], true, () => { + self.events.request(`module:${moduleName}:reset`, paraCb); + }); + }); + } + }); + + async.parallel(restartModules, (err, _result) => { + callback(err); + }); + } + config(options, callback) { const self = this; self.needConfig = false; @@ -198,6 +223,9 @@ class Test { function checkDeploymentOpts(next) { self.checkDeploymentOptions(options, next); }, + function checkModuleConfigs(next) { + self.checkModuleConfigs(options, next); + }, function prepareContracts(next) { if (!self.firstDeployment || !self.options.coverage) { return next(); diff --git a/packages/embark/src/lib/core/config.js b/packages/embark/src/lib/core/config.js index 159a710aa..43e5400af 100644 --- a/packages/embark/src/lib/core/config.js +++ b/packages/embark/src/lib/core/config.js @@ -32,11 +32,12 @@ const PACKAGE = require('../../../package.json'); const embark5ChangesUrl = 'https://...'; var Config = function(options) { - const self = this; this.env = options.env || 'default'; this.blockchainConfig = {}; this.contractsConfig = {}; this.pipelineConfig = {}; + this.namesystemConfig = {}; + this.communicationConfig = {}; this.webServerConfig = options.webServerConfig; this.chainTracker = {}; this.assetFiles = {}; @@ -53,51 +54,65 @@ var Config = function(options) { this.shownNoAccountConfigMsg = false; // flag to ensure "no account config" message is only displayed once to the user this.corsParts = []; this.providerUrl = null; + + this.registerEvents(); +}; + +Config.prototype.setConfig = function(configName, newConfig, recursive, cb) { + if (typeof recursive === 'function') { + cb = recursive; + recursive = false; + } + if (recursive) { + this[configName] = recursiveMerge(this[configName], newConfig); + } else { + this[configName] = newConfig; + } + cb(); +}; + +Config.prototype.registerEvents = function() { this.events.setCommandHandler("config:cors:add", (url) => { this.corsParts.push(url); this._updateBlockchainCors(); }); - self.events.setCommandHandler("config:contractsConfig", (cb) => { - cb(self.contractsConfig); + this.events.setCommandHandler("config:contractsConfig", (cb) => { + cb(this.contractsConfig); }); - self.events.setCommandHandler("config:contractsConfig:set", (config, cb) => { - self.contractsConfig = config; - cb(); - }); + this.events.setCommandHandler("config:contractsConfig:set", this.setConfig.bind(this, 'contractsConfig')); + this.events.setCommandHandler("config:blockchainConfig:set", this.setConfig.bind(this, 'blockchainConfig')); + this.events.setCommandHandler("config:storageConfig:set", this.setConfig.bind(this, 'storageConfig')); + this.events.setCommandHandler("config:namesystemConfig:set", this.setConfig.bind(this, 'namesystemConfig')); + this.events.setCommandHandler("config:communicationConfig:set", this.setConfig.bind(this, 'communicationConfig')); - self.events.setCommandHandler("config:blockchainConfig:set", (config, cb) => { - self.blockchainConfig = config; - cb(); - }); - - self.events.setCommandHandler("config:contractsFiles", (cb) => { - cb(self.contractsFiles); + this.events.setCommandHandler("config:contractsFiles", (cb) => { + cb(this.contractsFiles); }); // TODO: refactor this so reading the file can be done with a normal resolver or something that takes advantage of the plugin api - self.events.setCommandHandler("config:contractsFiles:add", (filename, resolver) => { + this.events.setCommandHandler("config:contractsFiles:add", (filename, resolver) => { resolver = resolver || function(callback) { callback(fs.readFileSync(filename).toString()); }; - self.contractsFiles.push(new File({path: filename, originalPath: filename, type: Types.custom, resolver})); + this.contractsFiles.push(new File({path: filename, originalPath: filename, type: Types.custom, resolver})); }); - self.events.setCommandHandler("config:contractsFiles:reset", (cb) => { - self.contractsFiles.forEach((file) => { + this.events.setCommandHandler("config:contractsFiles:reset", (cb) => { + this.contractsFiles.forEach((file) => { if(file.path.includes(".embark")) { fs.removeSync(file.path); } - self.contractsFiles = self.contractsFiles.filter((contractFile) => contractFile.path !== file.path); + this.contractsFiles = this.contractsFiles.filter((contractFile) => contractFile.path !== file.path); }); cb(); }); - self.events.on('file-remove', (fileType, removedPath) => { + this.events.on('file-remove', (fileType, removedPath) => { if(fileType !== 'contract') return; const normalizedPath = path.normalize(removedPath); - self.contractsFiles = self.contractsFiles.filter(file => path.normalize(file.path) !== normalizedPath); + this.contractsFiles = this.contractsFiles.filter(file => path.normalize(file.path) !== normalizedPath); }); };