diff --git a/README.md b/README.md index 1539804..ad2d3fa 100644 --- a/README.md +++ b/README.md @@ -69,6 +69,7 @@ docker exec ogcore-php php bin/console doctrine:migrations:migrate --no-interact ```sh docker exec ogcore-php php bin/console doctrine:fixtures:load --no-interaction docker exec ogcore-php php bin/console app:load-default-user-groups #cargamos los grupos por defecto +docker exec ogcore-php php bin/console app:load-default-commands #cargamos los commands por defecto ``` ## UX Api Platform @@ -133,8 +134,6 @@ docker exec ogcore-php php bin/console opengnsys:migration:hardware-profiles #ca docker exec ogcore-php php bin/console opengnsys:migration:clients #cargamos los clientes docker exec ogcore-php php bin/console opengnsys:migration:os #cargamos los sistemas operativos docker exec ogcore-php php bin/console opengnsys:migration:partition #cargamos las particiones - - ``` ## Objetos de interés diff --git a/config/api_platform/Command.yaml b/config/api_platform/Command.yaml new file mode 100644 index 0000000..425259b --- /dev/null +++ b/config/api_platform/Command.yaml @@ -0,0 +1,39 @@ +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' + - 'api_platform.filter.command.boolean' + + 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: ~ + + command_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: + identifier: false + uuid: + identifier: true \ No newline at end of file diff --git a/config/api_platform/CommandGroup.yaml b/config/api_platform/CommandGroup.yaml new file mode 100644 index 0000000..aa09ccc --- /dev/null +++ b/config/api_platform/CommandGroup.yaml @@ -0,0 +1,46 @@ +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: ~ + + add_commands: + class: ApiPlatform\Metadata\Post + method: POST + input: App\Dto\Input\CommandGroupAddCommandsInput + uriTemplate: /command-groups/{uuid}/add-commands + controller: App\Controller\CommandGroupAddCommandsAction + + command_group_execute: + class: ApiPlatform\Metadata\Post + method: POST + input: App\Dto\Input\CommandGroupExecuteInput + uriTemplate: /command-groups/{uuid}/execute + controller: App\Controller\CommandGroupExecuteAction + +properties: + App\Entity\CommandGroup: + id: + identifier: false + uuid: + identifier: true \ No newline at end of file diff --git a/config/api_platform/CommandTask.yaml b/config/api_platform/CommandTask.yaml new file mode 100644 index 0000000..d008252 --- /dev/null +++ b/config/api_platform/CommandTask.yaml @@ -0,0 +1,32 @@ +resources: + App\Entity\CommandTask: + processor: App\State\Processor\CommandTaskProcessor + input: App\Dto\Input\CommandTaskInput + output: App\Dto\Output\CommandTaskOutput + normalizationContext: + groups: ['default', 'command-task:read'] + denormalizationContext: + groups: ['command-task:write'] + operations: + ApiPlatform\Metadata\GetCollection: + provider: App\State\Provider\CommandTaskProvider + filters: + - 'api_platform.filter.command_task.order' + - 'api_platform.filter.command_task.search' + - 'api_platform.filter.command_task.boolean' + + ApiPlatform\Metadata\Get: + provider: App\State\Provider\CommandTaskProvider + ApiPlatform\Metadata\Put: + provider: App\State\Provider\CommandTaskProvider + ApiPlatform\Metadata\Patch: + provider: App\State\Provider\CommandTaskProvider + ApiPlatform\Metadata\Post: ~ + ApiPlatform\Metadata\Delete: ~ + +properties: + App\Entity\CommandTask: + id: + identifier: false + uuid: + identifier: true \ No newline at end of file diff --git a/config/api_platform/Trace.yaml b/config/api_platform/Trace.yaml new file mode 100644 index 0000000..c7e74db --- /dev/null +++ b/config/api_platform/Trace.yaml @@ -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 \ No newline at end of file diff --git a/config/packages/api_platform.yaml b/config/packages/api_platform.yaml index 352abc2..ee6fc53 100644 --- a/config/packages/api_platform.yaml +++ b/config/packages/api_platform.yaml @@ -40,4 +40,6 @@ api_platform: api_keys: apiKey: name: Authorization - type: header \ No newline at end of file + type: header + exception_to_status: + Doctrine\DBAL\Exception\ForeignKeyConstraintViolationException: 409 \ No newline at end of file diff --git a/config/services.yaml b/config/services.yaml index 50cfa8d..2613198 100644 --- a/config/services.yaml +++ b/config/services.yaml @@ -110,17 +110,36 @@ services: $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' + App\State\Provider\PxeTemplateProvider: 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' + App\State\Provider\PxeBootFileProvider: bind: $collectionProvider: '@api_platform.doctrine.orm.state.collection_provider' $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\SubnetProvider: 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' diff --git a/config/services/api_platform.yaml b/config/services/api_platform.yaml index d8f0fa4..4441b4e 100644 --- a/config/services/api_platform.yaml +++ b/config/services/api_platform.yaml @@ -8,7 +8,24 @@ services: api_platform.filter.client.search: parent: 'api_platform.doctrine.orm.search_filter' - arguments: [ { 'id': 'exact', 'name': 'partial', 'serialNumber': 'exact', organizationalUnit.id: 'exact' } ] + arguments: [ { 'id': 'exact', 'name': 'partial', 'serialNumber': 'exact', organizationalUnit.id: 'exact', mac: 'exact', ip: '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: diff --git a/migrations/Version20240916073039.php b/migrations/Version20240916073039.php new file mode 100644 index 0000000..ba99c0b --- /dev/null +++ b/migrations/Version20240916073039.php @@ -0,0 +1,31 @@ +addSql('CREATE TABLE command (id INT AUTO_INCREMENT 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, script VARCHAR(255) NOT NULL, comments VARCHAR(255) DEFAULT NULL, read_only TINYINT(1) NOT NULL, name VARCHAR(255) NOT NULL, enabled TINYINT(1) NOT NULL, UNIQUE INDEX UNIQ_8ECAEAD4D17F50A6 (uuid), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB'); + } + + public function down(Schema $schema): void + { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql('DROP TABLE command'); + } +} diff --git a/migrations/Version20240916091601.php b/migrations/Version20240916091601.php new file mode 100644 index 0000000..9f3304d --- /dev/null +++ b/migrations/Version20240916091601.php @@ -0,0 +1,37 @@ +addSql('CREATE TABLE command_group (id INT AUTO_INCREMENT 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, position INT NOT NULL, name VARCHAR(255) NOT NULL, enabled TINYINT(1) NOT NULL, UNIQUE INDEX UNIQ_FE6811F6D17F50A6 (uuid), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB'); + $this->addSql('CREATE TABLE command_group_command (command_group_id INT NOT NULL, command_id INT NOT NULL, INDEX IDX_118CE215C7B800D6 (command_group_id), INDEX IDX_118CE21533E1689A (command_id), PRIMARY KEY(command_group_id, command_id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB'); + $this->addSql('ALTER TABLE command_group_command ADD CONSTRAINT FK_118CE215C7B800D6 FOREIGN KEY (command_group_id) REFERENCES command_group (id) ON DELETE CASCADE'); + $this->addSql('ALTER TABLE command_group_command ADD CONSTRAINT FK_118CE21533E1689A FOREIGN KEY (command_id) REFERENCES command (id) ON DELETE CASCADE'); + } + + public function down(Schema $schema): void + { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql('ALTER TABLE command_group_command DROP FOREIGN KEY FK_118CE215C7B800D6'); + $this->addSql('ALTER TABLE command_group_command DROP FOREIGN KEY FK_118CE21533E1689A'); + $this->addSql('DROP TABLE command_group'); + $this->addSql('DROP TABLE command_group_command'); + } +} diff --git a/migrations/Version20240917064754.php b/migrations/Version20240917064754.php new file mode 100644 index 0000000..64f1f9b --- /dev/null +++ b/migrations/Version20240917064754.php @@ -0,0 +1,43 @@ +addSql('CREATE TABLE command_task (id INT AUTO_INCREMENT 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, datetime DATETIME NOT NULL, notes VARCHAR(255) DEFAULT NULL, status VARCHAR(255) NOT NULL, UNIQUE INDEX UNIQ_F3D475A8D17F50A6 (uuid), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB'); + $this->addSql('CREATE TABLE command_task_command (command_task_id INT NOT NULL, command_id INT NOT NULL, INDEX IDX_BB417CA862DC5265 (command_task_id), INDEX IDX_BB417CA833E1689A (command_id), PRIMARY KEY(command_task_id, command_id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB'); + $this->addSql('CREATE TABLE command_task_command_group (command_task_id INT NOT NULL, command_group_id INT NOT NULL, INDEX IDX_C43618BD62DC5265 (command_task_id), INDEX IDX_C43618BDC7B800D6 (command_group_id), PRIMARY KEY(command_task_id, command_group_id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB'); + $this->addSql('ALTER TABLE command_task_command ADD CONSTRAINT FK_BB417CA862DC5265 FOREIGN KEY (command_task_id) REFERENCES command_task (id) ON DELETE CASCADE'); + $this->addSql('ALTER TABLE command_task_command ADD CONSTRAINT FK_BB417CA833E1689A FOREIGN KEY (command_id) REFERENCES command (id) ON DELETE CASCADE'); + $this->addSql('ALTER TABLE command_task_command_group ADD CONSTRAINT FK_C43618BD62DC5265 FOREIGN KEY (command_task_id) REFERENCES command_task (id) ON DELETE CASCADE'); + $this->addSql('ALTER TABLE command_task_command_group ADD CONSTRAINT FK_C43618BDC7B800D6 FOREIGN KEY (command_group_id) REFERENCES command_group (id) ON DELETE CASCADE'); + } + + public function down(Schema $schema): void + { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql('ALTER TABLE command_task_command DROP FOREIGN KEY FK_BB417CA862DC5265'); + $this->addSql('ALTER TABLE command_task_command DROP FOREIGN KEY FK_BB417CA833E1689A'); + $this->addSql('ALTER TABLE command_task_command_group DROP FOREIGN KEY FK_C43618BD62DC5265'); + $this->addSql('ALTER TABLE command_task_command_group DROP FOREIGN KEY FK_C43618BDC7B800D6'); + $this->addSql('DROP TABLE command_task'); + $this->addSql('DROP TABLE command_task_command'); + $this->addSql('DROP TABLE command_task_command_group'); + } +} diff --git a/migrations/Version20240917091950.php b/migrations/Version20240917091950.php new file mode 100644 index 0000000..7645fa2 --- /dev/null +++ b/migrations/Version20240917091950.php @@ -0,0 +1,35 @@ +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'); + } +} diff --git a/migrations/Version20240917092207.php b/migrations/Version20240917092207.php new file mode 100644 index 0000000..f291bd8 --- /dev/null +++ b/migrations/Version20240917092207.php @@ -0,0 +1,31 @@ +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'); + } +} diff --git a/migrations/Version20240924071858.php b/migrations/Version20240924071858.php new file mode 100644 index 0000000..d758402 --- /dev/null +++ b/migrations/Version20240924071858.php @@ -0,0 +1,31 @@ +addSql('ALTER TABLE command CHANGE script script LONGTEXT NOT NULL'); + } + + public function down(Schema $schema): void + { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql('ALTER TABLE command CHANGE script script VARCHAR(255) NOT NULL'); + } +} diff --git a/migrations/Version20240926104532.php b/migrations/Version20240926104532.php new file mode 100644 index 0000000..125bc93 --- /dev/null +++ b/migrations/Version20240926104532.php @@ -0,0 +1,32 @@ +addSql('ALTER TABLE command_group DROP position'); + } + + public function down(Schema $schema): void + { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql('ALTER TABLE organizational_unit ADD FK_749AEB2DC56641EE FOREIGN KEY (remote_calendar_id) REFERENCES remote_calendar (id)'); + $this->addSql('ALTER TABLE command_group ADD position INT NOT NULL'); + } +} diff --git a/src/Command/LoadDefaultCommandsCommand.php b/src/Command/LoadDefaultCommandsCommand.php new file mode 100644 index 0000000..8069f4e --- /dev/null +++ b/src/Command/LoadDefaultCommandsCommand.php @@ -0,0 +1,94 @@ + 'Encender', + 'enabled' => true, + 'readOnly' => true, + ], + [ + 'name' => 'Apagar', + 'enabled' => true, + 'readOnly' => true, + ], + [ + 'name' => 'Restaurar Imagen', + 'enabled' => true, + 'readOnly' => true, + ], + [ + 'name' => 'Crear Imagen', + 'enabled' => true, + 'readOnly' => true, + ], + [ + 'name' => 'Reiniciar', + 'enabled' => true, + 'readOnly' => true, + ], + [ + 'name' => 'Inventario Hardware', + 'enabled' => true, + 'readOnly' => true, + ], + [ + 'name' => 'Inventario Software', + 'enabled' => true, + 'readOnly' => true, + ], + [ + 'name' => 'Ejecutar Script', + 'enabled' => true, + 'readOnly' => true, + ], + [ + 'name' => 'Iniciar Sesion', + 'enabled' => true, + 'readOnly' => true, + ], + [ + 'name' => 'Particionar y Formatear', + 'enabled' => true, + 'readOnly' => true, + ], + [ + 'name' => 'Eliminar Imagen Cache', + 'enabled' => true, + 'readOnly' => true, + ], + ]; + + foreach ($commands as $command) { + $entity = new \App\Entity\Command(); + $entity->setName($command['name']); + $entity->setScript(''); + $entity->setEnabled($command['enabled']); + $entity->setReadOnly($command['readOnly']); + + $this->entityManager->persist($entity); + } + + $this->entityManager->flush(); + + return 1; + } + +} \ No newline at end of file diff --git a/src/Controller/CommandExecuteAction.php b/src/Controller/CommandExecuteAction.php new file mode 100644 index 0000000..fbdfda9 --- /dev/null +++ b/src/Controller/CommandExecuteAction.php @@ -0,0 +1,44 @@ +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); + } +} \ No newline at end of file diff --git a/src/Controller/CommandGroupAddCommandsAction.php b/src/Controller/CommandGroupAddCommandsAction.php new file mode 100644 index 0000000..e779057 --- /dev/null +++ b/src/Controller/CommandGroupAddCommandsAction.php @@ -0,0 +1,35 @@ +commands; + + /** @var Command $command */ + foreach ($commands as $command) { + $commandGroup->addCommand($command->getEntity()); + } + + $this->entityManager->persist($commandGroup); + $this->entityManager->flush(); + + return new JsonResponse(data: 'Commands added to command group successfully', status: Response::HTTP_OK); + } +} \ No newline at end of file diff --git a/src/Controller/CommandGroupExecuteAction.php b/src/Controller/CommandGroupExecuteAction.php new file mode 100644 index 0000000..563a488 --- /dev/null +++ b/src/Controller/CommandGroupExecuteAction.php @@ -0,0 +1,46 @@ +clients; + + foreach ($commandGroup->getCommands() as $command) { + /** @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 group executed successfully', status: Response::HTTP_OK); + } +} \ No newline at end of file diff --git a/src/Dto/Input/CommandExecuteInput.php b/src/Dto/Input/CommandExecuteInput.php new file mode 100644 index 0000000..4630a47 --- /dev/null +++ b/src/Dto/Input/CommandExecuteInput.php @@ -0,0 +1,17 @@ +name = $commandGroup->getName(); + + if ($commandGroup->getCommands()) { + foreach ($commandGroup->getCommands() as $command) { + $this->commands[] = new CommandOutput($command); + } + } + + $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 ?? [] ); + + return $commandGroup; + } +} diff --git a/src/Dto/Input/CommandInput.php b/src/Dto/Input/CommandInput.php new file mode 100644 index 0000000..10697d8 --- /dev/null +++ b/src/Dto/Input/CommandInput.php @@ -0,0 +1,77 @@ +name = $command->getName(); + $this->script = $command->getScript(); + $this->enabled = $command->isEnabled(); + $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->setEnabled($this->enabled); + $command->setReadOnly($this->readOnly); + $command->setComments($this->comments); + + return $command; + } +} \ No newline at end of file diff --git a/src/Dto/Input/CommandTaskInput.php b/src/Dto/Input/CommandTaskInput.php new file mode 100644 index 0000000..c7e08e7 --- /dev/null +++ b/src/Dto/Input/CommandTaskInput.php @@ -0,0 +1,120 @@ +getCommands()) { + foreach ($commandTask->getCommands() as $command) { + $this->commands[] = new CommandOutput($command); + } + } + + if ($commandTask->getCommandGroups()) { + foreach ($commandTask->getCommandGroups() as $commandGroup) { + $this->commandGroups[] = new CommandGroupOutput($commandGroup); + } + } + + if ($commandTask->getClients()) { + foreach ($commandTask->getClients() as $client) { + $this->clients[] = new ClientOutput($client); + } + } + + $this->dateTime = $commandTask->getDatetime(); + $this->notes = $commandTask->getNotes(); + } + + public function createOrUpdateEntity(?CommandTask $commandTask = null): CommandTask + { + if (!$commandTask) { + $commandTask = new CommandTask(); + } + + foreach ($this->commands as $command) { + $commandsToAdd[] = $command->getEntity(); + } + + $commandTask->setCommands( $commandsToAdd ?? [] ); + + foreach ($this->commandGroups as $commandGroup) { + $commandGroupsToAdd[] = $commandGroup->getEntity(); + } + + $commandTask->setCommandGroups( $commandGroupsToAdd ?? [] ); + + foreach ($this->clients as $client) { + $clientsToAdd[] = $client->getEntity(); + } + + $commandTask->setClients( $clientsToAdd ?? [] ); + + $commandTask->setDatetime($this->dateTime); + $commandTask->setStatus(CommandTaskStatus::PENDING); + $commandTask->setNotes($this->notes); + + return $commandTask; + } +} diff --git a/src/Dto/Output/ClientOutput.php b/src/Dto/Output/ClientOutput.php index 97c0b5a..97f9da5 100644 --- a/src/Dto/Output/ClientOutput.php +++ b/src/Dto/Output/ClientOutput.php @@ -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'])] diff --git a/src/Dto/Output/CommandGroupOutput.php b/src/Dto/Output/CommandGroupOutput.php new file mode 100644 index 0000000..617bf79 --- /dev/null +++ b/src/Dto/Output/CommandGroupOutput.php @@ -0,0 +1,38 @@ +name = $commandGroup->getName(); + + $this->commands = $commandGroup->getCommands()->map( + fn(Command $command) => new CommandOutput($command) + )->toArray(); + + $this->createdAt = $commandGroup->getCreatedAt(); + $this->createdBy = $commandGroup->getCreatedBy(); + } +} \ No newline at end of file diff --git a/src/Dto/Output/CommandOutput.php b/src/Dto/Output/CommandOutput.php new file mode 100644 index 0000000..394a5d1 --- /dev/null +++ b/src/Dto/Output/CommandOutput.php @@ -0,0 +1,45 @@ +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(); + } +} \ No newline at end of file diff --git a/src/Dto/Output/CommandTaskOutput.php b/src/Dto/Output/CommandTaskOutput.php new file mode 100644 index 0000000..76c519c --- /dev/null +++ b/src/Dto/Output/CommandTaskOutput.php @@ -0,0 +1,62 @@ +commands = $commandTask->getCommands()->map( + fn(Command $command) => new CommandOutput($command) + )->toArray(); + + $this->commandGroups = $commandTask->getCommandGroups()->map( + fn(CommandGroup $commandGroup) => new CommandGroupOutput($commandGroup) + )->toArray(); + + $this->clients = $commandTask->getClients()->map( + fn(Client $client) => new ClientOutput($client) + )->toArray(); + + $this->dateTime = $commandTask->getDateTime(); + $this->notes = $commandTask->getNotes(); + $this->status = $commandTask->getStatus(); + $this->createdAt = $commandTask->getCreatedAt(); + $this->createdBy = $commandTask->getCreatedBy(); + } +} \ No newline at end of file diff --git a/src/Dto/Output/TraceOutput.php b/src/Dto/Output/TraceOutput.php new file mode 100644 index 0000000..265a9b2 --- /dev/null +++ b/src/Dto/Output/TraceOutput.php @@ -0,0 +1,50 @@ +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(); + } +} \ No newline at end of file diff --git a/src/Entity/Command.php b/src/Entity/Command.php new file mode 100644 index 0000000..f43aa30 --- /dev/null +++ b/src/Entity/Command.php @@ -0,0 +1,134 @@ + + */ + #[ORM\ManyToMany(targetEntity: CommandGroup::class, mappedBy: 'commands')] + private Collection $commandGroups; + + /** + * @var Collection + */ + #[ORM\ManyToMany(targetEntity: CommandTask::class, mappedBy: 'commands')] + private Collection $commandTasks; + + public function __construct() + { + parent::__construct(); + $this->commandGroups = new ArrayCollection(); + $this->commandTasks = new ArrayCollection(); + } + + public function getScript(): ?string + { + return $this->script; + } + + public function setScript(string $script): static + { + $this->script = $script; + + return $this; + } + + public function getComments(): ?string + { + return $this->comments; + } + + public function setComments(?string $comments): static + { + $this->comments = $comments; + + return $this; + } + + public function isReadOnly(): ?bool + { + return $this->readOnly; + } + + public function setReadOnly(bool $readOnly): static + { + $this->readOnly = $readOnly; + + return $this; + } + + /** + * @return Collection + */ + public function getCommandGroups(): Collection + { + return $this->commandGroups; + } + + public function addCommandGroup(CommandGroup $commandGroup): static + { + if (!$this->commandGroups->contains($commandGroup)) { + $this->commandGroups->add($commandGroup); + $commandGroup->addCommand($this); + } + + return $this; + } + + public function removeCommandGroup(CommandGroup $commandGroup): static + { + if ($this->commandGroups->removeElement($commandGroup)) { + $commandGroup->removeCommand($this); + } + + return $this; + } + + /** + * @return Collection + */ + public function getCommandTasks(): Collection + { + return $this->commandTasks; + } + + public function addCommandTask(CommandTask $commandTask): static + { + if (!$this->commandTasks->contains($commandTask)) { + $this->commandTasks->add($commandTask); + $commandTask->addCommand($this); + } + + return $this; + } + + public function removeCommandTask(CommandTask $commandTask): static + { + if ($this->commandTasks->removeElement($commandTask)) { + $commandTask->removeCommand($this); + } + + return $this; + } +} diff --git a/src/Entity/CommandGroup.php b/src/Entity/CommandGroup.php new file mode 100644 index 0000000..90bec8e --- /dev/null +++ b/src/Entity/CommandGroup.php @@ -0,0 +1,96 @@ + + */ + #[ORM\ManyToMany(targetEntity: Command::class, inversedBy: 'commandGroups')] + private Collection $commands; + + /** + * @var Collection + */ + #[ORM\ManyToMany(targetEntity: CommandTask::class, mappedBy: 'commandGroups')] + private Collection $commandTasks; + + public function __construct() + { + parent::__construct(); + $this->commands = new ArrayCollection(); + $this->commandTasks = new ArrayCollection(); + } + + /** + * @return Collection + */ + public function getCommands(): Collection + { + 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)) { + $this->commands->add($command); + } + + return $this; + } + + public function removeCommand(Command $command): static + { + $this->commands->removeElement($command); + + return $this; + } + + /** + * @return Collection + */ + public function getCommandTasks(): Collection + { + return $this->commandTasks; + } + + public function addCommandTask(CommandTask $commandTask): static + { + if (!$this->commandTasks->contains($commandTask)) { + $this->commandTasks->add($commandTask); + $commandTask->addCommandGroup($this); + } + + return $this; + } + + public function removeCommandTask(CommandTask $commandTask): static + { + if ($this->commandTasks->removeElement($commandTask)) { + $commandTask->removeCommandGroup($this); + } + + return $this; + } +} diff --git a/src/Entity/CommandTask.php b/src/Entity/CommandTask.php new file mode 100644 index 0000000..b742212 --- /dev/null +++ b/src/Entity/CommandTask.php @@ -0,0 +1,190 @@ + + */ + #[ORM\ManyToMany(targetEntity: Command::class, inversedBy: 'commandTasks')] + private Collection $commands; + + /** + * @var Collection + */ + #[ORM\ManyToMany(targetEntity: CommandGroup::class, inversedBy: 'commandTasks')] + private Collection $commandGroups; + + #[ORM\Column(type: Types::DATETIME_MUTABLE)] + private ?\DateTimeInterface $datetime = null; + + #[ORM\Column(length: 255, nullable: true)] + private ?string $notes = null; + + #[ORM\Column(length: 255)] + private ?string $status = null; + + /** + * @var Collection + */ + #[ORM\ManyToMany(targetEntity: Client::class)] + private Collection $clients; + + public function __construct() + { + parent::__construct(); + + $this->commands = new ArrayCollection(); + $this->commandGroups = new ArrayCollection(); + $this->clients = new ArrayCollection(); + } + + /** + * @return Collection + */ + public function getCommands(): Collection + { + 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)) { + $this->commands->add($command); + } + + return $this; + } + + public function removeCommand(Command $command): static + { + $this->commands->removeElement($command); + + return $this; + } + + /** + * @return Collection + */ + public function getCommandGroups(): Collection + { + return $this->commandGroups; + } + + public function setCommandGroups(array $commandGroups): static + { + $this->commandGroups->clear(); + + foreach ($commandGroups as $commandGroup){ + $this->addCommandGroup($commandGroup); + } + + return $this; + } + + public function addCommandGroup(CommandGroup $commandGroup): static + { + if (!$this->commandGroups->contains($commandGroup)) { + $this->commandGroups->add($commandGroup); + } + + return $this; + } + + public function removeCommandGroup(CommandGroup $commandGroup): static + { + $this->commandGroups->removeElement($commandGroup); + + return $this; + } + + public function getDatetime(): ?\DateTimeInterface + { + return $this->datetime; + } + + public function setDatetime(\DateTimeInterface $datetime): static + { + $this->datetime = $datetime; + + return $this; + } + + public function getNotes(): ?string + { + return $this->notes; + } + + public function setNotes(?string $notes): static + { + $this->notes = $notes; + + return $this; + } + + public function getStatus(): ?string + { + return $this->status; + } + + public function setStatus(string $status): static + { + $this->status = $status; + + return $this; + } + + /** + * @return Collection + */ + public function getClients(): Collection + { + return $this->clients; + } + + public function addClient(Client $client): static + { + if (!$this->clients->contains($client)) { + $this->clients->add($client); + } + + return $this; + } + + public function removeClient(Client $client): static + { + $this->clients->removeElement($client); + + return $this; + } + + public function setClients(array $clients): static + { + $this->clients->clear(); + + foreach ($clients as $client){ + $this->addClient($client); + } + + return $this; + } +} diff --git a/src/Entity/Trace.php b/src/Entity/Trace.php new file mode 100644 index 0000000..1f2f318 --- /dev/null +++ b/src/Entity/Trace.php @@ -0,0 +1,103 @@ +client; + } + + public function setClient(?Client $client): static + { + $this->client = $client; + + return $this; + } + + public function getCommand(): ?Command + { + return $this->command; + } + + public function setCommand(?Command $command): static + { + $this->command = $command; + + return $this; + } + + public function getStatus(): ?string + { + return $this->status; + } + + public function setStatus(string $status): static + { + $this->status = $status; + + return $this; + } + + public function getOutput(): ?string + { + return $this->output; + } + + public function setOutput(?string $output): static + { + $this->output = $output; + + return $this; + } + + public function getExecutedAt(): ?\DateTimeInterface + { + return $this->executedAt; + } + + public function setExecutedAt(\DateTimeInterface $executedAt): static + { + $this->executedAt = $executedAt; + + return $this; + } + + public function getFinishedAt(): ?\DateTimeInterface + { + return $this->finishedAt; + } + + public function setFinishedAt(\DateTimeInterface $finishedAt): static + { + $this->finishedAt = $finishedAt; + + return $this; + } +} diff --git a/src/Factory/CommandFactory.php b/src/Factory/CommandFactory.php new file mode 100644 index 0000000..2e318ed --- /dev/null +++ b/src/Factory/CommandFactory.php @@ -0,0 +1,59 @@ + + */ +final class CommandFactory extends ModelFactory +{ + /** + * @see https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#factories-as-services + * + * @todo inject services if required + */ + public function __construct() + { + parent::__construct(); + } + + /** + * @see https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#model-factories + * + * @todo add your default values here + */ + protected function getDefaults(): array + { + return [ + 'createdAt' => self::faker()->dateTime(), + 'enabled' => self::faker()->boolean(), + 'name' => self::faker()->text(255), + 'readOnly' => self::faker()->boolean(), + 'script' => self::faker()->text(255), + 'updatedAt' => self::faker()->dateTime() + ]; + } + + /** + * @see https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#initialization + */ + protected function initialize(): self + { + return $this + // ->afterInstantiate(function(Command $command): void {}) + ; + } + + + public static function getClass(): string + { + return Command::class; + } +} diff --git a/src/Factory/CommandGroupFactory.php b/src/Factory/CommandGroupFactory.php new file mode 100644 index 0000000..4608586 --- /dev/null +++ b/src/Factory/CommandGroupFactory.php @@ -0,0 +1,56 @@ + + */ +final class CommandGroupFactory extends ModelFactory +{ + /** + * @see https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#factories-as-services + * + * @todo inject services if required + */ + public function __construct() + { + parent::__construct(); + } + + public static function getClass(): string + { + return CommandGroup::class; + } + + /** + * @see https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#model-factories + * + * @todo add your default values here + */ + protected function getDefaults(): array + { + return [ + 'createdAt' => self::faker()->dateTime(), + 'enabled' => self::faker()->boolean(), + 'name' => self::faker()->text(255), + 'updatedAt' => self::faker()->dateTime() + ]; + } + + /** + * @see https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#initialization + */ + protected function initialize(): self + { + return $this + // ->afterInstantiate(function(CommandGroup $commandGroup): void {}) + ; + } +} diff --git a/src/Factory/CommandTaskFactory.php b/src/Factory/CommandTaskFactory.php new file mode 100644 index 0000000..91d2d09 --- /dev/null +++ b/src/Factory/CommandTaskFactory.php @@ -0,0 +1,56 @@ + + */ +final class CommandTaskFactory extends ModelFactory +{ + /** + * @see https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#factories-as-services + * + * @todo inject services if required + */ + public function __construct() + { + parent::__construct(); + } + + public static function getClass(): string + { + return CommandTask::class; + } + + /** + * @see https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#model-factories + * + * @todo add your default values here + */ + protected function getDefaults(): array + { + return [ + 'createdAt' => self::faker()->dateTime(), + 'datetime' => self::faker()->dateTime(), + 'status' => self::faker()->text(255), + 'updatedAt' => self::faker()->dateTime() + ]; + } + + /** + * @see https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#initialization + */ + protected function initialize(): self + { + return $this + // ->afterInstantiate(function(CommandTask $commandTask): void {}) + ; + } +} diff --git a/src/Factory/TraceFactory.php b/src/Factory/TraceFactory.php new file mode 100644 index 0000000..14aec4b --- /dev/null +++ b/src/Factory/TraceFactory.php @@ -0,0 +1,58 @@ + + */ +final class TraceFactory extends ModelFactory +{ + /** + * @see https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#factories-as-services + * + * @todo inject services if required + */ + public function __construct() + { + parent::__construct(); + } + + public static function getClass(): string + { + return Trace::class; + } + + /** + * @see https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#model-factories + * + * @todo add your default values here + */ + protected function getDefaults(): array + { + return [ + 'client' => ClientFactory::new(), + 'command' => CommandFactory::new(), + 'createdAt' => self::faker()->dateTime(), + 'executedAt' => self::faker()->dateTime(), + 'status' => self::faker()->text(255), + 'updatedAt' => self::faker()->dateTime() + ]; + } + + /** + * @see https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#initialization + */ + protected function initialize(): static + { + return $this + // ->afterInstantiate(function(Trace $trace): void {}) + ; + } +} diff --git a/src/Model/CommandTaskStatus.php b/src/Model/CommandTaskStatus.php new file mode 100644 index 0000000..9c1cab1 --- /dev/null +++ b/src/Model/CommandTaskStatus.php @@ -0,0 +1,33 @@ + 'Pendiente', + self::IN_PROGRESS => 'En progreso', + self::COMPLETED => 'Completado', + self::FAILED => 'Fallido', + ]; + + public static function getStatus(): array + { + return self::STATUS; + } + + public static function getCommandTaskStatus(string $status): ?string + { + return self::STATUS[$status] ?? null; + } + + public static function getStatusKeys(): array + { + return array_keys(self::STATUS); + } +} \ No newline at end of file diff --git a/src/Model/TraceStatus.php b/src/Model/TraceStatus.php new file mode 100644 index 0000000..fc790b2 --- /dev/null +++ b/src/Model/TraceStatus.php @@ -0,0 +1,33 @@ + '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); + } +} \ No newline at end of file diff --git a/src/Repository/CommandGroupRepository.php b/src/Repository/CommandGroupRepository.php new file mode 100644 index 0000000..f8cf8b2 --- /dev/null +++ b/src/Repository/CommandGroupRepository.php @@ -0,0 +1,18 @@ + + */ +class CommandGroupRepository extends AbstractRepository +{ + public function __construct(ManagerRegistry $registry) + { + parent::__construct($registry, CommandGroup::class); + } +} diff --git a/src/Repository/CommandRepository.php b/src/Repository/CommandRepository.php new file mode 100644 index 0000000..544b708 --- /dev/null +++ b/src/Repository/CommandRepository.php @@ -0,0 +1,18 @@ + + */ +class CommandRepository extends AbstractRepository +{ + public function __construct(ManagerRegistry $registry) + { + parent::__construct($registry, Command::class); + } +} diff --git a/src/Repository/CommandTaskRepository.php b/src/Repository/CommandTaskRepository.php new file mode 100644 index 0000000..fc859f8 --- /dev/null +++ b/src/Repository/CommandTaskRepository.php @@ -0,0 +1,18 @@ + + */ +class CommandTaskRepository extends AbstractRepository +{ + public function __construct(ManagerRegistry $registry) + { + parent::__construct($registry, CommandTask::class); + } +} diff --git a/src/Repository/TraceRepository.php b/src/Repository/TraceRepository.php new file mode 100644 index 0000000..102fa7b --- /dev/null +++ b/src/Repository/TraceRepository.php @@ -0,0 +1,19 @@ + + */ +class TraceRepository extends AbstractRepository +{ + public function __construct(ManagerRegistry $registry) + { + parent::__construct($registry, Trace::class); + } + +} diff --git a/src/State/Processor/CommandGroupProcessor.php b/src/State/Processor/CommandGroupProcessor.php new file mode 100644 index 0000000..65ffacd --- /dev/null +++ b/src/State/Processor/CommandGroupProcessor.php @@ -0,0 +1,69 @@ +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; + } +} diff --git a/src/State/Processor/CommandProcessor.php b/src/State/Processor/CommandProcessor.php new file mode 100644 index 0000000..fffae4f --- /dev/null +++ b/src/State/Processor/CommandProcessor.php @@ -0,0 +1,68 @@ +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/Processor/CommandTaskProcessor.php b/src/State/Processor/CommandTaskProcessor.php new file mode 100644 index 0000000..bad348b --- /dev/null +++ b/src/State/Processor/CommandTaskProcessor.php @@ -0,0 +1,68 @@ +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 = []): CommandTaskOutput + { + if (!($data instanceof CommandTaskInput)) { + throw new \Exception(sprintf('data is not instance of %s', CommandTaskInput::class)); + } + + $entity = null; + if (isset($uriVariables['uuid'])) { + $entity = $this->commandTaskRepository->findOneByUuid($uriVariables['uuid']); + } + + $command = $data->createOrUpdateEntity($entity); + $this->validator->validate($command); + $this->commandTaskRepository->save($command); + + return new CommandTaskOutput($command); + } + + private function processDelete($data, Operation $operation, array $uriVariables = [], array $context = []): null + { + $user = $this->commandTaskRepository->findOneByUuid($uriVariables['uuid']); + $this->commandTaskRepository->delete($user); + + return null; + } +} diff --git a/src/State/Provider/CommandGroupProvider.php b/src/State/Provider/CommandGroupProvider.php new file mode 100644 index 0000000..767d0cc --- /dev/null +++ b/src/State/Provider/CommandGroupProvider.php @@ -0,0 +1,72 @@ +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|array|null + { + $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(); + } +} diff --git a/src/State/Provider/CommandProvider.php b/src/State/Provider/CommandProvider.php new file mode 100644 index 0000000..b5854b1 --- /dev/null +++ b/src/State/Provider/CommandProvider.php @@ -0,0 +1,71 @@ +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('Command 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(); + } +} diff --git a/src/State/Provider/CommandTaskProvider.php b/src/State/Provider/CommandTaskProvider.php new file mode 100644 index 0000000..49ef3d3 --- /dev/null +++ b/src/State/Provider/CommandTaskProvider.php @@ -0,0 +1,71 @@ +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|array|null + { + $paginator = $this->collectionProvider->provide($operation, $uriVariables, $context); + + $items = new \ArrayObject(); + foreach ($paginator->getIterator() as $item){ + $items[] = new CommandTaskOutput($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 task not found'); + } + + return new CommandTaskOutput($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 CommandTaskInput($item) : null; + } + + return new CommandTaskInput(); + } +} diff --git a/src/State/Provider/TraceProvider.php b/src/State/Provider/TraceProvider.php new file mode 100644 index 0000000..aeefd56 --- /dev/null +++ b/src/State/Provider/TraceProvider.php @@ -0,0 +1,56 @@ +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); + } +} diff --git a/tests/Functional/CommandGroupTest.php b/tests/Functional/CommandGroupTest.php new file mode 100644 index 0000000..de5faef --- /dev/null +++ b/tests/Functional/CommandGroupTest.php @@ -0,0 +1,127 @@ + self::USER_ADMIN, 'roles'=> [UserGroupPermissions::ROLE_SUPER_ADMIN]]); + + CommandGroupFactory::createMany(10); + + $this->createClientWithCredentials()->request('GET', '/command-groups'); + $this->assertResponseStatusCodeSame(Response::HTTP_OK); + $this->assertResponseHeaderSame('content-type', 'application/ld+json; charset=utf-8'); + $this->assertJsonContains([ + '@context' => '/contexts/CommandGroup', + '@id' => '/command-groups', + '@type' => 'hydra:Collection', + 'hydra:totalItems' => 10, + ]); + } + + /** + * @throws RedirectionExceptionInterface + * @throws DecodingExceptionInterface + * @throws ClientExceptionInterface + * @throws TransportExceptionInterface + * @throws ServerExceptionInterface + */ + public function testCreateCommandGroup(): void + { + UserFactory::createOne(['username' => self::USER_ADMIN, 'roles'=> [UserGroupPermissions::ROLE_SUPER_ADMIN]]); + + CommandFactory::createOne(['name' => self::CMD_CREATE]); + $commandIri = $this->findIriBy(Command::class, ['name' => self::CMD_CREATE]); + + $this->createClientWithCredentials()->request('POST', '/command-groups',['json' => [ + 'name' => self::CMD_GROUP_CREATE, + 'commands' => [ + $commandIri + ] + ]]); + + $this->assertResponseStatusCodeSame(201); + $this->assertResponseHeaderSame('content-type', 'application/ld+json; charset=utf-8'); + $this->assertJsonContains([ + '@context' => '/contexts/CommandGroupOutput', + '@type' => 'CommandGroup', + 'name' => self::CMD_GROUP_CREATE, + ]); + } + + /** + * @throws RedirectionExceptionInterface + * @throws DecodingExceptionInterface + * @throws ClientExceptionInterface + * @throws TransportExceptionInterface + * @throws ServerExceptionInterface + */ + public function testUpdateCommandGroup(): void + { + UserFactory::createOne(['username' => self::USER_ADMIN, 'roles'=> [UserGroupPermissions::ROLE_SUPER_ADMIN]]); + + CommandGroupFactory::createOne(['name' => self::CMD_GROUP_CREATE]); + $iri = $this->findIriBy(CommandGroup::class, ['name' => self::CMD_GROUP_CREATE]); + + $this->createClientWithCredentials()->request('PUT', $iri, ['json' => [ + 'name' => self::CMD_GROUP_UPDATE, + ]]); + + $this->assertResponseIsSuccessful(); + $this->assertJsonContains([ + '@id' => $iri, + 'name' => self::CMD_GROUP_UPDATE + ]); + } + + /** + * @throws TransportExceptionInterface + * @throws ServerExceptionInterface + * @throws RedirectionExceptionInterface + * @throws DecodingExceptionInterface + * @throws ClientExceptionInterface + */ + public function testDeleteCommandGroup(): void + { + UserFactory::createOne(['username' => self::USER_ADMIN, 'roles'=> [UserGroupPermissions::ROLE_SUPER_ADMIN]]); + + CommandGroupFactory::createOne(['name' => self::CMD_GROUP_DELETE]); + $iri = $this->findIriBy(CommandGroup::class, ['name' => self::CMD_GROUP_DELETE]); + + $this->createClientWithCredentials()->request('DELETE', $iri); + $this->assertResponseStatusCodeSame(204); + $this->assertNull( + static::getContainer()->get('doctrine')->getRepository(CommandGroup::class)->findOneBy(['name' => self::CMD_GROUP_DELETE]) + ); + } +} \ No newline at end of file diff --git a/tests/Functional/CommandTaskTest.php b/tests/Functional/CommandTaskTest.php new file mode 100644 index 0000000..49b9555 --- /dev/null +++ b/tests/Functional/CommandTaskTest.php @@ -0,0 +1,129 @@ + self::USER_ADMIN, 'roles'=> [UserGroupPermissions::ROLE_SUPER_ADMIN]]); + + CommandTaskFactory::createMany(10); + + $this->createClientWithCredentials()->request('GET', '/command-tasks'); + $this->assertResponseStatusCodeSame(Response::HTTP_OK); + $this->assertResponseHeaderSame('content-type', 'application/ld+json; charset=utf-8'); + $this->assertJsonContains([ + '@context' => '/contexts/CommandTask', + '@id' => '/command-tasks', + '@type' => 'hydra:Collection', + 'hydra:totalItems' => 10, + ]); + } + + /** + * @throws RedirectionExceptionInterface + * @throws DecodingExceptionInterface + * @throws ClientExceptionInterface + * @throws TransportExceptionInterface + * @throws ServerExceptionInterface + */ + public function testCreateCommandTask(): void + { + UserFactory::createOne(['username' => self::USER_ADMIN, 'roles'=> [UserGroupPermissions::ROLE_SUPER_ADMIN]]); + + CommandFactory::createOne(['name' => self::CMD_CREATE]); + $commandIri = $this->findIriBy(Command::class, ['name' => self::CMD_CREATE]); + + $date = new \DateTimeImmutable(); + + $this->createClientWithCredentials()->request('POST', '/command-tasks',['json' => [ + 'dateTime' => $date->format('Y-m-d H:i:s'), + 'notes' => self::CMD_TASK_CREATE, + 'commands' => [ + $commandIri + ] + ]]); + + $this->assertResponseStatusCodeSame(201); + $this->assertResponseHeaderSame('content-type', 'application/ld+json; charset=utf-8'); + $this->assertJsonContains([ + '@context' => '/contexts/CommandTaskOutput', + '@type' => 'CommandTask', + 'notes' => self::CMD_TASK_CREATE, + ]); + } + + /** + * @throws RedirectionExceptionInterface + * @throws DecodingExceptionInterface + * @throws ClientExceptionInterface + * @throws TransportExceptionInterface + * @throws ServerExceptionInterface + */ + public function testUpdateCommandTask(): void + { + UserFactory::createOne(['username' => self::USER_ADMIN, 'roles'=> [UserGroupPermissions::ROLE_SUPER_ADMIN]]); + + CommandTaskFactory::createOne(['notes' => self::CMD_TASK_CREATE]); + $iri = $this->findIriBy(CommandTask::class, ['notes' => self::CMD_TASK_CREATE]); + + $this->createClientWithCredentials()->request('PUT', $iri, ['json' => [ + 'notes' => self::CMD_TASK_UPDATE, + ]]); + + $this->assertResponseIsSuccessful(); + $this->assertJsonContains([ + '@id' => $iri, + 'notes' => self::CMD_TASK_UPDATE + ]); + } + + /** + * @throws TransportExceptionInterface + * @throws ServerExceptionInterface + * @throws RedirectionExceptionInterface + * @throws DecodingExceptionInterface + * @throws ClientExceptionInterface + */ + public function testDeleteCommandTask(): void + { + UserFactory::createOne(['username' => self::USER_ADMIN, 'roles'=> [UserGroupPermissions::ROLE_SUPER_ADMIN]]); + + CommandTaskFactory::createOne(['notes' => self::CMD_TASK_DELETE]); + $iri = $this->findIriBy(CommandTask::class, ['notes' => self::CMD_TASK_DELETE]); + + $this->createClientWithCredentials()->request('DELETE', $iri); + $this->assertResponseStatusCodeSame(204); + $this->assertNull( + static::getContainer()->get('doctrine')->getRepository(CommandTask::class)->findOneBy(['notes' => self::CMD_TASK_DELETE]) + ); + } +} \ No newline at end of file diff --git a/tests/Functional/CommandTest.php b/tests/Functional/CommandTest.php new file mode 100644 index 0000000..68a55ad --- /dev/null +++ b/tests/Functional/CommandTest.php @@ -0,0 +1,122 @@ + self::USER_ADMIN, 'roles'=> [UserGroupPermissions::ROLE_SUPER_ADMIN]]); + + CommandFactory::createMany(10); + + $this->createClientWithCredentials()->request('GET', '/commands'); + $this->assertResponseStatusCodeSame(Response::HTTP_OK); + $this->assertResponseHeaderSame('content-type', 'application/ld+json; charset=utf-8'); + $this->assertJsonContains([ + '@context' => '/contexts/Command', + '@id' => '/commands', + '@type' => 'hydra:Collection', + 'hydra:totalItems' => 10, + ]); + } + + /** + * @throws RedirectionExceptionInterface + * @throws DecodingExceptionInterface + * @throws ClientExceptionInterface + * @throws TransportExceptionInterface + * @throws ServerExceptionInterface + */ + public function testCreateCommand(): void + { + UserFactory::createOne(['username' => self::USER_ADMIN, 'roles'=> [UserGroupPermissions::ROLE_SUPER_ADMIN]]); + + $this->createClientWithCredentials()->request('POST', '/commands',['json' => [ + 'name' => self::CMD_CREATE, + 'script' => 'echo "Hello World!"', + ]]); + + $this->assertResponseStatusCodeSame(201); + $this->assertResponseHeaderSame('content-type', 'application/ld+json; charset=utf-8'); + $this->assertJsonContains([ + '@context' => '/contexts/CommandOutput', + '@type' => 'Command', + 'name' => self::CMD_CREATE, + 'script' => 'echo "Hello World!"' + ]); + } + + /** + * @throws RedirectionExceptionInterface + * @throws DecodingExceptionInterface + * @throws ClientExceptionInterface + * @throws TransportExceptionInterface + * @throws ServerExceptionInterface + */ + public function testUpdateCommand(): void + { + UserFactory::createOne(['username' => self::USER_ADMIN, 'roles'=> [UserGroupPermissions::ROLE_SUPER_ADMIN]]); + + CommandFactory::createOne(['name' => self::CMD_CREATE]); + $iri = $this->findIriBy(Command::class, ['name' => self::CMD_CREATE]); + + $this->createClientWithCredentials()->request('PUT', $iri, ['json' => [ + 'name' => self::CMD_UPDATE, + 'readOnly' => true + ]]); + + $this->assertResponseIsSuccessful(); + $this->assertJsonContains([ + '@id' => $iri, + 'name' => self::CMD_UPDATE, + 'readOnly' => true + ]); + } + + /** + * @throws TransportExceptionInterface + * @throws ServerExceptionInterface + * @throws RedirectionExceptionInterface + * @throws DecodingExceptionInterface + * @throws ClientExceptionInterface + */ + public function testDeleteCommand(): void + { + UserFactory::createOne(['username' => self::USER_ADMIN, 'roles'=> [UserGroupPermissions::ROLE_SUPER_ADMIN]]); + + CommandFactory::createOne(['name' => self::CMD_DELETE]); + $iri = $this->findIriBy(Command::class, ['name' => self::CMD_DELETE]); + + $this->createClientWithCredentials()->request('DELETE', $iri); + $this->assertResponseStatusCodeSame(204); + $this->assertNull( + static::getContainer()->get('doctrine')->getRepository(Command::class)->findOneBy(['name' => self::CMD_DELETE]) + ); + } +} \ No newline at end of file diff --git a/translations/validators.en.yaml b/translations/validators.en.yaml index ad9b5aa..5281532 100644 --- a/translations/validators.en.yaml +++ b/translations/validators.en.yaml @@ -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.' diff --git a/translations/validators.es.yaml b/translations/validators.es.yaml index 9bf6d15..5a2984d 100644 --- a/translations/validators.es.yaml +++ b/translations/validators.es.yaml @@ -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.'