Wire up new error handling stack

This commit is contained in:
Franz Liedke 2019-08-09 20:38:51 +02:00
parent 3417f5a77e
commit cfbaa84fbc
No known key found for this signature in database
GPG Key ID: 9A0231A879B055F4
7 changed files with 253 additions and 0 deletions

View File

@ -0,0 +1,37 @@
<?php
/*
* This file is part of Flarum.
*
* (c) Toby Zerner <toby.zerner@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Flarum\Foundation\ErrorHandling\ExceptionHandler;
use Flarum\Foundation\ErrorHandling\HandledError;
use Illuminate\Validation\ValidationException;
class IlluminateValidationExceptionHandler
{
public function handle(ValidationException $e): HandledError
{
return (new HandledError(
$e, 'validation_error', 422
))->withDetails($this->errorDetails($e));
}
protected function errorDetails(ValidationException $e): array
{
$errors = $e->errors();
return array_map(function ($field, $messages) {
return [
'detail' => implode("\n", $messages),
'source' => ['pointer' => "/data/attributes/$field"]
];
}, array_keys($errors), $errors);
}
}

View File

@ -0,0 +1,38 @@
<?php
/*
* This file is part of Flarum.
*
* (c) Toby Zerner <toby.zerner@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Flarum\Foundation\ErrorHandling\ExceptionHandler;
use Flarum\Foundation\ErrorHandling\HandledError;
use Flarum\Foundation\ValidationException;
class ValidationExceptionHandler
{
public function handle(ValidationException $e)
{
return (new HandledError(
$e, 'validation_error', 422
))->withDetails(array_merge(
$this->buildDetails($e->getAttributes(), '/data/attributes'),
$this->buildDetails($e->getRelationships(), '/data/relationships')
));
}
private function buildDetails(array $messages, $pointer): array
{
return array_map(function ($path, $detail) use ($pointer) {
return [
'detail' => $detail,
'source' => ['pointer' => $pointer.'/'.$path]
];
}, array_keys($messages), $messages);
}
}

View File

@ -0,0 +1,76 @@
<?php
/*
* This file is part of Flarum.
*
* (c) Toby Zerner <toby.zerner@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Flarum\Foundation;
use Flarum\Foundation\ErrorHandling\ExceptionHandler;
use Flarum\Foundation\ErrorHandling\LogReporter;
use Flarum\Foundation\ErrorHandling\Registry;
use Flarum\Foundation\ErrorHandling\Reporter;
use Illuminate\Database\Eloquent\ModelNotFoundException;
use Illuminate\Validation\ValidationException as IlluminateValidationException;
use Tobscure\JsonApi\Exception\InvalidParameterException;
class ErrorServiceProvider extends AbstractServiceProvider
{
public function register()
{
$this->app->singleton('flarum.error.statuses', function () {
return [
// 400 Bad Request
'csrf_token_mismatch' => 400,
'invalid_parameter' => 400,
// 401 Unauthorized
'invalid_access_token' => 401,
// 403 Forbidden
'forbidden' => 403,
'invalid_confirmation_token' => 403,
'permission_denied' => 403,
// 404 Not Found
'model_not_found' => 404,
'route_not_found' => 404,
// 405 Method Not Allowed
'method_not_allowed' => 405,
// 429 Too Many Requests
'too_many_requests' => 429,
];
});
$this->app->singleton('flarum.error.classes', function () {
return [
InvalidParameterException::class => 'invalid_parameter',
ModelNotFoundException::class => 'model_not_found',
];
});
$this->app->singleton('flarum.error.handlers', function () {
return [
IlluminateValidationException::class => ExceptionHandler\IlluminateValidationExceptionHandler::class,
ValidationException::class => ExceptionHandler\ValidationExceptionHandler::class,
];
});
$this->app->singleton(Registry::class, function () {
return new Registry(
$this->app->make('flarum.error.statuses'),
$this->app->make('flarum.error.classes'),
$this->app->make('flarum.error.handlers')
);
});
$this->app->singleton(Reporter::class, LogReporter::class);
}
}

View File

@ -117,6 +117,7 @@ class InstalledSite implements SiteInterface
$laravel->register(DatabaseServiceProvider::class);
$laravel->register(DiscussionServiceProvider::class);
$laravel->register(ExtensionServiceProvider::class);
$laravel->register(ErrorServiceProvider::class);
$laravel->register(FilesystemServiceProvider::class);
$laravel->register(FormatterServiceProvider::class);
$laravel->register(ForumServiceProvider::class);

View File

@ -69,6 +69,7 @@ class UninstalledSite implements SiteInterface
$this->registerLogger($laravel);
$laravel->register(ErrorServiceProvider::class);
$laravel->register(LocaleServiceProvider::class);
$laravel->register(FilesystemServiceProvider::class);
$laravel->register(SessionServiceProvider::class);

View File

@ -0,0 +1,53 @@
<?php
/*
* This file is part of Flarum.
*
* (c) Toby Zerner <toby.zerner@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Flarum\Tests\unit\Foundation\ErrorHandling\ExceptionHandler;
use Flarum\Foundation\ErrorHandling\ExceptionHandler\IlluminateValidationExceptionHandler;
use Illuminate\Translation\ArrayLoader;
use Illuminate\Translation\Translator;
use Illuminate\Validation\Factory;
use Illuminate\Validation\ValidationException;
use PHPUnit\Framework\TestCase;
class IlluminateValidationExceptionHandlerTest extends TestCase
{
private $handler;
public function setUp()
{
$this->handler = new IlluminateValidationExceptionHandler;
}
public function test_it_creates_the_desired_output()
{
$exception = new ValidationException($this->makeValidator(['foo' => ''], ['foo' => 'required']));
$error = $this->handler->handle($exception);
$this->assertEquals(422, $error->getStatusCode());
$this->assertEquals('validation_error', $error->getType());
$this->assertEquals([
[
'detail' => 'validation.required',
'source' => ['pointer' => '/data/attributes/foo']
]
], $error->getDetails());
}
private function makeValidator($data = [], $rules = [])
{
$translator = new Translator(new ArrayLoader(), 'en');
$factory = new Factory($translator);
return $factory->make($data, $rules);
}
}

View File

@ -0,0 +1,47 @@
<?php
/*
* This file is part of Flarum.
*
* (c) Toby Zerner <toby.zerner@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Flarum\Tests\unit\Foundation\ErrorHandling\ExceptionHandler;
use Flarum\Foundation\ErrorHandling\ExceptionHandler\ValidationExceptionHandler;
use Flarum\Foundation\ValidationException;
use PHPUnit\Framework\TestCase;
class ValidationExceptionHandlerTest extends TestCase
{
private $handler;
public function setUp()
{
$this->handler = new ValidationExceptionHandler;
}
public function test_managing_exceptions()
{
$error = $this->handler->handle(new ValidationException(
['foo' => 'Attribute error'],
['bar' => 'Relationship error']
));
$this->assertEquals(422, $error->getStatusCode());
$this->assertEquals('validation_error', $error->getType());
$this->assertEquals([
[
'detail' => 'Attribute error',
'source' => ['pointer' => '/data/attributes/foo']
],
[
'detail' => 'Relationship error',
'source' => ['pointer' => '/data/relationships/bar']
]
], $error->getDetails());
}
}