Skip to content

Commit dad3d07

Browse files
authored
Merge pull request #402 from dotkernel/issue-314-2
Issue #314: Implemented `mezzio/mezzio-problem-details`
2 parents fcb3a9f + bc6e5a8 commit dad3d07

File tree

85 files changed

+598
-456
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

85 files changed

+598
-456
lines changed

README.md

Lines changed: 34 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -27,52 +27,58 @@ Documentation is available at: https://docs.dotkernel.org/api-documentation/
2727

2828
## Getting Started
2929

30-
## Step 1: Clone the project
30+
### Clone the project
3131

32-
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:
32+
Using your terminal, navigate inside the directory you want to download the project files into.
33+
Make sure that the directory is empty before proceeding to the download process.
34+
Once there, run the following command:
3335

3436
```shell
3537
git clone https://github.com/dotkernel/api.git .
3638
```
3739

38-
## Step 2: Install project's dependencies
40+
### Install the project dependencies
3941

4042
```shell
4143
composer install
4244
```
4345

44-
## Step 3: Development mode
46+
### Development mode
4547

46-
If you're installing the project for development, make sure you have development mode enabled, by running:
48+
> **Do not enable development mode in production!**
49+
50+
If you're installing the project for development, you should **enable** development mode, by running:
4751

4852
```shell
4953
composer development-enable
5054
```
5155

52-
You can disable development mode by running:
56+
You can **disable** development mode by running:
5357

5458
```shell
5559
composer development-disable
5660
```
5761

58-
You can check if you have development mode enabled by running:
62+
You can **check** development status by running:
5963

6064
```shell
6165
composer development-status
6266
```
6367

64-
## Step 4: Prepare config files
68+
### Prepare config files
6569

6670
* duplicate `config/autoload/cors.local.php.dist` as `config/autoload/cors.local.php` <- if your API will be consumed by another application, make sure configure the `allowed_origins`
6771
* duplicate `config/autoload/local.php.dist` as `config/autoload/local.php`
68-
* duplicate `config/autoload/mail.local.php.dist` as `config/autoload/mail.local.php` <- if your API will send emails, make sure you fill in SMTP connection params
6972
* **optional**: in order to run/create tests, duplicate `config/autoload/local.test.php.dist` as `config/autoload/local.test.php` <- this creates a new in-memory database that your tests will run on
7073

