Skip to content
This repository has been archived by the owner on Dec 20, 2021. It is now read-only.

Commit

Permalink
Merge 2c53c67 into 14f6e33
Browse files Browse the repository at this point in the history
  • Loading branch information
RETFU committed Nov 9, 2017
2 parents 14f6e33 + 2c53c67 commit 085c1c5
Show file tree
Hide file tree
Showing 37 changed files with 1,161 additions and 106 deletions.
10 changes: 10 additions & 0 deletions .atoum.php
Original file line number Diff line number Diff line change
Expand Up @@ -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');

Expand All @@ -27,3 +32,8 @@


$report = $script->addDefaultReport();

$coverage = new coverage\html();
$coverage->addWriter(new std\out());
$coverage->setOutPutDirectory(__DIR__ . '/coverage');
$runner->addReport($coverage);
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/vendor/
.idea
composer.lock
coverage
6 changes: 4 additions & 2 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand All @@ -34,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": [
Expand Down
20 changes: 18 additions & 2 deletions src/Error.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,22 @@

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';
const DATA_VALIDATION_PATTERN = 'DATA_VALIDATION_PATTERN';


/**
* @var string
*/
Expand All @@ -24,9 +40,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;
Expand Down
62 changes: 7 additions & 55 deletions src/RREST.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -411,63 +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();
$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)
Expand Down
52 changes: 7 additions & 45 deletions src/Response.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -178,7 +179,7 @@ public function setContent($content)
$this->assertReponseSchema(
$this->getFormat(),
$this->getSchema(),
$content
\json_encode($content)
);
}

Expand Down Expand Up @@ -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()
);
}
}
}
97 changes: 97 additions & 0 deletions src/Validator/JsonValidator.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
<?php

namespace RREST\Validator;

use League\JsonGuard\Validator;
use League\JsonReference\Dereferencer;
use RREST\Error;
use RREST\Exception\InvalidJSONException;
use RREST\Validator\Util\JsonGuardValidationErrorConverter\Converter;

class JsonValidator
{
/**
* @var Error[]
*/
private $errors = [];

/**
* @var bool
*/
private $isValidated = false;

/**
* @var string
*/
private $jsonValue;

/**
* @var string
*/
private $jsonSchema;

/**
* @param string $jsonValue
* @param string $jsonSchema
*/
public function __construct($jsonValue, $jsonSchema)
{
$this->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 Converter($jsonGuardError))->getErrors()
);
}
}
}

/**
* @throws InvalidJSONException
* @param $value
* @return \stdClass|array
*/
private function getJsonFromString($value)
{
$json = json_decode($value);
if (json_last_error() !== JSON_ERROR_NONE) {
$error = new Error(ucfirst(json_last_error_msg()), Error::INVALID_JSON);
throw new InvalidJSONException([$error]);
}

return $json;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<?php

namespace RREST\Validator\Util\JsonGuardValidationErrorConverter;

use League\JsonGuard\Constraint\DraftFour\Required;
use RREST\Error;

class AnyOfConverter extends ConverterAbstract
{
/**
* @inheritdoc
*/
public function getErrors()
{
$errors = [];
$fields = [];
foreach ($this->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;
}
}
Loading

0 comments on commit 085c1c5

Please sign in to comment.