diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 300d307..4eae2c3 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -41,6 +41,7 @@ jobs: - '8.2' - '8.3' - '8.4' + - '8.5' steps: - uses: actions/checkout@v4 @@ -81,6 +82,7 @@ jobs: - '8.2' - '8.3' - '8.4' + - '8.5' services: mariadb: diff --git a/docker-compose.yml b/docker-compose.yml index b96a807..e9c9e40 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -71,7 +71,34 @@ services: networks: - app-network environment: - - PHP_IDE_CONFIG=serverName=rest-api-app-83 + - PHP_IDE_CONFIG=serverName=rest-api-app-84 + - APP_ENV=development + - APP_ENV_ADAPTER=dotenv + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost/fpm-ping"] + interval: 30s + timeout: 3s + retries: 3 + start_period: 5s + + app-8.5: + build: + dockerfile: ./resources/docker/Dockerfile + args: + PHP_VERSION: 8.5 + hostname: rest-api-app-85 + container_name: "${PROJECT_NAME}-api-8.5" + tty: true + working_dir: /app + volumes: + - ./:/app + depends_on: + - app-db + - app-cache + networks: + - app-network + environment: + - PHP_IDE_CONFIG=serverName=rest-api-app-85 - APP_ENV=development - APP_ENV_ADAPTER=dotenv healthcheck: diff --git a/resources/db/migrations/20251226161244_add_companies_table.php b/resources/db/migrations/20251226161244_add_companies_table.php new file mode 100644 index 0000000..107d8af --- /dev/null +++ b/resources/db/migrations/20251226161244_add_companies_table.php @@ -0,0 +1,190 @@ +table('co_companies')->drop()->save(); + } + + public function up(): void + { + $table = $this->table( + 'co_companies', + [ + 'id' => 'com_id', + 'signed' => false, + ] + ); + + $table + ->addColumn( + 'com_name', + 'string', + [ + 'limit' => 128, + 'null' => false, + 'default' => '', + ] + ) + ->addColumn( + 'com_phone', + 'string', + [ + 'limit' => 32, + 'null' => false, + 'default' => '', + ] + ) + ->addColumn( + 'com_email', + 'string', + [ + 'limit' => 128, + 'null' => false, + 'default' => '', + ] + ) + ->addColumn( + 'com_website', + 'string', + [ + 'limit' => 128, + 'null' => false, + 'default' => '', + ] + ) + ->addColumn( + 'com_address_line_1', + 'string', + [ + 'limit' => 128, + 'null' => false, + 'default' => '', + ] + ) + ->addColumn( + 'com_address_line_2', + 'string', + [ + 'limit' => 128, + 'null' => false, + 'default' => '', + ] + ) + ->addColumn( + 'com_city', + 'string', + [ + 'limit' => 128, + 'null' => false, + 'default' => '', + ] + ) + ->addColumn( + 'com_state_province', + 'string', + [ + 'limit' => 64, + 'null' => false, + 'default' => '', + ] + ) + ->addColumn( + 'com_zip_code', + 'string', + [ + 'limit' => 32, + 'null' => false, + 'default' => '', + ] + ) + ->addColumn( + 'com_country', + 'string', + [ + 'limit' => 8, + 'null' => false, + 'default' => '', + ] + ) + ->addColumn( + 'com_created_date', + 'timestamp', + [ + 'timezone' => true, + 'default' => 'CURRENT_TIMESTAMP', + ] + ) + ->addColumn( + 'com_created_usr_id', + 'biginteger', + [ + 'null' => false, + 'default' => 0, + ] + ) + ->addColumn( + 'com_updated_date', + 'timestamp', + [ + 'timezone' => true, + 'default' => null, + ] + ) + ->addColumn( + 'com_updated_usr_id', + 'biginteger', + [ + 'null' => false, + 'default' => 0, + ] + ) + ->addIndex( + [ + 'com_name', + ], + [ + 'name' => 'i_name', + ] + ) + ->addIndex( + [ + 'com_email', + ], + [ + 'name' => 'i_email', + ] + ) + ->addIndex( + [ + 'com_city', + ], + [ + 'name' => 'i_city', + ] + ) + ->addIndex( + [ + 'com_state_province', + ], + [ + 'name' => 'i_state_province', + ] + ) + ->addIndex( + [ + 'com_country', + ], + [ + 'name' => 'i_country', + ] + ) + ->save() + ; + } +} diff --git a/src/Domain/ADR/InputTypes.php b/src/Domain/ADR/InputTypes.php index 43a1471..1f3c666 100644 --- a/src/Domain/ADR/InputTypes.php +++ b/src/Domain/ADR/InputTypes.php @@ -29,6 +29,26 @@ * token?: string * } * + * @phpstan-type TCompanyInput array{ + * id?: int, + * name?: string, + * phone?: string, + * email?: string, + * website?: string, + * addressLine1?: string, + * addressLine2?: string, + * city?: string, + * stateProvince?: string, + * zipCode?: string, + * country?: string, + * createdDate?: string, + * createdUserId?: int, + * updatedDate?: string, + * updatedUserId?: int, + * page?: int, + * perPage?: int + * } + * * @phpstan-type TUserInput array{ * id?: int, * status?: int, diff --git a/src/Domain/Application/Auth/Facade/AuthFacade.php b/src/Domain/Application/Auth/Facade/AuthFacade.php index c32dffb..b0cc23b 100644 --- a/src/Domain/Application/Auth/Facade/AuthFacade.php +++ b/src/Domain/Application/Auth/Facade/AuthFacade.php @@ -26,7 +26,8 @@ final readonly class AuthFacade { /** - * @param CommandBusInterface $bus + * @param CommandBusInterface $bus + * @param AuthCommandFactoryInterface $factory */ public function __construct( private CommandBusInterface $bus, diff --git a/src/Domain/Application/Auth/Handler/AbstractAuthLogoutRefreshHandler.php b/src/Domain/Application/Auth/Handler/AbstractAuthLogoutRefreshHandler.php index 9b190da..8a124c3 100644 --- a/src/Domain/Application/Auth/Handler/AbstractAuthLogoutRefreshHandler.php +++ b/src/Domain/Application/Auth/Handler/AbstractAuthLogoutRefreshHandler.php @@ -15,6 +15,7 @@ use Phalcon\Api\Domain\ADR\InputTypes; use Phalcon\Api\Domain\Infrastructure\CommandBus\HandlerInterface; +use Phalcon\Api\Domain\Infrastructure\DataSource\Auth\Transformer\AuthTransformer; use Phalcon\Api\Domain\Infrastructure\DataSource\Validation\ValidatorInterface; use Phalcon\Api\Domain\Infrastructure\Encryption\TokenManagerInterface; @@ -25,10 +26,12 @@ abstract class AbstractAuthLogoutRefreshHandler implements HandlerInterface { /** * @param TokenManagerInterface $tokenManager + * @param AuthTransformer $transformer * @param ValidatorInterface $validator */ public function __construct( protected readonly TokenManagerInterface $tokenManager, + protected readonly AuthTransformer $transformer, protected readonly ValidatorInterface $validator, ) { } diff --git a/src/Domain/Application/Auth/Handler/AuthLoginPostHandler.php b/src/Domain/Application/Auth/Handler/AuthLoginPostHandler.php index 0f7365b..3521de6 100644 --- a/src/Domain/Application/Auth/Handler/AuthLoginPostHandler.php +++ b/src/Domain/Application/Auth/Handler/AuthLoginPostHandler.php @@ -18,6 +18,7 @@ use Phalcon\Api\Domain\Application\Auth\Command\AuthLoginPostCommand; use Phalcon\Api\Domain\Infrastructure\CommandBus\CommandInterface; use Phalcon\Api\Domain\Infrastructure\CommandBus\HandlerInterface; +use Phalcon\Api\Domain\Infrastructure\DataSource\Auth\Transformer\AuthTransformer; use Phalcon\Api\Domain\Infrastructure\DataSource\User\Repository\UserRepositoryInterface; use Phalcon\Api\Domain\Infrastructure\DataSource\Validation\ValidatorInterface; use Phalcon\Api\Domain\Infrastructure\Encryption\Security; @@ -27,19 +28,21 @@ /** * @phpstan-import-type TAuthLoginInput from InputTypes */ -final class AuthLoginPostHandler implements HandlerInterface +final readonly class AuthLoginPostHandler implements HandlerInterface { /** * @param UserRepositoryInterface $repository * @param TokenManagerInterface $tokenManager + * @param AuthTransformer $transformer * @param Security $security * @param ValidatorInterface $validator */ public function __construct( - private readonly UserRepositoryInterface $repository, - private readonly TokenManagerInterface $tokenManager, - private readonly Security $security, - private readonly ValidatorInterface $validator + private UserRepositoryInterface $repository, + private TokenManagerInterface $tokenManager, + private AuthTransformer $transformer, + private Security $security, + private ValidatorInterface $validator ) { } @@ -87,22 +90,6 @@ public function __invoke(CommandInterface $command): Payload */ $tokens = $this->tokenManager->issue($domainUser); - /** - * Construct the response - */ - $results = [ - 'authenticated' => true, - 'user' => [ - 'id' => $domainUser->id, - 'name' => $domainUser->fullName(), - 'email' => $domainUser->email, - ], - 'jwt' => [ - 'token' => $tokens['token'], - 'refreshToken' => $tokens['refreshToken'], - ], - ]; - - return Payload::success($results); + return Payload::success($this->transformer->login($domainUser, $tokens)); } } diff --git a/src/Domain/Application/Auth/Handler/AuthLogoutPostHandler.php b/src/Domain/Application/Auth/Handler/AuthLogoutPostHandler.php index 26f1dc7..0ca76f3 100644 --- a/src/Domain/Application/Auth/Handler/AuthLogoutPostHandler.php +++ b/src/Domain/Application/Auth/Handler/AuthLogoutPostHandler.php @@ -49,6 +49,6 @@ public function __invoke(CommandInterface $command): Payload $this->tokenManager->revoke($domainUser); - return Payload::success(['authenticated' => false]); + return Payload::success($this->transformer->logout()); } } diff --git a/src/Domain/Application/Auth/Handler/AuthRefreshPostHandler.php b/src/Domain/Application/Auth/Handler/AuthRefreshPostHandler.php index be368c1..15f1412 100644 --- a/src/Domain/Application/Auth/Handler/AuthRefreshPostHandler.php +++ b/src/Domain/Application/Auth/Handler/AuthRefreshPostHandler.php @@ -49,9 +49,6 @@ public function __invoke(CommandInterface $command): Payload $tokens = $this->tokenManager->refresh($domainUser); - return Payload::success([ - 'token' => $tokens['token'], - 'refreshToken' => $tokens['refreshToken'], - ]); + return Payload::success($this->transformer->refresh($tokens)); } } diff --git a/src/Domain/Application/Company/Command/CompanyCommandFactory.php b/src/Domain/Application/Company/Command/CompanyCommandFactory.php new file mode 100644 index 0000000..b8defa2 --- /dev/null +++ b/src/Domain/Application/Company/Command/CompanyCommandFactory.php @@ -0,0 +1,172 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Phalcon\Api\Domain\Application\Company\Command; + +use Phalcon\Api\Domain\ADR\InputTypes; +use Phalcon\Api\Domain\Infrastructure\DataSource\Company\DTO\CompanyInput; +use Phalcon\Api\Domain\Infrastructure\DataSource\Interface\SanitizerInterface; + +/** + * @phpstan-import-type TCompanyInput from InputTypes + */ +final class CompanyCommandFactory implements CompanyCommandFactoryInterface +{ + /** + * @param SanitizerInterface $sanitizer + */ + public function __construct( + private readonly SanitizerInterface $sanitizer + ) { + } + + /** + * @param TCompanyInput $input + * + * @return CompanyDeleteCommand + */ + public function delete(array $input): CompanyDeleteCommand + { + /** + * Sanitize the input + */ + $dto = CompanyInput::new($this->sanitizer, $input); + + /** + * Return the DTO back + */ + return new CompanyDeleteCommand($dto->id); + } + + /** + * @param TCompanyInput $input + * + * @return CompanyGetCommand + */ + public function get(array $input): CompanyGetCommand + { + /** + * Sanitize the input + */ + $dto = CompanyInput::new($this->sanitizer, $input); + + /** + * Return the DTO back + */ + return new CompanyGetCommand($dto->id); + } + + /** + * @param TCompanyInput $input + * + * @return CompanyGetManyCommand + */ + public function getMany(array $input): CompanyGetManyCommand + { + /** + * Sanitize the input + */ + $dto = CompanyInput::new($this->sanitizer, $input); + + /** + * Return the DTO back + */ + return new CompanyGetManyCommand( + $dto->id, + $dto->name, + $dto->phone, + $dto->email, + $dto->website, + $dto->addressLine1, + $dto->addressLine2, + $dto->city, + $dto->stateProvince, + $dto->zipCode, + $dto->country, + $dto->createdDate, + $dto->createdUserId, + $dto->updatedDate, + $dto->updatedUserId, + $dto->page, + $dto->perPage, + ); + } + + /** + * @param TCompanyInput $input + * + * @return CompanyPostCommand + */ + public function insert(array $input): CompanyPostCommand + { + /** + * Sanitize the input + */ + $dto = CompanyInput::new($this->sanitizer, $input); + + /** + * Return the DTO back + */ + return new CompanyPostCommand( + null, + $dto->name, + $dto->phone, + $dto->email, + $dto->website, + $dto->addressLine1, + $dto->addressLine2, + $dto->city, + $dto->stateProvince, + $dto->zipCode, + $dto->country, + $dto->createdDate, + $dto->createdUserId, + $dto->updatedDate, + $dto->updatedUserId, + ); + } + + /** + * @param TCompanyInput $input + * + * @return CompanyPutCommand + */ + public function update(array $input): CompanyPutCommand + { + /** + * Sanitize the input + */ + $dto = CompanyInput::new($this->sanitizer, $input); + + /** + * Return the DTO back + */ + return new CompanyPutCommand( + $dto->id, + $dto->name, + $dto->phone, + $dto->email, + $dto->website, + $dto->addressLine1, + $dto->addressLine2, + $dto->city, + $dto->stateProvince, + $dto->zipCode, + $dto->country, + $dto->createdDate, + $dto->createdUserId, + $dto->updatedDate, + $dto->updatedUserId, + ); + } +} diff --git a/src/Domain/Application/Company/Command/CompanyCommandFactoryInterface.php b/src/Domain/Application/Company/Command/CompanyCommandFactoryInterface.php new file mode 100644 index 0000000..985a0f7 --- /dev/null +++ b/src/Domain/Application/Company/Command/CompanyCommandFactoryInterface.php @@ -0,0 +1,57 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Phalcon\Api\Domain\Application\Company\Command; + +use Phalcon\Api\Domain\ADR\InputTypes; + +/** + * @phpstan-import-type TCompanyInput from InputTypes + */ +interface CompanyCommandFactoryInterface +{ + /** + * @param TCompanyInput $input + * + * @return CompanyDeleteCommand + */ + public function delete(array $input): CompanyDeleteCommand; + + /** + * @param TCompanyInput $input + * + * @return CompanyGetCommand + */ + public function get(array $input): CompanyGetCommand; + + /** + * @param TCompanyInput $input + * + * @return CompanyGetManyCommand + */ + public function getMany(array $input): CompanyGetManyCommand; + + /** + * @param TCompanyInput $input + * + * @return CompanyPostCommand + */ + public function insert(array $input): CompanyPostCommand; + + /** + * @param TCompanyInput $input + * + * @return CompanyPutCommand + */ + public function update(array $input): CompanyPutCommand; +} diff --git a/src/Domain/Application/Company/Command/CompanyDeleteCommand.php b/src/Domain/Application/Company/Command/CompanyDeleteCommand.php new file mode 100644 index 0000000..31ba47d --- /dev/null +++ b/src/Domain/Application/Company/Command/CompanyDeleteCommand.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Phalcon\Api\Domain\Application\Company\Command; + +use Phalcon\Api\Domain\Infrastructure\CommandBus\CommandInterface; + +final readonly class CompanyDeleteCommand implements CommandInterface +{ + /** + * @param int $id + */ + public function __construct( + public int $id, + ) { + } +} diff --git a/src/Domain/Application/Company/Command/CompanyGetCommand.php b/src/Domain/Application/Company/Command/CompanyGetCommand.php new file mode 100644 index 0000000..dc93731 --- /dev/null +++ b/src/Domain/Application/Company/Command/CompanyGetCommand.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Phalcon\Api\Domain\Application\Company\Command; + +use Phalcon\Api\Domain\Infrastructure\CommandBus\CommandInterface; + +final readonly class CompanyGetCommand implements CommandInterface +{ + /** + * @param int $id + */ + public function __construct( + public int $id, + ) { + } +} diff --git a/src/Domain/Application/Company/Command/CompanyGetManyCommand.php b/src/Domain/Application/Company/Command/CompanyGetManyCommand.php new file mode 100644 index 0000000..776e1c1 --- /dev/null +++ b/src/Domain/Application/Company/Command/CompanyGetManyCommand.php @@ -0,0 +1,59 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Phalcon\Api\Domain\Application\Company\Command; + +use Phalcon\Api\Domain\Infrastructure\CommandBus\CommandInterface; + +final readonly class CompanyGetManyCommand implements CommandInterface +{ + /** + * @param int|null $id + * @param string|null $name + * @param string|null $phone + * @param string|null $email + * @param string|null $website + * @param string|null $addressLine1 + * @param string|null $addressLine2 + * @param string|null $city + * @param string|null $stateProvince + * @param string|null $zipCode + * @param string|null $country + * @param string|null $createdDate + * @param int|null $createdUserId + * @param string|null $updatedDate + * @param int|null $updatedUserId + * @param int $page + * @param int $perPage + */ + public function __construct( + public ?int $id, + public ?string $name, + public ?string $phone, + public ?string $email, + public ?string $website, + public ?string $addressLine1, + public ?string $addressLine2, + public ?string $city, + public ?string $stateProvince, + public ?string $zipCode, + public ?string $country, + public ?string $createdDate, + public ?int $createdUserId, + public ?string $updatedDate, + public ?int $updatedUserId, + public int $page, + public int $perPage, + ) { + } +} diff --git a/src/Domain/Application/Company/Command/CompanyPostCommand.php b/src/Domain/Application/Company/Command/CompanyPostCommand.php new file mode 100644 index 0000000..3f1324c --- /dev/null +++ b/src/Domain/Application/Company/Command/CompanyPostCommand.php @@ -0,0 +1,55 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Phalcon\Api\Domain\Application\Company\Command; + +use Phalcon\Api\Domain\Infrastructure\CommandBus\CommandInterface; + +final readonly class CompanyPostCommand implements CommandInterface +{ + /** + * @param int|null $id + * @param string|null $name + * @param string|null $phone + * @param string|null $email + * @param string|null $website + * @param string|null $addressLine1 + * @param string|null $addressLine2 + * @param string|null $city + * @param string|null $stateProvince + * @param string|null $zipCode + * @param string|null $country + * @param string|null $createdDate + * @param int|null $createdUserId + * @param string|null $updatedDate + * @param int|null $updatedUserId + */ + public function __construct( + public ?int $id, + public ?string $name, + public ?string $phone, + public ?string $email, + public ?string $website, + public ?string $addressLine1, + public ?string $addressLine2, + public ?string $city, + public ?string $stateProvince, + public ?string $zipCode, + public ?string $country, + public ?string $createdDate, + public ?int $createdUserId, + public ?string $updatedDate, + public ?int $updatedUserId, + ) { + } +} diff --git a/src/Domain/Application/Company/Command/CompanyPutCommand.php b/src/Domain/Application/Company/Command/CompanyPutCommand.php new file mode 100644 index 0000000..d5c1586 --- /dev/null +++ b/src/Domain/Application/Company/Command/CompanyPutCommand.php @@ -0,0 +1,55 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Phalcon\Api\Domain\Application\Company\Command; + +use Phalcon\Api\Domain\Infrastructure\CommandBus\CommandInterface; + +final readonly class CompanyPutCommand implements CommandInterface +{ + /** + * @param int $id + * @param string|null $name + * @param string|null $phone + * @param string|null $email + * @param string|null $website + * @param string|null $addressLine1 + * @param string|null $addressLine2 + * @param string|null $city + * @param string|null $stateProvince + * @param string|null $zipCode + * @param string|null $country + * @param string|null $createdDate + * @param int|null $createdUserId + * @param string|null $updatedDate + * @param int|null $updatedUserId + */ + public function __construct( + public int $id, + public ?string $name, + public ?string $phone, + public ?string $email, + public ?string $website, + public ?string $addressLine1, + public ?string $addressLine2, + public ?string $city, + public ?string $stateProvince, + public ?string $zipCode, + public ?string $country, + public ?string $createdDate, + public ?int $createdUserId, + public ?string $updatedDate, + public ?int $updatedUserId, + ) { + } +} diff --git a/src/Domain/Application/Company/Facade/CompanyFacade.php b/src/Domain/Application/Company/Facade/CompanyFacade.php new file mode 100644 index 0000000..4cd2ae0 --- /dev/null +++ b/src/Domain/Application/Company/Facade/CompanyFacade.php @@ -0,0 +1,106 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Phalcon\Api\Domain\Application\Company\Facade; + +use Phalcon\Api\Domain\ADR\InputTypes; +use Phalcon\Api\Domain\ADR\Payload; +use Phalcon\Api\Domain\Application\Company\Command\CompanyCommandFactoryInterface; +use Phalcon\Api\Domain\Infrastructure\CommandBus\CommandBusInterface; +use Phalcon\Api\Domain\Infrastructure\DataSource\Company\CompanyTypes; + +/** + * Orchestration for workflow + * + * - Sanitization + * - DTO creation + * - Validation + * - Pre-operation checks (when necessary) + * - Repository operation + * + * @phpstan-import-type TCompanyInput from InputTypes + * @phpstan-import-type TCompanyDomainToDbRecord from CompanyTypes + * @phpstan-import-type TCompanyDbRecordOptional from CompanyTypes + */ +final readonly class CompanyFacade +{ + /** + * @param CommandBusInterface $bus + * @param CompanyCommandFactoryInterface $factory + */ + public function __construct( + private CommandBusInterface $bus, + private CompanyCommandFactoryInterface $factory, + ) { + } + + /** + * Delete a company. + * + * @param TCompanyInput $input + * + * @return Payload + */ + public function delete(array $input): Payload + { + return $this->bus->dispatch($this->factory->delete($input)); + } + + /** + * Get a company. + * + * @param TCompanyInput $input + * + * @return Payload + */ + public function get(array $input): Payload + { + return $this->bus->dispatch($this->factory->get($input)); + } + + /** + * Get many records + * + * @param TCompanyInput $input + * + * @return Payload + */ + public function getMany(array $input): Payload + { + return $this->bus->dispatch($this->factory->getMany($input)); + } + + /** + * Create a company. + * + * @param TCompanyInput $input + * + * @return Payload + */ + public function insert(array $input): Payload + { + return $this->bus->dispatch($this->factory->insert($input)); + } + + /** + * Create a company. + * + * @param TCompanyInput $input + * + * @return Payload + */ + public function update(array $input): Payload + { + return $this->bus->dispatch($this->factory->update($input)); + } +} diff --git a/src/Domain/Application/Company/Handler/AbstractCompanyPutPostHandler.php b/src/Domain/Application/Company/Handler/AbstractCompanyPutPostHandler.php new file mode 100644 index 0000000..656f096 --- /dev/null +++ b/src/Domain/Application/Company/Handler/AbstractCompanyPutPostHandler.php @@ -0,0 +1,81 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Phalcon\Api\Domain\Application\Company\Handler; + +use Phalcon\Api\Domain\ADR\Payload; +use Phalcon\Api\Domain\Infrastructure\CommandBus\HandlerInterface; +use Phalcon\Api\Domain\Infrastructure\DataSource\Company\CompanyTypes; +use Phalcon\Api\Domain\Infrastructure\DataSource\Company\DTO\Company; +use Phalcon\Api\Domain\Infrastructure\DataSource\Company\Mapper\CompanyMapperInterface; +use Phalcon\Api\Domain\Infrastructure\DataSource\Company\Repository\CompanyRepositoryInterface; +use Phalcon\Api\Domain\Infrastructure\DataSource\Transformer\Transformer; +use Phalcon\Api\Domain\Infrastructure\DataSource\Validation\ValidatorInterface; +use Phalcon\Api\Domain\Infrastructure\Enums\Http\HttpCodesEnum; +use Phalcon\Events\ManagerInterface as EventsManagerInterface; +use Phalcon\Support\Registry; + +use function array_filter; + +/** + * @phpstan-import-type TCompanyDomainToDbRecord from CompanyTypes + * @phpstan-import-type TCompanyDbRecordOptional from CompanyTypes + */ +abstract class AbstractCompanyPutPostHandler implements HandlerInterface +{ + /** + * @param ValidatorInterface $validator + * @param CompanyMapperInterface $mapper + * @param CompanyRepositoryInterface $repository + * @param EventsManagerInterface $eventsManager + * @param Transformer $transformer + * @param Registry $registry + */ + public function __construct( + protected readonly ValidatorInterface $validator, + protected readonly CompanyMapperInterface $mapper, + protected readonly CompanyRepositoryInterface $repository, + protected readonly EventsManagerInterface $eventsManager, + protected readonly Transformer $transformer, + protected readonly Registry $registry, + ) { + } + + /** + * @param TCompanyDbRecordOptional $row + * + * @return TCompanyDbRecordOptional + */ + protected function cleanupFields(array $row): array + { + unset($row['com_id']); + + return array_filter( + $row, + static fn($v) => $v !== null && $v !== '' + ); + } + + /** + * @param HttpCodesEnum $item + * @param string $message + * + * @return Payload + */ + protected function getErrorPayload( + HttpCodesEnum $item, + string $message + ): Payload { + return Payload::error([[$item->text() . $message]]); + } +} diff --git a/src/Domain/Application/Company/Handler/CompanyDeleteHandler.php b/src/Domain/Application/Company/Handler/CompanyDeleteHandler.php new file mode 100644 index 0000000..ce03a94 --- /dev/null +++ b/src/Domain/Application/Company/Handler/CompanyDeleteHandler.php @@ -0,0 +1,64 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Phalcon\Api\Domain\Application\Company\Handler; + +use Phalcon\Api\Domain\ADR\Payload; +use Phalcon\Api\Domain\Application\Company\Command\CompanyDeleteCommand; +use Phalcon\Api\Domain\Infrastructure\CommandBus\CommandInterface; +use Phalcon\Api\Domain\Infrastructure\CommandBus\HandlerInterface; +use Phalcon\Api\Domain\Infrastructure\DataSource\Company\DTO\Company; +use Phalcon\Api\Domain\Infrastructure\DataSource\Company\Repository\CompanyRepositoryInterface; +use Phalcon\Api\Domain\Infrastructure\DataSource\Transformer\Transformer; + +final readonly class CompanyDeleteHandler implements HandlerInterface +{ + /** + * @param CompanyRepositoryInterface $repository + * @param Transformer $transformer + */ + public function __construct( + private CompanyRepositoryInterface $repository, + private Transformer $transformer + ) { + } + + /** + * Delete a company. + * + * @param CommandInterface $command + * + * @return Payload + */ + public function __invoke(CommandInterface $command): Payload + { + /** @var CompanyDeleteCommand $command */ + $companyId = $command->id; + + /** + * Success + */ + if ($companyId > 0) { + $rowCount = $this->repository->deleteById($companyId); + + if (0 !== $rowCount) { + return Payload::deleted($this->transformer->delete($companyId)); + } + } + + /** + * 404 + */ + return Payload::notFound(); + } +} diff --git a/src/Domain/Application/Company/Handler/CompanyGetHandler.php b/src/Domain/Application/Company/Handler/CompanyGetHandler.php new file mode 100644 index 0000000..6ab5371 --- /dev/null +++ b/src/Domain/Application/Company/Handler/CompanyGetHandler.php @@ -0,0 +1,64 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Phalcon\Api\Domain\Application\Company\Handler; + +use Phalcon\Api\Domain\ADR\Payload; +use Phalcon\Api\Domain\Application\Company\Command\CompanyGetCommand; +use Phalcon\Api\Domain\Infrastructure\CommandBus\CommandInterface; +use Phalcon\Api\Domain\Infrastructure\CommandBus\HandlerInterface; +use Phalcon\Api\Domain\Infrastructure\DataSource\Company\DTO\Company; +use Phalcon\Api\Domain\Infrastructure\DataSource\Company\Repository\CompanyRepositoryInterface; +use Phalcon\Api\Domain\Infrastructure\DataSource\Transformer\Transformer; + +final readonly class CompanyGetHandler implements HandlerInterface +{ + /** + * @param CompanyRepositoryInterface $repository + * @param Transformer $transformer + */ + public function __construct( + private CompanyRepositoryInterface $repository, + private Transformer $transformer + ) { + } + + /** + * Get a company. + * + * @param CommandInterface $command + * + * @return Payload + */ + public function __invoke(CommandInterface $command): Payload + { + /** @var CompanyGetCommand $command */ + $companyId = $command->id; + + /** + * Success + */ + if ($companyId > 0) { + $company = $this->repository->findById($companyId); + + if (null !== $company) { + return Payload::success($this->transformer->get($company)); + } + } + + /** + * 404 + */ + return Payload::notFound(); + } +} diff --git a/src/Domain/Application/Company/Handler/CompanyGetManyHandler.php b/src/Domain/Application/Company/Handler/CompanyGetManyHandler.php new file mode 100644 index 0000000..6fe6ab6 --- /dev/null +++ b/src/Domain/Application/Company/Handler/CompanyGetManyHandler.php @@ -0,0 +1,51 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Phalcon\Api\Domain\Application\Company\Handler; + +use Phalcon\Api\Domain\ADR\Payload; +use Phalcon\Api\Domain\Infrastructure\CommandBus\CommandInterface; +use Phalcon\Api\Domain\Infrastructure\CommandBus\HandlerInterface; +use Phalcon\Api\Domain\Infrastructure\DataSource\Company\DTO\Company; +use Phalcon\Api\Domain\Infrastructure\DataSource\Company\DTO\CompanyCriteria; +use Phalcon\Api\Domain\Infrastructure\DataSource\Company\Repository\CompanyRepositoryInterface; +use Phalcon\Api\Domain\Infrastructure\DataSource\Transformer\Transformer; + +final readonly class CompanyGetManyHandler implements HandlerInterface +{ + /** + * @param CompanyRepositoryInterface $repository + * @param Transformer $transformer + */ + public function __construct( + private CompanyRepositoryInterface $repository, + private Transformer $transformer, + ) { + } + + /** + * Get a company. + * + * @param CommandInterface $command + * + * @return Payload + */ + public function __invoke(CommandInterface $command): Payload + { + $criteria = CompanyCriteria::fromDto($command); + + $results = $this->repository->find($criteria); + + return Payload::success($this->transformer->getMany($results)); + } +} diff --git a/src/Domain/Application/Company/Handler/CompanyPostHandler.php b/src/Domain/Application/Company/Handler/CompanyPostHandler.php new file mode 100644 index 0000000..64df3a5 --- /dev/null +++ b/src/Domain/Application/Company/Handler/CompanyPostHandler.php @@ -0,0 +1,125 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Phalcon\Api\Domain\Application\Company\Handler; + +use PDOException; +use Phalcon\Api\Domain\ADR\Payload; +use Phalcon\Api\Domain\Infrastructure\CommandBus\CommandInterface; +use Phalcon\Api\Domain\Infrastructure\Constants\Dates; +use Phalcon\Api\Domain\Infrastructure\DataSource\Company\CompanyTypes; +use Phalcon\Api\Domain\Infrastructure\DataSource\Company\DTO\Company; +use Phalcon\Api\Domain\Infrastructure\DataSource\User\DTO\User; +use Phalcon\Api\Domain\Infrastructure\Enums\Http\HttpCodesEnum; + +/** + * @phpstan-import-type TCompanyDomainToDbRecord from CompanyTypes + * @phpstan-import-type TCompanyDbRecordOptional from CompanyTypes + */ +final class CompanyPostHandler extends AbstractCompanyPutPostHandler +{ + /** + * Create a company. + * + * @param CommandInterface $command + * + * @return Payload + */ + public function __invoke(CommandInterface $command): Payload + { + $validation = $this->validator->validate($command); + if (!$validation->isValid()) { + return Payload::invalid($validation->getErrors()); + } + + /** + * Array for inserting + */ + $company = $this->mapper->db($command); + + /** + * Pre-insert checks and manipulations + */ + $company = $this->preInsert($company); + + /** + * Insert the record + */ + try { + $companyId = $this->repository->insert($company); + } catch (PDOException $ex) { + /** + * Fire the event to the listeners + */ + $this->eventsManager->fire( + 'company:pdoError', + $this, + $ex->getMessage() + ); + + return $this->getErrorPayload( + HttpCodesEnum::AppCannotCreateDatabaseRecord, + $ex->getMessage() + ); + } + + if ($companyId < 1) { + return $this->getErrorPayload( + HttpCodesEnum::AppCannotCreateDatabaseRecord, + 'No id returned' + ); + } + + /** + * Get the company from the database + */ + /** @var Company $domainCompany */ + $domainCompany = $this->repository->findById($companyId); + + /** + * Return the company back + */ + return Payload::created($this->transformer->get($domainCompany)); + } + + /** + * @param TCompanyDomainToDbRecord $input + * + * @return TCompanyDbRecordOptional + */ + private function preInsert(array $input): array + { + $now = Dates::toUTC(format: Dates::DATE_TIME_FORMAT); + /** @var User $sessionUser */ + $sessionUser = $this->registry->get('user'); + + /** + * Created and updated company IDs come from the company in the session + */ + $input['com_created_usr_id'] = $sessionUser->id; + $input['com_updated_usr_id'] = $sessionUser->id; + + /** + * Set the created/updated dates if need be + */ + if (true === empty($input['com_created_date'])) { + $input['com_created_date'] = $now; + } + if (true === empty($input['com_updated_date'])) { + $input['com_updated_date'] = $now; + } + + /** @var TCompanyDbRecordOptional $input */ + return $this->cleanupFields($input); + } +} diff --git a/src/Domain/Application/Company/Handler/CompanyPutHandler.php b/src/Domain/Application/Company/Handler/CompanyPutHandler.php new file mode 100644 index 0000000..172e5d2 --- /dev/null +++ b/src/Domain/Application/Company/Handler/CompanyPutHandler.php @@ -0,0 +1,135 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Phalcon\Api\Domain\Application\Company\Handler; + +use PDOException; +use Phalcon\Api\Domain\ADR\Payload; +use Phalcon\Api\Domain\Application\Company\Command\CompanyPutCommand; +use Phalcon\Api\Domain\Infrastructure\CommandBus\CommandInterface; +use Phalcon\Api\Domain\Infrastructure\Constants\Dates; +use Phalcon\Api\Domain\Infrastructure\DataSource\Company\CompanyTypes; +use Phalcon\Api\Domain\Infrastructure\DataSource\Company\DTO\Company; +use Phalcon\Api\Domain\Infrastructure\DataSource\User\DTO\User; +use Phalcon\Api\Domain\Infrastructure\Enums\Http\HttpCodesEnum; + +/** + * @phpstan-import-type TCompanyDomainToDbRecord from CompanyTypes + * @phpstan-import-type TCompanyDbRecordOptional from CompanyTypes + */ +final class CompanyPutHandler extends AbstractCompanyPutPostHandler +{ + /** + * Delete a company. + * + * @param CommandInterface $command + * + * @return Payload + */ + public function __invoke(CommandInterface $command): Payload + { + $validation = $this->validator->validate($command); + if (!$validation->isValid()) { + return Payload::invalid($validation->getErrors()); + } + + /** + * Check if the company exists, If not, return an error + */ + /** @var CompanyPutCommand $command */ + $domainCompany = $this->repository->findById($command->id); + + if (null === $domainCompany) { + return Payload::notFound(); + } + + /** + * Array for updating + */ + $company = $this->mapper->db($command); + + /** + * Pre-update checks and manipulations + */ + $company = $this->preUpdate($company); + + /** + * Update the record + */ + try { + $companyId = $this->repository->update($command->id, $company); + } catch (PDOException $ex) { + /** + * Fire the event to the listeners + */ + $this->eventsManager->fire( + 'company:pdoError', + $this, + $ex->getMessage() + ); + + return $this->getErrorPayload( + HttpCodesEnum::AppCannotUpdateDatabaseRecord, + $ex->getMessage() + ); + } + + if ($companyId < 1) { + return $this->getErrorPayload( + HttpCodesEnum::AppCannotUpdateDatabaseRecord, + 'No id returned' + ); + } + + /** + * Get the company from the database + */ + /** @var Company $domainCompany */ + $domainCompany = $this->repository->findById($companyId); + + /** + * Return the company back + */ + return Payload::updated($this->transformer->get($domainCompany)); + } + + /** + * @param TCompanyDomainToDbRecord $input + * + * @return TCompanyDbRecordOptional + */ + private function preUpdate(array $input): array + { + $now = Dates::toUTC(format: Dates::DATE_TIME_FORMAT); + /** @var User $sessionUser */ + $sessionUser = $this->registry->get('user'); + + $input['com_updated_usr_id'] = $sessionUser->id; + + /** + * Set updated date to now if it has not been set + */ + if (true === empty($input['com_updated_date'])) { + $input['com_updated_date'] = $now; + } + + /** + * Remove createdDate and createdCompanyId - cannot be changed. This + * needs to be here because we don't want to touch those fields. + */ + unset($input['com_created_date'], $input['com_created_usr_id']); + + + return $this->cleanupFields($input); + } +} diff --git a/src/Domain/Application/Company/Service/AbstractCompanyService.php b/src/Domain/Application/Company/Service/AbstractCompanyService.php new file mode 100644 index 0000000..a329769 --- /dev/null +++ b/src/Domain/Application/Company/Service/AbstractCompanyService.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Phalcon\Api\Domain\Application\Company\Service; + +use Phalcon\Api\Domain\ADR\DomainInterface; +use Phalcon\Api\Domain\Application\Company\Facade\CompanyFacade; + +abstract class AbstractCompanyService implements DomainInterface +{ + /** + * @param CompanyFacade $facade + */ + public function __construct( + protected readonly CompanyFacade $facade, + ) { + } +} diff --git a/src/Domain/Application/Company/Service/CompanyDeleteService.php b/src/Domain/Application/Company/Service/CompanyDeleteService.php new file mode 100644 index 0000000..ecae262 --- /dev/null +++ b/src/Domain/Application/Company/Service/CompanyDeleteService.php @@ -0,0 +1,33 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Phalcon\Api\Domain\Application\Company\Service; + +use Phalcon\Api\Domain\ADR\InputTypes; +use Phalcon\Api\Domain\ADR\Payload; + +/** + * @phpstan-import-type TCompanyInput from InputTypes + */ +final class CompanyDeleteService extends AbstractCompanyService +{ + /** + * @param TCompanyInput $input + * + * @return Payload + */ + public function __invoke(array $input): Payload + { + return $this->facade->delete($input); + } +} diff --git a/src/Domain/Application/Company/Service/CompanyGetManyService.php b/src/Domain/Application/Company/Service/CompanyGetManyService.php new file mode 100644 index 0000000..6479cf9 --- /dev/null +++ b/src/Domain/Application/Company/Service/CompanyGetManyService.php @@ -0,0 +1,33 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Phalcon\Api\Domain\Application\Company\Service; + +use Phalcon\Api\Domain\ADR\InputTypes; +use Phalcon\Api\Domain\ADR\Payload; + +/** + * @phpstan-import-type TCompanyInput from InputTypes + */ +final class CompanyGetManyService extends AbstractCompanyService +{ + /** + * @param TCompanyInput $input + * + * @return Payload + */ + public function __invoke(array $input): Payload + { + return $this->facade->getMany($input); + } +} diff --git a/src/Domain/Application/Company/Service/CompanyGetService.php b/src/Domain/Application/Company/Service/CompanyGetService.php new file mode 100644 index 0000000..29353dc --- /dev/null +++ b/src/Domain/Application/Company/Service/CompanyGetService.php @@ -0,0 +1,33 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Phalcon\Api\Domain\Application\Company\Service; + +use Phalcon\Api\Domain\ADR\InputTypes; +use Phalcon\Api\Domain\ADR\Payload; + +/** + * @phpstan-import-type TCompanyInput from InputTypes + */ +final class CompanyGetService extends AbstractCompanyService +{ + /** + * @param TCompanyInput $input + * + * @return Payload + */ + public function __invoke(array $input): Payload + { + return $this->facade->get($input); + } +} diff --git a/src/Domain/Application/Company/Service/CompanyPostService.php b/src/Domain/Application/Company/Service/CompanyPostService.php new file mode 100644 index 0000000..5138e77 --- /dev/null +++ b/src/Domain/Application/Company/Service/CompanyPostService.php @@ -0,0 +1,33 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Phalcon\Api\Domain\Application\Company\Service; + +use Phalcon\Api\Domain\ADR\InputTypes; +use Phalcon\Api\Domain\ADR\Payload; + +/** + * @phpstan-import-type TCompanyInput from InputTypes + */ +final class CompanyPostService extends AbstractCompanyService +{ + /** + * @param TCompanyInput $input + * + * @return Payload + */ + public function __invoke(array $input): Payload + { + return $this->facade->insert($input); + } +} diff --git a/src/Domain/Application/Company/Service/CompanyPutService.php b/src/Domain/Application/Company/Service/CompanyPutService.php new file mode 100644 index 0000000..94ea7ae --- /dev/null +++ b/src/Domain/Application/Company/Service/CompanyPutService.php @@ -0,0 +1,33 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Phalcon\Api\Domain\Application\Company\Service; + +use Phalcon\Api\Domain\ADR\InputTypes; +use Phalcon\Api\Domain\ADR\Payload; + +/** + * @phpstan-import-type TCompanyInput from InputTypes + */ +final class CompanyPutService extends AbstractCompanyService +{ + /** + * @param TCompanyInput $input + * + * @return Payload + */ + public function __invoke(array $input): Payload + { + return $this->facade->update($input); + } +} diff --git a/src/Domain/Application/User/Handler/AbstractUserPutPostHandler.php b/src/Domain/Application/User/Handler/AbstractUserPutPostHandler.php index aa89c39..b8a532a 100644 --- a/src/Domain/Application/User/Handler/AbstractUserPutPostHandler.php +++ b/src/Domain/Application/User/Handler/AbstractUserPutPostHandler.php @@ -15,12 +15,15 @@ use Phalcon\Api\Domain\ADR\Payload; use Phalcon\Api\Domain\Infrastructure\CommandBus\HandlerInterface; -use Phalcon\Api\Domain\Infrastructure\DataSource\Interface\MapperInterface; +use Phalcon\Api\Domain\Infrastructure\DataSource\Transformer\Transformer; +use Phalcon\Api\Domain\Infrastructure\DataSource\User\DTO\User; +use Phalcon\Api\Domain\Infrastructure\DataSource\User\Mapper\UserMapperInterface; use Phalcon\Api\Domain\Infrastructure\DataSource\User\Repository\UserRepositoryInterface; use Phalcon\Api\Domain\Infrastructure\DataSource\User\UserTypes; use Phalcon\Api\Domain\Infrastructure\DataSource\Validation\ValidatorInterface; use Phalcon\Api\Domain\Infrastructure\Encryption\Security; use Phalcon\Api\Domain\Infrastructure\Enums\Http\HttpCodesEnum; +use Phalcon\Events\ManagerInterface as EventsManagerInterface; use Phalcon\Support\Registry; use function array_filter; @@ -33,14 +36,19 @@ abstract class AbstractUserPutPostHandler implements HandlerInterface { /** * @param ValidatorInterface $validator - * @param MapperInterface $mapper + * @param UserMapperInterface $mapper * @param UserRepositoryInterface $repository + * @param EventsManagerInterface $eventsManager + * @param Transformer $transformer + * @param Registry $registry * @param Security $security */ public function __construct( protected readonly ValidatorInterface $validator, - protected readonly MapperInterface $mapper, + protected readonly UserMapperInterface $mapper, protected readonly UserRepositoryInterface $repository, + protected readonly EventsManagerInterface $eventsManager, + protected readonly Transformer $transformer, protected readonly Registry $registry, private readonly Security $security, ) { diff --git a/src/Domain/Application/User/Handler/UserDeleteHandler.php b/src/Domain/Application/User/Handler/UserDeleteHandler.php index bb830fe..a1a518c 100644 --- a/src/Domain/Application/User/Handler/UserDeleteHandler.php +++ b/src/Domain/Application/User/Handler/UserDeleteHandler.php @@ -17,20 +17,24 @@ use Phalcon\Api\Domain\Application\User\Command\UserDeleteCommand; use Phalcon\Api\Domain\Infrastructure\CommandBus\CommandInterface; use Phalcon\Api\Domain\Infrastructure\CommandBus\HandlerInterface; +use Phalcon\Api\Domain\Infrastructure\DataSource\Transformer\Transformer; +use Phalcon\Api\Domain\Infrastructure\DataSource\User\DTO\User; use Phalcon\Api\Domain\Infrastructure\DataSource\User\Repository\UserRepositoryInterface; final readonly class UserDeleteHandler implements HandlerInterface { /** * @param UserRepositoryInterface $repository + * @param Transformer $transformer */ public function __construct( private UserRepositoryInterface $repository, + private Transformer $transformer ) { } /** - * Get a user. + * Delete a user. * * @param CommandInterface $command * @@ -48,11 +52,7 @@ public function __invoke(CommandInterface $command): Payload $rowCount = $this->repository->deleteById($userId); if (0 !== $rowCount) { - return Payload::deleted( - [ - 'Record deleted successfully [#' . $userId . '].', - ], - ); + return Payload::deleted($this->transformer->delete($userId)); } } diff --git a/src/Domain/Application/User/Handler/UserGetHandler.php b/src/Domain/Application/User/Handler/UserGetHandler.php index 4638e99..ce8466e 100644 --- a/src/Domain/Application/User/Handler/UserGetHandler.php +++ b/src/Domain/Application/User/Handler/UserGetHandler.php @@ -17,15 +17,19 @@ use Phalcon\Api\Domain\Application\User\Command\UserGetCommand; use Phalcon\Api\Domain\Infrastructure\CommandBus\CommandInterface; use Phalcon\Api\Domain\Infrastructure\CommandBus\HandlerInterface; +use Phalcon\Api\Domain\Infrastructure\DataSource\Transformer\Transformer; +use Phalcon\Api\Domain\Infrastructure\DataSource\User\DTO\User; use Phalcon\Api\Domain\Infrastructure\DataSource\User\Repository\UserRepositoryInterface; -final class UserGetHandler implements HandlerInterface +final readonly class UserGetHandler implements HandlerInterface { /** * @param UserRepositoryInterface $repository + * @param Transformer $transformer */ public function __construct( - private readonly UserRepositoryInterface $repository, + private UserRepositoryInterface $repository, + private Transformer $transformer, ) { } @@ -48,7 +52,7 @@ public function __invoke(CommandInterface $command): Payload $user = $this->repository->findById($userId); if (null !== $user) { - return Payload::success([$user->id => $user->toArray()]); + return Payload::success($this->transformer->get($user)); } } diff --git a/src/Domain/Application/User/Handler/UserPostHandler.php b/src/Domain/Application/User/Handler/UserPostHandler.php index a0e42b6..07f4512 100644 --- a/src/Domain/Application/User/Handler/UserPostHandler.php +++ b/src/Domain/Application/User/Handler/UserPostHandler.php @@ -58,8 +58,14 @@ public function __invoke(CommandInterface $command): Payload $userId = $this->repository->insert($user); } catch (PDOException $ex) { /** - * @todo send generic response and log the error + * Fire the event to the listeners */ + $this->eventsManager->fire( + 'user:pdoError', + $this, + $ex->getMessage() + ); + return $this->getErrorPayload( HttpCodesEnum::AppCannotCreateDatabaseRecord, $ex->getMessage() @@ -82,7 +88,7 @@ public function __invoke(CommandInterface $command): Payload /** * Return the user back */ - return Payload::created([$domainUser->id => $domainUser->toArray()]); + return Payload::created($this->transformer->get($domainUser)); } /** diff --git a/src/Domain/Application/User/Handler/UserPutHandler.php b/src/Domain/Application/User/Handler/UserPutHandler.php index 12ad103..5ebd73d 100644 --- a/src/Domain/Application/User/Handler/UserPutHandler.php +++ b/src/Domain/Application/User/Handler/UserPutHandler.php @@ -69,8 +69,14 @@ public function __invoke(CommandInterface $command): Payload $userId = $this->repository->update($command->id, $user); } catch (PDOException $ex) { /** - * @todo send generic response and log the error + * Fire the event to the listeners */ + $this->eventsManager->fire( + 'user:pdoError', + $this, + $ex->getMessage() + ); + return $this->getErrorPayload( HttpCodesEnum::AppCannotUpdateDatabaseRecord, $ex->getMessage() @@ -93,7 +99,7 @@ public function __invoke(CommandInterface $command): Payload /** * Return the user back */ - return Payload::updated([$domainUser->id => $domainUser->toArray()]); + return Payload::updated($this->transformer->get($domainUser)); } /** diff --git a/src/Domain/Infrastructure/Container.php b/src/Domain/Infrastructure/Container.php index a12e871..947f24c 100644 --- a/src/Domain/Infrastructure/Container.php +++ b/src/Domain/Infrastructure/Container.php @@ -17,18 +17,22 @@ use Phalcon\Api\Domain\Infrastructure\Constants\Cache as CacheConstants; use Phalcon\Api\Domain\Infrastructure\Enums\Container\AuthDefinitionsEnum; use Phalcon\Api\Domain\Infrastructure\Enums\Container\CommonDefinitionsEnum; +use Phalcon\Api\Domain\Infrastructure\Enums\Container\CompanyDefinitionsEnum; use Phalcon\Api\Domain\Infrastructure\Enums\Container\DefinitionsEnumInterface; use Phalcon\Api\Domain\Infrastructure\Enums\Container\UserDefinitionsEnum; use Phalcon\Api\Domain\Infrastructure\Env\EnvManager; +use Phalcon\Api\Domain\Infrastructure\Listeners\DbErrorListener; use Phalcon\Cache\AdapterFactory; use Phalcon\Cache\Cache; use Phalcon\DataMapper\Pdo\Connection; use Phalcon\Di\Di; use Phalcon\Di\Service; +use Phalcon\Events\Manager as EventsManager; use Phalcon\Filter\FilterFactory; use Phalcon\Logger\Adapter\Stream; use Phalcon\Logger\Logger; use Phalcon\Storage\SerializerFactory; +use Psr\Log\LoggerInterface; use function sprintf; @@ -87,6 +91,7 @@ public function __construct() $services = [ Cache::class => $this->getServiceCache(), Connection::class => $this->getServiceConnection(), + self::EVENTS_MANAGER => $this->getServiceEventsManager(), self::FILTER => $this->getServiceFilter(), Logger::class => $this->getServiceLogger(), ContainerHandlerLocator::class => $this->getServiceHandlerLocator(), @@ -99,6 +104,10 @@ public function __construct() * Auth related services */ $services = $this->registerFromEnum(AuthDefinitionsEnum::class, $services); + /** + * Company related services + */ + $services = $this->registerFromEnum(CompanyDefinitionsEnum::class, $services); /** * User related services */ @@ -195,6 +204,29 @@ function () { ); } + /** + * @return Service + */ + private function getServiceEventsManager(): Service + { + return new Service( + function () { + $evManager = new EventsManager(); + $evManager->enablePriorities(true); + + /** @var LoggerInterface $logger */ + $logger = $this->getShared(Logger::class); + $listener = new DbErrorListener($logger); + + $evManager->attach('user', $listener); + $evManager->attach('company', $listener); + + return $evManager; + }, + true + ); + } + /** * @return Service */ diff --git a/src/Domain/Infrastructure/DataSource/AbstractCollection.php b/src/Domain/Infrastructure/DataSource/AbstractCollection.php new file mode 100644 index 0000000..ebf0b2f --- /dev/null +++ b/src/Domain/Infrastructure/DataSource/AbstractCollection.php @@ -0,0 +1,68 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Phalcon\Api\Domain\Infrastructure\DataSource; + +use Countable; +use Generator; +use IteratorAggregate; + +use function count; + +/** + * Collection for domain objects + * + * @template T of object + * @implements IteratorAggregate + */ +abstract class AbstractCollection implements IteratorAggregate, Countable +{ + /** @var array */ + private array $items = []; + + /** + * Add an item to the collection. + * + * @param T&object{id:int} $item + */ + public function add(object $item): void + { + $this->items[$item->id] = $item; + } + + /** + * @return int + */ + public function count(): int + { + return count($this->items); + } + + /** + * @return Generator + */ + public function getIterator(): Generator + { + foreach ($this->items as $key => $item) { + yield $key => $item; + } + } + + /** + * @return T[] + */ + public function toArray(): array + { + return $this->items; + } +} diff --git a/src/Domain/Infrastructure/DataSource/AbstractInputValueObject.php b/src/Domain/Infrastructure/DataSource/AbstractInputValueObject.php new file mode 100644 index 0000000..76f032c --- /dev/null +++ b/src/Domain/Infrastructure/DataSource/AbstractInputValueObject.php @@ -0,0 +1,75 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Phalcon\Api\Domain\Infrastructure\DataSource; + +use Phalcon\Api\Domain\ADR\InputTypes; +use Phalcon\Api\Domain\Infrastructure\DataSource\Company\CompanyTypes; +use Phalcon\Api\Domain\Infrastructure\DataSource\Interface\SanitizerInterface; +use Phalcon\Api\Domain\Infrastructure\DataSource\Interface\ValueObjectInterface; +use Phalcon\Api\Domain\Infrastructure\DataSource\User\UserTypes; + +use function get_object_vars; + +/** + * Base factory for value objects such as input DTOs. + * + * Concrete DTOs must implement protected static function + * + * `fromArray(array $sanitized): static` + * + * and keep themselves immutable/read\-only. + * + * @phpstan-import-type TInputSanitize from InputTypes + * @phpstan-import-type TAuthInput from InputTypes + * @phpstan-import-type TCompany from CompanyTypes + * @phpstan-import-type TUser from UserTypes + * @phpstan-type TInputs TAuthInput|TCompany|TUser + */ +abstract class AbstractInputValueObject implements ValueObjectInterface +{ + /** + * Factory that accepts a SanitizerInterface and returns the concrete DTO. + * + * @param SanitizerInterface $sanitizer + * @param TInputSanitize $input + * + * @return static + */ + public static function new(SanitizerInterface $sanitizer, array $input): static + { + $sanitized = $sanitizer->sanitize($input); + + return static::fromArray($sanitized); + } + + /** + * @return TInputs + */ + public function toArray(): array + { + /** @var TInputs $vars */ + $vars = get_object_vars($this); + + return $vars; + } + + /** + * Build the concrete DTO from a sanitized array. + * + * @param TInputSanitize $sanitized + * + * @return static + */ + abstract protected static function fromArray(array $sanitized): static; +} diff --git a/src/Domain/Infrastructure/DataSource/AbstractRepository.php b/src/Domain/Infrastructure/DataSource/AbstractRepository.php index 4ac90dc..2ac92b3 100644 --- a/src/Domain/Infrastructure/DataSource/AbstractRepository.php +++ b/src/Domain/Infrastructure/DataSource/AbstractRepository.php @@ -13,12 +13,19 @@ namespace Phalcon\Api\Domain\Infrastructure\DataSource; +use Phalcon\Api\Domain\Infrastructure\DataSource\Company\CompanyTypes; use Phalcon\Api\Domain\Infrastructure\DataSource\User\UserTypes; use Phalcon\DataMapper\Pdo\Connection; +use Phalcon\DataMapper\Pdo\Exception\Exception as DataMapperException; use Phalcon\DataMapper\Query\Delete; +use Phalcon\DataMapper\Query\Insert; +use Phalcon\DataMapper\Query\Update; /** * @phpstan-import-type TCriteria from UserTypes + * @phpstan-import-type TUserDbRecordOptional from UserTypes + * @phpstan-import-type TCompanyDbRecordOptional from CompanyTypes + * @phpstan-type TDbRecordOptional TCompanyDbRecordOptional|TUserDbRecordOptional */ abstract class AbstractRepository { @@ -40,6 +47,7 @@ public function __construct( * @param TCriteria $criteria * * @return int + * @throws DataMapperException */ public function deleteBy(array $criteria): int { @@ -67,4 +75,43 @@ public function deleteById(int $recordId): int ] ); } + + /** + * + * @param TDbRecordOptional $columns + * + * @return int + * @throws DataMapperException + */ + public function insert(array $columns): int + { + $insert = Insert::new($this->connection); + $insert + ->into($this->table) + ->columns($columns) + ->perform() + ; + + return (int)$insert->getLastInsertId(); + } + + /** + * @param int $recordId + * @param TDbRecordOptional $columns + * + * @return int + * @throws DataMapperException + */ + public function update(int $recordId, array $columns): int + { + $update = Update::new($this->connection); + $update + ->table($this->table) + ->columns($columns) + ->where($this->idField . ' = ', $recordId) + ->perform() + ; + + return $recordId; + } } diff --git a/src/Domain/Infrastructure/DataSource/AbstractSanitizer.php b/src/Domain/Infrastructure/DataSource/AbstractSanitizer.php index 18f2bb8..a5ef880 100644 --- a/src/Domain/Infrastructure/DataSource/AbstractSanitizer.php +++ b/src/Domain/Infrastructure/DataSource/AbstractSanitizer.php @@ -14,8 +14,8 @@ namespace Phalcon\Api\Domain\Infrastructure\DataSource; use Phalcon\Api\Domain\ADR\InputTypes; +use Phalcon\Api\Domain\Infrastructure\DataSource\Interface\SanitizerEnumInterface; use Phalcon\Api\Domain\Infrastructure\DataSource\Interface\SanitizerInterface; -use Phalcon\Api\Domain\Infrastructure\Enums\Sanitizer\SanitizersEnumInterface; use Phalcon\Filter\FilterInterface; /** @@ -38,7 +38,7 @@ public function __construct( public function sanitize(array $input): array { $enum = $this->enum; - /** @var SanitizersEnumInterface[] $fields */ + /** @var SanitizerEnumInterface[] $fields */ $fields = $enum::cases(); /** @@ -53,7 +53,7 @@ public function sanitize(array $input): array * * If there is no sanitizer defined, the value will be left intact. */ - /** @var SanitizersEnumInterface $field */ + /** @var SanitizerEnumInterface $field */ foreach ($fields as $field) { $value = $input[$field->name] ?? $field->default(); diff --git a/src/Domain/Infrastructure/DataSource/AbstractValueObject.php b/src/Domain/Infrastructure/DataSource/AbstractValueObject.php index f0f7e0d..4810697 100644 --- a/src/Domain/Infrastructure/DataSource/AbstractValueObject.php +++ b/src/Domain/Infrastructure/DataSource/AbstractValueObject.php @@ -13,43 +13,26 @@ namespace Phalcon\Api\Domain\Infrastructure\DataSource; -use Phalcon\Api\Domain\ADR\InputTypes; -use Phalcon\Api\Domain\Infrastructure\DataSource\Interface\SanitizerInterface; +use Phalcon\Api\Domain\Infrastructure\DataSource\Company\CompanyTypes; +use Phalcon\Api\Domain\Infrastructure\DataSource\Interface\ValueObjectInterface; +use Phalcon\Api\Domain\Infrastructure\DataSource\User\UserTypes; + +use function get_object_vars; /** - * Base factory for value objects such as input DTOs. - * - * Concrete DTOs must implement protected static function - * - * `fromArray(array $sanitized): static` - * - * and keep themselves immutable/read\-only. - * - * @phpstan-import-type TInputSanitize from InputTypes + * @phpstan-import-type TCompany from CompanyTypes + * @phpstan-import-type TUser from UserTypes */ -abstract class AbstractValueObject +abstract class AbstractValueObject implements ValueObjectInterface { /** - * Factory that accepts a SanitizerInterface and returns the concrete DTO. - * - * @param SanitizerInterface $sanitizer - * @param TInputSanitize $input - * - * @return static + * @return TUser|TCompany */ - public static function new(SanitizerInterface $sanitizer, array $input): static + public function toArray(): array { - $sanitized = $sanitizer->sanitize($input); + /** @var TUser|TCompany $vars */ + $vars = get_object_vars($this); - return static::fromArray($sanitized); + return $vars; } - - /** - * Build the concrete DTO from a sanitized array. - * - * @param TInputSanitize $sanitized - * - * @return static - */ - abstract protected static function fromArray(array $sanitized): static; } diff --git a/src/Domain/Infrastructure/DataSource/Auth/DTO/AuthInput.php b/src/Domain/Infrastructure/DataSource/Auth/DTO/AuthInput.php index 6194bd8..51dc658 100644 --- a/src/Domain/Infrastructure/DataSource/Auth/DTO/AuthInput.php +++ b/src/Domain/Infrastructure/DataSource/Auth/DTO/AuthInput.php @@ -14,12 +14,12 @@ namespace Phalcon\Api\Domain\Infrastructure\DataSource\Auth\DTO; use Phalcon\Api\Domain\ADR\InputTypes; -use Phalcon\Api\Domain\Infrastructure\DataSource\AbstractValueObject; +use Phalcon\Api\Domain\Infrastructure\DataSource\AbstractInputValueObject; /** * @phpstan-import-type TAuthInput from InputTypes */ -final class AuthInput extends AbstractValueObject +final class AuthInput extends AbstractInputValueObject { /** * @param string|null $email diff --git a/src/Domain/Infrastructure/DataSource/Auth/Sanitizer/AuthSanitizer.php b/src/Domain/Infrastructure/DataSource/Auth/Sanitizer/AuthSanitizer.php index c3a6da2..202e532 100644 --- a/src/Domain/Infrastructure/DataSource/Auth/Sanitizer/AuthSanitizer.php +++ b/src/Domain/Infrastructure/DataSource/Auth/Sanitizer/AuthSanitizer.php @@ -14,7 +14,6 @@ namespace Phalcon\Api\Domain\Infrastructure\DataSource\Auth\Sanitizer; use Phalcon\Api\Domain\Infrastructure\DataSource\AbstractSanitizer; -use Phalcon\Api\Domain\Infrastructure\Enums\Sanitizer\AuthSanitizerEnum; final class AuthSanitizer extends AbstractSanitizer { diff --git a/src/Domain/Infrastructure/Enums/Sanitizer/AuthSanitizerEnum.php b/src/Domain/Infrastructure/DataSource/Auth/Sanitizer/AuthSanitizerEnum.php similarity index 73% rename from src/Domain/Infrastructure/Enums/Sanitizer/AuthSanitizerEnum.php rename to src/Domain/Infrastructure/DataSource/Auth/Sanitizer/AuthSanitizerEnum.php index 5baf791..93dbb72 100644 --- a/src/Domain/Infrastructure/Enums/Sanitizer/AuthSanitizerEnum.php +++ b/src/Domain/Infrastructure/DataSource/Auth/Sanitizer/AuthSanitizerEnum.php @@ -11,11 +11,12 @@ declare(strict_types=1); -namespace Phalcon\Api\Domain\Infrastructure\Enums\Sanitizer; +namespace Phalcon\Api\Domain\Infrastructure\DataSource\Auth\Sanitizer; +use Phalcon\Api\Domain\Infrastructure\DataSource\Interface\SanitizerEnumInterface; use Phalcon\Filter\Filter; -enum AuthSanitizerEnum implements SanitizersEnumInterface +enum AuthSanitizerEnum implements SanitizerEnumInterface { case email; case password; diff --git a/src/Domain/Infrastructure/DataSource/Auth/Transformer/AuthTransformer.php b/src/Domain/Infrastructure/DataSource/Auth/Transformer/AuthTransformer.php new file mode 100644 index 0000000..dad8079 --- /dev/null +++ b/src/Domain/Infrastructure/DataSource/Auth/Transformer/AuthTransformer.php @@ -0,0 +1,72 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Phalcon\Api\Domain\Infrastructure\DataSource\Auth\Transformer; + +use Phalcon\Api\Domain\Infrastructure\DataSource\User\DTO\User; +use Phalcon\Api\Domain\Infrastructure\DataSource\User\UserTypes; + +/** + * Transforms the data for the payload + * + * @phpstan-type TTokens = array{ + * token: string, + * refreshToken: string + * } + * @phpstan-import-type TLoginResponsePayload from UserTypes + */ +final class AuthTransformer +{ + /** + * @param User $domainUser + * @param TTokens $tokens + * + * @return TLoginResponsePayload + */ + public function login(User $domainUser, array $tokens): array + { + return [ + 'authenticated' => true, + 'user' => [ + 'id' => $domainUser->id, + 'name' => $domainUser->fullName(), + 'email' => $domainUser->email, + ], + 'jwt' => [ + 'token' => $tokens['token'], + 'refreshToken' => $tokens['refreshToken'], + ], + ]; + } + + /** + * @return array{authenticated: false} + */ + public function logout(): array + { + return ['authenticated' => false]; + } + + /** + * @param TTokens $tokens + * + * @return TTokens + */ + public function refresh(array $tokens): array + { + return [ + 'token' => $tokens['token'], + 'refreshToken' => $tokens['refreshToken'], + ]; + } +} diff --git a/src/Domain/Infrastructure/DataSource/Auth/Validator/AuthLoginValidator.php b/src/Domain/Infrastructure/DataSource/Auth/Validator/AuthLoginValidator.php index 275775b..c65caab 100644 --- a/src/Domain/Infrastructure/DataSource/Auth/Validator/AuthLoginValidator.php +++ b/src/Domain/Infrastructure/DataSource/Auth/Validator/AuthLoginValidator.php @@ -17,7 +17,6 @@ use Phalcon\Api\Domain\Infrastructure\DataSource\Validation\AbstractValidator; use Phalcon\Api\Domain\Infrastructure\DataSource\Validation\Result; use Phalcon\Api\Domain\Infrastructure\Enums\Http\HttpCodesEnum; -use Phalcon\Api\Domain\Infrastructure\Enums\Validator\AuthLoginValidatorEnum; final class AuthLoginValidator extends AbstractValidator { diff --git a/src/Domain/Infrastructure/Enums/Validator/AuthLoginValidatorEnum.php b/src/Domain/Infrastructure/DataSource/Auth/Validator/AuthLoginValidatorEnum.php similarity index 83% rename from src/Domain/Infrastructure/Enums/Validator/AuthLoginValidatorEnum.php rename to src/Domain/Infrastructure/DataSource/Auth/Validator/AuthLoginValidatorEnum.php index 3043b4c..44f7e39 100644 --- a/src/Domain/Infrastructure/Enums/Validator/AuthLoginValidatorEnum.php +++ b/src/Domain/Infrastructure/DataSource/Auth/Validator/AuthLoginValidatorEnum.php @@ -11,8 +11,9 @@ declare(strict_types=1); -namespace Phalcon\Api\Domain\Infrastructure\Enums\Validator; +namespace Phalcon\Api\Domain\Infrastructure\DataSource\Auth\Validator; +use Phalcon\Api\Domain\Infrastructure\DataSource\Interface\ValidatorEnumInterface; use Phalcon\Filter\Validation\Validator\Email; use Phalcon\Filter\Validation\Validator\PresenceOf; diff --git a/src/Domain/Infrastructure/DataSource/Auth/Validator/AuthTokenValidator.php b/src/Domain/Infrastructure/DataSource/Auth/Validator/AuthTokenValidator.php index a8508f7..81ba9bf 100644 --- a/src/Domain/Infrastructure/DataSource/Auth/Validator/AuthTokenValidator.php +++ b/src/Domain/Infrastructure/DataSource/Auth/Validator/AuthTokenValidator.php @@ -22,7 +22,6 @@ use Phalcon\Api\Domain\Infrastructure\Encryption\TokenManagerInterface; use Phalcon\Api\Domain\Infrastructure\Enums\Common\JWTEnum; use Phalcon\Api\Domain\Infrastructure\Enums\Http\HttpCodesEnum; -use Phalcon\Api\Domain\Infrastructure\Enums\Validator\AuthTokenValidatorEnum; use Phalcon\Encryption\Security\JWT\Token\Token; use Phalcon\Filter\Validation\ValidationInterface; @@ -31,8 +30,8 @@ final class AuthTokenValidator extends AbstractValidator protected string $fields = AuthTokenValidatorEnum::class; public function __construct( - private TokenManagerInterface $tokenManager, - private UserRepositoryInterface $userRepository, + private readonly TokenManagerInterface $tokenManager, + private readonly UserRepositoryInterface $userRepository, ValidationInterface $validator, ) { parent::__construct($validator); @@ -53,9 +52,6 @@ public function validate(CommandInterface $command): Result return Result::error([HttpCodesEnum::AppTokenNotPresent->error()]); } - /** - * @todo add the docblock for $command here - */ /** @var AuthLogoutPostCommand|AuthRefreshPostCommand $command */ /** @var string $token */ $token = $command->token; diff --git a/src/Domain/Infrastructure/Enums/Validator/AuthTokenValidatorEnum.php b/src/Domain/Infrastructure/DataSource/Auth/Validator/AuthTokenValidatorEnum.php similarity index 78% rename from src/Domain/Infrastructure/Enums/Validator/AuthTokenValidatorEnum.php rename to src/Domain/Infrastructure/DataSource/Auth/Validator/AuthTokenValidatorEnum.php index 0ba003b..bab53fb 100644 --- a/src/Domain/Infrastructure/Enums/Validator/AuthTokenValidatorEnum.php +++ b/src/Domain/Infrastructure/DataSource/Auth/Validator/AuthTokenValidatorEnum.php @@ -11,8 +11,9 @@ declare(strict_types=1); -namespace Phalcon\Api\Domain\Infrastructure\Enums\Validator; +namespace Phalcon\Api\Domain\Infrastructure\DataSource\Auth\Validator; +use Phalcon\Api\Domain\Infrastructure\DataSource\Interface\ValidatorEnumInterface; use Phalcon\Filter\Validation\Validator\PresenceOf; enum AuthTokenValidatorEnum implements ValidatorEnumInterface diff --git a/src/Domain/Infrastructure/DataSource/Company/CompanyTypes.php b/src/Domain/Infrastructure/DataSource/Company/CompanyTypes.php new file mode 100644 index 0000000..2a1f22d --- /dev/null +++ b/src/Domain/Infrastructure/DataSource/Company/CompanyTypes.php @@ -0,0 +1,96 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Phalcon\Api\Domain\Infrastructure\DataSource\Company; + +/** + * @phpstan-type TCompanyDbRecord array{ + * com_id: int, + * com_name: string, + * com_phone: string, + * com_email: string, + * com_website: string, + * com_address_line_1: string, + * com_address_line_2: string, + * com_city: string, + * com_state_province: string, + * com_zip_code: string, + * com_country: string, + * com_created_date: string, + * com_created_usr_id: int, + * com_updated_date: string, + * com_updated_usr_id: int + * } + * + * @phpstan-type TCompanyDomainToDbRecord array{ + * com_id: int, + * com_name: ?string, + * com_phone: ?string, + * com_email: ?string, + * com_website: ?string, + * com_address_line_1: ?string, + * com_address_line_2: ?string, + * com_city: ?string, + * com_state_province: ?string, + * com_zip_code: ?string, + * com_country: ?string, + * com_created_date: ?string, + * com_created_usr_id: ?int, + * com_updated_date: ?string, + * com_updated_usr_id: ?int + * } + * + * @phpstan-type TCompanyDbRecordOptional array{ + * com_id?: int, + * com_name?: ?string, + * com_phone?: ?string, + * com_email?: ?string, + * com_website?: ?string, + * com_address_line_1?: ?string, + * com_address_line_2?: ?string, + * com_city?: ?string, + * com_state_province?: ?string, + * com_zip_code?: ?string, + * com_country?: ?string, + * com_created_date?: ?string, + * com_created_usr_id?: ?int, + * com_updated_date?: ?string, + * com_updated_usr_id?: ?int + * } + * + * @phpstan-type TCompany array{ + * id: int, + * name: ?string, + * phone: ?string, + * email: ?string, + * website: ?string, + * addressLine1: ?string, + * addressLine2: ?string, + * city: ?string, + * stateProvince: ?string, + * zipCode: ?string, + * country: ?string, + * createdDate: ?string, + * createdUserId: ?int, + * updatedDate: ?string, + * updatedUserId: ?int + * } + * + * @phpstan-type TCompanyRecord array{}|TCompanyDbRecord + * @phpstan-type TCompanyRecords array{}|TCompanyDbRecord[] + * + * @phpstan-type TCriteria array + */ +final class CompanyTypes +{ +} diff --git a/src/Domain/Infrastructure/DataSource/Company/DTO/Company.php b/src/Domain/Infrastructure/DataSource/Company/DTO/Company.php new file mode 100644 index 0000000..560ca08 --- /dev/null +++ b/src/Domain/Infrastructure/DataSource/Company/DTO/Company.php @@ -0,0 +1,38 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Phalcon\Api\Domain\Infrastructure\DataSource\Company\DTO; + +use Phalcon\Api\Domain\Infrastructure\DataSource\AbstractValueObject; + +final class Company extends AbstractValueObject +{ + public function __construct( + public readonly int $id, + public readonly ?string $name, + public readonly ?string $phone, + public readonly ?string $email, + public readonly ?string $website, + public readonly ?string $addressLine1, + public readonly ?string $addressLine2, + public readonly ?string $city, + public readonly ?string $stateProvince, + public readonly ?string $zipCode, + public readonly ?string $country, + public readonly ?string $createdDate, + public readonly ?int $createdUserId, + public readonly ?string $updatedDate, + public readonly ?int $updatedUserId, + ) { + } +} diff --git a/src/Domain/Infrastructure/DataSource/Company/DTO/CompanyCollection.php b/src/Domain/Infrastructure/DataSource/Company/DTO/CompanyCollection.php new file mode 100644 index 0000000..a85a821 --- /dev/null +++ b/src/Domain/Infrastructure/DataSource/Company/DTO/CompanyCollection.php @@ -0,0 +1,23 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Phalcon\Api\Domain\Infrastructure\DataSource\Company\DTO; + +use Phalcon\Api\Domain\Infrastructure\DataSource\AbstractCollection; + +/** + * @extends AbstractCollection + */ +final class CompanyCollection extends AbstractCollection +{ +} diff --git a/src/Domain/Infrastructure/DataSource/Company/DTO/CompanyCriteria.php b/src/Domain/Infrastructure/DataSource/Company/DTO/CompanyCriteria.php new file mode 100644 index 0000000..0aa1cc3 --- /dev/null +++ b/src/Domain/Infrastructure/DataSource/Company/DTO/CompanyCriteria.php @@ -0,0 +1,133 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Phalcon\Api\Domain\Infrastructure\DataSource\Company\DTO; + +use Phalcon\Api\Domain\Application\Company\Command\CompanyGetManyCommand; +use Phalcon\Api\Domain\Infrastructure\CommandBus\CommandInterface; +use Phalcon\Api\Domain\Infrastructure\DataSource\Company\CompanyTypes; + +/** + * Value object for database criteria + * + * @phpstan-import-type TCriteria from CompanyTypes + * @phpstan-import-type TCompanyDbRecordOptional from CompanyTypes + */ +final readonly class CompanyCriteria +{ + /** + * @param int|null $id + * @param string|null $name + * @param string|null $phone + * @param string|null $email + * @param string|null $website + * @param string|null $addressLine1 + * @param string|null $addressLine2 + * @param string|null $city + * @param string|null $stateProvince + * @param string|null $zipCode + * @param string|null $country + * @param string|null $createdDate + * @param int|null $createdUserId + * @param string|null $updatedDate + * @param int|null $updatedUserId + * @param int $page + * @param int $perPage + */ + private function __construct( + public ?int $id, + public ?string $name, + public ?string $phone, + public ?string $email, + public ?string $website, + public ?string $addressLine1, + public ?string $addressLine2, + public ?string $city, + public ?string $stateProvince, + public ?string $zipCode, + public ?string $country, + public ?string $createdDate, + public ?int $createdUserId, + public ?string $updatedDate, + public ?int $updatedUserId, + public int $page, + public int $perPage, + ) { + } + + /** + * @param CommandInterface $command + * + * @return self + */ + public static function fromDto(CommandInterface $command): self + { + /** @var CompanyGetManyCommand $command */ + return new self( + $command->id, + $command->name, + $command->phone, + $command->email, + $command->website, + $command->addressLine1, + $command->addressLine2, + $command->city, + $command->stateProvince, + $command->zipCode, + $command->country, + $command->createdDate, + $command->createdUserId, + $command->updatedDate, + $command->updatedUserId, + self::checkLimit($command->page, 1), + self::checkLimit($command->perPage, 10) + ); + } + + /** + * @return TCompanyDbRecordOptional + */ + public function toArray(): array + { + $criteria = [ + 'com_id' => $this->id, + 'com_name' => $this->name, + 'com_phone' => $this->phone, + 'com_email' => $this->email, + 'com_website' => $this->website, + 'com_address_line_1' => $this->addressLine1, + 'com_address_line_2' => $this->addressLine2, + 'com_city' => $this->city, + 'com_state_province' => $this->stateProvince, + 'com_zip_code' => $this->zipCode, + 'com_country' => $this->country, + 'com_created_date' => $this->createdDate, + 'com_created_usr_id' => $this->createdUserId, + 'com_updated_date' => $this->updatedDate, + 'com_updated_usr_id' => $this->updatedUserId, + ]; + + return array_filter($criteria); + } + + /** + * @param int $value + * @param int $defaultValue + * + * @return int + */ + private static function checkLimit(int $value, int $defaultValue): int + { + return $value > 0 ? $value : $defaultValue; + } +} diff --git a/src/Domain/Infrastructure/DataSource/Company/DTO/CompanyInput.php b/src/Domain/Infrastructure/DataSource/Company/DTO/CompanyInput.php new file mode 100644 index 0000000..71f0f5c --- /dev/null +++ b/src/Domain/Infrastructure/DataSource/Company/DTO/CompanyInput.php @@ -0,0 +1,99 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Phalcon\Api\Domain\Infrastructure\DataSource\Company\DTO; + +use Phalcon\Api\Domain\ADR\InputTypes; +use Phalcon\Api\Domain\Infrastructure\DataSource\AbstractInputValueObject; +use Phalcon\Api\Domain\Infrastructure\DataSource\Company\CompanyTypes; + +use function abs; + +/** + * Value object for input + * + * @phpstan-import-type TCompanyInput from InputTypes + * @phpstan-import-type TCompany from CompanyTypes + */ +final class CompanyInput extends AbstractInputValueObject +{ + /** + * @param int $id + * @param string|null $name + * @param string|null $phone + * @param string|null $email + * @param string|null $website + * @param string|null $addressLine1 + * @param string|null $addressLine2 + * @param string|null $city + * @param string|null $stateProvince + * @param string|null $zipCode + * @param string|null $country + * @param string|null $createdDate + * @param int|null $createdUserId + * @param string|null $updatedDate + * @param int|null $updatedUserId + * @param int $page + * @param int $perPage + */ + public function __construct( + public readonly int $id, + public readonly ?string $name, + public readonly ?string $phone, + public readonly ?string $email, + public readonly ?string $website, + public readonly ?string $addressLine1, + public readonly ?string $addressLine2, + public readonly ?string $city, + public readonly ?string $stateProvince, + public readonly ?string $zipCode, + public readonly ?string $country, + public readonly ?string $createdDate, + public readonly ?int $createdUserId, + public readonly ?string $updatedDate, + public readonly ?int $updatedUserId, + public readonly int $page, + public readonly int $perPage, + ) { + } + + /** + * Build the concrete DTO from a sanitized array. + * + * @param TCompanyInput $sanitized + * + * @return static + */ + protected static function fromArray(array $sanitized): static + { + return new self( + abs(isset($sanitized['id']) ? (int)$sanitized['id'] : 0), + isset($sanitized['name']) ? (string)$sanitized['name'] : null, + isset($sanitized['phone']) ? (string)$sanitized['phone'] : null, + isset($sanitized['email']) ? (string)$sanitized['email'] : null, + isset($sanitized['website']) ? (string)$sanitized['website'] : null, + isset($sanitized['addressLine1']) ? (string)$sanitized['addressLine1'] : null, + isset($sanitized['addressLine2']) ? (string)$sanitized['addressLine2'] : null, + isset($sanitized['city']) ? (string)$sanitized['city'] : null, + isset($sanitized['stateProvince']) ? (string)$sanitized['stateProvince'] : null, + isset($sanitized['zipCode']) ? (string)$sanitized['zipCode'] : null, + isset($sanitized['country']) ? (string)$sanitized['country'] : null, + isset($sanitized['createdDate']) ? (string)$sanitized['createdDate'] : null, + abs(isset($sanitized['createdUserId']) ? (int)$sanitized['createdUserId'] : 0), + isset($sanitized['updatedDate']) ? (string)$sanitized['updatedDate'] : null, + abs(isset($sanitized['updatedUserId']) ? (int)$sanitized['updatedUserId'] : 0), + abs(isset($sanitized['page']) ? (int)$sanitized['page'] : 1), + abs(isset($sanitized['perPage']) ? (int)$sanitized['perPage'] : 10) + ); + } +} diff --git a/src/Domain/Infrastructure/DataSource/Company/Mapper/CompanyMapper.php b/src/Domain/Infrastructure/DataSource/Company/Mapper/CompanyMapper.php new file mode 100644 index 0000000..ba57b38 --- /dev/null +++ b/src/Domain/Infrastructure/DataSource/Company/Mapper/CompanyMapper.php @@ -0,0 +1,99 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Phalcon\Api\Domain\Infrastructure\DataSource\Company\Mapper; + +use Phalcon\Api\Domain\Application\Company\Command\CompanyPutCommand; +use Phalcon\Api\Domain\Infrastructure\CommandBus\CommandInterface; +use Phalcon\Api\Domain\Infrastructure\DataSource\Company\CompanyTypes; +use Phalcon\Api\Domain\Infrastructure\DataSource\Company\DTO\Company; +use Phalcon\Api\Domain\Infrastructure\DataSource\Company\DTO\CompanyCollection; + +/** + * @phpstan-import-type TCompany from CompanyTypes + * @phpstan-import-type TCompanyDbRecord from CompanyTypes + * @phpstan-import-type TCompanyDomainToDbRecord from CompanyTypes + */ +final class CompanyMapper implements CompanyMapperInterface +{ + /** + * Map Domain Company -> DB row (usr_*) + * + * @return TCompanyDomainToDbRecord + */ + public function db(CommandInterface $company): array + { + /** @var CompanyPutCommand $company */ + return [ + 'com_id' => $company->id, + 'com_name' => $company->name, + 'com_phone' => $company->phone, + 'com_email' => $company->email, + 'com_website' => $company->website, + 'com_address_line_1' => $company->addressLine1, + 'com_address_line_2' => $company->addressLine2, + 'com_city' => $company->city, + 'com_state_province' => $company->stateProvince, + 'com_zip_code' => $company->zipCode, + 'com_country' => $company->country, + 'com_created_date' => $company->createdDate, + 'com_created_usr_id' => $company->createdUserId, + 'com_updated_date' => $company->updatedDate, + 'com_updated_usr_id' => $company->updatedUserId, + ]; + } + + /** + * Map DB row (usr_*) -> Domain Company + * + * @param TCompanyDbRecord|array{} $row + */ + public function domain(array $row): Company + { + return new Company( + (int)($row['com_id'] ?? 0), + $row['com_name'] ?? null, + $row['com_phone'] ?? null, + $row['com_email'] ?? null, + $row['com_website'] ?? null, + $row['com_address_line_1'] ?? null, + $row['com_address_line_2'] ?? null, + $row['com_city'] ?? null, + $row['com_state_province'] ?? null, + $row['com_zip_code'] ?? null, + $row['com_country'] ?? null, + $row['com_created_date'] ?? null, + isset($row['com_created_usr_id']) ? (int)$row['com_created_usr_id'] : null, + $row['com_updated_date'] ?? null, + isset($row['com_updated_usr_id']) ? (int)$row['com_updated_usr_id'] : null, + ); + } + + /** + * Map DB resultset -> Domain CompanyCollection + * + * @param TCompanyDbRecord[]|array{} $rows + */ + public function domainRows(array $rows): CompanyCollection + { + /** + * Return the DTO collection back + */ + $collection = new CompanyCollection(); + foreach ($rows as $row) { + $collection->add($this->domain($row)); + } + + return $collection; + } +} diff --git a/src/Domain/Infrastructure/DataSource/Company/Mapper/CompanyMapperInterface.php b/src/Domain/Infrastructure/DataSource/Company/Mapper/CompanyMapperInterface.php new file mode 100644 index 0000000..9d5130f --- /dev/null +++ b/src/Domain/Infrastructure/DataSource/Company/Mapper/CompanyMapperInterface.php @@ -0,0 +1,52 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Phalcon\Api\Domain\Infrastructure\DataSource\Company\Mapper; + +use Phalcon\Api\Domain\Infrastructure\CommandBus\CommandInterface; +use Phalcon\Api\Domain\Infrastructure\DataSource\Company\CompanyTypes; +use Phalcon\Api\Domain\Infrastructure\DataSource\Company\DTO\Company; +use Phalcon\Api\Domain\Infrastructure\DataSource\Company\DTO\CompanyCollection; + +/** + * Contract for mapping between domain DTO/objects and persistence arrays. + * + * @phpstan-import-type TCompany from CompanyTypes + * @phpstan-import-type TCompanyDbRecord from CompanyTypes + * @phpstan-import-type TCompanyDomainToDbRecord from CompanyTypes + */ +interface CompanyMapperInterface +{ + /** + * Map Domain User -> DB row (usr_*) + * + * @param CommandInterface $company + * + * @return TCompanyDomainToDbRecord + */ + public function db(CommandInterface $company): array; + + /** + * Map DB row (usr_*) -> Domain User + * + * @param TCompanyDbRecord|array{} $row + */ + public function domain(array $row): Company; + + /** + * Map DB resultset -> Domain CompanyCollection + * + * @param TCompanyDbRecord[]|array{} $rows + */ + public function domainRows(array $rows): CompanyCollection; +} diff --git a/src/Domain/Infrastructure/DataSource/Company/Repository/CompanyRepository.php b/src/Domain/Infrastructure/DataSource/Company/Repository/CompanyRepository.php new file mode 100644 index 0000000..ef6c11a --- /dev/null +++ b/src/Domain/Infrastructure/DataSource/Company/Repository/CompanyRepository.php @@ -0,0 +1,118 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Phalcon\Api\Domain\Infrastructure\DataSource\Company\Repository; + +use Phalcon\Api\Domain\Infrastructure\DataSource\AbstractRepository; +use Phalcon\Api\Domain\Infrastructure\DataSource\Company\CompanyTypes; +use Phalcon\Api\Domain\Infrastructure\DataSource\Company\DTO\Company; +use Phalcon\Api\Domain\Infrastructure\DataSource\Company\DTO\CompanyCollection; +use Phalcon\Api\Domain\Infrastructure\DataSource\Company\DTO\CompanyCriteria; +use Phalcon\Api\Domain\Infrastructure\DataSource\Company\Mapper\CompanyMapperInterface; +use Phalcon\DataMapper\Pdo\Connection; +use Phalcon\DataMapper\Query\Select; + +/** + * @phpstan-import-type TCriteria from CompanyTypes + * @phpstan-import-type TCompanyRecord from CompanyTypes + * @phpstan-import-type TCompanyRecords from CompanyTypes + * @phpstan-import-type TCompanyDbRecordOptional from CompanyTypes + */ +class CompanyRepository extends AbstractRepository implements CompanyRepositoryInterface +{ + /** + * @var string + */ + protected string $idField = 'com_id'; + /** + * @var string + */ + protected string $table = 'co_companies'; + + public function __construct( + Connection $connection, + private readonly CompanyMapperInterface $mapper, + ) { + parent::__construct($connection); + } + + /** + * @param CompanyCriteria $criteria + * + * @return CompanyCollection + */ + public function find(CompanyCriteria $criteria): CompanyCollection + { + $select = Select::new($this->connection); + + $select->from($this->table); + + $columns = $criteria->toArray(); + if (true !== empty($columns)) { + $select->whereEquals($columns); + } + + /** @var TCompanyRecords $results */ + $results = $select + ->page($criteria->page) + ->perPage($criteria->perPage) + ->fetchAll() + ; + + return $this->mapper->domainRows($results); + } + + /** + * @param int $recordId + * + * @return Company|null + */ + public function findById(int $recordId): ?Company + { + if ($recordId > 0) { + return $this->findOneBy( + [ + $this->idField => $recordId, + ] + ); + } + + return null; + } + + + /** + * @param TCriteria $criteria + * + * @return Company|null + */ + public function findOneBy(array $criteria): ?Company + { + $select = Select::new($this->connection); + + $select->from($this->table); + + if (true !== empty($criteria)) { + $select->whereEquals($criteria); + } + + /** @var TCompanyRecord $result */ + $result = $select->fetchOne(); + + if (empty($result)) { + return null; + } + + return $this->mapper->domain($result); + } +} diff --git a/src/Domain/Infrastructure/DataSource/Company/Repository/CompanyRepositoryInterface.php b/src/Domain/Infrastructure/DataSource/Company/Repository/CompanyRepositoryInterface.php new file mode 100644 index 0000000..0007f1f --- /dev/null +++ b/src/Domain/Infrastructure/DataSource/Company/Repository/CompanyRepositoryInterface.php @@ -0,0 +1,76 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Phalcon\Api\Domain\Infrastructure\DataSource\Company\Repository; + +use Phalcon\Api\Domain\Infrastructure\DataSource\Company\CompanyTypes; +use Phalcon\Api\Domain\Infrastructure\DataSource\Company\DTO\Company; +use Phalcon\Api\Domain\Infrastructure\DataSource\Company\DTO\CompanyCollection; +use Phalcon\Api\Domain\Infrastructure\DataSource\Company\DTO\CompanyCriteria; + +/** + * @phpstan-import-type TCriteria from CompanyTypes + * @phpstan-import-type TCompanyDbRecordOptional from CompanyTypes + */ +interface CompanyRepositoryInterface +{ + /** + * @param TCriteria $criteria + * + * @return int + */ + public function deleteBy(array $criteria): int; + + /** + * @param int $recordId + * + * @return int + */ + public function deleteById(int $recordId): int; + + /** + * @param CompanyCriteria $criteria + * + * @return CompanyCollection + */ + public function find(CompanyCriteria $criteria): CompanyCollection; + + /** + * @param int $recordId + * + * @return Company|null + */ + public function findById(int $recordId): ?Company; + + /** + * @param TCriteria $criteria + * + * @return Company|null + */ + public function findOneBy(array $criteria): ?Company; + + /** + * @param TCompanyDbRecordOptional $columns + * + * @return int + */ + public function insert(array $columns): int; + + /** + * @param int $recordId + * @param TCompanyDbRecordOptional $columns + * + * @return int + */ + public function update(int $recordId, array $columns): int; +} diff --git a/src/Domain/Infrastructure/DataSource/Company/Sanitizer/CompanySanitizer.php b/src/Domain/Infrastructure/DataSource/Company/Sanitizer/CompanySanitizer.php new file mode 100644 index 0000000..1489a50 --- /dev/null +++ b/src/Domain/Infrastructure/DataSource/Company/Sanitizer/CompanySanitizer.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Phalcon\Api\Domain\Infrastructure\DataSource\Company\Sanitizer; + +use Phalcon\Api\Domain\Infrastructure\DataSource\AbstractSanitizer; + +final class CompanySanitizer extends AbstractSanitizer +{ + protected string $enum = CompanySanitizerEnum::class; +} diff --git a/src/Domain/Infrastructure/DataSource/Company/Sanitizer/CompanySanitizerEnum.php b/src/Domain/Infrastructure/DataSource/Company/Sanitizer/CompanySanitizerEnum.php new file mode 100644 index 0000000..366258b --- /dev/null +++ b/src/Domain/Infrastructure/DataSource/Company/Sanitizer/CompanySanitizerEnum.php @@ -0,0 +1,63 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Phalcon\Api\Domain\Infrastructure\DataSource\Company\Sanitizer; + +use Phalcon\Api\Domain\Infrastructure\DataSource\Interface\SanitizerEnumInterface; +use Phalcon\Filter\Filter; + +enum CompanySanitizerEnum implements SanitizerEnumInterface +{ + case id; + case name; + case phone; + case email; + case website; + case addressLine1; + case addressLine2; + case city; + case stateProvince; + case zipCode; + case country; + case createdDate; + case createdUserId; + case updatedDate; + case updatedUserId; + case page; + case perPage; + + public function default(): mixed + { + return match ($this) { + self::id, + self::createdUserId, + self::updatedUserId => 0, + self::page => 1, + self::perPage => 10, + default => null + }; + } + + public function sanitizer(): string + { + return match ($this) { + self::id, + self::createdUserId, + self::updatedUserId, + self::page, + self::perPage => Filter::FILTER_ABSINT, + self::email => Filter::FILTER_EMAIL, + default => Filter::FILTER_STRIPTAGS + }; + } +} diff --git a/src/Domain/Infrastructure/DataSource/Company/Validator/CompanyInsertEnum.php b/src/Domain/Infrastructure/DataSource/Company/Validator/CompanyInsertEnum.php new file mode 100644 index 0000000..9a13ae8 --- /dev/null +++ b/src/Domain/Infrastructure/DataSource/Company/Validator/CompanyInsertEnum.php @@ -0,0 +1,40 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Phalcon\Api\Domain\Infrastructure\DataSource\Company\Validator; + +use Phalcon\Api\Domain\Infrastructure\DataSource\Interface\ValidatorEnumInterface; +use Phalcon\Filter\Validation\Validator\Email; +use Phalcon\Filter\Validation\Validator\PresenceOf; + +enum CompanyInsertEnum implements ValidatorEnumInterface +{ + case name; + case email; + + public function allowEmpty(): bool + { + return false; + } + + public function validators(): array + { + return match ($this) { + self::email => [ + PresenceOf::class, + Email::class, + ], + default => [PresenceOf::class], + }; + } +} diff --git a/src/Domain/Infrastructure/DataSource/Company/Validator/CompanyUpdateEnum.php b/src/Domain/Infrastructure/DataSource/Company/Validator/CompanyUpdateEnum.php new file mode 100644 index 0000000..0152f60 --- /dev/null +++ b/src/Domain/Infrastructure/DataSource/Company/Validator/CompanyUpdateEnum.php @@ -0,0 +1,46 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Phalcon\Api\Domain\Infrastructure\DataSource\Company\Validator; + +use Phalcon\Api\Domain\Infrastructure\DataSource\Interface\ValidatorEnumInterface; +use Phalcon\Api\Domain\Infrastructure\DataSource\Validation\AbsInt; +use Phalcon\Filter\Validation\Validator\Email; +use Phalcon\Filter\Validation\Validator\PresenceOf; + +enum CompanyUpdateEnum implements ValidatorEnumInterface +{ + case id; + case email; + + public function allowEmpty(): bool + { + return match ($this) { + self::id => false, + default => true, + }; + } + + public function validators(): array + { + return match ($this) { + self::id => [ + PresenceOf::class, + AbsInt::class, + ], + self::email => [ + Email::class, + ], + }; + } +} diff --git a/src/Domain/Infrastructure/DataSource/Company/Validator/CompanyValidator.php b/src/Domain/Infrastructure/DataSource/Company/Validator/CompanyValidator.php new file mode 100644 index 0000000..0d8117c --- /dev/null +++ b/src/Domain/Infrastructure/DataSource/Company/Validator/CompanyValidator.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Phalcon\Api\Domain\Infrastructure\DataSource\Company\Validator; + +use Phalcon\Api\Domain\Infrastructure\DataSource\Validation\AbstractValidator; + +final class CompanyValidator extends AbstractValidator +{ + protected string $fields = CompanyInsertEnum::class; +} diff --git a/src/Domain/Infrastructure/DataSource/Company/Validator/CompanyValidatorUpdate.php b/src/Domain/Infrastructure/DataSource/Company/Validator/CompanyValidatorUpdate.php new file mode 100644 index 0000000..247b072 --- /dev/null +++ b/src/Domain/Infrastructure/DataSource/Company/Validator/CompanyValidatorUpdate.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Phalcon\Api\Domain\Infrastructure\DataSource\Company\Validator; + +use Phalcon\Api\Domain\Infrastructure\DataSource\Validation\AbstractValidator; + +final class CompanyValidatorUpdate extends AbstractValidator +{ + protected string $fields = CompanyUpdateEnum::class; +} diff --git a/src/Domain/Infrastructure/Enums/Sanitizer/SanitizersEnumInterface.php b/src/Domain/Infrastructure/DataSource/Interface/SanitizerEnumInterface.php similarity index 75% rename from src/Domain/Infrastructure/Enums/Sanitizer/SanitizersEnumInterface.php rename to src/Domain/Infrastructure/DataSource/Interface/SanitizerEnumInterface.php index bdc84f8..894960d 100644 --- a/src/Domain/Infrastructure/Enums/Sanitizer/SanitizersEnumInterface.php +++ b/src/Domain/Infrastructure/DataSource/Interface/SanitizerEnumInterface.php @@ -11,11 +11,11 @@ declare(strict_types=1); -namespace Phalcon\Api\Domain\Infrastructure\Enums\Sanitizer; +namespace Phalcon\Api\Domain\Infrastructure\DataSource\Interface; use UnitEnum; -interface SanitizersEnumInterface extends UnitEnum +interface SanitizerEnumInterface extends UnitEnum { public function default(): mixed; diff --git a/src/Domain/Infrastructure/Enums/Validator/ValidatorEnumInterface.php b/src/Domain/Infrastructure/DataSource/Interface/ValidatorEnumInterface.php similarity index 88% rename from src/Domain/Infrastructure/Enums/Validator/ValidatorEnumInterface.php rename to src/Domain/Infrastructure/DataSource/Interface/ValidatorEnumInterface.php index 50ccb95..73c4c3f 100644 --- a/src/Domain/Infrastructure/Enums/Validator/ValidatorEnumInterface.php +++ b/src/Domain/Infrastructure/DataSource/Interface/ValidatorEnumInterface.php @@ -11,7 +11,7 @@ declare(strict_types=1); -namespace Phalcon\Api\Domain\Infrastructure\Enums\Validator; +namespace Phalcon\Api\Domain\Infrastructure\DataSource\Interface; use UnitEnum; diff --git a/src/Domain/Infrastructure/DataSource/Interface/ValueObjectInterface.php b/src/Domain/Infrastructure/DataSource/Interface/ValueObjectInterface.php new file mode 100644 index 0000000..d1e2d20 --- /dev/null +++ b/src/Domain/Infrastructure/DataSource/Interface/ValueObjectInterface.php @@ -0,0 +1,29 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Phalcon\Api\Domain\Infrastructure\DataSource\Interface; + +use Phalcon\Api\Domain\Infrastructure\DataSource\Company\CompanyTypes; +use Phalcon\Api\Domain\Infrastructure\DataSource\User\UserTypes; + +/** + * @phpstan-import-type TCompany from CompanyTypes + * @phpstan-import-type TUser from UserTypes + */ +interface ValueObjectInterface +{ + /** + * @return TCompany|TUser + */ + public function toArray(): array; +} diff --git a/src/Domain/Infrastructure/DataSource/Transformer/Transformer.php b/src/Domain/Infrastructure/DataSource/Transformer/Transformer.php new file mode 100644 index 0000000..6fabb92 --- /dev/null +++ b/src/Domain/Infrastructure/DataSource/Transformer/Transformer.php @@ -0,0 +1,71 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Phalcon\Api\Domain\Infrastructure\DataSource\Transformer; + +use Phalcon\Api\Domain\Infrastructure\DataSource\Interface\ValueObjectInterface; + +/** + * Transforms objects to array representations so that they can be passed + * to the payload + * + * @template T of ValueObjectInterface + * @phpstan-type TItem = array + */ +final class Transformer +{ + /** + * @param int $recordId + * + * @return string[] + */ + public function delete(int $recordId): array + { + return [ + 'Record deleted successfully [#' . $recordId . '].', + ]; + } + + /** + * Transform a single object. + * + * @param T&object{id:int} $object + * + * @return array + * @phpstan-return array + */ + public function get(object $object): array + { + return [$object->id => $object->toArray()]; + } + + /** + * @param iterable $collection + * + * @return array + */ + public function getMany(iterable $collection): array + { + $result = []; + + /** + * @var int|string $key + * @var ValueObjectInterface $object + */ + foreach ($collection as $key => $object) { + $result[$key] = $object->toArray(); + } + + return $result; + } +} diff --git a/src/Domain/Infrastructure/DataSource/User/DTO/User.php b/src/Domain/Infrastructure/DataSource/User/DTO/User.php index 1c14fd3..81ab31b 100644 --- a/src/Domain/Infrastructure/DataSource/User/DTO/User.php +++ b/src/Domain/Infrastructure/DataSource/User/DTO/User.php @@ -13,16 +13,31 @@ namespace Phalcon\Api\Domain\Infrastructure\DataSource\User\DTO; -use Phalcon\Api\Domain\Infrastructure\DataSource\User\UserTypes; +use Phalcon\Api\Domain\Infrastructure\DataSource\AbstractValueObject; -use function get_object_vars; use function trim; -/** - * @phpstan-import-type TUser from UserTypes - */ -final readonly class User +final class User extends AbstractValueObject { + /** + * @param int $id + * @param int $status + * @param string $email + * @param string $password + * @param string|null $namePrefix + * @param string|null $nameFirst + * @param string|null $nameMiddle + * @param string|null $nameLast + * @param string|null $nameSuffix + * @param string|null $issuer + * @param string|null $tokenPassword + * @param string|null $tokenId + * @param string|null $preferences + * @param string|null $createdDate + * @param int|null $createdUserId + * @param string|null $updatedDate + * @param int|null $updatedUserId + */ public function __construct( public int $id, public int $status, @@ -44,21 +59,13 @@ public function __construct( ) { } + /** + * @return string + */ public function fullName(): string { return trim( ($this->nameLast ?? '') . ', ' . ($this->nameFirst ?? '') . ' ' . ($this->nameMiddle ?? '') ); } - - /** - * @return TUser - */ - public function toArray(): array - { - /** @var TUser $vars */ - $vars = get_object_vars($this); - - return $vars; - } } diff --git a/src/Domain/Infrastructure/DataSource/User/DTO/UserInput.php b/src/Domain/Infrastructure/DataSource/User/DTO/UserInput.php index cce2cca..a92a65c 100644 --- a/src/Domain/Infrastructure/DataSource/User/DTO/UserInput.php +++ b/src/Domain/Infrastructure/DataSource/User/DTO/UserInput.php @@ -14,18 +14,14 @@ namespace Phalcon\Api\Domain\Infrastructure\DataSource\User\DTO; use Phalcon\Api\Domain\ADR\InputTypes; -use Phalcon\Api\Domain\Infrastructure\DataSource\AbstractValueObject; -use Phalcon\Api\Domain\Infrastructure\DataSource\User\UserTypes; - -use function get_object_vars; +use Phalcon\Api\Domain\Infrastructure\DataSource\AbstractInputValueObject; /** * Value object for input * * @phpstan-import-type TUserInput from InputTypes - * @phpstan-import-type TUser from UserTypes */ -final class UserInput extends AbstractValueObject +final class UserInput extends AbstractInputValueObject { /** * @param int $id @@ -67,17 +63,6 @@ public function __construct( ) { } - /** - * @return TUser - */ - public function toArray(): array - { - /** @var TUser $vars */ - $vars = get_object_vars($this); - - return $vars; - } - /** * Build the concrete DTO from a sanitized array. * diff --git a/src/Domain/Infrastructure/DataSource/User/Mapper/UserMapper.php b/src/Domain/Infrastructure/DataSource/User/Mapper/UserMapper.php index 70866ff..53ec88f 100644 --- a/src/Domain/Infrastructure/DataSource/User/Mapper/UserMapper.php +++ b/src/Domain/Infrastructure/DataSource/User/Mapper/UserMapper.php @@ -15,7 +15,6 @@ use Phalcon\Api\Domain\Application\User\Command\UserPutCommand; use Phalcon\Api\Domain\Infrastructure\CommandBus\CommandInterface; -use Phalcon\Api\Domain\Infrastructure\DataSource\Interface\MapperInterface; use Phalcon\Api\Domain\Infrastructure\DataSource\User\DTO\User; use Phalcon\Api\Domain\Infrastructure\DataSource\User\UserTypes; @@ -24,11 +23,13 @@ * @phpstan-import-type TUserDbRecord from UserTypes * @phpstan-import-type TUserDomainToDbRecord from UserTypes */ -final class UserMapper implements MapperInterface +final class UserMapper implements UserMapperInterface { /** * Map Domain User -> DB row (usr_*) * + * @param CommandInterface $user + * * @return TUserDomainToDbRecord */ public function db(CommandInterface $user): array @@ -82,32 +83,4 @@ public function domain(array $row): User isset($row['usr_updated_usr_id']) ? (int)$row['usr_updated_usr_id'] : null, ); } - - /** - * Map input row -> Domain User - * - * @param TUser $row - */ - public function input(array $row): User - { - return new User( - $row['id'], - $row['status'], - (string)$row['email'], - (string)$row['password'], - $row['namePrefix'], - $row['nameFirst'], - $row['nameMiddle'], - $row['nameLast'], - $row['nameSuffix'], - $row['issuer'], - $row['tokenPassword'], - $row['tokenId'], - $row['preferences'], - $row['createdDate'], - $row['createdUserId'], - $row['updatedDate'], - $row['updatedUserId'], - ); - } } diff --git a/src/Domain/Infrastructure/DataSource/Interface/MapperInterface.php b/src/Domain/Infrastructure/DataSource/User/Mapper/UserMapperInterface.php similarity index 81% rename from src/Domain/Infrastructure/DataSource/Interface/MapperInterface.php rename to src/Domain/Infrastructure/DataSource/User/Mapper/UserMapperInterface.php index edf829a..5a2eaa4 100644 --- a/src/Domain/Infrastructure/DataSource/Interface/MapperInterface.php +++ b/src/Domain/Infrastructure/DataSource/User/Mapper/UserMapperInterface.php @@ -11,7 +11,7 @@ declare(strict_types=1); -namespace Phalcon\Api\Domain\Infrastructure\DataSource\Interface; +namespace Phalcon\Api\Domain\Infrastructure\DataSource\User\Mapper; use Phalcon\Api\Domain\Infrastructure\CommandBus\CommandInterface; use Phalcon\Api\Domain\Infrastructure\DataSource\User\DTO\User; @@ -24,7 +24,7 @@ * @phpstan-import-type TUserDbRecord from UserTypes * @phpstan-import-type TUserDomainToDbRecord from UserTypes */ -interface MapperInterface +interface UserMapperInterface { /** * Map Domain User -> DB row (usr_*) @@ -39,11 +39,4 @@ public function db(CommandInterface $user): array; * @param TUserDbRecord|array{} $row */ public function domain(array $row): User; - - /** - * Map input row -> Domain User - * - * @param TUser $row - */ - public function input(array $row): User; } diff --git a/src/Domain/Infrastructure/DataSource/User/Repository/UserRepository.php b/src/Domain/Infrastructure/DataSource/User/Repository/UserRepository.php index ae62a64..d176fd8 100644 --- a/src/Domain/Infrastructure/DataSource/User/Repository/UserRepository.php +++ b/src/Domain/Infrastructure/DataSource/User/Repository/UserRepository.php @@ -14,19 +14,16 @@ namespace Phalcon\Api\Domain\Infrastructure\DataSource\User\Repository; use Phalcon\Api\Domain\Infrastructure\DataSource\AbstractRepository; -use Phalcon\Api\Domain\Infrastructure\DataSource\Interface\MapperInterface; use Phalcon\Api\Domain\Infrastructure\DataSource\User\DTO\User; +use Phalcon\Api\Domain\Infrastructure\DataSource\User\Mapper\UserMapperInterface; use Phalcon\Api\Domain\Infrastructure\DataSource\User\UserTypes; use Phalcon\Api\Domain\Infrastructure\Enums\Common\FlagsEnum; use Phalcon\DataMapper\Pdo\Connection; -use Phalcon\DataMapper\Query\Insert; use Phalcon\DataMapper\Query\Select; -use Phalcon\DataMapper\Query\Update; /** * @phpstan-import-type TCriteria from UserTypes * @phpstan-import-type TUserRecord from UserTypes - * @phpstan-import-type TUserDbRecordOptional from UserTypes */ class UserRepository extends AbstractRepository implements UserRepositoryInterface { @@ -41,7 +38,7 @@ class UserRepository extends AbstractRepository implements UserRepositoryInterfa public function __construct( Connection $connection, - private readonly MapperInterface $mapper, + private readonly UserMapperInterface $mapper, ) { parent::__construct($connection); } @@ -107,42 +104,4 @@ public function findOneBy(array $criteria): ?User return $this->mapper->domain($result); } - - - /** - * - * @param TUserDbRecordOptional $columns - * - * @return int - */ - public function insert(array $columns): int - { - $insert = Insert::new($this->connection); - $insert - ->into($this->table) - ->columns($columns) - ->perform() - ; - - return (int)$insert->getLastInsertId(); - } - - /** - * @param int $recordId - * @param TUserDbRecordOptional $columns - * - * @return int - */ - public function update(int $recordId, array $columns): int - { - $update = Update::new($this->connection); - $update - ->table($this->table) - ->columns($columns) - ->where($this->idField . ' = ', $recordId) - ->perform() - ; - - return $recordId; - } } diff --git a/src/Domain/Infrastructure/DataSource/User/Sanitizer/UserSanitizer.php b/src/Domain/Infrastructure/DataSource/User/Sanitizer/UserSanitizer.php index db9c45b..58dd843 100644 --- a/src/Domain/Infrastructure/DataSource/User/Sanitizer/UserSanitizer.php +++ b/src/Domain/Infrastructure/DataSource/User/Sanitizer/UserSanitizer.php @@ -14,7 +14,6 @@ namespace Phalcon\Api\Domain\Infrastructure\DataSource\User\Sanitizer; use Phalcon\Api\Domain\Infrastructure\DataSource\AbstractSanitizer; -use Phalcon\Api\Domain\Infrastructure\Enums\Sanitizer\UserSanitizerEnum; final class UserSanitizer extends AbstractSanitizer { diff --git a/src/Domain/Infrastructure/Enums/Sanitizer/UserSanitizerEnum.php b/src/Domain/Infrastructure/DataSource/User/Sanitizer/UserSanitizerEnum.php similarity index 85% rename from src/Domain/Infrastructure/Enums/Sanitizer/UserSanitizerEnum.php rename to src/Domain/Infrastructure/DataSource/User/Sanitizer/UserSanitizerEnum.php index 498e66e..c561838 100644 --- a/src/Domain/Infrastructure/Enums/Sanitizer/UserSanitizerEnum.php +++ b/src/Domain/Infrastructure/DataSource/User/Sanitizer/UserSanitizerEnum.php @@ -11,11 +11,12 @@ declare(strict_types=1); -namespace Phalcon\Api\Domain\Infrastructure\Enums\Sanitizer; +namespace Phalcon\Api\Domain\Infrastructure\DataSource\User\Sanitizer; +use Phalcon\Api\Domain\Infrastructure\DataSource\Interface\SanitizerEnumInterface; use Phalcon\Filter\Filter; -enum UserSanitizerEnum implements SanitizersEnumInterface +enum UserSanitizerEnum implements SanitizerEnumInterface { case id; case status; @@ -56,6 +57,7 @@ public function sanitizer(): string self::email => Filter::FILTER_EMAIL, self::password, self::tokenId, + self::issuer, self::tokenPassword => '', // Password will be distorted default => Filter::FILTER_STRIPTAGS }; diff --git a/src/Domain/Infrastructure/Enums/Validator/UserInsertEnum.php b/src/Domain/Infrastructure/DataSource/User/Validator/UserInsertEnum.php similarity index 84% rename from src/Domain/Infrastructure/Enums/Validator/UserInsertEnum.php rename to src/Domain/Infrastructure/DataSource/User/Validator/UserInsertEnum.php index f91b54c..56864ce 100644 --- a/src/Domain/Infrastructure/Enums/Validator/UserInsertEnum.php +++ b/src/Domain/Infrastructure/DataSource/User/Validator/UserInsertEnum.php @@ -11,8 +11,9 @@ declare(strict_types=1); -namespace Phalcon\Api\Domain\Infrastructure\Enums\Validator; +namespace Phalcon\Api\Domain\Infrastructure\DataSource\User\Validator; +use Phalcon\Api\Domain\Infrastructure\DataSource\Interface\ValidatorEnumInterface; use Phalcon\Filter\Validation\Validator\Email; use Phalcon\Filter\Validation\Validator\PresenceOf; diff --git a/src/Domain/Infrastructure/Enums/Validator/UserUpdateEnum.php b/src/Domain/Infrastructure/DataSource/User/Validator/UserUpdateEnum.php similarity index 87% rename from src/Domain/Infrastructure/Enums/Validator/UserUpdateEnum.php rename to src/Domain/Infrastructure/DataSource/User/Validator/UserUpdateEnum.php index 2ce9983..7e08ea5 100644 --- a/src/Domain/Infrastructure/Enums/Validator/UserUpdateEnum.php +++ b/src/Domain/Infrastructure/DataSource/User/Validator/UserUpdateEnum.php @@ -11,8 +11,9 @@ declare(strict_types=1); -namespace Phalcon\Api\Domain\Infrastructure\Enums\Validator; +namespace Phalcon\Api\Domain\Infrastructure\DataSource\User\Validator; +use Phalcon\Api\Domain\Infrastructure\DataSource\Interface\ValidatorEnumInterface; use Phalcon\Api\Domain\Infrastructure\DataSource\Validation\AbsInt; use Phalcon\Filter\Validation\Validator\Email; use Phalcon\Filter\Validation\Validator\PresenceOf; diff --git a/src/Domain/Infrastructure/DataSource/User/Validator/UserValidator.php b/src/Domain/Infrastructure/DataSource/User/Validator/UserValidator.php index a22c578..52d9a00 100644 --- a/src/Domain/Infrastructure/DataSource/User/Validator/UserValidator.php +++ b/src/Domain/Infrastructure/DataSource/User/Validator/UserValidator.php @@ -14,11 +14,8 @@ namespace Phalcon\Api\Domain\Infrastructure\DataSource\User\Validator; use Phalcon\Api\Domain\Infrastructure\DataSource\Validation\AbstractValidator; -use Phalcon\Api\Domain\Infrastructure\Enums\Validator\UserInsertEnum; final class UserValidator extends AbstractValidator { - use UserValidatorTrait; - protected string $fields = UserInsertEnum::class; } diff --git a/src/Domain/Infrastructure/DataSource/User/Validator/UserValidatorTrait.php b/src/Domain/Infrastructure/DataSource/User/Validator/UserValidatorTrait.php deleted file mode 100644 index c2fc0b9..0000000 --- a/src/Domain/Infrastructure/DataSource/User/Validator/UserValidatorTrait.php +++ /dev/null @@ -1,45 +0,0 @@ - - * - * For the full copyright and license information, please view - * the LICENSE file that was distributed with this source code. - */ - -declare(strict_types=1); - -namespace Phalcon\Api\Domain\Infrastructure\DataSource\User\Validator; - -use Phalcon\Api\Domain\Infrastructure\CommandBus\CommandInterface; -use Phalcon\Api\Domain\Infrastructure\DataSource\Validation\Result; - -trait UserValidatorTrait -{ - /** - * Validate a AuthInput and return an array of errors. - * Empty array means valid. - * - * @param CommandInterface $command - * - * @return Result - */ - public function validate(CommandInterface $command): Result - { - $errors = $this->runValidations($command); - if (true !== empty($errors)) { - return Result::error($errors); - } - - return Result::success(); - } - - /** - * @param CommandInterface $command - * - * @return array - */ - abstract protected function runValidations(CommandInterface $command): array; -} diff --git a/src/Domain/Infrastructure/DataSource/User/Validator/UserValidatorUpdate.php b/src/Domain/Infrastructure/DataSource/User/Validator/UserValidatorUpdate.php index 4bded48..34f7d03 100644 --- a/src/Domain/Infrastructure/DataSource/User/Validator/UserValidatorUpdate.php +++ b/src/Domain/Infrastructure/DataSource/User/Validator/UserValidatorUpdate.php @@ -14,11 +14,8 @@ namespace Phalcon\Api\Domain\Infrastructure\DataSource\User\Validator; use Phalcon\Api\Domain\Infrastructure\DataSource\Validation\AbstractValidator; -use Phalcon\Api\Domain\Infrastructure\Enums\Validator\UserUpdateEnum; final class UserValidatorUpdate extends AbstractValidator { - use UserValidatorTrait; - protected string $fields = UserUpdateEnum::class; } diff --git a/src/Domain/Infrastructure/DataSource/Validation/AbsInt.php b/src/Domain/Infrastructure/DataSource/Validation/AbsInt.php index 2344291..38f0018 100644 --- a/src/Domain/Infrastructure/DataSource/Validation/AbsInt.php +++ b/src/Domain/Infrastructure/DataSource/Validation/AbsInt.php @@ -13,6 +13,7 @@ namespace Phalcon\Api\Domain\Infrastructure\DataSource\Validation; +use Phalcon\Di\Exception as DIException; use Phalcon\Filter\Validation; use Phalcon\Filter\Validation\Validator\Numericality; @@ -31,6 +32,7 @@ final class AbsInt extends Numericality * * @return bool * @throws Validation\Exception + * @throws DIException */ public function validate(Validation $validation, string $field): bool { diff --git a/src/Domain/Infrastructure/DataSource/Validation/AbstractValidator.php b/src/Domain/Infrastructure/DataSource/Validation/AbstractValidator.php index bc15ea5..bc6bbd6 100644 --- a/src/Domain/Infrastructure/DataSource/Validation/AbstractValidator.php +++ b/src/Domain/Infrastructure/DataSource/Validation/AbstractValidator.php @@ -14,7 +14,7 @@ namespace Phalcon\Api\Domain\Infrastructure\DataSource\Validation; use Phalcon\Api\Domain\Infrastructure\CommandBus\CommandInterface; -use Phalcon\Api\Domain\Infrastructure\Enums\Validator\ValidatorEnumInterface; +use Phalcon\Api\Domain\Infrastructure\DataSource\Interface\ValidatorEnumInterface; use Phalcon\Filter\Validation\ValidationInterface; use Phalcon\Filter\Validation\ValidatorInterface as PhalconValidator; @@ -27,6 +27,24 @@ public function __construct( ) { } + /** + * Validate a Command and return an array of errors. + * Empty array means valid. + * + * @param CommandInterface $command + * + * @return Result + */ + public function validate(CommandInterface $command): Result + { + $errors = $this->runValidations($command); + if (true !== empty($errors)) { + return Result::error($errors); + } + + return Result::success(); + } + /** * @param CommandInterface $command * diff --git a/src/Domain/Infrastructure/Encryption/TokenCache.php b/src/Domain/Infrastructure/Encryption/TokenCache.php index c974469..7a53d99 100644 --- a/src/Domain/Infrastructure/Encryption/TokenCache.php +++ b/src/Domain/Infrastructure/Encryption/TokenCache.php @@ -30,10 +30,10 @@ * @phpstan-import-type TTokenIssue from TokenManagerInterface * @phpstan-import-type TValidatorErrors from InputTypes */ -final class TokenCache implements TokenCacheInterface +final readonly class TokenCache implements TokenCacheInterface { public function __construct( - private readonly Cache $cache, + private Cache $cache, ) { } diff --git a/src/Domain/Infrastructure/Enums/Container/AuthDefinitionsEnum.php b/src/Domain/Infrastructure/Enums/Container/AuthDefinitionsEnum.php index 2f09a74..afacbe2 100644 --- a/src/Domain/Infrastructure/Enums/Container/AuthDefinitionsEnum.php +++ b/src/Domain/Infrastructure/Enums/Container/AuthDefinitionsEnum.php @@ -24,6 +24,7 @@ use Phalcon\Api\Domain\Infrastructure\CommandBus\CommandBus; use Phalcon\Api\Domain\Infrastructure\Container; use Phalcon\Api\Domain\Infrastructure\DataSource\Auth\Sanitizer\AuthSanitizer; +use Phalcon\Api\Domain\Infrastructure\DataSource\Auth\Transformer\AuthTransformer; use Phalcon\Api\Domain\Infrastructure\DataSource\Auth\Validator\AuthLoginValidator; use Phalcon\Api\Domain\Infrastructure\DataSource\Auth\Validator\AuthTokenValidator; use Phalcon\Api\Domain\Infrastructure\DataSource\User\Repository\UserRepository; @@ -47,6 +48,7 @@ enum AuthDefinitionsEnum: string implements DefinitionsEnumInterface case AuthSanitizer = AuthSanitizer::class; case AuthLoginValidator = AuthLoginValidator::class; case AuthTokenValidator = AuthTokenValidator::class; + case AuthTransformer = AuthTransformer::class; /** * @return TService @@ -55,7 +57,7 @@ public function definition(): array { return match ($this) { self::AuthCommandFactory => [ - 'className' => AuthCommandFactory::class, + 'className' => $this->value, 'arguments' => [ [ 'type' => 'service', @@ -63,11 +65,19 @@ public function definition(): array ], ], ], - self::AuthLoginPost => $this->getService(AuthLoginPostService::class), - self::AuthLogoutPost => $this->getService(AuthLogoutPostService::class), - self::AuthRefreshPost => $this->getService(AuthRefreshPostService::class), + self::AuthLoginPost, + self::AuthLogoutPost, + self::AuthRefreshPost => [ + 'className' => $this->value, + 'arguments' => [ + [ + 'type' => 'service', + 'name' => AuthFacade::class, + ], + ], + ], self::AuthFacade => [ - 'className' => AuthFacade::class, + 'className' => $this->value, 'arguments' => [ [ 'type' => 'service', @@ -80,7 +90,7 @@ public function definition(): array ], ], self::AuthLoginHandler => [ - 'className' => AuthLoginPostHandler::class, + 'className' => $this->value, 'arguments' => [ [ 'type' => 'service', @@ -90,6 +100,10 @@ public function definition(): array 'type' => 'service', 'name' => TokenManager::class, ], + [ + 'type' => 'service', + 'name' => AuthTransformer::class, + ], [ 'type' => 'service', 'name' => Security::class, @@ -100,10 +114,26 @@ public function definition(): array ], ], ], - self::AuthLogoutHandler => $this->getHandlerService(AuthLogoutPostHandler::class), - self::AuthRefreshHandler => $this->getHandlerService(AuthRefreshPostHandler::class), + self::AuthLogoutHandler, + self::AuthRefreshHandler => [ + 'className' => $this->value, + 'arguments' => [ + [ + 'type' => 'service', + 'name' => TokenManager::class, + ], + [ + 'type' => 'service', + 'name' => AuthTransformer::class, + ], + [ + 'type' => 'service', + 'name' => AuthTokenValidator::class, + ], + ], + ], self::AuthSanitizer => [ - 'className' => AuthSanitizer::class, + 'className' => $this->value, 'arguments' => [ [ 'type' => 'service', @@ -112,7 +142,7 @@ public function definition(): array ], ], self::AuthLoginValidator => [ - 'className' => AuthLoginValidator::class, + 'className' => $this->value, 'arguments' => [ [ 'type' => 'service', @@ -121,7 +151,7 @@ public function definition(): array ], ], self::AuthTokenValidator => [ - 'className' => AuthTokenValidator::class, + 'className' => $this->value, 'arguments' => [ [ 'type' => 'service', @@ -137,49 +167,17 @@ public function definition(): array ], ], ], + self::AuthTransformer => [ + 'className' => $this->value, + ], }; } public function isShared(): bool { - return false; - } - - /** - * @param class-string $className - * - * @return TService - */ - private function getHandlerService(string $className): array - { - return [ - 'className' => $className, - 'arguments' => [ - [ - 'type' => 'service', - 'name' => TokenManager::class, - ], - [ - 'type' => 'service', - 'name' => AuthTokenValidator::class, - ], - ], - ]; - } - - /** - * @return TService - */ - private function getService(string $className): array - { - return [ - 'className' => $className, - 'arguments' => [ - [ - 'type' => 'service', - 'name' => AuthFacade::class, - ], - ], - ]; + return match ($this) { + self::AuthSanitizer => true, + default => false, + }; } } diff --git a/src/Domain/Infrastructure/Enums/Container/CommonDefinitionsEnum.php b/src/Domain/Infrastructure/Enums/Container/CommonDefinitionsEnum.php index 70f95ea..627dc6e 100644 --- a/src/Domain/Infrastructure/Enums/Container/CommonDefinitionsEnum.php +++ b/src/Domain/Infrastructure/Enums/Container/CommonDefinitionsEnum.php @@ -16,13 +16,13 @@ use Phalcon\Api\Domain\Infrastructure\CommandBus\CommandBus; use Phalcon\Api\Domain\Infrastructure\CommandBus\ContainerHandlerLocator; use Phalcon\Api\Domain\Infrastructure\Container; +use Phalcon\Api\Domain\Infrastructure\DataSource\Transformer\Transformer; use Phalcon\Api\Domain\Infrastructure\Encryption\JWTToken; use Phalcon\Api\Domain\Infrastructure\Encryption\TokenCache; use Phalcon\Api\Domain\Infrastructure\Encryption\TokenManager; use Phalcon\Api\Domain\Infrastructure\Env\EnvManager; use Phalcon\Api\Responder\JsonResponder; use Phalcon\Cache\Cache; -use Phalcon\Events\Manager as EventsManager; use Phalcon\Http\Request; use Phalcon\Http\Response; use Phalcon\Mvc\Router; @@ -34,7 +34,6 @@ enum CommonDefinitionsEnum: string implements DefinitionsEnumInterface { case CommandBus = CommandBus::class; - case EventsManager = Container::EVENTS_MANAGER; case EnvManager = EnvManager::class; case JsonResponder = JsonResponder::class; case JWTToken = JWTToken::class; @@ -44,6 +43,7 @@ enum CommonDefinitionsEnum: string implements DefinitionsEnumInterface case Request = Container::REQUEST; case Response = Container::RESPONSE; case Router = Container::ROUTER; + case Transformer = Transformer::class; /** * @return TService @@ -52,7 +52,7 @@ public function definition(): array { return match ($this) { self::CommandBus => [ - 'className' => CommandBus::class, + 'className' => $this->value, 'arguments' => [ [ 'type' => 'service', @@ -60,28 +60,14 @@ public function definition(): array ], ], ], - self::EventsManager => [ - 'className' => EventsManager::class, - 'calls' => [ - [ - 'method' => 'enablePriorities', - 'arguments' => [ - [ - 'type' => 'parameter', - 'value' => true, - ], - ], - ], - ], - ], - self::EnvManager => [ - 'className' => EnvManager::class, - ], - self::JsonResponder => [ - 'className' => JsonResponder::class, + self::EnvManager, + self::JsonResponder, + self::Registry, + self::Transformer => [ + 'className' => $this->value, ], self::JWTToken => [ - 'className' => JWTToken::class, + 'className' => $this->value, 'arguments' => [ [ 'type' => 'service', @@ -90,7 +76,7 @@ public function definition(): array ], ], self::JWTTokenCache => [ - 'className' => TokenCache::class, + 'className' => $this->value, 'arguments' => [ [ 'type' => 'service', @@ -99,7 +85,7 @@ public function definition(): array ], ], self::JWTTokenManager => [ - 'className' => TokenManager::class, + 'className' => $this->value, 'arguments' => [ [ 'type' => 'service', @@ -115,9 +101,6 @@ public function definition(): array ], ], ], - self::Registry => [ - 'className' => Registry::class, - ], self::Request => [ 'className' => Request::class, ], diff --git a/src/Domain/Infrastructure/Enums/Container/CompanyDefinitionsEnum.php b/src/Domain/Infrastructure/Enums/Container/CompanyDefinitionsEnum.php new file mode 100644 index 0000000..0f7f973 --- /dev/null +++ b/src/Domain/Infrastructure/Enums/Container/CompanyDefinitionsEnum.php @@ -0,0 +1,218 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Phalcon\Api\Domain\Infrastructure\Enums\Container; + +use Phalcon\Api\Domain\Application\Company\Command\CompanyCommandFactory; +use Phalcon\Api\Domain\Application\Company\Facade\CompanyFacade; +use Phalcon\Api\Domain\Application\Company\Handler\CompanyDeleteHandler; +use Phalcon\Api\Domain\Application\Company\Handler\CompanyGetHandler; +use Phalcon\Api\Domain\Application\Company\Handler\CompanyGetManyHandler; +use Phalcon\Api\Domain\Application\Company\Handler\CompanyPostHandler; +use Phalcon\Api\Domain\Application\Company\Handler\CompanyPutHandler; +use Phalcon\Api\Domain\Application\Company\Service\CompanyDeleteService; +use Phalcon\Api\Domain\Application\Company\Service\CompanyGetManyService; +use Phalcon\Api\Domain\Application\Company\Service\CompanyGetService; +use Phalcon\Api\Domain\Application\Company\Service\CompanyPostService; +use Phalcon\Api\Domain\Application\Company\Service\CompanyPutService; +use Phalcon\Api\Domain\Infrastructure\CommandBus\CommandBus; +use Phalcon\Api\Domain\Infrastructure\Container; +use Phalcon\Api\Domain\Infrastructure\DataSource\Company\Mapper\CompanyMapper; +use Phalcon\Api\Domain\Infrastructure\DataSource\Company\Repository\CompanyRepository; +use Phalcon\Api\Domain\Infrastructure\DataSource\Company\Sanitizer\CompanySanitizer; +use Phalcon\Api\Domain\Infrastructure\DataSource\Company\Validator\CompanyValidator; +use Phalcon\Api\Domain\Infrastructure\DataSource\Company\Validator\CompanyValidatorUpdate; +use Phalcon\Api\Domain\Infrastructure\DataSource\Transformer\Transformer; +use Phalcon\Api\Domain\Infrastructure\Encryption\Security; +use Phalcon\DataMapper\Pdo\Connection; +use Phalcon\Filter\Validation; +use Phalcon\Support\Registry; + +/** + * @phpstan-import-type TService from Container + */ +enum CompanyDefinitionsEnum: string implements DefinitionsEnumInterface +{ + case CompanyCommandFactory = CompanyCommandFactory::class; + case CompanyDelete = CompanyDeleteService::class; + case CompanyGet = CompanyGetService::class; + case CompanyGetMany = CompanyGetManyService::class; + case CompanyPost = CompanyPostService::class; + case CompanyPut = CompanyPutService::class; + case CompanyFacade = CompanyFacade::class; + case CompanyDeleteHandler = CompanyDeleteHandler::class; + case CompanyGetHandler = CompanyGetHandler::class; + case CompanyGetManyHandler = CompanyGetManyHandler::class; + case CompanyPostHandler = CompanyPostHandler::class; + case CompanyPutHandler = CompanyPutHandler::class; + case CompanyMapper = CompanyMapper::class; + case CompanyRepository = CompanyRepository::class; + case CompanySanitizer = CompanySanitizer::class; + case CompanyValidator = CompanyValidator::class; + case CompanyValidatorUpdate = CompanyValidatorUpdate::class; + + /** + * @return TService + */ + public function definition(): array + { + return match ($this) { + self::CompanyCommandFactory => [ + 'className' => $this->value, + 'arguments' => [ + [ + 'type' => 'service', + 'name' => CompanySanitizer::class, + ], + ], + ], + self::CompanyDelete, + self::CompanyGet, + self::CompanyGetMany, + self::CompanyPost, + self::CompanyPut => [ + 'className' => $this->value, + 'arguments' => [ + [ + 'type' => 'service', + 'name' => CompanyFacade::class, + ], + ], + ], + self::CompanyFacade => [ + 'className' => $this->value, + 'arguments' => [ + [ + 'type' => 'service', + 'name' => CommandBus::class, + ], + [ + 'type' => 'service', + 'name' => CompanyCommandFactory::class, + ], + ], + ], + self::CompanyDeleteHandler, + self::CompanyGetHandler, + self::CompanyGetManyHandler => [ + 'className' => $this->value, + 'arguments' => [ + [ + 'type' => 'service', + 'name' => CompanyRepository::class, + ], + [ + 'type' => 'service', + 'name' => Transformer::class, + ], + ], + ], + self::CompanyPostHandler => $this->getServicePutPost( + $this->value, + CompanyValidator::class + ), + self::CompanyPutHandler => $this->getServicePutPost( + $this->value, + CompanyValidatorUpdate::class + ), + self::CompanyMapper => [ + 'className' => $this->value, + ], + self::CompanyRepository => [ + 'className' => $this->value, + 'arguments' => [ + [ + 'type' => 'service', + 'name' => Connection::class, + ], + [ + 'type' => 'service', + 'name' => CompanyMapper::class, + ], + ], + ], + self::CompanySanitizer => [ + 'className' => $this->value, + 'arguments' => [ + [ + 'type' => 'service', + 'name' => Container::FILTER, + ], + ], + ], + self::CompanyValidator, + self::CompanyValidatorUpdate => [ + 'className' => $this->value, + 'arguments' => [ + [ + 'type' => 'service', + 'name' => Validation::class, + ], + ], + ], + }; + } + + public function isShared(): bool + { + return match ($this) { + self::CompanyMapper, + self::CompanyRepository, + self::CompanySanitizer => true, + default => false, + }; + } + + /** + * @param class-string $className + * @param class-string $validatorName + * + * @return TService + */ + private function getServicePutPost(string $className, string $validatorName): array + { + return [ + 'className' => $className, + 'arguments' => [ + [ + 'type' => 'service', + 'name' => $validatorName, + ], + [ + 'type' => 'service', + 'name' => CompanyMapper::class, + ], + [ + 'type' => 'service', + 'name' => CompanyRepository::class, + ], + [ + 'type' => 'service', + 'name' => Container::EVENTS_MANAGER, + ], + [ + 'type' => 'service', + 'name' => Transformer::class, + ], + [ + 'type' => 'service', + 'name' => Registry::class, + ], + [ + 'type' => 'service', + 'name' => Security::class, + ], + ], + ]; + } +} diff --git a/src/Domain/Infrastructure/Enums/Container/UserDefinitionsEnum.php b/src/Domain/Infrastructure/Enums/Container/UserDefinitionsEnum.php index 2e19070..c6a3463 100644 --- a/src/Domain/Infrastructure/Enums/Container/UserDefinitionsEnum.php +++ b/src/Domain/Infrastructure/Enums/Container/UserDefinitionsEnum.php @@ -25,6 +25,7 @@ use Phalcon\Api\Domain\Application\User\Service\UserPutService; use Phalcon\Api\Domain\Infrastructure\CommandBus\CommandBus; use Phalcon\Api\Domain\Infrastructure\Container; +use Phalcon\Api\Domain\Infrastructure\DataSource\Transformer\Transformer; use Phalcon\Api\Domain\Infrastructure\DataSource\User\Mapper\UserMapper; use Phalcon\Api\Domain\Infrastructure\DataSource\User\Repository\UserRepository; use Phalcon\Api\Domain\Infrastructure\DataSource\User\Sanitizer\UserSanitizer; @@ -63,7 +64,7 @@ public function definition(): array { return match ($this) { self::UserCommandFactory => [ - 'className' => UserCommandFactory::class, + 'className' => $this->value, 'arguments' => [ [ 'type' => 'service', @@ -71,12 +72,20 @@ public function definition(): array ], ], ], - self::UserDelete => $this->getService(UserDeleteService::class), - self::UserGet => $this->getService(UserGetService::class), - self::UserPost => $this->getService(UserPostService::class), - self::UserPut => $this->getService(UserPutService::class), + self::UserDelete, + self::UserGet, + self::UserPost, + self::UserPut => [ + 'className' => $this->value, + 'arguments' => [ + [ + 'type' => 'service', + 'name' => UserFacade::class, + ], + ], + ], self::UserFacade => [ - 'className' => UserFacade::class, + 'className' => $this->value, 'arguments' => [ [ 'type' => 'service', @@ -88,37 +97,33 @@ public function definition(): array ], ], ], - self::UserDeleteHandler => [ - 'className' => UserDeleteHandler::class, + self::UserDeleteHandler, + self::UserGetHandler => [ + 'className' => $this->value, 'arguments' => [ [ 'type' => 'service', 'name' => UserRepository::class, ], - ], - ], - self::UserGetHandler => [ - 'className' => UserGetHandler::class, - 'arguments' => [ [ 'type' => 'service', - 'name' => UserRepository::class, + 'name' => Transformer::class, ], ], ], self::UserPostHandler => $this->getServicePutPost( - UserPostHandler::class, + $this->value, UserValidator::class ), self::UserPutHandler => $this->getServicePutPost( - UserPutHandler::class, + $this->value, UserValidatorUpdate::class ), self::UserMapper => [ - 'className' => UserMapper::class, + 'className' => $this->value, ], self::UserRepository => [ - 'className' => UserRepository::class, + 'className' => $this->value, 'arguments' => [ [ 'type' => 'service', @@ -131,7 +136,7 @@ public function definition(): array ], ], self::UserSanitizer => [ - 'className' => UserSanitizer::class, + 'className' => $this->value, 'arguments' => [ [ 'type' => 'service', @@ -139,32 +144,27 @@ public function definition(): array ], ], ], - self::UserValidator => $this->getServiceValidator(UserValidator::class), - self::UserValidatorUpdate => $this->getServiceValidator(UserValidatorUpdate::class), + self::UserValidator, + self::UserValidatorUpdate => [ + 'className' => $this->value, + 'arguments' => [ + [ + 'type' => 'service', + 'name' => Validation::class, + ], + ], + ], }; } public function isShared(): bool { - return false; - } - - /** - * @param class-string $className - * - * @return TService - */ - private function getService(string $className): array - { - return [ - 'className' => $className, - 'arguments' => [ - [ - 'type' => 'service', - 'name' => UserFacade::class, - ], - ], - ]; + return match ($this) { + self::UserMapper, + self::UserRepository, + self::UserSanitizer => true, + default => false, + }; } /** @@ -192,29 +192,19 @@ private function getServicePutPost(string $className, string $validatorName): ar ], [ 'type' => 'service', - 'name' => Registry::class, + 'name' => Container::EVENTS_MANAGER, ], [ 'type' => 'service', - 'name' => Security::class, + 'name' => Transformer::class, ], - ], - ]; - } - - /** - * @param class-string $className - * - * @return TService - */ - private function getServiceValidator(string $className): array - { - return [ - 'className' => $className, - 'arguments' => [ [ 'type' => 'service', - 'name' => Validation::class, + 'name' => Registry::class, + ], + [ + 'type' => 'service', + 'name' => Security::class, ], ], ]; diff --git a/src/Domain/Infrastructure/Enums/Http/RoutesEnum.php b/src/Domain/Infrastructure/Enums/Http/RoutesEnum.php index 48660c4..ea17f0b 100644 --- a/src/Domain/Infrastructure/Enums/Http/RoutesEnum.php +++ b/src/Domain/Infrastructure/Enums/Http/RoutesEnum.php @@ -16,6 +16,11 @@ use Phalcon\Api\Domain\Application\Auth\Service\AuthLoginPostService; use Phalcon\Api\Domain\Application\Auth\Service\AuthLogoutPostService; use Phalcon\Api\Domain\Application\Auth\Service\AuthRefreshPostService; +use Phalcon\Api\Domain\Application\Company\Service\CompanyDeleteService; +use Phalcon\Api\Domain\Application\Company\Service\CompanyGetManyService; +use Phalcon\Api\Domain\Application\Company\Service\CompanyGetService; +use Phalcon\Api\Domain\Application\Company\Service\CompanyPostService; +use Phalcon\Api\Domain\Application\Company\Service\CompanyPutService; use Phalcon\Api\Domain\Application\User\Service\UserDeleteService; use Phalcon\Api\Domain\Application\User\Service\UserGetService; use Phalcon\Api\Domain\Application\User\Service\UserPostService; @@ -52,6 +57,12 @@ enum RoutesEnum: int case authLogoutPost = 12; case authRefreshPost = 13; + case companyDelete = 31; + case companyGet = 32; + case companyGetMany = 33; + case companyPost = 34; + case companyPut = 35; + case userDelete = 21; case userGet = 22; case userPost = 23; @@ -74,9 +85,14 @@ public function method(): string self::authLoginPost, self::authLogoutPost, self::authRefreshPost, + self::companyPost, self::userPost => self::POST, + self::companyDelete, self::userDelete => self::DELETE, + self::companyGet, + self::companyGetMany, self::userGet => self::GET, + self::companyPut, self::userPut => self::PUT, }; } @@ -106,6 +122,11 @@ public function prefix(): string self::authLoginPost, self::authLogoutPost, self::authRefreshPost => 'auth', + self::companyDelete, + self::companyGet, + self::companyGetMany, + self::companyPost, + self::companyPut => 'company', self::userDelete, self::userGet, self::userPost, @@ -121,6 +142,11 @@ public function service(): string self::authLoginPost => AuthLoginPostService::class, self::authLogoutPost => AuthLogoutPostService::class, self::authRefreshPost => AuthRefreshPostService::class, + self::companyDelete => CompanyDeleteService::class, + self::companyGet => CompanyGetService::class, + self::companyGetMany => CompanyGetManyService::class, + self::companyPost => CompanyPostService::class, + self::companyPut => CompanyPutService::class, self::userDelete => UserDeleteService::class, self::userGet => UserGetService::class, self::userPost => UserPostService::class, @@ -137,10 +163,8 @@ public function suffix(): string self::authLoginPost => '/login', self::authLogoutPost => '/logout', self::authRefreshPost => '/refresh', - self::userDelete, - self::userGet, - self::userPost, - self::userPut => '', + self::companyGetMany => '/all', + default => '', }; } } diff --git a/src/Domain/Infrastructure/Listeners/DbErrorListener.php b/src/Domain/Infrastructure/Listeners/DbErrorListener.php new file mode 100644 index 0000000..881a645 --- /dev/null +++ b/src/Domain/Infrastructure/Listeners/DbErrorListener.php @@ -0,0 +1,44 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Phalcon\Api\Domain\Infrastructure\Listeners; + +use Phalcon\Api\Domain\Infrastructure\CommandBus\HandlerInterface; +use Phalcon\Events\Event; +use Psr\Log\LoggerInterface; + +use function get_class; + +final readonly class DbErrorListener +{ + public function __construct( + private LoggerInterface $logger, + ) { + } + + /** + * @param Event $event + * @param HandlerInterface $handler + * + * @return void + */ + public function pdoError( + Event $event, + HandlerInterface $handler + ): void { + $class = get_class($handler); + /** @var string $data */ + $data = $event->getData(); + $this->logger->error($class . ': ' . $data); + } +} diff --git a/src/Domain/Infrastructure/Middleware/AbstractMiddleware.php b/src/Domain/Infrastructure/Middleware/AbstractMiddleware.php index fafaaeb..605af96 100644 --- a/src/Domain/Infrastructure/Middleware/AbstractMiddleware.php +++ b/src/Domain/Infrastructure/Middleware/AbstractMiddleware.php @@ -19,9 +19,7 @@ use Phalcon\Api\Responder\JsonResponder; use Phalcon\Api\Responder\ResponderInterface; use Phalcon\Api\Responder\ResponderTypes; -use Phalcon\Events\Exception as EventsException; use Phalcon\Http\RequestInterface; -use Phalcon\Http\Response\Exception as ResponseException; use Phalcon\Http\ResponseInterface; use Phalcon\Mvc\Micro; use Phalcon\Mvc\Micro\MiddlewareInterface; @@ -76,8 +74,6 @@ public function isEmptyBearerToken( * @param TErrors $errors * * @return void - * @throws EventsException - * @throws ResponseException */ protected function halt( Micro $application, diff --git a/tests/AbstractUnitTestCase.php b/tests/AbstractUnitTestCase.php index 4755dbc..e8a7619 100644 --- a/tests/AbstractUnitTestCase.php +++ b/tests/AbstractUnitTestCase.php @@ -20,6 +20,7 @@ use Phalcon\Api\Domain\Infrastructure\Container; use Phalcon\Api\Domain\Infrastructure\Encryption\Security; use Phalcon\Api\Domain\Infrastructure\Enums\Common\JWTEnum; +use Phalcon\Api\Tests\Fixtures\Domain\Migrations\CompaniesMigration; use Phalcon\Api\Tests\Fixtures\Domain\Migrations\UsersMigration; use Phalcon\DataMapper\Pdo\Connection; use Phalcon\Encryption\Security\JWT\Builder; @@ -97,6 +98,71 @@ public function getConnection(): Connection return $this->connection; } + /** + * @param CompaniesMigration $migration + * @param array $fields + * + * @return array + */ + public function getNewCompany(CompaniesMigration $migration, array $fields = []): array + { + $companyData = $this->getNewCompanyData($fields); + $companyId = $migration->insert( + null, + $companyData['com_name'], + $companyData['com_email'], + $companyData['com_phone'], + $companyData['com_website'], + $companyData['com_address_line_1'], + $companyData['com_address_line_2'], + $companyData['com_city'], + $companyData['com_state_province'], + $companyData['com_zip_code'], + $companyData['com_country'], + $companyData['com_created_date'], + $companyData['com_created_usr_id'], + $companyData['com_updated_date'], + $companyData['com_updated_usr_id'], + ); + + $dbUser = $this->getFromDatabase( + 'co_companies', + [ + 'com_id' => $companyId, + ] + ); + + return $dbUser[0]; + } + + /** + * @param array $fields + * + * @return array + */ + public function getNewCompanyData(array $fields = []): array + { + $faker = Factory::create(); + + return [ + 'com_id' => 0, + 'com_name' => $fields['com_name'] ?? $faker->company(), + 'com_email' => $fields['com_email'] ?? $faker->safeEmail(), + 'com_phone' => $fields['com_phone'] ?? $faker->phoneNumber(), + 'com_website' => $fields['com_website'] ?? $faker->domainName(), + 'com_address_line_1' => $fields['com_address_line_1'] ?? $faker->streetAddress(), + 'com_address_line_2' => $fields['com_address_line_2'] ?? $faker->streetAddress(), + 'com_city' => $fields['com_city'] ?? $faker->city(), + 'com_state_province' => $fields['com_state_province'] ?? $faker->city(), + 'com_zip_code' => $fields['com_zip_code'] ?? $faker->postcode(), + 'com_country' => $fields['com_country'] ?? $faker->countryISOAlpha3(), + 'com_created_date' => $fields['com_created_date'] ?? $faker->date(Dates::DATE_TIME_FORMAT), + 'com_created_usr_id' => $fields['com_created_usr_id'] ?? 0, + 'com_updated_date' => $fields['com_updated_date'] ?? $faker->date(Dates::DATE_TIME_FORMAT), + 'com_updated_usr_id' => $fields['com_updated_usr_id'] ?? 0, + ]; + } + /** * @param UsersMigration $migration * @param array $fields diff --git a/tests/Fixtures/Domain/Migrations/CompaniesMigration.php b/tests/Fixtures/Domain/Migrations/CompaniesMigration.php new file mode 100644 index 0000000..220b699 --- /dev/null +++ b/tests/Fixtures/Domain/Migrations/CompaniesMigration.php @@ -0,0 +1,100 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Phalcon\Api\Tests\Fixtures\Domain\Migrations; + +use PHPUnit\Framework\Assert; + +final class CompaniesMigration extends AbstractMigration +{ + protected string $table = 'co_companies'; + + public function insert( + ?int $id, + string $name, + string $email, + string $phone, + string $website, + string $addressLine1, + string $addressLine2, + string $city, + string $stateProvince, + string $zip_code, + string $country, + string $createdDate, + int $createdUserId, + string $updatedDate, + int $updatedUserId + ): int { + $sql = "INSERT INTO $this->table ( + com_id, + com_name, + com_email, + com_phone, + com_website, + com_address_line_1, + com_address_line_2, + com_city, + com_state_province, + com_zip_code, + com_country, + com_created_date, + com_created_usr_id, + com_updated_date, + com_updated_usr_id + ) VALUES ( + :id, + :name, + :email, + :phone, + :website, + :addressLine1, + :addressLine2, + :city, + :stateProvince, + :zip_code, + :country, + :createdDate, + :createdUserId, + :updatedDate, + :updatedUserId + )"; + + $stmt = $this->connection->prepare($sql); + $params = [ + ':id' => $id, + ':name' => $name, + ':email' => $email, + ':phone' => $phone, + ':website' => $website, + ':addressLine1' => $addressLine1, + ':addressLine2' => $addressLine2, + ':city' => $city, + ':stateProvince' => $stateProvince, + ':zip_code' => $zip_code, + ':country' => $country, + ':createdDate' => $createdDate, + ':createdUserId' => $createdUserId, + ':updatedDate' => $updatedDate, + ':updatedUserId' => $updatedUserId, + ]; + $result = $stmt->execute($params); + if (!$result) { + Assert::fail( + "Failed to insert id [#$id] into table [$this->table]" + ); + } + + return (int)$this->connection->lastInsertId(); + } +} diff --git a/tests/Unit/Domain/Application/Company/Command/CompanyCommandFactoryTest.php b/tests/Unit/Domain/Application/Company/Command/CompanyCommandFactoryTest.php new file mode 100644 index 0000000..2997bcb --- /dev/null +++ b/tests/Unit/Domain/Application/Company/Command/CompanyCommandFactoryTest.php @@ -0,0 +1,277 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Phalcon\Api\Tests\Unit\Domain\Application\Company\Command; + +use Faker\Factory as FakerFactory; +use Phalcon\Api\Domain\Application\Company\Command\CompanyCommandFactory; +use Phalcon\Api\Domain\Application\Company\Command\CompanyDeleteCommand; +use Phalcon\Api\Domain\Application\Company\Command\CompanyGetCommand; +use Phalcon\Api\Domain\Application\Company\Command\CompanyPostCommand; +use Phalcon\Api\Domain\Application\Company\Command\CompanyPutCommand; +use Phalcon\Api\Domain\Infrastructure\Container; +use Phalcon\Api\Domain\Infrastructure\DataSource\Company\Sanitizer\CompanySanitizer; +use Phalcon\Api\Tests\AbstractUnitTestCase; +use Phalcon\Filter\Filter; + +final class CompanyCommandFactoryTest extends AbstractUnitTestCase +{ + public function testDelete(): void + { + $sanitizer = $this->container->get(CompanySanitizer::class); + $factory = new CompanyCommandFactory($sanitizer); + $faker = FakerFactory::create(); + + $input = [ + 'id' => $faker->numberBetween(1, 1000), + 'name' => $faker->company(), + 'email' => " Foo.Bar+tag@Example.COM ", + 'phone' => $faker->phoneNumber(), + 'website' => $faker->domainName(), + 'addressLine1' => $faker->streetAddress(), + 'addressLine2' => $faker->streetAddress(), + 'city' => $faker->city(), + 'stateProvince' => $faker->city(), + 'zipCode' => $faker->postcode(), + 'country' => $faker->countryISOAlpha3(), + 'createdDate' => $faker->date(), + 'createdUserId' => (string)$faker->numberBetween(1, 1000), + 'updatedDate' => $faker->date(), + 'updatedUserId' => (string)$faker->numberBetween(1, 1000), + ]; + + /** @var CompanyDeleteCommand $command */ + $command = $factory->delete($input); + + $this->assertInstanceOf(CompanyDeleteCommand::class, $command); + + $expected = $input['id']; + $actual = $command->id; + $this->assertSame($expected, $actual); + } + + public function testGet(): void + { + $sanitizer = $this->container->get(CompanySanitizer::class); + $factory = new CompanyCommandFactory($sanitizer); + $faker = FakerFactory::create(); + $input = [ + 'id' => $faker->numberBetween(1, 1000), + 'name' => $faker->company(), + 'email' => " Foo.Bar+tag@Example.COM ", + 'phone' => $faker->phoneNumber(), + 'website' => $faker->domainName(), + 'addressLine1' => $faker->streetAddress(), + 'addressLine2' => $faker->streetAddress(), + 'city' => $faker->city(), + 'stateProvince' => $faker->city(), + 'zipCode' => $faker->postcode(), + 'country' => $faker->countryISOAlpha3(), + 'createdDate' => $faker->date(), + 'createdUserId' => (string)$faker->numberBetween(1, 1000), + 'updatedDate' => $faker->date(), + 'updatedUserId' => (string)$faker->numberBetween(1, 1000), + ]; + + $command = $factory->get($input); + + $this->assertInstanceOf(CompanyGetCommand::class, $command); + $expected = $input['id']; + $actual = $command->id; + $this->assertSame($expected, $actual); + } + + public function testInsert(): void + { + $sanitizer = $this->container->get(CompanySanitizer::class); + /** @var Filter $filter */ + $filter = $this->container->get(Container::FILTER); + $factory = new CompanyCommandFactory($sanitizer); + $faker = FakerFactory::create(); + + $input = [ + 'id' => (string)$faker->numberBetween(1, 1000), + 'name' => $faker->company(), + 'email' => " Foo.Bar+tag@Example.COM ", + 'phone' => $faker->phoneNumber(), + 'website' => $faker->domainName(), + 'addressLine1' => $faker->streetAddress(), + 'addressLine2' => $faker->streetAddress(), + 'city' => $faker->city(), + 'stateProvince' => $faker->city(), + 'zipCode' => $faker->postcode(), + 'country' => $faker->countryISOAlpha3(), + 'createdDate' => $faker->date(), + 'createdUserId' => (string)$faker->numberBetween(1, 1000), + 'updatedDate' => $faker->date(), + 'updatedUserId' => (string)$faker->numberBetween(1, 1000), + ]; + + /** @var CompanyPostCommand $command */ + $command = $factory->insert($input); + + $this->assertInstanceOf(CompanyPostCommand::class, $command); + + $expected = null; + $actual = $command->id; + $this->assertSame($expected, $actual); + + $expected = $input['name']; + $actual = $command->name; + $this->assertSame($expected, $actual); + + $expected = $filter->email($input['email']); + $actual = $command->email; + $this->assertSame($expected, $actual); + + $expected = $input['phone']; + $actual = $command->phone; + $this->assertSame($expected, $actual); + + $expected = $input['website']; + $actual = $command->website; + $this->assertSame($expected, $actual); + + $expected = $input['addressLine1']; + $actual = $command->addressLine1; + $this->assertSame($expected, $actual); + + $expected = $input['addressLine2']; + $actual = $command->addressLine2; + $this->assertSame($expected, $actual); + + $expected = $input['city']; + $actual = $command->city; + $this->assertSame($expected, $actual); + + $expected = $input['stateProvince']; + $actual = $command->stateProvince; + $this->assertSame($expected, $actual); + + $expected = $input['zipCode']; + $actual = $command->zipCode; + $this->assertSame($expected, $actual); + + $expected = $input['country']; + $actual = $command->country; + $this->assertSame($expected, $actual); + + $expected = $input['createdDate']; + $actual = $command->createdDate; + $this->assertSame($expected, $actual); + + $expected = $filter->absint($input['createdUserId']); + $actual = $command->createdUserId; + $this->assertSame($expected, $actual); + + $expected = $input['updatedDate']; + $actual = $command->updatedDate; + $this->assertSame($expected, $actual); + + $expected = $filter->absint($input['updatedUserId']); + $actual = $command->updatedUserId; + $this->assertSame($expected, $actual); + } + + public function testUpdate(): void + { + $sanitizer = $this->container->get(CompanySanitizer::class); + /** @var Filter $filter */ + $filter = $this->container->get(Container::FILTER); + $factory = new CompanyCommandFactory($sanitizer); + $faker = FakerFactory::create(); + + $input = [ + 'id' => (string)$faker->numberBetween(1, 1000), + 'name' => $faker->company(), + 'email' => " Foo.Bar+tag@Example.COM ", + 'phone' => $faker->phoneNumber(), + 'website' => $faker->domainName(), + 'addressLine1' => $faker->streetAddress(), + 'addressLine2' => $faker->streetAddress(), + 'city' => $faker->city(), + 'stateProvince' => $faker->city(), + 'zipCode' => $faker->postcode(), + 'country' => $faker->countryISOAlpha3(), + 'createdDate' => $faker->date(), + 'createdUserId' => (string)$faker->numberBetween(1, 1000), + 'updatedDate' => $faker->date(), + 'updatedUserId' => (string)$faker->numberBetween(1, 1000), + ]; + + /** @var CompanyPutCommand $command */ + $command = $factory->update($input); + + $this->assertInstanceOf(CompanyPutCommand::class, $command); + + $expected = $filter->absint($input['id']); + $actual = $command->id; + $this->assertSame($expected, $actual); + + $expected = $input['name']; + $actual = $command->name; + $this->assertSame($expected, $actual); + + $expected = $filter->email($input['email']); + $actual = $command->email; + $this->assertSame($expected, $actual); + + $expected = $input['phone']; + $actual = $command->phone; + $this->assertSame($expected, $actual); + + $expected = $input['website']; + $actual = $command->website; + $this->assertSame($expected, $actual); + + $expected = $input['addressLine1']; + $actual = $command->addressLine1; + $this->assertSame($expected, $actual); + + $expected = $input['addressLine2']; + $actual = $command->addressLine2; + $this->assertSame($expected, $actual); + + $expected = $input['city']; + $actual = $command->city; + $this->assertSame($expected, $actual); + + $expected = $input['stateProvince']; + $actual = $command->stateProvince; + $this->assertSame($expected, $actual); + + $expected = $input['zipCode']; + $actual = $command->zipCode; + $this->assertSame($expected, $actual); + + $expected = $input['country']; + $actual = $command->country; + $this->assertSame($expected, $actual); + + $expected = $input['createdDate']; + $actual = $command->createdDate; + $this->assertSame($expected, $actual); + + $expected = $filter->absint($input['createdUserId']); + $actual = $command->createdUserId; + $this->assertSame($expected, $actual); + + $expected = $input['updatedDate']; + $actual = $command->updatedDate; + $this->assertSame($expected, $actual); + + $expected = $filter->absint($input['updatedUserId']); + $actual = $command->updatedUserId; + $this->assertSame($expected, $actual); + } +} diff --git a/tests/Unit/Domain/Application/Company/Handler/CompanyHandlerDeleteTest.php b/tests/Unit/Domain/Application/Company/Handler/CompanyHandlerDeleteTest.php new file mode 100644 index 0000000..1c650f4 --- /dev/null +++ b/tests/Unit/Domain/Application/Company/Handler/CompanyHandlerDeleteTest.php @@ -0,0 +1,100 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Phalcon\Api\Tests\Unit\Domain\Application\Company\Handler; + +use PayloadInterop\DomainStatus; +use Phalcon\Api\Domain\Application\Company\Command\CompanyCommandFactory; +use Phalcon\Api\Domain\Application\Company\Handler\CompanyDeleteHandler; +use Phalcon\Api\Tests\AbstractUnitTestCase; +use Phalcon\Api\Tests\Fixtures\Domain\Migrations\CompaniesMigration; + +final class CompanyHandlerDeleteTest extends AbstractUnitTestCase +{ + public function testHandlerWithCompanyId(): void + { + /** @var CompanyDeleteHandler $handler */ + $handler = $this->container->get(CompanyDeleteHandler::class); + /** @var CompanyCommandFactory $factory */ + $factory = $this->container->get(CompanyCommandFactory::class); + + /** + * We need to ask for a company to be deleted with an ID that does not + * exist in the database. To ensure that, we will create a company, + * delete it and then try to delete the same company with that ID + */ + $migration = new CompaniesMigration($this->getConnection()); + $dbCompany = $this->getNewCompany($migration); + $companyId = $dbCompany['com_id']; + + $command = $factory->delete(['id' => $companyId]); + $payload = $handler->__invoke($command); + + $expected = DomainStatus::DELETED; + $actual = $payload->getStatus(); + $this->assertSame($expected, $actual); + + $actual = $payload->getResult(); + $this->assertArrayHasKey('data', $actual); + + $data = $actual['data']; + + $expected = [ + 'Record deleted successfully [#' . $companyId . '].', + ]; + $actual = $data; + $this->assertSame($expected, $actual); + + /** + * Now deleting it again, will result in a 404 + */ + $payload = $handler->__invoke($command); + + $expected = DomainStatus::NOT_FOUND; + $actual = $payload->getStatus(); + $this->assertSame($expected, $actual); + + $actual = $payload->getResult(); + $this->assertArrayHasKey('errors', $actual); + + $errors = $actual['errors']; + + $expected = [['Record(s) not found']]; + $actual = $errors; + $this->assertSame($expected, $actual); + } + + public function testHandlerZeroCompanyId(): void + { + /** @var CompanyDeleteHandler $handler */ + $handler = $this->container->get(CompanyDeleteHandler::class); + /** @var CompanyCommandFactory $factory */ + $factory = $this->container->get(CompanyCommandFactory::class); + + $command = $factory->delete(['id' => 0]); + $payload = $handler->__invoke($command); + + $expected = DomainStatus::NOT_FOUND; + $actual = $payload->getStatus(); + $this->assertSame($expected, $actual); + + $actual = $payload->getResult(); + $this->assertArrayHasKey('errors', $actual); + + $errors = $actual['errors']; + + $expected = [['Record(s) not found']]; + $actual = $errors; + $this->assertSame($expected, $actual); + } +} diff --git a/tests/Unit/Domain/Application/Company/Handler/CompanyHandlerGetManyTest.php b/tests/Unit/Domain/Application/Company/Handler/CompanyHandlerGetManyTest.php new file mode 100644 index 0000000..f4b42f2 --- /dev/null +++ b/tests/Unit/Domain/Application/Company/Handler/CompanyHandlerGetManyTest.php @@ -0,0 +1,149 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Phalcon\Api\Tests\Unit\Domain\Application\Company\Handler; + +use PayloadInterop\DomainStatus; +use Phalcon\Api\Domain\Application\Company\Command\CompanyCommandFactory; +use Phalcon\Api\Domain\Application\Company\Handler\CompanyGetManyHandler; +use Phalcon\Api\Tests\AbstractUnitTestCase; +use Phalcon\Api\Tests\Fixtures\Domain\Migrations\CompaniesMigration; + +use function count; +use function uniqid; + +final class CompanyHandlerGetManyTest extends AbstractUnitTestCase +{ + public function testHandlerSuccess(): void + { + /** @var CompanyGetManyHandler $handler */ + $handler = $this->container->get(CompanyGetManyHandler::class); + /** @var CompanyCommandFactory $factory */ + $factory = $this->container->get(CompanyCommandFactory::class); + + $companyMigration = new CompaniesMigration($this->getConnection()); + $companyName1 = uniqid('companyName'); + $companyName2 = uniqid('companyName'); + + $company1 = $this->getNewCompany($companyMigration, ['com_name' => $companyName1]); + $company2 = $this->getNewCompany($companyMigration, ['com_name' => $companyName1]); + $company3 = $this->getNewCompany($companyMigration, ['com_name' => $companyName2]); + $company4 = $this->getNewCompany($companyMigration, ['com_name' => $companyName2]); + $company5 = $this->getNewCompany($companyMigration, ['com_name' => $companyName2]); + + /** + * Make the call + */ + $command = $factory->getMany([]); + $payload = $handler->__invoke($command); + + $expected = DomainStatus::SUCCESS; + $actual = $payload->getStatus(); + $this->assertSame($expected, $actual); + + $actual = $payload->getResult(); + $this->assertArrayHasKey('data', $actual); + + $data = $actual['data']; + + $expected = 5; + $actual = count($data); + $this->assertSame($expected, $actual); + + $this->assertArrayHasKey($company1['com_id'], $data); + $this->assertArrayHasKey($company2['com_id'], $data); + $this->assertArrayHasKey($company3['com_id'], $data); + $this->assertArrayHasKey($company4['com_id'], $data); + $this->assertArrayHasKey($company5['com_id'], $data); + + $expected = $company1['com_name']; + $actual = $data[$company1['com_id']]['name']; + $this->assertSame($expected, $actual); + + $expected = $company2['com_name']; + $actual = $data[$company2['com_id']]['name']; + $this->assertSame($expected, $actual); + + $expected = $company3['com_name']; + $actual = $data[$company3['com_id']]['name']; + $this->assertSame($expected, $actual); + + $expected = $company4['com_name']; + $actual = $data[$company4['com_id']]['name']; + $this->assertSame($expected, $actual); + + $expected = $company5['com_name']; + $actual = $data[$company5['com_id']]['name']; + $this->assertSame($expected, $actual); + + /** + * Make the call for paginated + */ + $command = $factory->getMany(['page' => 2, 'perPage' => 2]); + $payload = $handler->__invoke($command); + + $expected = DomainStatus::SUCCESS; + $actual = $payload->getStatus(); + $this->assertSame($expected, $actual); + + $actual = $payload->getResult(); + $this->assertArrayHasKey('data', $actual); + + $data = $actual['data']; + + $expected = 2; + $actual = count($data); + $this->assertSame($expected, $actual); + + $this->assertArrayHasKey($company3['com_id'], $data); + $this->assertArrayHasKey($company4['com_id'], $data); + + $expected = $company3['com_name']; + $actual = $data[$company3['com_id']]['name']; + $this->assertSame($expected, $actual); + + $expected = $company4['com_name']; + $actual = $data[$company4['com_id']]['name']; + $this->assertSame($expected, $actual); + + /** + * Make the call for name + */ + $command = $factory->getMany(['name' => $companyName1]); + $payload = $handler->__invoke($command); + + $expected = DomainStatus::SUCCESS; + $actual = $payload->getStatus(); + $this->assertSame($expected, $actual); + + $actual = $payload->getResult(); + $this->assertArrayHasKey('data', $actual); + + $data = $actual['data']; + + $expected = 2; + $actual = count($data); + $this->assertSame($expected, $actual); + + $this->assertArrayHasKey($company1['com_id'], $data); + $this->assertArrayHasKey($company2['com_id'], $data); + + $expected = $company1['com_name']; + $actual = $data[$company1['com_id']]['name']; + $this->assertSame($expected, $actual); + + $expected = $company2['com_name']; + $actual = $data[$company2['com_id']]['name']; + $this->assertSame($expected, $actual); + } +} diff --git a/tests/Unit/Domain/Application/Company/Handler/CompanyHandlerGetTest.php b/tests/Unit/Domain/Application/Company/Handler/CompanyHandlerGetTest.php new file mode 100644 index 0000000..e2c75e7 --- /dev/null +++ b/tests/Unit/Domain/Application/Company/Handler/CompanyHandlerGetTest.php @@ -0,0 +1,160 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Phalcon\Api\Tests\Unit\Domain\Application\Company\Handler; + +use PayloadInterop\DomainStatus; +use Phalcon\Api\Domain\Application\Company\Command\CompanyCommandFactory; +use Phalcon\Api\Domain\Application\Company\Handler\CompanyGetHandler; +use Phalcon\Api\Tests\AbstractUnitTestCase; +use Phalcon\Api\Tests\Fixtures\Domain\Migrations\CompaniesMigration; + +use function array_key_first; + +final class CompanyHandlerGetTest extends AbstractUnitTestCase +{ + public function testHandlerEmptyCompanyId(): void + { + /** @var CompanyGetHandler $handler */ + $handler = $this->container->get(CompanyGetHandler::class); + /** @var CompanyCommandFactory $factory */ + $factory = $this->container->get(CompanyCommandFactory::class); + + $command = $factory->get([]); + $payload = $handler->__invoke($command); + + $expected = DomainStatus::NOT_FOUND; + $actual = $payload->getStatus(); + $this->assertSame($expected, $actual); + + $actual = $payload->getResult(); + $this->assertArrayHasKey('errors', $actual); + + $errors = $actual['errors']; + + $expected = [['Record(s) not found']]; + $actual = $errors; + $this->assertSame($expected, $actual); + } + + public function testHandlerWithCompanyId(): void + { + /** @var CompanyGetHandler $handler */ + $handler = $this->container->get(CompanyGetHandler::class); + /** @var CompanyCommandFactory $factory */ + $factory = $this->container->get(CompanyCommandFactory::class); + + $companyMigration = new CompaniesMigration($this->getConnection()); + + $dbCompany = $this->getNewCompany($companyMigration); + $companyId = $dbCompany['com_id']; + + $command = $factory->get(['id' => $companyId]); + $payload = $handler->__invoke($command); + + $expected = DomainStatus::SUCCESS; + $actual = $payload->getStatus(); + $this->assertSame($expected, $actual); + + $actual = $payload->getResult(); + $this->assertArrayHasKey('data', $actual); + + $company = $actual['data']; + $key = array_key_first($company); + $company = $company[$key]; + + $expected = $dbCompany['com_id']; + $actual = $company['id']; + $this->assertSame($expected, $actual); + + $expected = $dbCompany['com_name']; + $actual = $company['name']; + $this->assertSame($expected, $actual); + + $expected = $dbCompany['com_email']; + $actual = $company['email']; + $this->assertSame($expected, $actual); + + $expected = $dbCompany['com_phone']; + $actual = $company['phone']; + $this->assertSame($expected, $actual); + + $expected = $dbCompany['com_website']; + $actual = $company['website']; + $this->assertSame($expected, $actual); + + $expected = $dbCompany['com_address_line_1']; + $actual = $company['addressLine1']; + $this->assertSame($expected, $actual); + + $expected = $dbCompany['com_address_line_2']; + $actual = $company['addressLine2']; + $this->assertSame($expected, $actual); + + $expected = $dbCompany['com_city']; + $actual = $company['city']; + $this->assertSame($expected, $actual); + + $expected = $dbCompany['com_state_province']; + $actual = $company['stateProvince']; + $this->assertSame($expected, $actual); + + $expected = $dbCompany['com_zip_code']; + $actual = $company['zipCode']; + $this->assertSame($expected, $actual); + + $expected = $dbCompany['com_country']; + $actual = $company['country']; + $this->assertSame($expected, $actual); + + $expected = $dbCompany['com_created_date']; + $actual = $company['createdDate']; + $this->assertSame($expected, $actual); + + $expected = $dbCompany['com_created_usr_id']; + $actual = $company['createdUserId']; + $this->assertSame($expected, $actual); + + $expected = $dbCompany['com_updated_date']; + $actual = $company['updatedDate']; + $this->assertSame($expected, $actual); + + $expected = $dbCompany['com_updated_usr_id']; + $actual = $company['updatedUserId']; + $this->assertSame($expected, $actual); + } + + public function testHandlerWrongCompanyId(): void + { + /** @var CompanyGetHandler $handler */ + $handler = $this->container->get(CompanyGetHandler::class); + /** @var CompanyCommandFactory $factory */ + $factory = $this->container->get(CompanyCommandFactory::class); + + $command = $factory->get(['id' => 999999]); + $payload = $handler->__invoke($command); + + $expected = DomainStatus::NOT_FOUND; + $actual = $payload->getStatus(); + $this->assertSame($expected, $actual); + + $actual = $payload->getResult(); + $this->assertArrayHasKey('errors', $actual); + + $errors = $actual['errors']; + + $expected = [['Record(s) not found']]; + $actual = $errors; + $this->assertSame($expected, $actual); + } +} diff --git a/tests/Unit/Domain/Application/Company/Handler/CompanyHandlerPostTest.php b/tests/Unit/Domain/Application/Company/Handler/CompanyHandlerPostTest.php new file mode 100644 index 0000000..a73fbb3 --- /dev/null +++ b/tests/Unit/Domain/Application/Company/Handler/CompanyHandlerPostTest.php @@ -0,0 +1,421 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Phalcon\Api\Tests\Unit\Domain\Application\Company\Handler; + +use DateTimeImmutable; +use PayloadInterop\DomainStatus; +use PDOException; +use Phalcon\Api\Domain\Application\Company\Command\CompanyCommandFactory; +use Phalcon\Api\Domain\Application\Company\Handler\CompanyPostHandler; +use Phalcon\Api\Domain\Infrastructure\DataSource\Company\Mapper\CompanyMapper; +use Phalcon\Api\Domain\Infrastructure\DataSource\Company\Mapper\CompanyMapperInterface; +use Phalcon\Api\Domain\Infrastructure\DataSource\Company\Repository\CompanyRepository; +use Phalcon\Api\Domain\Infrastructure\DataSource\User\Mapper\UserMapper; +use Phalcon\Api\Tests\AbstractUnitTestCase; +use Phalcon\Support\Registry; + +use function array_key_first; +use function uniqid; + +final class CompanyHandlerPostTest extends AbstractUnitTestCase +{ + public function testHandlerFailureNoIdReturned(): void + { + $companyRepository = $this + ->getMockBuilder(CompanyRepository::class) + ->disableOriginalConstructor() + ->onlyMethods( + [ + 'insert', + ] + ) + ->getMock() + ; + $companyRepository->method('insert')->willReturn(0); + + $this->container->setShared(CompanyRepository::class, $companyRepository); + + /** @var CompanyPostHandler $handler */ + $handler = $this->container->get(CompanyPostHandler::class); + /** @var CompanyMapper $companyMapper */ + $companyMapper = $this->container->get(CompanyMapper::class); + /** @var UserMapper $userMapper */ + $userMapper = $this->container->get(UserMapper::class); + /** @var Registry $registry */ + $registry = $this->container->get(Registry::class); + /** @var CompanyCommandFactory $factory */ + $factory = $this->container->get(CompanyCommandFactory::class); + + $userData = $this->getNewUserData(); + $userData['usr_id'] = 1; + $companyData = $this->getNewCompanyData(); + $companyData['com_id'] = 1; + + /** + * $companyData is a db record. We need a domain object here + */ + $domainCompany = $companyMapper->domain($companyData); + $domainUser = $userMapper->domain($userData); + /** + * Store the domain user in the registry - to be used for *_com_id + * field updates + */ + $registry->set('user', $domainUser); + + $domainData = $domainCompany->toArray(); + $command = $factory->insert($domainData); + $payload = $handler->__invoke($command); + + $expected = DomainStatus::ERROR; + $actual = $payload->getStatus(); + $this->assertSame($expected, $actual); + + $actual = $payload->getResult(); + $this->assertArrayHasKey('errors', $actual); + + $errors = $actual['errors']; + + $expected = [['Cannot create database record: No id returned']]; + $actual = $errors; + $this->assertSame($expected, $actual); + } + + public function testHandlerFailurePdoError(): void + { + $message = uniqid('pdo-error-'); + $companyRepository = $this + ->getMockBuilder(CompanyRepository::class) + ->disableOriginalConstructor() + ->onlyMethods( + [ + 'insert', + ] + ) + ->getMock() + ; + $companyRepository + ->method('insert') + ->willThrowException(new PDOException($message)) + ; + + $this->container->setShared(CompanyRepository::class, $companyRepository); + + /** @var CompanyPostHandler $handler */ + $handler = $this->container->get(CompanyPostHandler::class); + /** @var CompanyMapper $companyMapper */ + $companyMapper = $this->container->get(CompanyMapper::class); + /** @var UserMapper $userMapper */ + $userMapper = $this->container->get(UserMapper::class); + /** @var Registry $registry */ + $registry = $this->container->get(Registry::class); + /** @var CompanyCommandFactory $factory */ + $factory = $this->container->get(CompanyCommandFactory::class); + + $userData = $this->getNewUserData(); + $userData['usr_id'] = 1; + $companyData = $this->getNewCompanyData(); + $companyData['com_id'] = 1; + + /** + * $companyData is a db record. We need a domain object here + */ + $domainCompany = $companyMapper->domain($companyData); + $domainUser = $userMapper->domain($userData); + /** + * Store the domain user in the registry - to be used for *_com_id + * field updates + */ + $registry->set('user', $domainUser); + + $domainData = $domainCompany->toArray(); + $command = $factory->insert($domainData); + $payload = $handler->__invoke($command); + + $expected = DomainStatus::ERROR; + $actual = $payload->getStatus(); + $this->assertSame($expected, $actual); + + $actual = $payload->getResult(); + $this->assertArrayHasKey('errors', $actual); + + $errors = $actual['errors']; + + $expected = [['Cannot create database record: ' . $message]]; + $actual = $errors; + $this->assertSame($expected, $actual); + } + + public function testHandlerFailureValidation(): void + { + /** @var CompanyPostHandler $handler */ + $handler = $this->container->get(CompanyPostHandler::class); + /** @var CompanyMapper $companyMapper */ + $companyMapper = $this->container->get(CompanyMapper::class); + /** @var CompanyCommandFactory $factory */ + $factory = $this->container->get(CompanyCommandFactory::class); + + $companyData = $this->getNewCompanyData(); + + unset( + $companyData['com_name'], + $companyData['com_email'], + ); + + /** + * $companyData is a db record. We need a domain object here + */ + $domainCompany = $companyMapper->domain($companyData); + $domainData = $domainCompany->toArray(); + $command = $factory->insert($domainData); + $payload = $handler->__invoke($command); + + $expected = DomainStatus::INVALID; + $actual = $payload->getStatus(); + $this->assertSame($expected, $actual); + + $actual = $payload->getResult(); + $this->assertArrayHasKey('errors', $actual); + + $errors = $actual['errors']; + + $expected = [ + ['Field name is required'], + ['Field email is required'], + ['Field email must be an email address'], + ]; + $actual = $errors; + $this->assertSame($expected, $actual); + } + + public function testHandlerSuccess(): void + { + /** @var CompanyPostHandler $handler */ + $handler = $this->container->get(CompanyPostHandler::class); + /** @var CompanyMapperInterface $companyMapper */ + $companyMapper = $this->container->get(CompanyMapper::class); + /** @var UserMapper $userMapper */ + $userMapper = $this->container->get(UserMapper::class); + /** @var Registry $registry */ + $registry = $this->container->get(Registry::class); + /** @var CompanyCommandFactory $factory */ + $factory = $this->container->get(CompanyCommandFactory::class); + + $userData = $this->getNewUserData(); + $userData['usr_id'] = 1; + + $companyData = $this->getNewCompanyData(); + + /** + * $companyData is a db record. We need a domain object here + */ + $domainCompany = $companyMapper->domain($companyData); + $domainUser = $userMapper->domain($userData); + + /** + * Store the domain user in the registry - to be used for *_com_id + * field updates + */ + $registry->set('user', $domainUser); + + $domainData = $domainCompany->toArray(); + $command = $factory->insert($domainData); + $payload = $handler->__invoke($command); + + $expected = DomainStatus::CREATED; + $actual = $payload->getStatus(); + $this->assertSame($expected, $actual); + + $actual = $payload->getResult(); + $this->assertArrayHasKey('data', $actual); + + $data = $actual['data']; + + $companyId = array_key_first($data); + + $this->assertGreaterThan(0, $companyId); + + $data = $data[$companyId]; + + $expected = $domainData['name']; + $actual = $data['name']; + $this->assertSame($expected, $actual); + + $expected = $domainData['email']; + $actual = $data['email']; + $this->assertSame($expected, $actual); + + $expected = $domainData['phone']; + $actual = $data['phone']; + $this->assertSame($expected, $actual); + + $expected = $domainData['website']; + $actual = $data['website']; + $this->assertSame($expected, $actual); + + $expected = $domainData['addressLine1']; + $actual = $data['addressLine1']; + $this->assertSame($expected, $actual); + + $expected = $domainData['addressLine2']; + $actual = $data['addressLine2']; + $this->assertSame($expected, $actual); + + $expected = $domainData['city']; + $actual = $data['city']; + $this->assertSame($expected, $actual); + + $expected = $domainData['stateProvince']; + $actual = $data['stateProvince']; + $this->assertSame($expected, $actual); + + $expected = $domainData['zipCode']; + $actual = $data['zipCode']; + $this->assertSame($expected, $actual); + + $expected = $domainData['country']; + $actual = $data['country']; + $this->assertSame($expected, $actual); + + $expected = $domainData['createdDate']; + $actual = $data['createdDate']; + $this->assertSame($expected, $actual); + + $expected = $domainUser->id; + $actual = $data['createdUserId']; + $this->assertSame($expected, $actual); + + $expected = $domainData['updatedDate']; + $actual = $data['updatedDate']; + $this->assertSame($expected, $actual); + + $expected = $domainUser->id; + $actual = $data['updatedUserId']; + $this->assertSame($expected, $actual); + } + + public function testHandlerSuccessEmptyDates(): void + { + $now = new DateTimeImmutable(); + $today = $now->format('Y-m-d'); + /** @var CompanyPostHandler $handler */ + $handler = $this->container->get(CompanyPostHandler::class); + /** @var CompanyMapperInterface $companyMapper */ + $companyMapper = $this->container->get(CompanyMapper::class); + /** @var UserMapper $userMapper */ + $userMapper = $this->container->get(UserMapper::class); + /** @var Registry $registry */ + $registry = $this->container->get(Registry::class); + /** @var CompanyCommandFactory $factory */ + $factory = $this->container->get(CompanyCommandFactory::class); + + $userData = $this->getNewUserData(); + $userData['usr_id'] = 1; + + $companyData = $this->getNewCompanyData(); + + /** + * $companyData is a db record. We need a domain object here + */ + $domainCompany = $companyMapper->domain($companyData); + $domainUser = $userMapper->domain($userData); + + /** + * Store the domain user in the registry - to be used for *_com_id + * field updates + */ + $registry->set('user', $domainUser); + + unset( + $companyData['com_created_date'], + $companyData['com_updated_date'], + ); + + /** + * $companyData is a db record. We need a domain object here + */ + $domainCompany = $companyMapper->domain($companyData); + $domainData = $domainCompany->toArray(); + $command = $factory->insert($domainData); + $payload = $handler->__invoke($command); + + $expected = DomainStatus::CREATED; + $actual = $payload->getStatus(); + $this->assertSame($expected, $actual); + + $actual = $payload->getResult(); + $this->assertArrayHasKey('data', $actual); + + $data = $actual['data']; + + $companyId = array_key_first($data); + + $this->assertGreaterThan(0, $companyId); + + $data = $data[$companyId]; + + $expected = $domainData['name']; + $actual = $data['name']; + $this->assertSame($expected, $actual); + + $expected = $domainData['email']; + $actual = $data['email']; + $this->assertSame($expected, $actual); + + $expected = $domainData['phone']; + $actual = $data['phone']; + $this->assertSame($expected, $actual); + + $expected = $domainData['website']; + $actual = $data['website']; + $this->assertSame($expected, $actual); + + $expected = $domainData['addressLine1']; + $actual = $data['addressLine1']; + $this->assertSame($expected, $actual); + + $expected = $domainData['addressLine2']; + $actual = $data['addressLine2']; + $this->assertSame($expected, $actual); + + $expected = $domainData['city']; + $actual = $data['city']; + $this->assertSame($expected, $actual); + + $expected = $domainData['stateProvince']; + $actual = $data['stateProvince']; + $this->assertSame($expected, $actual); + + $expected = $domainData['zipCode']; + $actual = $data['zipCode']; + $this->assertSame($expected, $actual); + + $expected = $domainData['country']; + $actual = $data['country']; + $this->assertSame($expected, $actual); + + $actual = $data['createdDate']; + $this->assertStringContainsString($today, $actual); + + $expected = 1; + $actual = $data['createdUserId']; + $this->assertSame($expected, $actual); + + $actual = $data['updatedDate']; + $this->assertStringContainsString($today, $actual); + + $expected = 1; + $actual = $data['updatedUserId']; + $this->assertSame($expected, $actual); + } +} diff --git a/tests/Unit/Domain/Application/Company/Handler/CompanyHandlerPutTest.php b/tests/Unit/Domain/Application/Company/Handler/CompanyHandlerPutTest.php new file mode 100644 index 0000000..7bf2334 --- /dev/null +++ b/tests/Unit/Domain/Application/Company/Handler/CompanyHandlerPutTest.php @@ -0,0 +1,493 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Phalcon\Api\Tests\Unit\Domain\Application\Company\Handler; + +use DateTimeImmutable; +use Faker\Factory; +use PayloadInterop\DomainStatus; +use PDOException; +use Phalcon\Api\Domain\Application\Company\Command\CompanyCommandFactory; +use Phalcon\Api\Domain\Application\Company\Handler\CompanyPutHandler; +use Phalcon\Api\Domain\Infrastructure\DataSource\Company\Mapper\CompanyMapper; +use Phalcon\Api\Domain\Infrastructure\DataSource\Company\Repository\CompanyRepository; +use Phalcon\Api\Domain\Infrastructure\DataSource\User\Mapper\UserMapper; +use Phalcon\Api\Tests\AbstractUnitTestCase; +use Phalcon\Api\Tests\Fixtures\Domain\Migrations\CompaniesMigration; +use Phalcon\Support\Registry; + +use function uniqid; + +final class CompanyHandlerPutTest extends AbstractUnitTestCase +{ + public function testServiceFailureNoIdReturned(): void + { + /** @var CompanyMapper $companyMapper */ + $companyMapper = $this->container->get(CompanyMapper::class); + $companyData = $this->getNewCompanyData(); + + $companyData['com_id'] = 1; + + $findByCompany = $companyMapper->domain($companyData); + + $userRepository = $this + ->getMockBuilder(CompanyRepository::class) + ->disableOriginalConstructor() + ->onlyMethods( + [ + 'update', + 'findById', + ] + ) + ->getMock() + ; + $userRepository->method('update')->willReturn(0); + $userRepository->method('findById')->willReturn($findByCompany); + + $this->container->setShared(CompanyRepository::class, $userRepository); + + /** + * Add a user in the repository for the session + */ + /** @var Registry $registry */ + $registry = $this->container->get(Registry::class); + /** @var UserMapper $userMapper */ + $userMapper = $this->container->get(UserMapper::class); + $sessionUser = $userMapper->domain(['id' => 1]); + $registry->set('user', $sessionUser); + + /** @var CompanyPutHandler $handler */ + $handler = $this->container->get(CompanyPutHandler::class); + /** @var CompanyCommandFactory $factory */ + $factory = $this->container->get(CompanyCommandFactory::class); + + /** + * Update company + */ + $companyData = $this->getNewCompanyData(); + $companyData['com_id'] = 1; + + $updateCompany = $companyMapper->domain($companyData); + $updateCompany = $updateCompany->toArray(); + + $command = $factory->update($updateCompany); + $payload = $handler->__invoke($command); + + $expected = DomainStatus::ERROR; + $actual = $payload->getStatus(); + $this->assertSame($expected, $actual); + + $actual = $payload->getResult(); + $this->assertArrayHasKey('errors', $actual); + + $errors = $actual['errors']; + + $expected = [['Cannot update database record: No id returned']]; + $actual = $errors; + $this->assertSame($expected, $actual); + } + + public function testServiceFailurePdoError(): void + { + $message = uniqid('pdo-error-'); + /** @var CompanyMapper $companyMapper */ + $companyMapper = $this->container->get(CompanyMapper::class); + $companyData = $this->getNewCompanyData(); + + $companyData['com_id'] = 1; + + $findByCompany = $companyMapper->domain($companyData); + + $userRepository = $this + ->getMockBuilder(CompanyRepository::class) + ->disableOriginalConstructor() + ->onlyMethods( + [ + 'update', + 'findById', + ] + ) + ->getMock() + ; + $userRepository->method('findById')->willReturn($findByCompany); + $userRepository + ->method('update') + ->willThrowException(new PDOException($message)) + ; + + $this->container->setShared(CompanyRepository::class, $userRepository); + + /** + * Add a user in the repository for the session + */ + /** @var Registry $registry */ + $registry = $this->container->get(Registry::class); + /** @var UserMapper $userMapper */ + $userMapper = $this->container->get(UserMapper::class); + $sessionUser = $userMapper->domain(['id' => 1]); + $registry->set('user', $sessionUser); + + /** @var CompanyPutHandler $handler */ + $handler = $this->container->get(CompanyPutHandler::class); + /** @var CompanyCommandFactory $factory */ + $factory = $this->container->get(CompanyCommandFactory::class); + + $companyData = $this->getNewCompanyData(); + $companyData['com_id'] = 1; + + /** + * $companyData is a db record. We need a domain object here + */ + $updateCompany = $companyMapper->domain($companyData); + $updateCompany = $updateCompany->toArray(); + + $command = $factory->update($updateCompany); + $payload = $handler->__invoke($command); + + $expected = DomainStatus::ERROR; + $actual = $payload->getStatus(); + $this->assertSame($expected, $actual); + + $actual = $payload->getResult(); + $this->assertArrayHasKey('errors', $actual); + + $errors = $actual['errors']; + + $expected = [['Cannot update database record: ' . $message]]; + $actual = $errors; + $this->assertSame($expected, $actual); + } + + public function testServiceFailureRecordNotFound(): void + { + /** @var CompanyMapper $companyMapper */ + $companyMapper = $this->container->get(CompanyMapper::class); + $userRepository = $this + ->getMockBuilder(CompanyRepository::class) + ->disableOriginalConstructor() + ->onlyMethods( + [ + 'findById', + ] + ) + ->getMock() + ; + $userRepository->method('findById')->willReturn(null); + + $this->container->setShared(CompanyRepository::class, $userRepository); + + /** @var CompanyPutHandler $handler */ + $handler = $this->container->get(CompanyPutHandler::class); + /** @var CompanyCommandFactory $factory */ + $factory = $this->container->get(CompanyCommandFactory::class); + + /** + * Update user + */ + $companyData = $this->getNewCompanyData(); + $companyData['com_id'] = 1; + + $updateCompany = $companyMapper->domain($companyData); + $updateCompany = $updateCompany->toArray(); + + $command = $factory->update($updateCompany); + $payload = $handler->__invoke($command); + + $expected = DomainStatus::NOT_FOUND; + $actual = $payload->getStatus(); + $this->assertSame($expected, $actual); + + $actual = $payload->getResult(); + $this->assertArrayHasKey('errors', $actual); + + $errors = $actual['errors']; + + $expected = [['Record(s) not found']]; + $actual = $errors; + $this->assertSame($expected, $actual); + } + + public function testServiceFailureValidation(): void + { + /** @var CompanyPutHandler $handler */ + $handler = $this->container->get(CompanyPutHandler::class); + /** @var CompanyCommandFactory $factory */ + $factory = $this->container->get(CompanyCommandFactory::class); + /** @var CompanyMapper $companyMapper */ + $companyMapper = $this->container->get(CompanyMapper::class); + $companyData = $this->getNewCompanyData(); + + $companyData['com_email'] = 'wrong-email'; + unset($companyData['com_id']); + + /** + * $companyData is a db record. We need a domain object here + */ + $updateCompany = $companyMapper->domain($companyData); + $updateCompany = $updateCompany->toArray(); + + $command = $factory->update($updateCompany); + $payload = $handler->__invoke($command); + + $expected = DomainStatus::INVALID; + $actual = $payload->getStatus(); + $this->assertSame($expected, $actual); + + $actual = $payload->getResult(); + $this->assertArrayHasKey('errors', $actual); + + $errors = $actual['errors']; + + $expected = [ + ['Field id is not a valid absolute integer and greater than 0'], + ['Field email must be an email address'], + ]; + $actual = $errors; + $this->assertSame($expected, $actual); + } + + public function testServiceSuccess(): void + { + $faker = Factory::create(); + /** @var CompanyPutHandler $handler */ + $handler = $this->container->get(CompanyPutHandler::class); + /** @var CompanyCommandFactory $factory */ + $factory = $this->container->get(CompanyCommandFactory::class); + /** @var UserMapper $userMapper */ + $userMapper = $this->container->get(UserMapper::class); + /** @var CompanyMapper $companyMapper */ + $companyMapper = $this->container->get(CompanyMapper::class); + /** @var Registry $registry */ + $registry = $this->container->get(Registry::class); + + $migration = new CompaniesMigration($this->getConnection()); + $dbCompany = $this->getNewCompany($migration); + $companyId = $dbCompany['com_id']; + $userData = $this->getNewUserData(); + $userId = $faker->numberBetween(1, 10); + $userData['usr_id'] = $userId; + + /** + * $userData is a db record. We need a domain object here + */ + $domainUser = $userMapper->domain($userData); + /** + * Store the domain user in the registry - to be used for *_com_id + * field updates + */ + $registry->set('user', $domainUser); + + $domainCompany = $companyMapper->domain($dbCompany); + $domainData = $domainCompany->toArray(); + $command = $factory->update($domainData); + $payload = $handler->__invoke($command); + + $expected = DomainStatus::UPDATED; + $actual = $payload->getStatus(); + $this->assertSame($expected, $actual); + + $actual = $payload->getResult(); + $this->assertArrayHasKey('data', $actual); + + $data = $actual['data']; + + $this->assertArrayHasKey($companyId, $data); + + $data = $data[$companyId]; + + $expected = $domainData['id']; + $actual = $data['id']; + $this->assertSame($expected, $actual); + + $expected = $domainData['name']; + $actual = $data['name']; + $this->assertSame($expected, $actual); + + $expected = $domainData['email']; + $actual = $data['email']; + $this->assertSame($expected, $actual); + + $expected = $domainData['phone']; + $actual = $data['phone']; + $this->assertSame($expected, $actual); + + $expected = $domainData['website']; + $actual = $data['website']; + $this->assertSame($expected, $actual); + + $expected = $domainData['addressLine1']; + $actual = $data['addressLine1']; + $this->assertSame($expected, $actual); + + $expected = $domainData['addressLine2']; + $actual = $data['addressLine2']; + $this->assertSame($expected, $actual); + + $expected = $domainData['city']; + $actual = $data['city']; + $this->assertSame($expected, $actual); + + $expected = $domainData['stateProvince']; + $actual = $data['stateProvince']; + $this->assertSame($expected, $actual); + + $expected = $domainData['zipCode']; + $actual = $data['zipCode']; + $this->assertSame($expected, $actual); + + $expected = $domainData['country']; + $actual = $data['country']; + $this->assertSame($expected, $actual); + + /** + * These have to be the same on an update + */ + $expected = $domainData['createdDate']; + $actual = $data['createdDate']; + $this->assertSame($expected, $actual); + + $expected = 0; + $actual = $data['createdUserId']; + $this->assertSame($expected, $actual); + + $expected = $domainData['updatedDate']; + $actual = $data['updatedDate']; + $this->assertSame($expected, $actual); + + $expected = $userId; + $actual = $data['updatedUserId']; + $this->assertSame($expected, $actual); + } + + public function testServiceSuccessEmptyDates(): void + { + $faker = Factory::create(); + $now = new DateTimeImmutable(); + $today = $now->format('Y-m-d'); + /** @var CompanyPutHandler $handler */ + $handler = $this->container->get(CompanyPutHandler::class); + /** @var CompanyCommandFactory $factory */ + $factory = $this->container->get(CompanyCommandFactory::class); + /** @var UserMapper $userMapper */ + $userMapper = $this->container->get(UserMapper::class); + /** @var CompanyMapper $companyMapper */ + $companyMapper = $this->container->get(CompanyMapper::class); + /** @var Registry $registry */ + $registry = $this->container->get(Registry::class); + + $migration = new CompaniesMigration($this->getConnection()); + $dbCompany = $this->getNewCompany($migration); + + $companyId = $dbCompany['com_id']; + $userData = $this->getNewUserData(); + $userId = $faker->numberBetween(1, 10); + $userData['usr_id'] = $userId; + + unset( + $dbCompany['com_updated_date'], + ); + + /** + * $userData is a db record. We need a domain object here + */ + $domainUser = $userMapper->domain($userData); + /** + * Store the domain user in the registry - to be used for *_usr_id + * field updates + */ + $registry->set('user', $domainUser); + + $domainCompany = $companyMapper->domain($dbCompany); + $domainData = $domainCompany->toArray(); + $command = $factory->update($domainData); + $payload = $handler->__invoke($command); + + $expected = DomainStatus::UPDATED; + $actual = $payload->getStatus(); + $this->assertSame($expected, $actual); + + $actual = $payload->getResult(); + $this->assertArrayHasKey('data', $actual); + + $data = $actual['data']; + + $this->assertArrayHasKey($companyId, $data); + + $data = $data[$companyId]; + + $expected = $domainData['id']; + $actual = $data['id']; + $this->assertSame($expected, $actual); + + $expected = $domainData['name']; + $actual = $data['name']; + $this->assertSame($expected, $actual); + + $expected = $domainData['email']; + $actual = $data['email']; + $this->assertSame($expected, $actual); + + $expected = $domainData['phone']; + $actual = $data['phone']; + $this->assertSame($expected, $actual); + + $expected = $domainData['website']; + $actual = $data['website']; + $this->assertSame($expected, $actual); + + $expected = $domainData['addressLine1']; + $actual = $data['addressLine1']; + $this->assertSame($expected, $actual); + + $expected = $domainData['addressLine2']; + $actual = $data['addressLine2']; + $this->assertSame($expected, $actual); + + $expected = $domainData['city']; + $actual = $data['city']; + $this->assertSame($expected, $actual); + + $expected = $domainData['stateProvince']; + $actual = $data['stateProvince']; + $this->assertSame($expected, $actual); + + $expected = $domainData['zipCode']; + $actual = $data['zipCode']; + $this->assertSame($expected, $actual); + + $expected = $domainData['country']; + $actual = $data['country']; + $this->assertSame($expected, $actual); + + /** + * These have to be the same on an update + */ + $expected = $dbCompany['com_created_date']; + $actual = $data['createdDate']; + $this->assertSame($expected, $actual); + + /** + * These have to be the same on an update + */ + $expected = 0; + $actual = $data['createdUserId']; + $this->assertSame($expected, $actual); + + $actual = $data['updatedDate']; + $this->assertStringContainsString($today, $actual); + + $expected = $userId; + $actual = $data['updatedUserId']; + $this->assertSame($expected, $actual); + } +} diff --git a/tests/Unit/Domain/Application/Company/Service/CompanyServiceDeleteTest.php b/tests/Unit/Domain/Application/Company/Service/CompanyServiceDeleteTest.php new file mode 100644 index 0000000..892fb9d --- /dev/null +++ b/tests/Unit/Domain/Application/Company/Service/CompanyServiceDeleteTest.php @@ -0,0 +1,105 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Phalcon\Api\Tests\Unit\Domain\Application\Company\Service; + +use PayloadInterop\DomainStatus; +use Phalcon\Api\Domain\Application\Company\Service\CompanyDeleteService; +use Phalcon\Api\Tests\AbstractUnitTestCase; +use Phalcon\Api\Tests\Fixtures\Domain\Migrations\CompaniesMigration; + +final class CompanyServiceDeleteTest extends AbstractUnitTestCase +{ + public function testServiceWithCompanyId(): void + { + /** @var CompanyDeleteService $service */ + $service = $this->container->get(CompanyDeleteService::class); + + /** + * We need to ask for a company to be deleted with an ID that does not + * exist in the database. To ensure that, we will create a company, + * delete it and then try to delete the same company with that ID + */ + $migration = new CompaniesMigration($this->getConnection()); + $dbCompany = $this->getNewCompany($migration); + $companyId = $dbCompany['com_id']; + + $payload = $service->__invoke( + [ + 'id' => $companyId, + ] + ); + + $expected = DomainStatus::DELETED; + $actual = $payload->getStatus(); + $this->assertSame($expected, $actual); + + $actual = $payload->getResult(); + $this->assertArrayHasKey('data', $actual); + + $data = $actual['data']; + + $expected = [ + 'Record deleted successfully [#' . $companyId . '].', + ]; + $actual = $data; + $this->assertSame($expected, $actual); + + /** + * Now deleting it again, will result in a 404 + */ + $payload = $service->__invoke( + [ + 'id' => $companyId, + ] + ); + + $expected = DomainStatus::NOT_FOUND; + $actual = $payload->getStatus(); + $this->assertSame($expected, $actual); + + $actual = $payload->getResult(); + $this->assertArrayHasKey('errors', $actual); + + $errors = $actual['errors']; + + $expected = [['Record(s) not found']]; + $actual = $errors; + $this->assertSame($expected, $actual); + } + + public function testServiceZeroCompanyId(): void + { + /** @var CompanyDeleteService $service */ + $service = $this->container->get(CompanyDeleteService::class); + + $payload = $service->__invoke( + [ + 'id' => 0, + ] + ); + + $expected = DomainStatus::NOT_FOUND; + $actual = $payload->getStatus(); + $this->assertSame($expected, $actual); + + $actual = $payload->getResult(); + $this->assertArrayHasKey('errors', $actual); + + $errors = $actual['errors']; + + $expected = [['Record(s) not found']]; + $actual = $errors; + $this->assertSame($expected, $actual); + } +} diff --git a/tests/Unit/Domain/Application/Company/Service/CompanyServiceDispatchTest.php b/tests/Unit/Domain/Application/Company/Service/CompanyServiceDispatchTest.php new file mode 100644 index 0000000..30e0d8a --- /dev/null +++ b/tests/Unit/Domain/Application/Company/Service/CompanyServiceDispatchTest.php @@ -0,0 +1,117 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Phalcon\Api\Tests\Unit\Domain\Application\Company\Service; + +use DateTimeImmutable; +use Phalcon\Api\Domain\Infrastructure\Constants\Cache as CacheConstants; +use Phalcon\Api\Domain\Infrastructure\Constants\Dates; +use Phalcon\Api\Domain\Infrastructure\DataSource\Company\Mapper\CompanyMapper; +use Phalcon\Api\Domain\Infrastructure\DataSource\User\Mapper\UserMapper; +use Phalcon\Api\Domain\Infrastructure\Enums\Http\RoutesEnum; +use Phalcon\Api\Domain\Infrastructure\Env\EnvManager; +use Phalcon\Api\Tests\AbstractUnitTestCase; +use Phalcon\Api\Tests\Fixtures\Domain\Migrations\CompaniesMigration; +use Phalcon\Api\Tests\Fixtures\Domain\Migrations\UsersMigration; +use Phalcon\Cache\Cache; +use PHPUnit\Framework\Attributes\BackupGlobals; +use PHPUnit\Framework\Attributes\RunInSeparateProcess; + +use function json_decode; +use function ob_get_clean; +use function ob_start; +use function restore_error_handler; +use function time; + +#[BackupGlobals(true)] +final class CompanyServiceDispatchTest extends AbstractUnitTestCase +{ + #[RunInSeparateProcess] + public function testDispatchGet(): void + { + /** @var EnvManager $env */ + $env = $this->container->getShared(EnvManager::class); + /** @var Cache $cache */ + $cache = $this->container->getShared(Cache::class); + /** @var UserMapper $userMapper */ + $userMapper = $this->container->get(UserMapper::class); + /** @var CompanyMapper $companyMapper */ + $companyMapper = $this->container->get(CompanyMapper::class); + + $companyMigration = new CompaniesMigration($this->getConnection()); + $userMigration = new UsersMigration($this->getConnection()); + + $dbUser = $this->getNewUser($userMigration); + $dbCompany = $this->getNewCompany($companyMigration); + $companyId = $dbCompany['com_id']; + $token = $this->getUserToken($dbUser); + $domainUser = $userMapper->domain($dbUser); + $domainCompany = $companyMapper->domain($dbCompany); + + /** + * Store the token in the cache + */ + $cacheKey = CacheConstants::getCacheTokenKey($domainUser, $token); + /** @var int $expiration */ + $expiration = $env->get( + 'TOKEN_EXPIRATION', + CacheConstants::CACHE_TOKEN_EXPIRY, + 'int' + ); + $expirationDate = (new DateTimeImmutable()) + ->modify('+' . $expiration . ' seconds') + ->format(Dates::DATE_TIME_FORMAT) + ; + + $payload = [ + 'token' => $token, + 'expiry' => $expirationDate, + ]; + + $cache->set($cacheKey, $payload, $expiration); + + $time = $_SERVER['REQUEST_TIME_FLOAT'] ?? time(); + $_SERVER = [ + 'REQUEST_METHOD' => 'GET', + 'REQUEST_TIME_FLOAT' => $time, + 'HTTP_AUTHORIZATION' => 'Bearer ' . $token, + 'REQUEST_URI' => RoutesEnum::companyGet->endpoint(), + ]; + + $_GET = [ + 'id' => $companyId, + ]; + + ob_start(); + require_once $env->appPath('public/index.php'); + $response = ob_get_clean(); + + $contents = json_decode($response, true); + + restore_error_handler(); + + $this->assertArrayHasKey('data', $contents); + $this->assertArrayHasKey('errors', $contents); + + $data = $contents['data']; + $errors = $contents['errors']; + + $expected = []; + $actual = $errors; + $this->assertSame($expected, $actual); + + $expected = [$companyId => $domainCompany->toArray()]; + $actual = $data; + $this->assertSame($expected, $actual); + } +} diff --git a/tests/Unit/Domain/Application/Company/Service/CompanyServiceGetManyTest.php b/tests/Unit/Domain/Application/Company/Service/CompanyServiceGetManyTest.php new file mode 100644 index 0000000..c447e3a --- /dev/null +++ b/tests/Unit/Domain/Application/Company/Service/CompanyServiceGetManyTest.php @@ -0,0 +1,79 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Phalcon\Api\Tests\Unit\Domain\Application\Company\Service; + +use PayloadInterop\DomainStatus; +use Phalcon\Api\Domain\Application\Company\Service\CompanyGetManyService; +use Phalcon\Api\Tests\AbstractUnitTestCase; +use Phalcon\Api\Tests\Fixtures\Domain\Migrations\CompaniesMigration; + +use function count; + +final class CompanyServiceGetManyTest extends AbstractUnitTestCase +{ + public function testServiceWithCompanyId(): void + { + /** @var CompanyGetManyService $service */ + $service = $this->container->get(CompanyGetManyService::class); + + $companyMigration = new CompaniesMigration($this->getConnection()); + + $company1 = $this->getNewCompany($companyMigration); + $company2 = $this->getNewCompany($companyMigration); + $company3 = $this->getNewCompany($companyMigration); + $company4 = $this->getNewCompany($companyMigration); + $company5 = $this->getNewCompany($companyMigration); + + $payload = $service->__invoke([]); + + $expected = DomainStatus::SUCCESS; + $actual = $payload->getStatus(); + $this->assertSame($expected, $actual); + + $actual = $payload->getResult(); + $this->assertArrayHasKey('data', $actual); + + $data = $actual['data']; + + $expected = 5; + $actual = count($data); + $this->assertSame($expected, $actual); + + $this->assertArrayHasKey($company1['com_id'], $data); + $this->assertArrayHasKey($company2['com_id'], $data); + $this->assertArrayHasKey($company3['com_id'], $data); + $this->assertArrayHasKey($company4['com_id'], $data); + $this->assertArrayHasKey($company5['com_id'], $data); + + $expected = $company1['com_name']; + $actual = $data[$company1['com_id']]['name']; + $this->assertSame($expected, $actual); + + $expected = $company2['com_name']; + $actual = $data[$company2['com_id']]['name']; + $this->assertSame($expected, $actual); + + $expected = $company3['com_name']; + $actual = $data[$company3['com_id']]['name']; + $this->assertSame($expected, $actual); + + $expected = $company4['com_name']; + $actual = $data[$company4['com_id']]['name']; + $this->assertSame($expected, $actual); + + $expected = $company5['com_name']; + $actual = $data[$company5['com_id']]['name']; + $this->assertSame($expected, $actual); + } +} diff --git a/tests/Unit/Domain/Application/Company/Service/CompanyServiceGetTest.php b/tests/Unit/Domain/Application/Company/Service/CompanyServiceGetTest.php new file mode 100644 index 0000000..614e913 --- /dev/null +++ b/tests/Unit/Domain/Application/Company/Service/CompanyServiceGetTest.php @@ -0,0 +1,158 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Phalcon\Api\Tests\Unit\Domain\Application\Company\Service; + +use PayloadInterop\DomainStatus; +use Phalcon\Api\Domain\Application\Company\Service\CompanyGetService; +use Phalcon\Api\Tests\AbstractUnitTestCase; +use Phalcon\Api\Tests\Fixtures\Domain\Migrations\CompaniesMigration; + +use function array_key_first; + +final class CompanyServiceGetTest extends AbstractUnitTestCase +{ + public function testServiceEmptyCompanyId(): void + { + /** @var CompanyGetService $service */ + $service = $this->container->get(CompanyGetService::class); + + $payload = $service->__invoke([]); + + $expected = DomainStatus::NOT_FOUND; + $actual = $payload->getStatus(); + $this->assertSame($expected, $actual); + + $actual = $payload->getResult(); + $this->assertArrayHasKey('errors', $actual); + + $errors = $actual['errors']; + + $expected = [['Record(s) not found']]; + $actual = $errors; + $this->assertSame($expected, $actual); + } + + public function testServiceWithCompanyId(): void + { + /** @var CompanyGetService $service */ + $service = $this->container->get(CompanyGetService::class); + + $companyMigration = new CompaniesMigration($this->getConnection()); + + $dbCompany = $this->getNewCompany($companyMigration); + $companyId = $dbCompany['com_id']; + + $payload = $service->__invoke( + [ + 'id' => $companyId, + ] + ); + + $expected = DomainStatus::SUCCESS; + $actual = $payload->getStatus(); + $this->assertSame($expected, $actual); + + $actual = $payload->getResult(); + $this->assertArrayHasKey('data', $actual); + + $company = $actual['data']; + $key = array_key_first($company); + $company = $company[$key]; + + $expected = $dbCompany['com_id']; + $actual = $company['id']; + $this->assertSame($expected, $actual); + + $expected = $dbCompany['com_name']; + $actual = $company['name']; + $this->assertSame($expected, $actual); + + $expected = $dbCompany['com_email']; + $actual = $company['email']; + $this->assertSame($expected, $actual); + + $expected = $dbCompany['com_phone']; + $actual = $company['phone']; + $this->assertSame($expected, $actual); + + $expected = $dbCompany['com_website']; + $actual = $company['website']; + $this->assertSame($expected, $actual); + + $expected = $dbCompany['com_address_line_1']; + $actual = $company['addressLine1']; + $this->assertSame($expected, $actual); + + $expected = $dbCompany['com_address_line_2']; + $actual = $company['addressLine2']; + $this->assertSame($expected, $actual); + + $expected = $dbCompany['com_city']; + $actual = $company['city']; + $this->assertSame($expected, $actual); + + $expected = $dbCompany['com_state_province']; + $actual = $company['stateProvince']; + $this->assertSame($expected, $actual); + + $expected = $dbCompany['com_zip_code']; + $actual = $company['zipCode']; + $this->assertSame($expected, $actual); + + $expected = $dbCompany['com_country']; + $actual = $company['country']; + $this->assertSame($expected, $actual); + + $expected = $dbCompany['com_created_date']; + $actual = $company['createdDate']; + $this->assertSame($expected, $actual); + + $expected = $dbCompany['com_created_usr_id']; + $actual = $company['createdUserId']; + $this->assertSame($expected, $actual); + + $expected = $dbCompany['com_updated_date']; + $actual = $company['updatedDate']; + $this->assertSame($expected, $actual); + + $expected = $dbCompany['com_updated_usr_id']; + $actual = $company['updatedUserId']; + $this->assertSame($expected, $actual); + } + + public function testServiceWrongCompanyId(): void + { + /** @var CompanyGetService $service */ + $service = $this->container->get(CompanyGetService::class); + + $payload = $service->__invoke( + [ + 'id' => 999999, + ] + ); + + $expected = DomainStatus::NOT_FOUND; + $actual = $payload->getStatus(); + $this->assertSame($expected, $actual); + + $actual = $payload->getResult(); + $this->assertArrayHasKey('errors', $actual); + + $errors = $actual['errors']; + + $expected = [['Record(s) not found']]; + $actual = $errors; + $this->assertSame($expected, $actual); + } +} diff --git a/tests/Unit/Domain/Application/Company/Service/CompanyServicePostTest.php b/tests/Unit/Domain/Application/Company/Service/CompanyServicePostTest.php new file mode 100644 index 0000000..4e23634 --- /dev/null +++ b/tests/Unit/Domain/Application/Company/Service/CompanyServicePostTest.php @@ -0,0 +1,409 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Phalcon\Api\Tests\Unit\Domain\Application\Company\Service; + +use DateTimeImmutable; +use PayloadInterop\DomainStatus; +use PDOException; +use Phalcon\Api\Domain\Application\Company\Service\CompanyPostService; +use Phalcon\Api\Domain\Infrastructure\DataSource\Company\Mapper\CompanyMapper; +use Phalcon\Api\Domain\Infrastructure\DataSource\Company\Mapper\CompanyMapperInterface; +use Phalcon\Api\Domain\Infrastructure\DataSource\Company\Repository\CompanyRepository; +use Phalcon\Api\Domain\Infrastructure\DataSource\User\Mapper\UserMapper; +use Phalcon\Api\Domain\Infrastructure\DataSource\User\Mapper\UserMapperInterface; +use Phalcon\Api\Tests\AbstractUnitTestCase; +use Phalcon\Support\Registry; + +use function array_key_first; +use function uniqid; + +final class CompanyServicePostTest extends AbstractUnitTestCase +{ + public function testServiceFailureNoIdReturned(): void + { + $companyRepository = $this + ->getMockBuilder(CompanyRepository::class) + ->disableOriginalConstructor() + ->onlyMethods( + [ + 'insert', + ] + ) + ->getMock() + ; + $companyRepository->method('insert')->willReturn(0); + + $this->container->setShared(CompanyRepository::class, $companyRepository); + + /** @var CompanyPostService $service */ + $service = $this->container->get(CompanyPostService::class); + /** @var CompanyMapper $companyMapper */ + $companyMapper = $this->container->get(CompanyMapper::class); + /** @var UserMapper $userMapper */ + $userMapper = $this->container->get(UserMapper::class); + /** @var Registry $registry */ + $registry = $this->container->get(Registry::class); + + $userData = $this->getNewUserData(); + $userData['usr_id'] = 1; + $companyData = $this->getNewCompanyData(); + $companyData['com_id'] = 1; + + /** + * $companyData is a db record. We need a domain object here + */ + $domainCompany = $companyMapper->domain($companyData); + $domainUser = $userMapper->domain($userData); + /** + * Store the domain user in the registry - to be used for *_com_id + * field updates + */ + $registry->set('user', $domainUser); + + $domainData = $domainCompany->toArray(); + + $payload = $service->__invoke($domainData); + + $expected = DomainStatus::ERROR; + $actual = $payload->getStatus(); + $this->assertSame($expected, $actual); + + $actual = $payload->getResult(); + $this->assertArrayHasKey('errors', $actual); + + $errors = $actual['errors']; + + $expected = [['Cannot create database record: No id returned']]; + $actual = $errors; + $this->assertSame($expected, $actual); + } + + public function testServiceFailurePdoError(): void + { + $message = uniqid('pdo-error-'); + $companyRepository = $this + ->getMockBuilder(CompanyRepository::class) + ->disableOriginalConstructor() + ->onlyMethods( + [ + 'insert', + ] + ) + ->getMock() + ; + $companyRepository + ->method('insert') + ->willThrowException(new PDOException($message)) + ; + + $this->container->setShared(CompanyRepository::class, $companyRepository); + + /** @var CompanyPostService $service */ + $service = $this->container->get(CompanyPostService::class); + /** @var CompanyMapper $companyMapper */ + $companyMapper = $this->container->get(CompanyMapper::class); + /** @var UserMapper $userMapper */ + $userMapper = $this->container->get(UserMapper::class); + /** @var Registry $registry */ + $registry = $this->container->get(Registry::class); + + $userData = $this->getNewUserData(); + $userData['usr_id'] = 1; + $companyData = $this->getNewCompanyData(); + $companyData['com_id'] = 1; + + /** + * $companyData is a db record. We need a domain object here + */ + $domainCompany = $companyMapper->domain($companyData); + $domainUser = $userMapper->domain($userData); + /** + * Store the domain user in the registry - to be used for *_com_id + * field updates + */ + $registry->set('user', $domainUser); + + $domainData = $domainCompany->toArray(); + + $payload = $service->__invoke($domainData); + + $expected = DomainStatus::ERROR; + $actual = $payload->getStatus(); + $this->assertSame($expected, $actual); + + $actual = $payload->getResult(); + $this->assertArrayHasKey('errors', $actual); + + $errors = $actual['errors']; + + $expected = [['Cannot create database record: ' . $message]]; + $actual = $errors; + $this->assertSame($expected, $actual); + } + + public function testServiceFailureValidation(): void + { + /** @var CompanyPostService $service */ + $service = $this->container->get(CompanyPostService::class); + /** @var CompanyMapper $companyMapper */ + $companyMapper = $this->container->get(CompanyMapper::class); + + $companyData = $this->getNewCompanyData(); + + unset( + $companyData['com_name'], + $companyData['com_email'], + ); + + /** + * $companyData is a db record. We need a domain object here + */ + $domainCompany = $companyMapper->domain($companyData); + $domainData = $domainCompany->toArray(); + + $payload = $service->__invoke($domainData); + + $expected = DomainStatus::INVALID; + $actual = $payload->getStatus(); + $this->assertSame($expected, $actual); + + $actual = $payload->getResult(); + $this->assertArrayHasKey('errors', $actual); + + $errors = $actual['errors']; + + $expected = [ + ['Field name is required'], + ['Field email is required'], + ['Field email must be an email address'], + ]; + $actual = $errors; + $this->assertSame($expected, $actual); + } + + public function testServiceSuccess(): void + { + /** @var CompanyPostService $service */ + $service = $this->container->get(CompanyPostService::class); + /** @var UserMapperInterface $companyMapper */ + $userMapper = $this->container->get(UserMapper::class); + /** @var CompanyMapperInterface $companyMapper */ + $companyMapper = $this->container->get(CompanyMapper::class); + /** @var Registry $registry */ + $registry = $this->container->get(Registry::class); + + $userData = $this->getNewUserData(); + $userData['usr_id'] = 1; + $companyData = $this->getNewCompanyData(); + $companyData['com_id'] = 1; + + /** + * $companyData is a db record. We need a domain object here + */ + $domainCompany = $companyMapper->domain($companyData); + $domainUser = $userMapper->domain($userData); + /** + * Store the domain user in the registry - to be used for *_com_id + * field updates + */ + $registry->set('user', $domainUser); + + $domainCompany = $companyMapper->domain($companyData); + $domainData = $domainCompany->toArray(); + + $payload = $service->__invoke($domainData); + + $expected = DomainStatus::CREATED; + $actual = $payload->getStatus(); + $this->assertSame($expected, $actual); + + $actual = $payload->getResult(); + $this->assertArrayHasKey('data', $actual); + + $data = $actual['data']; + + $companyId = array_key_first($data); + + $this->assertGreaterThan(0, $companyId); + + $data = $data[$companyId]; + + $expected = $domainData['name']; + $actual = $data['name']; + $this->assertSame($expected, $actual); + + $expected = $domainData['email']; + $actual = $data['email']; + $this->assertSame($expected, $actual); + + $expected = $domainData['phone']; + $actual = $data['phone']; + $this->assertSame($expected, $actual); + + $expected = $domainData['website']; + $actual = $data['website']; + $this->assertSame($expected, $actual); + + $expected = $domainData['addressLine1']; + $actual = $data['addressLine1']; + $this->assertSame($expected, $actual); + + $expected = $domainData['addressLine2']; + $actual = $data['addressLine2']; + $this->assertSame($expected, $actual); + + $expected = $domainData['city']; + $actual = $data['city']; + $this->assertSame($expected, $actual); + + $expected = $domainData['stateProvince']; + $actual = $data['stateProvince']; + $this->assertSame($expected, $actual); + + $expected = $domainData['zipCode']; + $actual = $data['zipCode']; + $this->assertSame($expected, $actual); + + $expected = $domainData['country']; + $actual = $data['country']; + $this->assertSame($expected, $actual); + + $expected = $domainData['createdDate']; + $actual = $data['createdDate']; + $this->assertSame($expected, $actual); + + $expected = $domainUser->id; + $actual = $data['createdUserId']; + $this->assertSame($expected, $actual); + + $expected = $domainData['updatedDate']; + $actual = $data['updatedDate']; + $this->assertSame($expected, $actual); + + $expected = $domainUser->id; + $actual = $data['updatedUserId']; + $this->assertSame($expected, $actual); + } + + public function testServiceSuccessEmptyDates(): void + { + $now = new DateTimeImmutable(); + $today = $now->format('Y-m-d'); + /** @var CompanyPostService $service */ + $service = $this->container->get(CompanyPostService::class); + /** @var CompanyMapperInterface $companyMapper */ + $companyMapper = $this->container->get(CompanyMapper::class); + /** @var UserMapper $userMapper */ + $userMapper = $this->container->get(UserMapper::class); + /** @var Registry $registry */ + $registry = $this->container->get(Registry::class); + + $userData = $this->getNewUserData(); + $userData['usr_id'] = 1; + $companyData = $this->getNewCompanyData(); + $companyData['com_id'] = 1; + + /** + * $companyData is a db record. We need a domain object here + */ + $domainCompany = $companyMapper->domain($companyData); + $domainUser = $userMapper->domain($userData); + /** + * Store the domain user in the registry - to be used for *_com_id + * field updates + */ + $registry->set('user', $domainUser); + + unset( + $companyData['com_created_date'], + $companyData['com_updated_date'], + ); + + /** + * $companyData is a db record. We need a domain object here + */ + $domainCompany = $companyMapper->domain($companyData); + $domainData = $domainCompany->toArray(); + $payload = $service->__invoke($domainData); + + $expected = DomainStatus::CREATED; + $actual = $payload->getStatus(); + $this->assertSame($expected, $actual); + + $actual = $payload->getResult(); + $this->assertArrayHasKey('data', $actual); + + $data = $actual['data']; + + $companyId = array_key_first($data); + + $this->assertGreaterThan(0, $companyId); + + $data = $data[$companyId]; + + $expected = $domainData['name']; + $actual = $data['name']; + $this->assertSame($expected, $actual); + + $expected = $domainData['email']; + $actual = $data['email']; + $this->assertSame($expected, $actual); + + $expected = $domainData['phone']; + $actual = $data['phone']; + $this->assertSame($expected, $actual); + + $expected = $domainData['website']; + $actual = $data['website']; + $this->assertSame($expected, $actual); + + $expected = $domainData['addressLine1']; + $actual = $data['addressLine1']; + $this->assertSame($expected, $actual); + + $expected = $domainData['addressLine2']; + $actual = $data['addressLine2']; + $this->assertSame($expected, $actual); + + $expected = $domainData['city']; + $actual = $data['city']; + $this->assertSame($expected, $actual); + + $expected = $domainData['stateProvince']; + $actual = $data['stateProvince']; + $this->assertSame($expected, $actual); + + $expected = $domainData['zipCode']; + $actual = $data['zipCode']; + $this->assertSame($expected, $actual); + + $expected = $domainData['country']; + $actual = $data['country']; + $this->assertSame($expected, $actual); + + $actual = $data['createdDate']; + $this->assertStringContainsString($today, $actual); + + $expected = 1; + $actual = $data['createdUserId']; + $this->assertSame($expected, $actual); + + $actual = $data['updatedDate']; + $this->assertStringContainsString($today, $actual); + + $expected = 1; + $actual = $data['updatedUserId']; + $this->assertSame($expected, $actual); + } +} diff --git a/tests/Unit/Domain/Application/Company/Service/CompanyServicePutTest.php b/tests/Unit/Domain/Application/Company/Service/CompanyServicePutTest.php new file mode 100644 index 0000000..1ad5ea2 --- /dev/null +++ b/tests/Unit/Domain/Application/Company/Service/CompanyServicePutTest.php @@ -0,0 +1,476 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Phalcon\Api\Tests\Unit\Domain\Application\Company\Service; + +use DateTimeImmutable; +use Faker\Factory; +use PayloadInterop\DomainStatus; +use PDOException; +use Phalcon\Api\Domain\Application\Company\Service\CompanyPutService; +use Phalcon\Api\Domain\Infrastructure\DataSource\Company\Mapper\CompanyMapper; +use Phalcon\Api\Domain\Infrastructure\DataSource\Company\Repository\CompanyRepository; +use Phalcon\Api\Domain\Infrastructure\DataSource\User\Mapper\UserMapper; +use Phalcon\Api\Tests\AbstractUnitTestCase; +use Phalcon\Api\Tests\Fixtures\Domain\Migrations\CompaniesMigration; +use Phalcon\Support\Registry; + +use function uniqid; + +final class CompanyServicePutTest extends AbstractUnitTestCase +{ + public function testServiceFailureNoIdReturned(): void + { + /** @var CompanyMapper $companyMapper */ + $companyMapper = $this->container->get(CompanyMapper::class); + $companyData = $this->getNewCompanyData(); + + $companyData['com_id'] = 1; + + $findByCompany = $companyMapper->domain($companyData); + + $userRepository = $this + ->getMockBuilder(CompanyRepository::class) + ->disableOriginalConstructor() + ->onlyMethods( + [ + 'update', + 'findById', + ] + ) + ->getMock() + ; + $userRepository->method('update')->willReturn(0); + $userRepository->method('findById')->willReturn($findByCompany); + + $this->container->setShared(CompanyRepository::class, $userRepository); + + /** + * Add a user in the repository for the session + */ + /** @var Registry $registry */ + $registry = $this->container->get(Registry::class); + /** @var UserMapper $userMapper */ + $userMapper = $this->container->get(UserMapper::class); + $sessionUser = $userMapper->domain(['id' => 1]); + $registry->set('user', $sessionUser); + + /** @var CompanyPutService $service */ + $service = $this->container->get(CompanyPutService::class); + + /** + * Update user + */ + $companyData = $this->getNewCompanyData(); + $companyData['com_id'] = 1; + + $updateCompany = $companyMapper->domain($companyData); + $updateCompany = $updateCompany->toArray(); + + $payload = $service->__invoke($updateCompany); + + $expected = DomainStatus::ERROR; + $actual = $payload->getStatus(); + $this->assertSame($expected, $actual); + + $actual = $payload->getResult(); + $this->assertArrayHasKey('errors', $actual); + + $errors = $actual['errors']; + + $expected = [['Cannot update database record: No id returned']]; + $actual = $errors; + $this->assertSame($expected, $actual); + } + + public function testServiceFailurePdoError(): void + { + $message = uniqid('pdo-error-'); + /** @var CompanyMapper $companyMapper */ + $companyMapper = $this->container->get(CompanyMapper::class); + $companyData = $this->getNewCompanyData(); + + $companyData['com_id'] = 1; + + $findByCompany = $companyMapper->domain($companyData); + + $userRepository = $this + ->getMockBuilder(CompanyRepository::class) + ->disableOriginalConstructor() + ->onlyMethods( + [ + 'update', + 'findById', + ] + ) + ->getMock() + ; + $userRepository->method('findById')->willReturn($findByCompany); + $userRepository + ->method('update') + ->willThrowException(new PDOException($message)) + ; + + $this->container->setShared(CompanyRepository::class, $userRepository); + + /** + * Add a user in the repository for the session + */ + /** @var Registry $registry */ + $registry = $this->container->get(Registry::class); + /** @var UserMapper $userMapper */ + $userMapper = $this->container->get(UserMapper::class); + $sessionUser = $userMapper->domain(['id' => 1]); + $registry->set('user', $sessionUser); + + /** @var CompanyPutService $service */ + $service = $this->container->get(CompanyPutService::class); + + $companyData = $this->getNewCompanyData(); + $companyData['com_id'] = 1; + + /** + * $companyData is a db record. We need a domain object here + */ + $updateCompany = $companyMapper->domain($companyData); + $updateCompany = $updateCompany->toArray(); + + $payload = $service->__invoke($updateCompany); + + $expected = DomainStatus::ERROR; + $actual = $payload->getStatus(); + $this->assertSame($expected, $actual); + + $actual = $payload->getResult(); + $this->assertArrayHasKey('errors', $actual); + + $errors = $actual['errors']; + + $expected = [['Cannot update database record: ' . $message]]; + $actual = $errors; + $this->assertSame($expected, $actual); + } + + public function testServiceFailureRecordNotFound(): void + { + /** @var CompanyMapper $companyMapper */ + $companyMapper = $this->container->get(CompanyMapper::class); + $userRepository = $this + ->getMockBuilder(CompanyRepository::class) + ->disableOriginalConstructor() + ->onlyMethods( + [ + 'findById', + ] + ) + ->getMock() + ; + $userRepository->method('findById')->willReturn(null); + + $this->container->setShared(CompanyRepository::class, $userRepository); + + /** @var CompanyPutService $service */ + $service = $this->container->get(CompanyPutService::class); + + /** + * Update user + */ + $companyData = $this->getNewCompanyData(); + $companyData['com_id'] = 1; + + $updateCompany = $companyMapper->domain($companyData); + $updateCompany = $updateCompany->toArray(); + + $payload = $service->__invoke($updateCompany); + + $expected = DomainStatus::NOT_FOUND; + $actual = $payload->getStatus(); + $this->assertSame($expected, $actual); + + $actual = $payload->getResult(); + $this->assertArrayHasKey('errors', $actual); + + $errors = $actual['errors']; + + $expected = [['Record(s) not found']]; + $actual = $errors; + $this->assertSame($expected, $actual); + } + + public function testServiceFailureValidation(): void + { + /** @var CompanyPutService $service */ + $service = $this->container->get(CompanyPutService::class); + /** @var CompanyMapper $companyMapper */ + $companyMapper = $this->container->get(CompanyMapper::class); + $companyData = $this->getNewCompanyData(); + + $companyData['com_email'] = 'wrong-email'; + unset($companyData['com_id']); + + /** + * $companyData is a db record. We need a domain object here + */ + $updateCompany = $companyMapper->domain($companyData); + $updateCompany = $updateCompany->toArray(); + + $payload = $service->__invoke($updateCompany); + + $expected = DomainStatus::INVALID; + $actual = $payload->getStatus(); + $this->assertSame($expected, $actual); + + $actual = $payload->getResult(); + $this->assertArrayHasKey('errors', $actual); + + $errors = $actual['errors']; + + $expected = [ + ['Field id is not a valid absolute integer and greater than 0'], + ['Field email must be an email address'], + ]; + $actual = $errors; + $this->assertSame($expected, $actual); + } + + public function testServiceSuccess(): void + { + $faker = Factory::create(); + /** @var CompanyPutService $service */ + $service = $this->container->get(CompanyPutService::class); + /** @var UserMapper $userMapper */ + $userMapper = $this->container->get(UserMapper::class); + /** @var CompanyMapper $companyMapper */ + $companyMapper = $this->container->get(CompanyMapper::class); + /** @var Registry $registry */ + $registry = $this->container->get(Registry::class); + + $migration = new CompaniesMigration($this->getConnection()); + $dbCompany = $this->getNewCompany($migration); + $companyId = $dbCompany['com_id']; + $userData = $this->getNewUserData(); + $userId = $faker->numberBetween(1, 10); + $userData['usr_id'] = $userId; + + /** + * $userData is a db record. We need a domain object here + */ + $domainUser = $userMapper->domain($userData); + /** + * Store the domain user in the registry - to be used for *_com_id + * field updates + */ + $registry->set('user', $domainUser); + + $domainCompany = $companyMapper->domain($dbCompany); + $domainData = $domainCompany->toArray(); + + $payload = $service->__invoke($domainData); + + $expected = DomainStatus::UPDATED; + $actual = $payload->getStatus(); + $this->assertSame($expected, $actual); + + $actual = $payload->getResult(); + $this->assertArrayHasKey('data', $actual); + + $data = $actual['data']; + + $this->assertArrayHasKey($companyId, $data); + + $data = $data[$companyId]; + + $expected = $domainData['id']; + $actual = $data['id']; + $this->assertSame($expected, $actual); + + $expected = $domainData['name']; + $actual = $data['name']; + $this->assertSame($expected, $actual); + + $expected = $domainData['email']; + $actual = $data['email']; + $this->assertSame($expected, $actual); + + $expected = $domainData['phone']; + $actual = $data['phone']; + $this->assertSame($expected, $actual); + + $expected = $domainData['website']; + $actual = $data['website']; + $this->assertSame($expected, $actual); + + $expected = $domainData['addressLine1']; + $actual = $data['addressLine1']; + $this->assertSame($expected, $actual); + + $expected = $domainData['addressLine2']; + $actual = $data['addressLine2']; + $this->assertSame($expected, $actual); + + $expected = $domainData['city']; + $actual = $data['city']; + $this->assertSame($expected, $actual); + + $expected = $domainData['stateProvince']; + $actual = $data['stateProvince']; + $this->assertSame($expected, $actual); + + $expected = $domainData['zipCode']; + $actual = $data['zipCode']; + $this->assertSame($expected, $actual); + + $expected = $domainData['country']; + $actual = $data['country']; + $this->assertSame($expected, $actual); + + /** + * These have to be the same on an update + */ + $expected = $domainData['createdDate']; + $actual = $data['createdDate']; + $this->assertSame($expected, $actual); + + $expected = 0; + $actual = $data['createdUserId']; + $this->assertSame($expected, $actual); + + $expected = $domainData['updatedDate']; + $actual = $data['updatedDate']; + $this->assertSame($expected, $actual); + + $expected = $userId; + $actual = $data['updatedUserId']; + $this->assertSame($expected, $actual); + } + + public function testServiceSuccessEmptyDates(): void + { + $faker = Factory::create(); + $now = new DateTimeImmutable(); + $today = $now->format('Y-m-d'); + /** @var CompanyPutService $service */ + $service = $this->container->get(CompanyPutService::class); + /** @var UserMapper $userMapper */ + $userMapper = $this->container->get(UserMapper::class); + /** @var CompanyMapper $companyMapper */ + $companyMapper = $this->container->get(CompanyMapper::class); + /** @var Registry $registry */ + $registry = $this->container->get(Registry::class); + + $migration = new CompaniesMigration($this->getConnection()); + $dbCompany = $this->getNewCompany($migration); + + $companyId = $dbCompany['com_id']; + $userData = $this->getNewUserData(); + $userId = $faker->numberBetween(1, 10); + $userData['usr_id'] = $userId; + + unset( + $dbCompany['com_updated_date'], + ); + + /** + * $userData is a db record. We need a domain object here + */ + $domainUser = $userMapper->domain($userData); + /** + * Store the domain user in the registry - to be used for *_usr_id + * field updates + */ + $registry->set('user', $domainUser); + + $domainCompany = $companyMapper->domain($dbCompany); + $domainData = $domainCompany->toArray(); + + $payload = $service->__invoke($domainData); + + $expected = DomainStatus::UPDATED; + $actual = $payload->getStatus(); + $this->assertSame($expected, $actual); + + $actual = $payload->getResult(); + $this->assertArrayHasKey('data', $actual); + + $data = $actual['data']; + + $this->assertArrayHasKey($companyId, $data); + + $data = $data[$companyId]; + + $expected = $domainData['id']; + $actual = $data['id']; + $this->assertSame($expected, $actual); + + $expected = $domainData['name']; + $actual = $data['name']; + $this->assertSame($expected, $actual); + + $expected = $domainData['email']; + $actual = $data['email']; + $this->assertSame($expected, $actual); + + $expected = $domainData['phone']; + $actual = $data['phone']; + $this->assertSame($expected, $actual); + + $expected = $domainData['website']; + $actual = $data['website']; + $this->assertSame($expected, $actual); + + $expected = $domainData['addressLine1']; + $actual = $data['addressLine1']; + $this->assertSame($expected, $actual); + + $expected = $domainData['addressLine2']; + $actual = $data['addressLine2']; + $this->assertSame($expected, $actual); + + $expected = $domainData['city']; + $actual = $data['city']; + $this->assertSame($expected, $actual); + + $expected = $domainData['stateProvince']; + $actual = $data['stateProvince']; + $this->assertSame($expected, $actual); + + $expected = $domainData['zipCode']; + $actual = $data['zipCode']; + $this->assertSame($expected, $actual); + + $expected = $domainData['country']; + $actual = $data['country']; + $this->assertSame($expected, $actual); + + /** + * These have to be the same on an update + */ + $expected = $dbCompany['com_created_date']; + $actual = $data['createdDate']; + $this->assertSame($expected, $actual); + + /** + * These have to be the same on an update + */ + $expected = 0; + $actual = $data['createdUserId']; + $this->assertSame($expected, $actual); + + $actual = $data['updatedDate']; + $this->assertStringContainsString($today, $actual); + + $expected = $userId; + $actual = $data['updatedUserId']; + $this->assertSame($expected, $actual); + } +} diff --git a/tests/Unit/Domain/Application/User/Handler/UserHandlerPostTest.php b/tests/Unit/Domain/Application/User/Handler/UserHandlerPostTest.php index 0a4ac1a..47eef85 100644 --- a/tests/Unit/Domain/Application/User/Handler/UserHandlerPostTest.php +++ b/tests/Unit/Domain/Application/User/Handler/UserHandlerPostTest.php @@ -24,6 +24,7 @@ use Phalcon\Support\Registry; use function strip_tags; +use function uniqid; final class UserHandlerPostTest extends AbstractUnitTestCase { @@ -86,6 +87,7 @@ public function testHandlerFailureNoIdReturned(): void public function testHandlerFailurePdoError(): void { + $message = uniqid('pdo-error-'); $userRepository = $this ->getMockBuilder(UserRepository::class) ->disableOriginalConstructor() @@ -98,7 +100,7 @@ public function testHandlerFailurePdoError(): void ; $userRepository ->method('insert') - ->willThrowException(new PDOException('abcde')) + ->willThrowException(new PDOException($message)) ; $this->container->setShared(UserRepository::class, $userRepository); @@ -138,7 +140,7 @@ public function testHandlerFailurePdoError(): void $errors = $actual['errors']; - $expected = [['Cannot create database record: abcde']]; + $expected = [['Cannot create database record: ' . $message]]; $actual = $errors; $this->assertSame($expected, $actual); } diff --git a/tests/Unit/Domain/Application/User/Handler/UserHandlerPutTest.php b/tests/Unit/Domain/Application/User/Handler/UserHandlerPutTest.php index 015cf82..c2f645b 100644 --- a/tests/Unit/Domain/Application/User/Handler/UserHandlerPutTest.php +++ b/tests/Unit/Domain/Application/User/Handler/UserHandlerPutTest.php @@ -24,6 +24,8 @@ use Phalcon\Api\Tests\Fixtures\Domain\Migrations\UsersMigration; use Phalcon\Support\Registry; +use function uniqid; + final class UserHandlerPutTest extends AbstractUnitTestCase { public function testHandlerFailureNoIdReturned(): void @@ -92,6 +94,7 @@ public function testHandlerFailureNoIdReturned(): void public function testHandlerFailurePdoError(): void { + $message = uniqid('pdo-error-'); /** @var UserMapper $userMapper */ $userMapper = $this->container->get(UserMapper::class); /** @var Registry $registry */ @@ -118,7 +121,7 @@ public function testHandlerFailurePdoError(): void $userRepository->method('findById')->willReturn($findByUser); $userRepository ->method('update') - ->willThrowException(new PDOException('abcde')) + ->willThrowException(new PDOException($message)) ; $this->container->setShared(UserRepository::class, $userRepository); @@ -152,7 +155,7 @@ public function testHandlerFailurePdoError(): void $errors = $actual['errors']; - $expected = [['Cannot update database record: abcde']]; + $expected = [['Cannot update database record: ' . $message]]; $actual = $errors; $this->assertSame($expected, $actual); } diff --git a/tests/Unit/Domain/Application/User/Service/UserServiceDispatchTest.php b/tests/Unit/Domain/Application/User/Service/UserServiceDispatchTest.php index 6e615af..607af0e 100644 --- a/tests/Unit/Domain/Application/User/Service/UserServiceDispatchTest.php +++ b/tests/Unit/Domain/Application/User/Service/UserServiceDispatchTest.php @@ -23,6 +23,7 @@ use Phalcon\Api\Tests\Fixtures\Domain\Migrations\UsersMigration; use Phalcon\Cache\Cache; use PHPUnit\Framework\Attributes\BackupGlobals; +use PHPUnit\Framework\Attributes\RunInSeparateProcess; use function json_decode; use function ob_get_clean; @@ -33,6 +34,7 @@ #[BackupGlobals(true)] final class UserServiceDispatchTest extends AbstractUnitTestCase { + #[RunInSeparateProcess] public function testDispatchGet(): void { /** @var EnvManager $env */ diff --git a/tests/Unit/Domain/Application/User/Service/UserServicePostTest.php b/tests/Unit/Domain/Application/User/Service/UserServicePostTest.php index d025312..bbb6961 100644 --- a/tests/Unit/Domain/Application/User/Service/UserServicePostTest.php +++ b/tests/Unit/Domain/Application/User/Service/UserServicePostTest.php @@ -16,15 +16,18 @@ use DateTimeImmutable; use PayloadInterop\DomainStatus; use PDOException; +use Phalcon\Api\Domain\Application\User\Handler\UserPostHandler; use Phalcon\Api\Domain\Application\User\Service\UserPostService; use Phalcon\Api\Domain\Infrastructure\DataSource\User\Mapper\UserMapper; use Phalcon\Api\Domain\Infrastructure\DataSource\User\Repository\UserRepository; +use Phalcon\Api\Domain\Infrastructure\Env\EnvManager; use Phalcon\Api\Tests\AbstractUnitTestCase; use Phalcon\Support\Registry; use function array_key_first; use function str_starts_with; use function strip_tags; +use function uniqid; final class UserServicePostTest extends AbstractUnitTestCase { @@ -84,6 +87,7 @@ public function testServiceFailureNoIdReturned(): void public function testServiceFailurePdoError(): void { + $message = uniqid('pdo-error-'); $userRepository = $this ->getMockBuilder(UserRepository::class) ->disableOriginalConstructor() @@ -96,7 +100,7 @@ public function testServiceFailurePdoError(): void ; $userRepository ->method('insert') - ->willThrowException(new PDOException('abcde')) + ->willThrowException(new PDOException($message)) ; $this->container->setShared(UserRepository::class, $userRepository); @@ -107,6 +111,8 @@ public function testServiceFailurePdoError(): void $userMapper = $this->container->get(UserMapper::class); /** @var Registry $registry */ $registry = $this->container->get(Registry::class); + /** @var EnvManager $env */ + $env = $this->container->get(EnvManager::class); $userData = $this->getNewUserData(); $userData['usr_id'] = 1; @@ -134,9 +140,24 @@ public function testServiceFailurePdoError(): void $errors = $actual['errors']; - $expected = [['Cannot create database record: abcde']]; + $expected = [['Cannot create database record: ' . $message]]; $actual = $errors; $this->assertSame($expected, $actual); + /** + * Also check the logger if the message was logged + */ + /** @var string $logName */ + $logName = $env->get('LOG_FILENAME', 'rest-api'); + /** @var string $logPath */ + $logPath = $env->get('LOG_PATH', 'storage/logs/'); + $logFile = $env->appPath($logPath) . '/' . $logName . '.log'; + + $message = sprintf( + '[error] %s: %s', + UserPostHandler::class, + $message + ); + $this->assertFileContentsContains($logFile, $message); } public function testServiceFailureValidation(): void diff --git a/tests/Unit/Domain/Application/User/Service/UserServicePutTest.php b/tests/Unit/Domain/Application/User/Service/UserServicePutTest.php index 6efc574..4c8d4dd 100644 --- a/tests/Unit/Domain/Application/User/Service/UserServicePutTest.php +++ b/tests/Unit/Domain/Application/User/Service/UserServicePutTest.php @@ -26,6 +26,7 @@ use function date; use function str_starts_with; use function strip_tags; +use function uniqid; final class UserServicePutTest extends AbstractUnitTestCase { @@ -93,6 +94,7 @@ public function testServiceFailureNoIdReturned(): void public function testServiceFailurePdoError(): void { + $message = uniqid('pdo-error-'); /** @var UserMapper $userMapper */ $userMapper = $this->container->get(UserMapper::class); /** @var Registry $registry */ @@ -117,7 +119,7 @@ public function testServiceFailurePdoError(): void $userRepository->method('findById')->willReturn($findByUser); $userRepository ->method('update') - ->willThrowException(new PDOException('abcde')) + ->willThrowException(new PDOException($message)) ; $this->container->setShared(UserRepository::class, $userRepository); @@ -151,7 +153,7 @@ public function testServiceFailurePdoError(): void $errors = $actual['errors']; - $expected = [['Cannot update database record: abcde']]; + $expected = [['Cannot update database record: ' . $message]]; $actual = $errors; $this->assertSame($expected, $actual); } diff --git a/tests/Unit/Domain/Infrastructure/DataSource/Auth/Sanitizer/AuthSanitizerEnumTest.php b/tests/Unit/Domain/Infrastructure/DataSource/Auth/Sanitizer/AuthSanitizerEnumTest.php new file mode 100644 index 0000000..c3da9e5 --- /dev/null +++ b/tests/Unit/Domain/Infrastructure/DataSource/Auth/Sanitizer/AuthSanitizerEnumTest.php @@ -0,0 +1,52 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Phalcon\Api\Tests\Unit\Domain\Infrastructure\DataSource\Auth\Sanitizer; + +use Phalcon\Api\Domain\Infrastructure\DataSource\Auth\Sanitizer\AuthSanitizerEnum; +use Phalcon\Api\Tests\AbstractUnitTestCase; +use Phalcon\Filter\Filter; + +final class AuthSanitizerEnumTest extends AbstractUnitTestCase +{ + public function testEnum(): void + { + $expected = 3; + $actual = AuthSanitizerEnum::cases(); + $this->assertCount($expected, $actual); + + $expected = Filter::FILTER_EMAIL; + $actual = AuthSanitizerEnum::email->sanitizer(); + $this->assertSame($expected, $actual); + + $expected = null; + $actual = AuthSanitizerEnum::email->default(); + $this->assertSame($expected, $actual); + + $expected = ''; + $actual = AuthSanitizerEnum::password->sanitizer(); + $this->assertSame($expected, $actual); + + $expected = null; + $actual = AuthSanitizerEnum::password->default(); + $this->assertSame($expected, $actual); + + $expected = ''; + $actual = AuthSanitizerEnum::token->sanitizer(); + $this->assertSame($expected, $actual); + + $expected = null; + $actual = AuthSanitizerEnum::token->default(); + $this->assertSame($expected, $actual); + } +} diff --git a/tests/Unit/Domain/Infrastructure/DataSource/Auth/Validator/AuthLoginValidatorEnumTest.php b/tests/Unit/Domain/Infrastructure/DataSource/Auth/Validator/AuthLoginValidatorEnumTest.php new file mode 100644 index 0000000..28ec6a2 --- /dev/null +++ b/tests/Unit/Domain/Infrastructure/DataSource/Auth/Validator/AuthLoginValidatorEnumTest.php @@ -0,0 +1,50 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Phalcon\Api\Tests\Unit\Domain\Infrastructure\DataSource\Auth\Validator; + +use Phalcon\Api\Domain\Infrastructure\DataSource\Auth\Validator\AuthLoginValidatorEnum; +use Phalcon\Api\Tests\AbstractUnitTestCase; +use Phalcon\Filter\Validation\Validator\Email; +use Phalcon\Filter\Validation\Validator\PresenceOf; + +final class AuthLoginValidatorEnumTest extends AbstractUnitTestCase +{ + public function testEnum(): void + { + $expected = 2; + $actual = AuthLoginValidatorEnum::cases(); + $this->assertCount($expected, $actual); + + $expected = [ + PresenceOf::class, + Email::class, + ]; + $actual = AuthLoginValidatorEnum::email->validators(); + $this->assertSame($expected, $actual); + + $expected = false; + $actual = AuthLoginValidatorEnum::email->allowEmpty(); + $this->assertSame($expected, $actual); + + $expected = [ + PresenceOf::class, + ]; + $actual = AuthLoginValidatorEnum::password->validators(); + $this->assertSame($expected, $actual); + + $expected = false; + $actual = AuthLoginValidatorEnum::password->allowEmpty(); + $this->assertSame($expected, $actual); + } +} diff --git a/tests/Unit/Domain/Infrastructure/DataSource/Auth/Validator/AuthTokenValidatorEnumTest.php b/tests/Unit/Domain/Infrastructure/DataSource/Auth/Validator/AuthTokenValidatorEnumTest.php new file mode 100644 index 0000000..969b3a5 --- /dev/null +++ b/tests/Unit/Domain/Infrastructure/DataSource/Auth/Validator/AuthTokenValidatorEnumTest.php @@ -0,0 +1,38 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Phalcon\Api\Tests\Unit\Domain\Infrastructure\DataSource\Auth\Validator; + +use Phalcon\Api\Domain\Infrastructure\DataSource\Auth\Validator\AuthTokenValidatorEnum; +use Phalcon\Api\Tests\AbstractUnitTestCase; +use Phalcon\Filter\Validation\Validator\PresenceOf; + +final class AuthTokenValidatorEnumTest extends AbstractUnitTestCase +{ + public function testEnum(): void + { + $expected = 1; + $actual = AuthTokenValidatorEnum::cases(); + $this->assertCount($expected, $actual); + + $expected = [ + PresenceOf::class, + ]; + $actual = AuthTokenValidatorEnum::token->validators(); + $this->assertSame($expected, $actual); + + $expected = false; + $actual = AuthTokenValidatorEnum::token->allowEmpty(); + $this->assertSame($expected, $actual); + } +} diff --git a/tests/Unit/Domain/Infrastructure/DataSource/Company/DTO/CompanyInputTest.php b/tests/Unit/Domain/Infrastructure/DataSource/Company/DTO/CompanyInputTest.php new file mode 100644 index 0000000..7ced16a --- /dev/null +++ b/tests/Unit/Domain/Infrastructure/DataSource/Company/DTO/CompanyInputTest.php @@ -0,0 +1,114 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Phalcon\Api\Tests\Unit\Domain\Infrastructure\DataSource\Company\DTO; + +use Faker\Factory as FakerFactory; +use Phalcon\Api\Domain\Infrastructure\DataSource\Company\DTO\CompanyInput; +use Phalcon\Api\Domain\Infrastructure\DataSource\Company\Sanitizer\CompanySanitizer; +use Phalcon\Api\Tests\AbstractUnitTestCase; + +final class CompanyInputTest extends AbstractUnitTestCase +{ + public function testToArray(): void + { + /** @var CompanySanitizer $sanitizer */ + $sanitizer = $this->container->get(CompanySanitizer::class); + $faker = FakerFactory::create(); + + $input = [ + 'id' => $faker->numberBetween(1, 1000), + 'name' => $faker->company(), + 'email' => $faker->safeEmail(), + 'phone' => $faker->phoneNumber(), + 'website' => $faker->domainName(), + 'addressLine1' => $faker->streetAddress(), + 'addressLine2' => $faker->streetAddress(), + 'city' => $faker->city(), + 'stateProvince' => $faker->city(), + 'zipCode' => $faker->postcode(), + 'country' => $faker->countryISOAlpha3(), + 'createdDate' => $faker->date(), + 'createdUserId' => (string)$faker->numberBetween(1, 1000), + 'updatedDate' => $faker->date(), + 'updatedUserId' => (string)$faker->numberBetween(1, 1000), + ]; + + $sanitized = $sanitizer->sanitize($input); + $companyInput = CompanyInput::new($sanitizer, $input); + + $expected = $sanitized['id']; + $actual = $companyInput->id; + $this->assertSame($expected, $actual); + + $expected = $sanitized['name']; + $actual = $companyInput->name; + $this->assertSame($expected, $actual); + + $expected = $sanitized['email']; + $actual = $companyInput->email; + $this->assertSame($expected, $actual); + + $expected = $sanitized['phone']; + $actual = $companyInput->phone; + $this->assertSame($expected, $actual); + + $expected = $sanitized['website']; + $actual = $companyInput->website; + $this->assertSame($expected, $actual); + + $expected = $sanitized['addressLine1']; + $actual = $companyInput->addressLine1; + $this->assertSame($expected, $actual); + + $expected = $sanitized['addressLine2']; + $actual = $companyInput->addressLine2; + $this->assertSame($expected, $actual); + + $expected = $sanitized['city']; + $actual = $companyInput->city; + $this->assertSame($expected, $actual); + + $expected = $sanitized['stateProvince']; + $actual = $companyInput->stateProvince; + $this->assertSame($expected, $actual); + + $expected = $sanitized['zipCode']; + $actual = $companyInput->zipCode; + $this->assertSame($expected, $actual); + + $expected = $sanitized['country']; + $actual = $companyInput->country; + $this->assertSame($expected, $actual); + + $expected = $sanitized['createdDate']; + $actual = $companyInput->createdDate; + $this->assertSame($expected, $actual); + + $expected = $sanitized['createdUserId']; + $actual = $companyInput->createdUserId; + $this->assertSame($expected, $actual); + + $expected = $sanitized['updatedDate']; + $actual = $companyInput->updatedDate; + $this->assertSame($expected, $actual); + + $expected = $sanitized['updatedUserId']; + $actual = $companyInput->updatedUserId; + $this->assertSame($expected, $actual); + + $expected = get_object_vars($companyInput); + $actual = $companyInput->toArray(); + $this->assertSame($expected, $actual); + } +} diff --git a/tests/Unit/Domain/Infrastructure/DataSource/Company/DTO/CompanyTest.php b/tests/Unit/Domain/Infrastructure/DataSource/Company/DTO/CompanyTest.php new file mode 100644 index 0000000..4c38aa0 --- /dev/null +++ b/tests/Unit/Domain/Infrastructure/DataSource/Company/DTO/CompanyTest.php @@ -0,0 +1,67 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Phalcon\Api\Tests\Unit\Domain\Infrastructure\DataSource\Company\DTO; + +use Phalcon\Api\Domain\Infrastructure\DataSource\Company\DTO\Company; +use Phalcon\Api\Tests\AbstractUnitTestCase; + +use function rand; + +final class CompanyTest extends AbstractUnitTestCase +{ + public function testObject(): void + { + $companyData = $this->getNewCompanyData(); + $companyData['com_id'] = rand(1, 100); + + $company = new Company( + $companyData['com_id'], + $companyData['com_name'], + $companyData['com_phone'], + $companyData['com_email'], + $companyData['com_website'], + $companyData['com_address_line_1'], + $companyData['com_address_line_2'], + $companyData['com_city'], + $companyData['com_state_province'], + $companyData['com_zip_code'], + $companyData['com_country'], + $companyData['com_created_date'], + $companyData['com_created_usr_id'], + $companyData['com_updated_date'], + $companyData['com_updated_usr_id'], + ); + + + $expected = [ + 'id' => $companyData['com_id'], + 'name' => $companyData['com_name'], + 'phone' => $companyData['com_phone'], + 'email' => $companyData['com_email'], + 'website' => $companyData['com_website'], + 'addressLine1' => $companyData['com_address_line_1'], + 'addressLine2' => $companyData['com_address_line_2'], + 'city' => $companyData['com_city'], + 'stateProvince' => $companyData['com_state_province'], + 'zipCode' => $companyData['com_zip_code'], + 'country' => $companyData['com_country'], + 'createdDate' => $companyData['com_created_date'], + 'createdUserId' => $companyData['com_created_usr_id'], + 'updatedDate' => $companyData['com_updated_date'], + 'updatedUserId' => $companyData['com_updated_usr_id'], + ]; + $actual = $company->toArray(); + $this->assertSame($expected, $actual); + } +} diff --git a/tests/Unit/Domain/Infrastructure/DataSource/Company/Mapper/CompanyMapperTest.php b/tests/Unit/Domain/Infrastructure/DataSource/Company/Mapper/CompanyMapperTest.php new file mode 100644 index 0000000..e606070 --- /dev/null +++ b/tests/Unit/Domain/Infrastructure/DataSource/Company/Mapper/CompanyMapperTest.php @@ -0,0 +1,178 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Phalcon\Api\Tests\Unit\Domain\Infrastructure\DataSource\Company\Mapper; + +use Faker\Factory as FakerFactory; +use Phalcon\Api\Domain\Application\Company\Command\CompanyCommandFactory; +use Phalcon\Api\Domain\Infrastructure\Constants\Dates; +use Phalcon\Api\Domain\Infrastructure\DataSource\Company\Mapper\CompanyMapper; +use Phalcon\Api\Tests\AbstractUnitTestCase; + +final class CompanyMapperTest extends AbstractUnitTestCase +{ + public function testDb(): void + { + /** @var CompanyCommandFactory $factory */ + $factory = $this->container->get(CompanyCommandFactory::class); + $faker = FakerFactory::create(); + + $createdDate = $faker->date(Dates::DATE_TIME_FORMAT); + $updatedDate = $faker->date(Dates::DATE_TIME_FORMAT); + + $input = [ + 'id' => $faker->numberBetween(1, 1000), + 'name' => $faker->company(), + 'phone' => $faker->safeEmail(), + 'email' => $faker->phoneNumber(), + 'website' => $faker->domainName(), + 'addressLine1' => $faker->streetAddress(), + 'addressLine2' => $faker->streetAddress(), + 'city' => $faker->city(), + 'stateProvince' => $faker->city(), + 'zipCode' => $faker->postcode(), + 'country' => $faker->countryISOAlpha3(), + 'createdDate' => $createdDate, + 'createdUserId' => $faker->numberBetween(1, 1000), + 'updatedDate' => $updatedDate, + 'updatedUserId' => $faker->numberBetween(1, 1000), + ]; + + $company = $factory->update($input); + + /** @var CompanyMapper $mapper */ + $mapper = $this->container->get(CompanyMapper::class); + $row = $mapper->db($company); + + $expected = [ + 'com_id' => $company->id, + 'com_name' => $company->name, + 'com_phone' => $company->phone, + 'com_email' => $company->email, + 'com_website' => $company->website, + 'com_address_line_1' => $company->addressLine1, + 'com_address_line_2' => $company->addressLine2, + 'com_city' => $company->city, + 'com_state_province' => $company->stateProvince, + 'com_zip_code' => $company->zipCode, + 'com_country' => $company->country, + 'com_created_date' => $company->createdDate, + 'com_created_usr_id' => $company->createdUserId, + 'com_updated_date' => $company->updatedDate, + 'com_updated_usr_id' => $company->updatedUserId, + ]; + + $actual = $row; + $this->assertSame($expected, $actual); + } + + public function testDomain(): void + { + $faker = FakerFactory::create(); + $mapper = $this->container->get(CompanyMapper::class); + + // Empty row: defaults should be applied + $emptyCompany = $mapper->domain([]); + + $expected = 0; + $actual = $emptyCompany->id; + $this->assertSame($expected, $actual); + + $expected = null; + $actual = $emptyCompany->name; + $this->assertSame($expected, $actual); + + $expected = null; + $actual = $emptyCompany->phone; + $this->assertSame($expected, $actual); + + $expected = null; + $actual = $emptyCompany->email; + $this->assertSame($expected, $actual); + + $expected = null; + $actual = $emptyCompany->website; + $this->assertSame($expected, $actual); + + $expected = null; + $actual = $emptyCompany->addressLine1; + $this->assertSame($expected, $actual); + + $expected = null; + $actual = $emptyCompany->addressLine2; + $this->assertSame($expected, $actual); + + $expected = null; + $actual = $emptyCompany->city; + $this->assertSame($expected, $actual); + + $expected = null; + $actual = $emptyCompany->stateProvince; + $this->assertSame($expected, $actual); + + $expected = null; + $actual = $emptyCompany->zipCode; + $this->assertSame($expected, $actual); + + $expected = null; + $actual = $emptyCompany->country; + $this->assertSame($expected, $actual); + + $expected = null; + $actual = $emptyCompany->createdDate; + $this->assertSame($expected, $actual); + + $expected = null; + $actual = $emptyCompany->createdUserId; + $this->assertSame($expected, $actual); + + $expected = null; + $actual = $emptyCompany->updatedDate; + $this->assertSame($expected, $actual); + + $expected = null; + $actual = $emptyCompany->updatedUserId; + $this->assertSame($expected, $actual); + + // Row with present created/updated user ids as strings should be cast to int + $row = [ + 'com_id' => (string)$faker->numberBetween(1, 1000), + 'com_name' => $faker->company(), + 'com_email' => $faker->safeEmail(), + 'com_created_usr_id' => (string)$faker->numberBetween(1, 1000), + 'com_updated_usr_id' => (string)$faker->numberBetween(1, 1000), + ]; + + $company = $mapper->domain($row); + + $expected = (int)$row['com_id']; + $actual = $company->id; + $this->assertSame($expected, $actual); + + $expected = $row['com_name']; + $actual = $company->name; + $this->assertSame($expected, $actual); + + $expected = $row['com_email']; + $actual = $company->email; + $this->assertSame($expected, $actual); + + $expected = (int)$row['com_created_usr_id']; + $actual = $company->createdUserId; + $this->assertSame($expected, $actual); + + $expected = (int)$row['com_updated_usr_id']; + $actual = $company->updatedUserId; + $this->assertSame($expected, $actual); + } +} diff --git a/tests/Unit/Domain/Infrastructure/DataSource/Company/Repository/CompanyRepositoryTest.php b/tests/Unit/Domain/Infrastructure/DataSource/Company/Repository/CompanyRepositoryTest.php new file mode 100644 index 0000000..cc9bf09 --- /dev/null +++ b/tests/Unit/Domain/Infrastructure/DataSource/Company/Repository/CompanyRepositoryTest.php @@ -0,0 +1,194 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Phalcon\Api\Tests\Unit\Domain\Infrastructure\DataSource\Company\Repository; + +use Phalcon\Api\Domain\Application\Company\Command\CompanyCommandFactory; +use Phalcon\Api\Domain\Infrastructure\DataSource\Company\DTO\Company; +use Phalcon\Api\Domain\Infrastructure\DataSource\Company\DTO\CompanyCollection; +use Phalcon\Api\Domain\Infrastructure\DataSource\Company\DTO\CompanyCriteria; +use Phalcon\Api\Domain\Infrastructure\DataSource\Company\Repository\CompanyRepository; +use Phalcon\Api\Tests\AbstractUnitTestCase; +use Phalcon\Api\Tests\Fixtures\Domain\Migrations\CompaniesMigration; + +use function uniqid; + +final class CompanyRepositoryTest extends AbstractUnitTestCase +{ + public function testFind(): void + { + /** @var CompanyCommandFactory $factory */ + $factory = $this->container->get(CompanyCommandFactory::class); + /** @var CompanyRepository $repository */ + $repository = $this->container->get(CompanyRepository::class); + + $companyName = uniqid('name-'); + $migration = new CompaniesMigration($this->getConnection()); + + $company1 = $this->getNewCompany($migration, ['com_name' => $companyName]); + $company2 = $this->getNewCompany($migration, ['com_name' => $companyName]); + $company3 = $this->getNewCompany($migration, ['com_name' => $companyName]); + $company4 = $this->getNewCompany($migration); + $company5 = $this->getNewCompany($migration); + + $command = $factory->getMany( + [ + 'page' => 1, + 'perPage' => 10, + ] + ); + $criteria = CompanyCriteria::fromDto($command); + + $companies = $repository->find($criteria); + + $class = CompanyCollection::class; + $this->assertInstanceOf($class, $companies); + + $expected = 5; + $actual = $companies->count(); + $this->assertSame($expected, $actual); + + $companies = $companies->toArray(); + + $expected = $company1['com_id']; + $actual = $companies[$company1['com_id']]->id; + $this->assertSame($expected, $actual); + + $expected = $company2['com_id']; + $actual = $companies[$company2['com_id']]->id; + $this->assertSame($expected, $actual); + + $expected = $company3['com_id']; + $actual = $companies[$company3['com_id']]->id; + $this->assertSame($expected, $actual); + + $expected = $company4['com_id']; + $actual = $companies[$company4['com_id']]->id; + $this->assertSame($expected, $actual); + + $expected = $company5['com_id']; + $actual = $companies[$company5['com_id']]->id; + $this->assertSame($expected, $actual); + + $command = $factory->getMany( + [ + 'name' => $companyName, + 'page' => 1, + 'perPage' => 10, + ] + ); + $criteria = CompanyCriteria::fromDto($command); + $companies = $repository->find($criteria); + + $class = CompanyCollection::class; + $this->assertInstanceOf($class, $companies); + + $expected = 3; + $actual = $companies->count(); + $this->assertSame($expected, $actual); + + $companyIds = []; + foreach ($companies as $company) { + $companyIds[] = $company->id; + } + + $expected = [ + $company1['com_id'], + $company2['com_id'], + $company3['com_id'], + ]; + $actual = $companyIds; + $this->assertSame($expected, $actual); + } + + public function testFindById(): void + { + /** @var CompanyRepository $repository */ + $repository = $this->container->get(CompanyRepository::class); + + $migration = new CompaniesMigration($this->getConnection()); + + $repositoryCompany = $repository->findById(0); + $this->assertEmpty($repositoryCompany); + + $migrationCompany = $this->getNewCompany($migration); + $companyId = $migrationCompany['com_id']; + + $repositoryCompany = $repository->findById($companyId); + + $this->runAssertions($migrationCompany, $repositoryCompany); + } + + private function runAssertions(array $dbCompany, Company $company): void + { + $expected = $dbCompany['com_id']; + $actual = $company->id; + $this->assertSame($expected, $actual); + + $expected = $dbCompany['com_name']; + $actual = $company->name; + $this->assertSame($expected, $actual); + + $expected = $dbCompany['com_email']; + $actual = $company->email; + $this->assertSame($expected, $actual); + + $expected = $dbCompany['com_phone']; + $actual = $company->phone; + $this->assertSame($expected, $actual); + + $expected = $dbCompany['com_website']; + $actual = $company->website; + $this->assertSame($expected, $actual); + + $expected = $dbCompany['com_address_line_1']; + $actual = $company->addressLine1; + $this->assertSame($expected, $actual); + + $expected = $dbCompany['com_address_line_2']; + $actual = $company->addressLine2; + $this->assertSame($expected, $actual); + + $expected = $dbCompany['com_city']; + $actual = $company->city; + $this->assertSame($expected, $actual); + + $expected = $dbCompany['com_state_province']; + $actual = $company->stateProvince; + $this->assertSame($expected, $actual); + + $expected = $dbCompany['com_zip_code']; + $actual = $company->zipCode; + $this->assertSame($expected, $actual); + + $expected = $dbCompany['com_country']; + $actual = $company->country; + $this->assertSame($expected, $actual); + + $expected = $dbCompany['com_created_date']; + $actual = $company->createdDate; + $this->assertSame($expected, $actual); + + $expected = $dbCompany['com_created_usr_id']; + $actual = $company->createdUserId; + $this->assertSame($expected, $actual); + + $expected = $dbCompany['com_updated_date']; + $actual = $company->updatedDate; + $this->assertSame($expected, $actual); + + $expected = $dbCompany['com_updated_usr_id']; + $actual = $company->updatedUserId; + $this->assertSame($expected, $actual); + } +} diff --git a/tests/Unit/Domain/Infrastructure/DataSource/Company/Sanitizer/CompanySanitizerEnumTest.php b/tests/Unit/Domain/Infrastructure/DataSource/Company/Sanitizer/CompanySanitizerEnumTest.php new file mode 100644 index 0000000..e642f3b --- /dev/null +++ b/tests/Unit/Domain/Infrastructure/DataSource/Company/Sanitizer/CompanySanitizerEnumTest.php @@ -0,0 +1,164 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Phalcon\Api\Tests\Unit\Domain\Infrastructure\DataSource\Company\Sanitizer; + +use Phalcon\Api\Domain\Infrastructure\DataSource\Company\Sanitizer\CompanySanitizerEnum; +use Phalcon\Api\Tests\AbstractUnitTestCase; +use Phalcon\Filter\Filter; + +final class CompanySanitizerEnumTest extends AbstractUnitTestCase +{ + public function testEnum(): void + { + $expected = 17; + $actual = CompanySanitizerEnum::cases(); + $this->assertCount($expected, $actual); + + $expected = Filter::FILTER_ABSINT; + $actual = CompanySanitizerEnum::id->sanitizer(); + $this->assertSame($expected, $actual); + + $expected = 0; + $actual = CompanySanitizerEnum::id->default(); + $this->assertSame($expected, $actual); + + $expected = Filter::FILTER_STRIPTAGS; + $actual = CompanySanitizerEnum::name->sanitizer(); + $this->assertSame($expected, $actual); + + $expected = null; + $actual = CompanySanitizerEnum::name->default(); + $this->assertSame($expected, $actual); + + $expected = Filter::FILTER_STRIPTAGS; + $actual = CompanySanitizerEnum::phone->sanitizer(); + $this->assertSame($expected, $actual); + + $expected = null; + $actual = CompanySanitizerEnum::phone->default(); + $this->assertSame($expected, $actual); + + $expected = Filter::FILTER_EMAIL; + $actual = CompanySanitizerEnum::email->sanitizer(); + $this->assertSame($expected, $actual); + + $expected = null; + $actual = CompanySanitizerEnum::email->default(); + $this->assertSame($expected, $actual); + + $expected = Filter::FILTER_STRIPTAGS; + $actual = CompanySanitizerEnum::website->sanitizer(); + $this->assertSame($expected, $actual); + + $expected = null; + $actual = CompanySanitizerEnum::website->default(); + $this->assertSame($expected, $actual); + + $expected = Filter::FILTER_STRIPTAGS; + $actual = CompanySanitizerEnum::addressLine1->sanitizer(); + $this->assertSame($expected, $actual); + + $expected = null; + $actual = CompanySanitizerEnum::addressLine1->default(); + $this->assertSame($expected, $actual); + + $expected = Filter::FILTER_STRIPTAGS; + $actual = CompanySanitizerEnum::addressLine2->sanitizer(); + $this->assertSame($expected, $actual); + + $expected = null; + $actual = CompanySanitizerEnum::addressLine2->default(); + $this->assertSame($expected, $actual); + + $expected = Filter::FILTER_STRIPTAGS; + $actual = CompanySanitizerEnum::city->sanitizer(); + $this->assertSame($expected, $actual); + + $expected = null; + $actual = CompanySanitizerEnum::city->default(); + $this->assertSame($expected, $actual); + + $expected = Filter::FILTER_STRIPTAGS; + $actual = CompanySanitizerEnum::stateProvince->sanitizer(); + $this->assertSame($expected, $actual); + + $expected = null; + $actual = CompanySanitizerEnum::stateProvince->default(); + $this->assertSame($expected, $actual); + + $expected = Filter::FILTER_STRIPTAGS; + $actual = CompanySanitizerEnum::zipCode->sanitizer(); + $this->assertSame($expected, $actual); + + $expected = null; + $actual = CompanySanitizerEnum::zipCode->default(); + $this->assertSame($expected, $actual); + + $expected = Filter::FILTER_STRIPTAGS; + $actual = CompanySanitizerEnum::country->sanitizer(); + $this->assertSame($expected, $actual); + + $expected = null; + $actual = CompanySanitizerEnum::country->default(); + $this->assertSame($expected, $actual); + + $expected = Filter::FILTER_STRIPTAGS; + $actual = CompanySanitizerEnum::createdDate->sanitizer(); + $this->assertSame($expected, $actual); + + $expected = null; + $actual = CompanySanitizerEnum::createdDate->default(); + $this->assertSame($expected, $actual); + + $expected = Filter::FILTER_ABSINT; + $actual = CompanySanitizerEnum::createdUserId->sanitizer(); + $this->assertSame($expected, $actual); + + $expected = 0; + $actual = CompanySanitizerEnum::createdUserId->default(); + $this->assertSame($expected, $actual); + + $expected = Filter::FILTER_STRIPTAGS; + $actual = CompanySanitizerEnum::updatedDate->sanitizer(); + $this->assertSame($expected, $actual); + + $expected = null; + $actual = CompanySanitizerEnum::updatedDate->default(); + $this->assertSame($expected, $actual); + + $expected = Filter::FILTER_ABSINT; + $actual = CompanySanitizerEnum::updatedUserId->sanitizer(); + $this->assertSame($expected, $actual); + + $expected = 0; + $actual = CompanySanitizerEnum::updatedUserId->default(); + $this->assertSame($expected, $actual); + + $expected = Filter::FILTER_ABSINT; + $actual = CompanySanitizerEnum::page->sanitizer(); + $this->assertSame($expected, $actual); + + $expected = 1; + $actual = CompanySanitizerEnum::page->default(); + $this->assertSame($expected, $actual); + + $expected = Filter::FILTER_ABSINT; + $actual = CompanySanitizerEnum::perPage->sanitizer(); + $this->assertSame($expected, $actual); + + $expected = 10; + $actual = CompanySanitizerEnum::perPage->default(); + $this->assertSame($expected, $actual); + } +} diff --git a/tests/Unit/Domain/Infrastructure/DataSource/Company/Sanitizer/CompanySanitizerTest.php b/tests/Unit/Domain/Infrastructure/DataSource/Company/Sanitizer/CompanySanitizerTest.php new file mode 100644 index 0000000..d55dd5a --- /dev/null +++ b/tests/Unit/Domain/Infrastructure/DataSource/Company/Sanitizer/CompanySanitizerTest.php @@ -0,0 +1,100 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Phalcon\Api\Tests\Unit\Domain\Infrastructure\DataSource\Company\Sanitizer; + +use Phalcon\Api\Domain\Infrastructure\Container; +use Phalcon\Api\Domain\Infrastructure\DataSource\Company\Sanitizer\CompanySanitizer; +use Phalcon\Api\Tests\AbstractUnitTestCase; +use Phalcon\Filter\Filter; + +final class CompanySanitizerTest extends AbstractUnitTestCase +{ + public function testEmpty(): void + { + /** @var Filter $filter */ + $filter = $this->container->getShared(Container::FILTER); + $sanitizer = new CompanySanitizer($filter); + + $expected = [ + 'id' => 0, + 'name' => null, + 'phone' => null, + 'email' => null, + 'website' => null, + 'addressLine1' => null, + 'addressLine2' => null, + 'city' => null, + 'stateProvince' => null, + 'zipCode' => null, + 'country' => null, + 'createdDate' => null, + 'createdUserId' => 0, + 'updatedDate' => null, + 'updatedUserId' => 0, + 'page' => 1, + 'perPage' => 10, + ]; + $actual = $sanitizer->sanitize([]); + $this->assertSame($expected, $actual); + } + + public function testObject(): void + { + /** @var Filter $filter */ + $filter = $this->container->getShared(Container::FILTER); + $sanitizer = new CompanySanitizer($filter); + + $companyData = [ + 'id' => '123', + 'name' => 'some ', + 'phone' => 'some ', + 'email' => 'John.Doe (newsletter) +spam@example.COM', + 'website' => 'some ', + 'addressLine1' => 'some ', + 'addressLine2' => 'some ', + 'city' => 'some ', + 'stateProvince' => 'some ', + 'zipCode' => 'some ', + 'country' => 'some ', + 'createdDate' => 'some ', + 'createdUserId' => '123', + 'updatedDate' => null, + 'updatedUserId' => '123', + 'page' => '1', + 'perPage' => '10', + ]; + + $expected = [ + 'id' => 123, + 'name' => 'some ', + 'phone' => 'some ', + 'email' => 'John.Doenewsletter+spam@example.COM', + 'website' => 'some ', + 'addressLine1' => 'some ', + 'addressLine2' => 'some ', + 'city' => 'some ', + 'stateProvince' => 'some ', + 'zipCode' => 'some ', + 'country' => 'some ', + 'createdDate' => 'some ', + 'createdUserId' => 123, + 'updatedDate' => null, + 'updatedUserId' => 123, + 'page' => 1, + 'perPage' => 10, + ]; + $actual = $sanitizer->sanitize($companyData); + $this->assertSame($expected, $actual); + } +} diff --git a/tests/Unit/Domain/Infrastructure/DataSource/Company/Validator/CompanyInsertEnumTest.php b/tests/Unit/Domain/Infrastructure/DataSource/Company/Validator/CompanyInsertEnumTest.php new file mode 100644 index 0000000..185e4cf --- /dev/null +++ b/tests/Unit/Domain/Infrastructure/DataSource/Company/Validator/CompanyInsertEnumTest.php @@ -0,0 +1,50 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Phalcon\Api\Tests\Unit\Domain\Infrastructure\DataSource\Company\Validator; + +use Phalcon\Api\Domain\Infrastructure\DataSource\Company\Validator\CompanyInsertEnum; +use Phalcon\Api\Tests\AbstractUnitTestCase; +use Phalcon\Filter\Validation\Validator\Email; +use Phalcon\Filter\Validation\Validator\PresenceOf; + +final class CompanyInsertEnumTest extends AbstractUnitTestCase +{ + public function testEnum(): void + { + $expected = 2; + $actual = CompanyInsertEnum::cases(); + $this->assertCount($expected, $actual); + + $expected = [ + PresenceOf::class, + Email::class, + ]; + $actual = CompanyInsertEnum::email->validators(); + $this->assertSame($expected, $actual); + + $expected = false; + $actual = CompanyInsertEnum::email->allowEmpty(); + $this->assertSame($expected, $actual); + + $expected = [ + PresenceOf::class, + ]; + $actual = CompanyInsertEnum::name->validators(); + $this->assertSame($expected, $actual); + + $expected = false; + $actual = CompanyInsertEnum::name->allowEmpty(); + $this->assertSame($expected, $actual); + } +} diff --git a/tests/Unit/Domain/Infrastructure/DataSource/Company/Validator/CompanyUpdateEnumTest.php b/tests/Unit/Domain/Infrastructure/DataSource/Company/Validator/CompanyUpdateEnumTest.php new file mode 100644 index 0000000..4625328 --- /dev/null +++ b/tests/Unit/Domain/Infrastructure/DataSource/Company/Validator/CompanyUpdateEnumTest.php @@ -0,0 +1,51 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Phalcon\Api\Tests\Unit\Domain\Infrastructure\DataSource\Company\Validator; + +use Phalcon\Api\Domain\Infrastructure\DataSource\Company\Validator\CompanyUpdateEnum; +use Phalcon\Api\Domain\Infrastructure\DataSource\Validation\AbsInt; +use Phalcon\Api\Tests\AbstractUnitTestCase; +use Phalcon\Filter\Validation\Validator\Email; +use Phalcon\Filter\Validation\Validator\PresenceOf; + +final class CompanyUpdateEnumTest extends AbstractUnitTestCase +{ + public function testEnum(): void + { + $expected = 2; + $actual = CompanyUpdateEnum::cases(); + $this->assertCount($expected, $actual); + + $expected = [ + PresenceOf::class, + AbsInt::class, + ]; + $actual = CompanyUpdateEnum::id->validators(); + $this->assertSame($expected, $actual); + + $expected = false; + $actual = CompanyUpdateEnum::id->allowEmpty(); + $this->assertSame($expected, $actual); + + $expected = [ + Email::class, + ]; + $actual = CompanyUpdateEnum::email->validators(); + $this->assertSame($expected, $actual); + + $expected = true; + $actual = CompanyUpdateEnum::email->allowEmpty(); + $this->assertSame($expected, $actual); + } +} diff --git a/tests/Unit/Domain/Infrastructure/DataSource/Company/Validator/CompanyValidatorTest.php b/tests/Unit/Domain/Infrastructure/DataSource/Company/Validator/CompanyValidatorTest.php new file mode 100644 index 0000000..5650a51 --- /dev/null +++ b/tests/Unit/Domain/Infrastructure/DataSource/Company/Validator/CompanyValidatorTest.php @@ -0,0 +1,66 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Phalcon\Api\Tests\Unit\Domain\Infrastructure\DataSource\Company\Validator; + +use Faker\Factory as FakerFactory; +use Phalcon\Api\Domain\Application\Company\Command\CompanyCommandFactory; +use Phalcon\Api\Domain\Infrastructure\DataSource\Company\Validator\CompanyValidator; +use Phalcon\Api\Tests\AbstractUnitTestCase; + +final class CompanyValidatorTest extends AbstractUnitTestCase +{ + public function testError(): void + { + /** @var CompanyCommandFactory $factory */ + $factory = $this->container->get(CompanyCommandFactory::class); + /** @var CompanyValidator $validator */ + $validator = $this->container->get(CompanyValidator::class); + + $input = []; + $companyInput = $factory->insert($input); + + $result = $validator->validate($companyInput); + $actual = $result->getErrors(); + + $expected = [ + ['Field name is required'], + ['Field email is required'], + ['Field email must be an email address'], + ]; + + $this->assertSame($expected, $actual); + } + + public function testSuccess(): void + { + /** @var CompanyCommandFactory $factory */ + $factory = $this->container->get(CompanyCommandFactory::class); + /** @var CompanyValidator $validator */ + $validator = $this->container->get(CompanyValidator::class); + $faker = FakerFactory::create(); + + $input = [ + 'name' => $faker->company(), + 'email' => $faker->safeEmail(), + ]; + + $companyInput = $factory->insert($input); + + $result = $validator->validate($companyInput); + $actual = $result->getErrors(); + + $expected = []; + $this->assertSame($expected, $actual); + } +} diff --git a/tests/Unit/Domain/Infrastructure/DataSource/Company/Validator/CompanyValidatorUpdateTest.php b/tests/Unit/Domain/Infrastructure/DataSource/Company/Validator/CompanyValidatorUpdateTest.php new file mode 100644 index 0000000..fb8cd87 --- /dev/null +++ b/tests/Unit/Domain/Infrastructure/DataSource/Company/Validator/CompanyValidatorUpdateTest.php @@ -0,0 +1,82 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Phalcon\Api\Tests\Unit\Domain\Infrastructure\DataSource\Company\Validator; + +use Faker\Factory as FakerFactory; +use Phalcon\Api\Domain\Application\Company\Command\CompanyCommandFactory; +use Phalcon\Api\Domain\Infrastructure\DataSource\Company\Validator\CompanyValidatorUpdate; +use Phalcon\Api\Tests\AbstractUnitTestCase; + +final class CompanyValidatorUpdateTest extends AbstractUnitTestCase +{ + public function testError(): void + { + /** @var CompanyCommandFactory $factory */ + $factory = $this->container->get(CompanyCommandFactory::class); + /** @var CompanyValidatorUpdate $validator */ + $validator = $this->container->get(CompanyValidatorUpdate::class); + + $input = []; + $companyInput = $factory->update($input); + + $result = $validator->validate($companyInput); + $actual = $result->getErrors(); + + $expected = [ + ['Field id is not a valid absolute integer and greater than 0'], + ]; + + $this->assertSame($expected, $actual); + + $input = [ + 'id' => 1, + 'email' => 'not-email', + ]; + $companyInput = $factory->update($input); + + /** @var CompanyValidatorUpdate $validator */ + $validator = $this->container->get(CompanyValidatorUpdate::class); + + $result = $validator->validate($companyInput); + $actual = $result->getErrors(); + + $expected = [ + ['Field email must be an email address'], + ]; + $this->assertSame($expected, $actual); + } + + public function testSuccess(): void + { + /** @var CompanyCommandFactory $factory */ + $factory = $this->container->get(CompanyCommandFactory::class); + /** @var CompanyValidatorUpdate $validator */ + $validator = $this->container->get(CompanyValidatorUpdate::class); + $faker = FakerFactory::create(); + + $input = [ + 'id' => $faker->numberBetween(1, 100), + 'name' => $faker->company(), + 'email' => $faker->safeEmail(), + ]; + + $companyInput = $factory->update($input); + + $result = $validator->validate($companyInput); + $actual = $result->getErrors(); + + $expected = []; + $this->assertSame($expected, $actual); + } +} diff --git a/tests/Unit/Domain/Infrastructure/DataSource/User/Mapper/UserMapperTest.php b/tests/Unit/Domain/Infrastructure/DataSource/User/Mapper/UserMapperTest.php index 6e0040a..2b89b44 100644 --- a/tests/Unit/Domain/Infrastructure/DataSource/User/Mapper/UserMapperTest.php +++ b/tests/Unit/Domain/Infrastructure/DataSource/User/Mapper/UserMapperTest.php @@ -163,103 +163,4 @@ public function testDomain(): void $actual = $user->updatedUserId; $this->assertSame($expected, $actual); } - - public function testInput(): void - { - $faker = FakerFactory::create(); - $mapper = $this->container->get(UserMapper::class); - - $preferences = ['k' => $faker->word()]; - $preferencesJson = json_encode($preferences); - - $input = [ - 'id' => $faker->numberBetween(1, 1000), - 'status' => $faker->numberBetween(0, 9), - 'email' => $faker->safeEmail(), - 'password' => $faker->password(), - 'namePrefix' => $faker->optional()->title(), - 'nameFirst' => $faker->firstName(), - 'nameMiddle' => $faker->optional()->word(), - 'nameLast' => $faker->lastName(), - 'nameSuffix' => null, - 'issuer' => $faker->company(), - 'tokenPassword' => $faker->optional()->password(), - 'tokenId' => $faker->uuid(), - 'preferences' => $preferencesJson, - 'createdDate' => $faker->date(), - 'createdUserId' => $faker->numberBetween(1, 1000), - 'updatedDate' => $faker->date(), - 'updatedUserId' => $faker->numberBetween(1, 1000), - ]; - - $user = $mapper->input($input); - - $expected = $input['id']; - $actual = $user->id; - $this->assertSame($expected, $actual); - - $expected = $input['status']; - $actual = $user->status; - $this->assertSame($expected, $actual); - - $expected = $input['email']; - $actual = $user->email; - $this->assertSame($expected, $actual); - - $expected = $input['password']; - $actual = $user->password; - $this->assertSame($expected, $actual); - - $expected = $input['namePrefix']; - $actual = $user->namePrefix; - $this->assertSame($expected, $actual); - - $expected = $input['nameFirst']; - $actual = $user->nameFirst; - $this->assertSame($expected, $actual); - - $expected = $input['nameMiddle']; - $actual = $user->nameMiddle; - $this->assertSame($expected, $actual); - - $expected = $input['nameLast']; - $actual = $user->nameLast; - $this->assertSame($expected, $actual); - - $expected = null; - $actual = $user->nameSuffix; - $this->assertSame($expected, $actual); - - $expected = $input['issuer']; - $actual = $user->issuer; - $this->assertSame($expected, $actual); - - $expected = $input['tokenPassword']; - $actual = $user->tokenPassword; - $this->assertSame($expected, $actual); - - $expected = $input['tokenId']; - $actual = $user->tokenId; - $this->assertSame($expected, $actual); - - $expected = $input['preferences']; - $actual = $user->preferences; - $this->assertSame($expected, $actual); - - $expected = $input['createdDate']; - $actual = $user->createdDate; - $this->assertSame($expected, $actual); - - $expected = $input['createdUserId']; - $actual = $user->createdUserId; - $this->assertSame($expected, $actual); - - $expected = $input['updatedDate']; - $actual = $user->updatedDate; - $this->assertSame($expected, $actual); - - $expected = $input['updatedUserId']; - $actual = $user->updatedUserId; - $this->assertSame($expected, $actual); - } } diff --git a/tests/Unit/Domain/Infrastructure/DataSource/User/Sanitizer/UserSanitizerEnumTest.php b/tests/Unit/Domain/Infrastructure/DataSource/User/Sanitizer/UserSanitizerEnumTest.php new file mode 100644 index 0000000..816c868 --- /dev/null +++ b/tests/Unit/Domain/Infrastructure/DataSource/User/Sanitizer/UserSanitizerEnumTest.php @@ -0,0 +1,172 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Phalcon\Api\Tests\Unit\Domain\Infrastructure\DataSource\User\Sanitizer; + +use Phalcon\Api\Domain\Infrastructure\DataSource\User\Sanitizer\UserSanitizerEnum; +use Phalcon\Api\Tests\AbstractUnitTestCase; +use Phalcon\Filter\Filter; + +final class UserSanitizerEnumTest extends AbstractUnitTestCase +{ + public function testEnum(): void + { + $expected = 17; + $actual = UserSanitizerEnum::cases(); + $this->assertCount($expected, $actual); + + $expected = Filter::FILTER_ABSINT; + $actual = UserSanitizerEnum::id->sanitizer(); + $this->assertSame($expected, $actual); + + $expected = 0; + $actual = UserSanitizerEnum::id->default(); + $this->assertSame($expected, $actual); + + $expected = Filter::FILTER_EMAIL; + $actual = UserSanitizerEnum::email->sanitizer(); + $this->assertSame($expected, $actual); + + $expected = null; + $actual = UserSanitizerEnum::email->default(); + $this->assertSame($expected, $actual); + + $expected = Filter::FILTER_ABSINT; + $actual = UserSanitizerEnum::status->sanitizer(); + $this->assertSame($expected, $actual); + + $expected = 0; + $actual = UserSanitizerEnum::status->default(); + $this->assertSame($expected, $actual); + + $expected = Filter::FILTER_EMAIL; + $actual = UserSanitizerEnum::email->sanitizer(); + $this->assertSame($expected, $actual); + + $expected = null; + $actual = UserSanitizerEnum::email->default(); + $this->assertSame($expected, $actual); + + $expected = ''; + $actual = UserSanitizerEnum::password->sanitizer(); + $this->assertSame($expected, $actual); + + $expected = null; + $actual = UserSanitizerEnum::password->default(); + $this->assertSame($expected, $actual); + + $expected = Filter::FILTER_STRIPTAGS; + $actual = UserSanitizerEnum::namePrefix->sanitizer(); + $this->assertSame($expected, $actual); + + $expected = null; + $actual = UserSanitizerEnum::namePrefix->default(); + $this->assertSame($expected, $actual); + + $expected = Filter::FILTER_STRIPTAGS; + $actual = UserSanitizerEnum::nameFirst->sanitizer(); + $this->assertSame($expected, $actual); + + $expected = null; + $actual = UserSanitizerEnum::nameFirst->default(); + $this->assertSame($expected, $actual); + + $expected = Filter::FILTER_STRIPTAGS; + $actual = UserSanitizerEnum::nameLast->sanitizer(); + $this->assertSame($expected, $actual); + + $expected = null; + $actual = UserSanitizerEnum::nameLast->default(); + $this->assertSame($expected, $actual); + + $expected = Filter::FILTER_STRIPTAGS; + $actual = UserSanitizerEnum::nameMiddle->sanitizer(); + $this->assertSame($expected, $actual); + + $expected = null; + $actual = UserSanitizerEnum::nameMiddle->default(); + $this->assertSame($expected, $actual); + + $expected = Filter::FILTER_STRIPTAGS; + $actual = UserSanitizerEnum::nameSuffix->sanitizer(); + $this->assertSame($expected, $actual); + + $expected = null; + $actual = UserSanitizerEnum::nameSuffix->default(); + $this->assertSame($expected, $actual); + + $expected = ''; + $actual = UserSanitizerEnum::issuer->sanitizer(); + $this->assertSame($expected, $actual); + + $expected = null; + $actual = UserSanitizerEnum::issuer->default(); + $this->assertSame($expected, $actual); + + $expected = ''; + $actual = UserSanitizerEnum::tokenPassword->sanitizer(); + $this->assertSame($expected, $actual); + + $expected = null; + $actual = UserSanitizerEnum::tokenPassword->default(); + $this->assertSame($expected, $actual); + + $expected = ''; + $actual = UserSanitizerEnum::tokenId->sanitizer(); + $this->assertSame($expected, $actual); + + $expected = null; + $actual = UserSanitizerEnum::tokenId->default(); + $this->assertSame($expected, $actual); + + $expected = Filter::FILTER_STRIPTAGS; + $actual = UserSanitizerEnum::preferences->sanitizer(); + $this->assertSame($expected, $actual); + + $expected = null; + $actual = UserSanitizerEnum::preferences->default(); + $this->assertSame($expected, $actual); + + $expected = Filter::FILTER_ABSINT; + $actual = UserSanitizerEnum::createdUserId->sanitizer(); + $this->assertSame($expected, $actual); + + $expected = 0; + $actual = UserSanitizerEnum::createdUserId->default(); + $this->assertSame($expected, $actual); + + $expected = Filter::FILTER_STRIPTAGS; + $actual = UserSanitizerEnum::createdDate->sanitizer(); + $this->assertSame($expected, $actual); + + $expected = null; + $actual = UserSanitizerEnum::createdDate->default(); + $this->assertSame($expected, $actual); + + $expected = Filter::FILTER_ABSINT; + $actual = UserSanitizerEnum::updatedUserId->sanitizer(); + $this->assertSame($expected, $actual); + + $expected = 0; + $actual = UserSanitizerEnum::updatedUserId->default(); + $this->assertSame($expected, $actual); + + $expected = Filter::FILTER_STRIPTAGS; + $actual = UserSanitizerEnum::updatedDate->sanitizer(); + $this->assertSame($expected, $actual); + + $expected = null; + $actual = UserSanitizerEnum::updatedDate->default(); + $this->assertSame($expected, $actual); + } +} diff --git a/tests/Unit/Domain/Infrastructure/DataSource/User/Sanitizer/UserSanitizerTest.php b/tests/Unit/Domain/Infrastructure/DataSource/User/Sanitizer/UserSanitizerTest.php index f9252cc..170aa25 100644 --- a/tests/Unit/Domain/Infrastructure/DataSource/User/Sanitizer/UserSanitizerTest.php +++ b/tests/Unit/Domain/Infrastructure/DataSource/User/Sanitizer/UserSanitizerTest.php @@ -85,7 +85,7 @@ public function testObject(): void 'nameLast' => 'some ', 'nameMiddle' => 'some ', 'nameSuffix' => 'some ', - 'issuer' => 'some ', + 'issuer' => 'some ', 'tokenPassword' => 'some ', 'tokenId' => 'some ', 'preferences' => 'some ', diff --git a/tests/Unit/Domain/Infrastructure/DataSource/User/Validator/UserInsertEnumTest.php b/tests/Unit/Domain/Infrastructure/DataSource/User/Validator/UserInsertEnumTest.php new file mode 100644 index 0000000..10dc2b1 --- /dev/null +++ b/tests/Unit/Domain/Infrastructure/DataSource/User/Validator/UserInsertEnumTest.php @@ -0,0 +1,80 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Phalcon\Api\Tests\Unit\Domain\Infrastructure\DataSource\User\Validator; + +use Phalcon\Api\Domain\Infrastructure\DataSource\User\Validator\UserInsertEnum; +use Phalcon\Api\Tests\AbstractUnitTestCase; +use Phalcon\Filter\Validation\Validator\Email; +use Phalcon\Filter\Validation\Validator\PresenceOf; + +final class UserInsertEnumTest extends AbstractUnitTestCase +{ + public function testEnum(): void + { + $expected = 5; + $actual = UserInsertEnum::cases(); + $this->assertCount($expected, $actual); + + $expected = [ + PresenceOf::class, + Email::class, + ]; + $actual = UserInsertEnum::email->validators(); + $this->assertSame($expected, $actual); + + $expected = false; + $actual = UserInsertEnum::email->allowEmpty(); + $this->assertSame($expected, $actual); + + $expected = [ + PresenceOf::class, + ]; + $actual = UserInsertEnum::password->validators(); + $this->assertSame($expected, $actual); + + $expected = false; + $actual = UserInsertEnum::password->allowEmpty(); + $this->assertSame($expected, $actual); + + $expected = [ + PresenceOf::class, + ]; + $actual = UserInsertEnum::issuer->validators(); + $this->assertSame($expected, $actual); + + $expected = false; + $actual = UserInsertEnum::issuer->allowEmpty(); + $this->assertSame($expected, $actual); + + $expected = [ + PresenceOf::class, + ]; + $actual = UserInsertEnum::tokenPassword->validators(); + $this->assertSame($expected, $actual); + + $expected = false; + $actual = UserInsertEnum::tokenPassword->allowEmpty(); + $this->assertSame($expected, $actual); + + $expected = [ + PresenceOf::class, + ]; + $actual = UserInsertEnum::tokenId->validators(); + $this->assertSame($expected, $actual); + + $expected = false; + $actual = UserInsertEnum::tokenId->allowEmpty(); + $this->assertSame($expected, $actual); + } +} diff --git a/tests/Unit/Domain/Infrastructure/DataSource/User/Validator/UserUpdateEnumTest.php b/tests/Unit/Domain/Infrastructure/DataSource/User/Validator/UserUpdateEnumTest.php new file mode 100644 index 0000000..68c42e5 --- /dev/null +++ b/tests/Unit/Domain/Infrastructure/DataSource/User/Validator/UserUpdateEnumTest.php @@ -0,0 +1,83 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Phalcon\Api\Tests\Unit\Domain\Infrastructure\DataSource\User\Validator; + +use Phalcon\Api\Domain\Infrastructure\DataSource\User\Validator\UserUpdateEnum; +use Phalcon\Api\Domain\Infrastructure\DataSource\Validation\AbsInt; +use Phalcon\Api\Tests\AbstractUnitTestCase; +use Phalcon\Filter\Validation\Validator\Email; +use Phalcon\Filter\Validation\Validator\PresenceOf; + +final class UserUpdateEnumTest extends AbstractUnitTestCase +{ + public function testEnum(): void + { + $expected = 6; + $actual = UserUpdateEnum::cases(); + $this->assertCount($expected, $actual); + + $expected = [ + PresenceOf::class, + AbsInt::class, + ]; + $actual = UserUpdateEnum::id->validators(); + $this->assertSame($expected, $actual); + + $expected = false; + $actual = UserUpdateEnum::id->allowEmpty(); + $this->assertSame($expected, $actual); + + $expected = [ + Email::class, + ]; + $actual = UserUpdateEnum::email->validators(); + $this->assertSame($expected, $actual); + + $expected = true; + $actual = UserUpdateEnum::email->allowEmpty(); + $this->assertSame($expected, $actual); + + $expected = []; + $actual = UserUpdateEnum::password->validators(); + $this->assertSame($expected, $actual); + + $expected = true; + $actual = UserUpdateEnum::password->allowEmpty(); + $this->assertSame($expected, $actual); + + $expected = []; + $actual = UserUpdateEnum::issuer->validators(); + $this->assertSame($expected, $actual); + + $expected = true; + $actual = UserUpdateEnum::issuer->allowEmpty(); + $this->assertSame($expected, $actual); + + $expected = []; + $actual = UserUpdateEnum::tokenPassword->validators(); + $this->assertSame($expected, $actual); + + $expected = true; + $actual = UserUpdateEnum::tokenPassword->allowEmpty(); + $this->assertSame($expected, $actual); + + $expected = []; + $actual = UserUpdateEnum::tokenId->validators(); + $this->assertSame($expected, $actual); + + $expected = true; + $actual = UserUpdateEnum::tokenId->allowEmpty(); + $this->assertSame($expected, $actual); + } +} diff --git a/tests/Unit/Domain/Infrastructure/DataSource/User/Validator/UserValidatorUpdateTest.php b/tests/Unit/Domain/Infrastructure/DataSource/User/Validator/UserValidatorUpdateTest.php index 4cbe259..240acfa 100644 --- a/tests/Unit/Domain/Infrastructure/DataSource/User/Validator/UserValidatorUpdateTest.php +++ b/tests/Unit/Domain/Infrastructure/DataSource/User/Validator/UserValidatorUpdateTest.php @@ -15,7 +15,6 @@ use Faker\Factory as FakerFactory; use Phalcon\Api\Domain\Application\User\Command\UserCommandFactory; -use Phalcon\Api\Domain\Infrastructure\DataSource\User\Sanitizer\UserSanitizer; use Phalcon\Api\Domain\Infrastructure\DataSource\User\Validator\UserValidatorUpdate; use Phalcon\Api\Tests\AbstractUnitTestCase; @@ -23,8 +22,6 @@ final class UserValidatorUpdateTest extends AbstractUnitTestCase { public function testError(): void { - /** @var UserSanitizer $sanitizer */ - $sanitizer = $this->container->get(UserSanitizer::class); /** @var UserCommandFactory $factory */ $factory = $this->container->get(UserCommandFactory::class); /** @var UserValidatorUpdate $validator */ diff --git a/tests/Unit/Domain/Infrastructure/Enums/Container/AuthDefinitionsEnumTest.php b/tests/Unit/Domain/Infrastructure/Enums/Container/AuthDefinitionsEnumTest.php new file mode 100644 index 0000000..a7d025d --- /dev/null +++ b/tests/Unit/Domain/Infrastructure/Enums/Container/AuthDefinitionsEnumTest.php @@ -0,0 +1,313 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Phalcon\Api\Tests\Unit\Domain\Infrastructure\Enums\Container; + +use Phalcon\Api\Domain\Application\Auth\Command\AuthCommandFactory; +use Phalcon\Api\Domain\Application\Auth\Facade\AuthFacade; +use Phalcon\Api\Domain\Application\Auth\Handler\AuthLoginPostHandler; +use Phalcon\Api\Domain\Application\Auth\Handler\AuthLogoutPostHandler; +use Phalcon\Api\Domain\Application\Auth\Handler\AuthRefreshPostHandler; +use Phalcon\Api\Domain\Application\Auth\Service\AuthLoginPostService; +use Phalcon\Api\Domain\Application\Auth\Service\AuthLogoutPostService; +use Phalcon\Api\Domain\Application\Auth\Service\AuthRefreshPostService; +use Phalcon\Api\Domain\Infrastructure\CommandBus\CommandBus; +use Phalcon\Api\Domain\Infrastructure\Container; +use Phalcon\Api\Domain\Infrastructure\DataSource\Auth\Sanitizer\AuthSanitizer; +use Phalcon\Api\Domain\Infrastructure\DataSource\Auth\Transformer\AuthTransformer; +use Phalcon\Api\Domain\Infrastructure\DataSource\Auth\Validator\AuthLoginValidator; +use Phalcon\Api\Domain\Infrastructure\DataSource\Auth\Validator\AuthTokenValidator; +use Phalcon\Api\Domain\Infrastructure\DataSource\User\Repository\UserRepository; +use Phalcon\Api\Domain\Infrastructure\Encryption\Security; +use Phalcon\Api\Domain\Infrastructure\Encryption\TokenManager; +use Phalcon\Api\Domain\Infrastructure\Enums\Container\AuthDefinitionsEnum; +use Phalcon\Api\Tests\AbstractUnitTestCase; +use Phalcon\Filter\Validation; + +final class AuthDefinitionsEnumTest extends AbstractUnitTestCase +{ + public function testDefinition(): void + { + $expected = [ + 'className' => AuthCommandFactory::class, + 'arguments' => [ + [ + 'type' => 'service', + 'name' => AuthSanitizer::class, + ], + ], + ]; + $actual = AuthDefinitionsEnum::AuthCommandFactory->definition(); + $this->assertSame($expected, $actual); + + $expected = [ + 'className' => AuthLoginPostService::class, + 'arguments' => [ + [ + 'type' => 'service', + 'name' => AuthFacade::class, + ], + ], + ]; + $actual = AuthDefinitionsEnum::AuthLoginPost->definition(); + $this->assertSame($expected, $actual); + + $expected = [ + 'className' => AuthLogoutPostService::class, + 'arguments' => [ + [ + 'type' => 'service', + 'name' => AuthFacade::class, + ], + ], + ]; + $actual = AuthDefinitionsEnum::AuthLogoutPost->definition(); + $this->assertSame($expected, $actual); + + $expected = [ + 'className' => AuthRefreshPostService::class, + 'arguments' => [ + [ + 'type' => 'service', + 'name' => AuthFacade::class, + ], + ], + ]; + $actual = AuthDefinitionsEnum::AuthRefreshPost->definition(); + $this->assertSame($expected, $actual); + + $expected = [ + 'className' => AuthFacade::class, + 'arguments' => [ + [ + 'type' => 'service', + 'name' => CommandBus::class, + ], + [ + 'type' => 'service', + 'name' => AuthCommandFactory::class, + ], + ], + ]; + $actual = AuthDefinitionsEnum::AuthFacade->definition(); + $this->assertSame($expected, $actual); + + $expected = [ + 'className' => AuthLoginPostHandler::class, + 'arguments' => [ + [ + 'type' => 'service', + 'name' => UserRepository::class, + ], + [ + 'type' => 'service', + 'name' => TokenManager::class, + ], + [ + 'type' => 'service', + 'name' => AuthTransformer::class, + ], + [ + 'type' => 'service', + 'name' => Security::class, + ], + [ + 'type' => 'service', + 'name' => AuthLoginValidator::class, + ], + ], + ]; + $actual = AuthDefinitionsEnum::AuthLoginHandler->definition(); + $this->assertSame($expected, $actual); + + $expected = [ + 'className' => AuthLogoutPostHandler::class, + 'arguments' => [ + [ + 'type' => 'service', + 'name' => TokenManager::class, + ], + [ + 'type' => 'service', + 'name' => AuthTransformer::class, + ], + [ + 'type' => 'service', + 'name' => AuthTokenValidator::class, + ], + ], + ]; + $actual = AuthDefinitionsEnum::AuthLogoutHandler->definition(); + $this->assertSame($expected, $actual); + + $expected = [ + 'className' => AuthRefreshPostHandler::class, + 'arguments' => [ + [ + 'type' => 'service', + 'name' => TokenManager::class, + ], + [ + 'type' => 'service', + 'name' => AuthTransformer::class, + ], + [ + 'type' => 'service', + 'name' => AuthTokenValidator::class, + ], + ], + ]; + $actual = AuthDefinitionsEnum::AuthRefreshHandler->definition(); + $this->assertSame($expected, $actual); + + $expected = [ + 'className' => AuthSanitizer::class, + 'arguments' => [ + [ + 'type' => 'service', + 'name' => Container::FILTER, + ], + ], + ]; + $actual = AuthDefinitionsEnum::AuthSanitizer->definition(); + $this->assertSame($expected, $actual); + + $expected = [ + 'className' => AuthLoginValidator::class, + 'arguments' => [ + [ + 'type' => 'service', + 'name' => Validation::class, + ], + ], + ]; + $actual = AuthDefinitionsEnum::AuthLoginValidator->definition(); + $this->assertSame($expected, $actual); + + $expected = [ + 'className' => AuthTokenValidator::class, + 'arguments' => [ + [ + 'type' => 'service', + 'name' => TokenManager::class, + ], + [ + 'type' => 'service', + 'name' => UserRepository::class, + ], + [ + 'type' => 'service', + 'name' => Validation::class, + ], + ], + ]; + $actual = AuthDefinitionsEnum::AuthTokenValidator->definition(); + $this->assertSame($expected, $actual); + + $expected = [ + 'className' => AuthTransformer::class, + ]; + $actual = AuthDefinitionsEnum::AuthTransformer->definition(); + $this->assertSame($expected, $actual); + } + + public function testIsShared(): void + { + $actual = AuthDefinitionsEnum::AuthCommandFactory->isShared(); + $this->assertFalse($actual); + + $actual = AuthDefinitionsEnum::AuthLoginPost->isShared(); + $this->assertFalse($actual); + + $actual = AuthDefinitionsEnum::AuthLogoutPost->isShared(); + $this->assertFalse($actual); + + $actual = AuthDefinitionsEnum::AuthRefreshPost->isShared(); + $this->assertFalse($actual); + + $actual = AuthDefinitionsEnum::AuthFacade->isShared(); + $this->assertFalse($actual); + + $actual = AuthDefinitionsEnum::AuthLoginHandler->isShared(); + $this->assertFalse($actual); + + $actual = AuthDefinitionsEnum::AuthLogoutHandler->isShared(); + $this->assertFalse($actual); + + $actual = AuthDefinitionsEnum::AuthRefreshHandler->isShared(); + $this->assertFalse($actual); + + $actual = AuthDefinitionsEnum::AuthSanitizer->isShared(); + $this->assertTrue($actual); + + $actual = AuthDefinitionsEnum::AuthLoginValidator->isShared(); + $this->assertFalse($actual); + + $actual = AuthDefinitionsEnum::AuthTokenValidator->isShared(); + $this->assertFalse($actual); + + $actual = AuthDefinitionsEnum::AuthTransformer->isShared(); + $this->assertFalse($actual); + } + + public function testValues(): void + { + $expected = AuthCommandFactory::class; + $actual = AuthDefinitionsEnum::AuthCommandFactory->value; + $this->assertSame($expected, $actual); + + $expected = AuthLoginPostService::class; + $actual = AuthDefinitionsEnum::AuthLoginPost->value; + $this->assertSame($expected, $actual); + + $expected = AuthLogoutPostService::class; + $actual = AuthDefinitionsEnum::AuthLogoutPost->value; + $this->assertSame($expected, $actual); + + $expected = AuthRefreshPostService::class; + $actual = AuthDefinitionsEnum::AuthRefreshPost->value; + $this->assertSame($expected, $actual); + + $expected = AuthFacade::class; + $actual = AuthDefinitionsEnum::AuthFacade->value; + $this->assertSame($expected, $actual); + + $expected = AuthLoginPostHandler::class; + $actual = AuthDefinitionsEnum::AuthLoginHandler->value; + $this->assertSame($expected, $actual); + + $expected = AuthLogoutPostHandler::class; + $actual = AuthDefinitionsEnum::AuthLogoutHandler->value; + $this->assertSame($expected, $actual); + + $expected = AuthRefreshPostHandler::class; + $actual = AuthDefinitionsEnum::AuthRefreshHandler->value; + $this->assertSame($expected, $actual); + + $expected = AuthSanitizer::class; + $actual = AuthDefinitionsEnum::AuthSanitizer->value; + $this->assertSame($expected, $actual); + + $expected = AuthLoginValidator::class; + $actual = AuthDefinitionsEnum::AuthLoginValidator->value; + $this->assertSame($expected, $actual); + + $expected = AuthTokenValidator::class; + $actual = AuthDefinitionsEnum::AuthTokenValidator->value; + $this->assertSame($expected, $actual); + + $expected = AuthTransformer::class; + $actual = AuthDefinitionsEnum::AuthTransformer->value; + $this->assertSame($expected, $actual); + } +} diff --git a/tests/Unit/Domain/Infrastructure/Enums/Container/CommonDefinitionsEnumTest.php b/tests/Unit/Domain/Infrastructure/Enums/Container/CommonDefinitionsEnumTest.php new file mode 100644 index 0000000..165fed2 --- /dev/null +++ b/tests/Unit/Domain/Infrastructure/Enums/Container/CommonDefinitionsEnumTest.php @@ -0,0 +1,210 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Phalcon\Api\Tests\Unit\Domain\Infrastructure\Enums\Container; + +use Phalcon\Api\Domain\Infrastructure\CommandBus\CommandBus; +use Phalcon\Api\Domain\Infrastructure\CommandBus\ContainerHandlerLocator; +use Phalcon\Api\Domain\Infrastructure\Container; +use Phalcon\Api\Domain\Infrastructure\Encryption\JWTToken; +use Phalcon\Api\Domain\Infrastructure\Encryption\TokenCache; +use Phalcon\Api\Domain\Infrastructure\Encryption\TokenManager; +use Phalcon\Api\Domain\Infrastructure\Enums\Container\CommonDefinitionsEnum; +use Phalcon\Api\Domain\Infrastructure\Env\EnvManager; +use Phalcon\Api\Responder\JsonResponder; +use Phalcon\Api\Tests\AbstractUnitTestCase; +use Phalcon\Cache\Cache; +use Phalcon\Http\Request; +use Phalcon\Http\Response; +use Phalcon\Mvc\Router; +use Phalcon\Support\Registry; + +final class CommonDefinitionsEnumTest extends AbstractUnitTestCase +{ + public function testDefinition(): void + { + $expected = [ + 'className' => CommandBus::class, + 'arguments' => [ + [ + 'type' => 'service', + 'name' => ContainerHandlerLocator::class, + ], + ], + ]; + $actual = CommonDefinitionsEnum::CommandBus->definition(); + $this->assertSame($expected, $actual); + + $expected = [ + 'className' => EnvManager::class, + ]; + $actual = CommonDefinitionsEnum::EnvManager->definition(); + $this->assertSame($expected, $actual); + + $expected = [ + 'className' => JsonResponder::class, + ]; + $actual = CommonDefinitionsEnum::JsonResponder->definition(); + $this->assertSame($expected, $actual); + + $expected = [ + 'className' => JWTToken::class, + 'arguments' => [ + [ + 'type' => 'service', + 'name' => EnvManager::class, + ], + ], + ]; + $actual = CommonDefinitionsEnum::JWTToken->definition(); + $this->assertSame($expected, $actual); + + $expected = [ + 'className' => TokenCache::class, + 'arguments' => [ + [ + 'type' => 'service', + 'name' => Cache::class, + ], + ], + ]; + $actual = CommonDefinitionsEnum::JWTTokenCache->definition(); + $this->assertSame($expected, $actual); + + $expected = [ + 'className' => TokenManager::class, + 'arguments' => [ + [ + 'type' => 'service', + 'name' => TokenCache::class, + ], + [ + 'type' => 'service', + 'name' => EnvManager::class, + ], + [ + 'type' => 'service', + 'name' => JWTToken::class, + ], + ], + ]; + $actual = CommonDefinitionsEnum::JWTTokenManager->definition(); + $this->assertSame($expected, $actual); + + $expected = [ + 'className' => Registry::class, + ]; + $actual = CommonDefinitionsEnum::Registry->definition(); + $this->assertSame($expected, $actual); + + $expected = [ + 'className' => Request::class, + ]; + $actual = CommonDefinitionsEnum::Request->definition(); + $this->assertSame($expected, $actual); + + $expected = [ + 'className' => Response::class, + ]; + $actual = CommonDefinitionsEnum::Response->definition(); + $this->assertSame($expected, $actual); + + $expected = [ + 'className' => Router::class, + 'arguments' => [ + [ + 'type' => 'parameter', + 'value' => false, + ], + ], + ]; + $actual = CommonDefinitionsEnum::Router->definition(); + $this->assertSame($expected, $actual); + } + + public function testIsShared(): void + { + $actual = CommonDefinitionsEnum::CommandBus->isShared(); + $this->assertTrue($actual); + + $actual = CommonDefinitionsEnum::EnvManager->isShared(); + $this->assertTrue($actual); + + $actual = CommonDefinitionsEnum::JsonResponder->isShared(); + $this->assertTrue($actual); + + $actual = CommonDefinitionsEnum::JWTToken->isShared(); + $this->assertTrue($actual); + + $actual = CommonDefinitionsEnum::JWTTokenCache->isShared(); + $this->assertTrue($actual); + + $actual = CommonDefinitionsEnum::JWTTokenManager->isShared(); + $this->assertTrue($actual); + + $actual = CommonDefinitionsEnum::Registry->isShared(); + $this->assertTrue($actual); + + $actual = CommonDefinitionsEnum::Request->isShared(); + $this->assertTrue($actual); + + $actual = CommonDefinitionsEnum::Response->isShared(); + $this->assertTrue($actual); + + $actual = CommonDefinitionsEnum::Router->isShared(); + $this->assertTrue($actual); + } + + public function testValues(): void + { + $expected = CommandBus::class; + $actual = CommonDefinitionsEnum::CommandBus->value; + $this->assertSame($expected, $actual); + + $expected = EnvManager::class; + $actual = CommonDefinitionsEnum::EnvManager->value; + $this->assertSame($expected, $actual); + + $expected = JsonResponder::class; + $actual = CommonDefinitionsEnum::JsonResponder->value; + $this->assertSame($expected, $actual); + + $expected = JWTToken::class; + $actual = CommonDefinitionsEnum::JWTToken->value; + $this->assertSame($expected, $actual); + + $expected = TokenCache::class; + $actual = CommonDefinitionsEnum::JWTTokenCache->value; + $this->assertSame($expected, $actual); + + $expected = TokenManager::class; + $actual = CommonDefinitionsEnum::JWTTokenManager->value; + $this->assertSame($expected, $actual); + + $expected = Registry::class; + $actual = CommonDefinitionsEnum::Registry->value; + $this->assertSame($expected, $actual); + + $expected = Container::REQUEST; + $actual = CommonDefinitionsEnum::Request->value; + $this->assertSame($expected, $actual); + + $expected = Container::RESPONSE; + $actual = CommonDefinitionsEnum::Response->value; + $this->assertSame($expected, $actual); + + $expected = Container::ROUTER; + $actual = CommonDefinitionsEnum::Router->value; + $this->assertSame($expected, $actual); + } +} diff --git a/tests/Unit/Domain/Infrastructure/Enums/Container/CompanyDefinitionsEnumTest.php b/tests/Unit/Domain/Infrastructure/Enums/Container/CompanyDefinitionsEnumTest.php new file mode 100644 index 0000000..90d908b --- /dev/null +++ b/tests/Unit/Domain/Infrastructure/Enums/Container/CompanyDefinitionsEnumTest.php @@ -0,0 +1,431 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Phalcon\Api\Tests\Unit\Domain\Infrastructure\Enums\Container; + +use Phalcon\Api\Domain\Application\Company\Command\CompanyCommandFactory; +use Phalcon\Api\Domain\Application\Company\Facade\CompanyFacade; +use Phalcon\Api\Domain\Application\Company\Handler\CompanyDeleteHandler; +use Phalcon\Api\Domain\Application\Company\Handler\CompanyGetHandler; +use Phalcon\Api\Domain\Application\Company\Handler\CompanyGetManyHandler; +use Phalcon\Api\Domain\Application\Company\Handler\CompanyPostHandler; +use Phalcon\Api\Domain\Application\Company\Handler\CompanyPutHandler; +use Phalcon\Api\Domain\Application\Company\Service\CompanyDeleteService; +use Phalcon\Api\Domain\Application\Company\Service\CompanyGetManyService; +use Phalcon\Api\Domain\Application\Company\Service\CompanyGetService; +use Phalcon\Api\Domain\Application\Company\Service\CompanyPostService; +use Phalcon\Api\Domain\Application\Company\Service\CompanyPutService; +use Phalcon\Api\Domain\Infrastructure\CommandBus\CommandBus; +use Phalcon\Api\Domain\Infrastructure\Container; +use Phalcon\Api\Domain\Infrastructure\DataSource\Company\Mapper\CompanyMapper; +use Phalcon\Api\Domain\Infrastructure\DataSource\Company\Repository\CompanyRepository; +use Phalcon\Api\Domain\Infrastructure\DataSource\Company\Sanitizer\CompanySanitizer; +use Phalcon\Api\Domain\Infrastructure\DataSource\Company\Validator\CompanyValidator; +use Phalcon\Api\Domain\Infrastructure\DataSource\Company\Validator\CompanyValidatorUpdate; +use Phalcon\Api\Domain\Infrastructure\DataSource\Transformer\Transformer; +use Phalcon\Api\Domain\Infrastructure\Encryption\Security; +use Phalcon\Api\Domain\Infrastructure\Enums\Container\CompanyDefinitionsEnum; +use Phalcon\Api\Tests\AbstractUnitTestCase; +use Phalcon\DataMapper\Pdo\Connection; +use Phalcon\Filter\Validation; +use Phalcon\Support\Registry; + +final class CompanyDefinitionsEnumTest extends AbstractUnitTestCase +{ + public function testDefinition(): void + { + $expected = [ + 'className' => CompanyCommandFactory::class, + 'arguments' => [ + [ + 'type' => 'service', + 'name' => CompanySanitizer::class, + ], + ], + ]; + $actual = CompanyDefinitionsEnum::CompanyCommandFactory->definition(); + $this->assertSame($expected, $actual); + + $expected = [ + 'className' => CompanyDeleteService::class, + 'arguments' => [ + [ + 'type' => 'service', + 'name' => CompanyFacade::class, + ], + ], + ]; + $actual = CompanyDefinitionsEnum::CompanyDelete->definition(); + $this->assertSame($expected, $actual); + + $expected = [ + 'className' => CompanyGetService::class, + 'arguments' => [ + [ + 'type' => 'service', + 'name' => CompanyFacade::class, + ], + ], + ]; + $actual = CompanyDefinitionsEnum::CompanyGet->definition(); + $this->assertSame($expected, $actual); + + $expected = [ + 'className' => CompanyGetManyService::class, + 'arguments' => [ + [ + 'type' => 'service', + 'name' => CompanyFacade::class, + ], + ], + ]; + $actual = CompanyDefinitionsEnum::CompanyGetMany->definition(); + $this->assertSame($expected, $actual); + + $expected = [ + 'className' => CompanyPostService::class, + 'arguments' => [ + [ + 'type' => 'service', + 'name' => CompanyFacade::class, + ], + ], + ]; + $actual = CompanyDefinitionsEnum::CompanyPost->definition(); + $this->assertSame($expected, $actual); + + $expected = [ + 'className' => CompanyPutService::class, + 'arguments' => [ + [ + 'type' => 'service', + 'name' => CompanyFacade::class, + ], + ], + ]; + $actual = CompanyDefinitionsEnum::CompanyPut->definition(); + $this->assertSame($expected, $actual); + + $expected = [ + 'className' => CompanyFacade::class, + 'arguments' => [ + [ + 'type' => 'service', + 'name' => CommandBus::class, + ], + [ + 'type' => 'service', + 'name' => CompanyCommandFactory::class, + ], + ], + ]; + $actual = CompanyDefinitionsEnum::CompanyFacade->definition(); + $this->assertSame($expected, $actual); + + $expected = [ + 'className' => CompanyDeleteHandler::class, + 'arguments' => [ + [ + 'type' => 'service', + 'name' => CompanyRepository::class, + ], + [ + 'type' => 'service', + 'name' => Transformer::class, + ], + ], + ]; + $actual = CompanyDefinitionsEnum::CompanyDeleteHandler->definition(); + $this->assertSame($expected, $actual); + + $expected = [ + 'className' => CompanyGetHandler::class, + 'arguments' => [ + [ + 'type' => 'service', + 'name' => CompanyRepository::class, + ], + [ + 'type' => 'service', + 'name' => Transformer::class, + ], + ], + ]; + $actual = CompanyDefinitionsEnum::CompanyGetHandler->definition(); + $this->assertSame($expected, $actual); + + $expected = [ + 'className' => CompanyGetManyHandler::class, + 'arguments' => [ + [ + 'type' => 'service', + 'name' => CompanyRepository::class, + ], + [ + 'type' => 'service', + 'name' => Transformer::class, + ], + ], + ]; + $actual = CompanyDefinitionsEnum::CompanyGetManyHandler->definition(); + $this->assertSame($expected, $actual); + + $expected = [ + 'className' => CompanyPostHandler::class, + 'arguments' => [ + [ + 'type' => 'service', + 'name' => CompanyValidator::class, + ], + [ + 'type' => 'service', + 'name' => CompanyMapper::class, + ], + [ + 'type' => 'service', + 'name' => CompanyRepository::class, + ], + [ + 'type' => 'service', + 'name' => Container::EVENTS_MANAGER, + ], + [ + 'type' => 'service', + 'name' => Transformer::class, + ], + [ + 'type' => 'service', + 'name' => Registry::class, + ], + [ + 'type' => 'service', + 'name' => Security::class, + ], + ], + ]; + $actual = CompanyDefinitionsEnum::CompanyPostHandler->definition(); + $this->assertSame($expected, $actual); + + $expected = [ + 'className' => CompanyPutHandler::class, + 'arguments' => [ + [ + 'type' => 'service', + 'name' => CompanyValidatorUpdate::class, + ], + [ + 'type' => 'service', + 'name' => CompanyMapper::class, + ], + [ + 'type' => 'service', + 'name' => CompanyRepository::class, + ], + [ + 'type' => 'service', + 'name' => Container::EVENTS_MANAGER, + ], + [ + 'type' => 'service', + 'name' => Transformer::class, + ], + [ + 'type' => 'service', + 'name' => Registry::class, + ], + [ + 'type' => 'service', + 'name' => Security::class, + ], + ], + ]; + $actual = CompanyDefinitionsEnum::CompanyPutHandler->definition(); + $this->assertSame($expected, $actual); + + $expected = [ + 'className' => CompanyMapper::class, + ]; + $actual = CompanyDefinitionsEnum::CompanyMapper->definition(); + $this->assertSame($expected, $actual); + + $expected = [ + 'className' => CompanyRepository::class, + 'arguments' => [ + [ + 'type' => 'service', + 'name' => Connection::class, + ], + [ + 'type' => 'service', + 'name' => CompanyMapper::class, + ], + ], + ]; + $actual = CompanyDefinitionsEnum::CompanyRepository->definition(); + $this->assertSame($expected, $actual); + + $expected = [ + 'className' => CompanySanitizer::class, + 'arguments' => [ + [ + 'type' => 'service', + 'name' => Container::FILTER, + ], + ], + ]; + $actual = CompanyDefinitionsEnum::CompanySanitizer->definition(); + $this->assertSame($expected, $actual); + + $expected = [ + 'className' => CompanyValidator::class, + 'arguments' => [ + [ + 'type' => 'service', + 'name' => Validation::class, + ], + ], + ]; + $actual = CompanyDefinitionsEnum::CompanyValidator->definition(); + $this->assertSame($expected, $actual); + + $expected = [ + 'className' => CompanyValidatorUpdate::class, + 'arguments' => [ + [ + 'type' => 'service', + 'name' => Validation::class, + ], + ], + ]; + $actual = CompanyDefinitionsEnum::CompanyValidatorUpdate->definition(); + $this->assertSame($expected, $actual); + } + + public function testIsShared(): void + { + $actual = CompanyDefinitionsEnum::CompanyCommandFactory->isShared(); + $this->assertFalse($actual); + + $actual = CompanyDefinitionsEnum::CompanyDelete->isShared(); + $this->assertFalse($actual); + + $actual = CompanyDefinitionsEnum::CompanyGet->isShared(); + $this->assertFalse($actual); + + $actual = CompanyDefinitionsEnum::CompanyGetMany->isShared(); + $this->assertFalse($actual); + + $actual = CompanyDefinitionsEnum::CompanyPost->isShared(); + $this->assertFalse($actual); + + $actual = CompanyDefinitionsEnum::CompanyPut->isShared(); + $this->assertFalse($actual); + + $actual = CompanyDefinitionsEnum::CompanyFacade->isShared(); + $this->assertFalse($actual); + + $actual = CompanyDefinitionsEnum::CompanyDeleteHandler->isShared(); + $this->assertFalse($actual); + + $actual = CompanyDefinitionsEnum::CompanyGetHandler->isShared(); + $this->assertFalse($actual); + + $actual = CompanyDefinitionsEnum::CompanyPostHandler->isShared(); + $this->assertFalse($actual); + + $actual = CompanyDefinitionsEnum::CompanyPutHandler->isShared(); + $this->assertFalse($actual); + + $actual = CompanyDefinitionsEnum::CompanyMapper->isShared(); + $this->assertTrue($actual); + + $actual = CompanyDefinitionsEnum::CompanyRepository->isShared(); + $this->assertTrue($actual); + + $actual = CompanyDefinitionsEnum::CompanySanitizer->isShared(); + $this->assertTrue($actual); + + $actual = CompanyDefinitionsEnum::CompanyValidator->isShared(); + $this->assertFalse($actual); + + $actual = CompanyDefinitionsEnum::CompanyValidatorUpdate->isShared(); + $this->assertFalse($actual); + } + + public function testValue(): void + { + $expected = CompanyCommandFactory::class; + $actual = CompanyDefinitionsEnum::CompanyCommandFactory->value; + $this->assertSame($expected, $actual); + + $expected = CompanyDeleteService::class; + $actual = CompanyDefinitionsEnum::CompanyDelete->value; + $this->assertSame($expected, $actual); + + $expected = CompanyGetService::class; + $actual = CompanyDefinitionsEnum::CompanyGet->value; + $this->assertSame($expected, $actual); + + $expected = CompanyGetManyService::class; + $actual = CompanyDefinitionsEnum::CompanyGetMany->value; + $this->assertSame($expected, $actual); + + $expected = CompanyPostService::class; + $actual = CompanyDefinitionsEnum::CompanyPost->value; + $this->assertSame($expected, $actual); + + $expected = CompanyPutService::class; + $actual = CompanyDefinitionsEnum::CompanyPut->value; + $this->assertSame($expected, $actual); + + $expected = CompanyFacade::class; + $actual = CompanyDefinitionsEnum::CompanyFacade->value; + $this->assertSame($expected, $actual); + + $expected = CompanyDeleteHandler::class; + $actual = CompanyDefinitionsEnum::CompanyDeleteHandler->value; + $this->assertSame($expected, $actual); + + $expected = CompanyGetHandler::class; + $actual = CompanyDefinitionsEnum::CompanyGetHandler->value; + $this->assertSame($expected, $actual); + + $expected = CompanyPostHandler::class; + $actual = CompanyDefinitionsEnum::CompanyPostHandler->value; + $this->assertSame($expected, $actual); + + $expected = CompanyPutHandler::class; + $actual = CompanyDefinitionsEnum::CompanyPutHandler->value; + $this->assertSame($expected, $actual); + + $expected = CompanyMapper::class; + $actual = CompanyDefinitionsEnum::CompanyMapper->value; + $this->assertSame($expected, $actual); + + $expected = CompanyRepository::class; + $actual = CompanyDefinitionsEnum::CompanyRepository->value; + $this->assertSame($expected, $actual); + + $expected = CompanySanitizer::class; + $actual = CompanyDefinitionsEnum::CompanySanitizer->value; + $this->assertSame($expected, $actual); + + $expected = CompanyValidator::class; + $actual = CompanyDefinitionsEnum::CompanyValidator->value; + $this->assertSame($expected, $actual); + + $expected = CompanyValidatorUpdate::class; + $actual = CompanyDefinitionsEnum::CompanyValidatorUpdate->value; + $this->assertSame($expected, $actual); + } +} diff --git a/tests/Unit/Domain/Infrastructure/Enums/Container/UserDefinitionsEnumTest.php b/tests/Unit/Domain/Infrastructure/Enums/Container/UserDefinitionsEnumTest.php new file mode 100644 index 0000000..028f38d --- /dev/null +++ b/tests/Unit/Domain/Infrastructure/Enums/Container/UserDefinitionsEnumTest.php @@ -0,0 +1,394 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Phalcon\Api\Tests\Unit\Domain\Infrastructure\Enums\Container; + +use Phalcon\Api\Domain\Application\User\Command\UserCommandFactory; +use Phalcon\Api\Domain\Application\User\Facade\UserFacade; +use Phalcon\Api\Domain\Application\User\Handler\UserDeleteHandler; +use Phalcon\Api\Domain\Application\User\Handler\UserGetHandler; +use Phalcon\Api\Domain\Application\User\Handler\UserPostHandler; +use Phalcon\Api\Domain\Application\User\Handler\UserPutHandler; +use Phalcon\Api\Domain\Application\User\Service\UserDeleteService; +use Phalcon\Api\Domain\Application\User\Service\UserGetService; +use Phalcon\Api\Domain\Application\User\Service\UserPostService; +use Phalcon\Api\Domain\Application\User\Service\UserPutService; +use Phalcon\Api\Domain\Infrastructure\CommandBus\CommandBus; +use Phalcon\Api\Domain\Infrastructure\Container; +use Phalcon\Api\Domain\Infrastructure\DataSource\Transformer\Transformer; +use Phalcon\Api\Domain\Infrastructure\DataSource\User\Mapper\UserMapper; +use Phalcon\Api\Domain\Infrastructure\DataSource\User\Repository\UserRepository; +use Phalcon\Api\Domain\Infrastructure\DataSource\User\Sanitizer\UserSanitizer; +use Phalcon\Api\Domain\Infrastructure\DataSource\User\Validator\UserValidator; +use Phalcon\Api\Domain\Infrastructure\DataSource\User\Validator\UserValidatorUpdate; +use Phalcon\Api\Domain\Infrastructure\Encryption\Security; +use Phalcon\Api\Domain\Infrastructure\Enums\Container\UserDefinitionsEnum; +use Phalcon\Api\Tests\AbstractUnitTestCase; +use Phalcon\DataMapper\Pdo\Connection; +use Phalcon\Filter\Validation; +use Phalcon\Support\Registry; + +final class UserDefinitionsEnumTest extends AbstractUnitTestCase +{ + public function testDefinition(): void + { + $expected = [ + 'className' => UserCommandFactory::class, + 'arguments' => [ + [ + 'type' => 'service', + 'name' => UserSanitizer::class, + ], + ], + ]; + $actual = UserDefinitionsEnum::UserCommandFactory->definition(); + $this->assertSame($expected, $actual); + + $expected = [ + 'className' => UserDeleteService::class, + 'arguments' => [ + [ + 'type' => 'service', + 'name' => UserFacade::class, + ], + ], + ]; + $actual = UserDefinitionsEnum::UserDelete->definition(); + $this->assertSame($expected, $actual); + + $expected = [ + 'className' => UserGetService::class, + 'arguments' => [ + [ + 'type' => 'service', + 'name' => UserFacade::class, + ], + ], + ]; + $actual = UserDefinitionsEnum::UserGet->definition(); + $this->assertSame($expected, $actual); + + $expected = [ + 'className' => UserPostService::class, + 'arguments' => [ + [ + 'type' => 'service', + 'name' => UserFacade::class, + ], + ], + ]; + $actual = UserDefinitionsEnum::UserPost->definition(); + $this->assertSame($expected, $actual); + + $expected = [ + 'className' => UserPutService::class, + 'arguments' => [ + [ + 'type' => 'service', + 'name' => UserFacade::class, + ], + ], + ]; + $actual = UserDefinitionsEnum::UserPut->definition(); + $this->assertSame($expected, $actual); + + $expected = [ + 'className' => UserFacade::class, + 'arguments' => [ + [ + 'type' => 'service', + 'name' => CommandBus::class, + ], + [ + 'type' => 'service', + 'name' => UserCommandFactory::class, + ], + ], + ]; + $actual = UserDefinitionsEnum::UserFacade->definition(); + $this->assertSame($expected, $actual); + + $expected = [ + 'className' => UserDeleteHandler::class, + 'arguments' => [ + [ + 'type' => 'service', + 'name' => UserRepository::class, + ], + [ + 'type' => 'service', + 'name' => Transformer::class, + ], + ], + ]; + $actual = UserDefinitionsEnum::UserDeleteHandler->definition(); + $this->assertSame($expected, $actual); + + $expected = [ + 'className' => UserGetHandler::class, + 'arguments' => [ + [ + 'type' => 'service', + 'name' => UserRepository::class, + ], + [ + 'type' => 'service', + 'name' => Transformer::class, + ], + ], + ]; + $actual = UserDefinitionsEnum::UserGetHandler->definition(); + $this->assertSame($expected, $actual); + + $expected = [ + 'className' => UserPostHandler::class, + 'arguments' => [ + [ + 'type' => 'service', + 'name' => UserValidator::class, + ], + [ + 'type' => 'service', + 'name' => UserMapper::class, + ], + [ + 'type' => 'service', + 'name' => UserRepository::class, + ], + [ + 'type' => 'service', + 'name' => Container::EVENTS_MANAGER, + ], + [ + 'type' => 'service', + 'name' => Transformer::class, + ], + [ + 'type' => 'service', + 'name' => Registry::class, + ], + [ + 'type' => 'service', + 'name' => Security::class, + ], + ], + ]; + $actual = UserDefinitionsEnum::UserPostHandler->definition(); + $this->assertSame($expected, $actual); + + $expected = [ + 'className' => UserPutHandler::class, + 'arguments' => [ + [ + 'type' => 'service', + 'name' => UserValidatorUpdate::class, + ], + [ + 'type' => 'service', + 'name' => UserMapper::class, + ], + [ + 'type' => 'service', + 'name' => UserRepository::class, + ], + [ + 'type' => 'service', + 'name' => Container::EVENTS_MANAGER, + ], + [ + 'type' => 'service', + 'name' => Transformer::class, + ], + [ + 'type' => 'service', + 'name' => Registry::class, + ], + [ + 'type' => 'service', + 'name' => Security::class, + ], + ], + ]; + $actual = UserDefinitionsEnum::UserPutHandler->definition(); + $this->assertSame($expected, $actual); + + $expected = [ + 'className' => UserMapper::class, + ]; + $actual = UserDefinitionsEnum::UserMapper->definition(); + $this->assertSame($expected, $actual); + + $expected = [ + 'className' => UserRepository::class, + 'arguments' => [ + [ + 'type' => 'service', + 'name' => Connection::class, + ], + [ + 'type' => 'service', + 'name' => UserMapper::class, + ], + ], + ]; + $actual = UserDefinitionsEnum::UserRepository->definition(); + $this->assertSame($expected, $actual); + + $expected = [ + 'className' => UserSanitizer::class, + 'arguments' => [ + [ + 'type' => 'service', + 'name' => Container::FILTER, + ], + ], + ]; + $actual = UserDefinitionsEnum::UserSanitizer->definition(); + $this->assertSame($expected, $actual); + + $expected = [ + 'className' => UserValidator::class, + 'arguments' => [ + [ + 'type' => 'service', + 'name' => Validation::class, + ], + ], + ]; + $actual = UserDefinitionsEnum::UserValidator->definition(); + $this->assertSame($expected, $actual); + + $expected = [ + 'className' => UserValidatorUpdate::class, + 'arguments' => [ + [ + 'type' => 'service', + 'name' => Validation::class, + ], + ], + ]; + $actual = UserDefinitionsEnum::UserValidatorUpdate->definition(); + $this->assertSame($expected, $actual); + } + + public function testIsShared(): void + { + $actual = UserDefinitionsEnum::UserCommandFactory->isShared(); + $this->assertFalse($actual); + + $actual = UserDefinitionsEnum::UserDelete->isShared(); + $this->assertFalse($actual); + + $actual = UserDefinitionsEnum::UserGet->isShared(); + $this->assertFalse($actual); + + $actual = UserDefinitionsEnum::UserPost->isShared(); + $this->assertFalse($actual); + + $actual = UserDefinitionsEnum::UserPut->isShared(); + $this->assertFalse($actual); + + $actual = UserDefinitionsEnum::UserFacade->isShared(); + $this->assertFalse($actual); + + $actual = UserDefinitionsEnum::UserDeleteHandler->isShared(); + $this->assertFalse($actual); + + $actual = UserDefinitionsEnum::UserGetHandler->isShared(); + $this->assertFalse($actual); + + $actual = UserDefinitionsEnum::UserPostHandler->isShared(); + $this->assertFalse($actual); + + $actual = UserDefinitionsEnum::UserPutHandler->isShared(); + $this->assertFalse($actual); + + $actual = UserDefinitionsEnum::UserMapper->isShared(); + $this->assertTrue($actual); + + $actual = UserDefinitionsEnum::UserRepository->isShared(); + $this->assertTrue($actual); + + $actual = UserDefinitionsEnum::UserSanitizer->isShared(); + $this->assertTrue($actual); + + $actual = UserDefinitionsEnum::UserValidator->isShared(); + $this->assertFalse($actual); + + $actual = UserDefinitionsEnum::UserValidatorUpdate->isShared(); + $this->assertFalse($actual); + } + + public function testValue(): void + { + $expected = UserCommandFactory::class; + $actual = UserDefinitionsEnum::UserCommandFactory->value; + $this->assertSame($expected, $actual); + + $expected = UserDeleteService::class; + $actual = UserDefinitionsEnum::UserDelete->value; + $this->assertSame($expected, $actual); + + $expected = UserGetService::class; + $actual = UserDefinitionsEnum::UserGet->value; + $this->assertSame($expected, $actual); + + $expected = UserPostService::class; + $actual = UserDefinitionsEnum::UserPost->value; + $this->assertSame($expected, $actual); + + $expected = UserPutService::class; + $actual = UserDefinitionsEnum::UserPut->value; + $this->assertSame($expected, $actual); + + $expected = UserFacade::class; + $actual = UserDefinitionsEnum::UserFacade->value; + $this->assertSame($expected, $actual); + + $expected = UserDeleteHandler::class; + $actual = UserDefinitionsEnum::UserDeleteHandler->value; + $this->assertSame($expected, $actual); + + $expected = UserGetHandler::class; + $actual = UserDefinitionsEnum::UserGetHandler->value; + $this->assertSame($expected, $actual); + + $expected = UserPostHandler::class; + $actual = UserDefinitionsEnum::UserPostHandler->value; + $this->assertSame($expected, $actual); + + $expected = UserPutHandler::class; + $actual = UserDefinitionsEnum::UserPutHandler->value; + $this->assertSame($expected, $actual); + + $expected = UserMapper::class; + $actual = UserDefinitionsEnum::UserMapper->value; + $this->assertSame($expected, $actual); + + $expected = UserRepository::class; + $actual = UserDefinitionsEnum::UserRepository->value; + $this->assertSame($expected, $actual); + + $expected = UserSanitizer::class; + $actual = UserDefinitionsEnum::UserSanitizer->value; + $this->assertSame($expected, $actual); + + $expected = UserValidator::class; + $actual = UserDefinitionsEnum::UserValidator->value; + $this->assertSame($expected, $actual); + + $expected = UserValidatorUpdate::class; + $actual = UserDefinitionsEnum::UserValidatorUpdate->value; + $this->assertSame($expected, $actual); + } +} diff --git a/tests/Unit/Domain/Infrastructure/Enums/Http/RoutesEnumTest.php b/tests/Unit/Domain/Infrastructure/Enums/Http/RoutesEnumTest.php index 84d1788..6263075 100644 --- a/tests/Unit/Domain/Infrastructure/Enums/Http/RoutesEnumTest.php +++ b/tests/Unit/Domain/Infrastructure/Enums/Http/RoutesEnumTest.php @@ -16,6 +16,11 @@ use Phalcon\Api\Domain\Application\Auth\Service\AuthLoginPostService; use Phalcon\Api\Domain\Application\Auth\Service\AuthLogoutPostService; use Phalcon\Api\Domain\Application\Auth\Service\AuthRefreshPostService; +use Phalcon\Api\Domain\Application\Company\Service\CompanyDeleteService; +use Phalcon\Api\Domain\Application\Company\Service\CompanyGetManyService; +use Phalcon\Api\Domain\Application\Company\Service\CompanyGetService; +use Phalcon\Api\Domain\Application\Company\Service\CompanyPostService; +use Phalcon\Api\Domain\Application\Company\Service\CompanyPutService; use Phalcon\Api\Domain\Application\User\Service\UserDeleteService; use Phalcon\Api\Domain\Application\User\Service\UserGetService; use Phalcon\Api\Domain\Application\User\Service\UserPostService; @@ -60,6 +65,46 @@ public static function getExamples(): array RoutesEnum::POST, AuthRefreshPostService::class, ], + [ + RoutesEnum::companyDelete, + '/company', + '', + '/company', + RoutesEnum::DELETE, + CompanyDeleteService::class, + ], + [ + RoutesEnum::companyGet, + '/company', + '', + '/company', + RoutesEnum::GET, + CompanyGetService::class, + ], + [ + RoutesEnum::companyGetMany, + '/company', + '/all', + '/company/all', + RoutesEnum::GET, + CompanyGetManyService::class, + ], + [ + RoutesEnum::companyPost, + '/company', + '', + '/company', + RoutesEnum::POST, + CompanyPostService::class, + ], + [ + RoutesEnum::companyPut, + '/company', + '', + '/company', + RoutesEnum::PUT, + CompanyPutService::class, + ], [ RoutesEnum::userDelete, '/user', @@ -97,7 +142,7 @@ public static function getExamples(): array public function testCheckCount(): void { - $expected = 7; + $expected = 12; $actual = RoutesEnum::cases(); $this->assertCount($expected, $actual); } diff --git a/tests/Unit/Domain/Infrastructure/Providers/RouterProviderTest.php b/tests/Unit/Domain/Infrastructure/Providers/RouterProviderTest.php index d0a993b..8b1dba1 100644 --- a/tests/Unit/Domain/Infrastructure/Providers/RouterProviderTest.php +++ b/tests/Unit/Domain/Infrastructure/Providers/RouterProviderTest.php @@ -44,6 +44,26 @@ public function testCheckRegistration(): void 'method' => 'POST', 'pattern' => '/auth/refresh', ], + [ + 'method' => 'DELETE', + 'pattern' => '/company', + ], + [ + 'method' => 'GET', + 'pattern' => '/company', + ], + [ + 'method' => 'GET', + 'pattern' => '/company/all', + ], + [ + 'method' => 'POST', + 'pattern' => '/company', + ], + [ + 'method' => 'PUT', + 'pattern' => '/company', + ], [ 'method' => 'DELETE', 'pattern' => '/user',