71-
## Step 5: Setup database
74+
### Setup database
75+
76+
Use an existing empty one or create a new **MariaDB**/**MySQL** database.
77+
78+
> Recommended collation: `utf8mb4_general_ci`.
7279
73-
## Running migrations
80+
#### Running migrations
7481

75-
* create a new MySQL database - set collation to `utf8mb4_general_ci`
7682
* fill out the database connection params in `config/autoload/local.php` under `$databases['default']`
7783
* run the database migrations by using the following command:
7884

@@ -82,13 +88,13 @@ php ./vendor/bin/doctrine-migrations migrate
8288

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

85-
> 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]:
91+
> WARNING! You are about to execute a migration in database "`<database>`" that could result in schema changes and data loss. Are you sure you wish to continue? (yes/no) [yes]:
8692
8793
Hit `Enter` to confirm the operation.
8894

89-
## Executing fixtures
95+
#### Executing fixtures
9096

91-
**Fixtures are used to seed the database with initial values and should be executed after migrating the database.**
97+
Fixtures are used to seed the database with initial values and must be executed after migrating the database.
9298

9399
To list all the fixtures, run:
94100

@@ -110,29 +116,26 @@ To execute a specific fixture, run:
110116
php ./bin/doctrine fixtures:execute --class=FixtureClassName
111117
```
112118

113-
More details on how fixtures work can be found here: https://github.com/dotkernel/dot-data-fixtures#creating-fixtures
119+
More details on how fixtures work can be found in `dotkernel/dot-data-fixtures` [documentation](https://github.com/dotkernel/dot-data-fixtures#creating-fixtures).
120+
121+
### Mail configuration
122+
123+
If your application will send emails, you must configure an outgoing mail server under `config/autoload/mail.global.php`.
124+
125+
### Test the installation
114126

115-
## Step 6: Test the installation
127+
Run the following command in your project's directory to start PHPs built-in server:
116128

117129
```shell
118130
php -S 0.0.0.0:8080 -t public
119131
```
120132

121-
Sending a GET request to the [home page](http://0.0.0.0:8080/) should output the following message:
133+
> Running command `composer serve` will do the same thing, but the server will time out after a couple of minutes.
122134
123-
```text
135+
Sending a **GET** request to the application's [home page](http://localhost:8080/) should output the following message:
136+
137+
```json
124138
{
125-
"message": "Dotkernel API version 5"
139+
"message": "Dotkernel API version 6"
126140
}
127141
```
128-
129-
## Documentation
130-
131-
In order to access Dotkernel API documentation, check the provided [readme file](documentation/README.md).
132-
133-
Additionally, each CLI command available has it's own documentation:
134-
135-
* [Create admin account](documentation/command/admin-create.md)
136-
* [Generate database migrations](documentation/command/migrations-diff.md)
137-
* [Display available endpoints](documentation/command/route-list.md)
138-
* [Generate tokens](documentation/command/token-generate.md)

config/autoload/error-handling.global.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,5 +81,10 @@
8181
* 3. If you want to whitelist only specific IP addresses, add them to ip_whitelist.
8282
*/
8383
'ip_whitelist' => [],
84+
85+
/**
86+
* Documentation URL for problem details response
87+
*/
88+
'documentation_url' => 'https://docs.dotkernel.org/api-documentation/v5/core-features/error-reporting/',
8489
],
8590
];
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
use Fig\Http\Message\StatusCodeInterface;
6+
7+
return [
8+
/**
9+
* Unless specified when creating an error response,
10+
* mezzio-problem-details will set type based on the status code specified in the exception
11+
*/
12+
'problem-details' => [
13+
'default_types_map' => [
14+
StatusCodeInterface::STATUS_BAD_REQUEST
15+
=> 'https://datatracker.ietf.org/doc/html/rfc9110#name-400-bad-request',
16+
StatusCodeInterface::STATUS_UNAUTHORIZED
17+
=> 'https://datatracker.ietf.org/doc/html/rfc9110#name-401-unauthorized',
18+
StatusCodeInterface::STATUS_FORBIDDEN
19+
=> 'https://datatracker.ietf.org/doc/html/rfc9110#name-403-forbidden',
20+
StatusCodeInterface::STATUS_NOT_FOUND
21+
=> 'https://datatracker.ietf.org/doc/html/rfc9110#name-404-not-found',
22+
StatusCodeInterface::STATUS_METHOD_NOT_ALLOWED
23+
=> 'https://datatracker.ietf.org/doc/html/rfc9110#name-405-method-not-allowed',
24+
StatusCodeInterface::STATUS_NOT_ACCEPTABLE
25+
=> 'https://datatracker.ietf.org/doc/html/rfc9110#name-406-not-acceptable',
26+
StatusCodeInterface::STATUS_CONFLICT
27+
=> 'https://datatracker.ietf.org/doc/html/rfc9110#name-409-conflict',
28+
StatusCodeInterface::STATUS_GONE
29+
=> 'https://datatracker.ietf.org/doc/html/rfc9110#name-410-gone',
30+
StatusCodeInterface::STATUS_UNSUPPORTED_MEDIA_TYPE
31+
=> 'https://datatracker.ietf.org/doc/html/rfc9110#name-415-unsupported-media-type',
32+
StatusCodeInterface::STATUS_INTERNAL_SERVER_ERROR
33+
=> 'https://datatracker.ietf.org/doc/html/rfc9110#name-500-internal-server-error',
34+
],
35+
],
36+
];

config/pipeline.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
use Mezzio\Helper\BodyParams\BodyParamsMiddleware;
1616
use Mezzio\Helper\ServerUrlMiddleware;
1717
use Mezzio\Helper\UrlHelperMiddleware;
18+
use Mezzio\ProblemDetails\ProblemDetailsMiddleware;
19+
use Mezzio\ProblemDetails\ProblemDetailsNotFoundHandler;
1820
use Mezzio\Router\Middleware\DispatchMiddleware;
1921
use Mezzio\Router\Middleware\ImplicitHeadMiddleware;
2022
use Mezzio\Router\Middleware\ImplicitOptionsMiddleware;
@@ -25,6 +27,7 @@
2527
// The error handler should be the first (most outer) middleware to catch
2628
// all Exceptions.
2729
$app->pipe(ErrorHandlerInterface::class);
30+
$app->pipe(ProblemDetailsMiddleware::class);
2831

