diff --git a/CHANGELOG.md b/CHANGELOG.md index 9324f93..9422a7c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,22 @@ # Changelog +<<<<<<< HEAD +## [0.12.0] - 2025-05-13 +### Added +- Se ha añadido nueva API para poder gestionar las tareas y acciones programadas. +- Se ha añadido un nuevo campo en plantillas (defecto) el cual nos permite tener una plantilla por defecto en caso de que se elimine una. +- Se ha comenzado la integracion con ogGit. + +## Improved +- Mejorado el comportamiento de la API al crear una imagen. Ahora se guardan datos del pc de origen. + +## Fixed +- Se ha corregido el bug en la creacion de clientes masivos donde no se le asignaba la plantilla PXE. +- Se ha corregido un bug en el DTO de clientes, que hacia que PHP diera un timeout por bucle infinito. +======= ## [0.11.2] - 2025-04-23 ### Fixed - Se ha cambiado la forma en guardar la fecha al recibir "ping" de los clientes. +>>>>>>> main --- ## [0.11.1] - 2025-04-16 diff --git a/config/api_platform/CommandTaskSchedule.yaml b/config/api_platform/CommandTaskSchedule.yaml index e69de29..2f90cb9 100644 --- a/config/api_platform/CommandTaskSchedule.yaml +++ b/config/api_platform/CommandTaskSchedule.yaml @@ -0,0 +1,32 @@ +resources: + App\Entity\CommandTaskSchedule: + processor: App\State\Processor\CommandTaskScheduleProcessor + input: App\Dto\Input\CommandTaskScheduleInput + output: App\Dto\Output\CommandTaskScheduleOutput + normalizationContext: + groups: ['default', 'command-task-schedule:read'] + denormalizationContext: + groups: ['command-task-schedule:write'] + operations: + ApiPlatform\Metadata\GetCollection: + provider: App\State\Provider\CommandTaskScheduleProvider + filters: + - 'api_platform.filter.command_task_schedule.order' + - 'api_platform.filter.command_task_schedule.search' + - 'api_platform.filter.command_task_schedule.boolean' + + ApiPlatform\Metadata\Get: + provider: App\State\Provider\CommandTaskScheduleProvider + ApiPlatform\Metadata\Put: + provider: App\State\Provider\CommandTaskScheduleProvider + ApiPlatform\Metadata\Patch: + provider: App\State\Provider\CommandTaskScheduleProvider + ApiPlatform\Metadata\Post: ~ + ApiPlatform\Metadata\Delete: ~ + +properties: + App\Entity\CommandTaskSchedule: + id: + identifier: false + uuid: + identifier: true \ No newline at end of file diff --git a/config/api_platform/CommandTaskScript.yaml b/config/api_platform/CommandTaskScript.yaml new file mode 100644 index 0000000..cadc386 --- /dev/null +++ b/config/api_platform/CommandTaskScript.yaml @@ -0,0 +1,32 @@ +resources: + App\Entity\CommandTaskScript: + processor: App\State\Processor\CommandTaskScriptProcessor + input: App\Dto\Input\CommandTaskScriptInput + output: App\Dto\Output\CommandTaskScriptOutput + normalizationContext: + groups: ['default', 'command-task-script:read'] + denormalizationContext: + groups: ['command-task-script:write'] + operations: + ApiPlatform\Metadata\GetCollection: + provider: App\State\Provider\CommandTaskScriptProvider + filters: + - 'api_platform.filter.command_task_script.order' + - 'api_platform.filter.command_task_script.search' + - 'api_platform.filter.command_task_script.boolean' + + ApiPlatform\Metadata\Get: + provider: App\State\Provider\CommandTaskScriptProvider + ApiPlatform\Metadata\Put: + provider: App\State\Provider\CommandTaskScriptProvider + ApiPlatform\Metadata\Patch: + provider: App\State\Provider\CommandTaskScriptProvider + ApiPlatform\Metadata\Post: ~ + ApiPlatform\Metadata\Delete: ~ + +properties: + App\Entity\CommandTaskScript: + id: + identifier: false + uuid: + identifier: true \ No newline at end of file diff --git a/config/api_platform/GitImageRepository.yaml b/config/api_platform/GitImageRepository.yaml new file mode 100644 index 0000000..b1997a2 --- /dev/null +++ b/config/api_platform/GitImageRepository.yaml @@ -0,0 +1,50 @@ +resources: + App\Entity\GitImageRepository: + processor: App\State\Processor\GitImageRepositoryProcessor + input: App\Dto\Input\GitImageRepositoryInput + output: App\Dto\Output\GitImageRepositoryOutput + normalizationContext: + groups: ['default', 'git-image-repository:read'] + denormalizationContext: + groups: ['git-image-repository:write'] + operations: + ApiPlatform\Metadata\GetCollection: + provider: App\State\Provider\GitImageRepositoryProvider + filters: + - 'api_platform.filter.image_image_repository.order' + - 'api_platform.filter.image_image_repository.search' + + ApiPlatform\Metadata\Get: + provider: App\State\Provider\GitImageRepositoryProvider + ApiPlatform\Metadata\Put: + provider: App\State\Provider\GitImageRepositoryProvider + ApiPlatform\Metadata\Patch: + provider: App\State\Provider\GitImageRepositoryProvider + ApiPlatform\Metadata\Post: ~ + ApiPlatform\Metadata\Delete: ~ + + get_image_ogrepository: + shortName: OgRepository Server + description: Get image in OgRepository + class: ApiPlatform\Metadata\Get + method: GET + input: false + uriTemplate: /image-image-repositories/server/{uuid}/get + controller: App\Controller\OgRepository\Image\GetAction + + get_image_tags: + shortName: OgRepository Server + description: Get image tags in OgRepository + class: ApiPlatform\Metadata\Get + method: GET + input: false + uriTemplate: /git-image-repositories/server/{uuid}/get-tags + controller: App\Controller\OgRepository\Git\GetTagsAction + + +properties: + App\Entity\GitImageRepository: + id: + identifier: false + uuid: + identifier: true \ No newline at end of file diff --git a/config/api_platform/ImageRepository.yaml b/config/api_platform/ImageRepository.yaml index 97cd0b9..346aceb 100644 --- a/config/api_platform/ImageRepository.yaml +++ b/config/api_platform/ImageRepository.yaml @@ -30,7 +30,7 @@ resources: method: POST input: false uriTemplate: /image-repositories/server/{uuid}/sync - controller: App\Controller\OgRepository\SyncAction + controller: App\Controller\OgRepository\Image\SyncAction wol_client: class: ApiPlatform\Metadata\Post @@ -46,7 +46,7 @@ resources: method: POST input: false uriTemplate: /image-repositories/server/{uuid}/get-collection - controller: App\Controller\OgRepository\GetCollectionAction + controller: App\Controller\OgRepository\Image\GetCollectionAction images_ogrepository_status: shortName: OgRepository Server @@ -75,6 +75,23 @@ resources: uriTemplate: /image-repositories/{uuid}/convert-image controller: App\Controller\OgRepository\Image\ConvertAction + get_collection_images_oggit: + shortName: OgRepository Server + description: Get collection of image in OgRepository + class: ApiPlatform\Metadata\Post + method: POST + input: false + uriTemplate: /image-repositories/server/git/{uuid}/get-collection + controller: App\Controller\OgRepository\Git\GetCollectionAction + + git_image_ogrepository_sync: + shortName: OgRepository Server + class: ApiPlatform\Metadata\Post + method: POST + input: false + uriTemplate: /image-repositories/server/git/{uuid}/sync + controller: App\Controller\OgRepository\Git\SyncAction + properties: App\Entity\ImageRepository: id: diff --git a/config/services.yaml b/config/services.yaml index fbf0dc6..879364b 100644 --- a/config/services.yaml +++ b/config/services.yaml @@ -156,3 +156,18 @@ services: bind: $collectionProvider: '@api_platform.doctrine.orm.state.collection_provider' $itemProvider: '@api_platform.doctrine.orm.state.item_provider' + + App\State\Provider\CommandTaskScheduleProvider: + bind: + $collectionProvider: '@api_platform.doctrine.orm.state.collection_provider' + $itemProvider: '@api_platform.doctrine.orm.state.item_provider' + + App\State\Provider\CommandTaskScriptProvider: + bind: + $collectionProvider: '@api_platform.doctrine.orm.state.collection_provider' + $itemProvider: '@api_platform.doctrine.orm.state.item_provider' + + App\State\Provider\GitImageRepositoryProvider: + 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 6f91d56..2a7b829 100644 --- a/config/services/api_platform.yaml +++ b/config/services/api_platform.yaml @@ -45,6 +45,52 @@ services: arguments: [ { 'enabled': ~, 'readOnly': ~ } ] tags: [ 'api_platform.filter' ] + api_platform.filter.command_task.order: + parent: 'api_platform.doctrine.orm.order_filter' + arguments: + $properties: { 'id': ~, 'name': ~ } + $orderParameterName: 'order' + tags: [ 'api_platform.filter' ] + + api_platform.filter.command_task.search: + parent: 'api_platform.doctrine.orm.search_filter' + arguments: [ { 'id': 'exact', 'name': 'partial' } ] + tags: [ 'api_platform.filter' ] + + api_platform.filter.command_task_schedule.order: + parent: 'api_platform.doctrine.orm.order_filter' + arguments: + $properties: { 'id': ~, 'name': ~ } + $orderParameterName: 'order' + tags: [ 'api_platform.filter' ] + + api_platform.filter.command_task_schedule.search: + parent: 'api_platform.doctrine.orm.search_filter' + arguments: [ { 'id': 'exact', 'name': 'partial', 'commandTask.id': 'exact' } ] + tags: [ 'api_platform.filter' ] + + api_platform.filter.command_task_schedule.boolean: + parent: 'api_platform.doctrine.orm.boolean_filter' + arguments: [ { 'enabled': ~} ] + tags: [ 'api_platform.filter' ] + + api_platform.filter.command_task_script.order: + parent: 'api_platform.doctrine.orm.order_filter' + arguments: + $properties: { 'id': ~, 'name': ~ } + $orderParameterName: 'order' + tags: [ 'api_platform.filter' ] + + api_platform.filter.command_task_script.search: + parent: 'api_platform.doctrine.orm.search_filter' + arguments: [ { 'id': 'exact', 'name': 'partial', 'commandTask.id': 'exact' } ] + tags: [ 'api_platform.filter' ] + + api_platform.filter.command_task_script.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: @@ -61,7 +107,7 @@ services: api_platform.filter.image.search: parent: 'api_platform.doctrine.orm.search_filter' - arguments: [ { 'id': 'exact', 'name': 'partial', 'repository.id': 'exact', status: 'exact'} ] + arguments: [ { 'id': 'exact', 'name': 'partial', 'repository.id': 'exact', status: 'exact', type: 'exact' } ] tags: [ 'api_platform.filter' ] api_platform.filter.image.boolean: @@ -167,7 +213,7 @@ services: api_platform.filter.pxe_template.boolean: parent: 'api_platform.doctrine.orm.boolean_filter' - arguments: [ { 'synchronized': ~ } ] + arguments: [ { 'synchronized': ~, 'isDefault': ~ } ] tags: [ 'api_platform.filter' ] api_platform.filter.remote_calendar.order: @@ -248,7 +294,7 @@ services: api_platform.filter.trace.search: parent: 'api_platform.doctrine.orm.search_filter' - arguments: [ { 'id': 'exact', 'command.id': 'exact', 'client.id': 'exact', status: 'exact' } ] + arguments: [ { 'id': 'exact', 'command': 'exact', 'client.id': 'exact', status: 'exact' } ] tags: [ 'api_platform.filter' ] api_platform.filter.trace.order: diff --git a/migrations/Version20250421122715.php b/migrations/Version20250421122715.php new file mode 100644 index 0000000..f7cb306 --- /dev/null +++ b/migrations/Version20250421122715.php @@ -0,0 +1,31 @@ +addSql('ALTER TABLE command_task ADD parameters LONGTEXT DEFAULT NULL COMMENT \'(DC2Type:array)\''); + } + + public function down(Schema $schema): void + { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql('ALTER TABLE command_task DROP parameters'); + } +} diff --git a/migrations/Version20250422092348.php b/migrations/Version20250422092348.php new file mode 100644 index 0000000..85eefb7 --- /dev/null +++ b/migrations/Version20250422092348.php @@ -0,0 +1,35 @@ +addSql('ALTER TABLE network_settings ADD pxe_template_id INT DEFAULT NULL'); + $this->addSql('ALTER TABLE network_settings ADD CONSTRAINT FK_48869B543BD7665C FOREIGN KEY (pxe_template_id) REFERENCES pxe_template (id)'); + $this->addSql('CREATE INDEX IDX_48869B543BD7665C ON network_settings (pxe_template_id)'); + } + + public function down(Schema $schema): void + { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql('ALTER TABLE network_settings DROP FOREIGN KEY FK_48869B543BD7665C'); + $this->addSql('DROP INDEX IDX_48869B543BD7665C ON network_settings'); + $this->addSql('ALTER TABLE network_settings DROP pxe_template_id'); + } +} diff --git a/migrations/Version20250422140927.php b/migrations/Version20250422140927.php new file mode 100644 index 0000000..a871594 --- /dev/null +++ b/migrations/Version20250422140927.php @@ -0,0 +1,31 @@ +addSql('ALTER TABLE command_task ADD name VARCHAR(255) 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_task DROP name'); + } +} diff --git a/migrations/Version20250423070720.php b/migrations/Version20250423070720.php new file mode 100644 index 0000000..c896641 --- /dev/null +++ b/migrations/Version20250423070720.php @@ -0,0 +1,31 @@ +addSql('ALTER TABLE command_task DROP datetime'); + } + + public function down(Schema $schema): void + { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql('ALTER TABLE command_task ADD datetime DATETIME NOT NULL'); + } +} diff --git a/migrations/Version20250423071243.php b/migrations/Version20250423071243.php new file mode 100644 index 0000000..f2170cd --- /dev/null +++ b/migrations/Version20250423071243.php @@ -0,0 +1,35 @@ +addSql('ALTER TABLE command_task ADD organizational_unit_id INT DEFAULT NULL'); + $this->addSql('ALTER TABLE command_task ADD CONSTRAINT FK_F3D475A8FB84408A FOREIGN KEY (organizational_unit_id) REFERENCES organizational_unit (id)'); + $this->addSql('CREATE INDEX IDX_F3D475A8FB84408A ON command_task (organizational_unit_id)'); + } + + public function down(Schema $schema): void + { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql('ALTER TABLE command_task DROP FOREIGN KEY FK_F3D475A8FB84408A'); + $this->addSql('DROP INDEX IDX_F3D475A8FB84408A ON command_task'); + $this->addSql('ALTER TABLE command_task DROP organizational_unit_id'); + } +} diff --git a/migrations/Version20250423092037.php b/migrations/Version20250423092037.php new file mode 100644 index 0000000..5855b79 --- /dev/null +++ b/migrations/Version20250423092037.php @@ -0,0 +1,33 @@ +addSql('CREATE TABLE command_task_schedule (id INT AUTO_INCREMENT NOT NULL, command_task_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, recurrence_type VARCHAR(255) NOT NULL, start_time DATETIME NOT NULL, recurrence_details JSON DEFAULT NULL COMMENT \'(DC2Type:json)\', UNIQUE INDEX UNIQ_3BEA77AD17F50A6 (uuid), INDEX IDX_3BEA77A62DC5265 (command_task_id), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB'); + $this->addSql('ALTER TABLE command_task_schedule ADD CONSTRAINT FK_3BEA77A62DC5265 FOREIGN KEY (command_task_id) REFERENCES command_task (id)'); + } + + public function down(Schema $schema): void + { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql('ALTER TABLE command_task_schedule DROP FOREIGN KEY FK_3BEA77A62DC5265'); + $this->addSql('DROP TABLE command_task_schedule'); + } +} diff --git a/migrations/Version20250427110103.php b/migrations/Version20250427110103.php new file mode 100644 index 0000000..fb167da --- /dev/null +++ b/migrations/Version20250427110103.php @@ -0,0 +1,45 @@ +addSql('CREATE TABLE command_task_scripts (id INT AUTO_INCREMENT NOT NULL, command_task_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, content VARCHAR(255) NOT NULL, execution_order INT NOT NULL, UNIQUE INDEX UNIQ_E8950142D17F50A6 (uuid), INDEX IDX_E895014262DC5265 (command_task_id), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB'); + $this->addSql('ALTER TABLE command_task_scripts ADD CONSTRAINT FK_E895014262DC5265 FOREIGN KEY (command_task_id) REFERENCES command_task (id)'); + $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('ALTER TABLE command_task_command DROP FOREIGN KEY FK_BB417CA862DC5265'); + $this->addSql('ALTER TABLE command_task_command DROP FOREIGN KEY FK_BB417CA833E1689A'); + $this->addSql('DROP TABLE command_task_command_group'); + $this->addSql('DROP TABLE command_task_command'); + } + + public function down(Schema $schema): void + { + // this down() migration is auto-generated, please modify it to your needs + $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 COMMENT = \'\' '); + $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 COMMENT = \'\' '); + $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'); + $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_scripts DROP FOREIGN KEY FK_E895014262DC5265'); + $this->addSql('DROP TABLE command_task_scripts'); + } +} diff --git a/migrations/Version20250427125746.php b/migrations/Version20250427125746.php new file mode 100644 index 0000000..9679d89 --- /dev/null +++ b/migrations/Version20250427125746.php @@ -0,0 +1,37 @@ +addSql('CREATE TABLE command_task_script (id INT AUTO_INCREMENT NOT NULL, command_task_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, content VARCHAR(255) NOT NULL, execution_order INT NOT NULL, UNIQUE INDEX UNIQ_22BF0112D17F50A6 (uuid), INDEX IDX_22BF011262DC5265 (command_task_id), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB'); + $this->addSql('ALTER TABLE command_task_script ADD CONSTRAINT FK_22BF011262DC5265 FOREIGN KEY (command_task_id) REFERENCES command_task (id)'); + $this->addSql('ALTER TABLE command_task_scripts DROP FOREIGN KEY FK_E895014262DC5265'); + $this->addSql('DROP TABLE command_task_scripts'); + } + + public function down(Schema $schema): void + { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql('CREATE TABLE command_task_scripts (id INT AUTO_INCREMENT NOT NULL, command_task_id INT NOT NULL, uuid CHAR(36) CHARACTER SET utf8mb4 NOT NULL COLLATE `utf8mb4_unicode_ci` COMMENT \'(DC2Type:uuid)\', migration_id VARCHAR(255) CHARACTER SET utf8mb4 DEFAULT NULL COLLATE `utf8mb4_unicode_ci`, created_at DATETIME NOT NULL, updated_at DATETIME NOT NULL, created_by VARCHAR(255) CHARACTER SET utf8mb4 DEFAULT NULL COLLATE `utf8mb4_unicode_ci`, updated_by VARCHAR(255) CHARACTER SET utf8mb4 DEFAULT NULL COLLATE `utf8mb4_unicode_ci`, content VARCHAR(255) CHARACTER SET utf8mb4 NOT NULL COLLATE `utf8mb4_unicode_ci`, execution_order INT NOT NULL, INDEX IDX_E895014262DC5265 (command_task_id), UNIQUE INDEX UNIQ_E8950142D17F50A6 (uuid), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB COMMENT = \'\' '); + $this->addSql('ALTER TABLE command_task_scripts ADD CONSTRAINT FK_E895014262DC5265 FOREIGN KEY (command_task_id) REFERENCES command_task (id)'); + $this->addSql('ALTER TABLE command_task_script DROP FOREIGN KEY FK_22BF011262DC5265'); + $this->addSql('DROP TABLE command_task_script'); + } +} diff --git a/migrations/Version20250428065311.php b/migrations/Version20250428065311.php new file mode 100644 index 0000000..b1d3eec --- /dev/null +++ b/migrations/Version20250428065311.php @@ -0,0 +1,31 @@ +addSql('ALTER TABLE command_task_schedule ADD execution_date DATE NOT NULL, ADD execution_time TIME NOT NULL, DROP start_time'); + } + + public function down(Schema $schema): void + { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql('ALTER TABLE command_task_schedule ADD start_time DATETIME NOT NULL, DROP execution_date, DROP execution_time'); + } +} diff --git a/migrations/Version20250428065937.php b/migrations/Version20250428065937.php new file mode 100644 index 0000000..d74c6e4 --- /dev/null +++ b/migrations/Version20250428065937.php @@ -0,0 +1,31 @@ +addSql('ALTER TABLE command_task_schedule ADD enabled TINYINT(1) 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_task_schedule DROP enabled'); + } +} diff --git a/migrations/Version20250428070851.php b/migrations/Version20250428070851.php new file mode 100644 index 0000000..36da43e --- /dev/null +++ b/migrations/Version20250428070851.php @@ -0,0 +1,31 @@ +addSql('ALTER TABLE command_task_schedule CHANGE execution_date execution_date DATE DEFAULT NULL, CHANGE execution_time execution_time TIME DEFAULT NULL'); + } + + public function down(Schema $schema): void + { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql('ALTER TABLE command_task_schedule CHANGE execution_date execution_date DATE NOT NULL, CHANGE execution_time execution_time TIME NOT NULL'); + } +} diff --git a/migrations/Version20250429065612.php b/migrations/Version20250429065612.php new file mode 100644 index 0000000..5f64e24 --- /dev/null +++ b/migrations/Version20250429065612.php @@ -0,0 +1,31 @@ +addSql('ALTER TABLE command_task ADD last_execution DATETIME DEFAULT NULL, ADD next_execution 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 command_task DROP last_execution, DROP next_execution'); + } +} diff --git a/migrations/Version20250429091808.php b/migrations/Version20250429091808.php new file mode 100644 index 0000000..d81868d --- /dev/null +++ b/migrations/Version20250429091808.php @@ -0,0 +1,31 @@ +addSql('ALTER TABLE command_task ADD scope VARCHAR(255) 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_task DROP scope'); + } +} diff --git a/migrations/Version20250430052434.php b/migrations/Version20250430052434.php new file mode 100644 index 0000000..3dbfe8f --- /dev/null +++ b/migrations/Version20250430052434.php @@ -0,0 +1,31 @@ +addSql('ALTER TABLE command_task_script ADD type VARCHAR(255) 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_task_script DROP type'); + } +} diff --git a/migrations/Version20250430053742.php b/migrations/Version20250430053742.php new file mode 100644 index 0000000..fc079dc --- /dev/null +++ b/migrations/Version20250430053742.php @@ -0,0 +1,31 @@ +addSql('ALTER TABLE command_task_script ADD parameters LONGTEXT DEFAULT NULL COMMENT \'(DC2Type:array)\''); + } + + public function down(Schema $schema): void + { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql('ALTER TABLE command_task_script DROP parameters'); + } +} diff --git a/migrations/Version20250430054513.php b/migrations/Version20250430054513.php new file mode 100644 index 0000000..9a8c368 --- /dev/null +++ b/migrations/Version20250430054513.php @@ -0,0 +1,31 @@ +addSql('ALTER TABLE command_task_script CHANGE content content VARCHAR(255) DEFAULT NULL'); + } + + public function down(Schema $schema): void + { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql('ALTER TABLE command_task_script CHANGE content content VARCHAR(255) NOT NULL'); + } +} diff --git a/migrations/Version20250430094459.php b/migrations/Version20250430094459.php new file mode 100644 index 0000000..7e63dd4 --- /dev/null +++ b/migrations/Version20250430094459.php @@ -0,0 +1,31 @@ +addSql('ALTER TABLE command_task DROP status'); + } + + public function down(Schema $schema): void + { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql('ALTER TABLE command_task ADD status VARCHAR(255) NOT NULL'); + } +} diff --git a/migrations/Version20250430135412.php b/migrations/Version20250430135412.php new file mode 100644 index 0000000..23612d9 --- /dev/null +++ b/migrations/Version20250430135412.php @@ -0,0 +1,33 @@ +addSql('CREATE TABLE git_image_repository (id INT AUTO_INCREMENT NOT NULL, image_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, branch VARCHAR(255) NOT NULL, created TINYINT(1) NOT NULL, UNIQUE INDEX UNIQ_E6944D5ED17F50A6 (uuid), INDEX IDX_E6944D5E3DA5256D (image_id), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB'); + $this->addSql('ALTER TABLE git_image_repository ADD CONSTRAINT FK_E6944D5E3DA5256D FOREIGN KEY (image_id) REFERENCES image (id)'); + } + + public function down(Schema $schema): void + { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql('ALTER TABLE git_image_repository DROP FOREIGN KEY FK_E6944D5E3DA5256D'); + $this->addSql('DROP TABLE git_image_repository'); + } +} diff --git a/migrations/Version20250430140218.php b/migrations/Version20250430140218.php new file mode 100644 index 0000000..a8129e4 --- /dev/null +++ b/migrations/Version20250430140218.php @@ -0,0 +1,35 @@ +addSql('ALTER TABLE git_image_repository ADD image_repository_id INT DEFAULT NULL'); + $this->addSql('ALTER TABLE git_image_repository ADD CONSTRAINT FK_E6944D5E14C736FC FOREIGN KEY (image_repository_id) REFERENCES image_repository (id)'); + $this->addSql('CREATE INDEX IDX_E6944D5E14C736FC ON git_image_repository (image_repository_id)'); + } + + public function down(Schema $schema): void + { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql('ALTER TABLE git_image_repository DROP FOREIGN KEY FK_E6944D5E14C736FC'); + $this->addSql('DROP INDEX IDX_E6944D5E14C736FC ON git_image_repository'); + $this->addSql('ALTER TABLE git_image_repository DROP image_repository_id'); + } +} diff --git a/migrations/Version20250430140326.php b/migrations/Version20250430140326.php new file mode 100644 index 0000000..30d1c61 --- /dev/null +++ b/migrations/Version20250430140326.php @@ -0,0 +1,31 @@ +addSql('ALTER TABLE git_image_repository ADD name VARCHAR(255) NOT NULL'); + } + + public function down(Schema $schema): void + { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql('ALTER TABLE git_image_repository DROP name'); + } +} diff --git a/migrations/Version20250506094654.php b/migrations/Version20250506094654.php new file mode 100644 index 0000000..a3d3b19 --- /dev/null +++ b/migrations/Version20250506094654.php @@ -0,0 +1,31 @@ +addSql('ALTER TABLE pxe_template ADD is_default TINYINT(1) DEFAULT NULL'); + } + + public function down(Schema $schema): void + { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql('ALTER TABLE pxe_template DROP is_default'); + } +} diff --git a/migrations/Version20250506141057.php b/migrations/Version20250506141057.php new file mode 100644 index 0000000..0fd654f --- /dev/null +++ b/migrations/Version20250506141057.php @@ -0,0 +1,33 @@ +addSql('ALTER TABLE client DROP FOREIGN KEY FK_C74404555DA0FB8'); + $this->addSql('ALTER TABLE client ADD CONSTRAINT FK_C74404555DA0FB8 FOREIGN KEY (template_id) REFERENCES pxe_template (id) ON DELETE SET NULL'); + } + + public function down(Schema $schema): void + { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql('ALTER TABLE client DROP FOREIGN KEY FK_C74404555DA0FB8'); + $this->addSql('ALTER TABLE client ADD CONSTRAINT FK_C74404555DA0FB8 FOREIGN KEY (template_id) REFERENCES pxe_template (id)'); + } +} diff --git a/migrations/Version20250508134732.php b/migrations/Version20250508134732.php new file mode 100644 index 0000000..c337ed2 --- /dev/null +++ b/migrations/Version20250508134732.php @@ -0,0 +1,33 @@ +addSql('ALTER TABLE `partition` DROP FOREIGN KEY FK_9EB910E43DA5256D'); + $this->addSql('ALTER TABLE `partition` ADD CONSTRAINT FK_9EB910E43DA5256D FOREIGN KEY (image_id) REFERENCES image_image_repository (id)'); + } + + public function down(Schema $schema): void + { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql('ALTER TABLE `partition` DROP FOREIGN KEY FK_9EB910E43DA5256D'); + $this->addSql('ALTER TABLE `partition` ADD CONSTRAINT FK_9EB910E43DA5256D FOREIGN KEY (image_id) REFERENCES image (id)'); + } +} diff --git a/migrations/Version20250508152437.php b/migrations/Version20250508152437.php new file mode 100644 index 0000000..28147b3 --- /dev/null +++ b/migrations/Version20250508152437.php @@ -0,0 +1,31 @@ +addSql('ALTER TABLE image ADD type VARCHAR(255) NOT NULL, DROP description, DROP comments'); + } + + public function down(Schema $schema): void + { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql('ALTER TABLE image ADD description VARCHAR(255) DEFAULT NULL, ADD comments VARCHAR(255) DEFAULT NULL, DROP type'); + } +} diff --git a/migrations/Version20250512071804.php b/migrations/Version20250512071804.php new file mode 100644 index 0000000..b7abcc7 --- /dev/null +++ b/migrations/Version20250512071804.php @@ -0,0 +1,39 @@ +addSql('ALTER TABLE git_image_repository DROP FOREIGN KEY FK_E6944D5E14C736FC'); + $this->addSql('DROP INDEX IDX_E6944D5E14C736FC ON git_image_repository'); + $this->addSql('ALTER TABLE git_image_repository ADD tag VARCHAR(255) NOT NULL, CHANGE image_repository_id repository_id INT DEFAULT NULL'); + $this->addSql('ALTER TABLE git_image_repository ADD CONSTRAINT FK_E6944D5E50C9D4F7 FOREIGN KEY (repository_id) REFERENCES image_repository (id)'); + $this->addSql('CREATE INDEX IDX_E6944D5E50C9D4F7 ON git_image_repository (repository_id)'); + } + + public function down(Schema $schema): void + { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql('ALTER TABLE git_image_repository DROP FOREIGN KEY FK_E6944D5E50C9D4F7'); + $this->addSql('DROP INDEX IDX_E6944D5E50C9D4F7 ON git_image_repository'); + $this->addSql('ALTER TABLE git_image_repository DROP tag, CHANGE repository_id image_repository_id INT DEFAULT NULL'); + $this->addSql('ALTER TABLE git_image_repository ADD CONSTRAINT FK_E6944D5E14C736FC FOREIGN KEY (image_repository_id) REFERENCES image_repository (id)'); + $this->addSql('CREATE INDEX IDX_E6944D5E14C736FC ON git_image_repository (image_repository_id)'); + } +} diff --git a/migrations/Version20250512075927.php b/migrations/Version20250512075927.php new file mode 100644 index 0000000..5a5f81c --- /dev/null +++ b/migrations/Version20250512075927.php @@ -0,0 +1,31 @@ +addSql('ALTER TABLE git_image_repository CHANGE branch branch VARCHAR(255) DEFAULT NULL'); + } + + public function down(Schema $schema): void + { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql('ALTER TABLE git_image_repository CHANGE branch branch VARCHAR(255) NOT NULL'); + } +} diff --git a/migrations/Version20250512081045.php b/migrations/Version20250512081045.php new file mode 100644 index 0000000..7dbcda5 --- /dev/null +++ b/migrations/Version20250512081045.php @@ -0,0 +1,31 @@ +addSql('ALTER TABLE git_image_repository ADD version INT DEFAULT NULL'); + } + + public function down(Schema $schema): void + { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql('ALTER TABLE git_image_repository DROP version'); + } +} diff --git a/migrations/Version20250513050921.php b/migrations/Version20250513050921.php new file mode 100644 index 0000000..bd9ff66 --- /dev/null +++ b/migrations/Version20250513050921.php @@ -0,0 +1,31 @@ +addSql('ALTER TABLE git_image_repository ADD description VARCHAR(255) DEFAULT NULL'); + } + + public function down(Schema $schema): void + { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql('ALTER TABLE git_image_repository DROP description'); + } +} diff --git a/migrations/Version20250513055057.php b/migrations/Version20250513055057.php new file mode 100644 index 0000000..c96a29d --- /dev/null +++ b/migrations/Version20250513055057.php @@ -0,0 +1,31 @@ +addSql('ALTER TABLE image_image_repository ADD partition_info VARCHAR(255) DEFAULT NULL'); + } + + public function down(Schema $schema): void + { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql('ALTER TABLE image_image_repository DROP partition_info'); + } +} diff --git a/src/Command/RunScheduledCommandTasksCommand.php b/src/Command/RunScheduledCommandTasksCommand.php new file mode 100644 index 0000000..06e9971 --- /dev/null +++ b/src/Command/RunScheduledCommandTasksCommand.php @@ -0,0 +1,90 @@ +setTimezone(new \DateTimeZone('Europe/Madrid')); + $nowMinute = $now->format('Y-m-d H:i'); + + $tasks = $this->commandTaskRepository->findAll(); + + foreach ($tasks as $task) { + /** @var CommandTask $task */ + $nextExecution = $task->getNextExecution(); + if (!$nextExecution) { + continue; + } + + $taskMinute = $nextExecution->format('Y-m-d H:i'); + + if ($taskMinute === $nowMinute) { + $output->writeln("Ejecutando tarea: " . $task->getName()); + + $scripts = $task->getCommandTaskScripts()->toArray(); + usort($scripts, fn($a, $b) => $a->getExecutionOrder() <=> $b->getExecutionOrder()); + + foreach ($scripts as $script) { + try { + $output->writeln(" - Ejecutando script de tipo {$script->getType()} con orden {$script->getExecutionOrder()}"); + + if ($script->getType() === 'run-script') { + $input = new CommandExecuteInput(); + + foreach ($task->getOrganizationalUnit()?->getClients() as $client) { + $input->clients[] = new ClientOutput($client); + } + $input->script = $script->getContent(); + + $this->runScriptAction->__invoke($input); + } + } catch (TransportExceptionInterface $e) { + $output->writeln("Error ejecutando script: " . $e->getMessage()); + continue; + } + } + + $task->setLastExecution(new \DateTime()); + $task->setNextExecution($task->calculateNextExecutionDate()); + $this->entityManager->persist($task); + } + } + + $this->entityManager->flush(); + return Command::SUCCESS; + } +} diff --git a/src/Controller/DeployImageAction.php b/src/Controller/DeployImageAction.php index 4912d29..4d49607 100644 --- a/src/Controller/DeployImageAction.php +++ b/src/Controller/DeployImageAction.php @@ -55,6 +55,7 @@ class DeployImageAction extends AbstractController 'method' => $input->method, 'client' => $client->getEntity()->getUuid(), 'image' => $image->getUuid(), + 'imageName' => $image->getName(), 'numDisk' => (string) $input->diskNumber, 'numPartition' => (string) $input->partitionNumber, ]; diff --git a/src/Controller/OgAgent/CreateImageAction.php b/src/Controller/OgAgent/CreateImageAction.php index 09d5f6c..ed3bb96 100644 --- a/src/Controller/OgAgent/CreateImageAction.php +++ b/src/Controller/OgAgent/CreateImageAction.php @@ -4,8 +4,11 @@ declare(strict_types=1); namespace App\Controller\OgAgent; +use App\Controller\OgRepository\Git\CreateRepositoryAction; +use App\Controller\OgRepository\Git\CreateTagAction; use App\Entity\Client; use App\Entity\Command; +use App\Entity\GitImageRepository; use App\Entity\Image; use App\Entity\ImageImageRepository; use App\Entity\ImageRepository; @@ -38,6 +41,8 @@ class CreateImageAction extends AbstractController protected readonly HttpClientInterface $httpClient, protected readonly CreateService $createService, protected readonly LoggerInterface $logger, + protected readonly CreateRepositoryAction $createRepositoryAction, + protected readonly CreateTagAction $createTagAction, ) { } @@ -48,7 +53,7 @@ class CreateImageAction extends AbstractController * @throws RedirectionExceptionInterface * @throws ClientExceptionInterface */ - public function __invoke(Image $image, ?Partition $partition = null): JsonResponse + public function __invoke(Image $image, ?Partition $partition = null, ?Client $client = null): JsonResponse { if (!$image->getClient()->getIp()) { throw new ValidatorException('IP is required'); @@ -67,18 +72,56 @@ class CreateImageAction extends AbstractController $partitionInfo = json_decode($image->getPartitionInfo(), true); } - $repository = $image->getClient()->getRepository(); - $latestImageRepo = $this->entityManager->getRepository(ImageImageRepository::class)->findLatestVersionByImageAndRepository($image, $repository); + if ($image->getType() === 'monolithic') { + $repository = $image->getClient()->getRepository(); + $latestImageRepo = $this->entityManager->getRepository(ImageImageRepository::class)->findLatestVersionByImageAndRepository($image, $repository); - $imageImageRepository = new ImageImageRepository(); - $imageImageRepository->setName($image->getName().'_v'.($latestImageRepo ? $latestImageRepo->getVersion() + 1 : 1)); - $imageImageRepository->setImage($image); - $imageImageRepository->setRepository($repository); - $imageImageRepository->setStatus(ImageStatus::IN_PROGRESS); - $imageImageRepository->setVersion($latestImageRepo ? $latestImageRepo->getVersion() + 1 : 1); + $imageImageRepository = new ImageImageRepository(); + $imageImageRepository->setName($image->getName().'_v'.($latestImageRepo ? $latestImageRepo->getVersion() + 1 : 1)); + $imageImageRepository->setImage($image); + $imageImageRepository->setRepository($repository); + $imageImageRepository->setStatus(ImageStatus::IN_PROGRESS); + $imageImageRepository->setVersion($latestImageRepo ? $latestImageRepo->getVersion() + 1 : 1); + $imageImageRepository->setPartitionInfo(json_encode($partitionInfo)); - $this->entityManager->persist($imageImageRepository); + $this->entityManager->persist($imageImageRepository); + return $this->createMonolithicImage($imageImageRepository, $partitionInfo, $image, $repository, $client); + } else { + $repository = $image->getClient()->getRepository(); + $latestImageRepo = $this->entityManager->getRepository(GitImageRepository::class)->findLatestVersionByImageAndRepository($image, $repository); + + $gitImageRepository = new GitImageRepository(); + $gitImageRepository->setName($image->getName().'_v'.($latestImageRepo ? $latestImageRepo->getVersion() + 1 : 1)); + $gitImageRepository->setImage($image); + $gitImageRepository->setRepository($repository); + $gitImageRepository->setStatus(ImageStatus::IN_PROGRESS); + $gitImageRepository->setVersion($latestImageRepo ? $latestImageRepo->getVersion() + 1 : 1); + $gitImageRepository->setTag('v'.($latestImageRepo ? $latestImageRepo->getVersion() + 1 : 1)); + $gitImageRepository->setCreated(false); + + $this->entityManager->persist($gitImageRepository); + $this->entityManager->persist($image); + $this->entityManager->flush(); + + return $this->createGitImage($gitImageRepository, $partitionInfo, $image, $repository); + } + } + + /** + * @throws TransportExceptionInterface + * @throws ServerExceptionInterface + * @throws RedirectionExceptionInterface + * @throws ClientExceptionInterface + */ + public function createMonolithicImage( + ImageImageRepository $imageImageRepository, + array $partitionInfo, + Image $image, + ImageRepository $repository, + ?Client $client = null + ): JsonResponse + { $data = [ 'dsk' => (string) $partitionInfo['numDisk'], 'par' => (string) $partitionInfo['numPartition'], @@ -105,8 +148,10 @@ class CreateImageAction extends AbstractController } try { + + $client = $client ?? $image->getClient(); $this->logger->info('Creating image', ['image' => $image->getId()]); - $response = $this->httpClient->request('POST', 'https://'.$image->getClient()->getIp().':8000/opengnsys/CrearImagen', [ + $response = $this->httpClient->request('POST', 'https://'.$client->getIp().':8000/opengnsys/CrearImagen', [ 'verify_peer' => false, 'verify_host' => false, 'headers' => [ @@ -126,13 +171,62 @@ class CreateImageAction extends AbstractController $jobId = json_decode($response->getContent(), true)['job_id']; - $client = $image->getClient(); $client->setStatus(ClientStatus::BUSY); $this->entityManager->persist($client); $this->entityManager->flush(); - $this->createService->__invoke($image->getClient(), CommandTypes::CREATE_IMAGE, TraceStatus::IN_PROGRESS, $jobId, []); + $inputData = [ + 'method' => 'CrearImagen', + 'type' => 'monolithic', + 'client' => $client->getUuid(), + 'image' => $image->getUuid(), + 'partitionCode' => $partitionInfo['partitionCode'], + 'partitionType' => $partitionInfo['filesystem'], + 'repository' => $repository->getIp(), + 'name' => $image->getName().'_v'.$imageImageRepository->getVersion(), + ]; + + $this->createService->__invoke($image->getClient(), CommandTypes::CREATE_IMAGE, TraceStatus::IN_PROGRESS, $jobId, $inputData); return new JsonResponse(data: $image, status: Response::HTTP_OK); } + + public function createGitImage( + GitImageRepository $gitImageRepository, + array $partitionInfo, + Image $image, + ImageRepository $repository + ): JsonResponse + { + if (!isset($partitonInfo)) { + try { + $this->createRepositoryAction->__invoke($image, $repository); + } catch (Exception $e) { + $this->logger->error('Error creating repository', ['repository' => $repository->getId(), 'error' => $e->getMessage()]); + + return new JsonResponse( + data: ['error' => $e->getMessage()], + status: Response::HTTP_INTERNAL_SERVER_ERROR + ); + } + } else { + try { + $this->createTagAction->__invoke($image, $repository, $gitImageRepository); + } catch (Exception $e) { + $this->logger->error('Error creating tag', ['repository' => $repository->getId(), 'error' => $e->getMessage()]); + + return new JsonResponse( + data: ['error' => $e->getMessage()], + status: Response::HTTP_INTERNAL_SERVER_ERROR + ); + } + } + + return new JsonResponse( + data: ['message' => 'Repository created successfully'], + status: Response::HTTP_OK + ); + + //TODO: llamar al endpoint del agente. + } } diff --git a/src/Controller/OgAgent/Webhook/StatusController.php b/src/Controller/OgAgent/Webhook/StatusController.php index d1ff1ff..eeb2d27 100644 --- a/src/Controller/OgAgent/Webhook/StatusController.php +++ b/src/Controller/OgAgent/Webhook/StatusController.php @@ -159,6 +159,24 @@ class StatusController extends AbstractController $client->setStatus(ClientStatus::OG_LIVE); $this->entityManager->persist($client); $this->entityManager->persist($trace); + + if ($data['nfn'] === self::RESTORE_IMAGE) { + $partitionData = json_decode(json_encode($trace->getInput()), true); + + $numDisk = (int) $partitionData['numDisk'] ?? null; + $numPartition = (int) $partitionData['numPartition'] ?? null; + + $imageImageRepository = $this->entityManager->getRepository(ImageImageRepository::class)->findOneBy(['uuid' => $partitionData['image']]); + + $partition = $this->entityManager->getRepository(Partition::class) + ->findOneBy(['diskNumber' => $numDisk, 'partitionNumber' => $numPartition, 'client' => $client]); + + if ($partition) { + $partition->setImage($imageImageRepository); + $this->entityManager->persist($partition); + } + } + $this->entityManager->flush(); } diff --git a/src/Controller/OgBoot/OgLive/Webhook/InstallOgLiveResponseAction.php b/src/Controller/OgBoot/OgLive/Webhook/InstallOgLiveResponseAction.php index 645fe8f..f7ac0f6 100644 --- a/src/Controller/OgBoot/OgLive/Webhook/InstallOgLiveResponseAction.php +++ b/src/Controller/OgBoot/OgLive/Webhook/InstallOgLiveResponseAction.php @@ -30,7 +30,7 @@ class InstallOgLiveResponseAction extends AbstractController { public CONST string OG_LIVE_INSTALL_SUCCESS = 'success'; public CONST string OG_LIVE_INSTALL_FAILED = 'failure'; - const string OG_BOOT_DIRECTORY = '/opt/opengnsys/ogboot/tftpboot//'; + const string OG_BOOT_DIRECTORY = '/opt/opengnsys/ogboot/tftpboot/'; public function __construct( protected readonly EntityManagerInterface $entityManager, @@ -94,7 +94,7 @@ class InstallOgLiveResponseAction extends AbstractController private function updateOgLive (OgLive $ogLive, mixed $details, string $status): void { if ( is_array($details) && $status === self::OG_LIVE_INSTALL_SUCCESS) { - $ogLive->setName($this->simplifyOgLiveFilenameService->__invoke($details['directory'])); + $ogLive->setName($this->simplifyOgLiveFilenameService->__invoke(str_replace(self::OG_BOOT_DIRECTORY, '', $details['directory']))); $ogLive->setDate(new \DateTime($this->extractOgLiveFilenameDateService->__invoke($details['directory']))); $ogLive->setFilename(str_replace(self::OG_BOOT_DIRECTORY, '', $details['directory'])); $ogLive->setInstalled(true); diff --git a/src/Controller/OgBoot/PxeTemplate/DeleteAction.php b/src/Controller/OgBoot/PxeTemplate/DeleteAction.php index b8c71bb..c816365 100644 --- a/src/Controller/OgBoot/PxeTemplate/DeleteAction.php +++ b/src/Controller/OgBoot/PxeTemplate/DeleteAction.php @@ -3,6 +3,7 @@ namespace App\Controller\OgBoot\PxeTemplate; use App\Controller\OgBoot\AbstractOgBootController; +use App\Entity\Client; use App\Entity\PxeTemplate; use Doctrine\ORM\EntityManagerInterface; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; @@ -26,11 +27,20 @@ class DeleteAction extends AbstractOgBootController */ public function __invoke(PxeTemplate $data): JsonResponse { - $content = $this->createRequest('DELETE', 'http://'.$this->ogBootApiUrl.'/ogboot/v1/pxe-templates/'.$data->getName()); - $this->entityManager->remove($data); $this->entityManager->flush(); + $content = $this->createRequest('DELETE', 'http://'.$this->ogBootApiUrl.'/ogboot/v1/pxe-templates/'.$data->getName()); + + $defaultTemplateEntity = $this->entityManager->getRepository(PxeTemplate::class)->findOneBy(['isDefault' => true]); + + if ($defaultTemplateEntity === null) { + return new JsonResponse( + 'Default template not found', + Response::HTTP_NOT_FOUND + ); + } + return new JsonResponse(status: Response::HTTP_OK); } } \ No newline at end of file diff --git a/src/Controller/OgBoot/PxeTemplate/SyncAction.php b/src/Controller/OgBoot/PxeTemplate/SyncAction.php index 2b256e1..4988b84 100644 --- a/src/Controller/OgBoot/PxeTemplate/SyncAction.php +++ b/src/Controller/OgBoot/PxeTemplate/SyncAction.php @@ -28,25 +28,35 @@ class SyncAction extends AbstractOgBootController */ public function __invoke(): JsonResponse { - $content = $this->createRequest('GET', 'http://'.$this->ogBootApiUrl . '/ogboot/v1/pxe-templates'); + $content = $this->createRequest('GET', 'http://' . $this->ogBootApiUrl . '/ogboot/v1/pxe-templates'); + $templateNamesFromApi = $content['message']; - foreach ($content['message'] as $template) { - $templateEntity = $this->entityManager->getRepository(PxeTemplate::class)->findOneBy(['name' => $template]); + $existingTemplates = $this->entityManager->getRepository(PxeTemplate::class)->findAll(); - if ($templateEntity) { - $this->entityManager->persist($templateEntity); - } else { + $existingTemplateNames = array_map(fn($t) => $t->getName(), $existingTemplates); + + foreach ($existingTemplates as $existingTemplate) { + if (!in_array($existingTemplate->getName(), $templateNamesFromApi, true)) { + $this->entityManager->remove($existingTemplate); + } + } + + foreach ($templateNamesFromApi as $templateName) { + $templateEntity = $this->entityManager->getRepository(PxeTemplate::class)->findOneBy(['name' => $templateName]); + + if (!$templateEntity) { $templateEntity = new PxeTemplate(); - $templateEntity->setName($template); + $templateEntity->setName($templateName); } - $templateContent = $this->createRequest('GET', 'http://'.$this->ogBootApiUrl . '/ogboot/v1/pxe-templates/'.$templateEntity->getName()); + $templateContent = $this->createRequest('GET', 'http://' . $this->ogBootApiUrl . '/ogboot/v1/pxe-templates/' . $templateEntity->getName()); $templateEntity->setTemplateContent($templateContent['template_content']); $templateEntity->setSynchronized(true); $this->entityManager->persist($templateEntity); } + $this->entityManager->flush(); return new JsonResponse(data: $content, status: Response::HTTP_OK); diff --git a/src/Controller/OgRepository/Git/CreateRepositoryAction.php b/src/Controller/OgRepository/Git/CreateRepositoryAction.php new file mode 100644 index 0000000..4abca75 --- /dev/null +++ b/src/Controller/OgRepository/Git/CreateRepositoryAction.php @@ -0,0 +1,42 @@ + [ + 'name' => $image->getName(), + ] + ]; + + $content = $this->createRequest('POST', 'http://'.$repository->getIp().':8006/ogrepository/v1/git/repositories', $params); + + if (isset($content['error']) && $content['code'] === Response::HTTP_INTERNAL_SERVER_ERROR ) { + throw new ValidatorException('Error creating repository'); + } + + return new JsonResponse(data: [], status: Response::HTTP_OK); + } +} \ No newline at end of file diff --git a/src/Controller/OgRepository/Git/CreateTagAction.php b/src/Controller/OgRepository/Git/CreateTagAction.php new file mode 100644 index 0000000..d904418 --- /dev/null +++ b/src/Controller/OgRepository/Git/CreateTagAction.php @@ -0,0 +1,46 @@ + [ + 'commit' => "HEAD", + 'message' => $gitImageRepository->getVersion(), + 'name' => $image->getName(), + ] + ]; + + $content = $this->createRequest('POST', 'http://'.$repository->getIp().':8006/ogrepository/v1/git/repositories/'. + $image->getName().'/tags', $params); + + if (isset($content['error']) && $content['code'] === Response::HTTP_INTERNAL_SERVER_ERROR ) { + throw new ValidatorException('Error creating repository'); + } + + return new JsonResponse(data: [], status: Response::HTTP_OK); + } +} \ No newline at end of file diff --git a/src/Controller/OgRepository/Git/GetCollectionAction.php b/src/Controller/OgRepository/Git/GetCollectionAction.php new file mode 100644 index 0000000..55cc436 --- /dev/null +++ b/src/Controller/OgRepository/Git/GetCollectionAction.php @@ -0,0 +1,31 @@ +createRequest('GET', 'http://'.$data->getIp().':8006/ogrepository/v1/git/repositories'); + + return new JsonResponse(data: $content, status: Response::HTTP_OK); + } +} \ No newline at end of file diff --git a/src/Controller/OgRepository/Git/GetTagsAction.php b/src/Controller/OgRepository/Git/GetTagsAction.php new file mode 100644 index 0000000..213dd01 --- /dev/null +++ b/src/Controller/OgRepository/Git/GetTagsAction.php @@ -0,0 +1,38 @@ +getRepository(); + $image = $gitImageRepository->getImage(); + + $content = $this->createRequest('GET', 'http://'.$repository->getIp().':8006/ogrepository/v1/git/repositories/'.$image->getName().'/tags'); + + if (isset($content['error']) && $content['code'] === Response::HTTP_INTERNAL_SERVER_ERROR ) { + throw new ValidatorException('Error getting tags'); + } + + return new JsonResponse(data: $content, status: Response::HTTP_OK); + } +} \ No newline at end of file diff --git a/src/Controller/OgRepository/Git/SyncAction.php b/src/Controller/OgRepository/Git/SyncAction.php new file mode 100644 index 0000000..1168562 --- /dev/null +++ b/src/Controller/OgRepository/Git/SyncAction.php @@ -0,0 +1,73 @@ +createRequest('GET', 'http://' . $input->getIp() . ':8006/ogrepository/v1/git/repositories'); + + if (!isset($content['repositories'])) { + return new JsonResponse(data: 'No repositories found', status: Response::HTTP_NOT_FOUND); + } + + $repository = $this->entityManager->getRepository(GitImageRepository::class); + + $existingRepositories = $repository->findBy(['imageRepository' => $input]); + + foreach ($content['repositories'] as $repositoryData) { + $gitImageRepositoryEntity = $repository->findOneBy([ + 'imageRepository' => $input, + 'name' => $repositoryData + ]); + + $imageEntity = $this->entityManager->getRepository(Image::class)->findOneBy(['name' => $repositoryData]); + + if (!$imageEntity) { + $imageEntity = new Image(); + $imageEntity->setName($repositoryData); + $imageEntity->setRemotePc(false); + $imageEntity->setIsGlobal(false); + + $this->entityManager->persist($imageEntity); + } + + if (!$gitImageRepositoryEntity) { + $gitImageRepositoryEntity = new GitImageRepository(); + $gitImageRepositoryEntity->setName($repositoryData); + $gitImageRepositoryEntity->setStatus(ImageStatus::SUCCESS); + $gitImageRepositoryEntity->setImageRepository($input); + $gitImageRepositoryEntity->setImage($imageEntity); + $gitImageRepositoryEntity->setBranch('main'); + $gitImageRepositoryEntity->setCreated(true); + $gitImageRepositoryEntity->setCreatedAt(new \DateTime()); + + $this->entityManager->persist($gitImageRepositoryEntity); + } + } + + $this->entityManager->flush(); + + return new JsonResponse(data: $content, status: Response::HTTP_OK); + } +} \ No newline at end of file diff --git a/src/Controller/OgRepository/Image/ConvertImageToVirtualAction.php b/src/Controller/OgRepository/Image/ConvertImageToVirtualAction.php index cf3545c..7591e08 100644 --- a/src/Controller/OgRepository/Image/ConvertImageToVirtualAction.php +++ b/src/Controller/OgRepository/Image/ConvertImageToVirtualAction.php @@ -48,6 +48,10 @@ class ConvertImageToVirtualAction extends AbstractOgRepositoryController $content = $this->createRequest('PUT', 'http://'.$repository->getIp().':8006/ogrepository/v1/images/virtual', $params); + if (isset($content['error']) && $content['code'] === Response::HTTP_INTERNAL_SERVER_ERROR ) { + throw new ValidatorException('Error converting image'); + } + $inputData = [ 'imageName' => $image->getName(), 'repositoryUuid' => $repository->getUuid(), diff --git a/src/Controller/OgRepository/GetCollectionAction.php b/src/Controller/OgRepository/Image/GetCollectionAction.php similarity index 90% rename from src/Controller/OgRepository/GetCollectionAction.php rename to src/Controller/OgRepository/Image/GetCollectionAction.php index 594385a..26a01c7 100644 --- a/src/Controller/OgRepository/GetCollectionAction.php +++ b/src/Controller/OgRepository/Image/GetCollectionAction.php @@ -1,7 +1,8 @@ [ 'image' => $imageImageRepository->getName().'.img', 'repo_ip' => $imageImageRepository->getRepository()->getIp(), - 'user' => $repository->getUser(), - 'ssh_port' => $repository->getSshPort() + 'user' => $repository->getUser() ?? 'opengnsys', + 'ssh_port' => $repository->getSshPort() ?? '22', ] ]; - $this->logger->info('Importing image', ['image' => $image->getName(), 'repository' => $repository->getIp()]); + $this->logger->info('Importing image', ['image' => $imageImageRepository->getName(), 'repository' => $repository->getIp(), 'ssh_port' => $repository->getSshPort(), 'user' => $repository->getUser()]); $content = $this->createRequest('POST', 'http://'.$repository->getIp().':8006/ogrepository/v1/repo/images', $params); diff --git a/src/Controller/OgRepository/Webhook/ResponseController.php b/src/Controller/OgRepository/Webhook/ResponseController.php index 3dee082..05535dd 100644 --- a/src/Controller/OgRepository/Webhook/ResponseController.php +++ b/src/Controller/OgRepository/Webhook/ResponseController.php @@ -89,9 +89,12 @@ class ResponseController extends AbstractOgRepositoryController return $this->jsonResponseError('Action failed', Response::HTTP_BAD_REQUEST, $trace); } + $latestImageRepo = $this->entityManager->getRepository(ImageImageRepository::class)->findLatestVersionByImageAndRepository($image, $repository); + $newImageRepo = new ImageImageRepository(); - $newImageRepo->setName($image->getName().'_v'.($originImageImageRepository->getVersion() + 1)); + $newImageRepo->setName($image->getName().'_v'.($latestImageRepo ? $latestImageRepo->getVersion() + 1 : 1)); $newImageRepo->setImage($image); + $newImageRepo->setVersion($latestImageRepo ? $latestImageRepo->getVersion() + 1 : 1); $newImageRepo->setRepository($repository); $newImageRepo->setStatus(ImageStatus::SUCCESS); diff --git a/src/Controller/UDS/RemoteCalendarSyncUdsAction.php b/src/Controller/UDS/RemoteCalendarSyncUdsAction.php index c5ac842..6d9fec2 100644 --- a/src/Controller/UDS/RemoteCalendarSyncUdsAction.php +++ b/src/Controller/UDS/RemoteCalendarSyncUdsAction.php @@ -17,7 +17,6 @@ class RemoteCalendarSyncUdsAction extends AbstractController { public function __construct( - #[Autowire(env: 'UDS_URL')] private readonly UDSClient $udsClient ) { diff --git a/src/Dto/Input/ClientInput.php b/src/Dto/Input/ClientInput.php index c730555..f8cdac7 100644 --- a/src/Dto/Input/ClientInput.php +++ b/src/Dto/Input/ClientInput.php @@ -85,7 +85,7 @@ final class ClientInput #[ApiProperty( description: 'La plantilla PXE del cliente' )] - public ?PxeTemplateOutput $template = null; + public ?PxeTemplateOutput $pxeTemplate = null; #[Groups(['client:write'])] #[ApiProperty( @@ -143,7 +143,7 @@ final class ClientInput } if ($client->getTemplate()) { - $this->template = new PxeTemplateOutput($client->getTemplate()); + $this->pxeTemplate = new PxeTemplateOutput($client->getTemplate()); } if ($client->getHardwareProfile()) { @@ -165,6 +165,7 @@ final class ClientInput $ogLive = $this->ogLive?->getEntity() ?? $client->getOrganizationalUnit()?->getNetworkSettings()?->getOgLive(); $hardwareProfile = $this->hardwareProfile?->getEntity() ?? $client->getOrganizationalUnit()?->getNetworkSettings()?->getHardwareProfile(); $repository = $this->repository?->getEntity() ?? $client->getOrganizationalUnit()?->getNetworkSettings()?->getRepository(); + $template = $this->pxeTemplate?->getEntity() ?? $client->getOrganizationalUnit()?->getNetworkSettings()?->getPxeTemplate(); $client->setName($this->name); $client->setSerialNumber($this->serialNumber); @@ -177,7 +178,7 @@ final class ClientInput $client->setOgLive($ogLive); $client->setHardwareProfile($hardwareProfile); $client->setRepository($repository); - $client->setTemplate($this->template?->getEntity()); + $client->setTemplate($template); $client->setPosition($this->position); $client->setStatus($this->status); $client->setMaintenance($this->maintenance); diff --git a/src/Dto/Input/CommandTaskInput.php b/src/Dto/Input/CommandTaskInput.php index c7e08e7..71a2cd1 100644 --- a/src/Dto/Input/CommandTaskInput.php +++ b/src/Dto/Input/CommandTaskInput.php @@ -6,6 +6,7 @@ use ApiPlatform\Metadata\ApiProperty; use App\Dto\Output\ClientOutput; use App\Dto\Output\CommandGroupOutput; use App\Dto\Output\CommandOutput; +use App\Dto\Output\OrganizationalUnitOutput; use App\Entity\CommandTask; use App\Model\CommandTaskStatus; use phpDocumentor\Reflection\Types\Boolean; @@ -14,25 +15,12 @@ use Symfony\Component\Validator\Constraints as Assert; final class CommandTaskInput { - /** - * @var CommandOutput[] - */ #[Groups(['command-task:write'])] #[ApiProperty( - description: 'Los comandos de la tarea', - example: 'Comandos de la tarea', + description: 'El nombre de la tarea', + example: 'Tarea 1', )] - public array $commands = []; - - /** - * @var CommandGroupOutput[] - */ - #[Groups(['command-task:write'])] - #[ApiProperty( - description: 'Los grupos de comandos de la tarea', - example: 'Grupos de comandos de la tarea', - )] - public array $commandGroups = []; + public ?string $name = null; /** * @var ClientOutput[] @@ -44,38 +32,37 @@ final class CommandTaskInput )] public array $clients = []; - #[Assert\NotBlank(message: 'validators.command_task.datetime.not_blank')] - #[Groups(['command-task:write'])] - #[ApiProperty( - description: 'La fecha y hora de la tarea', - example: '2021-10-01T00:00:00+00:00', - )] - public ?\DateTimeInterface $dateTime = null; - #[Groups(['command-task:write'])] #[ApiProperty( description: 'Los comentarios de la tarea', - example: 'Comentarios de la tarea', + example: 'Ambito de la tarea', )] public ?string $notes = null; + #[Groups(['command-task:write'])] + #[ApiProperty( + description: 'El ambito de la tarea', + example: 'Comentarios de la tarea', + )] + public ?string $scope = null; + + #[Groups(['command-task:write'])] + public ?OrganizationalUnitOutput $organizationalUnit = null; + + #[Groups(['command-task:write'])] + #[ApiProperty( + description: 'Los parámetros de la tarea', + example: 'Parámetros de la tarea', + )] + public ?array $content = null; + public function __construct(?CommandTask $commandTask = null) { if (!$commandTask) { return; } - if ($commandTask->getCommands()) { - foreach ($commandTask->getCommands() as $command) { - $this->commands[] = new CommandOutput($command); - } - } - - if ($commandTask->getCommandGroups()) { - foreach ($commandTask->getCommandGroups() as $commandGroup) { - $this->commandGroups[] = new CommandGroupOutput($commandGroup); - } - } + $this->name = $commandTask->getName(); if ($commandTask->getClients()) { foreach ($commandTask->getClients() as $client) { @@ -83,37 +70,33 @@ final class CommandTaskInput } } - $this->dateTime = $commandTask->getDatetime(); + $this->organizationalUnit = new OrganizationalUnitOutput($commandTask->getOrganizationalUnit()); $this->notes = $commandTask->getNotes(); + $this->scope = $commandTask->getScope(); + $this->content = $commandTask->getParameters(); } + /** + * @throws \Exception + */ 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 ?? [] ); + $commandTask->setName($this->name); foreach ($this->clients as $client) { $clientsToAdd[] = $client->getEntity(); } + $commandTask->setOrganizationalUnit($this->organizationalUnit->getEntity()); $commandTask->setClients( $clientsToAdd ?? [] ); - - $commandTask->setDatetime($this->dateTime); - $commandTask->setStatus(CommandTaskStatus::PENDING); $commandTask->setNotes($this->notes); + $commandTask->setParameters($this->content); + $commandTask->setScope($this->scope); + $commandTask->setNextExecution($commandTask->calculateNextExecutionDate()); return $commandTask; } diff --git a/src/Dto/Input/CommandTaskScheduleInput.php b/src/Dto/Input/CommandTaskScheduleInput.php index ea8b688..afb0fde 100644 --- a/src/Dto/Input/CommandTaskScheduleInput.php +++ b/src/Dto/Input/CommandTaskScheduleInput.php @@ -2,7 +2,63 @@ namespace App\Dto\Input; -class CommandTaskScheduleInput -{ +use App\Dto\Output\CommandTaskOutput; +use App\Dto\Output\CommandTaskScheduleOutput; +use App\Entity\CommandTaskSchedule; +use Symfony\Component\Serializer\Annotation\Groups; +final class CommandTaskScheduleInput +{ + #[Groups(['command-task-schedule:write'])] + public ?CommandTaskOutput $commandTask = null; + + #[Groups(['command-task-schedule:write'])] + public ?string $recurrenceType = null; + + #[Groups(['command-task-schedule:write'])] + public ?\DateTimeInterface $executionDate = null; + + #[Groups(['command-task-schedule:write'])] + public ?\DateTimeInterface $executionTime = null; + + #[Groups(['command-task-schedule:write'])] + public ?array $recurrenceDetails = null; + + #[Groups(['command-task-schedule:write'])] + public ?bool $enabled = null; + + public function __construct(?CommandTaskSchedule $commandTaskSchedule = null) + { + if (!$commandTaskSchedule) { + return; + } + + $this->commandTask = new CommandTaskOutput($commandTaskSchedule->getCommandTask()); + $this->recurrenceType = $commandTaskSchedule->getRecurrenceType(); + $this->executionDate = $commandTaskSchedule->getExecutionDate(); + $this->executionTime = $commandTaskSchedule->getExecutionTime(); + $this->recurrenceDetails = $commandTaskSchedule->getRecurrenceDetails(); + $this->enabled = $commandTaskSchedule->isEnabled(); + } + + /** + * @throws \Exception + */ + public function createOrUpdateEntity(?CommandTaskSchedule $commandTaskSchedule = null): CommandTaskSchedule + { + if (!$commandTaskSchedule) { + $commandTaskSchedule = new CommandTaskSchedule(); + } + + if ($this->commandTask) { + $commandTaskSchedule->setCommandTask($this->commandTask->getEntity()); + } + $commandTaskSchedule->setRecurrenceType($this->recurrenceType); + $commandTaskSchedule->setExecutionTime($this->executionTime); + $commandTaskSchedule->setExecutionDate($this->executionDate); + $commandTaskSchedule->setRecurrenceDetails($this->recurrenceDetails); + $commandTaskSchedule->setEnabled($this->enabled); + + return $commandTaskSchedule; + } } \ No newline at end of file diff --git a/src/Dto/Input/CommandTaskScriptInput.php b/src/Dto/Input/CommandTaskScriptInput.php new file mode 100644 index 0000000..8eb8583 --- /dev/null +++ b/src/Dto/Input/CommandTaskScriptInput.php @@ -0,0 +1,57 @@ +content = $commandTaskScript->getContent(); + $this->type = $commandTaskScript->getType(); + $this->order = $commandTaskScript->getExecutionOrder(); + $this->parameters = $commandTaskScript->getParameters(); + $this->commandTask = new CommandTaskOutput($commandTaskScript->getCommandTask()); + } + + public function createOrUpdateEntity(?CommandTaskScript $commandTaskScript = null): CommandTaskScript + { + if (!$commandTaskScript) { + $commandTaskScript = new CommandTaskScript(); + } + + if ($this->commandTask) { + $commandTaskScript->setCommandTask($this->commandTask->getEntity()); + } + $commandTaskScript->setType($this->type); + $commandTaskScript->setParameters($this->parameters); + $commandTaskScript->setContent($this->content); + $commandTaskScript->setExecutionOrder($this->order); + + return $commandTaskScript; + } +} \ No newline at end of file diff --git a/src/Dto/Input/GitImageRepositoryInput.php b/src/Dto/Input/GitImageRepositoryInput.php new file mode 100644 index 0000000..8c8ed1b --- /dev/null +++ b/src/Dto/Input/GitImageRepositoryInput.php @@ -0,0 +1,33 @@ +description = $gitImageRepository->getDescription(); + } + + public function createOrUpdateEntity(?GitImageRepository $gitImageRepository = null): GitImageRepository + { + if (!$gitImageRepository) { + $gitImageRepository = new GitImageRepository(); + } + + $gitImageRepository->setDescription($this->description); + + return $gitImageRepository; + } +} \ No newline at end of file diff --git a/src/Dto/Input/ImageInput.php b/src/Dto/Input/ImageInput.php index 00dd01d..f2dd9de 100644 --- a/src/Dto/Input/ImageInput.php +++ b/src/Dto/Input/ImageInput.php @@ -27,16 +27,12 @@ final class ImageInput public ?string $name = null; #[Groups(['image:write'])] - #[ApiProperty(description: 'The description of the image', example: "Image 1 description")] - public ?string $description = null; - - #[Groups(['image:write'])] - #[ApiProperty(description: 'The comments of the image', example: "Image 1 comments")] - public ?string $comments = null; + #[ApiProperty(description: 'The type of the image', example: "Server")] + public ?string $source = 'input'; #[Groups(['image:write'])] #[ApiProperty(description: 'The type of the image', example: "Server")] - public ?string $source = 'input'; + public ?string $type = ''; #[Groups(['image:write'])] #[ApiProperty(description: 'The optional selected image')] @@ -84,8 +80,7 @@ final class ImageInput } $this->name = $image->getName(); - $this->description = $image->getDescription(); - $this->comments = $image->getComments(); + $this->type = $image->getType(); $this->remotePc = $image->isRemotePc(); $this->isGlobal = $image->isGlobal(); $this->version = $image->getVersion(); @@ -114,8 +109,7 @@ final class ImageInput } $image->setName($this->name); - $image->setDescription($this->description); - $image->setComments($this->comments); + $image->setType($this->type); if ($this->softwareProfile) { $image->setSoftwareProfile($this->softwareProfile->getEntity()); diff --git a/src/Dto/Input/NetworkSettingsInput.php b/src/Dto/Input/NetworkSettingsInput.php index fe3ef70..add2df6 100644 --- a/src/Dto/Input/NetworkSettingsInput.php +++ b/src/Dto/Input/NetworkSettingsInput.php @@ -7,6 +7,7 @@ use App\Dto\Output\HardwareProfileOutput; use App\Dto\Output\ImageRepositoryOutput; use App\Dto\Output\MenuOutput; use App\Dto\Output\OgLiveOutput; +use App\Dto\Output\PxeTemplateOutput; use App\Entity\HardwareProfile; use App\Entity\Menu; use App\Entity\NetworkSettings; @@ -80,6 +81,9 @@ class NetworkSettingsInput #[Groups(['organizational-unit:write'])] public ?ImageRepositoryOutput $repository = null; + #[Groups(['organizational-unit:write'])] + public ?PxeTemplateOutput $pxeTemplate = null; + #[Groups(['organizational-unit:write'])] public ?string $ogshare = null; @@ -120,6 +124,10 @@ class NetworkSettingsInput if ($networkSettings->getRepository()) { $this->repository = new ImageRepositoryOutput($networkSettings->getRepository()); } + + if ($networkSettings->getPxeTemplate()) { + $this->pxeTemplate = new PxeTemplateOutput($networkSettings->getPxeTemplate()); + } } public function createOrUpdateEntity(?NetworkSettings $networkSettings = null): NetworkSettings @@ -160,6 +168,10 @@ class NetworkSettingsInput $networkSettings->setRepository($this->repository->getEntity()); } + if ($this->pxeTemplate) { + $networkSettings->setPxeTemplate($this->pxeTemplate->getEntity()); + } + return $networkSettings; } } \ No newline at end of file diff --git a/src/Dto/Input/PxeTemplateInput.php b/src/Dto/Input/PxeTemplateInput.php index 3474781..58d4946 100644 --- a/src/Dto/Input/PxeTemplateInput.php +++ b/src/Dto/Input/PxeTemplateInput.php @@ -18,6 +18,10 @@ final class PxeTemplateInput #[ApiProperty(description: 'The content of the pxeTemplate', example: "content of the pxeTemplate 1")] public ?string $templateContent = null; + #[Groups(['pxe-template:write'])] + #[ApiProperty(description: 'The default pxeTemplate', example: "true")] + public ?bool $isDefault = null; + public function __construct(?PxeTemplate $pxeTemplate = null) { if (!$pxeTemplate) { @@ -26,6 +30,7 @@ final class PxeTemplateInput $this->name = $pxeTemplate->getName(); $this->templateContent = $pxeTemplate->getTemplateContent(); + $this->isDefault = $pxeTemplate->isDefault(); } public function createOrUpdateEntity(?PxeTemplate $pxeTemplate = null): PxeTemplate @@ -36,6 +41,7 @@ final class PxeTemplateInput $pxeTemplate->setName($this->name); $pxeTemplate->setTemplateContent($this->templateContent); + $pxeTemplate->setDefault($this->isDefault); return $pxeTemplate; } diff --git a/src/Dto/Input/RemoteCalendarRuleInput.php b/src/Dto/Input/RemoteCalendarRuleInput.php index c0883c4..55fd47c 100644 --- a/src/Dto/Input/RemoteCalendarRuleInput.php +++ b/src/Dto/Input/RemoteCalendarRuleInput.php @@ -5,9 +5,12 @@ namespace App\Dto\Input; use ApiPlatform\Metadata\ApiProperty; use App\Dto\Output\RemoteCalendarOutput; use App\Entity\RemoteCalendarRule; +use App\Validator\Constraints\RemoteCalendarRuleUnique; use Symfony\Component\Serializer\Annotation\Groups; use Symfony\Component\Validator\Constraints as Assert; + +#[RemoteCalendarRuleUnique] final class RemoteCalendarRuleInput { #[Assert\NotNull()] diff --git a/src/Dto/Output/ClientOutput.php b/src/Dto/Output/ClientOutput.php index 7e7e8ec..8e600d7 100644 --- a/src/Dto/Output/ClientOutput.php +++ b/src/Dto/Output/ClientOutput.php @@ -57,7 +57,7 @@ final class ClientOutput extends AbstractOutput #[Groups(['client:read', 'organizational-unit:read', 'pxe-template:read', 'trace:read', 'subnet:read'])] #[ApiProperty(readableLink: true )] - public ?PxeTemplateOutput $template = null; + public ?PxeTemplateOutput $pxeTemplate = null; #[Groups(['client:read', 'organizational-unit:read', 'pxe-template:read', 'trace:read', 'subnet:read'])] #[ApiProperty(readableLink: true )] @@ -106,7 +106,6 @@ final class ClientOutput extends AbstractOutput $this->menu = $client->getMenu() ? new MenuOutput($client->getMenu()) : null; $this->position = $client->getPosition(); - $this->template = $client->getTemplate() ? new PxeTemplateOutput($client->getTemplate()) : null; $repository = $client->getRepository() ?? $client->getOrganizationalUnit()?->getNetworkSettings()?->getRepository(); @@ -118,12 +117,33 @@ final class ClientOutput extends AbstractOutput $this->ogLive = $ogLive ? new OgLiveOutput($ogLive) : null; + $template = $client->getTemplate() + ?? $client->getOrganizationalUnit()?->getNetworkSettings()?->getPxeTemplate(); + + $this->pxeTemplate = $template ? new PxeTemplateOutput($template) : null; + $this->hardwareProfile = $client->getHardwareProfile() ? new HardwareProfileOutput($client->getHardwareProfile()) : null; - $this->subnet = $client->getSubnet()?->getIpAddress(); + + if ($client->getSubnet()){ + $this->subnet = $client->getSubnet()?->getIpAddress().'/' + . $this->convertMaskToCIDR($client->getSubnet() ? $client->getSubnet()->getNetmask() : $client->getOrganizationalUnit()->getNetworkSettings()->getNetmask()); + } + $this->status = $client->getStatus(); $this->createdAt = $client->getCreatedAt(); $this->createdBy = $client->getCreatedBy(); $this->maintenance = $client->isMaintenance(); $this->pxeSync = $client->isPxeSync(); } + + public function convertMaskToCIDR($mask): int + { + $bits = 0; + $mask = explode(".", $mask); + + foreach ($mask as $oct) + $bits += strlen(str_replace("0", "", decbin($oct))); + + return $bits; + } } \ No newline at end of file diff --git a/src/Dto/Output/CommandTaskOutput.php b/src/Dto/Output/CommandTaskOutput.php index 76c519c..b687016 100644 --- a/src/Dto/Output/CommandTaskOutput.php +++ b/src/Dto/Output/CommandTaskOutput.php @@ -13,22 +13,25 @@ use Symfony\Component\Serializer\Annotation\Groups; final class CommandTaskOutput extends AbstractOutput { #[Groups(['command-task:read'])] - public array $commands = []; - - #[Groups(['command-task:read'])] - public array $commandGroups = []; + public ?string $name = null; #[Groups(['command-task:read'])] public array $clients = []; #[Groups(['command-task:read'])] - public \DateTimeInterface $dateTime; + public ?OrganizationalUnitOutput $organizationalUnit = null; + + #[Groups(['command-task:read'])] + public ?\DateTimeInterface $lastExecution = null; + + #[Groups(['command-task:read'])] + public ?\DateTimeInterface $nextExecution = null; #[Groups(['command-task:read'])] public ?string $notes = null; #[Groups(['command-task:read'])] - public ?string $status = null; + public ?string $scope = null; #[Groups(['command-task:read'])] public \DateTime $createdAt; @@ -41,21 +44,17 @@ final class CommandTaskOutput extends AbstractOutput { parent::__construct($commandTask); - $this->commands = $commandTask->getCommands()->map( - fn(Command $command) => new CommandOutput($command) - )->toArray(); - - $this->commandGroups = $commandTask->getCommandGroups()->map( - fn(CommandGroup $commandGroup) => new CommandGroupOutput($commandGroup) - )->toArray(); + $this->name = $commandTask->getName(); $this->clients = $commandTask->getClients()->map( fn(Client $client) => new ClientOutput($client) )->toArray(); - $this->dateTime = $commandTask->getDateTime(); + $this->organizationalUnit = new OrganizationalUnitOutput($commandTask->getOrganizationalUnit()); $this->notes = $commandTask->getNotes(); - $this->status = $commandTask->getStatus(); + $this->scope = $commandTask->getScope(); + $this->lastExecution = $commandTask->getLastExecution(); + $this->nextExecution = $commandTask->getNextExecution(); $this->createdAt = $commandTask->getCreatedAt(); $this->createdBy = $commandTask->getCreatedBy(); } diff --git a/src/Dto/Output/CommandTaskScheduleOutput.php b/src/Dto/Output/CommandTaskScheduleOutput.php index e832865..45dcd02 100644 --- a/src/Dto/Output/CommandTaskScheduleOutput.php +++ b/src/Dto/Output/CommandTaskScheduleOutput.php @@ -2,7 +2,40 @@ namespace App\Dto\Output; -class CommandTaskScheduleOutput -{ +use ApiPlatform\Metadata\Get; +use App\Entity\CommandTaskSchedule; +use Symfony\Component\Serializer\Annotation\Groups; +#[Get(shortName: 'CommandTaskSchedule')] +class CommandTaskScheduleOutput extends AbstractOutput +{ + #[Groups(['command-task-schedule:read'])] + public ?string $recurrenceType = null; + + #[Groups(['command-task-schedule:read'])] + public ?\DateTimeInterface $executionDate = null; + + #[Groups(['command-task-schedule:read'])] + public ?\DateTimeInterface $executionTime = null; + + #[Groups(['command-task-schedule:read'])] + public ?array $recurrenceDetails = null; + + #[Groups(['command-task-schedule:read'])] + public ?bool $enabled = null; + + public function __construct(?CommandTaskSchedule $commandTaskSchedule = null) + { + parent::__construct($commandTaskSchedule); + + if (!$commandTaskSchedule) { + return; + } + + $this->recurrenceType = $commandTaskSchedule->getRecurrenceType(); + $this->executionTime = $commandTaskSchedule->getExecutionTime(); + $this->executionDate = $commandTaskSchedule->getExecutionDate(); + $this->recurrenceDetails = $commandTaskSchedule->getRecurrenceDetails(); + $this->enabled = $commandTaskSchedule->isEnabled(); + } } \ No newline at end of file diff --git a/src/Dto/Output/CommandTaskScriptOutput.php b/src/Dto/Output/CommandTaskScriptOutput.php new file mode 100644 index 0000000..1354ffc --- /dev/null +++ b/src/Dto/Output/CommandTaskScriptOutput.php @@ -0,0 +1,42 @@ +content = $commandTaskScript->getContent(); + $this->order = $commandTaskScript->getExecutionOrder(); + $this->type = $commandTaskScript->getType(); + $this->parameters = $commandTaskScript->getParameters(); + $this->commandTaskOutput = new CommandTaskOutput($commandTaskScript->getCommandTask()); + } +} \ No newline at end of file diff --git a/src/Dto/Output/GitImageRepositoryOutput.php b/src/Dto/Output/GitImageRepositoryOutput.php new file mode 100644 index 0000000..aeceb17 --- /dev/null +++ b/src/Dto/Output/GitImageRepositoryOutput.php @@ -0,0 +1,62 @@ +getImage()) { + $this->image = new ImageOutput($gitImageRepository->getImage()); + } + } + + if ($gitImageRepository->getRepository()) { + $this->imageRepository = new ImageRepositoryOutput($gitImageRepository->getRepository()); + } + + $this->name = $gitImageRepository->getName(); + $this->description = $gitImageRepository->getDescription(); + $this->status = $gitImageRepository->getStatus(); + $this->branch = $gitImageRepository->getBranch(); + $this->tag = $gitImageRepository->getTag(); + $this->createdAt = $gitImageRepository->getCreatedAt(); + $this->createdBy = $gitImageRepository->getCreatedBy(); + } +} \ No newline at end of file diff --git a/src/Dto/Output/ImageImageRepositoryOutput.php b/src/Dto/Output/ImageImageRepositoryOutput.php index 9d4c4a0..ba15f15 100644 --- a/src/Dto/Output/ImageImageRepositoryOutput.php +++ b/src/Dto/Output/ImageImageRepositoryOutput.php @@ -13,17 +13,20 @@ class ImageImageRepositoryOutput extends AbstractOutput public ?ImageOutput $image = null; #[Groups(['image-image-repository:read', 'image:read'])] - public ?ImageRepositoryOutput $imageRepository= null; + public ?ImageRepositoryOutput $imageRepository = null; #[Groups(['image-image-repository:read', 'image:read'])] public string $status; - #[Groups(['image-image-repository:read', 'image:read'])] + #[Groups(['image-image-repository:read', 'image:read', 'partition:read'])] public string $name; #[Groups(['image-image-repository:read', 'image:read'])] public ?string $imageFullsum = null; + #[Groups(['image-image-repository:read', 'image:read'])] + public ?array $partitionInfo = null; + #[Groups(['image-image-repository:read', 'image:read'])] public ?string $datasize = null; @@ -55,6 +58,7 @@ class ImageImageRepositoryOutput extends AbstractOutput $this->name = $imageImageRepository->getName(); $this->version = $imageImageRepository->getVersion(); + $this->partitionInfo = json_decode($imageImageRepository->getPartitionInfo(), true); $this->status = $imageImageRepository->getStatus(); $this->imageFullsum = $imageImageRepository->getImageFullsum(); $this->datasize = $imageImageRepository->getDatasize(); diff --git a/src/Dto/Output/ImageOutput.php b/src/Dto/Output/ImageOutput.php index 5daead3..a5cf26f 100644 --- a/src/Dto/Output/ImageOutput.php +++ b/src/Dto/Output/ImageOutput.php @@ -12,21 +12,17 @@ use Symfony\Component\Serializer\Annotation\Groups; #[Get(shortName: 'Image')] final class ImageOutput extends AbstractOutput { - #[Groups(['image:read', 'image-image-repository:read'])] + #[Groups(['image:read', 'image-image-repository:read', 'git-image-repository:read'])] public ?string $name = ''; - - #[Groups(['image:read'])] - public ?string $description = ''; - - #[Groups(['image:read'])] - public ?string $comments = ''; - #[Groups(['image:read', 'image-image-repository:read'])] public ?bool $remotePc = null; #[Groups(['image:read', 'image-image-repository:read'])] public ?bool $isGlobal = null; + #[Groups(['image:read', 'image-image-repository:read'])] + public ?string $type = ''; + #[Groups(['image:read'])] public ?SoftwareProfileOutput $softwareProfile = null; @@ -53,8 +49,6 @@ final class ImageOutput extends AbstractOutput parent::__construct($image); $this->name = $image->getName(); - $this->description = $image->getDescription(); - $this->comments = $image->getComments(); $this->softwareProfile = $image->getSoftwareProfile() ? new SoftwareProfileOutput($image->getSoftwareProfile()) : null; $this->imageRepositories = $image->getImageImageRepositories()->map( @@ -62,6 +56,7 @@ final class ImageOutput extends AbstractOutput )->toArray(); $this->version = $image->getVersion(); + $this->type = $image->getType(); $this->partitionInfo = json_decode($image->getPartitionInfo(), true); $this->remotePc = $image->isRemotePc(); $this->isGlobal = $image->isGlobal(); diff --git a/src/Dto/Output/NetworkSettingsOutput.php b/src/Dto/Output/NetworkSettingsOutput.php index 00c089a..329cede 100644 --- a/src/Dto/Output/NetworkSettingsOutput.php +++ b/src/Dto/Output/NetworkSettingsOutput.php @@ -63,6 +63,9 @@ final class NetworkSettingsOutput extends AbstractOutput #[Groups(['network-settings:read', "organizational-unit:read", "client:read"])] public ?ImageRepositoryOutput $repository = null; + #[Groups(['network-settings:read', "organizational-unit:read", "client:read"])] + public ?PxeTemplateOutput $pxeTemplate = null; + #[Groups(['network-settings:read', "organizational-unit:read", "client:read"])] public ?string $ogshare = null; @@ -108,6 +111,10 @@ final class NetworkSettingsOutput extends AbstractOutput $this->repository = new ImageRepositoryOutput($networkSettings->getRepository()); } + if ($networkSettings->getPxeTemplate()) { + $this->pxeTemplate = new PxeTemplateOutput($networkSettings->getPxeTemplate()); + } + $this->createdAt = $networkSettings->getCreatedAt(); $this->createdBy = $networkSettings->getCreatedBy(); } diff --git a/src/Dto/Output/OgLiveOutput.php b/src/Dto/Output/OgLiveOutput.php index 5e636bd..44f0b53 100644 --- a/src/Dto/Output/OgLiveOutput.php +++ b/src/Dto/Output/OgLiveOutput.php @@ -40,7 +40,7 @@ final class OgLiveOutput extends AbstractOutput #[Groups(['og-live:read', 'client:read', "organizational-unit:read"])] public ?string $filename = null; - #[Groups(['og-live:read'])] + #[Groups(['og-live:read', 'client:read', "organizational-unit:read"])] public ?string $kernel = ''; #[Groups(['og-live:read'])] diff --git a/src/Dto/Output/OrganizationalUnitOutput.php b/src/Dto/Output/OrganizationalUnitOutput.php index d5e0b69..d3705bd 100644 --- a/src/Dto/Output/OrganizationalUnitOutput.php +++ b/src/Dto/Output/OrganizationalUnitOutput.php @@ -11,7 +11,7 @@ use Symfony\Component\Serializer\Annotation\Groups; #[Get(shortName: 'OrganizationalUnit')] final class OrganizationalUnitOutput extends AbstractOutput { - #[Groups(['organizational-unit:read', "client:read", "user:read", 'organizational-unit:read:collection:short', 'software-profile:read'])] + #[Groups(['organizational-unit:read', "client:read", "user:read", 'organizational-unit:read:collection:short', 'software-profile:read', 'command-task:read'])] public string $name; #[Groups(['organizational-unit:read'])] diff --git a/src/Dto/Output/PartitionOutput.php b/src/Dto/Output/PartitionOutput.php index a9bb3d7..394abcb 100644 --- a/src/Dto/Output/PartitionOutput.php +++ b/src/Dto/Output/PartitionOutput.php @@ -3,6 +3,7 @@ namespace App\Dto\Output; use ApiPlatform\Metadata\Get; +use App\Entity\ImageImageRepository; use App\Entity\Partition; use Symfony\Component\Serializer\Annotation\Groups; @@ -30,6 +31,9 @@ class PartitionOutput extends AbstractOutput #[Groups(['partition:read', 'client:read'])] public ?OperativeSystemOutput $operativeSystem = null; + #[Groups(['partition:read', 'client:read'])] + public ?ImageImageRepositoryOutput $image = null; + #[Groups(['partition:read', 'client:read'])] public ?int $memoryUsage = null; @@ -43,9 +47,15 @@ class PartitionOutput extends AbstractOutput $this->size = $partition->getSize() / 1024 ; $this->cacheContent = $partition->getCacheContent(); $this->filesystem = $partition->getFilesystem(); + if ($partition->getOperativeSystem()) { $this->operativeSystem = new OperativeSystemOutput($partition->getOperativeSystem()); } + + if ($partition->getImage()) { + $this->image = new ImageImageRepositoryOutput($partition->getImage()); + } + $this->memoryUsage = $partition->getMemoryUsage() / 100; } } \ No newline at end of file diff --git a/src/Dto/Output/PxeTemplateOutput.php b/src/Dto/Output/PxeTemplateOutput.php index 41cbbe0..f625215 100644 --- a/src/Dto/Output/PxeTemplateOutput.php +++ b/src/Dto/Output/PxeTemplateOutput.php @@ -20,6 +20,9 @@ final class PxeTemplateOutput extends AbstractOutput #[Groups(['pxe-template:read'])] public ?string $templateContent = ''; + #[Groups(['pxe-template:read'])] + public ?bool $isDefault = null; + #[Groups(['pxe-template:read'])] public ?int $clientsLength = 0; @@ -37,6 +40,7 @@ final class PxeTemplateOutput extends AbstractOutput $this->synchronized = $pxeTemplate->isSynchronized(); $this->templateContent = $pxeTemplate->getTemplateContent(); $this->clientsLength = $pxeTemplate->getClients()->count(); + $this->isDefault = $pxeTemplate->isDefault(); $this->createdAt = $pxeTemplate->getCreatedAt(); $this->createdBy = $pxeTemplate->getCreatedBy(); } diff --git a/src/Entity/Client.php b/src/Entity/Client.php index a3930ca..023fcf8 100644 --- a/src/Entity/Client.php +++ b/src/Entity/Client.php @@ -64,6 +64,7 @@ class Client extends AbstractEntity private ?array $position = ['x' => 0, 'y' => 0]; #[ORM\ManyToOne(inversedBy: 'clients')] + #[ORM\JoinColumn( onDelete: 'SET NULL')] private ?PxeTemplate $template = null; #[ORM\ManyToOne()] diff --git a/src/Entity/Command.php b/src/Entity/Command.php index 0fea5cf..ab5d4bb 100644 --- a/src/Entity/Command.php +++ b/src/Entity/Command.php @@ -29,12 +29,6 @@ class Command extends AbstractEntity #[ORM\ManyToMany(targetEntity: CommandGroup::class, mappedBy: 'commands')] private Collection $commandGroups; - /** - * @var Collection - */ - #[ORM\ManyToMany(targetEntity: CommandTask::class, mappedBy: 'commands')] - private Collection $commandTasks; - #[ORM\Column(nullable: true)] private ?bool $parameters = null; @@ -42,7 +36,6 @@ class Command extends AbstractEntity { parent::__construct(); $this->commandGroups = new ArrayCollection(); - $this->commandTasks = new ArrayCollection(); } public function getScript(): ?string diff --git a/src/Entity/CommandTask.php b/src/Entity/CommandTask.php index b742212..f572155 100644 --- a/src/Entity/CommandTask.php +++ b/src/Entity/CommandTask.php @@ -11,122 +11,51 @@ use Doctrine\ORM\Mapping as ORM; #[ORM\Entity(repositoryClass: CommandTaskRepository::class)] class CommandTask extends AbstractEntity { - /** - * @var Collection - */ - #[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; + use NameableTrait; #[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; + #[ORM\Column(type: Types::ARRAY, nullable: true)] + private ?array $parameters = null; + + #[ORM\ManyToOne] + private ?OrganizationalUnit $organizationalUnit = null; + + /** + * @var Collection + */ + #[ORM\OneToMany(mappedBy: 'commandTask', targetEntity: CommandTaskSchedule::class, cascade: ['persist'], orphanRemoval: true)] + private Collection $commandTaskSchedules; + + /** + * @var Collection + */ + #[ORM\OneToMany(mappedBy: 'commandTask', targetEntity: CommandTaskScript::class, cascade: ['persist'], orphanRemoval: true)] + private Collection $commandTaskScripts; + + #[ORM\Column(type: Types::DATETIME_MUTABLE, nullable: true)] + private ?\DateTimeInterface $lastExecution = null; + + #[ORM\Column(type: Types::DATETIME_MUTABLE, nullable: true)] + private ?\DateTimeInterface $nextExecution = null; + + #[ORM\Column(length: 255)] + private ?string $scope = null; + 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; + $this->commandTaskSchedules = new ArrayCollection(); + $this->commandTaskScripts = new ArrayCollection(); } public function getNotes(): ?string @@ -141,18 +70,6 @@ class CommandTask extends AbstractEntity return $this; } - public function getStatus(): ?string - { - return $this->status; - } - - public function setStatus(string $status): static - { - $this->status = $status; - - return $this; - } - /** * @return Collection */ @@ -187,4 +104,200 @@ class CommandTask extends AbstractEntity return $this; } + + public function getParameters(): ?array + { + return $this->parameters; + } + + public function setParameters(?array $parameters): static + { + $this->parameters = $parameters; + + return $this; + } + + public function getOrganizationalUnit(): ?OrganizationalUnit + { + return $this->organizationalUnit; + } + + public function setOrganizationalUnit(?OrganizationalUnit $organizationalUnit): static + { + $this->organizationalUnit = $organizationalUnit; + + return $this; + } + + /** + * @return Collection + */ + public function getCommandTaskSchedules(): Collection + { + return $this->commandTaskSchedules; + } + + public function addCommandTaskSchedule(CommandTaskSchedule $commandTaskSchedule): static + { + if (!$this->commandTaskSchedules->contains($commandTaskSchedule)) { + $this->commandTaskSchedules->add($commandTaskSchedule); + $commandTaskSchedule->setCommandTask($this); + } + + return $this; + } + + public function removeCommandTaskSchedule(CommandTaskSchedule $commandTaskSchedule): static + { + if ($this->commandTaskSchedules->removeElement($commandTaskSchedule)) { + // set the owning side to null (unless already changed) + if ($commandTaskSchedule->getCommandTask() === $this) { + $commandTaskSchedule->setCommandTask(null); + } + } + + return $this; + } + + /** + * @return Collection + */ + public function getCommandTaskScripts(): Collection + { + return $this->commandTaskScripts; + } + + public function addCommandTaskScript(CommandTaskScript $commandTaskScript): static + { + if (!$this->commandTaskScripts->contains($commandTaskScript)) { + $this->commandTaskScripts->add($commandTaskScript); + $commandTaskScript->setCommandTask($this); + } + + return $this; + } + + public function removeCommandTaskScript(CommandTaskScript $commandTaskScript): static + { + if ($this->commandTaskScripts->removeElement($commandTaskScript)) { + // set the owning side to null (unless already changed) + if ($commandTaskScript->getCommandTask() === $this) { + $commandTaskScript->setCommandTask(null); + } + } + + return $this; + } + + public function getLastExecution(): ?\DateTimeInterface + { + return $this->lastExecution; + } + + public function setLastExecution(?\DateTimeInterface $lastExecution): static + { + $this->lastExecution = $lastExecution; + + return $this; + } + + public function getNextExecution(): ?\DateTimeInterface + { + return $this->nextExecution; + } + + public function setNextExecution(?\DateTimeInterface $nextExecution): static + { + $this->nextExecution = $nextExecution; + + return $this; + } + + /** + * @throws \Exception + */ + public function calculateNextExecutionDate(): ?\DateTimeInterface + { + $now = new \DateTime(); + $closestDateTime = null; + + foreach ($this->getCommandTaskSchedules() as $schedule) { + $type = $schedule->getRecurrenceType(); + $executionTime = $schedule->getExecutionTime(); + + if ($type === 'none') { + $execDate = $schedule->getExecutionDate(); + if ($execDate !== null && $execDate > $now) { + if ($executionTime !== null) { + $execDateTime = \DateTime::createFromFormat( + 'Y-m-d H:i:s', + $execDate->format('Y-m-d') . ' ' . $executionTime->format('H:i:s') + ); + } else { + $execDateTime = $execDate; + } + + if ($closestDateTime === null || $execDateTime < $closestDateTime) { + $closestDateTime = $execDateTime; + } + } + } else { + $details = $schedule->getRecurrenceDetails(); + if ($details === null) { + continue; + } + + $init = (new \DateTime($details['initDate'] ?? 'now'))->setTime(0, 0); + $end = (new \DateTime($details['endDate'] ?? '+1 year'))->setTime(0, 0); + + $validDays = array_map('mb_strtolower', $details['daysOfWeek'] ?? []); + $validMonths = array_map('mb_strtolower', $details['months'] ?? []); + + $current = (new \DateTime())->setTime(0, 0); + + while ($current <= $end) { + if ($current < $init) { + $current->modify('+1 day'); + continue; + } + + $translatedDay = mb_strtolower($current->format('l')); + $translatedMonth = mb_strtolower($current->format('F')); + + if (in_array($translatedDay, $validDays, true) && in_array($translatedMonth, $validMonths, true)) { + if ($executionTime !== null) { + $execDateTime = \DateTime::createFromFormat( + 'Y-m-d H:i:s', + $current->format('Y-m-d') . ' ' . $executionTime->format('H:i:s') + ); + } else { + $execDateTime = clone $current; + } + + if ($execDateTime > $now && ($closestDateTime === null || $execDateTime < $closestDateTime)) { + $closestDateTime = $execDateTime; + } + + break; + } + + $current->modify('+1 day'); + } + } + } + + return $closestDateTime; + } + + public function getScope(): ?string + { + return $this->scope; + } + + public function setScope(string $scope): static + { + $this->scope = $scope; + + return $this; + } } diff --git a/src/Entity/CommandTaskSchedule.php b/src/Entity/CommandTaskSchedule.php new file mode 100644 index 0000000..b0698fd --- /dev/null +++ b/src/Entity/CommandTaskSchedule.php @@ -0,0 +1,89 @@ +recurrenceType; + } + + public function setRecurrenceType(string $recurrenceType): static + { + $this->recurrenceType = $recurrenceType; + + return $this; + } + + public function getRecurrenceDetails(): ?array + { + return $this->recurrenceDetails; + } + + public function setRecurrenceDetails(?array $recurrenceDetails): static + { + $this->recurrenceDetails = $recurrenceDetails; + + return $this; + } + + public function getCommandTask(): ?CommandTask + { + return $this->commandTask; + } + + public function setCommandTask(?CommandTask $commandTask): static + { + $this->commandTask = $commandTask; + + return $this; + } + + public function getExecutionTime(): ?\DateTimeInterface + { + return $this->executionTime; + } + + public function setExecutionTime(?\DateTimeInterface $executionTime): static + { + $this->executionTime = $executionTime; + + return $this; + } + + public function getExecutionDate(): ?\DateTimeInterface + { + return $this->executionDate; + } + + public function setExecutionDate(?\DateTimeInterface $executionDate): static + { + $this->executionDate = $executionDate; + + return $this; + } +} diff --git a/src/Entity/CommandTaskScript.php b/src/Entity/CommandTaskScript.php new file mode 100644 index 0000000..028e3c5 --- /dev/null +++ b/src/Entity/CommandTaskScript.php @@ -0,0 +1,92 @@ +id; + } + + public function getContent(): ?string + { + return $this->content; + } + + public function setContent(?string $content): static + { + $this->content = $content; + + return $this; + } + + public function getExecutionOrder(): ?int + { + return $this->executionOrder; + } + + public function setExecutionOrder(int $executionOrder): static + { + $this->executionOrder = $executionOrder; + + return $this; + } + + public function getCommandTask(): ?CommandTask + { + return $this->commandTask; + } + + public function setCommandTask(?CommandTask $commandTask): static + { + $this->commandTask = $commandTask; + + return $this; + } + + public function getType(): ?string + { + return $this->type; + } + + public function setType(string $type): static + { + $this->type = $type; + + return $this; + } + + public function getParameters(): ?array + { + return $this->parameters; + } + + public function setParameters(?array $parameters): static + { + $this->parameters = $parameters; + + return $this; + } +} diff --git a/src/Entity/GitImageRepository.php b/src/Entity/GitImageRepository.php new file mode 100644 index 0000000..2a56010 --- /dev/null +++ b/src/Entity/GitImageRepository.php @@ -0,0 +1,134 @@ +image; + } + + public function setImage(?Image $image): static + { + $this->image = $image; + + return $this; + } + + public function getStatus(): ?string + { + return $this->status; + } + + public function setStatus(string $status): static + { + $this->status = $status; + + return $this; + } + + public function getDescription(): ?string + { + return $this->description; + } + + public function setDescription(?string $description): static + { + $this->description = $description; + + return $this; + } + + public function getBranch(): ?string + { + return $this->branch; + } + + public function setBranch(?string $branch): static + { + $this->branch = $branch; + + return $this; + } + + public function getTag(): ?string + { + return $this->tag; + } + + public function setTag(?string $tag): static + { + $this->tag = $tag; + + return $this; + } + + public function getVersion(): ?int + { + return $this->version; + } + + public function setVersion(?int $version): static + { + $this->version = $version; + + return $this; + } + + public function isCreated(): ?bool + { + return $this->created; + } + + public function setCreated(bool $created): static + { + $this->created = $created; + + return $this; + } + + public function getRepository(): ?ImageRepository + { + return $this->repository; + } + + public function setRepository(?ImageRepository $imageRepository): static + { + $this->repository = $imageRepository; + + return $this; + } +} diff --git a/src/Entity/Image.php b/src/Entity/Image.php index 727c212..5e465cd 100644 --- a/src/Entity/Image.php +++ b/src/Entity/Image.php @@ -15,12 +15,6 @@ class Image extends AbstractEntity { use NameableTrait; - #[ORM\Column(length: 255, nullable: true)] - private ?string $description = null; - - #[ORM\Column(length: 255, nullable: true)] - private ?string $comments = null; - #[ORM\ManyToOne] #[ORM\JoinColumn(nullable: true)] private ?SoftwareProfile $softwareProfile = null; @@ -49,34 +43,20 @@ class Image extends AbstractEntity #[ORM\Column(nullable: true)] private ?int $version = null; + /** + * @var Collection + */ + #[ORM\OneToMany(mappedBy: 'image', targetEntity: GitImageRepository::class,cascade: ['persist'], orphanRemoval: true)] + private Collection $gitImageRepositories; + + #[ORM\Column(length: 255)] + private ?string $type = null; + public function __construct() { parent::__construct(); $this->imageImageRepositories = new ArrayCollection(); - } - - public function getDescription(): ?string - { - return $this->description; - } - - public function setDescription(?string $description): static - { - $this->description = $description; - - return $this; - } - - public function getComments(): ?string - { - return $this->comments; - } - - public function setComments(?string $comments): static - { - $this->comments = $comments; - - return $this; + $this->gitImageRepositories = new ArrayCollection(); } public function getSoftwareProfile(): ?SoftwareProfile @@ -206,4 +186,46 @@ class Image extends AbstractEntity return $this; } + + /** + * @return Collection + */ + public function getGitImageRepositories(): Collection + { + return $this->gitImageRepositories; + } + + public function addGitImageRepository(GitImageRepository $gitImageRepository): static + { + if (!$this->gitImageRepositories->contains($gitImageRepository)) { + $this->gitImageRepositories->add($gitImageRepository); + $gitImageRepository->setImage($this); + } + + return $this; + } + + public function removeGitImageRepository(GitImageRepository $gitImageRepository): static + { + if ($this->gitImageRepositories->removeElement($gitImageRepository)) { + // set the owning side to null (unless already changed) + if ($gitImageRepository->getImage() === $this) { + $gitImageRepository->setImage(null); + } + } + + return $this; + } + + public function getType(): ?string + { + return $this->type; + } + + public function setType(string $type): static + { + $this->type = $type; + + return $this; + } } diff --git a/src/Entity/ImageImageRepository.php b/src/Entity/ImageImageRepository.php index 6de9f49..69c9116 100644 --- a/src/Entity/ImageImageRepository.php +++ b/src/Entity/ImageImageRepository.php @@ -23,6 +23,9 @@ class ImageImageRepository extends AbstractEntity #[ORM\Column(nullable: true)] private ?bool $created = null; + #[ORM\Column(length: 255, nullable: true)] + private ?string $partitionInfo = null; + #[ORM\Column(length: 255, nullable: true)] private ?string $imageFullsum = null; @@ -86,6 +89,18 @@ class ImageImageRepository extends AbstractEntity return $this; } + public function getPartitionInfo(): ?string + { + return $this->partitionInfo; + } + + public function setPartitionInfo(?string $partitionInfo): static + { + $this->partitionInfo = $partitionInfo; + + return $this; + } + public function getImageFullsum(): ?string { return $this->imageFullsum; diff --git a/src/Entity/ImageRepository.php b/src/Entity/ImageRepository.php index 7588190..8be575a 100644 --- a/src/Entity/ImageRepository.php +++ b/src/Entity/ImageRepository.php @@ -32,10 +32,17 @@ class ImageRepository extends AbstractEntity #[ORM\Column(length: 255, nullable: true)] private ?string $sshPort = null; + /** + * @var Collection + */ + #[ORM\OneToMany(mappedBy: 'repository', targetEntity: GitImageRepository::class)] + private Collection $gitImageRepositories; + public function __construct() { parent::__construct(); $this->imageImageRepositories = new ArrayCollection(); + $this->gitImageRepositories = new ArrayCollection(); } public function getIp(): ?string @@ -115,4 +122,34 @@ class ImageRepository extends AbstractEntity return $this; } + + /** + * @return Collection + */ + public function getGitImageRepositories(): Collection + { + return $this->gitImageRepositories; + } + + public function addGitImageRepository(GitImageRepository $gitImageRepository): static + { + if (!$this->gitImageRepositories->contains($gitImageRepository)) { + $this->gitImageRepositories->add($gitImageRepository); + $gitImageRepository->setImageRepository($this); + } + + return $this; + } + + public function removeGitImageRepository(GitImageRepository $gitImageRepository): static + { + if ($this->gitImageRepositories->removeElement($gitImageRepository)) { + // set the owning side to null (unless already changed) + if ($gitImageRepository->getImageRepository() === $this) { + $gitImageRepository->setImageRepository(null); + } + } + + return $this; + } } diff --git a/src/Entity/NetworkSettings.php b/src/Entity/NetworkSettings.php index bab7b65..7d58e0f 100644 --- a/src/Entity/NetworkSettings.php +++ b/src/Entity/NetworkSettings.php @@ -76,6 +76,9 @@ class NetworkSettings extends AbstractEntity #[ORM\Column(length: 255, nullable: true)] private ?string $ogShare = null; + #[ORM\ManyToOne] + private ?PxeTemplate $pxeTemplate = null; + public function __construct() { parent::__construct(); @@ -337,4 +340,16 @@ class NetworkSettings extends AbstractEntity return $this; } + + public function getPxeTemplate(): ?PxeTemplate + { + return $this->pxeTemplate; + } + + public function setPxeTemplate(?PxeTemplate $pxeTemplate): static + { + $this->pxeTemplate = $pxeTemplate; + + return $this; + } } diff --git a/src/Entity/Partition.php b/src/Entity/Partition.php index 554763d..5814948 100644 --- a/src/Entity/Partition.php +++ b/src/Entity/Partition.php @@ -40,7 +40,7 @@ class Partition extends AbstractEntity #[ORM\ManyToOne(inversedBy: 'partitions')] #[ORM\JoinColumn(nullable: true)] - private ?Image $image = null; + private ?ImageImageRepository $image = null; public function getDiskNumber(): ?int { @@ -150,12 +150,12 @@ class Partition extends AbstractEntity return $this; } - public function getImage(): ?Image + public function getImage(): ?ImageImageRepository { return $this->image; } - public function setImage(?Image $image): static + public function setImage(?ImageImageRepository $image): static { $this->image = $image; diff --git a/src/Entity/PxeTemplate.php b/src/Entity/PxeTemplate.php index 5a3efaa..bfd9745 100644 --- a/src/Entity/PxeTemplate.php +++ b/src/Entity/PxeTemplate.php @@ -26,6 +26,9 @@ class PxeTemplate extends AbstractEntity #[ORM\OneToMany(mappedBy: 'template', targetEntity: Client::class)] private Collection $clients; + #[ORM\Column(nullable: true)] + private ?bool $isDefault = null; + public function __construct() { parent::__construct(); @@ -82,4 +85,16 @@ class PxeTemplate extends AbstractEntity return $this; } + + public function isDefault(): ?bool + { + return $this->isDefault; + } + + public function setDefault(?bool $isDefault): static + { + $this->isDefault = $isDefault; + + return $this; + } } diff --git a/src/EventSubscriber/ClientSubscriber.php b/src/EventSubscriber/ClientSubscriber.php index 351e352..7c652c2 100644 --- a/src/EventSubscriber/ClientSubscriber.php +++ b/src/EventSubscriber/ClientSubscriber.php @@ -49,10 +49,14 @@ final readonly class ClientSubscriber implements EventSubscriberInterface /** @var Client $client */ $client = $clientOutput->getEntity(); - if ($client->getTemplate() === null) { + $template = !$client->getTemplate() ? + $client->getTemplate() : + $client->getOrganizationalUnit()?->getNetworkSettings()?->getPxeTemplate(); + + if ($template === null) { return; } - $this->postAction->__invoke($client, $client->getTemplate()); + $this->postAction->__invoke($client, $template); } } \ No newline at end of file diff --git a/src/EventSubscriber/CommandTaskScheduleSubscriber.php b/src/EventSubscriber/CommandTaskScheduleSubscriber.php new file mode 100644 index 0000000..61d7588 --- /dev/null +++ b/src/EventSubscriber/CommandTaskScheduleSubscriber.php @@ -0,0 +1,60 @@ + ['updateCommandTaskNextExecution', EventPriorities::POST_WRITE], + ]; + } + + /** + * @throws \Exception + */ + public function updateCommandTaskNextExecution(ViewEvent $event): void + { + $commandTaskScheduleOutput = $event->getControllerResult(); + $method = $event->getRequest()->getMethod(); + + if (!$commandTaskScheduleOutput instanceof CommandTaskScheduleOutput || + !in_array($method, [Request::METHOD_POST, Request::METHOD_PUT, Request::METHOD_PATCH, Request::METHOD_DELETE])) { + return; + } + + /** @var CommandTaskSchedule $commandTaskSchedule */ + $commandTaskSchedule = $commandTaskScheduleOutput->getEntity(); + + $commandTask = $commandTaskSchedule->getCommandTask(); + + if ($commandTask === null) { + return; + } + + $commandTask->setNextExecution($commandTask->calculateNextExecutionDate()); + + $this->entityManager->persist($commandTask); + $this->entityManager->flush(); + } + +} \ No newline at end of file diff --git a/src/EventSubscriber/CommandTaskScriptSubscriber.php b/src/EventSubscriber/CommandTaskScriptSubscriber.php new file mode 100644 index 0000000..7eddf30 --- /dev/null +++ b/src/EventSubscriber/CommandTaskScriptSubscriber.php @@ -0,0 +1,59 @@ + ['updateCommandTaskNextExecution', EventPriorities::POST_WRITE], + ]; + } + + /** + * @throws \Exception + */ + public function updateCommandTaskNextExecution(ViewEvent $event): void + { + $commandTaskScriptOutput = $event->getControllerResult(); + $method = $event->getRequest()->getMethod(); + + if (!$commandTaskScriptOutput instanceof CommandTaskScriptOutput || + !in_array($method, [Request::METHOD_POST, Request::METHOD_PUT, Request::METHOD_PATCH, Request::METHOD_DELETE])) { + return; + } + + /** @var CommandTaskScript $commandTaskScript */ + $commandTaskScript = $commandTaskScriptOutput->getEntity(); + + $commandTask = $commandTaskScript->getCommandTask(); + + if ($commandTask === null) { + return; + } + + $commandTask->setNextExecution($commandTask->calculateNextExecutionDate()); + + $this->entityManager->persist($commandTask); + $this->entityManager->flush(); + } + +} \ No newline at end of file diff --git a/src/EventSubscriber/OrganizationalUnitSubscriber.php b/src/EventSubscriber/OrganizationalUnitSubscriber.php index b41a44b..4e0c93f 100644 --- a/src/EventSubscriber/OrganizationalUnitSubscriber.php +++ b/src/EventSubscriber/OrganizationalUnitSubscriber.php @@ -64,7 +64,7 @@ final readonly class OrganizationalUnitSubscriber implements EventSubscriberInte $this->updateChildrenNetworkSettings($organizationalUnitEntity, $newNetworkSettings); if ($organizationalUnitEntity->getType() === OrganizationalUnitTypes::CLASSROOM) { - //$this->syncOgBoot($organizationalUnitEntity); + $this->syncOgBoot($organizationalUnitEntity); } } @@ -82,6 +82,7 @@ final readonly class OrganizationalUnitSubscriber implements EventSubscriberInte $client->setOgLive($networkSettings->getOgLive()); $client->setMenu($networkSettings->getMenu()); $client->setRepository($networkSettings->getRepository()); + $client->setTemplate($networkSettings->getPxeTemplate()); $this->entityManager->persist($client); } @@ -116,6 +117,7 @@ final readonly class OrganizationalUnitSubscriber implements EventSubscriberInte $newNetworkSettings->setMenu($organizationalUnitEntity->getNetworkSettings()->getMenu()); $newNetworkSettings->setRepository($organizationalUnitEntity->getNetworkSettings()->getRepository()); $newNetworkSettings->setOgLive($organizationalUnitEntity->getNetworkSettings()->getOgLive()); + $newNetworkSettings->setPxeTemplate($organizationalUnitEntity->getNetworkSettings()->getPxeTemplate()); $newNetworkSettings->setNetiface($organizationalUnitEntity->getNetworkSettings()->getNetiface()); return $newNetworkSettings; diff --git a/src/EventSubscriber/PxeTemplateSubscriber.php b/src/EventSubscriber/PxeTemplateSubscriber.php new file mode 100644 index 0000000..fc10220 --- /dev/null +++ b/src/EventSubscriber/PxeTemplateSubscriber.php @@ -0,0 +1,72 @@ + ['syncPxeTemplate', EventPriorities::POST_WRITE], + ]; + } + + /** + * @throws TransportExceptionInterface + * @throws ServerExceptionInterface + * @throws RedirectionExceptionInterface + * @throws ClientExceptionInterface + */ + public function syncPxeTemplate(ViewEvent $event): void + { + $pxeTemplateOutput = $event->getControllerResult(); + $method = $event->getRequest()->getMethod(); + + if (!$pxeTemplateOutput instanceof PxeTemplateOutput || + !in_array($method, [Request::METHOD_POST, Request::METHOD_PUT, Request::METHOD_PATCH])) { + return; + } + + /** @var PxeTemplate $templateEntity */ + $templateEntity = $pxeTemplateOutput->getEntity(); + + try { + $this->postTemplateAction->__invoke($templateEntity); + } catch (\Exception $e) { + return ; + } + + $clients = $this->entityManager->getRepository(Client::class)->findBy(['template' => $templateEntity]); + + foreach ($clients as $client) { + $this->postAction->__invoke($client, $templateEntity); + } + } +} \ No newline at end of file diff --git a/src/Factory/CommandTaskFactory.php b/src/Factory/CommandTaskFactory.php index 91d2d09..c1375f3 100644 --- a/src/Factory/CommandTaskFactory.php +++ b/src/Factory/CommandTaskFactory.php @@ -3,6 +3,7 @@ namespace App\Factory; use App\Entity\CommandTask; +use App\Model\OrganizationalUnitTypes; use App\Repository\CommandTaskRepository; use Zenstruck\Foundry\ModelFactory; use Zenstruck\Foundry\Persistence\PersistentProxyObjectFactory; @@ -38,8 +39,9 @@ final class CommandTaskFactory extends ModelFactory { return [ 'createdAt' => self::faker()->dateTime(), - 'datetime' => self::faker()->dateTime(), - 'status' => self::faker()->text(255), + 'name' => self::faker()->text(255), + 'organizationalUnit' => OrganizationalUnitFactory::createOne(['type' => OrganizationalUnitTypes::ORGANIZATIONAL_UNIT])->_save(), + 'scope' => 'organizational-unit', 'updatedAt' => self::faker()->dateTime() ]; } diff --git a/src/Factory/ImageFactory.php b/src/Factory/ImageFactory.php index 9941cc2..4a2b1c7 100644 --- a/src/Factory/ImageFactory.php +++ b/src/Factory/ImageFactory.php @@ -37,6 +37,7 @@ final class ImageFactory extends ModelFactory 'name' => self::faker()->text(255), 'softwareProfile' => SoftwareProfileFactory::new(), 'updatedAt' => self::faker()->dateTime(), + 'type' => 'monolithic', 'remotePc' => self::faker()->boolean(), 'isGlobal' => false, ]; diff --git a/src/Factory/PartitionFactory.php b/src/Factory/PartitionFactory.php index d791b7c..c40c6ab 100644 --- a/src/Factory/PartitionFactory.php +++ b/src/Factory/PartitionFactory.php @@ -43,7 +43,7 @@ final class PartitionFactory extends ModelFactory 'updatedAt' => self::faker()->dateTime(), 'operativeSystem' => OperativeSystemFactory::new(), 'client' => ClientFactory::new(), - 'image' => ImageFactory::new(), + 'image' => ImageImageRepositoryFactory::new(), ]; } diff --git a/src/Repository/CommandTaskScheduleRepository.php b/src/Repository/CommandTaskScheduleRepository.php new file mode 100644 index 0000000..1495bdd --- /dev/null +++ b/src/Repository/CommandTaskScheduleRepository.php @@ -0,0 +1,18 @@ + + */ +class CommandTaskScheduleRepository extends AbstractRepository +{ + public function __construct(ManagerRegistry $registry) + { + parent::__construct($registry, CommandTaskSchedule::class); + } +} diff --git a/src/Repository/CommandTaskScriptRepository.php b/src/Repository/CommandTaskScriptRepository.php new file mode 100644 index 0000000..ce498b8 --- /dev/null +++ b/src/Repository/CommandTaskScriptRepository.php @@ -0,0 +1,18 @@ + + */ +class CommandTaskScriptRepository extends AbstractRepository +{ + public function __construct(ManagerRegistry $registry) + { + parent::__construct($registry, CommandTaskScript::class); + } +} diff --git a/src/Repository/GitImageRepositoryRepository.php b/src/Repository/GitImageRepositoryRepository.php new file mode 100644 index 0000000..d9104c7 --- /dev/null +++ b/src/Repository/GitImageRepositoryRepository.php @@ -0,0 +1,33 @@ + + */ +class GitImageRepositoryRepository extends AbstractRepository +{ + public function __construct(ManagerRegistry $registry) + { + parent::__construct($registry, GitImageRepository::class); + } + + public function findLatestVersionByImageAndRepository(Image $image, Repository $repository): ?GitImageRepository + { + return $this->createQueryBuilder('i') + ->andWhere('i.image = :imageId') + ->setParameter('imageId', $image->getId()) + ->andWhere('i.repository = :repository') + ->setParameter('repository', $repository->getId()) + ->orderBy('i.version', 'DESC') + ->setMaxResults(1) + ->getQuery() + ->getOneOrNullResult(); + } +} diff --git a/src/Service/CreateTraceService.php b/src/Service/CreateTraceService.php index cb747d1..5d26221 100644 --- a/src/Service/CreateTraceService.php +++ b/src/Service/CreateTraceService.php @@ -21,8 +21,9 @@ readonly class CreateTraceService foreach ($commandTask->getClients() as $client) { $trace = new Trace(); $trace->setClient($client); - $trace->setCommand($command); + $trace->setCommand($command->getName()); $trace->setStatus(TraceStatus::PENDING); + $trace->setInput($commandTask->getParameters()); $trace->setExecutedAt($commandTask->getDatetime()); $this->entityManager->persist($trace); } @@ -33,7 +34,8 @@ readonly class CreateTraceService foreach ($commandTask->getClients() as $client) { $trace = new Trace(); $trace->setClient($client); - $trace->setCommand($command); + $trace->setCommand($command->getName()); + $trace->setInput($commandTask->getParameters()); $trace->setStatus(TraceStatus::PENDING); $trace->setExecutedAt($commandTask->getDatetime()); $this->entityManager->persist($trace); diff --git a/src/Service/UDS/UDSClient.php b/src/Service/UDS/UDSClient.php index a8e79c4..fe6dbad 100644 --- a/src/Service/UDS/UDSClient.php +++ b/src/Service/UDS/UDSClient.php @@ -23,16 +23,16 @@ class UDSClient private string $scrambler; public function __construct( - private HttpClientInterface $httpClient, - private readonly EntityManagerInterface $entityManager, + private HttpClientInterface $httpClient, + private readonly EntityManagerInterface $entityManager, #[Autowire(env: 'UDS_URL')] - private string $udsAPIurl, + private readonly string $udsAPIurl, #[Autowire(env: 'UDS_AUTH_LOGIN')] - private string $udsAuthLogin, + private readonly string $udsAuthLogin, #[Autowire(env: 'UDS_AUTH_USERNAME')] - private string $udsAuthUsername, + private readonly string $udsAuthUsername, #[Autowire(env: 'UDS_AUTH_PASSWORD')] - private string $udsAuthPassword, + private readonly string $udsAuthPassword, ) { } diff --git a/src/State/Processor/CommandTaskProcessor.php b/src/State/Processor/CommandTaskProcessor.php index fe39109..affab05 100644 --- a/src/State/Processor/CommandTaskProcessor.php +++ b/src/State/Processor/CommandTaskProcessor.php @@ -18,8 +18,7 @@ readonly class CommandTaskProcessor implements ProcessorInterface { public function __construct( private CommandTaskRepository $commandTaskRepository, - private ValidatorInterface $validator, - private CreateTraceService $createTraceService + private ValidatorInterface $validator ) { } @@ -56,7 +55,6 @@ readonly class CommandTaskProcessor implements ProcessorInterface $task = $data->createOrUpdateEntity($entity); $this->validator->validate($task); $this->commandTaskRepository->save($task); - $this->createTraceService->__invoke($task); return new CommandTaskOutput($task); } diff --git a/src/State/Processor/CommandTaskScheduleProcessor.php b/src/State/Processor/CommandTaskScheduleProcessor.php index 0817270..2a26017 100644 --- a/src/State/Processor/CommandTaskScheduleProcessor.php +++ b/src/State/Processor/CommandTaskScheduleProcessor.php @@ -2,7 +2,69 @@ namespace App\State\Processor; -class CommandTaskScheduleProcessor +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\CommandTaskScheduleInput; +use App\Dto\Output\CommandTaskScheduleOutput; +use App\Repository\CommandTaskScheduleRepository; +use App\Service\CreateTraceService; + +readonly class CommandTaskScheduleProcessor implements ProcessorInterface { + public function __construct( + private CommandTaskScheduleRepository $commandTaskScheduleRepository, + private ValidatorInterface $validator, + ) + { + } + + /** + * @throws \Exception + */ + public function process(mixed $data, Operation $operation, array $uriVariables = [], array $context = []): CommandTaskScheduleOutput |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 = []): CommandTaskScheduleOutput + { + if (!($data instanceof CommandTaskScheduleInput)) { + throw new \Exception(sprintf('data is not instance of %s', CommandTaskScheduleInput::class)); + } + + $entity = null; + if (isset($uriVariables['uuid'])) { + $entity = $this->commandTaskScheduleRepository->findOneByUuid($uriVariables['uuid']); + } + + $task = $data->createOrUpdateEntity($entity); + $this->validator->validate($task); + $this->commandTaskScheduleRepository->save($task); + + return new CommandTaskScheduleOutput($task); + } + + private function processDelete($data, Operation $operation, array $uriVariables = [], array $context = []): null + { + $user = $this->commandTaskScheduleRepository->findOneByUuid($uriVariables['uuid']); + $this->commandTaskScheduleRepository->delete($user); + + return null; + } } \ No newline at end of file diff --git a/src/State/Processor/CommandTaskScriptProcessor.php b/src/State/Processor/CommandTaskScriptProcessor.php new file mode 100644 index 0000000..7a16f7b --- /dev/null +++ b/src/State/Processor/CommandTaskScriptProcessor.php @@ -0,0 +1,70 @@ +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 = []): CommandTaskScriptOutput + { + if (!($data instanceof CommandTaskScriptInput)) { + throw new \Exception(sprintf('data is not instance of %s', CommandTaskScriptInput::class)); + } + + $entity = null; + if (isset($uriVariables['uuid'])) { + $entity = $this->commandTaskScriptRepository->findOneByUuid($uriVariables['uuid']); + } + + $task = $data->createOrUpdateEntity($entity); + $this->validator->validate($task); + $this->commandTaskScriptRepository->save($task); + + return new CommandTaskScriptOutput($task); + } + + private function processDelete($data, Operation $operation, array $uriVariables = [], array $context = []): null + { + $user = $this->commandTaskScriptRepository->findOneByUuid($uriVariables['uuid']); + $this->commandTaskScriptRepository->delete($user); + + return null; + } +} \ No newline at end of file diff --git a/src/State/Processor/GitImageRepositoryProcessor.php b/src/State/Processor/GitImageRepositoryProcessor.php new file mode 100644 index 0000000..095838e --- /dev/null +++ b/src/State/Processor/GitImageRepositoryProcessor.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 = []): GitImageRepositoryOutput + { + if (!($data instanceof GitImageRepositoryInput)) { + throw new \Exception(sprintf('data is not instance of %s', GitImageRepositoryInput::class)); + } + + $entity = null; + if (isset($uriVariables['uuid'])) { + $entity = $this->imageRepository->findOneByUuid($uriVariables['uuid']); + } + + $image = $data->createOrUpdateEntity($entity); + $this->validator->validate($image); + $this->imageRepository->save($image); + + return new GitImageRepositoryOutput($image); + } + + private function processDelete($data, Operation $operation, array $uriVariables = [], array $context = []): null + { + $user = $this->imageRepository->findOneByUuid($uriVariables['uuid']); + $this->imageRepository->delete($user); + + return null; + } +} diff --git a/src/State/Processor/ImageProcessor.php b/src/State/Processor/ImageProcessor.php index 5d72a40..dbc5302 100644 --- a/src/State/Processor/ImageProcessor.php +++ b/src/State/Processor/ImageProcessor.php @@ -65,9 +65,7 @@ readonly class ImageProcessor implements ProcessorInterface if ($data->selectedImage){ - - $response = $this->createImageActionController->__invoke($data->selectedImage->getEntity(), $data->partition->getEntity()); - + $response = $this->createImageActionController->__invoke($data->selectedImage->getEntity(), $data->partition->getEntity(), $data->client->getEntity()); } else { $image = $data->createOrUpdateEntity($entity); diff --git a/src/State/Provider/CommandTaskScheduleProvider.php b/src/State/Provider/CommandTaskScheduleProvider.php index c626f87..0b37eaf 100644 --- a/src/State/Provider/CommandTaskScheduleProvider.php +++ b/src/State/Provider/CommandTaskScheduleProvider.php @@ -2,7 +2,70 @@ namespace App\State\Provider; -class CommandTaskScheduleProvider -{ +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\CommandTaskScheduleInput; +use App\Dto\Output\CommandTaskScheduleOutput; +use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; +readonly class CommandTaskScheduleProvider 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|array|null + { + $paginator = $this->collectionProvider->provide($operation, $uriVariables, $context); + + $items = new \ArrayObject(); + foreach ($paginator->getIterator() as $item){ + $items[] = new CommandTaskScheduleOutput($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 CommandTaskScheduleOutput($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 CommandTaskScheduleInput($item) : null; + } + + return new CommandTaskScheduleInput(); + } } \ No newline at end of file diff --git a/src/State/Provider/CommandTaskScriptProvider.php b/src/State/Provider/CommandTaskScriptProvider.php new file mode 100644 index 0000000..92b0d41 --- /dev/null +++ b/src/State/Provider/CommandTaskScriptProvider.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 CommandTaskScriptOutput($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 CommandTaskScriptOutput($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 CommandTaskScriptInput($item) : null; + } + + return new CommandTaskScriptInput(); + } +} diff --git a/src/State/Provider/GitImageRepositoryProvider.php b/src/State/Provider/GitImageRepositoryProvider.php new file mode 100644 index 0000000..a13103b --- /dev/null +++ b/src/State/Provider/GitImageRepositoryProvider.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 GitImageRepositoryOutput($item, $context); + } + + 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('ImageImageRepository not found'); + } + + return new GitImageRepositoryOutput($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 GitImageRepositoryInput($item) : null; + } + + return new GitImageRepositoryInput(); + } +} diff --git a/src/Validator/Constraints/RemoteCalendarRuleUnique.php b/src/Validator/Constraints/RemoteCalendarRuleUnique.php index d44eda2..ef87639 100644 --- a/src/Validator/Constraints/RemoteCalendarRuleUnique.php +++ b/src/Validator/Constraints/RemoteCalendarRuleUnique.php @@ -2,7 +2,22 @@ namespace App\Validator\Constraints; -class RemoteCalendarRuleUnique -{ +use Symfony\Component\Validator\Constraint; +#[\Attribute] +class RemoteCalendarRuleUnique extends Constraint +{ + public string $message; + + public function __construct(mixed $options = null, ?array $groups = null, mixed $payload = null) + { + parent::__construct($options, $groups, $payload); + + $this->message = 'Ya hay una regla de calendario marcada como no disponible o reservada.'; + } + + public function getTargets(): string + { + return self::CLASS_CONSTRAINT; + } } \ No newline at end of file diff --git a/src/Validator/Constraints/RemoteCalendarRuleUniqueValidator.php b/src/Validator/Constraints/RemoteCalendarRuleUniqueValidator.php index 6e08231..8597b18 100644 --- a/src/Validator/Constraints/RemoteCalendarRuleUniqueValidator.php +++ b/src/Validator/Constraints/RemoteCalendarRuleUniqueValidator.php @@ -2,7 +2,47 @@ namespace App\Validator\Constraints; -class RemoteCalendarRuleUniqueValidator -{ +use App\Dto\Input\RemoteCalendarRuleInput; +use App\Entity\RemoteCalendarRule; +use Doctrine\ORM\EntityManagerInterface; +use Symfony\Component\HttpFoundation\RequestStack; +use Symfony\Component\Validator\Constraint; +use Symfony\Component\Validator\ConstraintValidator; +class RemoteCalendarRuleUniqueValidator extends ConstraintValidator +{ + public function __construct( + private readonly EntityManagerInterface $entityManager, + private readonly RequestStack $requestStack + ) + { + } + + public function validate(mixed $value, Constraint $constraint): void + { + $request = $this->requestStack->getCurrentRequest(); + + if (!$value instanceof RemoteCalendarRuleInput) { + return; + } + + if ($value->isRemoteAvailable) { + return; + } + + if ($request && $request->getMethod() !== 'POST') { + return; + } + + $isRemoteAvailable = $this->entityManager->getRepository(RemoteCalendarRule::class) + ->findOneBy([ + 'remoteCalendar' => $value->remoteCalendar->getEntity(), + 'isRemoteAvailable' => false, + ]); + + if ($isRemoteAvailable) { + $this->context->buildViolation($constraint->message)->addViolation(); + } + + } } \ No newline at end of file diff --git a/tests/Functional/CommandGroupTest.php b/tests/Functional/CommandGroupTest.php deleted file mode 100644 index de5faef..0000000 --- a/tests/Functional/CommandGroupTest.php +++ /dev/null @@ -1,127 +0,0 @@ - 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 index 49b9555..5653ec2 100644 --- a/tests/Functional/CommandTaskTest.php +++ b/tests/Functional/CommandTaskTest.php @@ -4,9 +4,12 @@ namespace Functional; use App\Entity\Command; use App\Entity\CommandTask; +use App\Entity\OrganizationalUnit; use App\Factory\CommandFactory; use App\Factory\CommandTaskFactory; +use App\Factory\OrganizationalUnitFactory; use App\Factory\UserFactory; +use App\Model\OrganizationalUnitTypes; use App\Model\UserGroupPermissions; use Symfony\Component\HttpFoundation\Response; use Symfony\Contracts\HttpClient\Exception\ClientExceptionInterface; @@ -59,17 +62,13 @@ class CommandTaskTest extends AbstractTest { 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(); + OrganizationalUnitFactory::createOne(['type' => OrganizationalUnitTypes::ORGANIZATIONAL_UNIT]); + $ouIri = $this->findIriBy(OrganizationalUnit::class, ['type' => OrganizationalUnitTypes::ORGANIZATIONAL_UNIT]); $this->createClientWithCredentials()->request('POST', '/command-tasks',['json' => [ - 'dateTime' => $date->format('Y-m-d H:i:s'), - 'notes' => self::CMD_TASK_CREATE, - 'commands' => [ - $commandIri - ] + 'name' => self::CMD_TASK_CREATE, + 'organizationalUnit' => $ouIri, + 'scope' => 'organizational-unit', ]]); $this->assertResponseStatusCodeSame(201); @@ -77,7 +76,8 @@ class CommandTaskTest extends AbstractTest $this->assertJsonContains([ '@context' => '/contexts/CommandTaskOutput', '@type' => 'CommandTask', - 'notes' => self::CMD_TASK_CREATE, + 'name' => self::CMD_TASK_CREATE, + 'scope' => 'organizational-unit', ]); } @@ -92,17 +92,23 @@ class CommandTaskTest extends AbstractTest { 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]); + CommandTaskFactory::createOne(['name' => self::CMD_TASK_CREATE]); + $iri = $this->findIriBy(CommandTask::class, ['name' => self::CMD_TASK_CREATE]); + + OrganizationalUnitFactory::createOne(['type' => OrganizationalUnitTypes::ORGANIZATIONAL_UNIT]); + $ouIri = $this->findIriBy(OrganizationalUnit::class, ['type' => OrganizationalUnitTypes::ORGANIZATIONAL_UNIT]); $this->createClientWithCredentials()->request('PUT', $iri, ['json' => [ - 'notes' => self::CMD_TASK_UPDATE, + 'name' => self::CMD_TASK_UPDATE, + 'organizationalUnit' => $ouIri, + 'scope' => 'organizational-unit', ]]); $this->assertResponseIsSuccessful(); $this->assertJsonContains([ '@id' => $iri, - 'notes' => self::CMD_TASK_UPDATE + 'name' => self::CMD_TASK_UPDATE, + 'scope' => 'organizational-unit', ]); } @@ -117,13 +123,15 @@ class CommandTaskTest extends AbstractTest { 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]); + $ou = OrganizationalUnitFactory::createOne(['type' => OrganizationalUnitTypes::ORGANIZATIONAL_UNIT]); + + CommandTaskFactory::createOne(['name' => self::CMD_TASK_DELETE, 'organizationalUnit' => $ou, 'scope' => 'organizational-unit']); + $iri = $this->findIriBy(CommandTask::class, ['name' => 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]) + static::getContainer()->get('doctrine')->getRepository(CommandTask::class)->findOneBy(['name' => self::CMD_TASK_DELETE]) ); } } \ No newline at end of file