Skip to content

Commit

Permalink
Merge pull request #54 from shystruk/dev
Browse files Browse the repository at this point in the history
Return the row number on not unique value
  • Loading branch information
shystruk authored May 10, 2021
2 parents be475f1 + 5eefe53 commit f846554
Show file tree
Hide file tree
Showing 9 changed files with 760 additions and 3,812 deletions.
9 changes: 2 additions & 7 deletions demo/dist/bundle.js

Large diffs are not rendered by default.

6 changes: 6 additions & 0 deletions demo/dist/bundle.js.LICENSE.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
/* @license
Papa Parse
v5.3.0
https://github.com/mholt/PapaParse
License: MIT
*/
46 changes: 23 additions & 23 deletions demo/index.js
Original file line number Diff line number Diff line change
@@ -1,39 +1,39 @@
import CSVFileValidator from '../src/csv-file-validator'

const requiredError = (headerName, rowNumber, columnNumber) => {
return `<div class="red">${headerName} is required in the <strong>${rowNumber} row</strong> / <strong>${columnNumber} column</strong></div>`
return `<div class="red">${headerName} is required in the <strong>${rowNumber} row</strong> / <strong>${columnNumber} column</strong></div>`
}
const validateError = (headerName, rowNumber, columnNumber) => {
return `<div class="red">${headerName} is not valid in the <strong>${rowNumber} row</strong> / <strong>${columnNumber} column</strong></div>`
return `<div class="red">${headerName} is not valid in the <strong>${rowNumber} row</strong> / <strong>${columnNumber} column</strong></div>`
}
const uniqueError = (headerName) => {
return `<div class="red">${headerName} is not unique</div>`
const uniqueError = (headerName, rowNumber) => {
return `<div class="red">${headerName} is not unique at the <strong>${rowNumber} row</strong></div>`
}
const isEmailValid = function (email) {
const reqExp = /[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,3}$/
return reqExp.test(email)
const reqExp = /[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,3}$/
return reqExp.test(email)
}
const isPasswordValid = function (password) {
return password.length >= 4
return password.length >= 4
}

const CSVConfig = {
headers: [
{ name: 'First Name', inputName: 'firstName', required: true, requiredError },
{ name: 'Last Name', inputName: 'lastName', required: true, requiredError },
{ name: 'Email', inputName: 'email', required: true, requiredError, unique: true, uniqueError, validate: isEmailValid, validateError },
{ name: 'Password', inputName: 'password', required: true, requiredError, validate: isPasswordValid, validateError },
{ name: 'Roles', inputName: 'roles', required: true, requiredError, isArray: true }
]
headers: [
{ name: 'First Name', inputName: 'firstName', required: true, requiredError },
{ name: 'Last Name', inputName: 'lastName', required: true, requiredError },
{ name: 'Email', inputName: 'email', required: true, requiredError, unique: true, uniqueError, validate: isEmailValid, validateError },
{ name: 'Password', inputName: 'password', required: true, requiredError, validate: isPasswordValid, validateError },
{ name: 'Roles', inputName: 'roles', required: true, requiredError, isArray: true }
]
}

document.getElementById('file').onchange = function(event) {
CSVFileValidator(event.target.files[0], CSVConfig)
.then(csvData => {
csvData.inValidMessages.forEach(message => {
document.getElementById('invalidMessages').insertAdjacentHTML('beforeend', message)
})
console.log(csvData.inValidMessages)
console.log(csvData.data)
})
document.getElementById('file').onchange = function (event) {
CSVFileValidator(event.target.files[0], CSVConfig)
.then(csvData => {
csvData.inValidMessages.forEach(message => {
document.getElementById('invalidMessages').insertAdjacentHTML('beforeend', message)
})
console.log(csvData.inValidMessages)
console.log(csvData.data)
})
}
4,421 changes: 669 additions & 3,752 deletions demo/package-lock.json

Large diffs are not rendered by default.

6 changes: 5 additions & 1 deletion demo/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,9 @@
"email": "[email protected]",
"url": "https://github.com/shystruk"
},
"license": "MIT"
"license": "MIT",
"devDependencies": {
"webpack": "^5.37.0",
"webpack-cli": "^4.7.0"
}
}
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "csv-file-validator",
"version": "1.10.1",
"version": "1.10.2",
"description": "Validation of CSV file against user defined schema (returns back object with data and invalid messages)",
"main": "./src/csv-file-validator.js",
"repository": "https://github.com/shystruk/csv-file-validator.git",
Expand Down
26 changes: 19 additions & 7 deletions src/csv-file-validator.js
Original file line number Diff line number Diff line change
Expand Up @@ -132,18 +132,30 @@