2932
$app->pipe(BodyParamsMiddleware::class);
3033
$app->pipe(ServerUrlMiddleware::class);
@@ -87,5 +90,6 @@
8790
// At this point, if no Response is returned by any middleware, the
8891
// NotFoundHandler kicks in; alternately, you can provide other fallback
8992
// middleware to execute.
93+
$app->pipe(ProblemDetailsNotFoundHandler::class);
9094
$app->pipe(GetNotFoundViewHandler::class);
9195
};

documentation/Dotkernel_API.postman_collection.json

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -129,21 +129,25 @@
129129
{
130130
"key": "page",
131131
"value": "1",
132+
"description": "Page number",
132133
"disabled": true
133134
},
134135
{
135136
"key": "limit",
136137
"value": "10",
138+
"description": "Items per page",
137139
"disabled": true
138140
},
139141
{
140142
"key": "sort",
141143
"value": "admin.created",
144+
"description": "Sort items by this field",
142145
"disabled": true
143146
},
144147
{
145148
"key": "dir",
146149
"value": "desc",
150+
"description": "Sort items in this direction",
147151
"disabled": true
148152
},
149153
{
@@ -182,13 +186,13 @@
182186
}
183187
},
184188
"url": {
185-
"raw": "{{APPLICATION_URL}}/admin/abd444ed-cc65-41e7-96c7-2391a9103f15",
189+
"raw": "{{APPLICATION_URL}}/admin/{{$randomUUID}}",
186190
"host": [
187191
"{{APPLICATION_URL}}"
188192
],
189193
"path": [
190194
"admin",
191-
"abd444ed-cc65-41e7-96c7-2391a9103f15"
195+
"{{$randomUUID}}"
192196
]
193197
},
194198
"description": "Admin updates admin account.\n\nReplace random UUID in URL with a valid user UUID."
@@ -1268,13 +1272,13 @@
12681272
{
12691273
"key": "sort",
12701274
"value": "user.created",
1271-
"description": "order results by this field",
1275+
"description": "Sort items by this field",
12721276
"disabled": true
12731277
},
12741278
{
12751279
"key": "dir",
12761280
"value": "desc",
1277-
"description": "order results direction",
1281+
"description": "Sort items in this direction",
12781282
"disabled": true
12791283
},
12801284
{
@@ -1471,4 +1475,4 @@
14711475
"value": "597155ec70defb9f969c9beaf609814933db53cbcb8b9be6db5e0bf7e051e1e4"
14721476
}
14731477
]
1474-
}
1478+
}

src/Admin/src/Command/AdminCreateCommand.php

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,15 @@
77
use Api\Admin\InputFilter\CreateAdminInputFilter;
88
use Api\Admin\Service\AdminRoleServiceInterface;
99
use Api\Admin\Service\AdminServiceInterface;
10+
use Api\App\Exception\BadRequestException;
11+
use Api\App\Exception\ConflictException;
12+
use Api\App\Exception\NotFoundException;
1013
use Core\Admin\Entity\AdminRole;
1114
use Core\Admin\Enum\AdminRoleEnum;
12-
use Core\App\Exception\BadRequestException;
13-
use Core\App\Exception\ConflictException;
14-
use Core\App\Exception\NotFoundException;
15+
use Core\Admin\Enum\AdminStatusEnum;
1516
use Core\App\Message;
1617
use Dot\DependencyInjection\Attribute\Inject;
18+
use Exception;
1719
use Symfony\Component\Console\Attribute\AsCommand;
1820
use Symfony\Component\Console\Command\Command;
1921
use Symfony\Component\Console\Input\InputInterface;
@@ -63,6 +65,7 @@ protected function configure(): void
6365
/**
6466
* @throws BadRequestException
6567
* @throws ConflictException
68+
* @throws Exception
6669
* @throws NotFoundException
6770
*/
6871
protected function execute(InputInterface $input, OutputInterface $output): int
@@ -76,7 +79,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int
7679
}
7780
}
7881

