refs #659. Command API

feature/actions
Manuel Aranda Rosales 2024-09-16 08:41:32 +02:00
parent 8d67ccd78a
commit babd702f85
7 changed files with 262 additions and 36 deletions

View File

@ -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

View File

@ -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'

View File

@ -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;
}
}

View File

@ -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();
}
}

View File

@ -9,35 +9,10 @@ use Doctrine\Persistence\ManagerRegistry;
/**
* @extends ServiceEntityRepository<Command>
*/
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()
// ;
// }
}

View File

@ -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;
}
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;
}
}

View File

@ -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;
}
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();
}
}