refs #659. Trace API. New endpoint execute in command
parent
b4f9f1e9bb
commit
d1105a1e89
|
@ -24,6 +24,13 @@ resources:
|
|||
ApiPlatform\Metadata\Post: ~
|
||||
ApiPlatform\Metadata\Delete: ~
|
||||
|
||||
execute:
|
||||
class: ApiPlatform\Metadata\Post
|
||||
method: POST
|
||||
input: App\Dto\Input\CommandExecuteInput
|
||||
uriTemplate: /commands/{uuid}/execute
|
||||
controller: App\Controller\CommandExecuteAction
|
||||
|
||||
properties:
|
||||
App\Entity\Command:
|
||||
id:
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
resources:
|
||||
App\Entity\Trace:
|
||||
output: App\Dto\Output\TraceOutput
|
||||
normalizationContext:
|
||||
groups: ['default', 'trace:read']
|
||||
operations:
|
||||
ApiPlatform\Metadata\GetCollection:
|
||||
provider: App\State\Provider\TraceProvider
|
||||
filters:
|
||||
- 'api_platform.filter.trace.order'
|
||||
- 'api_platform.filter.trace.search'
|
||||
ApiPlatform\Metadata\Get:
|
||||
provider: App\State\Provider\TraceProvider
|
||||
|
||||
properties:
|
||||
App\Entity\Trace:
|
||||
id:
|
||||
identifier: false
|
||||
uuid:
|
||||
identifier: true
|
|
@ -113,6 +113,11 @@ services:
|
|||
$itemProvider: '@api_platform.doctrine.orm.state.item_provider'
|
||||
|
||||
App\State\Provider\CommandTaskProvider:
|
||||
bind:
|
||||
$collectionProvider: '@api_platform.doctrine.orm.state.collection_provider'
|
||||
$itemProvider: '@api_platform.doctrine.orm.state.item_provider'
|
||||
|
||||
App\State\Provider\TraceProvider:
|
||||
bind:
|
||||
$collectionProvider: '@api_platform.doctrine.orm.state.collection_provider'
|
||||
$itemProvider: '@api_platform.doctrine.orm.state.item_provider'
|
|
@ -0,0 +1,35 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace DoctrineMigrations;
|
||||
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\Migrations\AbstractMigration;
|
||||
|
||||
/**
|
||||
* Auto-generated Migration: Please modify to your needs!
|
||||
*/
|
||||
final class Version20240917091950 extends AbstractMigration
|
||||
{
|
||||
public function getDescription(): string
|
||||
{
|
||||
return '';
|
||||
}
|
||||
|
||||
public function up(Schema $schema): void
|
||||
{
|
||||
// this up() migration is auto-generated, please modify it to your needs
|
||||
$this->addSql('CREATE TABLE trace (id INT AUTO_INCREMENT NOT NULL, client_id INT NOT NULL, command_id INT NOT NULL, uuid CHAR(36) NOT NULL COMMENT \'(DC2Type:uuid)\', migration_id VARCHAR(255) DEFAULT NULL, created_at DATETIME NOT NULL, updated_at DATETIME NOT NULL, created_by VARCHAR(255) DEFAULT NULL, updated_by VARCHAR(255) DEFAULT NULL, status VARCHAR(255) NOT NULL, output VARCHAR(255) DEFAULT NULL, executed_at DATETIME NOT NULL, finished_at DATETIME NOT NULL, UNIQUE INDEX UNIQ_315BD5A1D17F50A6 (uuid), INDEX IDX_315BD5A119EB6921 (client_id), INDEX IDX_315BD5A133E1689A (command_id), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB');
|
||||
$this->addSql('ALTER TABLE trace ADD CONSTRAINT FK_315BD5A119EB6921 FOREIGN KEY (client_id) REFERENCES client (id)');
|
||||
$this->addSql('ALTER TABLE trace ADD CONSTRAINT FK_315BD5A133E1689A FOREIGN KEY (command_id) REFERENCES command (id)');
|
||||
}
|
||||
|
||||
public function down(Schema $schema): void
|
||||
{
|
||||
// this down() migration is auto-generated, please modify it to your needs
|
||||
$this->addSql('ALTER TABLE trace DROP FOREIGN KEY FK_315BD5A119EB6921');
|
||||
$this->addSql('ALTER TABLE trace DROP FOREIGN KEY FK_315BD5A133E1689A');
|
||||
$this->addSql('DROP TABLE trace');
|
||||
}
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace DoctrineMigrations;
|
||||
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\Migrations\AbstractMigration;
|
||||
|
||||
/**
|
||||
* Auto-generated Migration: Please modify to your needs!
|
||||
*/
|
||||
final class Version20240917092207 extends AbstractMigration
|
||||
{
|
||||
public function getDescription(): string
|
||||
{
|
||||
return '';
|
||||
}
|
||||
|
||||
public function up(Schema $schema): void
|
||||
{
|
||||
// this up() migration is auto-generated, please modify it to your needs
|
||||
$this->addSql('ALTER TABLE trace CHANGE finished_at finished_at DATETIME DEFAULT NULL');
|
||||
}
|
||||
|
||||
public function down(Schema $schema): void
|
||||
{
|
||||
// this down() migration is auto-generated, please modify it to your needs
|
||||
$this->addSql('ALTER TABLE trace CHANGE finished_at finished_at DATETIME NOT NULL');
|
||||
}
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
<?php
|
||||
|
||||
namespace App\Controller;
|
||||
|
||||
use App\Dto\Input\CommandExecuteInput;
|
||||
use App\Dto\Input\CommandGroupAddCommandsInput;
|
||||
use App\Entity\Client;
|
||||
use App\Entity\Command;
|
||||
use App\Entity\CommandGroup;
|
||||
use App\Entity\Trace;
|
||||
use App\Model\TraceStatus;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
use Symfony\Component\HttpFoundation\JsonResponse;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
class CommandExecuteAction extends AbstractController
|
||||
{
|
||||
public function __construct(
|
||||
private readonly EntityManagerInterface $entityManager
|
||||
)
|
||||
{
|
||||
}
|
||||
|
||||
public function __invoke(CommandExecuteInput $input, Command $command): JsonResponse
|
||||
{
|
||||
$clients = $input->clients;
|
||||
|
||||
/** @var Client $client */
|
||||
foreach ($clients as $client) {
|
||||
$trace = new Trace();
|
||||
$trace->setClient($client->getEntity());
|
||||
$trace->setCommand($command);
|
||||
$trace->setStatus(TraceStatus::IN_PROGRESS);
|
||||
$trace->setExecutedAt(new \DateTimeImmutable());
|
||||
|
||||
$this->entityManager->persist($trace);
|
||||
}
|
||||
|
||||
$this->entityManager->flush();
|
||||
|
||||
return new JsonResponse(data: 'Command executed successfully', status: Response::HTTP_OK);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
<?php
|
||||
|
||||
namespace App\Dto\Input;
|
||||
|
||||
use App\Dto\Output\ClientOutput;
|
||||
use Symfony\Component\Serializer\Annotation\Groups;
|
||||
use Symfony\Component\Validator\Constraints as Assert;
|
||||
|
||||
final class CommandExecuteInput
|
||||
{
|
||||
/**
|
||||
* @var ClientOutput[]
|
||||
*/
|
||||
#[Assert\NotNull]
|
||||
#[Groups(['command:write'])]
|
||||
public array $clients = [];
|
||||
}
|
|
@ -13,19 +13,19 @@ final class ClientOutput extends AbstractOutput
|
|||
{
|
||||
CONST string TYPE = 'client';
|
||||
|
||||
#[Groups(['client:read', 'organizational-unit:read'])]
|
||||
#[Groups(['client:read', 'organizational-unit:read', 'trace:read'])]
|
||||
public string $name;
|
||||
|
||||
#[Groups(['client:read', 'organizational-unit:read'])]
|
||||
public string $type = self::TYPE;
|
||||
|
||||
#[Groups(['client:read', 'organizational-unit:read'])]
|
||||
#[Groups(['client:read', 'organizational-unit:read', 'trace:read'])]
|
||||
public ?string $ip = '';
|
||||
|
||||
#[Groups(['client:read', 'organizational-unit:read'])]
|
||||
#[Groups(['client:read', 'organizational-unit:read', 'trace:read'])]
|
||||
public ?string $mac = '';
|
||||
|
||||
#[Groups(['client:read', 'organizational-unit:read'])]
|
||||
#[Groups(['client:read', 'organizational-unit:read', 'trace:read'])]
|
||||
public ?string $serialNumber = '';
|
||||
|
||||
#[Groups(['client:read'])]
|
||||
|
|
|
@ -9,10 +9,10 @@ use Symfony\Component\Serializer\Annotation\Groups;
|
|||
#[Get(shortName: 'Command')]
|
||||
final class CommandOutput extends AbstractOutput
|
||||
{
|
||||
#[Groups(['command:read', 'command-group:read', 'command-task:read'])]
|
||||
#[Groups(['command:read', 'command-group:read', 'command-task:read', 'trace:read'])]
|
||||
public string $name;
|
||||
|
||||
#[Groups(['command:read', 'command-group:read', 'command-task:read'])]
|
||||
#[Groups(['command:read', 'command-group:read', 'command-task:read', 'trace:read'])]
|
||||
public ?string $script = '';
|
||||
|
||||
#[Groups(['command:read'])]
|
||||
|
|
|
@ -0,0 +1,50 @@
|
|||
<?php
|
||||
|
||||
namespace App\Dto\Output;
|
||||
|
||||
use ApiPlatform\Metadata\Get;
|
||||
use App\Entity\Menu;
|
||||
use App\Entity\Trace;
|
||||
use Symfony\Component\Serializer\Annotation\Groups;
|
||||
|
||||
#[Get(shortName: 'Trace')]
|
||||
final class TraceOutput extends AbstractOutput
|
||||
{
|
||||
#[Groups(['trace:read'])]
|
||||
public CommandOutput $command;
|
||||
|
||||
#[Groups(['trace:read'])]
|
||||
public ClientOutput $client;
|
||||
|
||||
#[Groups(['trace:read'])]
|
||||
public string $status;
|
||||
|
||||
#[Groups(['trace:read'])]
|
||||
public ?\DateTimeInterface $executedAt = null;
|
||||
|
||||
#[Groups(['trace:read'])]
|
||||
public ?string $output = null;
|
||||
|
||||
#[Groups(['trace:read'])]
|
||||
public ?\DateTimeInterface $finishedAt = null;
|
||||
|
||||
#[Groups(['trace:read'])]
|
||||
public \DateTime $createdAt;
|
||||
|
||||
#[Groups(['trace:read'])]
|
||||
public ?string $createdBy = null;
|
||||
|
||||
public function __construct(Trace $trace)
|
||||
{
|
||||
parent::__construct($trace);
|
||||
|
||||
$this->command = new CommandOutput($trace->getCommand());
|
||||
$this->client = new ClientOutput($trace->getClient());
|
||||
$this->status = $trace->getStatus();
|
||||
$this->executedAt = $trace->getExecutedAt();
|
||||
$this->output = $trace->getOutput();
|
||||
$this->finishedAt = $trace->getFinishedAt();
|
||||
$this->createdAt = $trace->getCreatedAt();
|
||||
$this->createdBy = $trace->getCreatedBy();
|
||||
}
|
||||
}
|
|
@ -24,10 +24,10 @@ class Trace extends AbstractEntity
|
|||
private ?string $output = null;
|
||||
|
||||
#[ORM\Column(type: Types::DATETIME_MUTABLE)]
|
||||
private ?\DateTimeImmutable $executedAt = null;
|
||||
private ?\DateTimeInterface $executedAt = null;
|
||||
|
||||
#[ORM\Column(type: Types::DATETIME_MUTABLE)]
|
||||
private ?\DateTimeImmutable $finishedAt = null;
|
||||
#[ORM\Column(type: Types::DATETIME_MUTABLE, nullable: true)]
|
||||
private ?\DateTimeInterface $finishedAt = null;
|
||||
|
||||
public function getClient(): ?Client
|
||||
{
|
||||
|
@ -77,24 +77,24 @@ class Trace extends AbstractEntity
|
|||
return $this;
|
||||
}
|
||||
|
||||
public function getExecutedAt(): ?\DateTimeImmutable
|
||||
public function getExecutedAt(): ?\DateTimeInterface
|
||||
{
|
||||
return $this->executedAt;
|
||||
}
|
||||
|
||||
public function setExecutedAt(\DateTimeImmutable $executedAt): static
|
||||
public function setExecutedAt(\DateTimeInterface $executedAt): static
|
||||
{
|
||||
$this->executedAt = $executedAt;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getFinishedAt(): ?\DateTimeImmutable
|
||||
public function getFinishedAt(): ?\DateTimeInterface
|
||||
{
|
||||
return $this->finishedAt;
|
||||
}
|
||||
|
||||
public function setFinishedAt(\DateTimeImmutable $finishedAt): static
|
||||
public function setFinishedAt(\DateTimeInterface $finishedAt): static
|
||||
{
|
||||
$this->finishedAt = $finishedAt;
|
||||
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
<?php
|
||||
|
||||
namespace App\Model;
|
||||
|
||||
final class TraceStatus
|
||||
{
|
||||
public const string PENDING = 'pending';
|
||||
public const string IN_PROGRESS = 'in-progress';
|
||||
public const string COMPLETED = 'completed';
|
||||
public const string FAILED = 'failed';
|
||||
|
||||
private const array STATUS = [
|
||||
self::PENDING => 'Pendiente',
|
||||
self::IN_PROGRESS => 'En progreso',
|
||||
self::COMPLETED => 'Completado',
|
||||
self::FAILED => 'Fallido',
|
||||
];
|
||||
|
||||
public static function getStatus(): array
|
||||
{
|
||||
return self::STATUS;
|
||||
}
|
||||
|
||||
public static function getTraceStatus(string $status): ?string
|
||||
{
|
||||
return self::STATUS[$status] ?? null;
|
||||
}
|
||||
|
||||
public static function getStatusKeys(): array
|
||||
{
|
||||
return array_keys(self::STATUS);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
<?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\Output\TraceOutput;
|
||||
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||
|
||||
readonly class TraceProvider 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 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 TraceOutput($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('Trace not found');
|
||||
}
|
||||
|
||||
return new TraceOutput($item);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue