Skip to content
Merged
Show file tree
Hide file tree
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
65 changes: 34 additions & 31 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,52 +27,58 @@ Documentation is available at: https://docs.dotkernel.org/api-documentation/

## Getting Started

## Step 1: Clone the project
### 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:
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:

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

## Step 2: Install project's dependencies
### Install the project dependencies

```shell
composer install
```

## Step 3: Development mode
### Development mode

If you're installing the project for development, make sure you have development mode enabled, by running:
> **Do not enable development mode in production!**

If you're installing the project for development, you should **enable** development mode, by running:

```shell
composer development-enable
```

You can disable development mode by running:
You can **disable** development mode by running:

```shell
composer development-disable
```

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

```shell
composer development-status
```

## Step 4: Prepare config files
### Prepare config files

* 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`
* duplicate `config/autoload/local.php.dist` as `config/autoload/local.php`
* 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
* **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

## Step 5: Setup database
### Setup database

Use an existing empty one or create a new **MariaDB**/**MySQL** database.

> Recommended collation: `utf8mb4_general_ci`.

## Running migrations
#### Running migrations

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

Expand All @@ -82,13 +88,13 @@ 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 "`<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.

## Executing fixtures
#### Executing fixtures

**Fixtures are used to seed the database with initial values and should be executed after migrating the database.**
Fixtures are used to seed the database with initial values and must be executed after migrating the database.

To list all the fixtures, run:

Expand All @@ -110,29 +116,26 @@ To execute a specific fixture, run:
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
More details on how fixtures work can be found in `dotkernel/dot-data-fixtures` [documentation](https://github.com/dotkernel/dot-data-fixtures#creating-fixtures).

### Mail configuration

If your application will send emails, you must configure an outgoing mail server under `config/autoload/mail.global.php`.

### Test the installation

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

```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:
> Running command `composer serve` will do the same thing, but the server will time out after a couple of minutes.

```text
Sending a **GET** request to the application's [home page](http://localhost:8080/) should output the following message:

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

## Documentation

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:

* [Create admin account](documentation/command/admin-create.md)
* [Generate database migrations](documentation/command/migrations-diff.md)
* [Display available endpoints](documentation/command/route-list.md)
* [Generate tokens](documentation/command/token-generate.md)
5 changes: 5 additions & 0 deletions config/autoload/error-handling.global.php
Original file line number Diff line number Diff line change
Expand Up @@ -81,5 +81,10 @@
* 3. If you want to whitelist only specific IP addresses, add them to ip_whitelist.
*/
'ip_whitelist' => [],

/**
* Documentation URL for problem details response
*/
'documentation_url' => 'https://docs.dotkernel.org/api-documentation/v5/core-features/error-reporting/',
],
];
36 changes: 36 additions & 0 deletions config/autoload/problem-details.global.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<?php

declare(strict_types=1);

use Fig\Http\Message\StatusCodeInterface;

return [
/**
* Unless specified when creating an error response,
* 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://datatracker.ietf.org/doc/html/rfc9110#name-400-bad-request',
StatusCodeInterface::STATUS_UNAUTHORIZED
=> 'https://datatracker.ietf.org/doc/html/rfc9110#name-401-unauthorized',
StatusCodeInterface::STATUS_FORBIDDEN
=> 'https://datatracker.ietf.org/doc/html/rfc9110#name-403-forbidden',
StatusCodeInterface::STATUS_NOT_FOUND
=> 'https://datatracker.ietf.org/doc/html/rfc9110#name-404-not-found',
StatusCodeInterface::STATUS_METHOD_NOT_ALLOWED
=> 'https://datatracker.ietf.org/doc/html/rfc9110#name-405-method-not-allowed',
StatusCodeInterface::STATUS_NOT_ACCEPTABLE
=> 'https://datatracker.ietf.org/doc/html/rfc9110#name-406-not-acceptable',
StatusCodeInterface::STATUS_CONFLICT
=> 'https://datatracker.ietf.org/doc/html/rfc9110#name-409-conflict',
StatusCodeInterface::STATUS_GONE
=> 'https://datatracker.ietf.org/doc/html/rfc9110#name-410-gone',
StatusCodeInterface::STATUS_UNSUPPORTED_MEDIA_TYPE
=> 'https://datatracker.ietf.org/doc/html/rfc9110#name-415-unsupported-media-type',
StatusCodeInterface::STATUS_INTERNAL_SERVER_ERROR
=> 'https://datatracker.ietf.org/doc/html/rfc9110#name-500-internal-server-error',
],
],
];
4 changes: 4 additions & 0 deletions config/pipeline.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,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;
Expand All @@ -25,6 +27,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);
Expand Down Expand Up @@ -87,5 +90,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(GetNotFoundViewHandler::class);
};
14 changes: 9 additions & 5 deletions documentation/Dotkernel_API.postman_collection.json
Original file line number Diff line number Diff line change
Expand Up @@ -129,21 +129,25 @@
{
"key": "page",
"value": "1",
"description": "Page number",
"disabled": true
},
{
"key": "limit",
"value": "10",
"description": "Items per page",
"disabled": true
},
{
"key": "sort",
"value": "admin.created",
"description": "Sort items by this field",
"disabled": true
},
{
"key": "dir",
"value": "desc",
"description": "Sort items in this direction",
"disabled": true
},
{
Expand Down Expand Up @@ -182,13 +186,13 @@
}
},
"url": {
"raw": "{{APPLICATION_URL}}/admin/abd444ed-cc65-41e7-96c7-2391a9103f15",
"raw": "{{APPLICATION_URL}}/admin/{{$randomUUID}}",
"host": [
"{{APPLICATION_URL}}"
],
"path": [
"admin",
"abd444ed-cc65-41e7-96c7-2391a9103f15"
"{{$randomUUID}}"
]
},
"description": "Admin updates admin account.\n\nReplace random UUID in URL with a valid user UUID."
Expand Down Expand Up @@ -1268,13 +1272,13 @@
{
"key": "sort",
"value": "user.created",
"description": "order results by this field",
"description": "Sort items by this field",
"disabled": true
},
{
"key": "dir",
"value": "desc",
"description": "order results direction",
"description": "Sort items in this direction",
"disabled": true
},
{
Expand Down Expand Up @@ -1471,4 +1475,4 @@
"value": "597155ec70defb9f969c9beaf609814933db53cbcb8b9be6db5e0bf7e051e1e4"
}
]
}
}
16 changes: 10 additions & 6 deletions src/Admin/src/Command/AdminCreateCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,15 @@
use Api\Admin\InputFilter\CreateAdminInputFilter;
use Api\Admin\Service\AdminRoleServiceInterface;
use Api\Admin\Service\AdminServiceInterface;
use Api\App\Exception\BadRequestException;
use Api\App\Exception\ConflictException;
use Api\App\Exception\NotFoundException;
use Core\Admin\Entity\AdminRole;
use Core\Admin\Enum\AdminRoleEnum;
use Core\App\Exception\BadRequestException;
use Core\App\Exception\ConflictException;
use Core\App\Exception\NotFoundException;
use Core\Admin\Enum\AdminStatusEnum;
use Core\App\Message;
use Dot\DependencyInjection\Attribute\Inject;
use Exception;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
Expand Down Expand Up @@ -63,6 +65,7 @@ protected function configure(): void
/**
* @throws BadRequestException
* @throws ConflictException
* @throws Exception
* @throws NotFoundException
*/
protected function execute(InputInterface $input, OutputInterface $output): int
Expand All @@ -76,7 +79,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int
}
}

