From 52db56323f37be372effc6eb05004516b86ad397 Mon Sep 17 00:00:00 2001 From: Fabien Furet Date: Fri, 8 Sep 2017 14:05:34 +0200 Subject: [PATCH 1/6] fix: Accept header is no longer mandatory A better fix is coming: https://github.com/RETFU/RREST/issues/14 --- src/RREST.php | 3 ++- tests/units/RREST.php | 20 ++++++++++---------- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/src/RREST.php b/src/RREST.php index 6127119..dfdb9f6 100644 --- a/src/RREST.php +++ b/src/RREST.php @@ -273,7 +273,8 @@ protected function assertHTTPHeaderContentType($availableContentTypes, $contentT protected function assertHTTPHeaderAccept(array $availableContentTypes, $acceptContentType) { if (empty($acceptContentType)) { - throw new NotAcceptableHttpException(); + //see https://github.com/RETFU/RREST/issues/14 + return; } if (empty($availableContentTypes)) { throw new \RuntimeException('No content type defined for this response'); diff --git a/tests/units/RREST.php b/tests/units/RREST.php index 57ea4e4..60811f1 100644 --- a/tests/units/RREST.php +++ b/tests/units/RREST.php @@ -99,16 +99,16 @@ function () use ($router) { ; //bad accept - $this - ->exception( - function () use ($apiSpec, $router) { - $_SERVER['Accept'] = $_SERVER['Content-Type'] = 'application/jxson'; - $this->newTestedInstance($apiSpec, $router, 'RREST\tests\units'); - $this->testedInstance->addRoute(); - } - ) - ->isInstanceOf('Symfony\Component\HttpKernel\Exception\NotAcceptableHttpException') - ; + // $this + // ->exception( + // function () use ($apiSpec, $router) { + // $_SERVER['Accept'] = $_SERVER['Content-Type'] = 'application/jxson'; + // $this->newTestedInstance($apiSpec, $router, 'RREST\tests\units'); + // $this->testedInstance->addRoute(); + // } + // ) + // ->isInstanceOf('Symfony\Component\HttpKernel\Exception\NotAcceptableHttpException') + // ; //bad content-type $this From e1c84e206c90e84a065e4723ac7d559922bb4b62 Mon Sep 17 00:00:00 2001 From: Fabien Furet Date: Fri, 27 Oct 2017 16:09:07 +0200 Subject: [PATCH 2/6] chore: update jsonguard 1.0 --- composer.json | 3 ++- src/RREST.php | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/composer.json b/composer.json index d61be53..05dae82 100644 --- a/composer.json +++ b/composer.json @@ -22,7 +22,8 @@ "jrfnl/PHP-cast-to-type": "^2.0", "willdurand/negotiation": "^2.0", "ralouphie/getallheaders": "^2.0", - "league/json-guard": "^0.5" + "league/json-guard": "^1.0", + "league/json-reference": "^1.0" }, "suggest": { "silex/silex": "To use the Silex Router", diff --git a/src/RREST.php b/src/RREST.php index dfdb9f6..ac3d045 100644 --- a/src/RREST.php +++ b/src/RREST.php @@ -427,7 +427,8 @@ protected function assertHTTPPayloadBodyJSON($value, $schema) $assertInvalidJSONException(); //validate JsonSchema - $deref = new JsonGuard\Dereferencer(); + //$deref = new JsonGuard\Dereferencer(); + $deref = \League\JsonReference\Dereferencer::draft4(); $schema = $deref->dereference($schemaJSON); $validator = new JsonGuard\Validator($valueJSON, $schema); if ($validator->fails()) { From 101005ce9559687c735d47e221e6df90068a5c52 Mon Sep 17 00:00:00 2001 From: Fabien Furet Date: Tue, 7 Nov 2017 17:53:26 +0100 Subject: [PATCH 3/6] feat: better error message when I/O validation fail --- src/Error.php | 19 +- src/RREST.php | 63 +--- src/Response.php | 50 +--- .../JsonGuardValidationErrorConverter.php | 279 ++++++++++++++++++ src/Validator/JsonValidator.php | 99 +++++++ tests/units/RREST.php | 4 +- 6 files changed, 410 insertions(+), 104 deletions(-) create mode 100644 src/Validator/JsonGuardValidationErrorConverter.php create mode 100644 src/Validator/JsonValidator.php diff --git a/src/Error.php b/src/Error.php index ab00562..f632406 100644 --- a/src/Error.php +++ b/src/Error.php @@ -4,6 +4,21 @@ class Error { + const INVALID_JSON = 'INVALID_JSON'; + const DATA_VALIDATION_REQUIRED = 'DATA_VALIDATION_REQUIRED'; + const DATA_VALIDATION_REQUIRED_ANYOF = 'DATA_VALIDATION_REQUIRED_ANYOF'; + const DATA_VALIDATION_UNKNOW = 'DATA_VALIDATION_UNKNOW'; + const DATA_VALIDATION_MINLENGTH = 'DATA_VALIDATION_MINLENGTH'; + const DATA_VALIDATION_MAXLENGTH = 'DATA_VALIDATION_MAXLENGTH'; + const DATA_VALIDATION_FORMAT = 'DATA_VALIDATION_FORMAT'; + const DATA_VALIDATION_TYPE = 'DATA_VALIDATION_TYPE'; + const DATA_VALIDATION_ENUM = 'DATA_VALIDATION_ENUM'; + const DATA_VALIDATION_MINITEMS = 'DATA_VALIDATION_MINITEMS'; + const DATA_VALIDATION_MAXITEMS = 'DATA_VALIDATION_MAXITEMS'; + const DATA_VALIDATION_UNIQUEITEMS = 'DATA_VALIDATION_UNIQUEITEMS'; + const DATA_VALIDATION_ONEOF = 'DATA_VALIDATION_ONEOF'; + + /** * @var string */ @@ -24,9 +39,9 @@ class Error /** * @param string $message * @param string $code - * @param \stdClass $context + * @param \stdClass|null $context */ - public function __construct($message = null, $code = null, $context = null) + public function __construct($message, $code, $context = null) { $this->message = $message; $this->code = $code; diff --git a/src/RREST.php b/src/RREST.php index ac3d045..c534729 100644 --- a/src/RREST.php +++ b/src/RREST.php @@ -2,6 +2,7 @@ namespace RREST; +use RREST\Validator\JsonValidator; use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException; use Symfony\Component\HttpKernel\Exception\UnsupportedMediaTypeHttpException; use Symfony\Component\HttpKernel\Exception\NotAcceptableHttpException; @@ -411,64 +412,14 @@ protected function assertHTTPPayloadBodyXML($value, $schema) */ protected function assertHTTPPayloadBodyJSON($value, $schema) { - $assertInvalidJSONException = function () { - if (json_last_error() !== JSON_ERROR_NONE) { - throw new InvalidJSONException([new Error( - ucfirst(json_last_error_msg()), - 'invalid-request-payloadbody-json' - )]); - } - }; - - //validate JSON format - $valueJSON = json_decode($value); - $assertInvalidJSONException(); - $schemaJSON = json_decode($schema); - $assertInvalidJSONException(); - - //validate JsonSchema - //$deref = new JsonGuard\Dereferencer(); - $deref = \League\JsonReference\Dereferencer::draft4(); - $schema = $deref->dereference($schemaJSON); - $validator = new JsonGuard\Validator($valueJSON, $schema); - if ($validator->fails()) { - $errors = $validator->errors(); - $jsonPointer = new JsonGuard\Pointer($value); - $invalidBodyError = []; - foreach ($validator->errors() as $jsonError) { - $error = $jsonError->toArray(); - $propertyValue = null; - $pointer = empty($error['pointer']) ? null : $error['pointer']; - if (empty($pointer) === false) { - try { - $propertyValue = $jsonPointer->get($pointer); - } catch (NonexistentValueReferencedException $e) { - //don't care if we can't have the value here, it's just - //for the context - } - } - $context = new \stdClass(); - $context->jsonPointer = $pointer; - $context->value = $propertyValue; - $context->constraints = $error['context']; - //$context->jsonSource = $valueJSON; - $message = strtolower($error['message']); - if (empty($pointer) === false) { - $message = strtolower($pointer.': '.$error['message']); - } - - $invalidBodyError[] = new Error( - strtolower($message), - strtolower($error['keyword']), - $context - ); - } - if (empty($invalidBodyError) == false) { - throw new InvalidRequestPayloadBodyException($invalidBodyError); - } + $validator = new JsonValidator($value, $schema); + if($validator->fails()) { + throw new InvalidRequestPayloadBodyException( + $validator->getErrors() + ); } - $this->hintedPayloadBody = $valueJSON; + $this->hintedPayloadBody = \json_decode($value); } protected function hintHTTPPayloadBody($hintedPayloadBody) diff --git a/src/Response.php b/src/Response.php index cb95995..bed116d 100644 --- a/src/Response.php +++ b/src/Response.php @@ -3,6 +3,7 @@ namespace RREST; use League\JsonGuard; +use RREST\Validator\JsonValidator; use Symfony\Component\Serializer\Serializer; use Symfony\Component\Serializer\Encoder\XmlEncoder; use Symfony\Component\Serializer\Normalizer\ObjectNormalizer; @@ -336,50 +337,11 @@ public function assertResponseXML($value, $schema) */ public function assertResponseJSON($value, $schema) { - $assertInvalidJSONException = function () { - if (json_last_error() !== JSON_ERROR_NONE) { - throw new InvalidJSONException([new Error( - ucfirst(json_last_error_msg()), - 'invalid-response-payloadbody-json' - )]); - } - }; - - //validate JSON format - $schemaJSON = json_decode($schema); - $assertInvalidJSONException(); - - //validate JsonSchema - $deref = new JsonGuard\Dereferencer(); - $schema = $deref->dereference($schemaJSON); - $validator = new JsonGuard\Validator($value, $schema); - if ($validator->fails()) { - $errors = $validator->errors(); - $jsonPointer = new JsonGuard\Pointer($value); - $invalidBodyError = []; - foreach ($validator->errors() as $jsonError) { - $error = $jsonError->toArray(); - $propertyValue = null; - try { - $propertyValue = $jsonPointer->get($error['pointer']); - } catch (NonexistentValueReferencedException $e) { - //don't care if we can't have the value here, it's just - //for the context - } - $context = new \stdClass(); - $context->jsonPointer = $error['pointer']; - $context->value = $propertyValue; - $context->constraints = $error['context']; - - $invalidBodyError[] = new Error( - strtolower($error['pointer'].': '.$error['message']), - strtolower($error['keyword']), - $context - ); - } - if (empty($invalidBodyError) == false) { - throw new InvalidResponsePayloadBodyException($invalidBodyError); - } + $validator = new JsonValidator($value, $schema); + if($validator->fails()) { + throw new InvalidResponsePayloadBodyException( + $validator->getErrors() + ); } } } diff --git a/src/Validator/JsonGuardValidationErrorConverter.php b/src/Validator/JsonGuardValidationErrorConverter.php new file mode 100644 index 0000000..7df16e3 --- /dev/null +++ b/src/Validator/JsonGuardValidationErrorConverter.php @@ -0,0 +1,279 @@ +validationError = $validationError; + } + + /** + * @return Error[] + */ + public function getErrors() + { + $method = 'get'.\ucfirst(\mb_strtolower($this->validationError->getKeyword())).'Errors'; + if(\method_exists($this, $method)) { + return $this->$method($this->validationError); + } + + return [new Error( + 'Data is not valid', + ERROR::DATA_VALIDATION_UNKNOW, + $this->validationError + )]; + } + + /** + * @param ValidationError $validationError + * @return array + */ + private function getRequiredErrors($validationError) + { + $errors = []; + foreach($validationError->getCause() as $field) { + $context = new \stdClass; + $context->field = $this->getFieldPath($validationError, $field); + $errors[] = new Error( + \sprintf('The field %s is required', $context->field), + ERROR::DATA_VALIDATION_REQUIRED, + $context + ); + } + return $errors; + } + + /** + * @param ValidationError $validationError + * @return array + */ + private function getAnyofErrors($validationError) + { + $errors = []; + $fields = []; + foreach($validationError->getParameter() as $subconstraint) { + if(isset($subconstraint->{Required::KEYWORD})) { + $fields = \array_merge($subconstraint->{Required::KEYWORD}, $fields); + } + } + + $context = new \stdClass; + $context->fields = $fields; + $errors[] = new Error( + \sprintf('The field %s is required', implode(' or/and ', $fields) ), + ERROR::DATA_VALIDATION_REQUIRED_ANYOF, + $context + ); + return $errors; + } + + /** + * @param ValidationError $validationError + * @return array + */ + private function getMinLengthErrors($validationError) + { + $context = new \stdClass; + $context->field = $this->getFieldPath($validationError, ''); + $context->minLength = $validationError->getParameter(); + return [new Error( + \sprintf('The field %s must be at least %s characters long', $context->field, $context->minLength), + ERROR::DATA_VALIDATION_MINLENGTH, + $context + )]; + } + + /** + * @param ValidationError $validationError + * @return array + */ + private function getMaxLengthErrors($validationError) + { + $context = new \stdClass; + $context->field = $this->getFieldPath($validationError, ''); + $context->minLength = $validationError->getParameter(); + return [new Error( + \sprintf('The field %s must be less than %s characters long', $context->field, $context->maxLength), + ERROR::DATA_VALIDATION_MINLENGTH, + $context + )]; + } + + /** + * @param ValidationError $validationError + * @return array + */ + private function getTypeErrors($validationError) + { + $context = new \stdClass; + $context->field = $this->getFieldPath($validationError, ''); + $context->type = $validationError->getParameter(); + return [new Error( + \sprintf('The type of the field %s is not valid, must be a/an %s', $context->field, implode(' or ', (array)$context->type)), + ERROR::DATA_VALIDATION_TYPE, + $context + )]; + } + + /** + * @param ValidationError $validationError + * @return array + */ + private function getEnumErrors($validationError) + { + $context = new \stdClass; + $context->field = $this->getFieldPath($validationError, ''); + $context->enum = $validationError->getParameter(); + return [new Error( + \sprintf('The field %s must be one of this values: %s', $context->field, implode(', ', $context->enum )), + ERROR::DATA_VALIDATION_ENUM, + $context + )]; + } + + /** + * @param ValidationError $validationError + * @return array + */ + private function getMinitemsErrors($validationError) + { + $context = new \stdClass; + $context->field = $this->getFieldPath($validationError, ''); + $context->minItems = $validationError->getParameter(); + return [new Error( + \sprintf('The field %s must contain at least %s item(s)', $context->field, $context->minItems), + ERROR::DATA_VALIDATION_MINITEMS, + $context + )]; + } + + /** + * @param ValidationError $validationError + * @return array + */ + private function getMaxitemsErrors($validationError) + { + $context = new \stdClass; + $context->field = $this->getFieldPath($validationError, ''); + $context->maxItems = $validationError->getParameter(); + return [new Error( + \sprintf('The field %s must contain less than %s item(s)', $context->field, $context->maxItems), + ERROR::DATA_VALIDATION_MAXITEMS, + $context + )]; + } + + /** + * @param ValidationError $validationError + * @return array + */ + private function getUniqueitemsErrors($validationError) + { + $context = new \stdClass; + $context->field = $this->getFieldPath($validationError, ''); + return [new Error( + \sprintf('The field %s must not contain duplicates values', $context->field), + ERROR::DATA_VALIDATION_UNIQUEITEMS, + $context + )]; + } + + /** + * @param ValidationError $validationError + * @return array + */ + private function getOneofErrors($validationError) + { + $context = new \stdClass; + $context->field = $this->getFieldPath($validationError, ''); + $context->rules = $validationError->getParameter(); + return [new Error( + \sprintf('The field %s don\'t follow any rules', $context->field), + ERROR::DATA_VALIDATION_ONEOF, + $context + )]; + } + + /** + * @param ValidationError $validationError + * @return array + */ + private function getPatternErrors($validationError) + { + $context = new \stdClass; + $context->field = $this->getFieldPath($validationError, ''); + $context->pattern = $validationError->getParameter(); + return [new Error( + \sprintf('The field %s must match the pattern %s', $context->field, $context->pattern), + ERROR::DATA_VALIDATION_UNIQUEITEMS, + $context + )]; + } + + /** + * @param ValidationError $validationError + * @return array + */ + private function getFormatErrors($validationError) + { + $context = new \stdClass; + $context->field = $this->getFieldPath($validationError, ''); + switch ($validationError->getParameter()) { + case 'date-time': + $message = \sprintf('The field %s must be a valid date, following RFC3339 (example: 2017-11-08T15:37:26+00:00)', $context->field); + break; + case 'uri': + $message = \sprintf('The field %s must be a valid URL', $context->field); + break; + case 'email': + $message = \sprintf('The field %s must be a valid email', $context->field); + break; + case 'ipv4': + $message = \sprintf('The field %s must be a valid ipv4', $context->field); + break; + case 'ipv6': + $message = \sprintf('The field %s must be a valid ipv6', $context->field); + break; + case 'hostname': + $message = \sprintf('The field %s must be a valid hostname', $context->field); + break; + default: + $message = 'Invalid format'; + } + return [new Error( + $message, + ERROR::DATA_VALIDATION_FORMAT, + $context + )]; + } + + /** + * @param $validationError + * @param $field + * @return string + */ + private function getFieldPath($validationError, $field) + { + $path = ''; + $dataPath = $validationError->getDataPath(); + if($dataPath !== '/') { + $path = \str_replace('/', '.',\substr($dataPath, 1)).'.'; + } + return rtrim($path.$field, '.'); + } +} \ No newline at end of file diff --git a/src/Validator/JsonValidator.php b/src/Validator/JsonValidator.php new file mode 100644 index 0000000..eee3192 --- /dev/null +++ b/src/Validator/JsonValidator.php @@ -0,0 +1,99 @@ +jsonValue = $jsonValue; + $this->jsonSchema = $jsonSchema; + } + + + /** + * @return bool + */ + public function fails() + { + return empty($this->getErrors()) === false; + } + + /** + * @return Error[] + */ + public function getErrors() + { + $this->validate(); + return $this->errors; + } + + public function validate() + { + if ($this->isValidated) return; + + $schema = Dereferencer::draft4()->dereference( + $this->getJsonFromString($this->jsonSchema) + ); + $json = $this->getJsonFromString($this->jsonValue); + $validator = new Validator($json, $schema); + if ($validator->fails()) { + $this->errors = []; + foreach ($validator->errors() as $jsonGuardError) { + $this->errors = \array_merge( + $this->errors, + (new JsonGuardValidationErrorConverter($jsonGuardError))->getErrors() + ); + } + } + } + + /** + * @throws InvalidJSONException + * @param $value + * @return \stdClass|array + */ + private function getJsonFromString($value) + { + $json = json_decode($value); + if (json_last_error() !== JSON_ERROR_NONE) { + throw new InvalidJSONException([new Error( + ucfirst(json_last_error_msg()), + Error::INVALID_JSON + )]); + } + + return $json; + } + +} \ No newline at end of file diff --git a/tests/units/RREST.php b/tests/units/RREST.php index 60811f1..ec39402 100644 --- a/tests/units/RREST.php +++ b/tests/units/RREST.php @@ -212,11 +212,11 @@ function () use ($app, $apiSpec, $router) { ) ->isInstanceOf('RREST\Exception\InvalidRequestPayloadBodyException') ->array($this->exception->getErrors()) - ->hasSize(1) + ->hasSize(2) ->object($this->exception->getErrors()[0]) ->isInstanceOf('RREST\Error') ->string($this->exception->getErrors()[0]->code) - ->isEqualTo('required') + ->isEqualTo('DATA_VALIDATION_REQUIRED') ; //json payload body hinted From 789bd418ba050cb9e10e7bae71e67cde4565adeb Mon Sep 17 00:00:00 2001 From: Fabien Furet Date: Thu, 9 Nov 2017 10:57:19 +0100 Subject: [PATCH 4/6] chore: add atoum html code coverage report --- .atoum.php | 10 ++++++++++ .gitignore | 1 + composer.json | 3 ++- 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/.atoum.php b/.atoum.php index 207a2e2..0c48a08 100644 --- a/.atoum.php +++ b/.atoum.php @@ -2,6 +2,11 @@ use \mageekguy\atoum; use mageekguy\atoum\reports; +use mageekguy\atoum\reports\coverage; +use mageekguy\atoum\writers\std; + +$extension = new reports\extension($script); +$extension->addToRunner($runner); $runner->addTestsFromDirectory('tests/units'); @@ -27,3 +32,8 @@ $report = $script->addDefaultReport(); + +$coverage = new coverage\html(); +$coverage->addWriter(new std\out()); +$coverage->setOutPutDirectory(__DIR__ . '/coverage'); +$runner->addReport($coverage); diff --git a/.gitignore b/.gitignore index 30babff..16188c4 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ /vendor/ .idea composer.lock +coverage diff --git a/composer.json b/composer.json index 05dae82..0ca439d 100644 --- a/composer.json +++ b/composer.json @@ -35,7 +35,8 @@ "silex/silex": "^2.0", "alecsammon/php-raml-parser": "^2.2", "friendsofphp/php-cs-fixer": "^2.0", - "squizlabs/php_codesniffer": "^2.0" + "squizlabs/php_codesniffer": "^2.0", + "atoum/reports-extension": "^3.0" }, "scripts": { "post-install-cmd": [ From 7910dff6fe2993407662b3a0addc1fde668ff1bc Mon Sep 17 00:00:00 2001 From: Fabien Furet Date: Thu, 9 Nov 2017 15:19:53 +0100 Subject: [PATCH 5/6] refactor: rework JsonGuard message conversion --- src/Response.php | 2 +- .../JsonGuardValidationErrorConverter.php | 279 ------------------ src/Validator/JsonValidator.php | 4 +- .../AnyOfConverter.php | 32 ++ .../JsonGuardValidationError/Converter.php | 62 ++++ .../ConverterAbstract.php | 36 +++ .../ConverterInterface.php | 13 + .../EnumConverter.php | 24 ++ .../FormatConverter.php | 45 +++ .../MaxItemsConverter.php | 24 ++ .../MaxLengthConverter.php | 24 ++ .../MinItemsConverter.php | 24 ++ .../MinLengthConverter.php | 24 ++ .../OneOfConverter.php | 24 ++ .../PatternConverter.php | 24 ++ .../RequiredConverter.php | 26 ++ .../TypeConverter.php | 24 ++ .../UniqueItemsConverter.php | 23 ++ tests/units/Validator/JsonValidator.php | 20 ++ 19 files changed, 452 insertions(+), 282 deletions(-) delete mode 100644 src/Validator/JsonGuardValidationErrorConverter.php create mode 100644 src/Validator/Util/JsonGuardValidationError/AnyOfConverter.php create mode 100644 src/Validator/Util/JsonGuardValidationError/Converter.php create mode 100644 src/Validator/Util/JsonGuardValidationError/ConverterAbstract.php create mode 100644 src/Validator/Util/JsonGuardValidationError/ConverterInterface.php create mode 100644 src/Validator/Util/JsonGuardValidationError/EnumConverter.php create mode 100644 src/Validator/Util/JsonGuardValidationError/FormatConverter.php create mode 100644 src/Validator/Util/JsonGuardValidationError/MaxItemsConverter.php create mode 100644 src/Validator/Util/JsonGuardValidationError/MaxLengthConverter.php create mode 100644 src/Validator/Util/JsonGuardValidationError/MinItemsConverter.php create mode 100644 src/Validator/Util/JsonGuardValidationError/MinLengthConverter.php create mode 100644 src/Validator/Util/JsonGuardValidationError/OneOfConverter.php create mode 100644 src/Validator/Util/JsonGuardValidationError/PatternConverter.php create mode 100644 src/Validator/Util/JsonGuardValidationError/RequiredConverter.php create mode 100644 src/Validator/Util/JsonGuardValidationError/TypeConverter.php create mode 100644 src/Validator/Util/JsonGuardValidationError/UniqueItemsConverter.php create mode 100644 tests/units/Validator/JsonValidator.php diff --git a/src/Response.php b/src/Response.php index bed116d..bdd3318 100644 --- a/src/Response.php +++ b/src/Response.php @@ -179,7 +179,7 @@ public function setContent($content) $this->assertReponseSchema( $this->getFormat(), $this->getSchema(), - $content + \json_encode($content) ); } diff --git a/src/Validator/JsonGuardValidationErrorConverter.php b/src/Validator/JsonGuardValidationErrorConverter.php deleted file mode 100644 index 7df16e3..0000000 --- a/src/Validator/JsonGuardValidationErrorConverter.php +++ /dev/null @@ -1,279 +0,0 @@ -validationError = $validationError; - } - - /** - * @return Error[] - */ - public function getErrors() - { - $method = 'get'.\ucfirst(\mb_strtolower($this->validationError->getKeyword())).'Errors'; - if(\method_exists($this, $method)) { - return $this->$method($this->validationError); - } - - return [new Error( - 'Data is not valid', - ERROR::DATA_VALIDATION_UNKNOW, - $this->validationError - )]; - } - - /** - * @param ValidationError $validationError - * @return array - */ - private function getRequiredErrors($validationError) - { - $errors = []; - foreach($validationError->getCause() as $field) { - $context = new \stdClass; - $context->field = $this->getFieldPath($validationError, $field); - $errors[] = new Error( - \sprintf('The field %s is required', $context->field), - ERROR::DATA_VALIDATION_REQUIRED, - $context - ); - } - return $errors; - } - - /** - * @param ValidationError $validationError - * @return array - */ - private function getAnyofErrors($validationError) - { - $errors = []; - $fields = []; - foreach($validationError->getParameter() as $subconstraint) { - if(isset($subconstraint->{Required::KEYWORD})) { - $fields = \array_merge($subconstraint->{Required::KEYWORD}, $fields); - } - } - - $context = new \stdClass; - $context->fields = $fields; - $errors[] = new Error( - \sprintf('The field %s is required', implode(' or/and ', $fields) ), - ERROR::DATA_VALIDATION_REQUIRED_ANYOF, - $context - ); - return $errors; - } - - /** - * @param ValidationError $validationError - * @return array - */ - private function getMinLengthErrors($validationError) - { - $context = new \stdClass; - $context->field = $this->getFieldPath($validationError, ''); - $context->minLength = $validationError->getParameter(); - return [new Error( - \sprintf('The field %s must be at least %s characters long', $context->field, $context->minLength), - ERROR::DATA_VALIDATION_MINLENGTH, - $context - )]; - } - - /** - * @param ValidationError $validationError - * @return array - */ - private function getMaxLengthErrors($validationError) - { - $context = new \stdClass; - $context->field = $this->getFieldPath($validationError, ''); - $context->minLength = $validationError->getParameter(); - return [new Error( - \sprintf('The field %s must be less than %s characters long', $context->field, $context->maxLength), - ERROR::DATA_VALIDATION_MINLENGTH, - $context - )]; - } - - /** - * @param ValidationError $validationError - * @return array - */ - private function getTypeErrors($validationError) - { - $context = new \stdClass; - $context->field = $this->getFieldPath($validationError, ''); - $context->type = $validationError->getParameter(); - return [new Error( - \sprintf('The type of the field %s is not valid, must be a/an %s', $context->field, implode(' or ', (array)$context->type)), - ERROR::DATA_VALIDATION_TYPE, - $context - )]; - } - - /** - * @param ValidationError $validationError - * @return array - */ - private function getEnumErrors($validationError) - { - $context = new \stdClass; - $context->field = $this->getFieldPath($validationError, ''); - $context->enum = $validationError->getParameter(); - return [new Error( - \sprintf('The field %s must be one of this values: %s', $context->field, implode(', ', $context->enum )), - ERROR::DATA_VALIDATION_ENUM, - $context - )]; - } - - /** - * @param ValidationError $validationError - * @return array - */ - private function getMinitemsErrors($validationError) - { - $context = new \stdClass; - $context->field = $this->getFieldPath($validationError, ''); - $context->minItems = $validationError->getParameter(); - return [new Error( - \sprintf('The field %s must contain at least %s item(s)', $context->field, $context->minItems), - ERROR::DATA_VALIDATION_MINITEMS, - $context - )]; - } - - /** - * @param ValidationError $validationError - * @return array - */ - private function getMaxitemsErrors($validationError) - { - $context = new \stdClass; - $context->field = $this->getFieldPath($validationError, ''); - $context->maxItems = $validationError->getParameter(); - return [new Error( - \sprintf('The field %s must contain less than %s item(s)', $context->field, $context->maxItems), - ERROR::DATA_VALIDATION_MAXITEMS, - $context - )]; - } - - /** - * @param ValidationError $validationError - * @return array - */ - private function getUniqueitemsErrors($validationError) - { - $context = new \stdClass; - $context->field = $this->getFieldPath($validationError, ''); - return [new Error( - \sprintf('The field %s must not contain duplicates values', $context->field), - ERROR::DATA_VALIDATION_UNIQUEITEMS, - $context - )]; - } - - /** - * @param ValidationError $validationError - * @return array - */ - private function getOneofErrors($validationError) - { - $context = new \stdClass; - $context->field = $this->getFieldPath($validationError, ''); - $context->rules = $validationError->getParameter(); - return [new Error( - \sprintf('The field %s don\'t follow any rules', $context->field), - ERROR::DATA_VALIDATION_ONEOF, - $context - )]; - } - - /** - * @param ValidationError $validationError - * @return array - */ - private function getPatternErrors($validationError) - { - $context = new \stdClass; - $context->field = $this->getFieldPath($validationError, ''); - $context->pattern = $validationError->getParameter(); - return [new Error( - \sprintf('The field %s must match the pattern %s', $context->field, $context->pattern), - ERROR::DATA_VALIDATION_UNIQUEITEMS, - $context - )]; - } - - /** - * @param ValidationError $validationError - * @return array - */ - private function getFormatErrors($validationError) - { - $context = new \stdClass; - $context->field = $this->getFieldPath($validationError, ''); - switch ($validationError->getParameter()) { - case 'date-time': - $message = \sprintf('The field %s must be a valid date, following RFC3339 (example: 2017-11-08T15:37:26+00:00)', $context->field); - break; - case 'uri': - $message = \sprintf('The field %s must be a valid URL', $context->field); - break; - case 'email': - $message = \sprintf('The field %s must be a valid email', $context->field); - break; - case 'ipv4': - $message = \sprintf('The field %s must be a valid ipv4', $context->field); - break; - case 'ipv6': - $message = \sprintf('The field %s must be a valid ipv6', $context->field); - break; - case 'hostname': - $message = \sprintf('The field %s must be a valid hostname', $context->field); - break; - default: - $message = 'Invalid format'; - } - return [new Error( - $message, - ERROR::DATA_VALIDATION_FORMAT, - $context - )]; - } - - /** - * @param $validationError - * @param $field - * @return string - */ - private function getFieldPath($validationError, $field) - { - $path = ''; - $dataPath = $validationError->getDataPath(); - if($dataPath !== '/') { - $path = \str_replace('/', '.',\substr($dataPath, 1)).'.'; - } - return rtrim($path.$field, '.'); - } -} \ No newline at end of file diff --git a/src/Validator/JsonValidator.php b/src/Validator/JsonValidator.php index eee3192..02a52e6 100644 --- a/src/Validator/JsonValidator.php +++ b/src/Validator/JsonValidator.php @@ -6,7 +6,7 @@ use League\JsonReference\Dereferencer; use RREST\Error; use RREST\Exception\InvalidJSONException; -use RREST\Validator\JsonGuardValidationErrorConverter; +use RREST\Validator\Util\JsonGuardValidationError\Converter; class JsonValidator { @@ -72,7 +72,7 @@ public function validate() foreach ($validator->errors() as $jsonGuardError) { $this->errors = \array_merge( $this->errors, - (new JsonGuardValidationErrorConverter($jsonGuardError))->getErrors() + (new Converter($jsonGuardError))->getErrors() ); } } diff --git a/src/Validator/Util/JsonGuardValidationError/AnyOfConverter.php b/src/Validator/Util/JsonGuardValidationError/AnyOfConverter.php new file mode 100644 index 0000000..6119625 --- /dev/null +++ b/src/Validator/Util/JsonGuardValidationError/AnyOfConverter.php @@ -0,0 +1,32 @@ +validationError->getParameter() as $subconstraint) { + if (isset($subconstraint->{Required::KEYWORD})) { + $fields = \array_merge($subconstraint->{Required::KEYWORD}, $fields); + } + } + + $context = new \stdClass; + $context->fields = $fields; + $errors[] = new Error( + \sprintf('The field %s is required', implode(' or/and ', $fields)), + Error::DATA_VALIDATION_REQUIRED_ANYOF, + $context + ); + return $errors; + } +} \ No newline at end of file diff --git a/src/Validator/Util/JsonGuardValidationError/Converter.php b/src/Validator/Util/JsonGuardValidationError/Converter.php new file mode 100644 index 0000000..ddec986 --- /dev/null +++ b/src/Validator/Util/JsonGuardValidationError/Converter.php @@ -0,0 +1,62 @@ +validationError = $validationError; + } + + /** + * @return Error[] + */ + public function getErrors() + { + $delegateConverter = $this->getDelegateConvert($this->validationError); + if ($delegateConverter instanceof ConverterInterface) { + return $delegateConverter->getErrors(); + } + + return [$this->getUnknowError()]; + } + + /** + * @param ValidationError $validationError + * @return ConverterInterface|null + */ + private function getDelegateConvert($validationError) + { + $converter = $this->validationError->getKeyword(); + $class = __NAMESPACE__ . '\\' . \ucfirst($converter) . 'Converter'; + if (\class_exists($class)) { + return new $class($validationError); + } + + return null; + } + + /** + * @return Error + */ + private function getUnknowError() + { + return new Error( + 'Data is not valid', + Error::DATA_VALIDATION_UNKNOW, + $this->validationError + ); + } +} \ No newline at end of file diff --git a/src/Validator/Util/JsonGuardValidationError/ConverterAbstract.php b/src/Validator/Util/JsonGuardValidationError/ConverterAbstract.php new file mode 100644 index 0000000..2f73c28 --- /dev/null +++ b/src/Validator/Util/JsonGuardValidationError/ConverterAbstract.php @@ -0,0 +1,36 @@ +validationError = $validationError; + } + + /** + * @param ValidationError $validationError + * @param string $field + * @return string + */ + protected function getFieldPath($validationError, $field) + { + $path = ''; + $dataPath = $validationError->getDataPath(); + if ($dataPath !== '/') { + $path = \str_replace('/', '.', \substr($dataPath, 1)) . '.'; + } + return rtrim($path . $field, '.'); + } +} \ No newline at end of file diff --git a/src/Validator/Util/JsonGuardValidationError/ConverterInterface.php b/src/Validator/Util/JsonGuardValidationError/ConverterInterface.php new file mode 100644 index 0000000..a3ecc29 --- /dev/null +++ b/src/Validator/Util/JsonGuardValidationError/ConverterInterface.php @@ -0,0 +1,13 @@ +field = $this->getFieldPath($this->validationError, ''); + $context->enum = $this->validationError->getParameter(); + $context->currentValue = $this->validationError->getData(); + return [new Error( + \sprintf('The field %s must be one of this values: %s', $context->field, implode(', ', $context->enum)), + Error::DATA_VALIDATION_ENUM, + $context + )]; + } +} \ No newline at end of file diff --git a/src/Validator/Util/JsonGuardValidationError/FormatConverter.php b/src/Validator/Util/JsonGuardValidationError/FormatConverter.php new file mode 100644 index 0000000..823b572 --- /dev/null +++ b/src/Validator/Util/JsonGuardValidationError/FormatConverter.php @@ -0,0 +1,45 @@ +field = $this->getFieldPath($this->validationError, ''); + $context->currentValue = $this->validationError->getData(); + switch ($this->validationError->getParameter()) { + case 'date-time': + $message = \sprintf('The field %s must be a valid date, following RFC3339 (example: 2017-11-08T15:37:26+00:00)', $context->field); + break; + case 'uri': + $message = \sprintf('The field %s must be a valid URL', $context->field); + break; + case 'email': + $message = \sprintf('The field %s must be a valid email', $context->field); + break; + case 'ipv4': + $message = \sprintf('The field %s must be a valid ipv4', $context->field); + break; + case 'ipv6': + $message = \sprintf('The field %s must be a valid ipv6', $context->field); + break; + case 'hostname': + $message = \sprintf('The field %s must be a valid hostname', $context->field); + break; + default: + $message = 'Invalid format'; + } + return [new Error( + $message, + Error::DATA_VALIDATION_FORMAT, + $context + )]; + } +} \ No newline at end of file diff --git a/src/Validator/Util/JsonGuardValidationError/MaxItemsConverter.php b/src/Validator/Util/JsonGuardValidationError/MaxItemsConverter.php new file mode 100644 index 0000000..3118ef6 --- /dev/null +++ b/src/Validator/Util/JsonGuardValidationError/MaxItemsConverter.php @@ -0,0 +1,24 @@ +field = $this->getFieldPath($this->validationError, ''); + $context->maxItems = $this->validationError->getParameter(); + $context->currentValues = $this->validationError->getData(); + return [new Error( + \sprintf('The field %s must contain less than %s item(s)', $context->field, $context->maxItems), + Error::DATA_VALIDATION_MAXITEMS, + $context + )]; + } +} \ No newline at end of file diff --git a/src/Validator/Util/JsonGuardValidationError/MaxLengthConverter.php b/src/Validator/Util/JsonGuardValidationError/MaxLengthConverter.php new file mode 100644 index 0000000..063778a --- /dev/null +++ b/src/Validator/Util/JsonGuardValidationError/MaxLengthConverter.php @@ -0,0 +1,24 @@ +field = $this->getFieldPath($this->validationError, ''); + $context->minLength = $this->validationError->getParameter(); + $context->currentValue = $this->validationError->getData(); + return [new Error( + \sprintf('The field %s must be less than %s characters long', $context->field, $context->maxLength), + Error::DATA_VALIDATION_MINLENGTH, + $context + )]; + } +} \ No newline at end of file diff --git a/src/Validator/Util/JsonGuardValidationError/MinItemsConverter.php b/src/Validator/Util/JsonGuardValidationError/MinItemsConverter.php new file mode 100644 index 0000000..3b39907 --- /dev/null +++ b/src/Validator/Util/JsonGuardValidationError/MinItemsConverter.php @@ -0,0 +1,24 @@ +field = $this->getFieldPath($this->validationError, ''); + $context->minItems = $this->validationError->getParameter(); + $context->currentValues = $this->validationError->getData(); + return [new Error( + \sprintf('The field %s must contain at least %s item(s)', $context->field, $context->minItems), + Error::DATA_VALIDATION_MINITEMS, + $context + )]; + } +} \ No newline at end of file diff --git a/src/Validator/Util/JsonGuardValidationError/MinLengthConverter.php b/src/Validator/Util/JsonGuardValidationError/MinLengthConverter.php new file mode 100644 index 0000000..c0af2e7 --- /dev/null +++ b/src/Validator/Util/JsonGuardValidationError/MinLengthConverter.php @@ -0,0 +1,24 @@ +field = $this->getFieldPath($this->validationError, ''); + $context->minLength = $this->validationError->getParameter(); + $context->currentValue = $this->validationError->getData(); + return [new Error( + \sprintf('The field %s must be at least %s characters long', $context->field, $context->minLength), + Error::DATA_VALIDATION_MINLENGTH, + $context + )]; + } +} \ No newline at end of file diff --git a/src/Validator/Util/JsonGuardValidationError/OneOfConverter.php b/src/Validator/Util/JsonGuardValidationError/OneOfConverter.php new file mode 100644 index 0000000..af95a59 --- /dev/null +++ b/src/Validator/Util/JsonGuardValidationError/OneOfConverter.php @@ -0,0 +1,24 @@ +field = $this->getFieldPath($this->validationError, ''); + $context->rules = $this->validationError->getParameter(); + $context->currentValue = $this->validationError->getData(); + return [new Error( + \sprintf('The field %s don\'t follow any rules', $context->field), + ERROR::DATA_VALIDATION_ONEOF, + $context + )]; + } +} \ No newline at end of file diff --git a/src/Validator/Util/JsonGuardValidationError/PatternConverter.php b/src/Validator/Util/JsonGuardValidationError/PatternConverter.php new file mode 100644 index 0000000..fd16e5d --- /dev/null +++ b/src/Validator/Util/JsonGuardValidationError/PatternConverter.php @@ -0,0 +1,24 @@ +field = $this->getFieldPath($this->validationError, ''); + $context->pattern = $this->validationError->getParameter(); + $context->currentValues = $this->validationError->getData(); + return [new Error( + \sprintf('The field %s must match the pattern %s', $context->field, $context->pattern), + Error::DATA_VALIDATION_UNIQUEITEMS, + $context + )]; + } +} diff --git a/src/Validator/Util/JsonGuardValidationError/RequiredConverter.php b/src/Validator/Util/JsonGuardValidationError/RequiredConverter.php new file mode 100644 index 0000000..0809636 --- /dev/null +++ b/src/Validator/Util/JsonGuardValidationError/RequiredConverter.php @@ -0,0 +1,26 @@ +validationError->getCause() as $field) { + $context = new \stdClass; + $context->field = $this->getFieldPath($this->validationError, $field); + $errors[] = new Error( + \sprintf('The field %s is required', $context->field), + Error::DATA_VALIDATION_REQUIRED, + $context + ); + } + return $errors; + } +} \ No newline at end of file diff --git a/src/Validator/Util/JsonGuardValidationError/TypeConverter.php b/src/Validator/Util/JsonGuardValidationError/TypeConverter.php new file mode 100644 index 0000000..5a41d38 --- /dev/null +++ b/src/Validator/Util/JsonGuardValidationError/TypeConverter.php @@ -0,0 +1,24 @@ +field = $this->getFieldPath($this->validationError, ''); + $context->type = $this->validationError->getParameter(); + $context->currentValue = $this->validationError->getData(); + return [new Error( + \sprintf('The type of the field %s is not valid, must be a/an %s', $context->field, implode(' or ', (array)$context->type)), + Error::DATA_VALIDATION_TYPE, + $context + )]; + } +} \ No newline at end of file diff --git a/src/Validator/Util/JsonGuardValidationError/UniqueItemsConverter.php b/src/Validator/Util/JsonGuardValidationError/UniqueItemsConverter.php new file mode 100644 index 0000000..e8b22bb --- /dev/null +++ b/src/Validator/Util/JsonGuardValidationError/UniqueItemsConverter.php @@ -0,0 +1,23 @@ +field = $this->getFieldPath($this->validationError, ''); + $context->currentValues = $this->validationError->getData(); + return [new Error( + \sprintf('The field %s must not contain duplicates values', $context->field), + Error::DATA_VALIDATION_UNIQUEITEMS, + $context + )]; + } +} diff --git a/tests/units/Validator/JsonValidator.php b/tests/units/Validator/JsonValidator.php new file mode 100644 index 0000000..1e5f8d1 --- /dev/null +++ b/tests/units/Validator/JsonValidator.php @@ -0,0 +1,20 @@ +given($jsonValidator) + ->boolean($this->testedInstance->fails()) + ->isFalse(); + } +} \ No newline at end of file From 2c53c677ec07976d5f057ffe9dd8da27984ee19c Mon Sep 17 00:00:00 2001 From: Fabien Furet Date: Thu, 9 Nov 2017 17:22:01 +0100 Subject: [PATCH 6/6] test: add more tests for the JsonGuard error conversion --- src/Error.php | 1 + src/Validator/JsonValidator.php | 8 +- .../AnyOfConverter.php | 2 +- .../Converter.php | 2 +- .../ConverterAbstract.php | 2 +- .../ConverterInterface.php | 2 +- .../EnumConverter.php | 2 +- .../FormatConverter.php | 9 +- .../MaxItemsConverter.php | 2 +- .../MaxLengthConverter.php | 6 +- .../MinItemsConverter.php | 2 +- .../MinLengthConverter.php | 2 +- .../OneOfConverter.php | 2 +- .../PatternConverter.php | 4 +- .../RequiredConverter.php | 2 +- .../TypeConverter.php | 2 +- .../UniqueItemsConverter.php | 2 +- tests/units/Validator/JsonValidator.php | 21 ++- .../AnyOfConverter.php | 38 +++++ .../Converter.php | 31 ++++ .../EnumConverter.php | 35 +++++ .../FormatConverter.php | 132 ++++++++++++++++++ .../MaxItemsConverter.php | 35 +++++ .../MaxLengthConverter.php | 35 +++++ .../MinItemsConverter.php | 35 +++++ .../MinLengthConverter.php | 35 +++++ .../OneOfConverter.php | 40 ++++++ .../PatternConverter.php | 35 +++++ .../RequiredConverter.php | 35 +++++ .../TypeConverter.php | 35 +++++ .../UniqueItemsConverter.php | 35 +++++ 31 files changed, 597 insertions(+), 32 deletions(-) rename src/Validator/Util/{JsonGuardValidationError => JsonGuardValidationErrorConverter}/AnyOfConverter.php (92%) rename src/Validator/Util/{JsonGuardValidationError => JsonGuardValidationErrorConverter}/Converter.php (95%) rename src/Validator/Util/{JsonGuardValidationError => JsonGuardValidationErrorConverter}/ConverterAbstract.php (92%) rename src/Validator/Util/{JsonGuardValidationError => JsonGuardValidationErrorConverter}/ConverterInterface.php (66%) rename src/Validator/Util/{JsonGuardValidationError => JsonGuardValidationErrorConverter}/EnumConverter.php (90%) rename src/Validator/Util/{JsonGuardValidationError => JsonGuardValidationErrorConverter}/FormatConverter.php (88%) rename src/Validator/Util/{JsonGuardValidationError => JsonGuardValidationErrorConverter}/MaxItemsConverter.php (90%) rename src/Validator/Util/{JsonGuardValidationError => JsonGuardValidationErrorConverter}/MaxLengthConverter.php (74%) rename src/Validator/Util/{JsonGuardValidationError => JsonGuardValidationErrorConverter}/MinItemsConverter.php (90%) rename src/Validator/Util/{JsonGuardValidationError => JsonGuardValidationErrorConverter}/MinLengthConverter.php (90%) rename src/Validator/Util/{JsonGuardValidationError => JsonGuardValidationErrorConverter}/OneOfConverter.php (89%) rename src/Validator/Util/{JsonGuardValidationError => JsonGuardValidationErrorConverter}/PatternConverter.php (83%) rename src/Validator/Util/{JsonGuardValidationError => JsonGuardValidationErrorConverter}/RequiredConverter.php (90%) rename src/Validator/Util/{JsonGuardValidationError => JsonGuardValidationErrorConverter}/TypeConverter.php (90%) rename src/Validator/Util/{JsonGuardValidationError => JsonGuardValidationErrorConverter}/UniqueItemsConverter.php (89%) create mode 100644 tests/units/Validator/Util/JsonGuardValidationErrorConverter/AnyOfConverter.php create mode 100644 tests/units/Validator/Util/JsonGuardValidationErrorConverter/Converter.php create mode 100644 tests/units/Validator/Util/JsonGuardValidationErrorConverter/EnumConverter.php create mode 100644 tests/units/Validator/Util/JsonGuardValidationErrorConverter/FormatConverter.php create mode 100644 tests/units/Validator/Util/JsonGuardValidationErrorConverter/MaxItemsConverter.php create mode 100644 tests/units/Validator/Util/JsonGuardValidationErrorConverter/MaxLengthConverter.php create mode 100644 tests/units/Validator/Util/JsonGuardValidationErrorConverter/MinItemsConverter.php create mode 100644 tests/units/Validator/Util/JsonGuardValidationErrorConverter/MinLengthConverter.php create mode 100644 tests/units/Validator/Util/JsonGuardValidationErrorConverter/OneOfConverter.php create mode 100644 tests/units/Validator/Util/JsonGuardValidationErrorConverter/PatternConverter.php create mode 100644 tests/units/Validator/Util/JsonGuardValidationErrorConverter/RequiredConverter.php create mode 100644 tests/units/Validator/Util/JsonGuardValidationErrorConverter/TypeConverter.php create mode 100644 tests/units/Validator/Util/JsonGuardValidationErrorConverter/UniqueItemsConverter.php diff --git a/src/Error.php b/src/Error.php index f632406..940f149 100644 --- a/src/Error.php +++ b/src/Error.php @@ -17,6 +17,7 @@ class Error const DATA_VALIDATION_MAXITEMS = 'DATA_VALIDATION_MAXITEMS'; const DATA_VALIDATION_UNIQUEITEMS = 'DATA_VALIDATION_UNIQUEITEMS'; const DATA_VALIDATION_ONEOF = 'DATA_VALIDATION_ONEOF'; + const DATA_VALIDATION_PATTERN = 'DATA_VALIDATION_PATTERN'; /** diff --git a/src/Validator/JsonValidator.php b/src/Validator/JsonValidator.php index 02a52e6..2f3631e 100644 --- a/src/Validator/JsonValidator.php +++ b/src/Validator/JsonValidator.php @@ -6,7 +6,7 @@ use League\JsonReference\Dereferencer; use RREST\Error; use RREST\Exception\InvalidJSONException; -use RREST\Validator\Util\JsonGuardValidationError\Converter; +use RREST\Validator\Util\JsonGuardValidationErrorConverter\Converter; class JsonValidator { @@ -87,10 +87,8 @@ private function getJsonFromString($value) { $json = json_decode($value); if (json_last_error() !== JSON_ERROR_NONE) { - throw new InvalidJSONException([new Error( - ucfirst(json_last_error_msg()), - Error::INVALID_JSON - )]); + $error = new Error(ucfirst(json_last_error_msg()), Error::INVALID_JSON); + throw new InvalidJSONException([$error]); } return $json; diff --git a/src/Validator/Util/JsonGuardValidationError/AnyOfConverter.php b/src/Validator/Util/JsonGuardValidationErrorConverter/AnyOfConverter.php similarity index 92% rename from src/Validator/Util/JsonGuardValidationError/AnyOfConverter.php rename to src/Validator/Util/JsonGuardValidationErrorConverter/AnyOfConverter.php index 6119625..41d7fe8 100644 --- a/src/Validator/Util/JsonGuardValidationError/AnyOfConverter.php +++ b/src/Validator/Util/JsonGuardValidationErrorConverter/AnyOfConverter.php @@ -1,6 +1,6 @@ field = $this->getFieldPath($this->validationError, ''); - $context->minLength = $this->validationError->getParameter(); + $context->maxLength = $this->validationError->getParameter(); $context->currentValue = $this->validationError->getData(); return [new Error( \sprintf('The field %s must be less than %s characters long', $context->field, $context->maxLength), - Error::DATA_VALIDATION_MINLENGTH, + Error::DATA_VALIDATION_MAXLENGTH, $context )]; } diff --git a/src/Validator/Util/JsonGuardValidationError/MinItemsConverter.php b/src/Validator/Util/JsonGuardValidationErrorConverter/MinItemsConverter.php similarity index 90% rename from src/Validator/Util/JsonGuardValidationError/MinItemsConverter.php rename to src/Validator/Util/JsonGuardValidationErrorConverter/MinItemsConverter.php index 3b39907..8e4bae3 100644 --- a/src/Validator/Util/JsonGuardValidationError/MinItemsConverter.php +++ b/src/Validator/Util/JsonGuardValidationErrorConverter/MinItemsConverter.php @@ -1,6 +1,6 @@ currentValues = $this->validationError->getData(); return [new Error( \sprintf('The field %s must match the pattern %s', $context->field, $context->pattern), - Error::DATA_VALIDATION_UNIQUEITEMS, + Error::DATA_VALIDATION_PATTERN, $context )]; } diff --git a/src/Validator/Util/JsonGuardValidationError/RequiredConverter.php b/src/Validator/Util/JsonGuardValidationErrorConverter/RequiredConverter.php similarity index 90% rename from src/Validator/Util/JsonGuardValidationError/RequiredConverter.php rename to src/Validator/Util/JsonGuardValidationErrorConverter/RequiredConverter.php index 0809636..10d87f5 100644 --- a/src/Validator/Util/JsonGuardValidationError/RequiredConverter.php +++ b/src/Validator/Util/JsonGuardValidationErrorConverter/RequiredConverter.php @@ -1,6 +1,6 @@ given($jsonValidator) + ->given($this->newTestedInstance('{}', '{} ')) ->boolean($this->testedInstance->fails()) ->isFalse(); + + $schema = file_get_contents(__DIR__ . '/../../fixture/song.json'); + $this + ->given($this->newTestedInstance('{}', $schema)) + ->boolean($this->testedInstance->fails()) + ->isTrue(); + + + $this + ->exception( + function () { + $this->newTestedInstance('}', '}')->fails(); + } + ) + ->isInstanceOf('\RREST\Exception\InvalidJSONException'); } } \ No newline at end of file diff --git a/tests/units/Validator/Util/JsonGuardValidationErrorConverter/AnyOfConverter.php b/tests/units/Validator/Util/JsonGuardValidationErrorConverter/AnyOfConverter.php new file mode 100644 index 0000000..de6346d --- /dev/null +++ b/tests/units/Validator/Util/JsonGuardValidationErrorConverter/AnyOfConverter.php @@ -0,0 +1,38 @@ +errors()[0]; + + $this + ->given($this->newTestedInstance($error)) + ->array($this->testedInstance->getErrors()) + ->hasSize(1) + ->values + ->object[0]->isInstanceOf('\RREST\Error') + ->string($this->testedInstance->getErrors()[0]->code)->isEqualTo(Error::DATA_VALIDATION_REQUIRED_ANYOF); + } +} \ No newline at end of file diff --git a/tests/units/Validator/Util/JsonGuardValidationErrorConverter/Converter.php b/tests/units/Validator/Util/JsonGuardValidationErrorConverter/Converter.php new file mode 100644 index 0000000..be8e027 --- /dev/null +++ b/tests/units/Validator/Util/JsonGuardValidationErrorConverter/Converter.php @@ -0,0 +1,31 @@ +given($this->newTestedInstance( + new \League\JsonGuard\ValidationError('a','b','c','d','e','f','g') + )) + ->array($this->testedInstance->getErrors()) + ->hasSize(1) + ->values + ->object[0]->isInstanceOf('\RREST\Error'); + + $this + ->given($this->newTestedInstance( + new \League\JsonGuard\ValidationError('a',Required::KEYWORD,'c',['d'],'e','f','g') + )) + ->array($this->testedInstance->getErrors()) + ->hasSize(1) + ; + } +} \ No newline at end of file diff --git a/tests/units/Validator/Util/JsonGuardValidationErrorConverter/EnumConverter.php b/tests/units/Validator/Util/JsonGuardValidationErrorConverter/EnumConverter.php new file mode 100644 index 0000000..255ae5d --- /dev/null +++ b/tests/units/Validator/Util/JsonGuardValidationErrorConverter/EnumConverter.php @@ -0,0 +1,35 @@ +errors()[0]; + + $this + ->given($this->newTestedInstance($error)) + ->array($this->testedInstance->getErrors()) + ->hasSize(1) + ->values + ->object[0]->isInstanceOf('\RREST\Error') + ->string($this->testedInstance->getErrors()[0]->code)->isEqualTo(Error::DATA_VALIDATION_ENUM); + } +} \ No newline at end of file diff --git a/tests/units/Validator/Util/JsonGuardValidationErrorConverter/FormatConverter.php b/tests/units/Validator/Util/JsonGuardValidationErrorConverter/FormatConverter.php new file mode 100644 index 0000000..6133832 --- /dev/null +++ b/tests/units/Validator/Util/JsonGuardValidationErrorConverter/FormatConverter.php @@ -0,0 +1,132 @@ +errors()[0]; + + $this + ->given($this->newTestedInstance($error)) + ->array($this->testedInstance->getErrors()) + ->hasSize(1) + ->values + ->object[0]->isInstanceOf('\RREST\Error') + ->string($this->testedInstance->getErrors()[0]->code)->isEqualTo(Error::DATA_VALIDATION_FORMAT); + + //uri + $schema = json_decode('{ + "$schema": "http://json-schema.org/schema", + "type": "object", + "properties": { + "artist": { "type": "string", "format": "uri" } + }, + "required": [ "artist" ] + }'); + $validator = new Validator($json, $schema); + $error = $validator->errors()[0]; + + $this + ->given($this->newTestedInstance($error)) + ->array($this->testedInstance->getErrors()) + ->hasSize(1) + ->values + ->object[0]->isInstanceOf('\RREST\Error'); + + //ipv4 + $schema = json_decode('{ + "$schema": "http://json-schema.org/schema", + "type": "object", + "properties": { + "artist": { "type": "string", "format": "ipv4" } + }, + "required": [ "artist" ] + }'); + $validator = new Validator($json, $schema); + $error = $validator->errors()[0]; + + $this + ->given($this->newTestedInstance($error)) + ->array($this->testedInstance->getErrors()) + ->hasSize(1) + ->values + ->object[0]->isInstanceOf('\RREST\Error'); + + //ipv6 + $schema = json_decode('{ + "$schema": "http://json-schema.org/schema", + "type": "object", + "properties": { + "artist": { "type": "string", "format": "ipv6" } + }, + "required": [ "artist" ] + }'); + $validator = new Validator($json, $schema); + $error = $validator->errors()[0]; + + $this + ->given($this->newTestedInstance($error)) + ->array($this->testedInstance->getErrors()) + ->hasSize(1) + ->values + ->object[0]->isInstanceOf('\RREST\Error'); + + //date-time + $schema = json_decode('{ + "$schema": "http://json-schema.org/schema", + "type": "object", + "properties": { + "artist": { "type": "string", "format": "date-time" } + }, + "required": [ "artist" ] + }'); + $validator = new Validator($json, $schema); + $error = $validator->errors()[0]; + + $this + ->given($this->newTestedInstance($error)) + ->array($this->testedInstance->getErrors()) + ->hasSize(1) + ->values + ->object[0]->isInstanceOf('\RREST\Error'); + + //hostname + $schema = json_decode('{ + "$schema": "http://json-schema.org/schema", + "type": "object", + "properties": { + "artist": { "type": "string", "format": "hostname" } + }, + "required": [ "artist" ] + }'); + $validator = new Validator($json, $schema); + $error = $validator->errors()[0]; + + $this + ->given($this->newTestedInstance($error)) + ->array($this->testedInstance->getErrors()) + ->hasSize(1) + ->values + ->object[0]->isInstanceOf('\RREST\Error'); + + } +} \ No newline at end of file diff --git a/tests/units/Validator/Util/JsonGuardValidationErrorConverter/MaxItemsConverter.php b/tests/units/Validator/Util/JsonGuardValidationErrorConverter/MaxItemsConverter.php new file mode 100644 index 0000000..0e9914d --- /dev/null +++ b/tests/units/Validator/Util/JsonGuardValidationErrorConverter/MaxItemsConverter.php @@ -0,0 +1,35 @@ +errors()[0]; + + $this + ->given($this->newTestedInstance($error)) + ->array($this->testedInstance->getErrors()) + ->hasSize(1) + ->values + ->object[0]->isInstanceOf('\RREST\Error') + ->string($this->testedInstance->getErrors()[0]->code)->isEqualTo(Error::DATA_VALIDATION_MAXITEMS); + } +} \ No newline at end of file diff --git a/tests/units/Validator/Util/JsonGuardValidationErrorConverter/MaxLengthConverter.php b/tests/units/Validator/Util/JsonGuardValidationErrorConverter/MaxLengthConverter.php new file mode 100644 index 0000000..a42272c --- /dev/null +++ b/tests/units/Validator/Util/JsonGuardValidationErrorConverter/MaxLengthConverter.php @@ -0,0 +1,35 @@ +errors()[0]; + + $this + ->given($this->newTestedInstance($error)) + ->array($this->testedInstance->getErrors()) + ->hasSize(1) + ->values + ->object[0]->isInstanceOf('\RREST\Error') + ->string($this->testedInstance->getErrors()[0]->code)->isEqualTo(Error::DATA_VALIDATION_MAXLENGTH); + } +} \ No newline at end of file diff --git a/tests/units/Validator/Util/JsonGuardValidationErrorConverter/MinItemsConverter.php b/tests/units/Validator/Util/JsonGuardValidationErrorConverter/MinItemsConverter.php new file mode 100644 index 0000000..34319e5 --- /dev/null +++ b/tests/units/Validator/Util/JsonGuardValidationErrorConverter/MinItemsConverter.php @@ -0,0 +1,35 @@ +errors()[0]; + + $this + ->given($this->newTestedInstance($error)) + ->array($this->testedInstance->getErrors()) + ->hasSize(1) + ->values + ->object[0]->isInstanceOf('\RREST\Error') + ->string($this->testedInstance->getErrors()[0]->code)->isEqualTo(Error::DATA_VALIDATION_MINITEMS); + } +} \ No newline at end of file diff --git a/tests/units/Validator/Util/JsonGuardValidationErrorConverter/MinLengthConverter.php b/tests/units/Validator/Util/JsonGuardValidationErrorConverter/MinLengthConverter.php new file mode 100644 index 0000000..f684aa3 --- /dev/null +++ b/tests/units/Validator/Util/JsonGuardValidationErrorConverter/MinLengthConverter.php @@ -0,0 +1,35 @@ +errors()[0]; + + $this + ->given($this->newTestedInstance($error)) + ->array($this->testedInstance->getErrors()) + ->hasSize(1) + ->values + ->object[0]->isInstanceOf('\RREST\Error') + ->string($this->testedInstance->getErrors()[0]->code)->isEqualTo(Error::DATA_VALIDATION_MINLENGTH); + } +} \ No newline at end of file diff --git a/tests/units/Validator/Util/JsonGuardValidationErrorConverter/OneOfConverter.php b/tests/units/Validator/Util/JsonGuardValidationErrorConverter/OneOfConverter.php new file mode 100644 index 0000000..bee8a97 --- /dev/null +++ b/tests/units/Validator/Util/JsonGuardValidationErrorConverter/OneOfConverter.php @@ -0,0 +1,40 @@ +errors()[0]; + + $this + ->given($this->newTestedInstance($error)) + ->array($this->testedInstance->getErrors()) + ->hasSize(1) + ->values + ->object[0]->isInstanceOf('\RREST\Error') + ->string($this->testedInstance->getErrors()[0]->code)->isEqualTo(Error::DATA_VALIDATION_ONEOF); + } +} \ No newline at end of file diff --git a/tests/units/Validator/Util/JsonGuardValidationErrorConverter/PatternConverter.php b/tests/units/Validator/Util/JsonGuardValidationErrorConverter/PatternConverter.php new file mode 100644 index 0000000..0d4d662 --- /dev/null +++ b/tests/units/Validator/Util/JsonGuardValidationErrorConverter/PatternConverter.php @@ -0,0 +1,35 @@ +errors()[0]; + + $this + ->given($this->newTestedInstance($error)) + ->array($this->testedInstance->getErrors()) + ->hasSize(1) + ->values + ->object[0]->isInstanceOf('\RREST\Error') + ->string($this->testedInstance->getErrors()[0]->code)->isEqualTo(Error::DATA_VALIDATION_PATTERN); + } +} \ No newline at end of file diff --git a/tests/units/Validator/Util/JsonGuardValidationErrorConverter/RequiredConverter.php b/tests/units/Validator/Util/JsonGuardValidationErrorConverter/RequiredConverter.php new file mode 100644 index 0000000..a11c74b --- /dev/null +++ b/tests/units/Validator/Util/JsonGuardValidationErrorConverter/RequiredConverter.php @@ -0,0 +1,35 @@ +errors()[0]; + + $this + ->given($this->newTestedInstance($error)) + ->array($this->testedInstance->getErrors()) + ->hasSize(1) + ->values + ->object[0]->isInstanceOf('\RREST\Error') + ->string($this->testedInstance->getErrors()[0]->code)->isEqualTo(Error::DATA_VALIDATION_REQUIRED); + } +} \ No newline at end of file diff --git a/tests/units/Validator/Util/JsonGuardValidationErrorConverter/TypeConverter.php b/tests/units/Validator/Util/JsonGuardValidationErrorConverter/TypeConverter.php new file mode 100644 index 0000000..1707e28 --- /dev/null +++ b/tests/units/Validator/Util/JsonGuardValidationErrorConverter/TypeConverter.php @@ -0,0 +1,35 @@ +errors()[0]; + + $this + ->given($this->newTestedInstance($error)) + ->array($this->testedInstance->getErrors()) + ->hasSize(1) + ->values + ->object[0]->isInstanceOf('\RREST\Error') + ->string($this->testedInstance->getErrors()[0]->code)->isEqualTo(Error::DATA_VALIDATION_TYPE); + } +} \ No newline at end of file diff --git a/tests/units/Validator/Util/JsonGuardValidationErrorConverter/UniqueItemsConverter.php b/tests/units/Validator/Util/JsonGuardValidationErrorConverter/UniqueItemsConverter.php new file mode 100644 index 0000000..117b027 --- /dev/null +++ b/tests/units/Validator/Util/JsonGuardValidationErrorConverter/UniqueItemsConverter.php @@ -0,0 +1,35 @@ +errors()[0]; + + $this + ->given($this->newTestedInstance($error)) + ->array($this->testedInstance->getErrors()) + ->hasSize(1) + ->values + ->object[0]->isInstanceOf('\RREST\Error') + ->string($this->testedInstance->getErrors()[0]->code)->isEqualTo(Error::DATA_VALIDATION_UNIQUEITEMS); + } +} \ No newline at end of file