Skip to content

Commit abc89b2

Browse files
committed
add code to parse all files to check for http imports
1 parent 9a79df6 commit abc89b2

File tree

7 files changed

+238
-144
lines changed

7 files changed

+238
-144
lines changed

lib/core/config.js

Lines changed: 2 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -143,49 +143,6 @@ Config.prototype.loadContractsConfigFile = function() {
143143
this.contractsConfig = this._mergeConfig(configFilePath, configObject, this.env);
144144
};
145145

146-
Config.prototype.getExternalContractUrl = function (contract) {
147-
let url;
148-
const RAW_URL = 'https://raw.githubusercontent.com/';
149-
const MALFORMED_ERROR = 'Malformed Github URL for ';
150-
if (contract.file.startsWith('https://github')) {
151-
const match = contract.file.match(/https:\/\/github\.[a-z]+\/(.*)/);
152-
if (!match) {
153-
this.logger.error(MALFORMED_ERROR + contract.file);
154-
return null;
155-
}
156-
url = `${RAW_URL}${match[1].replace('blob/', '')}`;
157-
} else if (contract.file.startsWith('git')) {
158-
// Match values
159-
// [0] entire input
160-
// [1] git://
161-
// [2] user
162-
// [3] repository
163-
// [4] path
164-
// [5] branch
165-
const match = contract.file.match(
166-
/(git:\/\/)?github\.[a-z]+\/([a-zA-Z0-9_\-.]+)\/([a-zA-Z0-9_\-]+)\/([a-zA-Z0-9_\-\/.]+)#?([a-zA-Z0-1_\-.]*)?/
167-
);
168-
if (!match) {
169-
this.logger.error(MALFORMED_ERROR + contract.file);
170-
return null;
171-
}
172-
let branch = match[5];
173-
if (!branch) {
174-
branch = 'master';
175-
}
176-
url = `${RAW_URL}${match[2]}/${match[3]}/${branch}/${match[4]}`;
177-
} else {
178-
url = contract.file;
179-
}
180-
const match = url.match(
181-
/\.[a-z]+\/([a-zA-Z0-9_\-\/.]+)/
182-
);
183-
return {
184-
url,
185-
filePath: match[1]
186-
};
187-
};
188-
189146
Config.prototype.loadExternalContractsFiles = function() {
190147
let contracts = this.contractsConfig.contracts;
191148
for (let contractName in contracts) {
@@ -194,11 +151,11 @@ Config.prototype.loadExternalContractsFiles = function() {
194151
continue;
195152
}
196153
if (contract.file.startsWith('http') || contract.file.startsWith('git')) {
197-
const fileObj = this.getExternalContractUrl(contract);
154+
const fileObj = utils.getExternalContractUrl(contract.file);
198155
if (!fileObj) {
199156
return this.logger.error("HTTP contract file not found: " + contract.file);
200157
}
201-
const localFile = constants.httpContractsDirectory + fileObj.filePath;
158+
const localFile = fileObj.filePath;
202159
this.contractsFiles.push(new File({filename: localFile, type: File.types.http, basedir: '', path: fileObj.url}));
203160
} else if (fs.existsSync(contract.file)) {
204161
this.contractsFiles.push(new File({filename: contract.file, type: File.types.dapp_file, basedir: '', path: contract.file}));

lib/core/file.js

Lines changed: 40 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ const async = require('async');
22
const fs = require('./fs.js');
33
const path = require('path');
44
const request = require('request');
5+
const utils = require('../utils/utils');
56

67
class File {
78

@@ -13,21 +14,34 @@ class File {
1314
this.resolver = options.resolver;
1415
}
1516

16-
parseFileForImport(content, callback) {
17+
parseFileForImport(content, isHttpContract, callback) {
18+
if (typeof isHttpContract === 'function') {
19+
callback = isHttpContract;
20+
isHttpContract = false;
21+
}
1722
const self = this;
1823
if (self.filename.indexOf('.sol') < 0) {
1924
// Only supported in Solidity
2025
return callback();
2126
}
22-
const regex = /import "([a-zA-Z0-9_\-.\\\/]+)";/g;
27+
const regex = /import "([a-zA-Z0-9_\-.\\\/:]+)";/g;
2328
let matches;
2429
const filesToDownload = [];
2530
const pathWithoutFile = path.dirname(self.path);
2631
while ((matches = regex.exec(content))) {
27-
filesToDownload.push({
32+
const httpFileObj = utils.getExternalContractUrl(matches[1]);
33+
const fileObj = {
2834
fileRelativePath: path.join(path.dirname(self.filename), matches[1]),
2935
url: `${pathWithoutFile}/${matches[1]}`
30-
});
36+
};
37+
if (httpFileObj) {
38+
fileObj.fileRelativePath = httpFileObj.filePath;
39+
fileObj.url = httpFileObj.url;
40+
} else if (!isHttpContract) {
41+
// Just a normal import
42+
continue;
43+
}
44+
filesToDownload.push(fileObj);
3145
}
3246

3347
async.each(filesToDownload, ((fileObj, eachCb) => {
@@ -63,7 +77,7 @@ class File {
6377
fs.readFile(filename, next);
6478
},
6579
function parseForImports(content, next) {
66-
self.parseFileForImport(content, (err) => {
80+
self.parseFileForImport(content, true, (err) => {
6781
next(err, content);
6882
});
6983
}
@@ -77,24 +91,41 @@ class File {
7791
}
7892

7993
content (callback) {
94+
let content;
8095
if (this.type === File.types.embark_internal) {
81-
return callback(fs.readFileSync(fs.embarkPath(this.path)).toString());
96+
content = fs.readFileSync(fs.embarkPath(this.path)).toString();
8297
} else if (this.type === File.types.dapp_file) {
83-
return callback(fs.readFileSync(this.path).toString());
98+
content = fs.readFileSync(this.path).toString();
8499
} else if (this.type === File.types.custom) {
85-
return this.resolver(callback);
100+
return this.resolver((theContent) => {
101+
if (!this.parsedImports) {
102+
this.parsedImports = true;
103+
return this.parseFileForImport(content, () => {
104+
callback(theContent);
105+
});
106+
}
107+
callback(theContent);
108+
});
86109
} else if (this.type === File.types.http) {
87-
this.downloadFile(this.filename, this.path, (content) => {
110+
return this.downloadFile(this.filename, this.path, (content) => {
88111
if (!content) {
89112
return callback(content);
90113
}
114+
this.parsedImports = true;
91115
this.path = this.filename;
92116
this.type = File.types.dapp_file;
93117
callback(content);
94118
});
95119
} else {
96120
throw new Error("unknown file: " + this.filename);
97121
}
122+
if (!this.parsedImports) {
123+
this.parsedImports = true;
124+
return this.parseFileForImport(content, () => {
125+
callback(content);
126+
});
127+
}
128+
callback(content);
98129
}
99130

100131
}

lib/utils/utils.js

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ let https = require('follow-redirects').https;
66
let shelljs = require('shelljs');
77
var tar = require('tar');
88
var propose = require('propose');
9+
const constants = require('../constants');
910

1011
//let fs = require('../core/fs.js');
1112
let o_fs = require('fs-extra');
@@ -127,6 +128,51 @@ function pwd() {
127128
return process.env.PWD || process.cwd();
128129
}
129130

131+
function getExternalContractUrl(file) {
132+
let url;
133+
const RAW_URL = 'https://raw.githubusercontent.com/';
134+
const MALFORMED_ERROR = 'Malformed Github URL for ';
135+
if (file.startsWith('https://github')) {
136+
const match = file.match(/https:\/\/github\.[a-z]+\/(.*)/);
137+
if (!match) {
138+
console.error(MALFORMED_ERROR + file);
139+
return null;
140+
}
141+
url = `${RAW_URL}${match[1].replace('blob/', '')}`;
142+
} else if (file.startsWith('git')) {
143+
// Match values
144+
// [0] entire input
145+
// [1] git://
146+
// [2] user
147+
// [3] repository
148+
// [4] path
149+
// [5] branch
150+
const match = file.match(
151+
/(git:\/\/)?github\.[a-z]+\/([a-zA-Z0-9_\-.]+)\/([a-zA-Z0-9_\-]+)\/([a-zA-Z0-9_\-\/.]+)#?([a-zA-Z0-1_\-.]*)?/
152+
);
153+
if (!match) {
154+
console.error(MALFORMED_ERROR + file);
155+
return null;
156+
}
157+
let branch = match[5];
158+
if (!branch) {
159+
branch = 'master';
160+
}
161+
url = `${RAW_URL}${match[2]}/${match[3]}/${branch}/${match[4]}`;
162+
} else if (file.startsWith('http')) {
163+
url = file;
164+
} else {
165+
return null;
166+
}
167+
const match = url.match(
168+
/\.[a-z]+\/([a-zA-Z0-9_\-\/.]+)/
169+
);
170+
return {
171+
url,
172+
filePath: constants.httpContractsDirectory + match[1]
173+
};
174+
}
175+
130176
module.exports = {
131177
joinPath: joinPath,
132178
filesMatchingPattern: filesMatchingPattern,
@@ -143,5 +189,6 @@ module.exports = {
143189
downloadFile: downloadFile,
144190
extractTar: extractTar,
145191
proposeAlternative: proposeAlternative,
146-
pwd: pwd
192+
pwd: pwd,
193+
getExternalContractUrl
147194
};

test/config.js

Lines changed: 0 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -57,94 +57,6 @@ describe('embark.Config', function () {
5757
});
5858
});
5959

60-
describe('#getExternalContractUrl', function () {
61-
it('should get the right url for a https://github file', function () {
62-
const fileObj = config.getExternalContractUrl(
63-
{file: 'https://github.com/embark-framework/embark/blob/master/test_app/app/contracts/simple_storage.sol'}
64-
);
65-
assert.deepEqual(fileObj,
66-
{
67-
filePath: 'embark-framework/embark/master/test_app/app/contracts/simple_storage.sol',
68-
url: 'https://raw.githubusercontent.com/embark-framework/embark/master/test_app/app/contracts/simple_storage.sol'
69-
});
70-
});
71-
72-
it('should fail for a malformed https://github file', function () {
73-
const fileObj = config.getExternalContractUrl(
74-
{file: 'https://github/embark-framework/embark/blob/master/test_app/app/contracts/simple_storage.sol'}
75-
);
76-
assert.strictEqual(fileObj, null);
77-
});
78-
79-
it('should get the right url for a git:// file with no branch #', function () {
80-
const fileObj = config.getExternalContractUrl(
81-
{file: 'git://github.com/status-im/contracts/contracts/identity/ERC725.sol'}
82-
);
83-
assert.deepEqual(fileObj,
84-
{
85-
filePath: 'status-im/contracts/master/contracts/identity/ERC725.sol',
86-
url: 'https://raw.githubusercontent.com/status-im/contracts/master/contracts/identity/ERC725.sol'
87-
});
88-
});
89-
90-
it('should get the right url for a git:// file with a branch #', function () {
91-
const fileObj = config.getExternalContractUrl(
92-
{file: 'git://github.com/status-im/contracts/contracts/identity/ERC725.sol#myBranch'}
93-
);
94-
assert.deepEqual(fileObj,
95-
{
96-
filePath: 'status-im/contracts/myBranch/contracts/identity/ERC725.sol',
97-
url: 'https://raw.githubusercontent.com/status-im/contracts/myBranch/contracts/identity/ERC725.sol'
98-
});
99-
});
100-
101-
it('should fail when the git:// file is malformed', function () {
102-
const fileObj = config.getExternalContractUrl(
103-
{file: 'git://github.com/identity/ERC725.sol#myBranch'}
104-
);
105-
assert.strictEqual(fileObj, null);
106-
});
107-
108-
it('should get the right url with a github.com file without branch #', function () {
109-
const fileObj = config.getExternalContractUrl(
110-
{file: 'github.com/status-im/contracts/contracts/identity/ERC725.sol'}
111-
);
112-
assert.deepEqual(fileObj,
113-
{
114-
filePath: 'status-im/contracts/master/contracts/identity/ERC725.sol',
115-
url: 'https://raw.githubusercontent.com/status-im/contracts/master/contracts/identity/ERC725.sol'
116-
});
117-
});
118-
119-
it('should get the right url with a github.com file with branch #', function () {
120-
const fileObj = config.getExternalContractUrl(
121-
{file: 'github.com/status-im/contracts/contracts/identity/ERC725.sol#theBranch'}
122-
);
123-
assert.deepEqual(fileObj,
124-
{
125-
filePath: 'status-im/contracts/theBranch/contracts/identity/ERC725.sol',
126-
url: 'https://raw.githubusercontent.com/status-im/contracts/theBranch/contracts/identity/ERC725.sol'
127-
});
128-
});
129-
130-
it('should fail with a malformed github.com url', function () {
131-
const fileObj = config.getExternalContractUrl(
132-
{file: 'github/status-im/contracts/contracts/identity/ERC725.sol#theBranch'}
133-
);
134-
assert.strictEqual(fileObj, null);
135-
});
136-
137-
it('should succeed with a generic http url', function () {
138-
const fileObj = config.getExternalContractUrl(
139-
{file: 'http://myurl.com/myFile.sol'}
140-
);
141-
assert.deepEqual(fileObj, {
142-
filePath: 'myFile.sol',
143-
url: 'http://myurl.com/myFile.sol'
144-
});
145-
});
146-
});
147-
14860
describe('#loadExternalContractsFiles', function () {
14961
it('should create the right list of files and download', function () {
15062
config.contractsFiles = [];
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
pragma solidity ^0.4.7;
2+
contract SimpleStorage {
3+
uint public storedData;
4+
import "https://github.com/embark-framework/embark/blob/develop/test_apps/contracts_app/contracts/contract_args.sol";
5+
6+
function SimpleStorage(uint initialValue) {
7+
storedData = initialValue;
8+
}
9+
10+
function set(uint x) {
11+
storedData = x;
12+
}
13+
14+
function get() constant returns (uint retVal) {
15+
return storedData;
16+
}
17+
18+
}
19+

test/file.js

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ describe('embark.File', function () {
1616
cb();
1717
});
1818

19-
file.parseFileForImport(contract, () => {
19+
file.parseFileForImport(contract, true, () => {
2020
assert.strictEqual(downloadFileStub.callCount, 1);
2121
assert.strictEqual(downloadFileStub.firstCall.args[0],
2222
path.normalize('.embark/contracts/embark-framework/embark/master/test_app/app/contracts/ownable.sol'));
@@ -25,5 +25,39 @@ describe('embark.File', function () {
2525
done();
2626
});
2727
});
28+
29+
it('should find all the imports but not call download because not a http contract', function (done) {
30+
const contract = fs.readFileSync('./test/contracts/contract_with_import.sol').toString();
31+
const file = new File({filename: '.embark/contracts/embark-framework/embark/master/test_app/app/contracts/simple_storage.sol',
32+
path: 'https://raw.githubusercontent.com/embark-framework/embark/develop/test_apps/test_app/app/contracts/simple_storage.sol'});
33+
const downloadFileStub = sinon.stub(file, 'downloadFile')
34+
.callsFake((path, url, cb) => {
35+
cb();
36+
});
37+
38+
file.parseFileForImport(contract, () => {
39+
assert.strictEqual(downloadFileStub.callCount, 0);
40+
done();
41+
});
42+
});
43+
44+
it('should find all the imports and call downlaod because it is an http import', function (done) {
45+
const contract = fs.readFileSync('./test/contracts/contract_with_http_import.sol').toString();
46+
const file = new File({filename: '.embark/contracts/embark-framework/embark/master/test_app/app/contracts/simple_storage.sol',
47+
path: 'https://raw.githubusercontent.com/embark-framework/embark/develop/test_apps/test_app/app/contracts/simple_storage.sol'});
48+
const downloadFileStub = sinon.stub(file, 'downloadFile')
49+
.callsFake((path, url, cb) => {
50+
cb();
51+
});
52+
53+
file.parseFileForImport(contract, () => {
54+
assert.strictEqual(downloadFileStub.callCount, 1);
55+
assert.strictEqual(downloadFileStub.firstCall.args[0],
56+
'.embark/contracts/embark-framework/embark/develop/test_apps/contracts_app/contracts/contract_args.sol');
57+
assert.strictEqual(downloadFileStub.firstCall.args[1],
58+
'https://raw.githubusercontent.com/embark-framework/embark/develop/test_apps/contracts_app/contracts/contract_args.sol');
59+
done();
60+
});
61+
});
2862
});
2963
});

0 commit comments

Comments
 (0)