Skip to content

Commit

Permalink
Fixed parameter doc block for configuration of components (#56)
Browse files Browse the repository at this point in the history
* Extend and streamline parameters

* Update docs

---------

Co-authored-by: Christian Kolb <[email protected]>
  • Loading branch information
christian-kolb and Christian Kolb authored Nov 27, 2024
1 parent 14f5bf1 commit 3a16f17
Show file tree
Hide file tree
Showing 21 changed files with 288 additions and 171 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
# Changelog

## 1.0.1

- Fixed parameter doc block to allow configuration up to one array deep.
- Streamlined parameters for components to `NormalizedConfigurationParameters`.

## 1.0.0

- **[Breaking change](./UPGRADE.md#renamed-package)**: Renamed package from `cqrs` to `cqs-routing`.
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Reduced cost of change through CQS in Symfony

[![Latest Stable Version](https://img.shields.io/badge/stable-1.0.0-blue)](https://packagist.org/packages/digital-craftsman/cqs-routing)
[![Latest Stable Version](https://img.shields.io/badge/stable-1.0.1-blue)](https://packagist.org/packages/digital-craftsman/cqs-routing)
[![PHP Version Require](https://img.shields.io/badge/php-8.2|8.3-5b5d95)](https://packagist.org/packages/digital-craftsman/cqs-routing)
[![codecov](https://codecov.io/gh/digital-craftsman-de/cqs-routing/branch/main/graph/badge.svg?token=YUKRDW1L8G)](https://codecov.io/gh/digital-craftsman-de/cqs-routing)
![Packagist Downloads](https://img.shields.io/packagist/dt/digital-craftsman/cqs-routing)
Expand Down
4 changes: 3 additions & 1 deletion src/Command/CommandHandlerInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@

namespace DigitalCraftsman\CQSRouting\Command;

/** @method void __invoke(Command $command) */
/**
* @method void __invoke(Command $command)
*/
interface CommandHandlerInterface
{
}
41 changes: 28 additions & 13 deletions src/Controller/CommandController.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,16 +19,19 @@
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;

/**
* @psalm-import-type NormalizedConfigurationParameters from RoutePayload
*/
final class CommandController extends AbstractController
{
/**
* @param array<class-string<RequestValidatorInterface>, scalar|array<array-key, scalar|null>|null>|null $defaultRequestValidatorClasses
* @param class-string<RequestDecoderInterface>|null $defaultRequestDecoderClass
* @param array<class-string<RequestDataTransformerInterface>, scalar|array<array-key, scalar|null>|null>|null $defaultRequestDataTransformerClasses
* @param class-string<DTOConstructorInterface>|null $defaultDTOConstructorClass
* @param array<class-string<DTOValidatorInterface>, scalar|array<array-key, scalar|null>|null>|null $defaultDTOValidatorClasses
* @param array<class-string<HandlerWrapperInterface>, scalar|array<array-key, scalar|null>|null>|null $defaultHandlerWrapperClasses
* @param class-string<ResponseConstructorInterface>|null $defaultResponseConstructorClass
* @param array<class-string<RequestValidatorInterface>, NormalizedConfigurationParameters>|null $defaultRequestValidatorClasses
* @param class-string<RequestDecoderInterface>|null $defaultRequestDecoderClass
* @param array<class-string<RequestDataTransformerInterface>, NormalizedConfigurationParameters>|null $defaultRequestDataTransformerClasses
* @param class-string<DTOConstructorInterface>|null $defaultDTOConstructorClass
* @param array<class-string<DTOValidatorInterface>, NormalizedConfigurationParameters>|null $defaultDTOValidatorClasses
* @param array<class-string<HandlerWrapperInterface>, NormalizedConfigurationParameters>|null $defaultHandlerWrapperClasses
* @param class-string<ResponseConstructorInterface>|null $defaultResponseConstructorClass
*
* @codeCoverageIgnore
*/
Expand All @@ -44,12 +47,16 @@ public function __construct(
) {
}

/** We don't type the $routePayload because we never trigger it manually, it's only supplied through Symfony. */
/**
* We don't type the $routePayload because we never trigger it manually, it's only supplied through Symfony.
*/
public function handle(
Request $request,
array $routePayload,
): Response {
/** @psalm-suppress MixedArgumentTypeCoercion */
/**
* @psalm-suppress MixedArgumentTypeCoercion
*/
$configuration = RoutePayload::fromPayload($routePayload);

// -- Validate request
Expand Down Expand Up @@ -87,7 +94,9 @@ public function handle(
$this->defaultDTOConstructorClass,
);

/** @var Command $command */
/**
* @var Command $command
*/
$command = $dtoConstructor->constructDTO($requestData, $configuration->dtoClass);

// -- Validate command
Expand All @@ -102,7 +111,9 @@ public function handle(
}

// -- Wrap handlers
/** The wrapper handlers are quite complex, so additional explanation can be found in @HandlerWrapperStep */
/**
* The wrapper handlers are quite complex, so additional explanation can be found in @HandlerWrapperStep.
*/
$handlerWrapperClasses = RoutePayload::mergeHandlerWrapperClassesFromRouteWithDefaults(
$configuration->handlerWrapperClasses,
$configuration->handlerWrapperClassesToMergeWithDefault,
Expand All @@ -121,9 +132,13 @@ public function handle(

try {
// -- Trigger command through command handler
/** @psalm-suppress PossiblyInvalidArgument */
/**
* @psalm-suppress PossiblyInvalidArgument
*/
$commandHandler = $this->serviceMap->getCommandHandler($configuration->handlerClass);
/** @psalm-suppress InvalidFunctionCall */
/**
* @psalm-suppress InvalidFunctionCall
*/
$commandHandler($command);

$handlerWrapperClassesForThenStep = HandlerWrapperStep::then($handlerWrapperClasses);
Expand Down
41 changes: 28 additions & 13 deletions src/Controller/QueryController.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,16 +19,19 @@
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;

/**
* @psalm-import-type NormalizedConfigurationParameters from RoutePayload
*/
final class QueryController extends AbstractController
{
/**
* @param array<class-string<RequestValidatorInterface>, scalar|array<array-key, scalar|null>|null>|null $defaultRequestValidatorClasses
* @param class-string<RequestDecoderInterface>|null $defaultRequestDecoderClass
* @param array<class-string<RequestDataTransformerInterface>, scalar|array<array-key, scalar|null>|null>|null $defaultRequestDataTransformerClasses
* @param class-string<DTOConstructorInterface>|null $defaultDTOConstructorClass
* @param array<class-string<DTOValidatorInterface>, scalar|array<array-key, scalar|null>|null>|null $defaultDTOValidatorClasses
* @param array<class-string<HandlerWrapperInterface>, scalar|array<array-key, scalar|null>|null>|null $defaultHandlerWrapperClasses
* @param class-string<ResponseConstructorInterface>|null $defaultResponseConstructorClass
* @param array<class-string<RequestValidatorInterface>, NormalizedConfigurationParameters>|null $defaultRequestValidatorClasses
* @param class-string<RequestDecoderInterface>|null $defaultRequestDecoderClass
* @param array<class-string<RequestDataTransformerInterface>, NormalizedConfigurationParameters>|null $defaultRequestDataTransformerClasses
* @param class-string<DTOConstructorInterface>|null $defaultDTOConstructorClass
* @param array<class-string<DTOValidatorInterface>, NormalizedConfigurationParameters>|null $defaultDTOValidatorClasses
* @param array<class-string<HandlerWrapperInterface>, NormalizedConfigurationParameters>|null $defaultHandlerWrapperClasses
* @param class-string<ResponseConstructorInterface>|null $defaultResponseConstructorClass
*
* @codeCoverageIgnore
*/
Expand All @@ -44,12 +47,16 @@ public function __construct(
) {
}

/** We don't type the $routePayload because we never trigger it manually, it's only supplied through Symfony. */
/**
* We don't type the $routePayload because we never trigger it manually, it's only supplied through Symfony.
*/
public function handle(
Request $request,
array $routePayload,
): Response {
/** @psalm-suppress MixedArgumentTypeCoercion */
/**
* @psalm-suppress MixedArgumentTypeCoercion
*/
$configuration = RoutePayload::fromPayload($routePayload);

// -- Validate request
Expand Down Expand Up @@ -87,7 +94,9 @@ public function handle(
$this->defaultDTOConstructorClass,
);

/** @var Query $query */
/**
* @var Query $query
*/
$query = $dtoConstructor->constructDTO($requestData, $configuration->dtoClass);

// -- Validate query
Expand All @@ -102,7 +111,9 @@ public function handle(
}

// -- Wrap handlers
/** The wrapper handlers are quite complex, so additional explanation can be found in @HandlerWrapperStep */
/**
* The wrapper handlers are quite complex, so additional explanation can be found in @HandlerWrapperStep.
*/
$handlerWrapperClasses = RoutePayload::mergeHandlerWrapperClassesFromRouteWithDefaults(
$configuration->handlerWrapperClasses,
$configuration->handlerWrapperClassesToMergeWithDefault,
Expand All @@ -120,13 +131,17 @@ public function handle(
}

// -- Trigger query through query handler
/** @psalm-suppress PossiblyInvalidArgument */
/**
* @psalm-suppress PossiblyInvalidArgument
*/
$queryHandler = $this->serviceMap->getQueryHandler($configuration->handlerClass);

$result = null;

try {
/** @psalm-suppress InvalidFunctionCall */
/**
* @psalm-suppress InvalidFunctionCall
*/
$result = $queryHandler($query);

$handlerWrapperClassesForThenStep = HandlerWrapperStep::then($handlerWrapperClasses);
Expand Down
8 changes: 6 additions & 2 deletions src/DTOConstructor/SerializerDTOConstructor.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@

final readonly class SerializerDTOConstructor implements DTOConstructorInterface
{
/** @codeCoverageIgnore */
/**
* @codeCoverageIgnore
*/
public function __construct(
private DenormalizerInterface $serializer,
) {
Expand All @@ -25,7 +27,9 @@ public function __construct(
*/
public function constructDTO(array $requestData, string $dtoClass): Command | Query
{
/** @psalm-var T */
/**
* @psalm-var T
*/
return $this->serializer->denormalize($requestData, $dtoClass);
}
}
11 changes: 9 additions & 2 deletions src/DTOValidator/DTOValidatorInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

use DigitalCraftsman\CQSRouting\Command\Command;
use DigitalCraftsman\CQSRouting\Query\Query;
use DigitalCraftsman\CQSRouting\Routing\RoutePayload;
use Symfony\Component\HttpFoundation\Request;

/**
Expand All @@ -20,16 +21,22 @@
*
* @see https://github.com/digital-craftsman-de/cqs-routing/blob/main/docs/process.md
* @see https://github.com/digital-craftsman-de/cqs-routing/blob/main/docs/examples/dto-validator.md
*
* @psalm-import-type NormalizedConfigurationParameters from RoutePayload
*/
interface DTOValidatorInterface
{
/** @param scalar|array<array-key, scalar|null>|null $parameters */
/**
* @param NormalizedConfigurationParameters $parameters
*/
public function validateDTO(
Request $request,
Command | Query $dto,
mixed $parameters,
): void;

/** @param scalar|array<array-key, scalar|null>|null $parameters */
/**
* @param NormalizedConfigurationParameters $parameters
*/
public static function areParametersValid(mixed $parameters): bool;
}
20 changes: 12 additions & 8 deletions src/DependencyInjection/CQSRoutingExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,15 @@
use DigitalCraftsman\CQSRouting\RequestValidator\RequestValidatorInterface;
use DigitalCraftsman\CQSRouting\ResponseConstructor\ResponseConstructorInterface;
use DigitalCraftsman\CQSRouting\Routing\RouteBuilder;
use DigitalCraftsman\CQSRouting\Routing\RoutePayload;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Extension\Extension;
use Symfony\Component\DependencyInjection\Loader\YamlFileLoader;

/**
* @psalm-import-type NormalizedConfigurationParameters from RoutePayload
*/
final class CQSRoutingExtension extends Extension
{
public function load(array $configs, ContainerBuilder $container): void
Expand Down Expand Up @@ -67,21 +71,21 @@ public function load(array $configs, ContainerBuilder $container): void
/**
* @var array{
* query_controller: array{
* default_request_validator_classes: array<class-string<RequestValidatorInterface>, scalar|array<array-key, scalar|null>|null>|null,
* default_request_validator_classes: array<class-string<RequestValidatorInterface>, NormalizedConfigurationParameters>|null,
* default_request_decoder_class: class-string<RequestDecoderInterface>|null,
* default_request_data_transformer_classes: array<class-string<RequestDataTransformerInterface>, scalar|array<array-key, scalar|null>|null>|null,
* default_request_data_transformer_classes: array<class-string<RequestDataTransformerInterface>, NormalizedConfigurationParameters>|null,
* default_dto_constructor_class: class-string<DTOConstructorInterface>|null,
* default_dto_validator_classes: array<class-string<DTOValidatorInterface>, scalar|array<array-key, scalar|null>|null>|null,
* default_handler_wrapper_classes: array<class-string<HandlerWrapperInterface>, scalar|array<array-key, scalar|null>|null>|null,
* default_dto_validator_classes: array<class-string<DTOValidatorInterface>, NormalizedConfigurationParameters>|null,
* default_handler_wrapper_classes: array<class-string<HandlerWrapperInterface>, NormalizedConfigurationParameters>|null,
* default_response_constructor_class: class-string<ResponseConstructorInterface>|null,
* },
* command_controller: array{
* default_request_validator_classes: array<class-string<RequestValidatorInterface>, scalar|array<array-key, scalar|null>|null>|null,
* default_request_validator_classes: array<class-string<RequestValidatorInterface>, NormalizedConfigurationParameters>|null,
* default_request_decoder_class: class-string<RequestDecoderInterface>|null,
* default_request_data_transformer_classes: array<class-string<RequestDataTransformerInterface>, scalar|array<array-key, scalar|null>|null>|null,
* default_request_data_transformer_classes: array<class-string<RequestDataTransformerInterface>, NormalizedConfigurationParameters>|null,
* default_dto_constructor_class: class-string<DTOConstructorInterface>|null,
* default_dto_validator_classes: array<class-string<DTOValidatorInterface>, scalar|array<array-key, scalar|null>|null>|null,
* default_handler_wrapper_classes: array<class-string<HandlerWrapperInterface>, scalar|array<array-key, scalar|null>|null>|null,
* default_dto_validator_classes: array<class-string<DTOValidatorInterface>, NormalizedConfigurationParameters>|null,
* default_handler_wrapper_classes: array<class-string<HandlerWrapperInterface>, NormalizedConfigurationParameters>|null,
* default_response_constructor_class: class-string<ResponseConstructorInterface>|null,
* },
* } $config
Expand Down
23 changes: 17 additions & 6 deletions src/HandlerWrapper/DTO/HandlerWrapperStep.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
namespace DigitalCraftsman\CQSRouting\HandlerWrapper\DTO;

use DigitalCraftsman\CQSRouting\HandlerWrapper\HandlerWrapperInterface;
use DigitalCraftsman\CQSRouting\Routing\RoutePayload;

/**
* Handler wrappers are used to wrap command handlers and query handlers. With them, it's possible to for example start a doctrine
Expand All @@ -14,6 +15,8 @@
* To wrap this logic we first combine the wrappers with the parameters and then sort them by the priority of the handlers for the separate
* steps. After this the controller is able to simply select a step and get all relevant wrappers in the correct order.
*
* @psalm-import-type NormalizedConfigurationParameters from RoutePayload
*
* @codeCoverageIgnore
*
* @internal
Expand All @@ -24,12 +27,14 @@
public const STEP_THEN = 'THEN';
public const STEP_CATCH = 'CATCH';

/** @var array<class-string<HandlerWrapperInterface>, scalar|array<array-key, scalar|null>|null> */
/**
* @var array<class-string<HandlerWrapperInterface>, NormalizedConfigurationParameters>
*/
public array $orderedHandlerWrapperClasses;

/**
* @param array<class-string<HandlerWrapperInterface>, scalar|array<array-key, scalar|null>|null> $handlerWrapperClasses
* @param self::STEP_* $step
* @param array<class-string<HandlerWrapperInterface>, NormalizedConfigurationParameters> $handlerWrapperClasses
* @param self::STEP_* $step
*/
private function __construct(
array $handlerWrapperClasses,
Expand All @@ -51,7 +56,9 @@ private function __construct(
$this->orderedHandlerWrapperClasses = $handlerWrapperClasses;
}

/** @param array<class-string<HandlerWrapperInterface>, scalar|array<array-key, scalar|null>|null> $handlerWrapperClasses */
/**
* @param array<class-string<HandlerWrapperInterface>, NormalizedConfigurationParameters> $handlerWrapperClasses
*/
public static function prepare(array $handlerWrapperClasses): self
{
return new self(
Expand All @@ -60,7 +67,9 @@ public static function prepare(array $handlerWrapperClasses): self
);
}

/** @param array<class-string<HandlerWrapperInterface>, scalar|array<array-key, scalar|null>|null> $handlerWrapperClasses */
/**
* @param array<class-string<HandlerWrapperInterface>, NormalizedConfigurationParameters> $handlerWrapperClasses
*/
public static function then(array $handlerWrapperClasses): self
{
return new self(
Expand All @@ -69,7 +78,9 @@ public static function then(array $handlerWrapperClasses): self
);
}

/** @param array<class-string<HandlerWrapperInterface>, scalar|array<array-key, scalar|null>|null> $handlerWrapperClasses */
/**
* @param array<class-string<HandlerWrapperInterface>, NormalizedConfigurationParameters> $handlerWrapperClasses
*/
public static function catch(array $handlerWrapperClasses): self
{
return new self(
Expand Down
Loading

0 comments on commit 3a16f17

Please sign in to comment.