Skip to content

Commit

Permalink
Merge pull request #6 from PcComponentes/add-docker-and-code-standard
Browse files Browse the repository at this point in the history
Add docker and code-standard
  • Loading branch information
AaronBernabeu authored Apr 26, 2020
2 parents cbbb263 + 096c3c4 commit 4a7e865
Show file tree
Hide file tree
Showing 15 changed files with 181 additions and 95 deletions.
14 changes: 14 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
FROM php:7.4-cli-alpine3.11

RUN apk update && \
apk add --no-cache \
libzip-dev \
openssl-dev && \
docker-php-ext-install -j$(nproc) \
zip

RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/bin/ --filename=composer

ENV PATH /var/app/bin:/var/app/vendor/bin:$PATH

WORKDIR /var/app
24 changes: 24 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
UID=$(shell id -u)
GID=$(shell id -g)
DOCKER_PHP_SERVICE=php

start: erase cache-folders build composer-install bash

erase:
docker-compose down -v

build:
docker-compose build && \
docker-compose pull

cache-folders:
mkdir -p ~/.composer && chown ${UID}:${GID} ~/.composer

composer-install:
docker-compose run --rm -u ${UID}:${GID} ${DOCKER_PHP_SERVICE} composer install

bash:
docker-compose run --rm -u ${UID}:${GID} ${DOCKER_PHP_SERVICE} sh

logs:
docker-compose logs -f ${DOCKER_PHP_SERVICE}
77 changes: 48 additions & 29 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,79 +1,98 @@
# Pccomponentes Apixception bundle

El objetivo de esta librería es ofrecer una pequeña ayuda al programador a la hora de renderizar las excepciones que llegan al controlador, como respuestas del API. Está integrado usando el evento ``Kernel.exception`` del Kernel de Symfony Framework v4.
Las respuestas serán en formato JSON ( ``Symfony\Component\HttpFoundation\JsonResponse``).
El objetivo de esta librería es ofrecer una pequeña ayuda al programador a la hora de renderizar las excepciones que llegan al controlador, como respuestas del API.
Está integrado usando el evento `Kernel.exception` del Kernel de Symfony Framework v4.
Las respuestas serán en formato JSON (`Symfony\Component\HttpFoundation\JsonResponse`).

## Instalación