config.headers
.filter(function (header) {
return header.unique
return header.unique;
})
.forEach(function (header) {
if (!isValuesUnique(file.data, header.inputName)) {
file.inValidMessages.push(
_isFunction(header.uniqueError)
? header.uniqueError(header.name)
: String(header.name + ' is not unique')
);
const duplicates = [];

file.data.forEach((row, rowIndex) => {
var value = row[header.inputName];

if (duplicates.indexOf(value) >= 0) {
file.inValidMessages.push(
_isFunction(header.uniqueError)
? header.uniqueError(header.name, rowIndex + 1)
: String(
header.name + " is not unique at the " + (rowIndex + 1) + "row"
)
);
} else {
duplicates.push(value);
}
});
}
});
};
}

return CSVFileValidator;
})));
26 changes: 20 additions & 6 deletions test.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ const isEmailValid = (email) => {
}

const isPasswordValid = (password) => (password.length >= 4)
const uniqueError = (headerName) => (`<div class="red">${headerName} is not unique</div>`)
const uniqueError = (headerName, rowNumber) => (`<div class="red">${headerName} is not unique at the <strong>${rowNumber} row</strong></div>`)

const CSVConfig = {
headers: [
Expand Down Expand Up @@ -47,6 +47,13 @@ const CSVValidFileWithoutHeaders = [
'Vasyl;Stokolosa;[email protected];123123123;user;Ukraine',
].join('\n');

const CSVInvalidFileWithDuplicates = [
CSVHeader,
'Vasyl;Stokolosa;[email protected];123123;admin,manager',
'Vasyl;Stokolosa;[email protected];123123123;user;Ukraine',
'Vasyl;Stokolosa;[email protected];123123123;user;Ukraine',
].join('\n');

test('module should be a function', t => {
t.is(typeof CSVFileValidator, 'function');
});
Expand All @@ -59,35 +66,35 @@ test('should return an object with empty inValidMessages/data keys', async t =>
t.deepEqual(csvData.data, []);
});

test('should validate .csv file and return invalid messages with data', async t => {
test('should return invalid messages with data', async t => {
const csvData = await CSVFileValidator(CSVInvalidFile, CSVConfig);

t.is(csvData.inValidMessages.length, 3);
t.is(csvData.data.length, 2);
});

test('should validate .csv file and return data, file is valid', async t => {
test('should return data, file is valid', async t => {
const csvData = await CSVFileValidator(CSVValidFile, CSVConfig);

t.is(csvData.inValidMessages.length, 0);
t.is(csvData.data.length, 2);
});

test('should validate .csv file without headers and return data, file is valid and headers are optional', async t => {
test('file without headers, the file is valid and headers are optional', async t => {
const csvData = await CSVFileValidator(CSVValidFileWithoutHeaders, { ...CSVConfig, isHeaderNameOptional: true });

t.is(csvData.inValidMessages.length, 0);
t.is(csvData.data.length, 2);
});

test('should validate .csv file with headers and return data, file is valid and headers are optional', async t => {
test('file with headers, the file is valid and headers are optional', async t => {
const csvData = await CSVFileValidator(CSVValidFile, { ...CSVConfig, isHeaderNameOptional: true });

t.is(csvData.inValidMessages.length, 0);
t.is(csvData.data.length, 2);
});

test('should validate .csv file without headers and return invalid messages with data, file is valid and headers are missed', async t => {
test('file is valid and headers are missed', async t => {
const csvData = await CSVFileValidator(CSVValidFileWithoutHeaders, CSVConfig);

t.is(csvData.inValidMessages.length, 5);
Expand All @@ -99,3 +106,10 @@ test('should return optional column', async t => {

t.is(csvData.data[1].country, 'Ukraine');
});

test('file is valid and Email is not unique at the ... row', async t => {
const csvData = await CSVFileValidator(CSVInvalidFileWithDuplicates, CSVConfig);

t.is(csvData.inValidMessages.length, 2);
t.is(csvData.data.length, 3);
});
30 changes: 15 additions & 15 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1694,9 +1694,9 @@ graceful-fs@^4.1.11, graceful-fs@^4.1.2:
integrity sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==

handlebars@^4.0.3:
version "4.7.6"
resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.7.6.tgz#d4c05c1baf90e9945f77aa68a7a219aa4a7df74e"
integrity sha512-1f2BACcBfiwAfStCKZNrUCgqNZkGsAT7UM3kkYtXuLo0KnaVfjKOyf7PRzB6++aK9STyT1Pd2ZCPe3EGOXleXA==
version "4.7.7"
resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.7.7.tgz#9ce33416aad02dbd6c8fafa8240d5d98004945a1"
integrity sha512-aAcXm5OAfE/8IXkcZvCepKU3VzW1/39Fb5ZuqMtgI/hT8X2YgoMvBY5dLhq/cpOvw7Lk1nK/UF71aLG/ZnVYRA==
dependencies:
minimist "^1.2.5"
neo-async "^2.6.0"
Expand Down Expand Up @@ -1804,9 +1804,9 @@ home-or-tmp@^2.0.0:
os-tmpdir "^1.0.1"

hosted-git-info@^2.1.4:
version "2.8.8"
resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.8.tgz#7539bd4bc1e0e0a895815a2e0262420b12858488"
integrity sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg==
version "2.8.9"
resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.9.tgz#dffc0bf9a21c02209090f2aa69429e1414daf3f9"
integrity sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==

http-signature@~0.10.0:
version "0.10.1"
Expand Down Expand Up @@ -2451,9 +2451,9 @@ lodash.merge@^4.6.0:
integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==

lodash@^4.17.20, lodash@^4.17.4:
version "4.17.20"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.20.tgz#b44a9b6297bcb698f1c51a3545a2b3b368d59c52"
integrity sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==
version "4.17.21"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==

loose-envify@^1.0.0:
version "1.4.0"
Expand Down Expand Up @@ -3940,9 +3940,9 @@ tunnel-agent@~0.4.0:
integrity sha1-Y3PbdpCf5XDgjXNYM2Xtgop07us=

uglify-js@^3.1.4:
version "3.12.1"
resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.12.1.tgz#78307f539f7b9ca5557babb186ea78ad30cc0375"
integrity sha512-o8lHP20KjIiQe5b/67Rh68xEGRrc2SRsCuuoYclXXoC74AfSRGblU1HKzJWH3HxPZ+Ort85fWHpSX7KwBUC9CQ==
version "3.13.5"
resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.13.5.tgz#5d71d6dbba64cf441f32929b1efce7365bb4f113"
integrity sha512-xtB8yEqIkn7zmOyS2zUNBsYCBRhDkvlNxMMY2smuJ/qA8NCHeQvKCF3i9Z4k8FJH4+PJvZRtMrPynfZ75+CSZw==

[email protected]:
version "0.0.3"
Expand Down Expand Up @@ -4137,9 +4137,9 @@ xtend@^4.0.0, xtend@~4.0.1:
integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==

y18n@^3.2.1:
version "3.2.1"
resolved "https://registry.yarnpkg.com/y18n/-/y18n-3.2.1.tgz#6d15fba884c08679c0d77e88e7759e811e07fa41"
integrity sha1-bRX7qITAhnnA136I53WegR4H+kE=
version "3.2.2"
resolved "https://registry.yarnpkg.com/y18n/-/y18n-3.2.2.tgz#85c901bd6470ce71fc4bb723ad209b70f7f28696"
integrity sha512-uGZHXkHnhF0XeeAPgnKfPv1bgKAYyVvmNL1xlKsPYZPaIHxGti2hHqvOCQv71XMsLxu1QjergkqogUnms5D3YQ==

yallist@^2.1.2:
version "2.1.2"
Expand Down

0 comments on commit f846554

Please sign in to comment.