diff --git a/.gitignore b/.gitignore index 81cded9..ffaacb7 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,35 @@ -/node_modules /vendor -/.phpintel \ No newline at end of file +/.phpintel + +# Created by https://www.gitignore.io/api/macos +# Edit at https://www.gitignore.io/?templates=macos + +### macOS ### +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +# End of https://www.gitignore.io/api/macos \ No newline at end of file diff --git a/README.md b/README.md index 9570d63..0eef271 100644 --- a/README.md +++ b/README.md @@ -133,7 +133,7 @@ class Handler extends ExceptionHandler ... - public function render($request, Exception $exception) + public function render($request, \Throwable $exception) { if ($request->expectsJson()) { return $this->jsonResponse($exception); diff --git a/composer.json b/composer.json index 6794ccd..77d3791 100644 --- a/composer.json +++ b/composer.json @@ -11,7 +11,7 @@ } ], "require": { - "php": ">=5.6.4" + "php": ">=7.1.0" }, "autoload": { "psr-4": { diff --git a/src/AuthenticationHandler.php b/src/AuthenticationHandler.php index d409802..8007dd7 100644 --- a/src/AuthenticationHandler.php +++ b/src/AuthenticationHandler.php @@ -6,17 +6,17 @@ trait AuthenticationHandler { - public function authenticationException(AuthenticationException $exception) - { - $error = [[ - 'status' => 401, - 'code' => $this->getCode('authentication'), - 'source' => ['pointer' => $exception->getFile().':'.$exception->getLine()], - 'title' => $exception->getMessage(), - 'detail' => __('exception::exceptions.authentication.detail'), - ]]; + public function authenticationException(AuthenticationException $exception) + { + $error = $this->getDefaultError(); + $error['status'] = '401'; + $error['code'] = (string) $this->getCode('authentication'); + $error['title'] = 'authentication'; + $error['detail'] = $exception->getMessage(); - $this->jsonApiResponse->setStatus(401); - $this->jsonApiResponse->setErrors($error); - } + $error = [$error]; + + $this->jsonApiResponse->setStatus(401); + $this->jsonApiResponse->setErrors($error); + } } diff --git a/src/AuthorizationHandler.php b/src/AuthorizationHandler.php index b323d14..2a538d6 100644 --- a/src/AuthorizationHandler.php +++ b/src/AuthorizationHandler.php @@ -6,46 +6,17 @@ trait AuthorizationHandler { - public function authorizationException(AuthorizationException $exception) - { - $error = [[ - 'status' => 403, - 'code' => $this->getCode('authorization'), - 'source' => ['pointer' => $exception->getFile().':'.$exception->getLine()], - 'title' => __('exception::exceptions.authorization.title'), - 'detail' => $exception->getMessage(), - ]]; - - $this->jsonApiResponse->setStatus(403); - $this->jsonApiResponse->setErrors($error); - } - - public function generateDescription($traces) - { - $action = ''; - foreach ($traces as $trace) { - if ($trace['function'] === 'authorize') { - $action = $this->extractAction($trace['args']); - break; - } - } - } - - public function extractAction($args) - { - $action = reset($args); - - $this->getWord($action); - } - - public function getWords($action) - { - $words = explode('.', $action); - if (! (count($words) > 1)) { - $words = explode('-', $action); - if (! (count($words) > 1)) { - $words = preg_split('/(?=[A-Z])/', $action); - } - } - } + public function authorizationException(AuthorizationException $exception) + { + $error = $this->getDefaultError(); + $error['status'] = '403'; + $error['code'] = (string) $this->getCode('authorization'); + $error['title'] = 'authorization'; + $error['detail'] = __('exception::exceptions.authorization.title'); + + $error = [$error]; + + $this->jsonApiResponse->setStatus(403); + $this->jsonApiResponse->setErrors($error); + } } diff --git a/src/BadRequestHttpHandler.php b/src/BadRequestHttpHandler.php index 122421a..4ec0472 100644 --- a/src/BadRequestHttpHandler.php +++ b/src/BadRequestHttpHandler.php @@ -6,19 +6,17 @@ trait BadRequestHttpHandler { - public function badRequestHttpException(BadRequestHttpException $exception) - { - $statusCode = $exception->getStatusCode(); + public function badRequestHttpException(BadRequestHttpException $exception) + { + $statusCode = $exception->getStatusCode(); + $error = $this->getDefaultError(); + $error['status'] = (string) $statusCode; + $error['code'] = (string) $this->getCode('bad_request'); + $error['title'] = 'bad_request'; + $error['detail'] = $exception->getMessage(); + $error = [$error]; - $error = [[ - 'status' => $statusCode, - 'code' => $this->getCode('bad_request'), - 'source' => ['pointer' => $exception->getFile().':'.$exception->getLine()], - 'title' => 'bad_request', - 'detail' => $exception->getMessage(), - ]]; - - $this->jsonApiResponse->setStatus($statusCode); - $this->jsonApiResponse->setErrors($error); - } + $this->jsonApiResponse->setStatus($statusCode); + $this->jsonApiResponse->setErrors($error); + } } diff --git a/src/CieloRequestHandler.php b/src/CieloRequestHandler.php deleted file mode 100644 index 1f8d75d..0000000 --- a/src/CieloRequestHandler.php +++ /dev/null @@ -1,31 +0,0 @@ -getCode(); - do { - $cieloError = $e->getCieloError(); - $error = [ - 'status' => $e->getCode(), - 'code' => $this->getCode('cielo').$cieloError->getCode(), - 'source' => ['pointer' => $e->getFile().':'.$e->getLine()], - 'title' => $e->getMessage(), - 'detail' => $cieloError->getMessage(), - ]; - if (! in_array($error, $errors)) { - array_push($errors, $error); - } - $e = $e->getPrevious(); - } while (method_exists($e, 'getPrevious')); - - $this->jsonApiResponse->setStatus($code); - $this->jsonApiResponse->setErrors($errors); - } -} diff --git a/src/ClientHandler.php b/src/ClientHandler.php deleted file mode 100644 index 5bbf5b3..0000000 --- a/src/ClientHandler.php +++ /dev/null @@ -1,81 +0,0 @@ -getMessage(); - $code = $this->getCode(); - - if ($exception instanceof ClientException) { - $requestHost = $exception->getRequest()->getUri()->getHost(); - $clientCausers = $this->clientExceptionCausers(); - - $response = $exception->getResponse(); - - if ($clientCausers->isPagarme($requestHost)) { - $code = config('json-exception-handler.codes.client.pagarme') ?? 'pagarme'; - $errors = json_decode($response->getBody())->errors; - - $firstErrorMessage = ''; - foreach ($errors as $error) { - $firstErrorMessage = $error->message; - break; - } - - $detailedError = $firstErrorMessage.' #'.$code; - } elseif ($clientCausers->isMailgun($requestHost)) { - $code = config('json-exception-handler.codes.client.mailgun') ?? 'mailgun'; - $detailedError = json_decode($response->getBody())->message.' #'.$code; - } else { - // Unknown error - $code = config('json-exception-handler.codes.client.default'); - } - - if (App::environment('production')) { - $detail = __('exception::exceptions.client.unavailable').' #'.$code; - } else { - // Return more details about error - $detail = $detailedError ?? $detail; - $statusCode = $response->getStatusCode(); - } - } - - $error = [[ - 'status' => $statusCode, - 'code' => $code, - 'source' => ['pointer' => $exception->getFile().':'.$exception->getLine()], - 'title' => $title, - 'detail' => $detail, - ]]; - - $this->jsonApiResponse->setStatus($statusCode); - $this->jsonApiResponse->setErrors($error); - } - - public function clientExceptionCausers() - { - return new class() { - const PAGARME_HOST = 'api.pagar.me'; - - const MAILGUN_HOST = 'api.mailgun.net'; - - public function isPagarme($host) - { - return self::PAGARME_HOST == $host; - } - - public function isMailgun($host) - { - return self::MAILGUN_HOST == $host; - } - }; - } -} diff --git a/src/JsonHandler.php b/src/JsonHandler.php index 873bc08..a4e669d 100644 --- a/src/JsonHandler.php +++ b/src/JsonHandler.php @@ -2,167 +2,182 @@ namespace SMartins\JsonHandler; -use Exception; +use Throwable; use SMartins\JsonHandler\Responses\JsonApiResponse; trait JsonHandler { - use ValidationHandler, - ModelNotFoundHandler, - AuthorizationHandler, - NotFoundHttpHandler, - AuthenticationHandler, - OAuthServerHandler, - MissingScopeHandler, - BadRequestHttpHandler, - ClientHandler, - CieloRequestHandler; - - /** - * Config file name. - * @var string - */ - public $configFile = 'json-exception-handler'; - - /** - * JsonApiResponse instance used on another traits to set response. - * @var SMartins\JsonHandler\Responses\JsonApiResponse; - */ - public $jsonApiResponse; - - /** - * Receive exception instance to be used on methods. - * @var Exception - */ - private $exception; - - /** - * Set the default response on $response attribute. Get default value from - * methods. - */ - public function setDefaultResponse() - { - $error = [[ - 'status' => $this->getStatusCode(), - 'code' => $this->getCode(), - 'source' => ['pointer' => $this->getDescription()], - 'title' => strtolower(class_basename($this->exception)), - 'detail' => $this->getMessage(), - ]]; - - $this->jsonApiResponse->setStatus($this->getStatusCode()); - $this->jsonApiResponse->setErrors($error); + use ValidationHandler, + ModelNotFoundHandler, + AuthorizationHandler, + NotFoundHttpHandler, + AuthenticationHandler, + OAuthServerHandler, + MissingScopeHandler, + BadRequestHttpHandler; + + /** + * Config file name. + * @var string + */ + public $configFile = 'json-exception-handler'; + + /** + * JsonApiResponse instance used on another traits to set response. + * @var SMartins\JsonHandler\Responses\JsonApiResponse; + */ + public $jsonApiResponse; + + /** + * Receive exception instance to be used on methods. + * @var Throwable + */ + private $throwable; + + public function getDefaultError() + { + $meta = []; + if (config($this->configFile . '.show_details_in_meta')) { + $meta['__raw_error__'] = [ + 'message' => $this->getMessage(), + 'detail' => $this->getDescription(), + 'backtrace' => explode("\n", $this->throwable->getTraceAsString()), + ]; } - - /** - * Get default message from exception. - * - * @return string Exception message - */ - public function getMessage() - { - return $this->exception->getMessage(); - } - - /** - * Mount the description with exception class, line and file. - * - * @return string - */ - public function getDescription() - { - return class_basename($this->exception). - ' line '.$this->exception->getLine(). - ' in '.$this->exception->getFile(); - } - - /** - * Get default http code. Check if exception has getStatusCode() methods. - * If not get from config file. - * - * @return int - */ - public function getStatusCode() - { - if (method_exists($this->exception, 'getStatusCode')) { - $httpCode = $this->exception->getStatusCode(); - } else { - $httpCode = config($this->configFile.'.http_code'); - } - - return $httpCode; + $error = [ + 'status' => (string) $this->getStatusCode(), + 'code' => (string) $this->getCode(), + 'title' => str_replace('Throwable', '', class_basename($this->throwable)), + 'detail' => (config($this->configFile . '.show_details_in_meta')) ? $this->getMessage() : 'An unknown error occurred', + 'meta' => $meta, + ]; + + return $error; + } + + /** + * Set the default response on $response attribute. Get default value from + * methods. + */ + public function setDefaultResponse() + { + $error = [$this->getDefaultError()]; + + $this->jsonApiResponse->setStatus($this->getStatusCode()); + $this->jsonApiResponse->setErrors($error); + } + + /** + * Get default message from exception. + * + * @return string Throwable message + */ + public function getMessage() + { + return $this->throwable->getMessage(); + } + + /** + * Mount the description with exception class, line and file. + * + * @return string + */ + public function getDescription() + { + return class_basename($this->throwable) . + ' line ' . $this->throwable->getLine() . + ' in ' . $this->throwable->getFile(); + } + + /** + * Get default http code. Check if exception has getStatusCode() methods. + * If not get from config file. + * + * @return int + */ + public function getStatusCode() + { + if (method_exists($this->throwable, 'getStatusCode')) { + $httpCode = $this->throwable->getStatusCode(); + } else { + $httpCode = config($this->configFile . '.http_code'); } - /** - * Get error code. If code is empty from config file based on type. - * - * @param string $type Code type from config file - * @return int - */ - public function getCode($type = 'default') - { - $code = $this->exception->getCode(); - if (empty($this->exception->getCode())) { - $code = config($this->configFile.'.codes.'.$type); - } - - return $code; + return $httpCode; + } + + /** + * Get error code. If code is empty from config file based on type. + * + * @param string $type Code type from config file + * @return int + */ + public function getCode($type = 'default') + { + $code = $this->throwable->getCode(); + if (empty($this->throwable->getCode())) { + $code = config($this->configFile . '.codes.' . $type); } - /** - * Handle the json response. Check if exception is treated. If true call - * the specific handler. If false set the default response to be returned. - * - * @param Exception $exception - * @return JsonResponse - */ - public function jsonResponse(Exception $exception) - { - $this->exception = $exception; - $this->jsonApiResponse = new JsonApiResponse; - - if ($this->exceptionIsTreated()) { - $this->callExceptionHandler(); - } else { - $this->setDefaultResponse(); - } - - return response()->json( - $this->jsonApiResponse->toArray(), - $this->jsonApiResponse->getStatus() - ); + return $code; + } + + /** + * Handle the json response. Check if exception is treated. If true call + * the specific handler. If false set the default response to be returned. + * + * @param Throwable $exception + * @return JsonResponse + */ + public function jsonResponse(Throwable $exception) + { + $this->throwable = $exception; + $this->jsonApiResponse = new JsonApiResponse; + + if ($this->exceptionIsTreated()) { + $this->callExceptionHandler(); + } else { + $this->setDefaultResponse(); } - /** - * Check if method to treat exception exists. - * - * @param Exception $exception The exception to be checked - * @return bool If method is callable - */ - public function exceptionIsTreated() - { - return is_callable([$this, $this->methodName()]); - } - - /** - * Call the exception handler after of to check if the method exists. - * - * @param Exception $exception - * @return void Call the method - */ - public function callExceptionHandler() - { - $this->{$this->methodName()}($this->exception); - } - - /** - * The method name is the exception name with first letter in lower case. - * - * @param Exception $exception - * @return string The method name - */ - public function methodName() - { - return lcfirst(class_basename($this->exception)); - } + return response()->json( + $this->jsonApiResponse->toArray(), + $this->jsonApiResponse->getStatus(), + config($this->configFile . '.additional_headers'), + config($this->configFile . '.json_options') + ); + } + + /** + * Check if method to treat exception exists. + * + * @param Throwable $exception The exception to be checked + * @return bool If method is callable + */ + public function exceptionIsTreated() + { + return is_callable([$this, $this->methodName()]); + } + + /** + * Call the exception handler after of to check if the method exists. + * + * @param Throwable $exception + * @return void Call the method + */ + public function callExceptionHandler() + { + $this->{$this->methodName()}($this->throwable); + } + + /** + * The method name is the exception name with first letter in lower case. + * + * @param Throwable $exception + * @return string The method name + */ + public function methodName() + { + return lcfirst(class_basename($this->throwable)); + } } diff --git a/src/JsonHandlerServiceProvider.php b/src/JsonHandlerServiceProvider.php index 7de8927..31b2f58 100644 --- a/src/JsonHandlerServiceProvider.php +++ b/src/JsonHandlerServiceProvider.php @@ -6,26 +6,26 @@ class JsonHandlerServiceProvider extends ServiceProvider { - public function boot() - { - $this->publishes([ - $this->configPath() => config_path('json-exception-handler.php'), - ]); + public function boot() + { + $this->publishes([ + $this->configPath() => config_path('json-exception-handler.php'), + ]); - $this->loadTranslationsFrom(__DIR__.'/resources/lang', 'exception'); + $this->loadTranslationsFrom(__DIR__ . '/resources/lang', 'exception'); - $this->publishes([ - __DIR__.'/resources/lang' => resource_path('lang/vendor/exception'), - ]); - } + $this->publishes([ + __DIR__ . '/resources/lang' => resource_path('lang/vendor/exception'), + ]); + } - public function register() - { - $this->mergeConfigFrom($this->configPath(), 'json-exception-handler'); - } + public function register() + { + $this->mergeConfigFrom($this->configPath(), 'json-exception-handler'); + } - public function configPath() - { - return __DIR__.'/config/json-exception-handler.php'; - } + public function configPath() + { + return __DIR__ . '/config/json-exception-handler.php'; + } } diff --git a/src/MissingScopeHandler.php b/src/MissingScopeHandler.php index d1331f9..a6144a6 100644 --- a/src/MissingScopeHandler.php +++ b/src/MissingScopeHandler.php @@ -6,17 +6,17 @@ trait MissingScopeHandler { - public function missingScopeException(MissingScopeException $exception) - { - $error = [[ - 'status' => 403, - 'code' => $this->getCode('missing_scope'), - 'source' => ['pointer' => $exception->getFile().':'.$exception->getLine()], - 'title' => 'missing_scope', - 'detail' => $exception->getMessage(), - ]]; + public function missingScopeException(MissingScopeException $exception) + { + $error = $this->getDefaultError(); + $error['status'] = '403'; + $error['code'] = (string) $this->getCode('missing_scope'); + $error['title'] = 'missing_scope'; + $error['detail'] = $exception->getMessage(); - $this->jsonApiResponse->setStatus(403); - $this->jsonApiResponse->setErrors($error); - } + $error = [$error]; + + $this->jsonApiResponse->setStatus(403); + $this->jsonApiResponse->setErrors($error); + } } diff --git a/src/ModelNotFoundHandler.php b/src/ModelNotFoundHandler.php index 32bcd54..5609dce 100644 --- a/src/ModelNotFoundHandler.php +++ b/src/ModelNotFoundHandler.php @@ -6,71 +6,77 @@ trait ModelNotFoundHandler { - /** - * Set the response if Exception is ModelNotFound. - * - * @param ModelNotFoundException $exception - */ - public function modelNotFoundException(ModelNotFoundException $exception) - { - $entity = $this->extractEntityName($exception->getModel()); - - $ids = implode($exception->getIds(), ','); - - $error = [[ - 'status' => 404, - 'code' => $this->getCode('model_not_found'), - 'source' => ['pointer' => 'data/id'], - 'title' => $exception->getMessage(), - 'detail' => __('exception::exceptions.model_not_found.title', ['model' => $entity]), - ]]; - - $this->jsonApiResponse->setStatus(404); - $this->jsonApiResponse->setErrors($error); - } + /** + * Set the response if Exception is ModelNotFound. + * + * @param ModelNotFoundException $exception + */ + public function modelNotFoundException(ModelNotFoundException $exception) + { + $entity = $this->extractEntityName($exception->getModel()); - /** - * Get entitie name based on model path to mount the message. - * - * @param string $model - * @return string - */ - public function extractEntityName($model) - { - $classNames = (array) explode('\\', $model); + $ids = implode(',', $exception->getIds()); - $entityName = end($classNames); + $meta = [ + 'attribute' => 'id' + ]; - if ($this->entityHasTranslation($entityName)) { - return __('exception::exceptions.models.'.$entityName); - } - return $entityName; - } + $error = $this->getDefaultError(); + $error['status'] = '404'; + $error['code'] = (string) $this->getCode('model_not_found'); + $error['title'] = 'model_not_found'; + $error['detail'] = __('exception::exceptions.model_not_found.title', ['model' => $entity]); + $error['meta']['attribute'] = 'id'; + + $error = [$error]; + + $this->jsonApiResponse->setStatus(404); + $this->jsonApiResponse->setErrors($error); + } + + /** + * Get entitie name based on model path to mount the message. + * + * @param string $model + * @return string + */ + public function extractEntityName($model) + { + $classNames = (array) explode('\\', $model); - /** - * Check if entity returned on ModelNotFoundException has translation on - * exceptions file - * @param string $entityName The model name to check if has translation - * @return bool Has translation or not - */ - public function entityHasTranslation(string $entityName): bool - { - $hasKey = in_array($entityName, $this->translationModelKeys()); - - if ($hasKey) { - return ! empty($hasKey); - } - - return false; + $entityName = end($classNames); + + if ($this->entityHasTranslation($entityName)) { + return __('exception::exceptions.models.' . $entityName); } - /** - * Get the models keys on exceptions lang file - * @return array An array with keys to translate - */ - private function translationModelKeys(): array - { - return array_keys(__('exception::exceptions.models')); + return $entityName; + } + + /** + * Check if entity returned on ModelNotFoundException has translation on + * exceptions file + * @param string $entityName The model name to check if has translation + * @return bool Has translation or not + */ + public function entityHasTranslation(string $entityName): bool + { + $hasKey = in_array($entityName, $this->translationModelKeys()); + + if ($hasKey) { + return !empty($hasKey); } + + return false; + } + + /** + * Get the models keys on exceptions lang file + * @return array An array with keys to translate + */ + private function translationModelKeys(): array + { + return array_keys(__('exception::exceptions.models')); + } } diff --git a/src/NotFoundHttpHandler.php b/src/NotFoundHttpHandler.php index f3a9037..1ce05c2 100644 --- a/src/NotFoundHttpHandler.php +++ b/src/NotFoundHttpHandler.php @@ -6,40 +6,41 @@ trait NotFoundHttpHandler { - /** - * Set response parameters to NotFoundHttpException. - * - * @param NotFoundHttpException $exception - */ - public function notFoundHttpException(NotFoundHttpException $exception) - { - $statuCode = $exception->getStatusCode(); - $error = [[ - 'status' => $statuCode, - 'code' => $this->getCode('not_found_http'), - 'source' => ['pointer' => $exception->getFile().':'.$exception->getLine()], - 'title' => $this->getDescription($exception), - 'detail' => $this->getNotFoundMessage($exception), - ]]; - - $this->jsonApiResponse->setStatus($statuCode); - $this->jsonApiResponse->setErrors($error); - } + /** + * Set response parameters to NotFoundHttpException. + * + * @param NotFoundHttpException $exception + */ + public function notFoundHttpException(NotFoundHttpException $exception) + { + $statusCode = $exception->getStatusCode(); + + $error = $this->getDefaultError(); + $error['status'] = (string) $statusCode; + $error['code'] = (string) $this->getCode('not_found_http'); + $error['title'] = $this->getDescription($exception); + $error['detail'] = $this->getNotFoundMessage($exception); + + $error = [$error]; - /** - * Get message based on file. If file is RouteCollection return specific - * message. - * - * @param NotFoundHttpException $exception - * @return string - */ - public function getNotFoundMessage(NotFoundHttpException $exception) - { - $message = ! empty($exception->getMessage()) ? $exception->getMessage() : class_basename($exception); - if (basename($exception->getFile()) === 'RouteCollection.php') { - $message = __('exception::exceptions.not_found_http.message'); - } - - return $message; + $this->jsonApiResponse->setStatus($statusCode); + $this->jsonApiResponse->setErrors($error); + } + + /** + * Get message based on file. If file is RouteCollection return specific + * message. + * + * @param NotFoundHttpException $exception + * @return string + */ + public function getNotFoundMessage(NotFoundHttpException $exception) + { + $message = !empty($exception->getMessage()) ? $exception->getMessage() : class_basename($exception); + if (basename($exception->getFile()) === 'RouteCollection.php') { + $message = __('exception::exceptions.not_found_http.message'); } + + return $message; + } } diff --git a/src/OAuthServerHandler.php b/src/OAuthServerHandler.php index 7ea7ad6..452bfca 100644 --- a/src/OAuthServerHandler.php +++ b/src/OAuthServerHandler.php @@ -6,19 +6,19 @@ trait OAuthServerHandler { - public function oAuthServerException(OAuthServerException $exception) - { - $statusCode = $exception->getHttpStatusCode(); + public function oAuthServerException(OAuthServerException $exception) + { + $statusCode = $exception->getHttpStatusCode(); - $error = [[ - 'status' => $statusCode, - 'code' => $this->getCode('not_found_http'), - 'source' => ['pointer' => $exception->getFile().':'.$exception->getLine()], - 'title' => $exception->getErrorType(), - 'detail' => $exception->getMessage(), - ]]; + $error = $this->getDefaultError(); + $error['status'] = (string) $statusCode; + $error['code'] = (string) $this->getCode('oauth_server'); + $error['title'] = 'auth:' . $exception->getErrorType(); + $error['detail'] = $exception->getMessage(); - $this->jsonApiResponse->setStatus($statusCode); - $this->jsonApiResponse->setErrors($error); - } + $error = [$error]; + + $this->jsonApiResponse->setStatus($statusCode); + $this->jsonApiResponse->setErrors($error); + } } diff --git a/src/Responses/JsonApiResponse.php b/src/Responses/JsonApiResponse.php index 63ce8e4..a6d0b0d 100644 --- a/src/Responses/JsonApiResponse.php +++ b/src/Responses/JsonApiResponse.php @@ -4,42 +4,42 @@ class JsonApiResponse { - protected $status; - - protected $errors; - - public function getStatus() - { - return $this->status; - } - - public function status() - { - return $this->getStatus(); - } - - public function getErrors() - { - return $this->errors; - } - - public function errors() - { - return $this->getErrors(); - } - - public function setStatus($status) - { - $this->status = $status; - } - - public function setErrors($errors) - { - $this->errors = $errors; - } - - public function toArray() - { - return ['errors' => $this->errors()]; - } + protected $status; + + protected $errors; + + public function getStatus() + { + return $this->status; + } + + public function status() + { + return $this->getStatus(); + } + + public function getErrors() + { + return $this->errors; + } + + public function errors() + { + return $this->getErrors(); + } + + public function setStatus($status) + { + $this->status = $status; + } + + public function setErrors($errors) + { + $this->errors = $errors; + } + + public function toArray() + { + return ['errors' => $this->errors()]; + } } diff --git a/src/ValidationHandler.php b/src/ValidationHandler.php index b273f79..f9b7819 100644 --- a/src/ValidationHandler.php +++ b/src/ValidationHandler.php @@ -6,101 +6,107 @@ trait ValidationHandler { - /** - * Assign to response attribute the value to ValidationException. - * - * @param ValidationException $exception - */ - public function validationException(ValidationException $exception) - { - $this->jsonApiResponse->setStatus(422); - $this->jsonApiResponse->setErrors($this->jsonApiFormatErrorMessages($exception)); - } + /** + * Assign to response attribute the value to ValidationException. + * + * @param ValidationException $exception + */ + public function validationException(ValidationException $exception) + { + $this->jsonApiResponse->setStatus(422); + $this->jsonApiResponse->setErrors($this->jsonApiFormatErrorMessages($exception)); + } + + /** + * Get formatted errors on standard code, field, message to each field with + * error. + * + * @param ValidationException $exception + * @return array + */ + public function formattedErrors(ValidationException $exception) + { + return $this->jsonApiFormatErrorMessages($exception); + } - /** - * Get formatted errors on standard code, field, message to each field with - * error. - * - * @param ValidationException $exception - * @return array - */ - public function formattedErrors(ValidationException $exception) - { - return $this->jsonApiFormatErrorMessages($exception); + public function jsonApiFormatErrorMessages(ValidationException $exception) + { + $validationMessages = $this->getTreatedMessages($exception); + $validationFails = $this->getTreatedFails($exception); + $errors = []; + foreach ($validationMessages as $field => $messages) { + foreach ($messages as $key => $message) { + $attributes = $this->getValidationAttributes($validationFails, $key, $field); + $path = str_replace('/', ':', request()->path()); + $error = $this->getDefaultError(); + $error['status'] = '422'; + $error['code'] = "V:{$path}::{$field}"; + $error['source'] = ['parameter' => $field]; + $error['title'] = $attributes['title']; + $error['detail'] = $message; + $error['meta']['attribute'] = $field; + $error['meta']['message'] = $message; + array_push($errors, $error); + } } - public function jsonApiFormatErrorMessages(ValidationException $exception) - { - $validationMessages = $this->getTreatedMessages($exception); - $validationFails = $this->getTreatedFails($exception); + return $errors; + } - $errors = []; - foreach ($validationMessages as $field => $messages) { - foreach ($messages as $key => $message) { - $attributes = $this->getValidationAttributes($validationFails, $key, $field); - $error = [ - 'status' => 422, - 'code' => $attributes['code'], - 'source' => ['parameter' => $field], - 'title' => $attributes['title'], - 'detail' => $message, - ]; - array_push($errors, $error); - } - } + public function getValidationAttributes(array $validationFails, $key, $field) + { + return [ + 'code' => (string) $this->getValidationCode($validationFails, $key, $field), + 'title' => $this->getValidationTitle($validationFails, $key, $field), + ]; + } - return $errors; + public function getValidationTitle(array $validationFails, $key, $field) + { + if (array_key_exists($field, $validationFails)) { + return __('exception::exceptions.validation.title', [ + 'fails' => array_keys($validationFails[$field])[$key], + 'field' => $field, + ]); } + return __('exception::exceptions.validation.generic_title', [ + 'field' => $field, + ]); + } - public function getValidationAttributes(array $validationFails, $key, $field) - { - return [ - 'code' => $this->getValidationCode($validationFails, $key, $field), - 'title' => $this->getValidationTitle($validationFails, $key, $field), - ]; - } + public function getValidationCode(array $validationFails, $key, $field) + { + $rule = (array_key_exists($field, $validationFails)) ? strtolower(array_keys($validationFails[$field])[$key]) : 'default'; - public function getValidationTitle(array $validationFails, $key, $field) - { - return __('exception::exceptions.validation.title', [ - 'fails' => array_keys($validationFails[$field])[$key], - 'field' => $field, - ]); - } + return config($this->configFile . '.codes.validation_fields.' . $field . '.' . $rule); + } - public function getValidationCode(array $validationFails, $key, $field) - { - $rule = strtolower(array_keys($validationFails[$field])[$key]); + /** + * Get message based on exception type. If exception is generated by + * $this->validate() from default Controller methods the exception has the + * response object. If exception is generated by Validator::make() the + * messages are getted different. + * + * @param Exception $exception + * @return array + */ + public function getTreatedMessages($exception) + { + return $this->getMessagesFromValidator($exception); + } - return config($this->configFile.'.codes.validation_fields.'.$field.'.'.$rule); - } + public function getMessagesFromValidator($exception) + { + return $exception->validator->messages()->messages(); + } - /** - * Get message based on exception type. If exception is generated by - * $this->validate() from default Controller methods the exception has the - * response object. If exception is generated by Validator::make() the - * messages are getted different. - * - * @param Exception $exception - * @return array - */ - public function getTreatedMessages($exception) - { - return $this->getMessagesFromValidator($exception); - } + public function getTreatedFails($exception) + { + return $this->getFailsFromValidator($exception); + } - public function getMessagesFromValidator($exception) - { - return $exception->validator->messages()->messages(); - } - - public function getTreatedFails($exception) - { - return $this->getFailsFromValidator($exception); - } - - public function getFailsFromValidator($exception) - { - return $exception->validator->failed(); - } + public function getFailsFromValidator($exception) + { + return $exception->validator->failed(); + } } diff --git a/src/config/json-exception-handler.php b/src/config/json-exception-handler.php index 1f67c05..6f22077 100644 --- a/src/config/json-exception-handler.php +++ b/src/config/json-exception-handler.php @@ -2,57 +2,62 @@ return [ - /* - |--------------------------------------------------------------------------- - | Exceptions Codes - |--------------------------------------------------------------------------- - | - | This values determine the code of each exception. This codes are useful - | to identify the errors generated by your API. You can set your own codes - | and add more fields, validation rules and codes to `validation_fields` - | array. The `default` code is from not managed exceptions. - */ - 'codes' => [ - 'default' => 1, - 'authorization' => 12, - 'model_not_found' => 13, - 'validation' => 14, - 'validation_fields' => [ - 'name' => [ - 'default' => 141, - 'required' => 1411, - 'string' => 1412, - ], - 'email' => [ - 'default' => 142, - 'email' => 1421, - ], - 'password' => [ - 'default' => 143, - 'string' => 1431, - 'confirmed' => 1432, - 'min' => 1433, - ], - ], - 'not_found_http' => 15, - 'authentication' => 16, - 'oauth_server' => 17, - 'bad_request' => 18, - 'client' => [ - 'default' => 19, - 'pagarme' => 20, - 'mailgun' => 21, - ], + /* + |--------------------------------------------------------------------------- + | Exceptions Codes + |--------------------------------------------------------------------------- + | + | This values determine the code of each exception. This codes are useful + | to identify the errors generated by your API. You can set your own codes + | and add more fields, validation rules and codes to `validation_fields` + | array. The `default` code is from not managed exceptions. + */ + 'codes' => [ + 'default' => 1, + 'authorization' => 12, + 'model_not_found' => 13, + 'validation' => 14, + 'validation_fields' => [ + 'name' => [ + 'default' => 141, + 'required' => 1411, + 'string' => 1412, + ], + 'email' => [ + 'default' => 142, + 'email' => 1421, + ], + 'password' => [ + 'default' => 143, + 'string' => 1431, + 'confirmed' => 1432, + 'min' => 1433, + ], ], + 'not_found_http' => 15, + 'authentication' => 16, + 'oauth_server' => 17, + 'bad_request' => 18, + 'client' => [ + 'default' => 19, + 'pagarme' => 20, + 'mailgun' => 21, + ], + ], + + 'additional_headers' => [], + 'json_options' => JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES, + + 'show_details_in_meta' => (env('APP_ENV', 'production') !== 'production'), - /* - |--------------------------------------------------------------------------- - | Default Http Code - |--------------------------------------------------------------------------- - | - | The http code value to not managed exceptions. Generally the code used is - | 500 - Internal Server Error. - */ - 'http_code' => 500, + /* + |--------------------------------------------------------------------------- + | Default Http Code + |--------------------------------------------------------------------------- + | + | The http code value to not managed exceptions. Generally the code used is + | 500 - Internal Server Error. + */ + 'http_code' => 500, ]; diff --git a/src/resources/lang/en/exceptions.php b/src/resources/lang/en/exceptions.php index ed24ee2..882f807 100644 --- a/src/resources/lang/en/exceptions.php +++ b/src/resources/lang/en/exceptions.php @@ -2,39 +2,40 @@ return [ - /* - |-------------------------------------------------------------------------- - | Exception Language Lines - |-------------------------------------------------------------------------- - | - | This is the translaction from exceptions - | - */ + /* + |-------------------------------------------------------------------------- + | Exception Language Lines + |-------------------------------------------------------------------------- + | + | This is the translaction from exceptions + | + */ - 'authentication' => [ - 'detail' => 'The request was made by an unauthenticated user.', - ], - 'authorization' => [ - 'title' => 'Action not allowed.', - ], - 'model_not_found' => [ - 'title' => ':Model not found', - ], - 'not_found_http' => [ - 'message' => 'Route not found.', - ], - 'validation' => [ - 'title' => ':Fails validation failed on field :field', - ], - 'client' => [ - 'unavailable' => 'Service unavailable now.', - ], + 'authentication' => [ + 'detail' => 'The request was made by an unauthenticated user.', + ], + 'authorization' => [ + 'title' => 'Action not allowed.', + ], + 'model_not_found' => [ + 'title' => ':Model not found', + ], + 'not_found_http' => [ + 'message' => 'Route not found.', + ], + 'validation' => [ + 'title' => ':Fails validation failed on field :field', + 'generic_title' => 'Validation failed on field :field', + ], + 'client' => [ + 'unavailable' => 'Service unavailable now.', + ], - /* - | Can set your model here to translate the models names on ModelNotFoundException - | - */ - 'models' => [ - 'User' => 'Usuário', - ] + /* + | Can set your model here to translate the models names on ModelNotFoundException + | + */ + 'models' => [ + 'User' => 'Usuário', + ], ]; diff --git a/src/resources/lang/pt-br/exceptions.php b/src/resources/lang/pt-br/exceptions.php index ed24ee2..0a045e9 100644 --- a/src/resources/lang/pt-br/exceptions.php +++ b/src/resources/lang/pt-br/exceptions.php @@ -2,39 +2,39 @@ return [ - /* - |-------------------------------------------------------------------------- - | Exception Language Lines - |-------------------------------------------------------------------------- - | - | This is the translaction from exceptions - | - */ + /* + |-------------------------------------------------------------------------- + | Exception Language Lines + |-------------------------------------------------------------------------- + | + | This is the translaction from exceptions + | + */ - 'authentication' => [ - 'detail' => 'The request was made by an unauthenticated user.', - ], - 'authorization' => [ - 'title' => 'Action not allowed.', - ], - 'model_not_found' => [ - 'title' => ':Model not found', - ], - 'not_found_http' => [ - 'message' => 'Route not found.', - ], - 'validation' => [ - 'title' => ':Fails validation failed on field :field', - ], - 'client' => [ - 'unavailable' => 'Service unavailable now.', - ], + 'authentication' => [ + 'detail' => 'The request was made by an unauthenticated user.', + ], + 'authorization' => [ + 'title' => 'Action not allowed.', + ], + 'model_not_found' => [ + 'title' => ':Model not found', + ], + 'not_found_http' => [ + 'message' => 'Route not found.', + ], + 'validation' => [ + 'title' => ':Fails validation failed on field :field', + ], + 'client' => [ + 'unavailable' => 'Service unavailable now.', + ], - /* - | Can set your model here to translate the models names on ModelNotFoundException - | - */ - 'models' => [ - 'User' => 'Usuário', - ] + /* + | Can set your model here to translate the models names on ModelNotFoundException + | + */ + 'models' => [ + 'User' => 'Usuário', + ], ];