1. Descarga e instala el vendor usando [composer](https://getcomposer.org/).

```bash
$ composer require pccomponentes/apixception-bundle
```
2. Añade el bundle en ``config\bundles.php``. Por ejemplo:
```bash
<?php
return [
Symfony\Bundle\FrameworkBundle\FrameworkBundle::class => ['all' => true],
Pccomponentes\Apixception\ApixceptionBundle::class => ['all' => true]

2. Añade el bundle en `config\bundles.php`. Por ejemplo:

```php
<?php
return [
Symfony\Bundle\FrameworkBundle\FrameworkBundle::class => ['all' => true],
PcComponentes\Apixception\ApixceptionBundle::class => ['all' => true]
];
```
4. Escribe el fichero de configuración del bundle indicando las excepciones que deseas capturar, y su transformación para generar la respuesta. Para ello, crea un archivo con nombre ``apixception.yaml`` en la ruta ``config/packages``. Un ejemplo de su contenido es:

3. Escribe el fichero de configuración del bundle indicando las excepciones que deseas capturar, y su transformación para generar la respuesta.
Para ello, crea un archivo con nombre `apixception.yaml` en la ruta `config/packages`.
Un ejemplo de su contenido es:

```yaml
apixception:
- exception: Pccomponentes\Ddd\Domain\Exception\NotFoundException
transformer: Pccomponentes\Apixception\Core\Transformer\JsonSerializableTransformer
- exception: PcComponentes\Ddd\Domain\Exception\NotFoundException
transformer: PcComponentes\Apixception\Core\Transformer\JsonSerializableTransformer
http_code: 404
- exception: Pccomponentes\Ddd\Domain\Exception\ExistsException
transformer: Pccomponentes\Apixception\Core\Transformer\JsonSerializableTransformer
- exception: PcComponentes\Ddd\Domain\Exception\ExistsException
transformer: PcComponentes\Apixception\Core\Transformer\JsonSerializableTransformer
http_code: 409
- exception: Pccomponentes\Ddd\Domain\Exception\LogicException
transformer: Pccomponentes\Apixception\Core\Transformer\JsonSerializableTransformer
- exception: PcComponentes\Ddd\Domain\Exception\LogicException
transformer: PcComponentes\Apixception\Core\Transformer\JsonSerializableTransformer
http_code: 409
- exception: \Throwable
transformer: Pccomponentes\Apixception\Core\Transformer\NoSerializableTransformer
transformer: PcComponentes\Apixception\Core\Transformer\NoSerializableTransformer
http_code: 500
```
Este archivo se modificará para añadir o quitar las reglas que sean necesarias para el proyecto en el que se use.

Este archivo se modificará para añadir o quitar las reglas que sean necesarias para el proyecto en el que se use.

## Configuración

Este archivo debe contener un listado de reglas. Cada regla debe tener:

- ``exception``: Nombre de la clase o interface, namespace incluído, que representa el tipo de la excepción que se quiere capturar.
- ``http_code``: Código HTTP que devolverá el objeto ``JsonResponse``.
- ``transformer``: Clase con namespace incluído, que transformará la excepción en un array de datos serializables por el objeto ``JsonResponse``.
- `exception`: Nombre de la clase o interface, namespace incluído, que representa el tipo de la excepción que se quiere capturar.
- `http_code`: Código HTTP que devolverá el objeto `Symfony\Component\HttpFoundation\JsonResponse`.
- `transformer`: Clase con namespace incluído, que transformará la excepción en un array de datos serializables por el objeto `Symfony\Component\HttpFoundation\JsonResponse`.

## Creación de un nuevo transformador

Aunque la aplicación proporciona un par de transformadores básicos, está abierto para que cada proyecto pueda inyectar sus propios transformadores.
Las excepción deben implementar la interfaz ``\Throwable`` o de heredar de clases que ya lo hagan. Los transformadores son clases que deben heredar de la clase ``Pccomponentes\Apixception\Core\Transformer\ExceptionTransformer``. Por cuestiones de simplicidad, no se permite inyectar dependencias, así que heredar de esta clase implica tener un constructor vacío y contener lógica de transformación ligera.
Las excepción deben implementar la interfaz `\Throwable` o de heredar de clases que ya lo hagan.
Los transformadores son clases que deben heredar de la clase `PcComponentes\Apixception\Core\Transformer\ExceptionTransformer`.
Por cuestiones de simplicidad, no se permite inyectar dependencias, así que heredar de esta clase implica tener un constructor vacío y contener lógica de transformación ligera.

Un ejemplo de un transformador propio sería:

```php
<?php
namespace MyApp\Transformers;
use Pccomponentes\Apixception\Core\Transformer\ExceptionTransformer;
use PcComponentes\Apixception\Core\Transformer\ExceptionTransformer;
class CustomTransformer extends ExceptionTransformer
{
public function transform(\Throwable $exception): array
{
return [
'exception' => \get_class($exception),
'message' => $exception->getMessage()
'message' => $exception->getMessage(),
];
}
}
```
Si llegara una excepción personalizada, con métodos únicos, puedes usarlas. Asegúrate que en la configuración garantice que a tu transformador sólo llegan excepciones del tipo que esperas, o haz que tu transformador actúe en consecuencia.

Si llegara una excepción personalizada, con métodos únicos, puedes usarlas.
Asegúrate que en la configuración garantice que a tu transformador sólo llegan excepciones del tipo que esperas, o haz que tu transformador actúe en consecuencia.

## Transformadores disponibles

La librería proporciona dos transformadores que puedes usar desde el primer momento.
- ``Pccomponentes\Apixception\Core\Transformer\NoSerializableTransformer`` : Este transformador es capaz de renderizar cualquier tipo de excepción. En la respuesta meterá un objeto JSON con las propiedades ``exception`` con el nombre de la excepción, y ``message``, con el mensaje de la excepción.
- ``Pccomponentes\Apixception\Core\Transformer\JsonSerializableTransformer``: Este transformador será capaz de renderizar cualquier excepción que implemente la interfaz ``\JsonSerializable``. El array que devuelva dicho método, será lo que se incluya en la respuesta.
- `PcComponentes\Apixception\Core\Transformer\NoSerializableTransformer`:
Este transformador es capaz de renderizar cualquier tipo de excepción.
En la respuesta meterá un objeto JSON con las propiedades `exception` con el nombre de la excepción, y `message`, con el mensaje de la excepción.
- `PcComponentes\Apixception\Core\Transformer\JsonSerializableTransformer`:
Este transformador será capaz de renderizar cualquier excepción que implemente la interfaz `\JsonSerializable`.
El array que devuelva dicho método, será lo que se incluya en la respuesta.

## Excepciones

Como se ha comentado anteriormente, esta librería acepta excepciones de todo tipo.

9 changes: 6 additions & 3 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,16 @@
}
],
"require": {
"php": "^7.1.3",
"symfony/framework-bundle": "^4.2",
"php": "^7.4",
"symfony/framework-bundle": "^4.0",
"ext-json": "*"
},
"autoload": {
"psr-4": {
"Pccomponentes\\Apixception\\": "src/"
"PcComponentes\\Apixception\\": "src/"
}
},
"require-dev": {
"pccomponentes/coding-standard": "^1.1"
}
}
8 changes: 8 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
version: '3.7'

services:
php:
build: .
volumes:
- .:/var/app
- ~/.composer:/.composer
5 changes: 5 additions & 0 deletions phpcs.xml.dist
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<?xml version="1.0" ?>
<ruleset name="Project rules">
<file>src</file>
<rule ref="vendor/pccomponentes/coding-standard/src/ruleset.xml" />
</ruleset>
7 changes: 4 additions & 3 deletions src/ApixceptionBundle.php
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
<?php
namespace Pccomponentes\Apixception;
declare(strict_types=1);

namespace PcComponentes\Apixception;

use Pccomponentes\Apixception\DependencyInjection\ApixceptionExtension;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\HttpKernel\Bundle\Bundle;

Expand All @@ -11,4 +12,4 @@ public function build(ContainerBuilder $container)
{
parent::build($container);
}
}
}
48 changes: 28 additions & 20 deletions src/Core/ApixceptionDispatcher.php
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
<?php
namespace Pccomponentes\Apixception\Core;
declare(strict_types=1);

namespace PcComponentes\Apixception\Core;

use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\JsonResponse;
Expand All @@ -8,7 +10,7 @@

class ApixceptionDispatcher implements EventSubscriberInterface
{
private $subscribers;
private array $subscribers;

public function __construct()
{
Expand All @@ -22,37 +24,43 @@ public function add(string $exception, int $httpCode, string $transformerClass):
$this->subscribers[] = new ApixceptionSubscriber(
$exception,
$httpCode,
(new \ReflectionClass($transformerClass))->newInstanceWithoutConstructor()
(new \ReflectionClass($transformerClass))->newInstanceWithoutConstructor(),
);
}

public static function getSubscribedEvents(): array
{
return [KernelEvents::EXCEPTION => ['onKernelException']];
}

public function onKernelException(GetResponseForExceptionEvent $event): void
{
$exception = $event->getException();

foreach ($this->subscribers as $subscriber) {
if (\is_a($exception, $subscriber->exception(), true)) {
$event->allowCustomResponseCode(); //Symfony disables all non 4XX status codes in kernel exception by default
$event->setResponse(
new JsonResponse(
$subscriber->transform($exception),
$subscriber->httpCode()
)
);
return;
if (false === \is_a($exception, $subscriber->exception(), true)) {
continue;
}

$event->allowCustomResponseCode();
$event->setResponse(
new JsonResponse(
$subscriber->transform($exception),
$subscriber->httpCode(),
),
);

return;
}
}

public static function getSubscribedEvents(): array
{
return [
KernelEvents::EXCEPTION => ['onKernelException'],
];
}

private function guardIfIsClassOrInterface(string $class): void
{
if (false === $this->classExists($class) && false === interface_exists($class)) {
throw new \InvalidArgumentException(
sprintf('%s should be a class or an interface', $class)
\sprintf('%s should be a class or an interface', $class),
);
}
}
Expand All @@ -61,13 +69,13 @@ private function guardIfClassExists(string $class): void
{
if (false === $this->classExists($class)) {
throw new \InvalidArgumentException(
sprintf('%s should be a class', $class)
\sprintf('%s should be a class', $class),
);
}
}

private function classExists(string $class): bool
{
return class_exists($class);
return \class_exists($class);
}
}
25 changes: 13 additions & 12 deletions src/Core/ApixceptionSubscriber.php
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
<?php
namespace Pccomponentes\Apixception\Core;
declare(strict_types=1);

use Symfony\Component\HttpFoundation\Response;
use Pccomponentes\Apixception\Core\Transformer\ExceptionTransformer;
namespace PcComponentes\Apixception\Core;

class ApixceptionSubscriber
use PcComponentes\Apixception\Core\Transformer\ExceptionTransformer;

final class ApixceptionSubscriber
{
private $exception;
private $httpCode;
private $transformer;
private string $exception;
private int $httpCode;
private ExceptionTransformer $transformer;

public function __construct(string $exception, int $httpCode, ExceptionTransformer $transformer)
{
Expand All @@ -17,18 +18,18 @@ public function __construct(string $exception, int $httpCode, ExceptionTransform
$this->transformer = $transformer;
}

public function httpCode(): int
public function exception(): string
{
return $this->httpCode;
return $this->exception;
}

public function exception(): string
public function httpCode(): int
{
return $this->exception;
return $this->httpCode;
}

public function transform(\Throwable $exception): array
{
return $this->transformer->transform($exception);
}
}
}
11 changes: 7 additions & 4 deletions src/Core/Transformer/ExceptionTransformer.php
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
<?php
namespace Pccomponentes\Apixception\Core\Transformer;
declare(strict_types=1);

namespace PcComponentes\Apixception\Core\Transformer;

abstract class ExceptionTransformer
{
abstract public function transform(\Throwable $exception): array;

final public function __construct()
{
// Disabled constructor
}

public abstract function transform(\Throwable $exception): array;
}
}
Loading

0 comments on commit 4a7e865

Please sign in to comment.