Compare commits

..

20 commits

Author SHA1 Message Date
Simon Vieille a2e1ac418c
add jenkins status
All checks were successful
Gitnet/csv-validator/pipeline/head This commit looks good
2020-09-01 16:28:22 +02:00
Simon Vieille 2d06d4adc5
add jenkins tests
All checks were successful
Gitnet/csv-validator/pipeline/head This commit looks good
2020-09-01 16:25:29 +02:00
Simon Vieille fac4371d69
fix issue with validation 2020-09-01 16:24:29 +02:00
Simon Vieille 0b9c77ae7f
add compatibility with deblan/csv@v3 2020-09-01 16:24:15 +02:00
Simon Vieille cd43e2fed9
upgrade dependencies (php7) 2020-01-20 11:04:04 +01:00
Simon Vieille af0c45c6bc
CI 2018-06-06 19:03:13 +02:00
Simon Vieille 6169083b5e
CI 2018-06-06 15:55:17 +02:00
Simon Vieille 95c470e404
symfony/validator v3.* 2018-05-29 15:37:17 +02:00
Simon Vieille 78610d50ea CI 2017-03-15 00:42:28 +01:00
Simon Vieille 8ee01fc209 CI 2017-03-15 00:39:46 +01:00
Simon Vieille 1e6ed3d204 Insight analyse fixes 2017-03-13 15:53:14 +01:00
Simon Vieille 31adc51188 deblan/csv upgraded to the v2 and more units tests 2017-03-12 19:22:52 +01:00
Simon Vieille 104b25d17c README 2016-12-03 18:46:28 +01:00
Simon Vieille 3cb4894bd0 phpci file 2016-12-03 18:41:54 +01:00
Simon Vieille d6f4e098a4 tests 2016-12-02 15:18:33 +01:00
Simon Vieille 4858f4a948 README 2016-12-02 14:31:27 +01:00
Simon Vieille c3f4921c26 README 2016-12-02 14:27:30 +01:00
Simon Vieille ac7ae6ba9a tests 2016-12-02 14:26:50 +01:00
Simon Vieille 7de3c8a9e8 PSR2 compliance 2016-12-02 13:50:07 +01:00
Simon Vieille d5b0c29c23 Merge branch 'master' of gautier.deruette/csv-validator into master 2016-12-02 13:47:17 +01:00
14 changed files with 289 additions and 127 deletions

2
.gitignore vendored
View file

@ -1,3 +1 @@
*.swp
tags
vendor

14
.php-censor.yml Normal file
View file

@ -0,0 +1,14 @@
build_settings:
verbose: false
prefer_symlink: false
setup:
composer:
action: "install"
test:
php_unit:
directory: "tests/"
args: "--configuration 'phpunit.xml'"
complete:

25
Jenkinsfile vendored Normal file
View file

@ -0,0 +1,25 @@
pipeline {
agent any
stages {
stage('PHP 7.1') {
steps {
sh '/usr/local/bin/composer-php7.1 install'
sh 'php7.1 /usr/local/bin/phpunit-7'
}
}
stage('PHP 7.3') {
steps {
sh '/usr/local/bin/composer-php7.3 update'
sh 'php7.3 /usr/local/bin/phpunit-9'
}
}
stage('PHP 7.4') {
steps {
sh '/usr/local/bin/composer-php7.4 update'
sh 'php7.4 /usr/local/bin/phpunit-9'
}
}
}
}

View file

@ -20,4 +20,5 @@ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

4
Makefile Normal file
View file

@ -0,0 +1,4 @@
.PHONY: tests
tests:
phpunit

View file

