Skip to content

Commit

Permalink
added validation attribute
Browse files Browse the repository at this point in the history
  • Loading branch information
ytake committed Jan 15, 2018
1 parent 3de24c2 commit 68cad2c
Show file tree
Hide file tree
Showing 11 changed files with 219 additions and 42 deletions.
1 change: 1 addition & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
"ext-asio": "*",
"hhvm/hhvm-autoload": "^1.5",
"hhvm/hsl": "^1.0.0",
"hhvm/type-assert": "^3.2.0",
"hack-psr/psr7-http-message-hhi": "^1.0.0",
"ytake/hh-container": "^0.5",
"ytake/hh-config-aggregator": "^0.1.1",
Expand Down
15 changes: 8 additions & 7 deletions src/Foundation/Middleware/Dispatcher.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
use ReflectionMethod;
use Ytake\Heredity\Heredity;
use Nazg\Foundation\Validation\Attribute;
use Nazg\Foundation\Validation\Validation;
use Nazg\Foundation\Validation\Validator;
use Nazg\Foundation\Validation\ValidatorFactory;
use Interop\Http\Server\MiddlewareInterface;
use Interop\Http\Server\RequestHandlerInterface;
Expand All @@ -27,22 +27,23 @@ protected function processor(
MiddlewareInterface $middleware,
ServerRequestInterface $request
): ResponseInterface {
$this->findAttributse($middleware, $request);
$this->validateInterceptor($middleware, $request);
return $middleware->process($request, $this);
}

