From babd702f85a064d9177b88083fbb9925353d895b Mon Sep 17 00:00:00 2001 From: Manuel Aranda Date: Mon, 16 Sep 2024 08:41:32 +0200 Subject: [PATCH] refs #659. Command API --- config/api_platform/Command.yaml | 31 +++++++++++ config/services.yaml | 5 ++ src/Dto/Input/CommandInput.php | 63 +++++++++++++++++++++- src/Dto/Output/CommandOutput.php | 37 ++++++++++++- src/Repository/CommandRepository.php | 27 +--------- src/State/Processor/CommandProcessor.php | 66 +++++++++++++++++++++-- src/State/Provider/CommandProvider.php | 69 ++++++++++++++++++++++-- 7 files changed, 262 insertions(+), 36 deletions(-) diff --git a/config/api_platform/Command.yaml b/config/api_platform/Command.yaml index e69de29..fd204fe 100644 --- a/config/api_platform/Command.yaml +++ b/config/api_platform/Command.yaml @@ -0,0 +1,31 @@ +resources: + App\Entity\Command: + processor: App\State\Processor\CommandProcessor + input: App\Dto\Input\CommandInput + output: App\Dto\Output\CommandOutput + normalizationContext: + groups: ['default', 'command:read'] + denormalizationContext: + groups: ['command:write'] + operations: + ApiPlatform\Metadata\GetCollection: + provider: App\State\Provider\CommandProvider + filters: + - 'api_platform.filter.command.order' + - 'api_platform.filter.command.search' + + ApiPlatform\Metadata\Get: + provider: App\State\Provider\CommandProvider + ApiPlatform\Metadata\Put: + provider: App\State\Provider\CommandProvider + ApiPlatform\Metadata\Patch: + provider: App\State\Provider\CommandProvider + ApiPlatform\Metadata\Post: ~ + ApiPlatform\Metadata\Delete: ~ + +properties: + App\Entity\Command: + id: + identifier: false + uuid: + identifier: true \ No newline at end of file diff --git a/config/services.yaml b/config/services.yaml index beee276..99d1d6c 100644 --- a/config/services.yaml +++ b/config/services.yaml @@ -101,3 +101,8 @@ services: bind: $collectionProvider: '@api_platform.doctrine.orm.state.collection_provider' $itemProvider: '@api_platform.doctrine.orm.state.item_provider' + + App\State\Provider\CommandProvider: + bind: + $collectionProvider: '@api_platform.doctrine.orm.state.collection_provider' + $itemProvider: '@api_platform.doctrine.orm.state.item_provider' diff --git a/src/Dto/Input/CommandInput.php b/src/Dto/Input/CommandInput.php index 03d64f2..a9d36af 100644 --- a/src/Dto/Input/CommandInput.php +++ b/src/Dto/Input/CommandInput.php @@ -2,7 +2,66 @@ namespace App\Dto\Input; -class CommandInput -{ +use ApiPlatform\Metadata\ApiProperty; +use App\Entity\Command; +use phpDocumentor\Reflection\Types\Boolean; +use Symfony\Component\Serializer\Annotation\Groups; +use Symfony\Component\Validator\Constraints as Assert; +final class CommandInput +{ + #[Assert\NotBlank(message: 'validators.command.name.not_blank')] + #[Groups(['command:write'])] + #[ApiProperty( + description: 'El nombre del comando', + example: 'Command 1', + )] + public ?string $name = null; + + #[Groups(['command:write'])] + #[ApiProperty( + description: 'El script del comando', + example: 'echo "Hello World"', + )] + public ?string $script = null; + + #[Groups(['command:write'])] + #[ApiProperty( + description: 'Es interno el comando?', + example: 'true', + )] + public ?bool $readOnly = false; + + #[Groups(['command:write'])] + #[ApiProperty( + description: 'Los comentarios del comando', + example: 'Comentarios del comando', + )] + public ?string $comments = null; + + public function __construct(?Command $command = null) + { + if (!$command) { + return; + } + + $this->name = $command->getName(); + $this->script = $command->getScript(); + $this->readOnly = $command->isReadOnly(); + $this->comments = $command->getComments(); + } + + public function createOrUpdateEntity(?Command $command = null): Command + { + if (!$command) { + $command = new Command(); + } + + $command->setName($this->name); + $command->setScript($this->script); + $command->setReadOnly($this->readOnly); + $command->setComments($this->comments); + + return $command; + } } \ No newline at end of file diff --git a/src/Dto/Output/CommandOutput.php b/src/Dto/Output/CommandOutput.php index 7eda910..d8598f5 100644 --- a/src/Dto/Output/CommandOutput.php +++ b/src/Dto/Output/CommandOutput.php @@ -2,7 +2,40 @@ namespace App\Dto\Output; -class CommandOutput -{ +use ApiPlatform\Metadata\Get; +use App\Entity\Command; +use Symfony\Component\Serializer\Annotation\Groups; +#[Get(shortName: 'Client')] +final class CommandOutput extends AbstractOutput +{ + #[Groups(['command:read'])] + public string $name; + + #[Groups(['command:read'])] + public ?string $script = ''; + + #[Groups(['command:read'])] + public ?bool $readOnly = false; + + #[Groups(['command:read'])] + public ?string $comments = ''; + + #[Groups(['command:read'])] + public \DateTime $createdAt; + + #[Groups(['command:read'])] + public ?string $createdBy = null; + + public function __construct(Command $command) + { + parent::__construct($command); + + $this->name = $command->getName(); + $this->script = $command->getScript(); + $this->readOnly = $command->isReadOnly(); + $this->comments = $command->getComments(); + $this->createdAt = $command->getCreatedAt(); + $this->createdBy = $command->getCreatedBy(); + } } \ No newline at end of file diff --git a/src/Repository/CommandRepository.php b/src/Repository/CommandRepository.php index 5afe393..544b708 100644 --- a/src/Repository/CommandRepository.php +++ b/src/Repository/CommandRepository.php @@ -9,35 +9,10 @@ use Doctrine\Persistence\ManagerRegistry; /** * @extends ServiceEntityRepository */ -class CommandRepository extends ServiceEntityRepository +class CommandRepository extends AbstractRepository { public function __construct(ManagerRegistry $registry) { parent::__construct($registry, Command::class); } - - // /** - // * @return Command[] Returns an array of Command objects - // */ - // public function findByExampleField($value): array - // { - // return $this->createQueryBuilder('c') - // ->andWhere('c.exampleField = :val') - // ->setParameter('val', $value) - // ->orderBy('c.id', 'ASC') - // ->setMaxResults(10) - // ->getQuery() - // ->getResult() - // ; - // } - - // public function findOneBySomeField($value): ?Command - // { - // return $this->createQueryBuilder('c') - // ->andWhere('c.exampleField = :val') - // ->setParameter('val', $value) - // ->getQuery() - // ->getOneOrNullResult() - // ; - // } } diff --git a/src/State/Processor/CommandProcessor.php b/src/State/Processor/CommandProcessor.php index 355a27b..fffae4f 100644 --- a/src/State/Processor/CommandProcessor.php +++ b/src/State/Processor/CommandProcessor.php @@ -2,7 +2,67 @@ namespace App\State\Processor; -class CommandProcessor -{ +use ApiPlatform\Metadata\Delete; +use ApiPlatform\Metadata\Operation; +use ApiPlatform\Metadata\Patch; +use ApiPlatform\Metadata\Post; +use ApiPlatform\Metadata\Put; +use ApiPlatform\State\ProcessorInterface; +use ApiPlatform\Validator\ValidatorInterface; +use App\Dto\Input\CommandInput; +use App\Dto\Output\CommandOutput; +use App\Repository\CommandRepository; -} \ No newline at end of file +readonly class CommandProcessor implements ProcessorInterface +{ + public function __construct( + private CommandRepository $commandRepository, + private ValidatorInterface $validator + ) + { + } + + /** + * @throws \Exception + */ + public function process(mixed $data, Operation $operation, array $uriVariables = [], array $context = []): CommandOutput|null + { + switch ($operation){ + case $operation instanceof Post: + case $operation instanceof Put: + case $operation instanceof Patch: + return $this->processCreateOrUpdate($data, $operation, $uriVariables, $context); + case $operation instanceof Delete: + return $this->processDelete($data, $operation, $uriVariables, $context); + } + } + + /** + * @throws \Exception + */ + private function processCreateOrUpdate($data, Operation $operation, array $uriVariables = [], array $context = []): CommandOutput + { + if (!($data instanceof CommandInput)) { + throw new \Exception(sprintf('data is not instance of %s', CommandInput::class)); + } + + $entity = null; + if (isset($uriVariables['uuid'])) { + $entity = $this->commandRepository->findOneByUuid($uriVariables['uuid']); + } + + $command = $data->createOrUpdateEntity($entity); + $this->validator->validate($command); + $this->commandRepository->save($command); + + return new CommandOutput($command); + } + + private function processDelete($data, Operation $operation, array $uriVariables = [], array $context = []): null + { + $user = $this->commandRepository->findOneByUuid($uriVariables['uuid']); + $this->commandRepository->delete($user); + + return null; + } +} diff --git a/src/State/Provider/CommandProvider.php b/src/State/Provider/CommandProvider.php index a32b218..ac2de21 100644 --- a/src/State/Provider/CommandProvider.php +++ b/src/State/Provider/CommandProvider.php @@ -2,7 +2,70 @@ namespace App\State\Provider; -class CommandProvider -{ +use ApiPlatform\Metadata\Get; +use ApiPlatform\Metadata\GetCollection; +use ApiPlatform\Metadata\Operation; +use ApiPlatform\Metadata\Patch; +use ApiPlatform\Metadata\Put; +use ApiPlatform\State\Pagination\TraversablePaginator; +use ApiPlatform\State\ProviderInterface; +use App\Dto\Input\CommandInput; +use App\Dto\Output\CommandOutput; +use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; -} \ No newline at end of file +readonly class CommandProvider implements ProviderInterface +{ + public function __construct( + private ProviderInterface $collectionProvider, + private ProviderInterface $itemProvider + ) + { + } + + public function provide(Operation $operation, array $uriVariables = [], array $context = []): object|array|null + { + switch ($operation){ + case $operation instanceof GetCollection: + return $this->provideCollection($operation, $uriVariables, $context); + case $operation instanceof Patch: + case $operation instanceof Put: + return $this->provideInput($operation, $uriVariables, $context); + case $operation instanceof Get: + return $this->provideItem($operation, $uriVariables, $context); + } + } + + private function provideCollection(Operation $operation, array $uriVariables = [], array $context = []): object + { + $paginator = $this->collectionProvider->provide($operation, $uriVariables, $context); + + $items = new \ArrayObject(); + foreach ($paginator->getIterator() as $item){ + $items[] = new CommandOutput($item); + } + + return new TraversablePaginator($items, $paginator->getCurrentPage(), $paginator->getItemsPerPage(), $paginator->getTotalItems()); + } + + public function provideItem(Operation $operation, array $uriVariables = [], array $context = []): object|array|null + { + $item = $this->itemProvider->provide($operation, $uriVariables, $context); + + if (!$item) { + throw new NotFoundHttpException('Hardware profile not found'); + } + + return new CommandOutput($item); + } + + public function provideInput(Operation $operation, array $uriVariables = [], array $context = []): object|array|null + { + if (isset($uriVariables['uuid'])) { + $item = $this->itemProvider->provide($operation, $uriVariables, $context); + + return $item !== null ? new CommandInput($item) : null; + } + + return new CommandInput(); + } +}