diff --git a/embark-ui/src/actions/index.js b/embark-ui/src/actions/index.js index d0d9db68..6c21af61 100644 --- a/embark-ui/src/actions/index.js +++ b/embark-ui/src/actions/index.js @@ -179,7 +179,7 @@ export const fiddleCompile = { export const FIDDLE_DEPLOY = createRequestTypes('FIDDLE_DEPLOY'); export const fiddleDeploy = { - post: (compiledCode) => action(FIDDLE_DEPLOY[REQUEST], {compiledCode}), + post: (compiledCode, timestamp) => action(FIDDLE_DEPLOY[REQUEST], {compiledCode, timestamp}), success: (response) => { return action(FIDDLE_DEPLOY[SUCCESS], {fiddleDeploys: response.result}); }, @@ -188,14 +188,20 @@ export const fiddleDeploy = { export const FIDDLE_FILE = createRequestTypes('FIDDLE_FILE'); export const fiddleFile = { - request: () => action(FIDDLE_FILE[REQUEST]), - success: (codeToCompile) => action(FIDDLE_FILE[SUCCESS], {codeToCompile}), - failure: (error) => action(FIDDLE_FILE[FAILURE], {error}) + request: (timestamp) => { + return action(FIDDLE_FILE[REQUEST], {timestamp}); + }, + success: (codeToCompile, payload) => action(FIDDLE_FILE[SUCCESS], {fiddleFiles: [{codeToCompile, ...payload}]}), + failure: (error, payload) => { + return action(FIDDLE_FILE[FAILURE], {fiddleFiles: [{error, ...payload}]}); + } }; export const FIDDLE_PROFILE = createRequestTypes('FIDDLE_PROFILE'); export const fiddleProfile = { - post: (compiledCode, timestamp) => action(FIDDLE_PROFILE[REQUEST], {compiledCode, timestamp}), + post: (compiledCode, timestamp) => { + return action(FIDDLE_PROFILE[REQUEST], {compiledCode, timestamp}); + }, success: (fiddleProfile, payload) => { return action(FIDDLE_PROFILE[SUCCESS], {fiddleProfiles: [{...fiddleProfile, ...payload}]}); }, diff --git a/embark-ui/src/api/index.js b/embark-ui/src/api/index.js index cb98dda5..d682a89b 100644 --- a/embark-ui/src/api/index.js +++ b/embark-ui/src/api/index.js @@ -141,7 +141,7 @@ export function postFiddleCompile(payload) { } export function postFiddleDeploy(payload) { - return post('/contract/deploy', {compiledContract: payload.compiledCode}); + return post('/contract/deploy', payload); } export function fetchFiles() { @@ -149,6 +149,6 @@ export function fetchFiles() { } export function postFiddleProfile(payload) { - return post('/contract/profiler/profile', {compiledContract: payload.compiledCode}); + return post('/profiler/profile', payload); } diff --git a/embark-ui/src/containers/FiddleContainer.js b/embark-ui/src/containers/FiddleContainer.js index b2035a4d..8fa8630d 100644 --- a/embark-ui/src/containers/FiddleContainer.js +++ b/embark-ui/src/containers/FiddleContainer.js @@ -41,7 +41,7 @@ class FiddleContainer extends Component { componentDidMount() { this.setState({loadingMessage: 'Loading saved state...'}); - this.props.fetchLastFiddle(); + this.props.fetchLastFiddle(Date.now()); } componentDidUpdate(prevProps) { @@ -222,7 +222,7 @@ class FiddleContainer extends Component { fatalFiddleCard={this._renderFatalCard("Failed to compile", fiddleCompileError)} fatalFiddleDeployCard={this._renderFatalCard("Failed to deploy", fiddleDeployError)} compiledContractsCard={compiledFiddle && compiledFiddle.compilationResult && this._renderSuccessCard("Contract(s) compiled!", - diff --git a/embark-ui/src/reducers/index.js b/embark-ui/src/reducers/index.js index 7cc3022d..95ac58e0 100644 --- a/embark-ui/src/reducers/index.js +++ b/embark-ui/src/reducers/index.js @@ -1,5 +1,5 @@ import {combineReducers} from 'redux'; -import {REQUEST, SUCCESS} from "../actions"; +import {REQUEST, SUCCESS, FAILURE} from "../actions"; const BN_FACTOR = 10000; const voidAddress = '0x0000000000000000000000000000000000000000'; @@ -22,6 +22,7 @@ const entitiesDefaultState = { fiddleCompiles: [], fiddleDeploys: [], fiddleProfiles: [], + fiddleFiles: [], versions: [], plugins: [], ensRecords: [], @@ -122,7 +123,7 @@ function errorMessage(state = null, action) { } function errorEntities(state = {}, action) { - if (!action.type.endsWith(SUCCESS)) { + if (!action.type.endsWith(FAILURE)) { return state; } let newState = {}; diff --git a/embark-ui/src/sagas/index.js b/embark-ui/src/sagas/index.js index 18678bd3..7a15fbbf 100644 --- a/embark-ui/src/sagas/index.js +++ b/embark-ui/src/sagas/index.js @@ -13,7 +13,7 @@ function *doRequest(entity, apiFn, payload) { if(response) { yield put(entity.success(response.data, payload)); } else if (error) { - yield put(entity.failure(error)); + yield put(entity.failure(error, payload)); } } @@ -264,6 +264,7 @@ export default function *root() { fork(watchFetchContract), fork(watchFetchTransaction), fork(watchPostFiddleCompile), + fork(watchPostFiddleCompileSuccess), fork(watchPostFiddleDeploy), fork(watchPostFiddleProfile), fork(watchFetchLastFiddle), diff --git a/lib/modules/contracts_manager/index.js b/lib/modules/contracts_manager/index.js index a3abed1f..de189325 100644 --- a/lib/modules/contracts_manager/index.js +++ b/lib/modules/contracts_manager/index.js @@ -169,11 +169,11 @@ class ContractsManager { '/embark-api/contract/deploy', (req, res) => { this.logger.trace(`POST request /embark-api/contract/deploy:\n ${JSON.stringify(req.body)}`); - if(typeof req.body.compiledContract !== 'object'){ - return res.send({error: 'Body parameter \'compiledContract\' must be an object'}); + if(typeof req.body.compiledCode !== 'object'){ + return res.send({error: 'Body parameter \'compiledCode\' must be an object'}); } - self.compiledContracts = Object.assign(self.compiledContracts, req.body.compiledContract); - const contractNames = Object.keys(req.body.compiledContract); + self.compiledContracts = Object.assign(self.compiledContracts, req.body.compiledCode); + const contractNames = Object.keys(req.body.compiledCode); self.build((err, _mgr) => { if(err){ return res.send({error: err.message}); diff --git a/lib/modules/profiler/index.js b/lib/modules/profiler/index.js index 4170688c..0eb85f8a 100644 --- a/lib/modules/profiler/index.js +++ b/lib/modules/profiler/index.js @@ -105,13 +105,14 @@ class Profiler { 'post', '/embark-api/profiler/profile', (req, res) => { - let contract = req.body.compiledContract; - - if(!contract){ - res.send({error: 'Body parameter \'compiledContract\' is required (MISSING_PARAM).'}); + if (!(req.body.fiddleCompiles && req.body.fiddleCompiles[0] && req.body.fiddleCompiles[0].compilationResult)) { + return res.status(204).send(); // send emptry response } - - this.profileJSON(contract, (err, table) => { + const contract = req.body.fiddleCompiles[0].compilationResult; + if (typeof contract !== 'object') { + return res.status(422).send({error: 'Body parameter \'compiledCode\' must be a string'}); + } + this.profileContract(contract, (err, table) => { if (err) { return res.send({error: err.message}); } diff --git a/lib/modules/solidity/index.js b/lib/modules/solidity/index.js index daaf36ee..403437bc 100644 --- a/lib/modules/solidity/index.js +++ b/lib/modules/solidity/index.js @@ -20,15 +20,19 @@ class Solidity { 'post', '/embark-api/contract/compile', (req, res) => { - if(typeof req.body.codeToCompile !== 'string'){ - return res.send({error: 'Body parameter \'codeToCompile\' must be a string'}); + if (!(req.body.fiddleFiles && req.body.fiddleFiles[0] && req.body.fiddleFiles[0].codeToCompile)) { + return res.status(204).send(); // send emptry response } - const input = {'fiddle': {content: req.body.codeToCompile.replace(/\r\n/g, '\n')}}; + const sourceCode = req.body.fiddleFiles[0].codeToCompile; + if (typeof sourceCode !== 'string') { + return res.status(422).send({error: 'Body parameter \'codeToCompile\' must be a string'}); + } + const input = {'fiddle': {content: sourceCode.replace(/\r\n/g, '\n')}}; this.compile_solidity_code(input, {}, true, (errors, compilationResult) => { // write code to filesystem so we can view the source after page refresh const className = !compilationResult ? 'temp' : Object.keys(compilationResult).join('_'); - this._writeFiddleToFile(req.body.codeToCompile, className, Boolean(compilationResult), (err) => { - if(err) this.logger.trace('Error writing fiddle to filesystem: ', err); + this._writeFiddleToFile(sourceCode, className, Boolean(compilationResult), (err) => { + if (err) this.logger.trace('Error writing fiddle to filesystem: ', err); }); // async, do not need to wait const responseData = {errors: errors, compilationResult: compilationResult}; @@ -39,23 +43,23 @@ class Solidity { ); } - _writeFiddleToFile(code, className, isCompiled, cb){ + _writeFiddleToFile(code, className, isCompiled, cb) { fs.mkdirp('.embark/fiddles', (err) => { - if(err) return cb(err); + if (err) return cb(err); // always write to temp.sol file const filePath = Solidity._getFiddlePath('temp'); fs.writeFile(filePath, code, 'utf8', cb); // if it's compiled, also write to [classname].sol - if(isCompiled){ + if (isCompiled) { const filePath = Solidity._getFiddlePath(className); fs.writeFile(filePath, code, 'utf8', cb); } }); } - static _getFiddlePath(className){ + static _getFiddlePath(className) { return fs.dappPath(`.embark/fiddles/${className}.sol`); } @@ -64,7 +68,7 @@ class Solidity { self.solcW.compile(jsonObj, function (err, output) { self.events.emit('contracts:compile:solc', jsonObj); - if(err){ + if (err) { return callback(err); } if (output.errors && returnAllErrors) { @@ -72,7 +76,7 @@ class Solidity { } if (output.errors) { - for (let i=0; i { // handle case where we have a fiddle file and not a file stored in the dapp - if(filename.indexOf('.embark/fiddles') > -1){ + if (filename.indexOf('.embark/fiddles') > -1) { return fs.readFile(filename, 'utf8', (err, source) => { if (err) return cb({error: err}); cb(source); @@ -47,7 +47,12 @@ class Pipeline { '/embark-api/files/lastfiddle', (req, res) => { fs.readFile(fs.dappPath('.embark/fiddles/temp.sol'), 'utf8', (err, source) => { - if (err) return res.send({error: err.message}); + if (err) { + if (err.message.indexOf('ENOENT') > -1) { + return res.status(204).send(); // send empty response + } + return res.status(400).send({error: err.message}); + } res.send(source); }); } @@ -79,7 +84,7 @@ class Pipeline { } async.waterfall([ - function createPlaceholderPage(next){ + function createPlaceholderPage(next) { self.events.request('embark-building-placeholder', (html) => { fs.mkdirpSync(self.buildDir); // create dist/ folder if not already exists fs.writeFile(self.buildDir + 'index.html', html, next); @@ -107,7 +112,7 @@ class Pipeline { self.events.request('contracts:list', (_err, contracts) => { // ensure the .embark/contracts directory exists (create if not exists) fs.mkdirp(fs.dappPath(".embark/contracts", ''), (err) => { - if(err) return next(err); + if (err) return next(err); // Create a file .embark/contracts/index.js that requires all contract files // Used to enable alternate import syntax: @@ -124,9 +129,9 @@ class Pipeline { // add the contract to the exports list to support alternate import syntax importsHelperFile.write(`"${contract.className}": require('./${contract.className}').default`); - if(idx < contracts.length - 1) importsHelperFile.write(',\n'); // add a comma if we have more contracts to add + if (idx < contracts.length - 1) importsHelperFile.write(',\n'); // add a comma if we have more contracts to add }); - }, function(){ + }, function () { importsHelperFile.write('\n}'); // close the module.exports = {} importsHelperFile.close(next); // close the write stream }); @@ -135,110 +140,110 @@ class Pipeline { }, function assetFileWrite(next) { async.eachOf(self.assetFiles, function (files, targetFile, cb) { - async.map(files, - function (file, fileCb) { - self.logger.trace("reading " + file.filename); + async.map(files, + function (file, fileCb) { + self.logger.trace("reading " + file.filename); - // Not a JS file - if (file.filename.indexOf('.js') < 0) { - return file.content(function (fileContent) { - self.runPlugins(file, fileContent, fileCb); - }); - } - - // JS files - async.waterfall([ - function runWebpack(next) { - let built = false; - const webpackProcess = new ProcessLauncher({ - modulePath: utils.joinPath(__dirname, 'webpackProcess.js'), - logger: self.logger, - events: self.events, - exitCallback: function (code) { - if (!built) { - return next(`File building of ${file.filename} exited with code ${code} before the process finished`); - } - if (code) { - self.logger(__('File building process exited with code ', code)); - } - } - }); - webpackProcess.send({action: constants.pipeline.init, options: {env: self.env}}); - webpackProcess.send({action: constants.pipeline.build, file, importsList}); - - webpackProcess.once('result', constants.pipeline.built, (msg) => { - built = true; - webpackProcess.kill(); - return next(msg.error); - }); - }, - - function readFile(next) { - fs.readFile('./.embark/' + file.filename, (err, data) => { - if (err) { - return next(err); - } - next(null, data.toString()); - }); - }, - - function runPluginsOnContent(fileContent, next) { - self.runPlugins(file, fileContent, next); - } - - ], function (err, contentFile) { - if (err) { - self.logger.error(err); - return fileCb(err); - } - - fileCb(null, contentFile); + // Not a JS file + if (file.filename.indexOf('.js') < 0) { + return file.content(function (fileContent) { + self.runPlugins(file, fileContent, fileCb); }); - }, - function (err, contentFiles) { - if (err) { - self.logger.error(__('errors found while generating') + ' ' + targetFile); - } - let dir = targetFile.split('/').slice(0, -1).join('/'); - self.logger.trace("creating dir " + self.buildDir + dir); - fs.mkdirpSync(self.buildDir + dir); - - // if it's a directory - if (targetFile.slice(-1) === '/' || targetFile.indexOf('.') === -1) { - let targetDir = targetFile; - - if (targetDir.slice(-1) !== '/') { - targetDir = targetDir + '/'; - } - - async.each(contentFiles, function (file, mapCb) { - let filename = file.filename.replace(file.basedir + '/', ''); - self.logger.info("writing file " + (self.buildDir + targetDir + filename).bold.dim); - - fs.copy(file.path, self.buildDir + targetDir + filename, {overwrite: true}, mapCb); - }, cb); - return; - } - - let content = contentFiles.map(function (file) { - if (file === undefined) { - return ""; - } - return file.content; - }).join("\n"); - - self.logger.info(__("writing file") + " " + (self.buildDir + targetFile).bold.dim); - if(new RegExp(/^index.html?/i).test(targetFile)){ - targetFile = targetFile.replace('index', 'index-temp'); - placeholderPage = targetFile; - } - fs.writeFile(self.buildDir + targetFile, content, cb); } - ); - }, + + // JS files + async.waterfall([ + function runWebpack(next) { + let built = false; + const webpackProcess = new ProcessLauncher({ + modulePath: utils.joinPath(__dirname, 'webpackProcess.js'), + logger: self.logger, + events: self.events, + exitCallback: function (code) { + if (!built) { + return next(`File building of ${file.filename} exited with code ${code} before the process finished`); + } + if (code) { + self.logger(__('File building process exited with code ', code)); + } + } + }); + webpackProcess.send({action: constants.pipeline.init, options: {env: self.env}}); + webpackProcess.send({action: constants.pipeline.build, file, importsList}); + + webpackProcess.once('result', constants.pipeline.built, (msg) => { + built = true; + webpackProcess.kill(); + return next(msg.error); + }); + }, + + function readFile(next) { + fs.readFile('./.embark/' + file.filename, (err, data) => { + if (err) { + return next(err); + } + next(null, data.toString()); + }); + }, + + function runPluginsOnContent(fileContent, next) { + self.runPlugins(file, fileContent, next); + } + + ], function (err, contentFile) { + if (err) { + self.logger.error(err); + return fileCb(err); + } + + fileCb(null, contentFile); + }); + }, + function (err, contentFiles) { + if (err) { + self.logger.error(__('errors found while generating') + ' ' + targetFile); + } + let dir = targetFile.split('/').slice(0, -1).join('/'); + self.logger.trace("creating dir " + self.buildDir + dir); + fs.mkdirpSync(self.buildDir + dir); + + // if it's a directory + if (targetFile.slice(-1) === '/' || targetFile.indexOf('.') === -1) { + let targetDir = targetFile; + + if (targetDir.slice(-1) !== '/') { + targetDir = targetDir + '/'; + } + + async.each(contentFiles, function (file, mapCb) { + let filename = file.filename.replace(file.basedir + '/', ''); + self.logger.info("writing file " + (self.buildDir + targetDir + filename).bold.dim); + + fs.copy(file.path, self.buildDir + targetDir + filename, {overwrite: true}, mapCb); + }, cb); + return; + } + + let content = contentFiles.map(function (file) { + if (file === undefined) { + return ""; + } + return file.content; + }).join("\n"); + + self.logger.info(__("writing file") + " " + (self.buildDir + targetFile).bold.dim); + if (new RegExp(/^index.html?/i).test(targetFile)) { + targetFile = targetFile.replace('index', 'index-temp'); + placeholderPage = targetFile; + } + fs.writeFile(self.buildDir + targetFile, content, cb); + } + ); + }, next); }, - function removePlaceholderPage(next){ + function removePlaceholderPage(next) { let placeholderFile = self.buildDir + placeholderPage; fs.access(self.buildDir + placeholderPage, (err) => { if (err) return next(); // index-temp doesn't exist, do nothing @@ -257,7 +262,7 @@ class Pipeline { return fileCb(null, {content: fileContent, filename: file.filename, path: file.path, basedir: file.basedir, modified: true}); } async.eachSeries(self.pipelinePlugins, - function(plugin, pluginCB) { + function (plugin, pluginCB) { if (file.options && file.options.skipPipeline) { return pluginCB(); } @@ -292,7 +297,7 @@ class Pipeline { function writeContractsJSON(contracts, next) { async.each(contracts, (contract, eachCb) => { fs.writeJson(fs.dappPath(self.buildDir, 'contracts', contract.className + ".json"), contract, {spaces: 2}, eachCb); - }, () => { next(); }); + }, () => {next();}); } ], cb); }