protected function findAttributse(
protected function validateInterceptor(
MiddlewareInterface $middleware,
ServerRequestInterface $request
): void {
$rm = new ReflectionMethod($middleware, InterceptorMethod::Process);
$attribute = $rm->getAttribute(Attribute::Named);
if (is_array($attribute)) {
if(array_key_exists($this->validatorIndex, $attribute)) {
$v = new ValidatorFactory(
$this->container?->get((string)$attribute[$this->validatorIndex])
);
var_dump($v->validate());
$validator = $this->container?->get((string)$attribute[$this->validatorIndex]);
if($validator instanceof Validator) {
$factory = new ValidatorFactory($validator, $request);
$factory->validator()->validate();
}
}
}
}
Expand Down
21 changes: 0 additions & 21 deletions src/Foundation/Validation/Validation.php

This file was deleted.

21 changes: 21 additions & 0 deletions src/Foundation/Validation/ValidationException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<?hh // strict

namespace Nazg\Foundation\Validation;

use Exception;

class ValidationException extends Exception {

protected int $status = 400;

protected Validator $validator;

public function __construct(Validator $validator) {
parent::__construct('The given data was invalid.');
$this->validator = $validator;
}

public function errors(): array<string> {
return $this->validator->errors()->toArray();
}
}
55 changes: 55 additions & 0 deletions src/Foundation/Validation/Validator.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
<?hh // strict

namespace Nazg\Foundation\Validation;

use Psr\Http\Message\ServerRequestInterface;
use Facebook\TypeAssert;

enum Attribute: string as string {
Named = 'RequestValidation';
}

<<__ConsistentConstruct>>
abstract class Validator {

protected ?ServerRequestInterface $request;

protected Vector<string> $messages = Vector{};

protected Vector<string> $validateMethods = Vector{};

protected bool $shouldThrowException = false;

// disabled type assert for request parameters
protected bool $skipValidateStructure = true;

public function validateRequest(ServerRequestInterface $request): void {
$this->request = $request;
}

public function validate(): bool {
if (!is_null($this->request)) {
$this->assertStructure();
}
if($this->errors()->count()) {
if ($this->shouldThrowException) {
throw new ValidationException($this);
}
return false;
}
return true;
}

<<__Memoize>>
public function errors(): ImmVector<string> {
return $this->assertValidateResult()->immutable();
}

protected function assertStructure(): void {
if(!$this->skipValidateStructure) {
// here
}
}

abstract protected function assertValidateResult(): Vector<string>;
}
14 changes: 8 additions & 6 deletions src/Foundation/Validation/ValidatorFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,18 @@

namespace Nazg\Foundation\Validation;

use Psr\Http\Message\ServerRequestInterface;

class ValidatorFactory {

public function __construct(
protected mixed $validatorName
protected Validator $validatorName,
protected ServerRequestInterface $request
) {}

public function validate(): bool {
if($this->validatorName instanceof Validation) {
return $this->validatorName->validate();
}
return true;
public function validator(): Validator {
$validator = $this->validatorName;
$validator->validateRequest($this->request);
return $validator;
}
}
3 changes: 2 additions & 1 deletion tests/Action/ValidateAction.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@
use Zend\Diactoros\Response\JsonResponse;

final class ValidateAction implements MiddlewareInterface {
<<RequestValidation(\ValidateActionValid::class)>>

<<RequestValidation(\MockValidateActionFaild::class)>>
public function process(
ServerRequestInterface $request,
RequestHandlerInterface $handler,
Expand Down
3 changes: 3 additions & 0 deletions tests/ApplicationTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@ public function testShouldReturnQueryParam(): void {
);
}

/**
* @expectedException \Nazg\Foundation\Validation\ValidationException
*/
public function testShouldBeValidationFaild():void {
$aggregator = new ConfigAggreagator([
new PhpFileProvider(__DIR__ . '/config/*.{hh,php}'),
Expand Down
13 changes: 13 additions & 0 deletions tests/Validation/MockValidateActionFaild.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?hh // strict

use Nazg\Foundation\Validation\Validator;
use Psr\Http\Message\ServerRequestInterface;

final class MockValidateActionFaild extends Validator {

protected bool $shouldThrowException = true;

protected function assertValidateResult(): Vector<string> {
return new Vector(['error1']);
}
}
7 changes: 0 additions & 7 deletions tests/Validation/ValidateActionValid.php

This file was deleted.

108 changes: 108 additions & 0 deletions tests/Validation/ValidatorTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
<?hh

use Facebook\TypeAssert;
use PHPUnit\Framework\TestCase;
use Nazg\Foundation\Validation\Validator;
use Zend\Diactoros\ServerRequestFactory;

class ValidatorTest extends TestCase {

public function testShouldBeValidatorFailed(): void {
$v = new MockValidateFalied();
$this->assertNotCount(0, $v->errors());
$this->assertFalse($v->validate());
}

/**
* @expectedException \Facebook\TypeAssert\IncorrectTypeException
*/
public function testShouldThrowTypeException(): void {
$v = new MockStructureValidateFalied();
$request = ServerRequestFactory::fromGlobals([
'REQUEST_URI' => '/testing/12?message=testing&server=hhvm',
'QUERY_STRING' => 'message=testing&server=hhvm',
'REQUEST_METHOD' => 'GET'
],
[
'message' => 'testing',
'server' => 'hhvm',
]);
$v->validateRequest($request);
$v->validate();
}

public function testShouldThrow(): void {
$v = new MockStructureValidateSuccess();
$request = ServerRequestFactory::fromGlobals([
'REQUEST_URI' => '/testing/12?message=testing&server=hhvm',
'QUERY_STRING' => 'message=testing&server=hhvm',
'REQUEST_METHOD' => 'GET'
],
[
'message' => 'testing',
'server' => 'hhvm',
]);
$v->validateRequest($request);
$this->assertTrue($v->validate());
}
}

class MockValidateFalied extends Validator {

protected bool $shouldThrowException = false;

protected function assertValidateResult(): Vector<string> {
return new Vector(['error1']);
}
}

class MockStructureValidateFalied extends Validator {

const type TestRequest = shape(
'message' => string,
'server' => int,
);

protected bool $shouldThrowException = false;
protected bool $skipValidateStructure = false;
protected function assertValidateResult(): Vector<string> {
return new Vector(['error1']);
}

protected function assertStructure(): void {
if(!$this->skipValidateStructure) {
TypeAssert\matches_type_structure(
type_structure(self::class, 'TestRequest'),
$this->request->getQueryParams(),
);
}
}
}

class MockStructureValidateSuccess extends Validator {

const type TestRequest = shape(
'message' => string,
'server' => string,
);

protected bool $shouldThrowException = false;
protected bool $skipValidateStructure = false;
protected function assertValidateResult(): Vector<string> {
$params = $this->request->getQueryParams();
$v = Vector{};
if ($params['message'] !== 'testing') {
$v = $v->concat(['error!']);
}
return $v;
}

protected function assertStructure(): void {
if(!$this->skipValidateStructure) {
TypeAssert\matches_type_structure(
type_structure(self::class, 'TestRequest'),
$this->request->getQueryParams(),
);
}
}
}

0 comments on commit 68cad2c

Please sign in to comment.