refs #659. CommandGroup API

feature/actions
Manuel Aranda Rosales 2024-09-16 13:31:37 +02:00
parent 05214bbd2e
commit ad02456de6
15 changed files with 372 additions and 27 deletions

View File

@ -13,6 +13,7 @@ resources:
filters:
- 'api_platform.filter.command.order'
- 'api_platform.filter.command.search'
- 'api_platform.filter.command.boolean'
ApiPlatform\Metadata\Get:
provider: App\State\Provider\CommandProvider

View File

@ -0,0 +1,32 @@
resources:
App\Entity\CommandGroup:
processor: App\State\Processor\CommandGroupProcessor
input: App\Dto\Input\CommandGroupInput
output: App\Dto\Output\CommandGroupOutput
normalizationContext:
groups: ['default', 'command-group:read']
denormalizationContext:
groups: ['command-group:write']
operations:
ApiPlatform\Metadata\GetCollection:
provider: App\State\Provider\CommandGroupProvider
filters:
- 'api_platform.filter.command.order'
- 'api_platform.filter.command.search'
- 'api_platform.filter.command.boolean'
ApiPlatform\Metadata\Get:
provider: App\State\Provider\CommandGroupProvider
ApiPlatform\Metadata\Put:
provider: App\State\Provider\CommandGroupProvider
ApiPlatform\Metadata\Patch:
provider: App\State\Provider\CommandGroupProvider
ApiPlatform\Metadata\Post: ~
ApiPlatform\Metadata\Delete: ~
properties:
App\Entity\CommandGroup:
id:
identifier: false
uuid:
identifier: true

View File

@ -106,3 +106,8 @@ services:
bind:
$collectionProvider: '@api_platform.doctrine.orm.state.collection_provider'
$itemProvider: '@api_platform.doctrine.orm.state.item_provider'
App\State\Provider\CommandGroupProvider:
bind:
$collectionProvider: '@api_platform.doctrine.orm.state.collection_provider'
$itemProvider: '@api_platform.doctrine.orm.state.item_provider'

View File

@ -11,6 +11,23 @@ services:
arguments: [ { 'id': 'exact', 'name': 'partial', 'serialNumber': 'exact', organizationalUnit.id: 'exact' } ]
tags: [ 'api_platform.filter' ]
api_platform.filter.command.order:
parent: 'api_platform.doctrine.orm.order_filter'
arguments:
$properties: { 'id': ~, 'name': ~ }
$orderParameterName: 'order'
tags: [ 'api_platform.filter' ]
api_platform.filter.command.search:
parent: 'api_platform.doctrine.orm.search_filter'
arguments: [ { 'id': 'exact', 'name': 'exact'} ]
tags: [ 'api_platform.filter' ]
api_platform.filter.command.boolean:
parent: 'api_platform.doctrine.orm.boolean_filter'
arguments: [ { 'enabled': ~ } ]
tags: [ 'api_platform.filter' ]
api_platform.filter.hardware.order:
parent: 'api_platform.doctrine.orm.order_filter'
arguments:

View File

