Skip to content

Commit

Permalink
change the package core
Browse files Browse the repository at this point in the history
  • Loading branch information
hamidreza2005 committed Oct 13, 2023
1 parent b778eb7 commit ef7dbac
Show file tree
Hide file tree
Showing 5 changed files with 195 additions and 33 deletions.
41 changes: 34 additions & 7 deletions src/Config/api-error-handler.php
Original file line number Diff line number Diff line change
@@ -1,11 +1,38 @@
<?php

use hamidreza2005\LaravelApiErrorHandler\Exceptions\{
ServerInternalException,
NotFoundException,
AccessDeniedException,
ValidationException
};

return [
"Symfony\Component\HttpKernel\Exception\NotFoundHttpException" => "\hamidreza2005\LaravelApiErrorHandler\Exceptions\NotFoundException",
"ErrorException" => "\hamidreza2005\LaravelApiErrorHandler\Exceptions\ServerInternalException",
"Illuminate\Database\QueryException" => "\hamidreza2005\LaravelApiErrorHandler\Exceptions\ServerInternalException",
"Illuminate\Auth\AuthenticationException" => "\hamidreza2005\LaravelApiErrorHandler\Exceptions\AccessDeniedException",
"Symfony\Component\HttpKernel\Exception\HttpException" => "\hamidreza2005\LaravelApiErrorHandler\Exceptions\AccessDeniedException",
"Illuminate\Validation\ValidationException" => "\hamidreza2005\LaravelApiErrorHandler\Exceptions\ValidationException",
"Symfony\Component\HttpKernel\Exception\MethodNotAllowedHttpException"=>"\hamidreza2005\LaravelApiErrorHandler\Exceptions\NotFoundException",

/*
* this is where you define which handler deal with which errors. each handler can handle multiple errors
*/

"handlers" =>[
NotFoundException::class => [
"Symfony\Component\HttpKernel\Exception\NotFoundHttpException",
"Symfony\Component\HttpKernel\Exception\MethodNotAllowedHttpException"
],
ServerInternalException::class => [
"ErrorException",
"Illuminate\Database\QueryException"
],
AccessDeniedException::class => [
"Illuminate\Auth\AuthenticationException",
"Symfony\Component\HttpKernel\Exception\HttpException"
],
ValidationException::class => [
"Illuminate\Validation\ValidationException"
],
],

/*
* if the app is not in debug mode. all unknown exceptions will be handled by this.
*/
"internal_error_handler" => ServerInternalException::class,
];
69 changes: 69 additions & 0 deletions src/HandlerFactory.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
<?php

namespace hamidreza2005\LaravelApiErrorHandler;

use hamidreza2005\LaravelApiErrorHandler\Exceptions\DefaultException;
use hamidreza2005\LaravelApiErrorHandler\Exceptions\ExceptionAbstract;
use hamidreza2005\LaravelApiErrorHandler\Exceptions\ServerInternalException;
use Illuminate\Support\Facades\Config;

class HandlerFactory
{
private bool $debugMode;

private array $handlers;

private string $internalErrorHandler;
public function __construct()
{
$this->debugMode = app()->hasDebugModeEnabled();
$this->loadHandlers();
}

protected function loadHandlers(): void
{
$this->handlers = $this->loadHandlersFromConfigs();
$this->internalErrorHandler = Config::get("api-error-handler.internal_error_handler") ?? ServerInternalException::class;
}

private function loadHandlersFromConfigs(): array
{
return array_map(
fn($exceptions)=> is_array($exceptions) ? $exceptions : [$exceptions],
Config::get("api-error-handler.handlers") ?? []
);
}

private function exceptionHasHandler($exception): bool
{
return !is_null($this->findHandlerByException($exception));
}

private function findHandlerByException($exception): string|null
{
$exceptionClassName = get_class($exception);
foreach ($this->handlers as $handler => $exceptions){
if (in_array($exceptionClassName,$exceptions)){
return $handler;
}
}
return null;
}

protected function makeHandler(string $handlerClassName,$exception)
{
return new $handlerClassName($exception);
}

public function getHandler($exception): ExceptionAbstract
{
$handlerClassName = $this->findHandlerByException($exception);
if (!$handlerClassName){
$handlerClassName = $this->debugMode ?
DefaultException::class :
$this->internalErrorHandler;
}

return $this->makeHandler($handlerClassName,$exception);
}
}
26 changes: 13 additions & 13 deletions src/Traits/ApiErrorHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,28 +2,28 @@

namespace hamidreza2005\LaravelApiErrorHandler\Traits;

