Skip to content

Commit 9cdaa29

Browse files
committed
CSV File Validator
1 parent 23e9d47 commit 9cdaa29

File tree

6 files changed

+4103
-0
lines changed

6 files changed

+4103
-0
lines changed

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
.idea
2+
.nyc_output
3+
node_modules
4+
coverage

.travis.yml

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
language: node_js
2+
3+
node_js:
4+
- stable
5+
6+
branches:
7+
only:
8+
- master
9+
10+
install:
11+
- npm install
12+
13+
script:
14+
- npm run test
15+
16+
after_success:
17+
- npm run report-coverage

package.json

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
{
2+
"name": "csv-file-validator",
3+
"version": "1.0.0",
4+
"description": "Validation of CSV file against user defined schema (returns back object with data and invalid messages)",
5+
"main": "./src/csv-file-validator.js",
6+
"repository": "https://github.com/shystruk/csv-file-validator.git",
7+
"directories": {
8+
"src": "src"
9+
},
10+
"scripts": {
11+
"report-coverage": "cat ./coverage/lcov.info | codecov",
12+
"coverage": "nyc report --reporter=lcov",
13+
"test": "nyc ava --browser && npm run coverage"
14+
},
15+
"keywords": [
16+
"csv parser",
17+
"parser",
18+
"validator",
19+
"csv validator",
20+
"csv file validator",
21+
"reviewer",
22+
"csv reviewer"
23+
],
24+
"author": {
25+
"name": "Vasyl Stokolosa",
26+
"email": "[email protected]",
27+
"url": "https://github.com/shystruk"
28+
},
29+
"license": "MIT",
30+
"dependencies": {
31+
"lodash": "^4.17.5",
32+
"papaparse": "^4.3.7"
33+
},
34+
"devDependencies": {
35+
"ava": "^0.25.0",
36+
"codecov.io": "^0.1.6",
37+
"nyc": "^11.4.1"
38+
}
39+
}

src/csv-file-validator.js

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
(function (global, factory) {
2+
typeof exports === 'object' && typeof module !== 'undefined'
3+
? module.exports = factory(require('papaparse'), require('lodash/uniqBy'), require('lodash/isFunction'))
4+
: typeof define === 'function' && define.amd
5+
? define(['papaparse', 'lodash/uniqBy', 'lodash/isFunction'], factory)
6+
: (global.myBundle = factory(global.Papa,global._uniqBy,global._isFunction));
7+
}(this, (function (Papa,_uniqBy,_isFunction) {
8+
'use strict';
9+
10+
Papa = Papa && Papa.hasOwnProperty('default') ? Papa['default'] : Papa;
11+
_uniqBy = _uniqBy && _uniqBy.hasOwnProperty('default') ? _uniqBy['default'] : _uniqBy;
12+
_isFunction = _isFunction && _isFunction.hasOwnProperty('default') ? _isFunction['default'] : _isFunction;
13+
14+
/**
15+
* @param {File} csvFile
16+
* @param {Object} config
17+
*/
18+
function CSVFileValidate (csvFile, config) {
19+
return new Promise((resolve, reject) => {
20+
Papa.parse(csvFile, {
21+
complete: function(results) {
22+
resolve(_prepareDataAndValidateFile(results.data, config));
23+
},
24+
error: function(error, file) {
25+
reject({error, file});
26+
}
27+
});
28+
})
29+
}
30+
31+
/**
32+
* @param {Array} csvData
33+
* @param {Object} config
34+
* @private
35+
*/
36+
function _prepareDataAndValidateFile(csvData, config) {
37+
const file = {
38+
inValidMessages: [],
39+
data: []
40+
};
41+
42+
csvData.splice(0,1); // skip first row as a header
43+
csvData.forEach((row, rowIndex) => {
44+
const columnData = {};
45+
46+
row.forEach((columnValue, columnIndex) => {
47+
const valueConfig = config.headers[columnIndex];
48+
49+
if (!valueConfig) {
50+
return;
51+
}
52+
53+
if (valueConfig.required && !columnValue.length) {
54+
file.inValidMessages.push(
55+
_isFunction(valueConfig.requiredError)
56+
? valueConfig.requiredError(rowIndex + 2, columnIndex + 1)
57+
: valueConfig.requiredError
58+
);
59+
} else if (valueConfig.validate && !valueConfig.validate(columnValue)) {
60+
file.inValidMessages.push(
61+
_isFunction(valueConfig.validateError)
62+
? valueConfig.validateError(rowIndex + 2, columnIndex + 1)
63+
: valueConfig.validateError
64+
);
65+
}
66+
67+
if (valueConfig.isArray) {
68+
columnData[valueConfig.inputName] = columnValue.split(',').map(value => value.trim());
69+
} else {
70+
columnData[valueConfig.inputName] = columnValue;
71+
}
72+
});
73+
74+
file.data.push(columnData);
75+
});
76+
77+
_checkUniqueFields(file, config);
78+
79+
return file;
80+
}
81+
82+
/**
83+
* @param {Object} file
84+
* @param {Object} config
85+
* @private
86+
*/
87+
function _checkUniqueFields(file, config) {
88+
if (!file.data.length) {
89+
return;
90+
}
91+
92+
config.headers
93+
.filter(header => header.unique)
94+
.forEach(header => {
95+
if (_uniqBy(file.data, header.inputName).length !== file.data.length) {
96+
file.inValidMessages.push(header.uniqueErrorMessage);
97+
}
98+
});
99+
};
100+
101+
return CSVFileValidate;
102+
})));

test.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import test from 'ava';
2+
import CSVFileValidate from './src/csv-file-validator';
3+
4+
test('module should be a function', t => {
5+
t.is(typeof CSVFileValidate, 'function');
6+
});

0 commit comments

Comments
 (0)