@ -0,0 +1,83 @@
<?php
namespace App\Dto\Input;
use ApiPlatform\Metadata\ApiProperty;
use App\Dto\Output\CommandOutput;
use App\Entity\Command;
use App\Entity\CommandGroup;
use phpDocumentor\Reflection\Types\Boolean;
use Symfony\Component\Serializer\Annotation\Groups;
use Symfony\Component\Validator\Constraints as Assert;
final class CommandGroupInput
{
#[Assert\NotBlank(message: 'validators.command_group.name.not_blank')]
#[Groups(['command-group:write'])]
#[ApiProperty(
description: 'El nombre del grupo de comandos',
example: 'Command Group 1',
)]
public ?string $name = null;
/**
* @var CommandOutput[]
*/
#[Groups(['command-group:write'])]
#[ApiProperty(
description: 'Los comandos del grupo',
example: 'Comandos del grupo',
)]
public array $commands = [];
#[Assert\NotBlank(message: 'validators.command_group.position.not_blank')]
#[Groups(['command-group:write'])]
#[ApiProperty(
description: 'La posición del grupo de comandos',
example: '1',
)]
public ?int $position = null;
#[Groups(['command-group:write'])]
#[ApiProperty(
description: '¿Está habilitado el grupo de comandos?',
example: 'true',
)]
public ?bool $enabled = true;
public function __construct(?CommandGroup $commandGroup = null)
{
if (!$commandGroup) {
return;
}
$this->name = $commandGroup->getName();
if ($commandGroup->getCommands()) {
foreach ($commandGroup->getCommands() as $command) {
$this->commands[] = new CommandOutput($command);
}
}
$this->position = $commandGroup->getPosition();
$this->enabled = $commandGroup->isEnabled();
}
public function createOrUpdateEntity(?CommandGroup $commandGroup = null): CommandGroup
{
if (!$commandGroup) {
$commandGroup = new CommandGroup();
}
$commandGroup->setName($this->name);
foreach ($this->commands as $command) {
$commandsToAdd[] = $command->getEntity();
}
$commandGroup->setCommands( $commandsToAdd ?? [] );
$commandGroup->setPosition($this->position);
return $commandGroup;
}
}

View File

@ -33,6 +33,13 @@ final class CommandInput
)]
public ?bool $readOnly = false;
#[Groups(['command:write'])]
#[ApiProperty(
description: 'Esta activo?',
example: 'true',
)]
public ?bool $enabled = true;
#[Groups(['command:write'])]
#[ApiProperty(
description: 'Los comentarios del comando',
@ -48,6 +55,7 @@ final class CommandInput
$this->name = $command->getName();
$this->script = $command->getScript();
$this->enabled = $command->isEnabled();
$this->readOnly = $command->isReadOnly();
$this->comments = $command->getComments();
}
@ -60,6 +68,7 @@ final class CommandInput
$command->setName($this->name);
$command->setScript($this->script);
$command->setEnabled($this->enabled);
$command->setReadOnly($this->readOnly);
$command->setComments($this->comments);

View File

@ -0,0 +1,42 @@
<?php
namespace App\Dto\Output;
use ApiPlatform\Metadata\Get;
use App\Entity\Command;
use App\Entity\CommandGroup;
use Symfony\Component\Serializer\Annotation\Groups;
#[Get(shortName: 'CommandGroup')]
final class CommandGroupOutput extends AbstractOutput
{
#[Groups(['command-group:read'])]
public string $name;
#[Groups(['command-group:read'])]
public array $commands = [];
#[Groups(['command-group:read'])]
public ?int $position = null;
#[Groups(['command-group:read'])]
public \DateTime $createdAt;
#[Groups(['command-group:read'])]
public ?string $createdBy = null;
public function __construct(CommandGroup $commandGroup)
{
parent::__construct($commandGroup);
$this->name = $commandGroup->getName();
$this->commands = $commandGroup->getCommands()->map(
fn(Command $command) => new CommandOutput($command)
)->toArray();
$this->position = $commandGroup->getPosition();
$this->createdAt = $commandGroup->getCreatedAt();
$this->createdBy = $commandGroup->getCreatedBy();
}
}

View File

@ -18,6 +18,9 @@ final class CommandOutput extends AbstractOutput
#[Groups(['command:read'])]
public ?bool $readOnly = false;
#[Groups(['command:read'])]
public ?bool $enabled = true;
#[Groups(['command:read'])]
public ?string $comments = '';
@ -34,6 +37,7 @@ final class CommandOutput extends AbstractOutput
$this->name = $command->getName();
$this->script = $command->getScript();
$this->readOnly = $command->isReadOnly();
$this->enabled = $command->isEnabled();
$this->comments = $command->getComments();
$this->createdAt = $command->getCreatedAt();
$this->createdBy = $command->getCreatedBy();

View File