@ -1,16 +1,22 @@
csv-validator
=============
[![Build Status](https://ci.gitnet.fr/buildStatus/icon?job=Gitnet%2Fcsv-validator%2F3)](https://ci.gitnet.fr/job/Gitnet/job/csv-validator/job/3/)
CSV validator library
That uses the constraints of Symfony framework: [http://symfony.com/doc/current/reference/constraints.html](http://symfony.com/doc/current/reference/constraints.html).
* [Installation](#installation)
* [Example](#example)
* [Contributors](#contributors)
Installation
------------
You need [composer](https://getcomposer.org/):
composer require deblan/csv-validator dev-master
composer require deblan/csv-validator
Example
@ -39,7 +45,7 @@ $validator->addFieldConstraint(0, new Email());
$validator->addFieldConstraint(1, new Date());
// Validate the legend
$validator->setExpectedLegend(array('foo', 'bar', 'bim'));
$validator->setExpectedHeaders(['foo', 'bar', 'bim']);
// An line must contain 3 columns
$validator->addDataConstraint(new Callback(function($data, ExecutionContextInterface $context) {
@ -49,17 +55,17 @@ $validator->addDataConstraint(new Callback(function($data, ExecutionContextInter
}));
// Initialisation of the parser
$parser = new CsvParser(__DIR__.'/tests/fixtures/example.csv');
$parser->setHasLegend(true);
$parser = new CsvParser();
$parser->setHasHeaders(true);
$validator->validate($parser);
$validator->validate($parser->parseFile(__DIR__.'/tests/fixtures/example.csv'));
if ($validator->isValid() === false) {
foreach ($validator->getErrors() as $error) {
$line = $error->getLine();
$line = $error->getLine();
$column = $error->getColumn();
$message = $error->getViolation()->getMessage();
echo <<<EOF
<ul>
<li>Line: $line</li>
@ -73,3 +79,42 @@ EOF;
}
}
```
Run `example.php` and see results:
```
<ul>
<li>Line: 1</li>
<li>Column: </li>
<li>
<p>Invalid legend.</p>
</li>
</ul>
<ul>
<li>Line: 4</li>
<li>Column: </li>
<li>
<p>The line must contain 3 columns</p>
</li>
</ul>
<ul>
<li>Line: 2</li>
<li>Column: 1</li>
<li>
<p>This value is not a valid email address.</p>
</li>
</ul>
<ul>
<li>Line: 3</li>
<li>Column: 2</li>
<li>
<p>This value is not a valid date.</p>
</li>
</ul>
```
Contributors
------------
* Simon Vieille
* Gautier Deruette

View file

@ -15,8 +15,8 @@
},
"minimum-stability": "dev",
"require": {
"php": ">=5.6.0",
"symfony/validator": "2.*",
"deblan/csv": "v1.1"
"php": "^7.1.3",
"symfony/validator": "^4",
"deblan/csv": "v2.0.*|v3.*"
}
}

View file

@ -2,7 +2,6 @@
use Deblan\Csv\CsvParser;
use Deblan\CsvValidator\Validator;
use Symfony\Component\Validator\Validation;
use Symfony\Component\Validator\Constraints\Email;
use Symfony\Component\Validator\Constraints\Date;
use Symfony\Component\Validator\Constraints\Callback;
@ -20,27 +19,27 @@ $validator->addFieldConstraint(0, new Email());
$validator->addFieldConstraint(1, new Date());
// Validate the legend
$validator->setExpectedLegend(array('foo', 'bar', 'bim'));
$validator->setExpectedHeaders(['foo', 'bar', 'bim']);
// An line must contain 3 columns
$validator->addDataConstraint(new Callback(function($data, ExecutionContextInterface $context) {
$validator->addDataConstraint(new Callback(function ($data, ExecutionContextInterface $context) {
if (count($data) !== 6) { // 6 because of the legend (3 fields * 2)
$context->addViolation('The line must contain 3 columns');
}
}));
// Initialisation of the parser
$parser = new CsvParser(__DIR__.'/tests/fixtures/example.csv');
$parser->setHasLegend(true);
$parser = new CsvParser();
$parser->setHasHeaders(true);
$validator->validate($parser);
$validator->validate($parser->parseFile(__DIR__.'/tests/fixtures/example.csv'));
if ($validator->isValid() === false) {
foreach ($validator->getErrors() as $error) {
$line = $error->getLine();
$line = $error->getLine();
$column = $error->getColumn();
$message = $error->getViolation()->getMessage();
echo <<<EOF
<ul>
<li>Line: $line</li>

View file

@ -7,7 +7,6 @@
convertWarningsToExceptions = "true"
processIsolation = "false"
stopOnFailure = "false"
syntaxCheck = "false"
bootstrap = "vendor/autoload.php" >
<testsuites>

View file

@ -10,7 +10,8 @@ use Symfony\Component\Validator\ConstraintViolation;
use Symfony\Component\Validator\Validation;
/**
* Class Validator
* Class Validator.
*
* @author Simon Vieille <simon@deblan.fr>
*/
class Validator
@ -36,7 +37,7 @@ class Validator
protected $dataConstraints = [];
/**
* @var boolean
* @var bool
*/
protected $hasValidate = false;
@ -44,14 +45,14 @@ class Validator
* @var array
*/
protected $errors = [];
/**
* @var array
*/
protected $expectedLegend = [];
protected $expectedHeaders = [];
/**
* Constructor
* Constructor.
*
* @param RecursiveValidator $validator
*/
@ -59,16 +60,17 @@ class Validator
{
if ($validator === null) {
$validator = Validation::createValidator();
}
}
$this->validator = $validator;
}
/**
* Append a constraint to a specific column
* Append a constraint to a specific column.
*
* @param integer $key The column number
* @param int $key The column number
* @param Constraint $constraint The constraint
*
* @return Validator
*/
public function addFieldConstraint($key, Constraint $constraint)
@ -83,9 +85,10 @@ class Validator
}
/**
* Append a constraint to a specific line
* Append a constraint to a specific line.
*
* @param Constraint $constraint The constraint
*
* @return Validator
*/
public function addDataConstraint(Constraint $constraint)
@ -96,63 +99,63 @@ class Validator
}
/**
* Set the expected legend
* Set the expected legend.
*
* @param array $legend Expected legend
*
* @return Validator
*/
public function setExpectedLegend(array $legend)
public function setExpectedHeaders(array $legend)
{
$this->expectedLegend = $legend;
$this->expectedHeaders = $legend;
return $this;
}
/**
* Run the validation
* Run the validation.
*
* @param CsvParser $parser
*/
public function validate(CsvParser $parser)
{
if ($this->parser !== $parser) {
$this->parser = $parser;
$this->parser->parse();
$this->errors = [];
}
elseif ($this->hasValidate) {
$this->hasValidate = false;
} elseif ($this->hasValidate) {
return;
}
$this->validateLegend();
$this->validateHeaders();
$this->validateDatas();
$this->validateFields();
$this->hasValidate = true;
}
/**
* Validates the legend
* Validates the legend.
*/
protected function validateLegend()
protected function validateHeaders()
{
if (!$this->parser->getHasLegend()) {
if (!$this->parser->getHasHeaders()) {
return;
}
if (empty($this->expectedLegend)) {
if (empty($this->expectedHeaders)) {
return;
}
if ($this->parser->getLegend() !== $this->expectedLegend) {
if ($this->parser->getHeaders() !== $this->expectedHeaders) {
$this->mergeErrorMessage('Invalid legend.', 1);
}
}
/**
* Validates datas
* Validates datas.
*/
protected function validateDatas()
protected function validateDatas()
{
if (empty($this->dataConstraints)) {
return;
@ -168,21 +171,21 @@ class Validator
}
/**
* Validates fields
* Validates fields.
*/
protected function validateFields()
protected function validateFields()
{
if (empty($this->fieldConstraints)) {
return;
}
foreach ($this->parser->getDatas() as $line => $data) {
foreach ($this->fieldConstraints as $key => $constraints) {
if (!isset($data[$key])) {
$column = $this->getTrueColunm($key);
$this->mergeErrorMessage(
sprintf('Field "%s" does not exist.', $column),
$this->getTrueLine($line),
sprintf('Field "%s" does not exist.', $column),
$this->getTrueLine($line),
$column
);
} else {
@ -190,8 +193,8 @@ class Validator
$violations = $this->validator->validate($data[$key], $constraint);
$this->mergeViolationsMessages(
$violations,
$this->getTrueLine($line),
$violations,
$this->getTrueLine($line),
$this->getTrueColunm($key)
);
}
@ -201,11 +204,11 @@ class Validator
}
/**
* Add violations
* Add violations.
*
* @param ConstraintViolationList $violations
* @param integer $line The line of the violations
* @param integer|null $key The column of the violations
* @param int $line The line of the violations
* @param int|null $key The column of the violations
*/
protected function mergeViolationsMessages(ConstraintViolationList $violations, $line, $key = null)
{
@ -219,11 +222,11 @@ class Validator
}
/**
* Create and append a violation from a string error
* Create and append a violation from a string error.
*
* @param string $message The error message
* @param integer $line The line of the violations
* @param integer|null $key The column of the violations
* @param string $message The error message
* @param int $line The line of the violations
* @param int|null $key The column of the violations
*/
protected function mergeErrorMessage($message, $line, $key = null)
{
@ -232,9 +235,9 @@ class Validator
}
/**
* Returns the validation status
* Returns the validation status.
*
* @return boolean
* @return bool
* @throw RuntimeException No validation yet
*/
public function isValid()
@ -247,7 +250,7 @@ class Validator
}
/**
* Returns the errors
* Returns the errors.
*
* @return array
*/
@ -257,51 +260,55 @@ class Validator
}
/**
* Generate a ConstraintViolation
* Generate a ConstraintViolation.
*
* @param string $message The error message
*
* @return ConstraintViolation
*/
protected function generateConstraintViolation($message)
protected function generateConstraintViolation($message)
{
return new ConstraintViolation($message, $message, [], null, '', null);
}
/**
* Generate a Violation
* Generate a Violation.
*
* @param string $message The error message
* @param int $line The line of the violations
* @param int|null $key The column of the violations
*
* @param string $message The error message
* @param integer $line The line of the violations
* @param integer|null $key The column of the violations
* @return Violation
*/
protected function generateViolation($line, $key, ConstraintViolation $violation)
protected function generateViolation($line, $key, ConstraintViolation $violation)
{
return new Violation($line, $key, $violation);
}
/**
* Get the true line number of an error
* Get the true line number of an error.
*
* @param integer $line
* @return integer
* @param int $line
*
* @return int
*/
protected function getTrueLine($line)
protected function getTrueLine($line)
{
if ($this->parser->getHasLegend()) {
$line++;
if ($this->parser->getHasHeaders()) {
++$line;
}
return ++$line;
}
/**
* Get the true culumn number of an error
* Get the true culumn number of an error.
*
* @param integer $key
* @return integer
* @param int $key
*
* @return int
*/
protected function getTrueColunm($key)
protected function getTrueColunm($key)
{
return ++$key;
}

View file

@ -5,31 +5,32 @@ namespace Deblan\CsvValidator;
use Symfony\Component\Validator\ConstraintViolation;
/**
* Class Violation
* Class Violation.
*
* @author Simon Vieille <simon@deblan.fr>
*/
class Violation
{
/**
* @var integer
* @var int
*/
protected $line;
/**
* @var integer
* @var int
*/
protected $column;
/**
* @var ConstraintViolation
*/
protected $violation;
/**
* Constructor
* Constructor.
*
* @param integer $line The line of the violation
* @param integer $column The column of the violation
* @param int $line The line of the violation
* @param int $column The column of the violation
* @param ConstraintViolation $violation The violation
*/
public function __construct($line, $column, ConstraintViolation $violation)
@ -41,6 +42,7 @@ class Violation
/**
* @param int $line
*
* @return Violation
*/
public function setLine($line)
@ -60,6 +62,7 @@ class Violation
/**
* @param int $column
*
* @return Violation
*/
public function setColumn($column)
@ -83,6 +86,7 @@ class Violation
/**
* @param ConstraintViolation $violation
*
* @return Violation
*/
public function setViolation(ConstraintViolation $violation)

46
tests/ExampleTest.php Normal file
View file

@ -0,0 +1,46 @@
<?php
use PHPUnit\Framework\TestCase;
/**
* class ExampleTest.
*
* @author Simon Vieille <simon@deblan.fr>
*/
class ExampleTest extends TestCase
{
public function testExemple()
{
$content = shell_exec('php -f '.__DIR__.'/../example.php');
$this->assertEquals('<ul>
<li>Line: 1</li>
<li>Column: </li>
<li>
<p>Invalid legend.</p>
</li>
</ul>
<ul>
<li>Line: 4</li>
<li>Column: </li>
<li>
<p>The line must contain 3 columns</p>
</li>
</ul>
<ul>
<li>Line: 2</li>
<li>Column: 1</li>
<li>
<p>This value is not a valid email address.</p>
</li>
</ul>
<ul>
<li>Line: 3</li>
<li>Column: 2</li>
<li>
<p>This value is not a valid date.</p>
</li>
</ul>
', $content);
}
}

View file

@ -1,86 +1,105 @@
<?php
use PHPUnit\Framework\TestCase;
use Deblan\Csv\CsvParser;
use Deblan\CsvValidator\Validator;
use Symfony\Component\Validator\Validation;
use Symfony\Component\Validator\Constraints\NotBlank;
use Symfony\Component\Validator\Constraints\Email;
class ValidatorTest extends \PHPUnit_Framework_TestCase
class ValidatorTest extends TestCase
{
public function testViolation()
{
$parser = $this->generateParser('example.csv');
$validator = $this->generateValidator($parser);
$this->setExpectedException('\RuntimeException');
$this->expectException('\RuntimeException');
$validator->isValid();
}
public function testExpectedHeaders()
{
$parser = $this->generateParser('example.csv');
$parser->setHasHeaders(true);
$validator = $this->generateValidator();
$validator->setExpectedHeaders(['foo', 'bar', 'boo']);
$validator->validate($parser->parseFile(__DIR__.'/fixtures/example.csv'));
$this->assertEquals(true, $validator->isValid());
$this->assertEquals(0, count($validator->getErrors()));
$validator = $this->generateValidator();
$validator->setExpectedHeaders(['bad', 'legend']);
$validator->validate($parser->parseFile(__DIR__.'/fixtures/example.csv'));
$this->assertEquals(false, $validator->isValid());
$this->assertEquals(1, count($validator->getErrors()));
}
public function testNoConstraint()
{
$parser = $this->generateParser('example.csv');
$validator = $this->generateValidator($parser);
$validator->validate();
$validator = $this->generateValidator();
$validator->validate($parser->parseFile(__DIR__.'/fixtures/example.csv'));
$this->assertEquals(true, $validator->isValid());
}
public function testFieldContraintsOk()
{
$parser = $this->generateParser('example.csv');
$validator = $this->generateValidator($parser);
$validator = $this->generateValidator();
$validator->addFieldConstraint(0, new NotBlank());
$validator->validate();
$validator->validate($parser->parseFile(__DIR__.'/fixtures/example.csv'));
$this->assertEquals(true, $validator->isValid());
$this->assertEquals(0, count($validator->getErrors()));
$parser = $this->generateParser('example.csv');
$validator = $this->generateValidator($parser);
$validator = $this->generateValidator();
$validator->addFieldConstraint(1, new NotBlank());
$validator->validate();
$validator->validate($parser->parseFile(__DIR__.'/fixtures/example.csv'));
$this->assertEquals(true, $validator->isValid());
$this->assertEquals(0, count($validator->getErrors()));
$parser = $this->generateParser('example.csv');
$validator = $this->generateValidator($parser);
$validator = $this->generateValidator();
$validator->addFieldConstraint(0, new NotBlank());
$validator->addFieldConstraint(1, new NotBlank());
$validator->validate();
$validator->validate($parser->parseFile(__DIR__.'/fixtures/example.csv'));
$this->assertEquals(true, $validator->isValid());
$this->assertEquals(0, count($validator->getErrors()));
}
public function testFieldContraintsKo()
{
$parser = $this->generateParser('example.csv');
$validator = $this->generateValidator($parser);
$parser = $this->generateParser();
$validator = $this->generateValidator();
$validator->addFieldConstraint(0, new Email());
$validator->validate();
$validator->validate($parser->parseFile(__DIR__.'/fixtures/example.csv'));
$this->assertEquals(false, $validator->isValid());
$this->assertEquals(4, count($validator->getErrors()));
$parser = $this->generateParser('example.csv');
$validator = $this->generateValidator($parser);
$this->assertEquals(2, count($validator->getErrors()));
$parser = $this->generateParser();
$validator = $this->generateValidator();
$validator->addFieldConstraint(1, new Email());
$validator->validate();
$validator->validate($parser->parseFile(__DIR__.'/fixtures/example.csv'));
$this->assertEquals(false, $validator->isValid());
$this->assertEquals(4, count($validator->getErrors()));
$this->assertEquals(5, count($validator->getErrors()));
$parser = $this->generateParser('example.csv');
$validator = $this->generateValidator($parser);
$validator = $this->generateValidator();
$validator->addFieldConstraint(0, new Email());
$validator->addFieldConstraint(1, new Email());
$validator->validate();
$validator->validate($parser->parseFile(__DIR__.'/fixtures/example.csv'));
$this->assertEquals(false, $validator->isValid());
$this->assertEquals(8, count($validator->getErrors()));
$this->assertEquals(7, count($validator->getErrors()));
}
protected function generateParser($file)
protected function generateParser()
{
return new CsvParser(__DIR__.'/fixtures/'.$file);
return new CsvParser();
}
protected function generateValidator(CsvParser $parser)
protected function generateValidator()
{
return new Validator($parser, Validation::createValidator());
return new Validator(Validation::createValidator());
}
}

View file

@ -1,9 +1,10 @@
<?php
use PHPUnit\Framework\TestCase;
use Symfony\Component\Validator\ConstraintViolation;
use Deblan\CsvValidator\Violation;
class ViolationTest extends \PHPUnit_Framework_TestCase
class ViolationTest extends TestCase
{
public function testViolation()
{