Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Issue #314: Implemented mezzio/mezzio-problem-details and handler delegators. #329

Draft
wants to merge 1 commit into
base: 5.0
Choose a base branch
from
Draft
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Issue #314: Implemented mezzio-problem-details and handler delegators.
Signed-off-by: alexmerlin <[email protected]>
  • Loading branch information
alexmerlin committed Oct 21, 2024

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
commit 5a97d6a6cb664d88dc241bfbdda19ace025d9f37
60 changes: 40 additions & 20 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
# DotKernel API
# Dotkernel API

Based on Enrico Zimuel's [Zend Expressive API - Skeleton example](https://github.com/ezimuel/zend-expressive-api), DotKernel API runs on [Laminas](https://github.com/laminas) and [Mezzio](https://github.com/mezzio) components and implements standards like PSR-3, PSR-4, PSR-7, PSR-11 and PSR-15.
Based on Enrico Zimuel's [Zend Expressive API - Skeleton example](https://github.com/ezimuel/zend-expressive-api), Dotkernel API runs on [Laminas](https://github.com/laminas) and [Mezzio](https://github.com/mezzio) components and implements standards like PSR-3, PSR-4, PSR-7, PSR-11 and PSR-15.

![OSS Lifecycle](https://img.shields.io/osslifecycle/dotkernel/api)
![PHP from Packagist (specify version)](https://img.shields.io/packagist/php-v/dotkernel/api/5.0.x-dev)
![PHP from Packagist (specify version)](https://img.shields.io/packagist/php-v/dotkernel/api/5.0.0)

[![GitHub issues](https://img.shields.io/github/issues/dotkernel/api)](https://github.com/dotkernel/api/issues)
[![GitHub forks](https://img.shields.io/github/forks/dotkernel/api)](https://github.com/dotkernel/api/network)
@@ -14,33 +14,41 @@ Based on Enrico Zimuel's [Zend Expressive API - Skeleton example](https://github
[![codecov](https://codecov.io/gh/dotkernel/api/graph/badge.svg?token=53FN78G5CK)](https://codecov.io/gh/dotkernel/api)
[![Qodana](https://github.com/dotkernel/api/actions/workflows/qodana_code_quality.yml/badge.svg?branch=5.0)](https://github.com/dotkernel/api/actions/workflows/qodana_code_quality.yml)

[![SymfonyInsight](https://insight.symfony.com/projects/7f9143cc-5e3c-4cfc-992c-377a001fde3e/big.svg)](https://insight.symfony.com/projects/7f9143cc-5e3c-4cfc-992c-377a001fde3e)

## Getting Started

## Step 1: Clone the project

Using your terminal, navigate inside the directory you want to download the project files into. Make sure that the directory is empty before proceeding to the download process. Once there, run the following command:

git clone https://github.com/dotkernel/api.git .
```shell
git clone https://github.com/dotkernel/api.git .
```

## Step 2: Install project's dependencies

composer install
```shell
composer install
```

## Step 3: Development mode

If you're installing the project for development, make sure you have development mode enabled, by running:

composer development-enable
```shell
composer development-enable
```

You can disable development mode by running:

composer development-disable
```shell
composer development-disable
```

You can check if you have development mode enabled by running:

composer development-status
```shell
composer development-status
```

## Step 4: Prepare config files

@@ -57,11 +65,13 @@ You can check if you have development mode enabled by running:
* fill out the database connection params in `config/autoload/local.php` under `$databases['default']`
* run the database migrations by using the following command:

php vendor/bin/doctrine-migrations migrate
```shell
php vendor/bin/doctrine-migrations migrate
```

This command will prompt you to confirm that you want to run it:

WARNING! You are about to execute a migration in database "..." that could result in schema changes and data loss. Are you sure you wish to continue? (yes/no) [yes]:
> WARNING! You are about to execute a migration in database "..." that could result in schema changes and data loss. Are you sure you wish to continue? (yes/no) [yes]:

Hit `Enter` to confirm the operation.

@@ -71,33 +81,43 @@ Hit `Enter` to confirm the operation.

To list all the fixtures, run:

php bin/doctrine fixtures:list
```shell
php bin/doctrine fixtures:list
```

This will output all the fixtures in the order of execution.

To execute all fixtures, run:

php bin/doctrine fixtures:execute
```shell
php bin/doctrine fixtures:execute
```

To execute a specific fixture, run:

php bin/doctrine fixtures:execute --class=FixtureClassName
```shell
php bin/doctrine fixtures:execute --class=FixtureClassName
```

More details on how fixtures work can be found here: https://github.com/dotkernel/dot-data-fixtures#creating-fixtures

## Step 6: Test the installation

php -S 0.0.0.0:8080 -t public
```shell
php -S 0.0.0.0:8080 -t public
```

Sending a GET request to the [home page](http://0.0.0.0:8080/) should output the following message:

{
"message": "DotKernel API version 5"
}
```json
{
"message": "Dotkernel API version 5"
}
```

## Documentation

In order to access DotKernel API documentation, check the provided [readme file](documentation/README.md).
In order to access Dotkernel API documentation, check the provided [readme file](documentation/README.md).

Additionally, each CLI command available has it's own documentation:

8 changes: 4 additions & 4 deletions composer.json
Original file line number Diff line number Diff line change
@@ -27,9 +27,9 @@
"sort-packages": true,
"allow-plugins": {
"dotkernel/*": true,
"laminas/laminas-component-installer": true,
"composer/package-versions-deprecated": true,
"dealerdirect/phpcodesniffer-composer-installer": true
"dealerdirect/phpcodesniffer-composer-installer": true,
"laminas/laminas-component-installer": true
}
},
"extra": {
@@ -58,7 +58,7 @@
"dotkernel/dot-errorhandler": "^4.0.0",
"dotkernel/dot-mail": "^4.1.1",
"dotkernel/dot-response-header": "^3.2.3",
"laminas/laminas-component-installer": "^3.4.0",
"laminas/laminas-component-installer": "^3.4",
"laminas/laminas-config": "^3.9.0",
"laminas/laminas-config-aggregator": "^1.14.0",
"laminas/laminas-http": "^2.19.0",
@@ -74,7 +74,7 @@
"mezzio/mezzio-cors": "^1.11.1",
"mezzio/mezzio-fastroute": "^3.11.0",
"mezzio/mezzio-hal": "^2.9",
"mezzio/mezzio-problem-details": "^1.13.1",
"mezzio/mezzio-problem-details": "^1.14",
"mezzio/mezzio-twigrenderer": "^2.15.0",
"ramsey/uuid-doctrine": "^2.1.0",
"roave/psr-container-doctrine": "^5.2.1",
21 changes: 9 additions & 12 deletions config/autoload/local.php.dist
Original file line number Diff line number Diff line change
@@ -2,7 +2,7 @@

declare(strict_types=1);

$baseUrl = 'http://localhost:8080';
$baseUrl = 'http://api.dotkernel.localhost';

$databases = [
'default' => [
@@ -19,14 +19,11 @@ $databases = [
];

return [
'application' => [
'name' => 'DotKernel API',
'url' => $baseUrl,
'versioning' => [
'documentation_url' => 'https://docs.dotkernel.org/api-documentation/v5/core-features/versioning',
],
'application' => [
'name' => 'DotKernel API',
'url' => $baseUrl,
],
'authentication' => [
'authentication' => [
'private_key' => [
'key_or_path' => getcwd() . '/data/oauth/private.key',
'key_permissions_check' => false,
@@ -45,15 +42,15 @@ return [
'message' => 'Invalid credentials.',
],
],
'databases' => $databases,
'doctrine' => [
'connection' => [
'databases' => $databases,
'doctrine' => [
'connection' => [
'orm_default' => [
'params' => $databases['default'],
],
],
],
'uploads' => [
'uploads' => [
'user' => [
'url' => $baseUrl . '/uploads/user',
'path' => realpath(__DIR__ . '/../../public/uploads/user'),
43 changes: 43 additions & 0 deletions config/autoload/problem-details.global.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
<?php

declare(strict_types=1);

use Api\App\Service\ErrorReportServiceInterface;
use Fig\Http\Message\StatusCodeInterface;

return [
/**
* Unless specified when throwing the exception,
* mezzio-problem-details will set type based on the status code specified in the exception
*/
'problem-details' => [
'default_types_map' => [
StatusCodeInterface::STATUS_BAD_REQUEST => 'https://example.com/error/bad-request',
StatusCodeInterface::STATUS_UNAUTHORIZED => 'https://example.com/error/unauthorized',
StatusCodeInterface::STATUS_FORBIDDEN => 'https://example.com/error/forbidden',
StatusCodeInterface::STATUS_NOT_FOUND => 'https://example.com/error/not-found',
StatusCodeInterface::STATUS_METHOD_NOT_ALLOWED => 'https://example.com/error/method-not-allowed',
StatusCodeInterface::STATUS_NOT_ACCEPTABLE => 'https://example.com/error/method-not-acceptable',
StatusCodeInterface::STATUS_CONFLICT => 'https://example.com/error/conflict',
StatusCodeInterface::STATUS_GONE => 'https://example.com/error/gone',
StatusCodeInterface::STATUS_UNSUPPORTED_MEDIA_TYPE => 'https://example.com/error/unsupported-media-type',
StatusCodeInterface::STATUS_INTERNAL_SERVER_ERROR => 'https://example.com/error/internal-server-error',
],
],

/**
* Misc documentation URLs used application-wide
*/
'application' => [
'versioning' => [
'documentation_url' => 'https://docs.dotkernel.org/api-documentation/v5/core-features/versioning',
],
],

/**
* Error-reporting specific documentation URLs
*/
ErrorReportServiceInterface::class => [
'documentation_url' => 'https://example.com/error/not-authorized/error-reporting',
],
];
4 changes: 4 additions & 0 deletions config/pipeline.php
Original file line number Diff line number Diff line change
@@ -14,6 +14,8 @@
use Mezzio\Helper\BodyParams\BodyParamsMiddleware;
use Mezzio\Helper\ServerUrlMiddleware;
use Mezzio\Helper\UrlHelperMiddleware;
use Mezzio\ProblemDetails\ProblemDetailsMiddleware;
use Mezzio\ProblemDetails\ProblemDetailsNotFoundHandler;
use Mezzio\Router\Middleware\DispatchMiddleware;
use Mezzio\Router\Middleware\ImplicitHeadMiddleware;
use Mezzio\Router\Middleware\ImplicitOptionsMiddleware;
@@ -24,6 +26,7 @@
// The error handler should be the first (most outer) middleware to catch
// all Exceptions.
$app->pipe(ErrorHandlerInterface::class);
$app->pipe(ProblemDetailsMiddleware::class);

$app->pipe(BodyParamsMiddleware::class);
$app->pipe(ServerUrlMiddleware::class);
@@ -84,5 +87,6 @@
// At this point, if no Response is returned by any middleware, the
// NotFoundHandler kicks in; alternately, you can provide other fallback
// middleware to execute.
$app->pipe(ProblemDetailsNotFoundHandler::class);
$app->pipe(NotFoundHandler::class);
};
16 changes: 11 additions & 5 deletions src/Admin/src/Command/AdminCreateCommand.php
Original file line number Diff line number Diff line change
@@ -8,7 +8,6 @@
use Api\Admin\InputFilter\CreateAdminInputFilter;
use Api\Admin\Service\AdminRoleService;
use Api\Admin\Service\AdminService;
use Api\App\Exception\BadRequestException;
use Api\App\Exception\ConflictException;
use Api\App\Exception\NotFoundException;
use Api\App\Message;
@@ -17,6 +16,7 @@
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;

use function implode;
use function sprintf;
@@ -54,12 +54,12 @@ protected function configure(): void
}

/**
* @throws BadRequestException
* @throws ConflictException
* @throws NotFoundException
*/
protected function execute(InputInterface $input, OutputInterface $output): int
{
$io = new SymfonyStyle($input, $output);

$inputFilter = (new CreateAdminInputFilter())->setData($this->getData($input));
if (! $inputFilter->isValid()) {
$messages = [];
@@ -69,10 +69,16 @@ protected function execute(InputInterface $input, OutputInterface $output): int
}
}

throw new BadRequestException(implode(PHP_EOL, $messages));
$io->error(implode(PHP_EOL, $messages));
return Command::FAILURE;
}

$this->adminService->createAdmin($inputFilter->getValues());
try {
$this->adminService->createAdmin($inputFilter->getValues());
} catch (ConflictException | NotFoundException $e) {
$io->error($e->getDetail());
return Command::FAILURE;
}

$output->writeln(Message::ADMIN_CREATED);

10 changes: 7 additions & 3 deletions src/Admin/src/ConfigProvider.php
Original file line number Diff line number Diff line change
@@ -22,6 +22,7 @@
use Api\Admin\Service\AdminService;
use Api\Admin\Service\AdminServiceInterface;
use Api\App\ConfigProvider as AppConfigProvider;
use Api\App\Factory\HandlerDelegatorFactory;
use Doctrine\ORM\Mapping\Driver\AttributeDriver;
use Dot\DependencyInjection\Factory\AttributedRepositoryFactory;
use Dot\DependencyInjection\Factory\AttributedServiceFactory;
@@ -43,9 +44,12 @@ public function getDependencies(): array
{
return [
'delegators' => [
Application::class => [
RoutesDelegator::class,
],
Application::class => [RoutesDelegator::class],
AdminHandler::class => [HandlerDelegatorFactory::class],
AdminCollectionHandler::class => [HandlerDelegatorFactory::class],
AdminAccountHandler::class => [HandlerDelegatorFactory::class],
AdminRoleHandler::class => [HandlerDelegatorFactory::class],
AdminRoleCollectionHandler::class => [HandlerDelegatorFactory::class],
],
'factories' => [
AdminHandler::class => AttributedServiceFactory::class,
28 changes: 10 additions & 18 deletions src/Admin/src/Handler/AdminAccountHandler.php
Original file line number Diff line number Diff line change
@@ -10,28 +10,17 @@
use Api\App\Exception\BadRequestException;
use Api\App\Exception\ConflictException;
use Api\App\Exception\NotFoundException;
use Api\App\Handler\HandlerTrait;
use Api\App\Handler\AbstractHandler;
use Api\App\Message;
use Dot\DependencyInjection\Attribute\Inject;
use Mezzio\Hal\HalResponseFactory;
use Mezzio\Hal\ResourceGenerator;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\RequestHandlerInterface;

class AdminAccountHandler implements RequestHandlerInterface
class AdminAccountHandler extends AbstractHandler
{
use HandlerTrait;

#[Inject(
HalResponseFactory::class,
ResourceGenerator::class,
AdminServiceInterface::class,
)]
public function __construct(
protected HalResponseFactory $responseFactory,
protected ResourceGenerator $resourceGenerator,
protected AdminServiceInterface $adminService,
) {
#[Inject(AdminServiceInterface::class)]
public function __construct(protected AdminServiceInterface $adminService)
{
}

public function get(ServerRequestInterface $request): ResponseInterface
@@ -48,7 +37,10 @@ public function patch(ServerRequestInterface $request): ResponseInterface
{
$inputFilter = (new UpdateAdminInputFilter())->setData((array) $request->getParsedBody());
if (! $inputFilter->isValid()) {
throw (new BadRequestException())->setMessages($inputFilter->getMessages());
throw BadRequestException::create(
detail: Message::VALIDATOR_FIX_ERRORS,
additional: ['errors' => $inputFilter->getMessages()],
);
}

$admin = $this->adminService->updateAdmin($request->getAttribute(Admin::class), $inputFilter->getValues());
Loading
Loading