use hamidreza2005\LaravelApiErrorHandler\Exceptions\DefaultException;
use hamidreza2005\LaravelApiErrorHandler\Exceptions\ServerInternalException;
use hamidreza2005\LaravelApiErrorHandler\HandlerFactory;
use Illuminate\Http\JsonResponse;
use Illuminate\Support\Facades\Response;
use Illuminate\Http\Response;

trait ApiErrorHandler
{
public function handleError($exception): JsonResponse
public function handle($exception): JsonResponse
{
$exceptions = config("api-error-handler") ?? [];
$class = array_key_exists(get_class($exception),$exceptions) ?
$exceptions[get_class($exception)] :
(config('app.debug') ? DefaultException::class : ServerInternalException::class
);
$handler = new $class($exception);
$handler = (new HandlerFactory())->getHandler($exception);
$handler->handleStatusCode();
$handler->handleMessage();
return $this->errorResponse(["error"=>$handler->getMessage()],$handler->getStatusCode(),["Content-Type"=>"application/json"]);
return $this->errorResponse($handler->getMessage(),[],$handler->getStatusCode(),["Content-Type"=>"application/json"]);
}

protected function errorResponse(array $data,int $statusCode, array $headers): JsonResponse
protected function errorResponse(string $message, array $data = [] ,int $statusCode = Response::HTTP_BAD_REQUEST, array $headers = []): JsonResponse
{
return Response::json($data,$statusCode,$headers);
$result = [
"message" => $message,
"success" => false,
"data" => $data,
"status" => $statusCode
];
return response()->json($result,$statusCode,$headers);
}
}
79 changes: 79 additions & 0 deletions tests/Unit/HandlerFactoryTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
<?php

namespace hamidreza2005\LaravelApiErrorHandler\Tests\Unit;

use App\Exceptions\Handler;
use hamidreza2005\LaravelApiErrorHandler\Exceptions\AccessDeniedException;
use hamidreza2005\LaravelApiErrorHandler\Exceptions\DefaultException;
use hamidreza2005\LaravelApiErrorHandler\Exceptions\ServerInternalException;
use hamidreza2005\LaravelApiErrorHandler\HandlerFactory;
use Illuminate\Support\Facades\Config;
use Orchestra\Testbench\TestCase;
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;

class HandlerFactoryTest extends TestCase
{
public function test_handler_class_works_with_configs_correctly()
{
Config::set("app.debug",true);
Config::shouldReceive("get")
->with('app.debug')
->andReturn(true);

Config::shouldReceive("get")
->with("api-error-handler.handlers");

Config::shouldReceive("get")
->with("api-error-handler.internal_error_handler")
->andReturn(ServerInternalException::class);

$exceptionClass = new \Exception("fdf");
$handler = (new HandlerFactory())->getHandler($exceptionClass);
$this->assertTrue(true); // just so phpunit doesn't give warning
}

public function test_handler_class_gives_the_right_handler()
{
Config::set("api-error-handler.handlers",[
AccessDeniedException::class => \Exception::class
]);

$exception = new \Exception("Error");
$handler = (new HandlerFactory())->getHandler($exception);
$this->assertInstanceOf(AccessDeniedException::class,$handler);
}

public function test_handler_can_have_multiple_exceptions()
{
Config::set("api-error-handler.handlers",[
AccessDeniedException::class => [\Exception::class,AccessDeniedHttpException::class]
]);

$exception = new AccessDeniedHttpException("Error");
$handler = (new HandlerFactory())->getHandler($exception);
$this->assertInstanceOf(AccessDeniedException::class,$handler);
}

public function test_handler_class_gives_server_internal_if_unknown_exception_occurs_when_debug_mode_is_false()
{
Config::set("app.debug",false);
Config::set("api-error-handler.handlers",[
AccessDeniedException::class => \Exception::class
]);

$exception = new AccessDeniedHttpException("Error");
$handler = (new HandlerFactory())->getHandler($exception);
$this->assertInstanceOf(ServerInternalException::class,$handler);
}
public function test_handler_class_gives_rised_exception_if_unknown_exception_occurs_when_debug_mode_is_false()
{
Config::set("app.debug",true);
Config::set("api-error-handler.handlers",[
AccessDeniedException::class => \Exception::class
]);

$exception = new AccessDeniedHttpException("Error");
$handler = (new HandlerFactory())->getHandler($exception);
$this->assertInstanceOf(DefaultException::class,$handler);
}
}
13 changes: 0 additions & 13 deletions tests/Unit/SimpleTest.php

This file was deleted.

0 comments on commit ef7dbac

Please sign in to comment.