throw new BadRequestException(implode(PHP_EOL, $messages));
throw new Exception(implode(PHP_EOL, $messages));
}

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

/**
* @throws NotFoundException
* @throws Exception
*/
private function getData(InputInterface $input): array
{
$adminRole = $this->adminRoleService->getAdminRoleRepository()->findOneBy(['name' => AdminRoleEnum::Admin]);
if (! $adminRole instanceof AdminRole) {
throw new NotFoundException(Message::ROLE_NOT_FOUND);
throw new Exception(Message::ROLE_NOT_FOUND);
}

return [
Expand All @@ -102,6 +105,7 @@ private function getData(InputInterface $input): array
'passwordConfirm' => $input->getOption('password'),
'firstName' => $input->getOption('firstName'),
'lastName' => $input->getOption('lastName'),
'status' => AdminStatusEnum::Active->value,
'roles' => [
['uuid' => $adminRole->getUuid()->toString()],
],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,12 @@

use Api\Admin\InputFilter\UpdateAdminInputFilter;
use Api\Admin\Service\AdminServiceInterface;
use Api\App\Exception\BadRequestException;
use Api\App\Exception\ConflictException;
use Api\App\Exception\NotFoundException;
use Api\App\Handler\AbstractHandler;
use Core\Admin\Entity\Admin;
use Core\App\Exception\BadRequestException;
use Core\App\Exception\ConflictException;
use Core\App\Exception\NotFoundException;
use Core\App\Message;
use Dot\DependencyInjection\Attribute\Inject;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
Expand All @@ -36,7 +37,10 @@ public function handle(ServerRequestInterface $request): ResponseInterface
{
$this->inputFilter->setData((array) $request->getParsedBody());
if (! $this->inputFilter->isValid()) {
throw (new BadRequestException())->setMessages($this->inputFilter->getMessages());
throw BadRequestException::create(
detail: Message::VALIDATOR_INVALID_DATA,
additional: ['errors' => $this->inputFilter->getMessages()]
);
}

return $this->createResponse(
Expand Down
2 changes: 1 addition & 1 deletion src/Admin/src/Handler/Admin/DeleteAdminResourceHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
namespace Api\Admin\Handler\Admin;

use Api\Admin\Service\AdminServiceInterface;
use Api\App\Exception\NotFoundException;
use Api\App\Handler\AbstractHandler;
use Core\App\Exception\NotFoundException;
use Dot\DependencyInjection\Attribute\Inject;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
Expand Down
2 changes: 1 addition & 1 deletion src/Admin/src/Handler/Admin/GetAdminResourceHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
namespace Api\Admin\Handler\Admin;

use Api\Admin\Service\AdminServiceInterface;
use Api\App\Exception\NotFoundException;
use Api\App\Handler\AbstractHandler;
use Core\App\Exception\NotFoundException;
use Dot\DependencyInjection\Attribute\Inject;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
Expand Down
Loading