79-
throw new BadRequestException(implode(PHP_EOL, $messages));
82+
throw new Exception(implode(PHP_EOL, $messages));
8083
}
8184

8285
$this->adminService->saveAdmin($inputFilter->getValues());
@@ -87,13 +90,13 @@ protected function execute(InputInterface $input, OutputInterface $output): int
8790
}
8891

8992
/**
90-
* @throws NotFoundException
93+
* @throws Exception
9194
*/
9295
private function getData(InputInterface $input): array
9396
{
9497
$adminRole = $this->adminRoleService->getAdminRoleRepository()->findOneBy(['name' => AdminRoleEnum::Admin]);
9598
if (! $adminRole instanceof AdminRole) {
96-
throw new NotFoundException(Message::ROLE_NOT_FOUND);
99+
throw new Exception(Message::ROLE_NOT_FOUND);
97100
}
98101

99102
return [
@@ -102,6 +105,7 @@ private function getData(InputInterface $input): array
102105
'passwordConfirm' => $input->getOption('password'),
103106
'firstName' => $input->getOption('firstName'),
104107
'lastName' => $input->getOption('lastName'),
108+
'status' => AdminStatusEnum::Active->value,
105109
'roles' => [
106110
['uuid' => $adminRole->getUuid()->toString()],
107111
],

src/Admin/src/Handler/Account/PatchAdminAccountResourceHandler.php

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,12 @@
66

77
use Api\Admin\InputFilter\UpdateAdminInputFilter;
88
use Api\Admin\Service\AdminServiceInterface;
9+
use Api\App\Exception\BadRequestException;
10+
use Api\App\Exception\ConflictException;
11+
use Api\App\Exception\NotFoundException;
912
use Api\App\Handler\AbstractHandler;
1013
use Core\Admin\Entity\Admin;
11-
use Core\App\Exception\BadRequestException;
12-
use Core\App\Exception\ConflictException;
13-
use Core\App\Exception\NotFoundException;
14+
use Core\App\Message;
1415
use Dot\DependencyInjection\Attribute\Inject;
1516
use Psr\Http\Message\ResponseInterface;
1617
use Psr\Http\Message\ServerRequestInterface;
@@ -36,7 +37,10 @@ public function handle(ServerRequestInterface $request): ResponseInterface
3637
{
3738
$this->inputFilter->setData((array) $request->getParsedBody());
3839
if (! $this->inputFilter->isValid()) {
39-
throw (new BadRequestException())->setMessages($this->inputFilter->getMessages());
40+
throw BadRequestException::create(
41+
detail: Message::VALIDATOR_INVALID_DATA,
42+
additional: ['errors' => $this->inputFilter->getMessages()]
43+
);
4044
}
4145

4246
return $this->createResponse(

src/Admin/src/Handler/Admin/DeleteAdminResourceHandler.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@
55
namespace Api\Admin\Handler\Admin;
66

77
use Api\Admin\Service\AdminServiceInterface;
8+
use Api\App\Exception\NotFoundException;
89
use Api\App\Handler\AbstractHandler;
9-
use Core\App\Exception\NotFoundException;
1010
use Dot\DependencyInjection\Attribute\Inject;
1111
use Psr\Http\Message\ResponseInterface;
1212
use Psr\Http\Message\ServerRequestInterface;

src/Admin/src/Handler/Admin/GetAdminResourceHandler.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@
55
namespace Api\Admin\Handler\Admin;
66

77
use Api\Admin\Service\AdminServiceInterface;
8+
use Api\App\Exception\NotFoundException;
89
use Api\App\Handler\AbstractHandler;
9-
use Core\App\Exception\NotFoundException;
1010
use Dot\DependencyInjection\Attribute\Inject;
1111
use Psr\Http\Message\ResponseInterface;
1212
use Psr\Http\Message\ServerRequestInterface;

0 commit comments

Comments
 (0)