@ -36,6 +36,18 @@ class CommandGroup extends AbstractEntity
return $this->commands;
}
public function setCommands(array $commands): static
{
$this->commands->clear();
foreach ($commands as $command){
$this->addCommand($command);
}
return $this;
}
public function addCommand(Command $command): static
{
if (!$this->commands->contains($command)) {

View File

@ -9,35 +9,10 @@ use Doctrine\Persistence\ManagerRegistry;
/**
* @extends ServiceEntityRepository<CommandGroup>
*/
class CommandGroupRepository extends ServiceEntityRepository
class CommandGroupRepository extends AbstractRepository
{
public function __construct(ManagerRegistry $registry)
{
parent::__construct($registry, CommandGroup::class);
}
// /**
// * @return CommandGroup[] Returns an array of CommandGroup 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): ?CommandGroup
// {
// return $this->createQueryBuilder('c')
// ->andWhere('c.exampleField = :val')
// ->setParameter('val', $value)
// ->getQuery()
// ->getOneOrNullResult()
// ;
// }
}

View File

@ -0,0 +1,69 @@
<?php
namespace App\State\Processor;
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\CommandGroupInput;
use App\Dto\Output\CommandGroupOutput;
use App\Repository\CommandGroupRepository;
use App\Repository\CommandRepository;
readonly class CommandGroupProcessor implements ProcessorInterface
{
public function __construct(
private CommandGroupRepository $commandGroupRepository,
private ValidatorInterface $validator
)
{
}
/**
* @throws \Exception
*/
public function process(mixed $data, Operation $operation, array $uriVariables = [], array $context = []): CommandGroupOutput|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 = []): CommandGroupOutput
{
if (!($data instanceof CommandGroupInput)) {
throw new \Exception(sprintf('data is not instance of %s', CommandGroupInput::class));
}
$entity = null;
if (isset($uriVariables['uuid'])) {
$entity = $this->commandGroupRepository->findOneByUuid($uriVariables['uuid']);
}
$command = $data->createOrUpdateEntity($entity);
$this->validator->validate($command);
$this->commandGroupRepository->save($command);
return new CommandGroupOutput($command);
}
private function processDelete($data, Operation $operation, array $uriVariables = [], array $context = []): null
{
$user = $this->commandGroupRepository->findOneByUuid($uriVariables['uuid']);
$this->commandGroupRepository->delete($user);
return null;
}
}

View File

@ -0,0 +1,72 @@
<?php
namespace App\State\Provider;
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\CommandGroupInput;
use App\Dto\Input\CommandInput;
use App\Dto\Output\CommandGroupOutput;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
readonly class CommandGroupProvider 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 CommandGroupOutput($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('Command group not found');
}
return new CommandGroupOutput($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 CommandGroupInput($item) : null;
}
return new CommandGroupInput();
}
}

View File

@ -52,7 +52,7 @@ readonly class CommandProvider implements ProviderInterface
$item = $this->itemProvider->provide($operation, $uriVariables, $context);
if (!$item) {
throw new NotFoundHttpException('Hardware profile not found');
throw new NotFoundHttpException('Command not found');
}
return new CommandOutput($item);

View File

@ -8,6 +8,18 @@ validators:
organizational_unit:
not_null: 'The organizational unit should not be null.'
command:
name:
not_blank: 'The name should not be blank.'
script:
not_blank: 'The script should not be blank.'
command_group:
name:
not_blank: 'The name should not be blank.'
position:
not_blank: 'The position should not be blank.'
view:
name:
not_blank: 'The name should not be blank.'

View File

@ -8,6 +8,18 @@ validators:
organizational_unit:
not_null: 'La unidad organizativa no debería estar vacía.'
command:
name:
not_blank: 'El nombre no debería estar vacío.'
script:
not_blank: 'El script no debería estar vacío.'
command_group:
name:
not_blank: 'El nombre no debería estar vacío.'
position:
not_blank: 'La posición no debería estar vacía.'
view:
name:
not_blank: 'El nombre no debería estar vacío.'