From b8555575940c1cfd790c7add21f4c5cea91a2390 Mon Sep 17 00:00:00 2001 From: Manuel Aranda Date: Fri, 9 Aug 2024 08:18:51 +0200 Subject: [PATCH 001/157] refs #632. Create table ogLive --- config/api_platform/OgLive.yaml | 0 migrations/Version20240808140716.php | 31 ++++++++++++++ src/Dto/Input/OgLiveInput.php | 8 ++++ src/Dto/Output/OgLiveOutput.php | 8 ++++ src/Entity/OgLive.php | 30 +++++++++++++ src/Factory/OgLiveFactory.php | 56 +++++++++++++++++++++++++ src/Repository/OgLiveRepository.php | 18 ++++++++ src/State/Processor/OgLiveProcessor.php | 8 ++++ src/State/Provider/OgLiveProvider.php | 8 ++++ tests/Functional/OgLiveTest.php | 8 ++++ 10 files changed, 175 insertions(+) create mode 100644 config/api_platform/OgLive.yaml create mode 100644 migrations/Version20240808140716.php create mode 100644 src/Dto/Input/OgLiveInput.php create mode 100644 src/Dto/Output/OgLiveOutput.php create mode 100644 src/Entity/OgLive.php create mode 100644 src/Factory/OgLiveFactory.php create mode 100644 src/Repository/OgLiveRepository.php create mode 100644 src/State/Processor/OgLiveProcessor.php create mode 100644 src/State/Provider/OgLiveProvider.php create mode 100644 tests/Functional/OgLiveTest.php diff --git a/config/api_platform/OgLive.yaml b/config/api_platform/OgLive.yaml new file mode 100644 index 0000000..e69de29 diff --git a/migrations/Version20240808140716.php b/migrations/Version20240808140716.php new file mode 100644 index 0000000..b61c4df --- /dev/null +++ b/migrations/Version20240808140716.php @@ -0,0 +1,31 @@ +addSql('CREATE TABLE og_live (id INT AUTO_INCREMENT NOT NULL, uuid CHAR(36) NOT NULL COMMENT \'(DC2Type:uuid)\', migration_id VARCHAR(255) DEFAULT NULL, created_at DATETIME NOT NULL, updated_at DATETIME NOT NULL, created_by VARCHAR(255) DEFAULT NULL, updated_by VARCHAR(255) DEFAULT NULL, download_url VARCHAR(255) DEFAULT NULL, name VARCHAR(255) NOT NULL, UNIQUE INDEX UNIQ_3D6B7739D17F50A6 (uuid), UNIQUE INDEX UNIQ_IDENTIFIER_NAME (name), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB'); + } + + public function down(Schema $schema): void + { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql('DROP TABLE og_live'); + } +} diff --git a/src/Dto/Input/OgLiveInput.php b/src/Dto/Input/OgLiveInput.php new file mode 100644 index 0000000..4f8d788 --- /dev/null +++ b/src/Dto/Input/OgLiveInput.php @@ -0,0 +1,8 @@ +downloadUrl; + } + + public function setDownloadUrl(?string $downloadUrl): static + { + $this->downloadUrl = $downloadUrl; + + return $this; + } +} diff --git a/src/Factory/OgLiveFactory.php b/src/Factory/OgLiveFactory.php new file mode 100644 index 0000000..cb90dcd --- /dev/null +++ b/src/Factory/OgLiveFactory.php @@ -0,0 +1,56 @@ + + */ +final class OgLiveFactory extends ModelFactory +{ + /** + * @see https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#factories-as-services + * + * @todo inject services if required + */ + public function __construct() + { + parent::__construct(); + } + + /** + * @see https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#model-factories + * + * @todo add your default values here + */ + protected function getDefaults(): array + { + return [ + 'createdAt' => self::faker()->dateTime(), + 'name' => self::faker()->text(255), + 'downloadUrl' => self::faker()->text(255), + 'updatedAt' => self::faker()->dateTime(), + ]; + } + + /** + * @see https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#initialization + */ + protected function initialize(): self + { + return $this + // ->afterInstantiate(function(OgLive $ogLive): void {}) + ; + } + + protected static function getClass(): string + { + return OgLive::class; + } +} diff --git a/src/Repository/OgLiveRepository.php b/src/Repository/OgLiveRepository.php new file mode 100644 index 0000000..63e83cc --- /dev/null +++ b/src/Repository/OgLiveRepository.php @@ -0,0 +1,18 @@ + + */ +class OgLiveRepository extends AbstractRepository +{ + public function __construct(ManagerRegistry $registry) + { + parent::__construct($registry, OgLive::class); + } +} diff --git a/src/State/Processor/OgLiveProcessor.php b/src/State/Processor/OgLiveProcessor.php new file mode 100644 index 0000000..dff06bf --- /dev/null +++ b/src/State/Processor/OgLiveProcessor.php @@ -0,0 +1,8 @@ + Date: Fri, 9 Aug 2024 08:19:27 +0200 Subject: [PATCH 002/157] refs #633. CRUD-API ogLive --- config/api_platform/OgLive.yaml | 31 ++++++ config/services.yaml | 5 + src/Dto/Input/OgLiveInput.php | 38 +++++++- src/Dto/Output/OgLiveOutput.php | 30 +++++- src/State/Processor/OgLiveProcessor.php | 66 ++++++++++++- src/State/Provider/OgLiveProvider.php | 69 +++++++++++++- tests/Functional/OgLiveTest.php | 120 +++++++++++++++++++++++- translations/validators.en.yaml | 7 +- 8 files changed, 353 insertions(+), 13 deletions(-) diff --git a/config/api_platform/OgLive.yaml b/config/api_platform/OgLive.yaml index e69de29..5a52f02 100644 --- a/config/api_platform/OgLive.yaml +++ b/config/api_platform/OgLive.yaml @@ -0,0 +1,31 @@ +resources: + App\Entity\OgLive: + processor: App\State\Processor\OgLiveProcessor + input: App\Dto\Input\OgLiveInput + output: App\Dto\Output\OgLiveOutput + normalizationContext: + groups: ['default', 'og-live:read'] + denormalizationContext: + groups: ['og-live:write'] + operations: + ApiPlatform\Metadata\GetCollection: + provider: App\State\Provider\OgLiveProvider + filters: + - 'api_platform.filter.og-live.order' + - 'api_platform.filter.og-live.search' + + ApiPlatform\Metadata\Get: + provider: App\State\Provider\OgLiveProvider + ApiPlatform\Metadata\Put: + provider: App\State\Provider\OgLiveProvider + ApiPlatform\Metadata\Patch: + provider: App\State\Provider\OgLiveProvider + ApiPlatform\Metadata\Post: ~ + ApiPlatform\Metadata\Delete: ~ + +properties: + App\Entity\OgLive: + id: + identifier: false + uuid: + identifier: true \ No newline at end of file diff --git a/config/services.yaml b/config/services.yaml index beee276..c3267cb 100644 --- a/config/services.yaml +++ b/config/services.yaml @@ -101,3 +101,8 @@ services: bind: $collectionProvider: '@api_platform.doctrine.orm.state.collection_provider' $itemProvider: '@api_platform.doctrine.orm.state.item_provider' + + App\State\Provider\OgLiveProvider: + bind: + $collectionProvider: '@api_platform.doctrine.orm.state.collection_provider' + $itemProvider: '@api_platform.doctrine.orm.state.item_provider' diff --git a/src/Dto/Input/OgLiveInput.php b/src/Dto/Input/OgLiveInput.php index 4f8d788..d9e86df 100644 --- a/src/Dto/Input/OgLiveInput.php +++ b/src/Dto/Input/OgLiveInput.php @@ -2,7 +2,41 @@ namespace App\Dto\Input; -class OgLiveInput -{ +use ApiPlatform\Metadata\ApiProperty; +use App\Entity\OgLive; +use Symfony\Component\Serializer\Annotation\Groups; +use Symfony\Component\Validator\Constraints as Assert; +final class OgLiveInput +{ + #[Assert\NotBlank(message: 'validators.hardware.name.not_blank')] + #[Groups(['og-live:write'])] + #[ApiProperty(description: 'The name of the ogLive', example: "OgLive 1")] + public ?string $name = null; + + #[Groups(['og-live:write'])] + #[ApiProperty(description: 'The download url of the ogLive', example: "http://example.com/oglive1.iso")] + public ?string $downloadUrl = null; + + public function __construct(?OgLive $ogLive = null) + { + if (!$ogLive) { + return; + } + + $this->name = $ogLive->getName(); + $this->downloadUrl = $ogLive->getDownloadUrl(); + } + + public function createOrUpdateEntity(?OgLive $ogLive = null): OgLive + { + if (!$ogLive) { + $ogLive = new OgLive(); + } + + $ogLive->setName($this->name); + $ogLive->setDownloadUrl($this->downloadUrl); + + return $ogLive; + } } \ No newline at end of file diff --git a/src/Dto/Output/OgLiveOutput.php b/src/Dto/Output/OgLiveOutput.php index 7f55a7a..1b15405 100644 --- a/src/Dto/Output/OgLiveOutput.php +++ b/src/Dto/Output/OgLiveOutput.php @@ -2,7 +2,33 @@ namespace App\Dto\Output; -class OgLiveOutput -{ +use ApiPlatform\Metadata\ApiProperty; +use ApiPlatform\Metadata\Get; +use App\Entity\OgLive; +use Symfony\Component\Serializer\Annotation\Groups; +#[Get(shortName: 'OgLive')] +final class OgLiveOutput extends AbstractOutput +{ + #[Groups(['og-live:read'])] + public string $name; + + #[Groups(['og-live:read'])] + public ?string $downloadUrl = ''; + + #[Groups(['og-live:read'])] + public \DateTime $createdAt; + + #[Groups(['og-live:read'])] + public ?string $createdBy = null; + + public function __construct(OgLive $ogLive) + { + parent::__construct($ogLive); + + $this->name = $ogLive->getName(); + $this->downloadUrl = $ogLive->getDownloadUrl(); + $this->createdAt = $ogLive->getCreatedAt(); + $this->createdBy = $ogLive->getCreatedBy(); + } } \ No newline at end of file diff --git a/src/State/Processor/OgLiveProcessor.php b/src/State/Processor/OgLiveProcessor.php index dff06bf..0cc9bc3 100644 --- a/src/State/Processor/OgLiveProcessor.php +++ b/src/State/Processor/OgLiveProcessor.php @@ -2,7 +2,67 @@ namespace App\State\Processor; -class OgLiveProcessor -{ +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\OgLiveInput; +use App\Dto\Output\OgLiveOutput; +use App\Repository\OgLiveRepository; -} \ No newline at end of file +readonly class OgLiveProcessor implements ProcessorInterface +{ + public function __construct( + private OgLiveRepository $ogLiveRepository, + private ValidatorInterface $validator + ) + { + } + + /** + * @throws \Exception + */ + public function process(mixed $data, Operation $operation, array $uriVariables = [], array $context = []): OgLiveOutput|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 = []): OgLiveOutput + { + if (!($data instanceof OgLiveInput)) { + throw new \Exception(sprintf('data is not instance of %s', OgLiveInput::class)); + } + + $entity = null; + if (isset($uriVariables['uuid'])) { + $entity = $this->ogLiveRepository->findOneByUuid($uriVariables['uuid']); + } + + $ogLive = $data->createOrUpdateEntity($entity); + $this->validator->validate($ogLive); + $this->ogLiveRepository->save($ogLive); + + return new OgLiveOutput($ogLive); + } + + private function processDelete($data, Operation $operation, array $uriVariables = [], array $context = []): null + { + $ogLive = $this->ogLiveRepository->findOneByUuid($uriVariables['uuid']); + $this->ogLiveRepository->delete($ogLive); + + return null; + } +} diff --git a/src/State/Provider/OgLiveProvider.php b/src/State/Provider/OgLiveProvider.php index 23f51e0..cf1bcc5 100644 --- a/src/State/Provider/OgLiveProvider.php +++ b/src/State/Provider/OgLiveProvider.php @@ -2,7 +2,70 @@ namespace App\State\Provider; -class OgLiveProvider -{ +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\OgLiveInput; +use App\Dto\Output\OgLiveOutput; +use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; -} \ No newline at end of file +readonly class OgLiveProvider implements ProviderInterface +{ + public function __construct( + private ProviderInterface $collectionProvider, + private ProviderInterface $itemProvider + ) + { + } + + public function provide(Operation $operation, array $uriVariables = [], array $context = []): object|array|null + { + switch ($operation){ + case $operation instanceof GetCollection: + return $this->provideCollection($operation, $uriVariables, $context); + case $operation instanceof Patch: + case $operation instanceof Put: + return $this->provideInput($operation, $uriVariables, $context); + case $operation instanceof Get: + return $this->provideItem($operation, $uriVariables, $context); + } + } + + private function provideCollection(Operation $operation, array $uriVariables = [], array $context = []): object + { + $paginator = $this->collectionProvider->provide($operation, $uriVariables, $context); + + $items = new \ArrayObject(); + foreach ($paginator->getIterator() as $item){ + $items[] = new OgLiveOutput($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('OgLive not found'); + } + + return new OgLiveOutput($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 OgLiveInput($item) : null; + } + + return new OgLiveInput(); + } +} diff --git a/tests/Functional/OgLiveTest.php b/tests/Functional/OgLiveTest.php index e3dc1d5..bb6c58a 100644 --- a/tests/Functional/OgLiveTest.php +++ b/tests/Functional/OgLiveTest.php @@ -2,7 +2,123 @@ namespace Functional; -class OgLiveTest -{ +use App\Entity\Client; +use App\Entity\HardwareProfile; +use App\Entity\OgLive; +use App\Entity\User; +use App\Factory\HardwareProfileFactory; +use App\Factory\OgLiveFactory; +use App\Factory\UserFactory; +use App\Model\UserGroupPermissions; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Contracts\HttpClient\Exception\ClientExceptionInterface; +use Symfony\Contracts\HttpClient\Exception\DecodingExceptionInterface; +use Symfony\Contracts\HttpClient\Exception\RedirectionExceptionInterface; +use Symfony\Contracts\HttpClient\Exception\ServerExceptionInterface; +use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface; +class OgLiveTest extends AbstractTest +{ + CONST string USER_ADMIN = 'ogadmin'; + CONST string OGLIVE_CREATE = 'test-oglive-create'; + CONST string OGLIVE_UPDATE = 'test-oglive-update'; + CONST string OGLIVE_DELETE = 'test-oglive-delete'; + + /** + * @throws RedirectionExceptionInterface + * @throws DecodingExceptionInterface + * @throws ClientExceptionInterface + * @throws TransportExceptionInterface + * @throws ServerExceptionInterface + */ + public function testGetCollectionOgLives(): void + { + UserFactory::createOne(['username' => self::USER_ADMIN, 'roles'=> [UserGroupPermissions::ROLE_SUPER_ADMIN]]); + + OgLiveFactory::createMany(10); + + $this->createClientWithCredentials()->request('GET', '/og-lives'); + $this->assertResponseStatusCodeSame(Response::HTTP_OK); + $this->assertResponseHeaderSame('content-type', 'application/ld+json; charset=utf-8'); + $this->assertJsonContains([ + '@context' => '/contexts/OgLive', + '@id' => '/og-lives', + '@type' => 'hydra:Collection', + 'hydra:totalItems' => 10, + ]); + } + + /** + * @throws RedirectionExceptionInterface + * @throws DecodingExceptionInterface + * @throws ClientExceptionInterface + * @throws TransportExceptionInterface + * @throws ServerExceptionInterface + */ + public function testCreateOgLive(): void + { + UserFactory::createOne(['username' => self::USER_ADMIN, 'roles'=> [UserGroupPermissions::ROLE_SUPER_ADMIN]]); + + $this->createClientWithCredentials()->request('POST', '/og-lives',['json' => [ + 'name' => self::OGLIVE_CREATE, + 'downloadUrl' => 'http://example.com' + ]]); + + $this->assertResponseStatusCodeSame(201); + $this->assertResponseHeaderSame('content-type', 'application/ld+json; charset=utf-8'); + $this->assertJsonContains([ + '@context' => '/contexts/OgLiveOutput', + '@type' => 'OgLive', + 'name' => self::OGLIVE_CREATE, + 'downloadUrl' => 'http://example.com' + ]); + } + + /** + * @throws RedirectionExceptionInterface + * @throws DecodingExceptionInterface + * @throws ClientExceptionInterface + * @throws TransportExceptionInterface + * @throws ServerExceptionInterface + */ + public function testUpdateOgLive(): void + { + UserFactory::createOne(['username' => self::USER_ADMIN, 'roles'=> [UserGroupPermissions::ROLE_SUPER_ADMIN]]); + + OgLiveFactory::createOne(['name' => self::OGLIVE_CREATE, 'downloadUrl' => 'http://example.com']); + $iri = $this->findIriBy(OgLive::class, ['name' => self::OGLIVE_CREATE]); + + $this->createClientWithCredentials()->request('PUT', $iri, ['json' => [ + 'name' => self::OGLIVE_UPDATE, + 'downloadUrl' => 'http://example-2.com', + ]]); + + $this->assertResponseIsSuccessful(); + $this->assertJsonContains([ + '@id' => $iri, + 'name' => self::OGLIVE_UPDATE, + 'downloadUrl' => 'http://example-2.com', + ]); + } + + /** + * @throws TransportExceptionInterface + * @throws ServerExceptionInterface + * @throws RedirectionExceptionInterface + * @throws DecodingExceptionInterface + * @throws ClientExceptionInterface + */ + public function testDeleteOgLive(): void + { + UserFactory::createOne(['username' => self::USER_ADMIN, 'roles'=> [UserGroupPermissions::ROLE_SUPER_ADMIN]]); + + OgLiveFactory::createOne(['name' => self::OGLIVE_CREATE, 'downloadUrl' => 'http://example.com']); + $iri = $this->findIriBy(OgLive::class, ['name' => self::OGLIVE_CREATE]); + + $this->createClientWithCredentials()->request('DELETE', $iri); + $this->assertResponseStatusCodeSame(204); + $this->assertNull( + static::getContainer()->get('doctrine')->getRepository(OgLive::class)->findOneBy(['name' => self::OGLIVE_CREATE]) + ); + } } \ No newline at end of file diff --git a/translations/validators.en.yaml b/translations/validators.en.yaml index e5b6e69..ad9b5aa 100644 --- a/translations/validators.en.yaml +++ b/translations/validators.en.yaml @@ -36,4 +36,9 @@ validators: operative_system: name: - not_blank: 'The name should not be blank.' \ No newline at end of file + not_blank: 'The name should not be blank.' + + og_live: + name: + not_blank: 'The name should not be blank.' + unique: 'The name should be unique.' -- 2.40.1 From 7889554b36bb03b8735ab9be7f05854dca885b9f Mon Sep 17 00:00:00 2001 From: Manuel Aranda Date: Fri, 9 Aug 2024 10:58:07 +0200 Subject: [PATCH 003/157] refs #601. Integration API ogLive --- config/api_platform/OgLive.yaml | 54 ++++++++++++++ src/Controller/OgBoot/OgBootController.php | 70 +++++++++++++++++++ src/Controller/OgBoot/OgLive/GetAction.php | 40 +++++++++++ .../OgBoot/OgLive/GetCollectionAction.php | 38 ++++++++++ .../OgBoot/OgLive/GetDefaultAction.php | 40 +++++++++++ .../OgBoot/OgLive/InstallAction.php | 40 +++++++++++ .../OgBoot/OgLive/SetDefaultAction.php | 40 +++++++++++ .../OgBoot/OgLive/UninstallAction.php | 40 +++++++++++ src/Service/OgBoot/StatusService.php | 8 +++ 9 files changed, 370 insertions(+) create mode 100644 src/Controller/OgBoot/OgBootController.php create mode 100644 src/Controller/OgBoot/OgLive/GetAction.php create mode 100644 src/Controller/OgBoot/OgLive/GetCollectionAction.php create mode 100644 src/Controller/OgBoot/OgLive/GetDefaultAction.php create mode 100644 src/Controller/OgBoot/OgLive/InstallAction.php create mode 100644 src/Controller/OgBoot/OgLive/SetDefaultAction.php create mode 100644 src/Controller/OgBoot/OgLive/UninstallAction.php create mode 100644 src/Service/OgBoot/StatusService.php diff --git a/config/api_platform/OgLive.yaml b/config/api_platform/OgLive.yaml index 5a52f02..3ea3e7b 100644 --- a/config/api_platform/OgLive.yaml +++ b/config/api_platform/OgLive.yaml @@ -23,6 +23,60 @@ resources: ApiPlatform\Metadata\Post: ~ ApiPlatform\Metadata\Delete: ~ + get_collection: + shortName: OgLive Server + description: Get collection of OgLive + class: ApiPlatform\Metadata\Get + method: GET + input: false + uriTemplate: /og-lives/server/get-collection + controller: App\Controller\OgBoot\OgLive\GetCollectionAction + + get: + shortName: OgLive Server + description: Get OgLive + class: ApiPlatform\Metadata\Get + method: GET + input: false + uriTemplate: /og-lives/server/{uuid}/get + controller: App\Controller\OgBoot\OgLive\GetAction + + get_default: + shortName: OgLive Server + description: Get default OgLive + class: ApiPlatform\Metadata\Get + method: GET + input: false + uriTemplate: /og-lives/server/get-default + controller: App\Controller\OgBoot\OgLive\GetDefaultAction + + set_default: + shortName: OgLive Server + description: Set default OgLive + class: ApiPlatform\Metadata\Post + method: POST + input: false + uriTemplate: /og-lives/server/{uuid}/set-default + controller: App\Controller\OgBoot\OgLive\SetDefaultAction + + install: + shortName: OgLive Server + description: Install OgLive + class: ApiPlatform\Metadata\Post + method: POST + input: false + uriTemplate: /og-lives/server/{uuid}/install + controller: App\Controller\OgBoot\OgLive\InstallAction + + uninstall: + shortName: OgLive Server + description: Uninstall OgLive + class: ApiPlatform\Metadata\Get + method: GET + input: false + uriTemplate: /og-lives/server/{uuid}/uninstall + controller: App\Controller\OgBoot\OgLive\UninstallAction + properties: App\Entity\OgLive: id: diff --git a/src/Controller/OgBoot/OgBootController.php b/src/Controller/OgBoot/OgBootController.php new file mode 100644 index 0000000..100ab39 --- /dev/null +++ b/src/Controller/OgBoot/OgBootController.php @@ -0,0 +1,70 @@ +request('GET', 'https://api.example.com/ogboot/v1/status', [ + 'headers' => [ + 'accept' => 'application/json', + ], + ]); + } catch (TransportExceptionInterface $e) { + return new JsonResponse( data: 'An error occurred', status: Response::HTTP_INTERNAL_SERVER_ERROR); + } + + $data = json_decode($response->getContent(), true); + + return new JsonResponse( data: $data, status: Response::HTTP_OK); + } + + + /** + * @throws TransportExceptionInterface + * @throws ServerExceptionInterface + * @throws RedirectionExceptionInterface + * @throws ClientExceptionInterface + */ + #[Route('/status', name: 'ogboot_config', methods: ['GET'])] + public function config(HttpClientInterface $httpClient): JsonResponse + { + try { + $response = $httpClient->request('GET', 'https://api.example.com/ogboot/v1/config', [ + 'headers' => [ + 'accept' => 'application/json', + ], + ]); + } catch (TransportExceptionInterface $e) { + return new JsonResponse( data: 'An error occurred', status: Response::HTTP_INTERNAL_SERVER_ERROR); + } + + $data = json_decode($response->getContent(), true); + + return new JsonResponse( data: $data, status: Response::HTTP_OK); + } +} diff --git a/src/Controller/OgBoot/OgLive/GetAction.php b/src/Controller/OgBoot/OgLive/GetAction.php new file mode 100644 index 0000000..f0fcce6 --- /dev/null +++ b/src/Controller/OgBoot/OgLive/GetAction.php @@ -0,0 +1,40 @@ +request('GET', 'https://api.example.com/ogboot/v1/oglives', [ + 'headers' => [ + 'accept' => 'application/json', + ], + ]); + } catch (TransportExceptionInterface $e) { + return new JsonResponse( data: 'An error occurred', status: Response::HTTP_INTERNAL_SERVER_ERROR); + } + + $data = json_decode($response->getContent(), true); + + return new JsonResponse( data: $data, status: Response::HTTP_OK); + } +} \ No newline at end of file diff --git a/src/Controller/OgBoot/OgLive/GetCollectionAction.php b/src/Controller/OgBoot/OgLive/GetCollectionAction.php new file mode 100644 index 0000000..cdcaca0 --- /dev/null +++ b/src/Controller/OgBoot/OgLive/GetCollectionAction.php @@ -0,0 +1,38 @@ +request('GET', 'https://api.example.com/ogboot/v1/oglives', [ + 'headers' => [ + 'accept' => 'application/json', + ], + ]); + } catch (TransportExceptionInterface $e) { + return new JsonResponse( data: 'An error occurred', status: Response::HTTP_INTERNAL_SERVER_ERROR); + } + + $data = json_decode($response->getContent(), true); + + return new JsonResponse( data: $data, status: Response::HTTP_OK); + } +} \ No newline at end of file diff --git a/src/Controller/OgBoot/OgLive/GetDefaultAction.php b/src/Controller/OgBoot/OgLive/GetDefaultAction.php new file mode 100644 index 0000000..34f86e5 --- /dev/null +++ b/src/Controller/OgBoot/OgLive/GetDefaultAction.php @@ -0,0 +1,40 @@ +request('GET', 'https://api.example.com/ogboot/v1/oglives/default', [ + 'headers' => [ + 'accept' => 'application/json', + ], + ]); + } catch (TransportExceptionInterface $e) { + return new JsonResponse( data: 'An error occurred', status: Response::HTTP_INTERNAL_SERVER_ERROR); + } + + $data = json_decode($response->getContent(), true); + + return new JsonResponse( data: $data, status: Response::HTTP_OK); + } +} \ No newline at end of file diff --git a/src/Controller/OgBoot/OgLive/InstallAction.php b/src/Controller/OgBoot/OgLive/InstallAction.php new file mode 100644 index 0000000..bea4132 --- /dev/null +++ b/src/Controller/OgBoot/OgLive/InstallAction.php @@ -0,0 +1,40 @@ +request('POST', 'https://api.example.com/ogboot/v1/oglives', [ + 'headers' => [ + 'accept' => 'application/json', + ], + ]); + } catch (TransportExceptionInterface $e) { + return new JsonResponse( data: 'An error occurred', status: Response::HTTP_INTERNAL_SERVER_ERROR); + } + + $data = json_decode($response->getContent(), true); + + return new JsonResponse( data: $data, status: Response::HTTP_OK); + } +} \ No newline at end of file diff --git a/src/Controller/OgBoot/OgLive/SetDefaultAction.php b/src/Controller/OgBoot/OgLive/SetDefaultAction.php new file mode 100644 index 0000000..373c8ec --- /dev/null +++ b/src/Controller/OgBoot/OgLive/SetDefaultAction.php @@ -0,0 +1,40 @@ +request('POST', 'https://api.example.com/ogboot/v1/oglives/default', [ + 'headers' => [ + 'accept' => 'application/json', + ], + ]); + } catch (TransportExceptionInterface $e) { + return new JsonResponse( data: 'An error occurred', status: Response::HTTP_INTERNAL_SERVER_ERROR); + } + + $data = json_decode($response->getContent(), true); + + return new JsonResponse( data: $data, status: Response::HTTP_OK); + } +} \ No newline at end of file diff --git a/src/Controller/OgBoot/OgLive/UninstallAction.php b/src/Controller/OgBoot/OgLive/UninstallAction.php new file mode 100644 index 0000000..2c2fde1 --- /dev/null +++ b/src/Controller/OgBoot/OgLive/UninstallAction.php @@ -0,0 +1,40 @@ +request('DELETE', 'https://api.example.com/ogboot/v1/oglives', [ + 'headers' => [ + 'accept' => 'application/json', + ], + ]); + } catch (TransportExceptionInterface $e) { + return new JsonResponse( data: 'An error occurred', status: Response::HTTP_INTERNAL_SERVER_ERROR); + } + + $data = json_decode($response->getContent(), true); + + return new JsonResponse( data: $data, status: Response::HTTP_OK); + } +} \ No newline at end of file diff --git a/src/Service/OgBoot/StatusService.php b/src/Service/OgBoot/StatusService.php new file mode 100644 index 0000000..d4621b3 --- /dev/null +++ b/src/Service/OgBoot/StatusService.php @@ -0,0 +1,8 @@ + Date: Mon, 12 Aug 2024 13:21:55 +0200 Subject: [PATCH 004/157] refs #637. create pxe template crud --- config/api_platform/PxeTemplate.yaml | 0 .../OgBoot/AbstractOgLiveController.php | 21 +++++++ .../OgBoot/OgLive/GetIsosAction.php | 8 +++ .../OgBoot/PxeTemplate/DeleteAction.php | 8 +++ .../OgBoot/PxeTemplate/GetAction.php | 8 +++ .../PxeTemplate/GetCollectionAction.php | 8 +++ .../OgBoot/PxeTemplate/PostAction.php | 8 +++ src/Dto/Input/PxeTemplateInput.php | 42 ++++++++++++++ src/Dto/Output/PxeTemplateOutput.php | 34 +++++++++++ src/Entity/PxeTemplate.php | 30 ++++++++++ src/Factory/PxeTemplateFactory.php | 56 +++++++++++++++++++ src/Repository/PxeBootFileRepository.php | 43 ++++++++++++++ src/Repository/PxeTemplateRepository.php | 18 ++++++ src/Service/OgBoot/ConfigService.php | 8 +++ src/State/Processor/PxeTemplateProcessor.php | 8 +++ src/State/Provider/PxeTemplateProvider.php | 8 +++ tests/Functional/PxeTemplateTest.php | 8 +++ 17 files changed, 316 insertions(+) create mode 100644 config/api_platform/PxeTemplate.yaml create mode 100644 src/Controller/OgBoot/AbstractOgLiveController.php create mode 100644 src/Controller/OgBoot/OgLive/GetIsosAction.php create mode 100644 src/Controller/OgBoot/PxeTemplate/DeleteAction.php create mode 100644 src/Controller/OgBoot/PxeTemplate/GetAction.php create mode 100644 src/Controller/OgBoot/PxeTemplate/GetCollectionAction.php create mode 100644 src/Controller/OgBoot/PxeTemplate/PostAction.php create mode 100644 src/Dto/Input/PxeTemplateInput.php create mode 100644 src/Dto/Output/PxeTemplateOutput.php create mode 100644 src/Entity/PxeTemplate.php create mode 100644 src/Factory/PxeTemplateFactory.php create mode 100644 src/Repository/PxeBootFileRepository.php create mode 100644 src/Repository/PxeTemplateRepository.php create mode 100644 src/Service/OgBoot/ConfigService.php create mode 100644 src/State/Processor/PxeTemplateProcessor.php create mode 100644 src/State/Provider/PxeTemplateProvider.php create mode 100644 tests/Functional/PxeTemplateTest.php diff --git a/config/api_platform/PxeTemplate.yaml b/config/api_platform/PxeTemplate.yaml new file mode 100644 index 0000000..e69de29 diff --git a/src/Controller/OgBoot/AbstractOgLiveController.php b/src/Controller/OgBoot/AbstractOgLiveController.php new file mode 100644 index 0000000..d5e8db5 --- /dev/null +++ b/src/Controller/OgBoot/AbstractOgLiveController.php @@ -0,0 +1,21 @@ +name = $pxeTemplate->getName(); + $this->templateContent = $pxeTemplate->getTemplateContent(); + } + + public function createOrUpdateEntity(?PxeTemplate $pxeTemplate = null): PxeTemplate + { + if (!$pxeTemplate) { + $pxeTemplate = new PxeTemplate(); + } + + $pxeTemplate->setName($this->name); + $pxeTemplate->setTemplateContent($this->templateContent); + + return $pxeTemplate; + } +} \ No newline at end of file diff --git a/src/Dto/Output/PxeTemplateOutput.php b/src/Dto/Output/PxeTemplateOutput.php new file mode 100644 index 0000000..245e007 --- /dev/null +++ b/src/Dto/Output/PxeTemplateOutput.php @@ -0,0 +1,34 @@ +name = $pxeTemplate->getName(); + $this->templateContent = $pxeTemplate->getTemplateContent(); + $this->createdAt = $pxeTemplate->getCreatedAt(); + $this->createdBy = $pxeTemplate->getCreatedBy(); + } +} \ No newline at end of file diff --git a/src/Entity/PxeTemplate.php b/src/Entity/PxeTemplate.php new file mode 100644 index 0000000..ef310ff --- /dev/null +++ b/src/Entity/PxeTemplate.php @@ -0,0 +1,30 @@ +templateContent; + } + + public function setTemplateContent(string $templateContent): static + { + $this->templateContent = $templateContent; + + return $this; + } +} diff --git a/src/Factory/PxeTemplateFactory.php b/src/Factory/PxeTemplateFactory.php new file mode 100644 index 0000000..1ae2601 --- /dev/null +++ b/src/Factory/PxeTemplateFactory.php @@ -0,0 +1,56 @@ + + */ +final class PxeTemplateFactory extends ModelFactory +{ + /** + * @see https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#factories-as-services + * + * @todo inject services if required + */ + public function __construct() + { + parent::__construct(); + } + + /** + * @see https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#model-factories + * + * @todo add your default values here + */ + protected function getDefaults(): array + { + return [ + 'createdAt' => self::faker()->dateTime(), + 'name' => self::faker()->text(255), + 'templateContent' => self::faker()->text(255), + 'updatedAt' => self::faker()->dateTime() + ]; + } + + /** + * @see https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#initialization + */ + protected function initialize(): self + { + return $this + // ->afterInstantiate(function(PxeTemplate $pxeTemplate): void {}) + ; + } + + protected static function getClass(): string + { + return PxeTemplate::class; + } +} diff --git a/src/Repository/PxeBootFileRepository.php b/src/Repository/PxeBootFileRepository.php new file mode 100644 index 0000000..f71c3c5 --- /dev/null +++ b/src/Repository/PxeBootFileRepository.php @@ -0,0 +1,43 @@ + + */ +class PxeBootFileRepository extends ServiceEntityRepository +{ + public function __construct(ManagerRegistry $registry) + { + parent::__construct($registry, PxeBootFile::class); + } + + // /** + // * @return PxeBootFile[] Returns an array of PxeBootFile objects + // */ + // public function findByExampleField($value): array + // { + // return $this->createQueryBuilder('p') + // ->andWhere('p.exampleField = :val') + // ->setParameter('val', $value) + // ->orderBy('p.id', 'ASC') + // ->setMaxResults(10) + // ->getQuery() + // ->getResult() + // ; + // } + + // public function findOneBySomeField($value): ?PxeBootFile + // { + // return $this->createQueryBuilder('p') + // ->andWhere('p.exampleField = :val') + // ->setParameter('val', $value) + // ->getQuery() + // ->getOneOrNullResult() + // ; + // } +} diff --git a/src/Repository/PxeTemplateRepository.php b/src/Repository/PxeTemplateRepository.php new file mode 100644 index 0000000..fb569b8 --- /dev/null +++ b/src/Repository/PxeTemplateRepository.php @@ -0,0 +1,18 @@ + + */ +class PxeTemplateRepository extends AbstractRepository +{ + public function __construct(ManagerRegistry $registry) + { + parent::__construct($registry, PxeTemplate::class); + } +} diff --git a/src/Service/OgBoot/ConfigService.php b/src/Service/OgBoot/ConfigService.php new file mode 100644 index 0000000..2da2d30 --- /dev/null +++ b/src/Service/OgBoot/ConfigService.php @@ -0,0 +1,8 @@ + Date: Mon, 12 Aug 2024 13:23:07 +0200 Subject: [PATCH 005/157] refs #638. Pxe databble --- migrations/Version20240812095940.php | 43 ++++++++++ tests/Functional/PxeTemplateTest.php | 118 ++++++++++++++++++++++++++- 2 files changed, 159 insertions(+), 2 deletions(-) create mode 100644 migrations/Version20240812095940.php diff --git a/migrations/Version20240812095940.php b/migrations/Version20240812095940.php new file mode 100644 index 0000000..448e24e --- /dev/null +++ b/migrations/Version20240812095940.php @@ -0,0 +1,43 @@ +addSql('CREATE TABLE pxe_boot_file (id INT AUTO_INCREMENT NOT NULL, template_id INT DEFAULT 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, UNIQUE INDEX UNIQ_7FD1F34BD17F50A6 (uuid), INDEX IDX_7FD1F34B5DA0FB8 (template_id), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB'); + $this->addSql('CREATE TABLE pxe_template (id INT AUTO_INCREMENT NOT NULL, uuid CHAR(36) NOT NULL COMMENT \'(DC2Type:uuid)\', migration_id VARCHAR(255) DEFAULT NULL, created_at DATETIME NOT NULL, updated_at DATETIME NOT NULL, created_by VARCHAR(255) DEFAULT NULL, updated_by VARCHAR(255) DEFAULT NULL, template_content VARCHAR(255) NOT NULL, name VARCHAR(255) NOT NULL, UNIQUE INDEX UNIQ_73197554D17F50A6 (uuid), UNIQUE INDEX UNIQ_IDENTIFIER_NAME (name), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB'); + $this->addSql('ALTER TABLE pxe_boot_file ADD CONSTRAINT FK_7FD1F34B5DA0FB8 FOREIGN KEY (template_id) REFERENCES pxe_template (id)'); + $this->addSql('ALTER TABLE client ADD pxe_boot_file_id INT DEFAULT NULL'); + $this->addSql('ALTER TABLE client ADD CONSTRAINT FK_C7440455D4CBF752 FOREIGN KEY (pxe_boot_file_id) REFERENCES pxe_boot_file (id)'); + $this->addSql('CREATE INDEX IDX_C7440455D4CBF752 ON client (pxe_boot_file_id)'); + $this->addSql('ALTER TABLE og_live ADD checksum VARCHAR(255) DEFAULT NULL, ADD distribution VARCHAR(255) DEFAULT NULL, ADD kernel VARCHAR(255) DEFAULT NULL, ADD architecture VARCHAR(255) DEFAULT NULL, ADD revision VARCHAR(255) DEFAULT NULL, ADD directory VARCHAR(255) DEFAULT NULL, ADD filename VARCHAR(255) DEFAULT NULL, ADD installed 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 client DROP FOREIGN KEY FK_C7440455D4CBF752'); + $this->addSql('ALTER TABLE pxe_boot_file DROP FOREIGN KEY FK_7FD1F34B5DA0FB8'); + $this->addSql('DROP TABLE pxe_boot_file'); + $this->addSql('DROP TABLE pxe_template'); + $this->addSql('ALTER TABLE og_live DROP checksum, DROP distribution, DROP kernel, DROP architecture, DROP revision, DROP directory, DROP filename, DROP installed'); + $this->addSql('DROP INDEX IDX_C7440455D4CBF752 ON client'); + $this->addSql('ALTER TABLE client DROP pxe_boot_file_id'); + } +} diff --git a/tests/Functional/PxeTemplateTest.php b/tests/Functional/PxeTemplateTest.php index 54d3919..d2aafdb 100644 --- a/tests/Functional/PxeTemplateTest.php +++ b/tests/Functional/PxeTemplateTest.php @@ -2,7 +2,121 @@ namespace Functional; -class PxeTemplateTest -{ +use App\Entity\Client; +use App\Entity\PxeTemplate; +use App\Entity\User; +use App\Factory\PxeTemplateFactory; +use App\Factory\UserFactory; +use App\Model\UserGroupPermissions; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Contracts\HttpClient\Exception\ClientExceptionInterface; +use Symfony\Contracts\HttpClient\Exception\DecodingExceptionInterface; +use Symfony\Contracts\HttpClient\Exception\RedirectionExceptionInterface; +use Symfony\Contracts\HttpClient\Exception\ServerExceptionInterface; +use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface; +class PxeTemplateTest extends AbstractTest +{ + CONST string USER_ADMIN = 'ogadmin'; + CONST string PXE_TEMPLATE_CREATE = 'test-pxetemplate-create'; + CONST string PXE_TEMPLATE_UPDATE = 'test-pxetemplate-update'; + CONST string PXE_TEMPLATE_DELETE = 'test-pxetemplate-delete'; + + /** + * @throws RedirectionExceptionInterface + * @throws DecodingExceptionInterface + * @throws ClientExceptionInterface + * @throws TransportExceptionInterface + * @throws ServerExceptionInterface + */ + public function testGetCollectionPxeTemplates(): void + { + UserFactory::createOne(['username' => self::USER_ADMIN, 'roles'=> [UserGroupPermissions::ROLE_SUPER_ADMIN]]); + + PxeTemplateFactory::createMany(10); + + $this->createClientWithCredentials()->request('GET', '/pxe-templates'); + $this->assertResponseStatusCodeSame(Response::HTTP_OK); + $this->assertResponseHeaderSame('content-type', 'application/ld+json; charset=utf-8'); + $this->assertJsonContains([ + '@context' => '/contexts/PxeTemplate', + '@id' => '/pxe-templates', + '@type' => 'hydra:Collection', + 'hydra:totalItems' => 10, + ]); + } + + /** + * @throws RedirectionExceptionInterface + * @throws DecodingExceptionInterface + * @throws ClientExceptionInterface + * @throws TransportExceptionInterface + * @throws ServerExceptionInterface + */ + public function testCreatePxeTemplate(): void + { + UserFactory::createOne(['username' => self::USER_ADMIN, 'roles'=> [UserGroupPermissions::ROLE_SUPER_ADMIN]]); + + $this->createClientWithCredentials()->request('POST', '/pxe-templates',['json' => [ + 'name' => self::PXE_TEMPLATE_CREATE, + 'templateContent' => 'content' + ]]); + + $this->assertResponseStatusCodeSame(201); + $this->assertResponseHeaderSame('content-type', 'application/ld+json; charset=utf-8'); + $this->assertJsonContains([ + '@context' => '/contexts/PxeTemplateOutput', + '@type' => 'PxeTemplate', + 'name' => self::PXE_TEMPLATE_CREATE, + 'templateContent' => 'content' + ]); + } + + /** + * @throws RedirectionExceptionInterface + * @throws DecodingExceptionInterface + * @throws ClientExceptionInterface + * @throws TransportExceptionInterface + * @throws ServerExceptionInterface + */ + public function testUpdatePxeTemplate(): void + { + UserFactory::createOne(['username' => self::USER_ADMIN, 'roles'=> [UserGroupPermissions::ROLE_SUPER_ADMIN]]); + + PxeTemplateFactory::createOne(['name' => self::PXE_TEMPLATE_CREATE, 'templateContent' => 'content']); + $iri = $this->findIriBy(PxeTemplate::class, ['name' => self::PXE_TEMPLATE_CREATE]); + + $this->createClientWithCredentials()->request('PUT', $iri, ['json' => [ + 'name' => self::PXE_TEMPLATE_UPDATE, + 'templateContent' => 'updated-content', + ]]); + + $this->assertResponseIsSuccessful(); + $this->assertJsonContains([ + '@id' => $iri, + 'name' => self::PXE_TEMPLATE_UPDATE, + 'templateContent' => 'updated-content', + ]); + } + + /** + * @throws TransportExceptionInterface + * @throws ServerExceptionInterface + * @throws RedirectionExceptionInterface + * @throws DecodingExceptionInterface + * @throws ClientExceptionInterface + */ + public function testDeletePxeTemplate(): void + { + UserFactory::createOne(['username' => self::USER_ADMIN, 'roles'=> [UserGroupPermissions::ROLE_SUPER_ADMIN]]); + + PxeTemplateFactory::createOne(['name' => self::PXE_TEMPLATE_DELETE, 'templateContent' => 'content']); + $iri = $this->findIriBy(PxeTemplate::class, ['name' => self::PXE_TEMPLATE_DELETE]); + + $this->createClientWithCredentials()->request('DELETE', $iri); + $this->assertResponseStatusCodeSame(204); + $this->assertNull( + static::getContainer()->get('doctrine')->getRepository(PxeTemplate::class)->findOneBy(['name' => self::PXE_TEMPLATE_DELETE]) + ); + } } \ No newline at end of file -- 2.40.1 From 524b833c0e6e826c3a8fbff0600479804282709a Mon Sep 17 00:00:00 2001 From: Manuel Aranda Date: Mon, 12 Aug 2024 13:23:30 +0200 Subject: [PATCH 006/157] refs #601. Integration API pxe-template --- .env | 2 + composer.json | 2 +- composer.lock | 344 +++++++++--------- config/api_platform/OgLive.yaml | 17 +- config/api_platform/PxeTemplate.yaml | 67 ++++ config/services.yaml | 10 +- docker/Dockerfile-php | 11 + .../OgBoot/AbstractOgLiveController.php | 5 +- src/Controller/OgBoot/OgBootController.php | 39 +- src/Controller/OgBoot/OgLive/GetAction.php | 9 +- .../OgBoot/OgLive/GetCollectionAction.php | 8 +- .../OgBoot/OgLive/GetDefaultAction.php | 6 +- .../OgBoot/OgLive/GetIsosAction.php | 68 +++- .../OgBoot/OgLive/InstallAction.php | 13 +- .../OgBoot/OgLive/SetDefaultAction.php | 12 +- .../OgBoot/OgLive/UninstallAction.php | 9 +- .../OgBoot/PxeTemplate/DeleteAction.php | 38 +- .../OgBoot/PxeTemplate/GetAction.php | 38 +- .../PxeTemplate/GetCollectionAction.php | 37 +- .../OgBoot/PxeTemplate/PostAction.php | 51 ++- src/Entity/Client.php | 15 + src/Entity/OgLive.php | 120 ++++++ src/Entity/PxeBootFile.php | 69 ++++ src/Service/OgBoot/ConfigService.php | 43 ++- src/Service/OgBoot/StatusService.php | 44 ++- src/State/Processor/PxeTemplateProcessor.php | 66 +++- src/State/Provider/PxeTemplateProvider.php | 69 +++- 27 files changed, 964 insertions(+), 248 deletions(-) create mode 100644 src/Entity/PxeBootFile.php diff --git a/.env b/.env index 2d1c753..001e100 100644 --- a/.env +++ b/.env @@ -40,3 +40,5 @@ JWT_SECRET_KEY=%kernel.project_dir%/config/jwt/private.pem JWT_PUBLIC_KEY=%kernel.project_dir%/config/jwt/public.pem JWT_PASSPHRASE=8b9154df37ffa91ef9186ce095324e39e50ff3b023bb1ed34383abd019ba4515 ###< lexik/jwt-authentication-bundle ### + +OGBOOT_API_URL=http://localhost:8085 diff --git a/composer.json b/composer.json index 580ef6b..7aa562a 100644 --- a/composer.json +++ b/composer.json @@ -25,6 +25,7 @@ "symfony/expression-language": "6.4.*", "symfony/flex": "^2", "symfony/framework-bundle": "6.4.*", + "symfony/http-client": "6.4.*", "symfony/property-access": "6.4.*", "symfony/property-info": "6.4.*", "symfony/runtime": "6.4.*", @@ -89,7 +90,6 @@ "phpunit/phpunit": "^9.5", "symfony/browser-kit": "6.4.*", "symfony/css-selector": "6.4.*", - "symfony/http-client": "6.4.*", "symfony/maker-bundle": "^1.59", "symfony/phpunit-bridge": "^7.0", "symfony/web-profiler-bundle": "^6.4", diff --git a/composer.lock b/composer.lock index e756ea9..05fab12 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "bf1165324e27bddd1a412f25e438fc4c", + "content-hash": "788f45c89f13c815d43700e8374bf655", "packages": [ { "name": "api-platform/core", @@ -4477,6 +4477,177 @@ ], "time": "2024-05-31T14:49:08+00:00" }, + { + "name": "symfony/http-client", + "version": "v6.4.10", + "source": { + "type": "git", + "url": "https://github.com/symfony/http-client.git", + "reference": "b5e498f763e0bf5eed8dcd946e50a3b3f71d4ded" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/http-client/zipball/b5e498f763e0bf5eed8dcd946e50a3b3f71d4ded", + "reference": "b5e498f763e0bf5eed8dcd946e50a3b3f71d4ded", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "psr/log": "^1|^2|^3", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/http-client-contracts": "^3.4.1", + "symfony/service-contracts": "^2.5|^3" + }, + "conflict": { + "php-http/discovery": "<1.15", + "symfony/http-foundation": "<6.3" + }, + "provide": { + "php-http/async-client-implementation": "*", + "php-http/client-implementation": "*", + "psr/http-client-implementation": "1.0", + "symfony/http-client-implementation": "3.0" + }, + "require-dev": { + "amphp/amp": "^2.5", + "amphp/http-client": "^4.2.1", + "amphp/http-tunnel": "^1.0", + "amphp/socket": "^1.1", + "guzzlehttp/promises": "^1.4|^2.0", + "nyholm/psr7": "^1.0", + "php-http/httplug": "^1.0|^2.0", + "psr/http-client": "^1.0", + "symfony/dependency-injection": "^5.4|^6.0|^7.0", + "symfony/http-kernel": "^5.4|^6.0|^7.0", + "symfony/messenger": "^5.4|^6.0|^7.0", + "symfony/process": "^5.4|^6.0|^7.0", + "symfony/stopwatch": "^5.4|^6.0|^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\HttpClient\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides powerful methods to fetch HTTP resources synchronously or asynchronously", + "homepage": "https://symfony.com", + "keywords": [ + "http" + ], + "support": { + "source": "https://github.com/symfony/http-client/tree/v6.4.10" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-07-15T09:26:24+00:00" + }, + { + "name": "symfony/http-client-contracts", + "version": "v3.5.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/http-client-contracts.git", + "reference": "20414d96f391677bf80078aa55baece78b82647d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/http-client-contracts/zipball/20414d96f391677bf80078aa55baece78b82647d", + "reference": "20414d96f391677bf80078aa55baece78b82647d", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.5-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Contracts\\HttpClient\\": "" + }, + "exclude-from-classmap": [ + "/Test/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Generic abstractions related to HTTP clients", + "homepage": "https://symfony.com", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], + "support": { + "source": "https://github.com/symfony/http-client-contracts/tree/v3.5.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-04-18T09:32:20+00:00" + }, { "name": "symfony/http-foundation", "version": "v6.4.8", @@ -9224,177 +9395,6 @@ ], "time": "2024-05-31T14:49:08+00:00" }, - { - "name": "symfony/http-client", - "version": "v6.4.8", - "source": { - "type": "git", - "url": "https://github.com/symfony/http-client.git", - "reference": "61faba993e620fc22d4f0ab3b6bcf8fbb0d44b05" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/http-client/zipball/61faba993e620fc22d4f0ab3b6bcf8fbb0d44b05", - "reference": "61faba993e620fc22d4f0ab3b6bcf8fbb0d44b05", - "shasum": "" - }, - "require": { - "php": ">=8.1", - "psr/log": "^1|^2|^3", - "symfony/deprecation-contracts": "^2.5|^3", - "symfony/http-client-contracts": "^3.4.1", - "symfony/service-contracts": "^2.5|^3" - }, - "conflict": { - "php-http/discovery": "<1.15", - "symfony/http-foundation": "<6.3" - }, - "provide": { - "php-http/async-client-implementation": "*", - "php-http/client-implementation": "*", - "psr/http-client-implementation": "1.0", - "symfony/http-client-implementation": "3.0" - }, - "require-dev": { - "amphp/amp": "^2.5", - "amphp/http-client": "^4.2.1", - "amphp/http-tunnel": "^1.0", - "amphp/socket": "^1.1", - "guzzlehttp/promises": "^1.4|^2.0", - "nyholm/psr7": "^1.0", - "php-http/httplug": "^1.0|^2.0", - "psr/http-client": "^1.0", - "symfony/dependency-injection": "^5.4|^6.0|^7.0", - "symfony/http-kernel": "^5.4|^6.0|^7.0", - "symfony/messenger": "^5.4|^6.0|^7.0", - "symfony/process": "^5.4|^6.0|^7.0", - "symfony/stopwatch": "^5.4|^6.0|^7.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\HttpClient\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Provides powerful methods to fetch HTTP resources synchronously or asynchronously", - "homepage": "https://symfony.com", - "keywords": [ - "http" - ], - "support": { - "source": "https://github.com/symfony/http-client/tree/v6.4.8" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2024-05-31T14:49:08+00:00" - }, - { - "name": "symfony/http-client-contracts", - "version": "v3.5.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/http-client-contracts.git", - "reference": "20414d96f391677bf80078aa55baece78b82647d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/http-client-contracts/zipball/20414d96f391677bf80078aa55baece78b82647d", - "reference": "20414d96f391677bf80078aa55baece78b82647d", - "shasum": "" - }, - "require": { - "php": ">=8.1" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "3.5-dev" - }, - "thanks": { - "name": "symfony/contracts", - "url": "https://github.com/symfony/contracts" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Contracts\\HttpClient\\": "" - }, - "exclude-from-classmap": [ - "/Test/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Generic abstractions related to HTTP clients", - "homepage": "https://symfony.com", - "keywords": [ - "abstractions", - "contracts", - "decoupling", - "interfaces", - "interoperability", - "standards" - ], - "support": { - "source": "https://github.com/symfony/http-client-contracts/tree/v3.5.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2024-04-18T09:32:20+00:00" - }, { "name": "symfony/maker-bundle", "version": "v1.60.0", diff --git a/config/api_platform/OgLive.yaml b/config/api_platform/OgLive.yaml index 3ea3e7b..0504100 100644 --- a/config/api_platform/OgLive.yaml +++ b/config/api_platform/OgLive.yaml @@ -11,8 +11,8 @@ resources: ApiPlatform\Metadata\GetCollection: provider: App\State\Provider\OgLiveProvider filters: - - 'api_platform.filter.og-live.order' - - 'api_platform.filter.og-live.search' + - 'api_platform.filter.og_live.order' + - 'api_platform.filter.og_live.search' ApiPlatform\Metadata\Get: provider: App\State\Provider\OgLiveProvider @@ -26,7 +26,7 @@ resources: get_collection: shortName: OgLive Server description: Get collection of OgLive - class: ApiPlatform\Metadata\Get + class: ApiPlatform\Metadata\GetCollection method: GET input: false uriTemplate: /og-lives/server/get-collection @@ -41,10 +41,19 @@ resources: uriTemplate: /og-lives/server/{uuid}/get controller: App\Controller\OgBoot\OgLive\GetAction + get_isos: + shortName: OgLive Server + description: Get Isos of OgLive + class: ApiPlatform\Metadata\GetCollection + method: GET + input: false + uriTemplate: /og-lives/server/get-isos + controller: App\Controller\OgBoot\OgLive\GetIsosAction + get_default: shortName: OgLive Server description: Get default OgLive - class: ApiPlatform\Metadata\Get + class: ApiPlatform\Metadata\GetCollection method: GET input: false uriTemplate: /og-lives/server/get-default diff --git a/config/api_platform/PxeTemplate.yaml b/config/api_platform/PxeTemplate.yaml index e69de29..0e36c96 100644 --- a/config/api_platform/PxeTemplate.yaml +++ b/config/api_platform/PxeTemplate.yaml @@ -0,0 +1,67 @@ +resources: + App\Entity\PxeTemplate: + processor: App\State\Processor\PxeTemplateProcessor + input: App\Dto\Input\PxeTemplateInput + output: App\Dto\Output\PxeTemplateOutput + normalizationContext: + groups: ['default', 'pxe-template:read'] + denormalizationContext: + groups: ['pxe-template:write'] + operations: + ApiPlatform\Metadata\GetCollection: + provider: App\State\Provider\PxeTemplateProvider + filters: + - 'api_platform.filter.pxe_template.order' + - 'api_platform.filter.pxe_template.search' + + ApiPlatform\Metadata\Get: + provider: App\State\Provider\PxeTemplateProvider + ApiPlatform\Metadata\Put: + provider: App\State\Provider\PxeTemplateProvider + ApiPlatform\Metadata\Patch: + provider: App\State\Provider\PxeTemplateProvider + ApiPlatform\Metadata\Post: ~ + ApiPlatform\Metadata\Delete: ~ + + get_collection: + shortName: PxeTemplate Server + description: Get collection of PxeTemplate + class: ApiPlatform\Metadata\GetCollection + method: GET + input: false + uriTemplate: /pxe-templates/server/get-collection + controller: App\Controller\OgBoot\PxeTemplate\GetCollectionAction + + get: + shortName: PxeTemplate Server + description: Get PxeTemplate + class: ApiPlatform\Metadata\Get + method: GET + input: false + uriTemplate: /pxe-templates/server/{uuid}/get + controller: App\Controller\OgBoot\PxeTemplate\GetAction + + post: + shortName: PxeTemplate Server + description: Create PxeTemplate + class: ApiPlatform\Metadata\Post + method: POST + input: false + uriTemplate: /pxe-templates/server/{uuid}/post + controller: App\Controller\OgBoot\PxeTemplate\PostAction + + delete: + shortName: PxeTemplate Server + description: Delete PxeTemplate + class: ApiPlatform\Metadata\Get + method: GET + input: false + uriTemplate: /pxe-templates/server/{uuid}/delete + controller: App\Controller\OgBoot\PxeTemplate\DeleteAction + +properties: + App\Entity\PxeTemplate: + id: + identifier: false + uuid: + identifier: true \ No newline at end of file diff --git a/config/services.yaml b/config/services.yaml index c3267cb..16b3247 100644 --- a/config/services.yaml +++ b/config/services.yaml @@ -4,11 +4,13 @@ imports: parameters: services: - # default configuration for services in *this* file _defaults: autowire: true # Automatically injects dependencies in your services. autoconfigure: true # Automatically registers your services as commands, event subscribers, etc. + bind: + $ogBootApiUrl: '%env(OGBOOT_API_URL)%' + App\: resource: '../src/' exclude: @@ -106,3 +108,9 @@ services: bind: $collectionProvider: '@api_platform.doctrine.orm.state.collection_provider' $itemProvider: '@api_platform.doctrine.orm.state.item_provider' + + App\State\Provider\PxeTemplateProvider: + bind: + $collectionProvider: '@api_platform.doctrine.orm.state.collection_provider' + $itemProvider: '@api_platform.doctrine.orm.state.item_provider' + diff --git a/docker/Dockerfile-php b/docker/Dockerfile-php index da6f17e..a4cdcb8 100644 --- a/docker/Dockerfile-php +++ b/docker/Dockerfile-php @@ -28,3 +28,14 @@ RUN apk del -f .build-deps COPY ./docker/xdebug.ini /usr/local/etc/php/conf.d/xdebug.ini +# Generate SSH keys +RUN ssh-keygen -t rsa -b 4096 -f /root/.ssh/id_rsa -N "" + +# Optionally, copy public key to a specific location +RUN cp /root/.ssh/id_rsa.pub /root/.ssh/authorized_keys + +# Expose any ports you may need +EXPOSE 9000 + +# Command to run the PHP-FPM server +CMD ["php-fpm"] diff --git a/src/Controller/OgBoot/AbstractOgLiveController.php b/src/Controller/OgBoot/AbstractOgLiveController.php index d5e8db5..89cbb0c 100644 --- a/src/Controller/OgBoot/AbstractOgLiveController.php +++ b/src/Controller/OgBoot/AbstractOgLiveController.php @@ -2,13 +2,12 @@ declare(strict_types=1); -namespace App\Controller\OgBoot\OgLive; +namespace App\Controller\OgBoot; use Doctrine\ORM\EntityManagerInterface; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; -use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\Attribute\AsController; -use Symfony\Component\Routing\Attribute\Route; + #[AsController] abstract class AbstractOgLiveController extends AbstractController { diff --git a/src/Controller/OgBoot/OgBootController.php b/src/Controller/OgBoot/OgBootController.php index 100ab39..f9e26f3 100644 --- a/src/Controller/OgBoot/OgBootController.php +++ b/src/Controller/OgBoot/OgBootController.php @@ -4,6 +4,8 @@ declare(strict_types=1); namespace App\Controller\OgBoot; +use App\Service\OgBoot\ConfigService; +use App\Service\OgBoot\StatusService; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Response; @@ -19,6 +21,13 @@ use Symfony\Contracts\HttpClient\HttpClientInterface; #[AsController] class OgBootController extends AbstractController { + public function __construct( + private readonly StatusService $ogbootStatusService, + private readonly ConfigService $ogbootConfigService, + ) + { + } + /** * @throws TransportExceptionInterface * @throws ServerExceptionInterface @@ -26,19 +35,9 @@ class OgBootController extends AbstractController * @throws ClientExceptionInterface */ #[Route('/status', name: 'ogboot_status', methods: ['GET'])] - public function status(HttpClientInterface $httpClient): JsonResponse + public function status(): JsonResponse { - try { - $response = $httpClient->request('GET', 'https://api.example.com/ogboot/v1/status', [ - 'headers' => [ - 'accept' => 'application/json', - ], - ]); - } catch (TransportExceptionInterface $e) { - return new JsonResponse( data: 'An error occurred', status: Response::HTTP_INTERNAL_SERVER_ERROR); - } - - $data = json_decode($response->getContent(), true); + $data = $this->ogbootStatusService->__invoke(); return new JsonResponse( data: $data, status: Response::HTTP_OK); } @@ -50,20 +49,10 @@ class OgBootController extends AbstractController * @throws RedirectionExceptionInterface * @throws ClientExceptionInterface */ - #[Route('/status', name: 'ogboot_config', methods: ['GET'])] - public function config(HttpClientInterface $httpClient): JsonResponse + #[Route('/config', name: 'ogboot_config', methods: ['GET'])] + public function config(): JsonResponse { - try { - $response = $httpClient->request('GET', 'https://api.example.com/ogboot/v1/config', [ - 'headers' => [ - 'accept' => 'application/json', - ], - ]); - } catch (TransportExceptionInterface $e) { - return new JsonResponse( data: 'An error occurred', status: Response::HTTP_INTERNAL_SERVER_ERROR); - } - - $data = json_decode($response->getContent(), true); + $data = $this->ogbootConfigService->__invoke(); return new JsonResponse( data: $data, status: Response::HTTP_OK); } diff --git a/src/Controller/OgBoot/OgLive/GetAction.php b/src/Controller/OgBoot/OgLive/GetAction.php index f0fcce6..c93d34b 100644 --- a/src/Controller/OgBoot/OgLive/GetAction.php +++ b/src/Controller/OgBoot/OgLive/GetAction.php @@ -2,7 +2,8 @@ namespace App\Controller\OgBoot\OgLive; -use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; +use App\Controller\OgBoot\AbstractOgLiveController; +use App\Entity\OgLive; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\Attribute\AsController; @@ -13,7 +14,7 @@ use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface; use Symfony\Contracts\HttpClient\HttpClientInterface; #[AsController] -class GetAction extends AbstractController +class GetAction extends AbstractOgLiveController { /** * @throws TransportExceptionInterface @@ -21,10 +22,10 @@ class GetAction extends AbstractController * @throws RedirectionExceptionInterface * @throws ClientExceptionInterface */ - public function __invoke(HttpClientInterface $httpClient): JsonResponse + public function __invoke(OgLive $data, HttpClientInterface $httpClient): JsonResponse { try { - $response = $httpClient->request('GET', 'https://api.example.com/ogboot/v1/oglives', [ + $response = $httpClient->request('GET', $this->ogBootApiUrl.'/ogboot/v1/oglives/'.$data->getChecksum(), [ 'headers' => [ 'accept' => 'application/json', ], diff --git a/src/Controller/OgBoot/OgLive/GetCollectionAction.php b/src/Controller/OgBoot/OgLive/GetCollectionAction.php index cdcaca0..236a7fc 100644 --- a/src/Controller/OgBoot/OgLive/GetCollectionAction.php +++ b/src/Controller/OgBoot/OgLive/GetCollectionAction.php @@ -2,16 +2,18 @@ namespace App\Controller\OgBoot\OgLive; -use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; +use App\Controller\OgBoot\AbstractOgLiveController; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpKernel\Attribute\AsController; use Symfony\Contracts\HttpClient\Exception\ClientExceptionInterface; use Symfony\Contracts\HttpClient\Exception\RedirectionExceptionInterface; use Symfony\Contracts\HttpClient\Exception\ServerExceptionInterface; use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface; use Symfony\Contracts\HttpClient\HttpClientInterface; -class GetCollectionAction extends AbstractController +#[AsController] +class GetCollectionAction extends AbstractOgLiveController { /** * @throws TransportExceptionInterface @@ -22,7 +24,7 @@ class GetCollectionAction extends AbstractController public function __invoke(HttpClientInterface $httpClient): JsonResponse { try { - $response = $httpClient->request('GET', 'https://api.example.com/ogboot/v1/oglives', [ + $response = $httpClient->request('GET', $this->ogBootApiUrl.'/ogboot/v1/oglives', [ 'headers' => [ 'accept' => 'application/json', ], diff --git a/src/Controller/OgBoot/OgLive/GetDefaultAction.php b/src/Controller/OgBoot/OgLive/GetDefaultAction.php index 34f86e5..9d0ff93 100644 --- a/src/Controller/OgBoot/OgLive/GetDefaultAction.php +++ b/src/Controller/OgBoot/OgLive/GetDefaultAction.php @@ -2,7 +2,7 @@ namespace App\Controller\OgBoot\OgLive; -use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; +use App\Controller\OgBoot\AbstractOgLiveController; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\Attribute\AsController; @@ -13,7 +13,7 @@ use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface; use Symfony\Contracts\HttpClient\HttpClientInterface; #[AsController] -class GetDefaultAction extends AbstractController +class GetDefaultAction extends AbstractOgLiveController { /** * @throws TransportExceptionInterface @@ -24,7 +24,7 @@ class GetDefaultAction extends AbstractController public function __invoke(HttpClientInterface $httpClient): JsonResponse { try { - $response = $httpClient->request('GET', 'https://api.example.com/ogboot/v1/oglives/default', [ + $response = $httpClient->request('GET', $this->ogBootApiUrl.'/ogboot/v1/oglives/default', [ 'headers' => [ 'accept' => 'application/json', ], diff --git a/src/Controller/OgBoot/OgLive/GetIsosAction.php b/src/Controller/OgBoot/OgLive/GetIsosAction.php index d9ca380..436f9d4 100644 --- a/src/Controller/OgBoot/OgLive/GetIsosAction.php +++ b/src/Controller/OgBoot/OgLive/GetIsosAction.php @@ -2,7 +2,71 @@ namespace App\Controller\OgBoot\OgLive; -class GetIsosAction -{ +use App\Controller\OgBoot\AbstractOgLiveController; +use App\Entity\OgLive; +use Symfony\Component\HttpFoundation\JsonResponse; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpKernel\Attribute\AsController; +use Symfony\Contracts\HttpClient\Exception\ClientExceptionInterface; +use Symfony\Contracts\HttpClient\Exception\RedirectionExceptionInterface; +use Symfony\Contracts\HttpClient\Exception\ServerExceptionInterface; +use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface; +use Symfony\Contracts\HttpClient\HttpClientInterface; +#[AsController] +class GetIsosAction extends AbstractOgLiveController +{ + /** + * @throws TransportExceptionInterface + * @throws ServerExceptionInterface + * @throws RedirectionExceptionInterface + * @throws ClientExceptionInterface + */ + public function __invoke(HttpClientInterface $httpClient): JsonResponse + { + $ogLivesInserted = 0; + + try { + $response = $httpClient->request('GET', $this->ogBootApiUrl.'/ogboot/v1/oglives/isos', [ + 'headers' => [ + 'accept' => 'application/json', + ], + ]); + } catch (TransportExceptionInterface $e) { + return new JsonResponse( data: 'An error occurred', status: Response::HTTP_INTERNAL_SERVER_ERROR); + } + + $data = json_decode($response->getContent(), true); + + if (!empty($data['downloads'])) { + $ogLivesInserted = $this->insertOglives($data); + } + + return new JsonResponse( data: [ 'data' => $data, 'ogLivesInserted' => $ogLivesInserted], status: Response::HTTP_OK); + } + + public function insertOglives(array $data): int + { + $count = 0; + + foreach ($data['downloads'] as $ogLive ) { + $ogLiveEntity = $this->entityManager->getRepository(OgLive::class)->findOneBy(['name' => $ogLive['filename']]); + + if ($ogLiveEntity) { + continue; + } + + $ogLiveEntity = new OgLive(); + $ogLiveEntity->setName($ogLive['filename']); + $ogLiveEntity->setInstalled($ogLive['installed']); + $ogLiveEntity->setFilename($ogLive['filename']); + + $this->entityManager->persist($ogLiveEntity); + $count++; + } + + $this->entityManager->flush(); + + return $count; + } } \ No newline at end of file diff --git a/src/Controller/OgBoot/OgLive/InstallAction.php b/src/Controller/OgBoot/OgLive/InstallAction.php index bea4132..485cb4d 100644 --- a/src/Controller/OgBoot/OgLive/InstallAction.php +++ b/src/Controller/OgBoot/OgLive/InstallAction.php @@ -2,7 +2,8 @@ namespace App\Controller\OgBoot\OgLive; -use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; +use App\Controller\OgBoot\AbstractOgLiveController; +use App\Entity\OgLive; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\Attribute\AsController; @@ -13,7 +14,7 @@ use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface; use Symfony\Contracts\HttpClient\HttpClientInterface; #[AsController] -class InstallAction extends AbstractController +class InstallAction extends AbstractOgLiveController { /** * @throws TransportExceptionInterface @@ -21,13 +22,17 @@ class InstallAction extends AbstractController * @throws RedirectionExceptionInterface * @throws ClientExceptionInterface */ - public function __invoke(HttpClientInterface $httpClient): JsonResponse + public function __invoke(OgLive $data, HttpClientInterface $httpClient): JsonResponse { try { - $response = $httpClient->request('POST', 'https://api.example.com/ogboot/v1/oglives', [ + $response = $httpClient->request('POST', $this->ogBootApiUrl.'/ogboot/v1/oglives/install', [ 'headers' => [ 'accept' => 'application/json', + 'Content-Type' => 'application/json', ], + 'json' => [ + 'isoname' => $data->getName() + ] ]); } catch (TransportExceptionInterface $e) { return new JsonResponse( data: 'An error occurred', status: Response::HTTP_INTERNAL_SERVER_ERROR); diff --git a/src/Controller/OgBoot/OgLive/SetDefaultAction.php b/src/Controller/OgBoot/OgLive/SetDefaultAction.php index 373c8ec..0f96d13 100644 --- a/src/Controller/OgBoot/OgLive/SetDefaultAction.php +++ b/src/Controller/OgBoot/OgLive/SetDefaultAction.php @@ -2,7 +2,8 @@ namespace App\Controller\OgBoot\OgLive; -use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; +use App\Controller\OgBoot\AbstractOgLiveController; +use App\Entity\OgLive; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\Attribute\AsController; @@ -13,7 +14,7 @@ use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface; use Symfony\Contracts\HttpClient\HttpClientInterface; #[AsController] -class SetDefaultAction extends AbstractController +class SetDefaultAction extends AbstractOgLiveController { /** * @throws TransportExceptionInterface @@ -21,13 +22,16 @@ class SetDefaultAction extends AbstractController * @throws RedirectionExceptionInterface * @throws ClientExceptionInterface */ - public function __invoke(HttpClientInterface $httpClient): JsonResponse + public function __invoke(OgLive $data, HttpClientInterface $httpClient): JsonResponse { try { - $response = $httpClient->request('POST', 'https://api.example.com/ogboot/v1/oglives/default', [ + $response = $httpClient->request('POST', $this->ogBootApiUrl.'/ogboot/v1/oglives/default', [ 'headers' => [ 'accept' => 'application/json', ], + 'json' => [ + 'checksum' => $data->getChecksum() + ] ]); } catch (TransportExceptionInterface $e) { return new JsonResponse( data: 'An error occurred', status: Response::HTTP_INTERNAL_SERVER_ERROR); diff --git a/src/Controller/OgBoot/OgLive/UninstallAction.php b/src/Controller/OgBoot/OgLive/UninstallAction.php index 2c2fde1..e2981ec 100644 --- a/src/Controller/OgBoot/OgLive/UninstallAction.php +++ b/src/Controller/OgBoot/OgLive/UninstallAction.php @@ -2,7 +2,8 @@ namespace App\Controller\OgBoot\OgLive; -use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; +use App\Controller\OgBoot\AbstractOgLiveController; +use App\Entity\OgLive; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\Attribute\AsController; @@ -13,7 +14,7 @@ use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface; use Symfony\Contracts\HttpClient\HttpClientInterface; #[AsController] -class UninstallAction extends AbstractController +class UninstallAction extends AbstractOgLiveController { /** * @throws TransportExceptionInterface @@ -21,10 +22,10 @@ class UninstallAction extends AbstractController * @throws RedirectionExceptionInterface * @throws ClientExceptionInterface */ - public function __invoke(HttpClientInterface $httpClient): JsonResponse + public function __invoke(OgLive $data, HttpClientInterface $httpClient): JsonResponse { try { - $response = $httpClient->request('DELETE', 'https://api.example.com/ogboot/v1/oglives', [ + $response = $httpClient->request('DELETE', $this->ogBootApiUrl.'/ogboot/v1/oglives/'.$data->getChecksum(), [ 'headers' => [ 'accept' => 'application/json', ], diff --git a/src/Controller/OgBoot/PxeTemplate/DeleteAction.php b/src/Controller/OgBoot/PxeTemplate/DeleteAction.php index 5a09574..4971708 100644 --- a/src/Controller/OgBoot/PxeTemplate/DeleteAction.php +++ b/src/Controller/OgBoot/PxeTemplate/DeleteAction.php @@ -2,7 +2,41 @@ namespace App\Controller\OgBoot\PxeTemplate; -class DeleteAction -{ +use App\Controller\OgBoot\AbstractOgLiveController; +use App\Entity\PxeTemplate; +use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; +use Symfony\Component\HttpFoundation\JsonResponse; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpKernel\Attribute\AsController; +use Symfony\Contracts\HttpClient\Exception\ClientExceptionInterface; +use Symfony\Contracts\HttpClient\Exception\RedirectionExceptionInterface; +use Symfony\Contracts\HttpClient\Exception\ServerExceptionInterface; +use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface; +use Symfony\Contracts\HttpClient\HttpClientInterface; +#[AsController] +class DeleteAction extends AbstractOgLiveController +{ + /** + * @throws TransportExceptionInterface + * @throws ServerExceptionInterface + * @throws RedirectionExceptionInterface + * @throws ClientExceptionInterface + */ + public function __invoke(PxeTemplate $data, HttpClientInterface $httpClient): JsonResponse + { + try { + $response = $httpClient->request('DELETE', $this->ogBootApiUrl.'/ogboot/v1/pxe-templates/'.$data->getName(), [ + 'headers' => [ + 'accept' => 'application/json', + ], + ]); + } catch (TransportExceptionInterface $e) { + return new JsonResponse( data: 'An error occurred', status: Response::HTTP_INTERNAL_SERVER_ERROR); + } + + $data = json_decode($response->getContent(), true); + + return new JsonResponse( data: $data, status: Response::HTTP_OK); + } } \ No newline at end of file diff --git a/src/Controller/OgBoot/PxeTemplate/GetAction.php b/src/Controller/OgBoot/PxeTemplate/GetAction.php index 9981c52..19e91a3 100644 --- a/src/Controller/OgBoot/PxeTemplate/GetAction.php +++ b/src/Controller/OgBoot/PxeTemplate/GetAction.php @@ -1,8 +1,42 @@ request('GET', $this->ogBootApiUrl.'/ogboot/v1/pxe-templates/'.$data->getName(), [ + 'headers' => [ + 'accept' => 'application/json', + ], + ]); + } catch (TransportExceptionInterface $e) { + return new JsonResponse( data: 'An error occurred', status: Response::HTTP_INTERNAL_SERVER_ERROR); + } + $data = json_decode($response->getContent(), true); + + return new JsonResponse( data: $data, status: Response::HTTP_OK); + } } \ No newline at end of file diff --git a/src/Controller/OgBoot/PxeTemplate/GetCollectionAction.php b/src/Controller/OgBoot/PxeTemplate/GetCollectionAction.php index 46f7b74..6693311 100644 --- a/src/Controller/OgBoot/PxeTemplate/GetCollectionAction.php +++ b/src/Controller/OgBoot/PxeTemplate/GetCollectionAction.php @@ -2,7 +2,40 @@ namespace App\Controller\OgBoot\PxeTemplate; -class GetCollectionAction -{ +use App\Controller\OgBoot\AbstractOgLiveController; +use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; +use Symfony\Component\HttpFoundation\JsonResponse; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpKernel\Attribute\AsController; +use Symfony\Contracts\HttpClient\Exception\ClientExceptionInterface; +use Symfony\Contracts\HttpClient\Exception\RedirectionExceptionInterface; +use Symfony\Contracts\HttpClient\Exception\ServerExceptionInterface; +use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface; +use Symfony\Contracts\HttpClient\HttpClientInterface; +#[AsController] +class GetCollectionAction extends AbstractOgLiveController +{ + /** + * @throws TransportExceptionInterface + * @throws ServerExceptionInterface + * @throws RedirectionExceptionInterface + * @throws ClientExceptionInterface + */ + public function __invoke(HttpClientInterface $httpClient): JsonResponse + { + try { + $response = $httpClient->request('GET', $this->ogBootApiUrl.'/ogboot/v1/pxe-templates', [ + 'headers' => [ + 'accept' => 'application/json', + ], + ]); + } catch (TransportExceptionInterface $e) { + return new JsonResponse( data: 'An error occurred', status: Response::HTTP_INTERNAL_SERVER_ERROR); + } + + $data = json_decode($response->getContent(), true); + + return new JsonResponse( data: $data, status: Response::HTTP_OK); + } } \ No newline at end of file diff --git a/src/Controller/OgBoot/PxeTemplate/PostAction.php b/src/Controller/OgBoot/PxeTemplate/PostAction.php index 75fc0e7..a2f996c 100644 --- a/src/Controller/OgBoot/PxeTemplate/PostAction.php +++ b/src/Controller/OgBoot/PxeTemplate/PostAction.php @@ -2,7 +2,54 @@ namespace App\Controller\OgBoot\PxeTemplate; -class PostAction -{ +use App\Controller\OgBoot\AbstractOgLiveController; +use App\Entity\PxeTemplate; +use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; +use Symfony\Component\HttpFoundation\JsonResponse; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpKernel\Attribute\AsController; +use Symfony\Contracts\HttpClient\Exception\ClientExceptionInterface; +use Symfony\Contracts\HttpClient\Exception\RedirectionExceptionInterface; +use Symfony\Contracts\HttpClient\Exception\ServerExceptionInterface; +use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface; +use Symfony\Contracts\HttpClient\HttpClientInterface; +#[AsController] +class PostAction extends AbstractOgLiveController +{ + /** + * @throws TransportExceptionInterface + * @throws ServerExceptionInterface + * @throws RedirectionExceptionInterface + * @throws ClientExceptionInterface + */ + public function __invoke(PxeTemplate $data, HttpClientInterface $httpClient): JsonResponse + { + try { + $response = $httpClient->request('POST', $this->ogBootApiUrl.'/ogboot/v1/pxe-templates', [ + 'headers' => [ + 'accept' => 'application/json', + 'Content-Type' => 'application/json', + ], + 'json' => [ + 'name_template' => $data->getName(), + 'content_template' => $data->getTemplateContent(), + ], + ]); + + $data = json_decode($response->getContent(), true); + return new JsonResponse($data, Response::HTTP_OK); + + } catch (ClientExceptionInterface $e) { + $errorResponse = $e->getResponse(); + $errorContent = $errorResponse ? $errorResponse->getContent(false) : 'Client error occurred'; + return new JsonResponse(['error' => $errorContent], Response::HTTP_BAD_REQUEST); + } catch (ServerExceptionInterface $e) { + $errorResponse = $e->getResponse(); + $errorContent = $errorResponse ? $errorResponse->getContent(false) : 'Server error occurred'; + return new JsonResponse(['error' => $errorContent], Response::HTTP_INTERNAL_SERVER_ERROR); + } catch (TransportExceptionInterface $e) { + return new JsonResponse(['error' => 'Transport error occurred'], Response::HTTP_INTERNAL_SERVER_ERROR); + } + } } \ No newline at end of file diff --git a/src/Entity/Client.php b/src/Entity/Client.php index 638ee48..80ef646 100644 --- a/src/Entity/Client.php +++ b/src/Entity/Client.php @@ -57,6 +57,9 @@ class Client extends AbstractEntity #[ORM\Column(nullable: true)] private ?array $position = ['x' => 0, 'y' => 0]; + #[ORM\ManyToOne(inversedBy: 'clients')] + private ?PxeBootFile $pxeBootFile = null; + public function __construct() { parent::__construct(); @@ -224,4 +227,16 @@ class Client extends AbstractEntity return $this; } + + public function getPxeBootFile(): ?PxeBootFile + { + return $this->pxeBootFile; + } + + public function setPxeBootFile(?PxeBootFile $pxeBootFile): static + { + $this->pxeBootFile = $pxeBootFile; + + return $this; + } } diff --git a/src/Entity/OgLive.php b/src/Entity/OgLive.php index d77b8fe..e9c6e82 100644 --- a/src/Entity/OgLive.php +++ b/src/Entity/OgLive.php @@ -16,6 +16,30 @@ class OgLive extends AbstractEntity #[ORM\Column(length: 255, nullable: true)] private ?string $downloadUrl = null; + #[ORM\Column(length: 255, nullable: true)] + private ?string $checksum = null; + + #[ORM\Column(length: 255, nullable: true)] + private ?string $distribution = null; + + #[ORM\Column(length: 255, nullable: true)] + private ?string $kernel = null; + + #[ORM\Column(length: 255, nullable: true)] + private ?string $architecture = null; + + #[ORM\Column(length: 255, nullable: true)] + private ?string $revision = null; + + #[ORM\Column(length: 255, nullable: true)] + private ?string $directory = null; + + #[ORM\Column(length: 255, nullable: true)] + private ?string $filename = null; + + #[ORM\Column(nullable: true)] + private ?bool $installed = null; + public function getDownloadUrl(): ?string { return $this->downloadUrl; @@ -27,4 +51,100 @@ class OgLive extends AbstractEntity return $this; } + + public function getChecksum(): ?string + { + return $this->checksum; + } + + public function setChecksum(?string $checksum): static + { + $this->checksum = $checksum; + + return $this; + } + + public function getDistribution(): ?string + { + return $this->distribution; + } + + public function setDistribution(?string $distribution): static + { + $this->distribution = $distribution; + + return $this; + } + + public function getKernel(): ?string + { + return $this->kernel; + } + + public function setKernel(?string $kernel): static + { + $this->kernel = $kernel; + + return $this; + } + + public function getArchitecture(): ?string + { + return $this->architecture; + } + + public function setArchitecture(?string $architecture): static + { + $this->architecture = $architecture; + + return $this; + } + + public function getRevision(): ?string + { + return $this->revision; + } + + public function setRevision(?string $revision): static + { + $this->revision = $revision; + + return $this; + } + + public function getDirectory(): ?string + { + return $this->directory; + } + + public function setDirectory(?string $directory): static + { + $this->directory = $directory; + + return $this; + } + + public function getFilename(): ?string + { + return $this->filename; + } + + public function setFilename(?string $filename): static + { + $this->filename = $filename; + + return $this; + } + + public function isInstalled(): ?bool + { + return $this->installed; + } + + public function setInstalled(?bool $installed): static + { + $this->installed = $installed; + + return $this; + } } diff --git a/src/Entity/PxeBootFile.php b/src/Entity/PxeBootFile.php new file mode 100644 index 0000000..7316351 --- /dev/null +++ b/src/Entity/PxeBootFile.php @@ -0,0 +1,69 @@ + + */ + #[ORM\OneToMany(mappedBy: 'pxeBootFile', targetEntity: Client::class)] + private Collection $clients; + + public function __construct() + { + parent::__construct(); + $this->clients = new ArrayCollection(); + } + + public function getTemplate(): ?PxeTemplate + { + return $this->template; + } + + public function setTemplate(?PxeTemplate $template): static + { + $this->template = $template; + + return $this; + } + + /** + * @return Collection + */ + public function getClients(): Collection + { + return $this->clients; + } + + public function addClient(Client $client): static + { + if (!$this->clients->contains($client)) { + $this->clients->add($client); + $client->setPxeBootFile($this); + } + + return $this; + } + + public function removeClient(Client $client): static + { + if ($this->clients->removeElement($client)) { + // set the owning side to null (unless already changed) + if ($client->getPxeBootFile() === $this) { + $client->setPxeBootFile(null); + } + } + + return $this; + } +} diff --git a/src/Service/OgBoot/ConfigService.php b/src/Service/OgBoot/ConfigService.php index 2da2d30..8b247b9 100644 --- a/src/Service/OgBoot/ConfigService.php +++ b/src/Service/OgBoot/ConfigService.php @@ -2,7 +2,46 @@ namespace App\Service\OgBoot; -class ConfigService -{ +use Symfony\Component\HttpClient\HttpClient; +use Symfony\Component\HttpFoundation\JsonResponse; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Contracts\HttpClient\Exception\ClientExceptionInterface; +use Symfony\Contracts\HttpClient\Exception\RedirectionExceptionInterface; +use Symfony\Contracts\HttpClient\Exception\ServerExceptionInterface; +use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface; +use Symfony\Contracts\HttpClient\HttpClientInterface; +readonly class ConfigService +{ + public function __construct( + private string $ogBootApiUrl + ) + { + } + + /** + * @throws TransportExceptionInterface + * @throws ServerExceptionInterface + * @throws RedirectionExceptionInterface + * @throws ClientExceptionInterface + */ + public function __invoke() + { + $httpClient = HttpClient::create([ + 'verify_peer' => false, // Ignorar la verificación del certificado SSL + 'verify_host' => false, // Ignorar la verificación del nombre del host + ]); + + try { + $response = $httpClient->request('GET', $this->ogBootApiUrl.'/ogboot/v1/config', [ + 'headers' => [ + 'accept' => 'application/json', + ], + ]); + } catch (TransportExceptionInterface $e) { + return new JsonResponse( data: 'An error occurred', status: Response::HTTP_INTERNAL_SERVER_ERROR); + } + + return json_decode($response->getContent(), true); + } } \ No newline at end of file diff --git a/src/Service/OgBoot/StatusService.php b/src/Service/OgBoot/StatusService.php index d4621b3..c935e28 100644 --- a/src/Service/OgBoot/StatusService.php +++ b/src/Service/OgBoot/StatusService.php @@ -2,7 +2,47 @@ namespace App\Service\OgBoot; -class StatusService -{ +use Symfony\Component\HttpFoundation\JsonResponse; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Contracts\HttpClient\Exception\ClientExceptionInterface; +use Symfony\Contracts\HttpClient\Exception\RedirectionExceptionInterface; +use Symfony\Contracts\HttpClient\Exception\ServerExceptionInterface; +use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface; +use Symfony\Contracts\HttpClient\HttpClientInterface; +use Symfony\Component\HttpClient\HttpClient; + +readonly class StatusService +{ + public function __construct( + private string $ogBootApiUrl + ) + { + } + + /** + * @throws TransportExceptionInterface + * @throws ServerExceptionInterface + * @throws RedirectionExceptionInterface + * @throws ClientExceptionInterface + */ + public function __invoke() + { + $httpClient = HttpClient::create([ + 'verify_peer' => false, // Ignorar la verificación del certificado SSL + 'verify_host' => false, // Ignorar la verificación del nombre del host + ]); + + try { + $response = $httpClient->request('GET', $this->ogBootApiUrl.'/ogboot/v1/status', [ + 'headers' => [ + 'accept' => 'application/json', + ], + ]); + } catch (TransportExceptionInterface $e) { + return new JsonResponse( data: 'An error occurred', status: Response::HTTP_INTERNAL_SERVER_ERROR); + } + + return json_decode($response->getContent(), true); + } } \ No newline at end of file diff --git a/src/State/Processor/PxeTemplateProcessor.php b/src/State/Processor/PxeTemplateProcessor.php index c2bc86a..637dc30 100644 --- a/src/State/Processor/PxeTemplateProcessor.php +++ b/src/State/Processor/PxeTemplateProcessor.php @@ -2,7 +2,67 @@ namespace App\State\Processor; -class PxeTemplateProcessor -{ +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\PxeTemplateInput; +use App\Dto\Output\PxeTemplateOutput; +use App\Repository\PxeTemplateRepository; -} \ No newline at end of file +readonly class PxeTemplateProcessor implements ProcessorInterface +{ + public function __construct( + private PxeTemplateRepository $pxeTemplateRepository, + private ValidatorInterface $validator + ) + { + } + + /** + * @throws \Exception + */ + public function process(mixed $data, Operation $operation, array $uriVariables = [], array $context = []): PxeTemplateOutput|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 = []): PxeTemplateOutput + { + if (!($data instanceof PxeTemplateInput)) { + throw new \Exception(sprintf('data is not instance of %s', PxeTemplateInput::class)); + } + + $entity = null; + if (isset($uriVariables['uuid'])) { + $entity = $this->pxeTemplateRepository->findOneByUuid($uriVariables['uuid']); + } + + $pxeTemplate = $data->createOrUpdateEntity($entity); + $this->validator->validate($pxeTemplate); + $this->pxeTemplateRepository->save($pxeTemplate); + + return new PxeTemplateOutput($pxeTemplate); + } + + private function processDelete($data, Operation $operation, array $uriVariables = [], array $context = []): null + { + $pxeTemplate = $this->pxeTemplateRepository->findOneByUuid($uriVariables['uuid']); + $this->pxeTemplateRepository->delete($pxeTemplate); + + return null; + } +} diff --git a/src/State/Provider/PxeTemplateProvider.php b/src/State/Provider/PxeTemplateProvider.php index 001daa9..8704364 100644 --- a/src/State/Provider/PxeTemplateProvider.php +++ b/src/State/Provider/PxeTemplateProvider.php @@ -2,7 +2,70 @@ namespace App\State\Provider; -class PxeTemplateProvider -{ +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\PxeTemplateInput; +use App\Dto\Output\PxeTemplateOutput; +use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; -} \ No newline at end of file +readonly class PxeTemplateProvider implements ProviderInterface +{ + public function __construct( + private ProviderInterface $collectionProvider, + private ProviderInterface $itemProvider + ) + { + } + + public function provide(Operation $operation, array $uriVariables = [], array $context = []): object|array|null + { + switch ($operation){ + case $operation instanceof GetCollection: + return $this->provideCollection($operation, $uriVariables, $context); + case $operation instanceof Patch: + case $operation instanceof Put: + return $this->provideInput($operation, $uriVariables, $context); + case $operation instanceof Get: + return $this->provideItem($operation, $uriVariables, $context); + } + } + + private function provideCollection(Operation $operation, array $uriVariables = [], array $context = []): object + { + $paginator = $this->collectionProvider->provide($operation, $uriVariables, $context); + + $items = new \ArrayObject(); + foreach ($paginator->getIterator() as $item){ + $items[] = new PxeTemplateOutput($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('PxeTemplate not found'); + } + + return new PxeTemplateOutput($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 PxeTemplateInput($item) : null; + } + + return new PxeTemplateInput(); + } +} -- 2.40.1 From aa2059b70c65cb5ed46a30b898092b7939d066a9 Mon Sep 17 00:00:00 2001 From: Manuel Aranda Date: Tue, 13 Aug 2024 09:05:54 +0200 Subject: [PATCH 007/157] refs #601. Integration API pxe-template --- migrations/Version20240812135824.php | 43 ++++++++++ .../OgBoot/PxeBootFile/GetAction.php | 43 ++++++++++ src/Dto/Input/ClientInput.php | 9 +- src/Entity/Client.php | 15 ++++ src/Entity/NetworkSettings.php | 15 ++++ src/Entity/OgRepository.php | 86 +++++++++++++++++++ src/Repository/OgRepositoryRepository.php | 18 ++++ 7 files changed, 228 insertions(+), 1 deletion(-) create mode 100644 migrations/Version20240812135824.php create mode 100644 src/Controller/OgBoot/PxeBootFile/GetAction.php create mode 100644 src/Entity/OgRepository.php create mode 100644 src/Repository/OgRepositoryRepository.php diff --git a/migrations/Version20240812135824.php b/migrations/Version20240812135824.php new file mode 100644 index 0000000..5bd9784 --- /dev/null +++ b/migrations/Version20240812135824.php @@ -0,0 +1,43 @@ +addSql('CREATE TABLE og_repository (id INT AUTO_INCREMENT NOT NULL, uuid CHAR(36) NOT NULL COMMENT \'(DC2Type:uuid)\', migration_id VARCHAR(255) DEFAULT NULL, created_at DATETIME NOT NULL, updated_at DATETIME NOT NULL, created_by VARCHAR(255) DEFAULT NULL, updated_by VARCHAR(255) DEFAULT NULL, ip_address VARCHAR(255) NOT NULL, description VARCHAR(255) DEFAULT NULL, name VARCHAR(255) NOT NULL, UNIQUE INDEX UNIQ_2E0FDA37D17F50A6 (uuid), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB'); + $this->addSql('ALTER TABLE client ADD repository_id INT DEFAULT NULL'); + $this->addSql('ALTER TABLE client ADD CONSTRAINT FK_C744045550C9D4F7 FOREIGN KEY (repository_id) REFERENCES og_repository (id)'); + $this->addSql('CREATE INDEX IDX_C744045550C9D4F7 ON client (repository_id)'); + $this->addSql('ALTER TABLE network_settings ADD repository_id INT DEFAULT NULL'); + $this->addSql('ALTER TABLE network_settings ADD CONSTRAINT FK_48869B5450C9D4F7 FOREIGN KEY (repository_id) REFERENCES og_repository (id)'); + $this->addSql('CREATE INDEX IDX_48869B5450C9D4F7 ON network_settings (repository_id)'); + } + + 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_C744045550C9D4F7'); + $this->addSql('ALTER TABLE network_settings DROP FOREIGN KEY FK_48869B5450C9D4F7'); + $this->addSql('DROP TABLE og_repository'); + $this->addSql('DROP INDEX IDX_48869B5450C9D4F7 ON network_settings'); + $this->addSql('ALTER TABLE network_settings DROP repository_id'); + $this->addSql('DROP INDEX IDX_C744045550C9D4F7 ON client'); + $this->addSql('ALTER TABLE client DROP repository_id'); + } +} diff --git a/src/Controller/OgBoot/PxeBootFile/GetAction.php b/src/Controller/OgBoot/PxeBootFile/GetAction.php new file mode 100644 index 0000000..766da38 --- /dev/null +++ b/src/Controller/OgBoot/PxeBootFile/GetAction.php @@ -0,0 +1,43 @@ +request('GET', $this->ogBootApiUrl.'/ogboot/v1/pxes/'.$data->getName(), [ + 'headers' => [ + 'accept' => 'application/json', + ], + ]); + } catch (TransportExceptionInterface $e) { + return new JsonResponse( data: 'An error occurred', status: Response::HTTP_INTERNAL_SERVER_ERROR); + } + + $data = json_decode($response->getContent(), true); + + return new JsonResponse( data: $data, status: Response::HTTP_OK); + } +} \ No newline at end of file diff --git a/src/Dto/Input/ClientInput.php b/src/Dto/Input/ClientInput.php index ec4a2c2..065e7a2 100644 --- a/src/Dto/Input/ClientInput.php +++ b/src/Dto/Input/ClientInput.php @@ -7,6 +7,7 @@ use App\Dto\Output\HardwareProfileOutput; use App\Dto\Output\MenuOutput; use App\Dto\Output\OrganizationalUnitOutput; use App\Entity\Client; +use App\Entity\OgRepository; use Symfony\Component\Serializer\Annotation\Groups; use Symfony\Component\Validator\Constraints as Assert; @@ -77,10 +78,16 @@ final class ClientInput #[Groups(['client:write'])] #[ApiProperty( - description: 'descriptions.client.validation' + description: 'La posición del cliente dentro del aula' )] public ?array $position = ['x' => 0, 'y' => 0]; + #[Groups(['client:write'])] + #[ApiProperty( + description: 'descriptions.client.validation' + )] + public ?OgRepository $repository = null; + public function __construct(?Client $client = null) { if (!$client) { diff --git a/src/Entity/Client.php b/src/Entity/Client.php index 80ef646..82f5cac 100644 --- a/src/Entity/Client.php +++ b/src/Entity/Client.php @@ -60,6 +60,9 @@ class Client extends AbstractEntity #[ORM\ManyToOne(inversedBy: 'clients')] private ?PxeBootFile $pxeBootFile = null; + #[ORM\ManyToOne(inversedBy: 'clients')] + private ?OgRepository $repository = null; + public function __construct() { parent::__construct(); @@ -239,4 +242,16 @@ class Client extends AbstractEntity return $this; } + + public function getRepository(): ?OgRepository + { + return $this->repository; + } + + public function setRepository(?OgRepository $repository): static + { + $this->repository = $repository; + + return $this; + } } diff --git a/src/Entity/NetworkSettings.php b/src/Entity/NetworkSettings.php index faf7002..de3b9c9 100644 --- a/src/Entity/NetworkSettings.php +++ b/src/Entity/NetworkSettings.php @@ -60,6 +60,9 @@ class NetworkSettings extends AbstractEntity #[ORM\Column(nullable: true)] private ?bool $validation = null; + #[ORM\ManyToOne] + private ?OgRepository $repository = null; + public function __construct() { parent::__construct(); @@ -268,4 +271,16 @@ class NetworkSettings extends AbstractEntity return $this; } + + public function getRepository(): ?OgRepository + { + return $this->repository; + } + + public function setRepository(?OgRepository $repository): static + { + $this->repository = $repository; + + return $this; + } } diff --git a/src/Entity/OgRepository.php b/src/Entity/OgRepository.php new file mode 100644 index 0000000..9aaaa74 --- /dev/null +++ b/src/Entity/OgRepository.php @@ -0,0 +1,86 @@ + + */ + #[ORM\OneToMany(mappedBy: 'repository', targetEntity: Client::class)] + private Collection $clients; + + public function __construct() + { + parent::__construct(); + $this->clients = new ArrayCollection(); + } + + public function getIpAddress(): ?string + { + return $this->ipAddress; + } + + public function setIpAddress(string $ipAddress): static + { + $this->ipAddress = $ipAddress; + + return $this; + } + + public function getDescription(): ?string + { + return $this->description; + } + + public function setDescription(?string $description): static + { + $this->description = $description; + + return $this; + } + + /** + * @return Collection + */ + public function getClients(): Collection + { + return $this->clients; + } + + public function addClient(Client $client): static + { + if (!$this->clients->contains($client)) { + $this->clients->add($client); + $client->setRepository($this); + } + + return $this; + } + + public function removeClient(Client $client): static + { + if ($this->clients->removeElement($client)) { + // set the owning side to null (unless already changed) + if ($client->getRepository() === $this) { + $client->setRepository(null); + } + } + + return $this; + } +} diff --git a/src/Repository/OgRepositoryRepository.php b/src/Repository/OgRepositoryRepository.php new file mode 100644 index 0000000..0db2b01 --- /dev/null +++ b/src/Repository/OgRepositoryRepository.php @@ -0,0 +1,18 @@ + + */ +class OgRepositoryRepository extends AbstractRepository +{ + public function __construct(ManagerRegistry $registry) + { + parent::__construct($registry, self::class); + } +} -- 2.40.1 From 3f5004dddd5442b998b3cf938bd172b11bf100b8 Mon Sep 17 00:00:00 2001 From: Manuel Aranda Date: Tue, 13 Aug 2024 11:52:04 +0200 Subject: [PATCH 008/157] refs #601. Integration API pxe-boot-file --- config/api_platform/PxeBootFile.yaml | 31 ++++++++ config/services.yaml | 4 ++ src/Dto/Input/PxeBootFileInput.php | 55 ++++++++++++++ src/Dto/Output/PxeBootFileOutput.php | 38 ++++++++++ src/Entity/PxeBootFile.php | 11 +++ src/Repository/PxeBootFileRepository.php | 27 +------ .../OgBoot/PxeBootFile/PostService.php | 70 ++++++++++++++++++ src/State/Processor/PxeBootFileProcessor.php | 72 +++++++++++++++++++ src/State/Provider/PxeBootFileProvider.php | 71 ++++++++++++++++++ 9 files changed, 353 insertions(+), 26 deletions(-) create mode 100644 config/api_platform/PxeBootFile.yaml create mode 100644 src/Dto/Input/PxeBootFileInput.php create mode 100644 src/Dto/Output/PxeBootFileOutput.php create mode 100644 src/Service/OgBoot/PxeBootFile/PostService.php create mode 100644 src/State/Processor/PxeBootFileProcessor.php create mode 100644 src/State/Provider/PxeBootFileProvider.php diff --git a/config/api_platform/PxeBootFile.yaml b/config/api_platform/PxeBootFile.yaml new file mode 100644 index 0000000..6c5886d --- /dev/null +++ b/config/api_platform/PxeBootFile.yaml @@ -0,0 +1,31 @@ +resources: + App\Entity\PxeBootFile: + processor: App\State\Processor\PxeBootFileProcessor + input: App\Dto\Input\PxeBootFileInput + output: App\Dto\Output\PxeBootFileOutput + normalizationContext: + groups: ['default', 'pxe-boot-file:read'] + denormalizationContext: + groups: ['pxe-boot-file:write'] + operations: + ApiPlatform\Metadata\GetCollection: + provider: App\State\Provider\PPxeBootFileProvider + filters: + - 'api_platform.filter.pxe_boot_file.order' + - 'api_platform.filter.pxe_boot_file.search' + + ApiPlatform\Metadata\Get: + provider: App\State\Provider\PxeTemplateProvider + ApiPlatform\Metadata\Put: + provider: App\State\Provider\PxeTemplateProvider + ApiPlatform\Metadata\Patch: + provider: App\State\Provider\PxeTemplateProvider + ApiPlatform\Metadata\Post: ~ + ApiPlatform\Metadata\Delete: ~ + +properties: + App\Entity\PxeBootFile: + id: + identifier: false + uuid: + identifier: true \ No newline at end of file diff --git a/config/services.yaml b/config/services.yaml index 16b3247..e03a116 100644 --- a/config/services.yaml +++ b/config/services.yaml @@ -114,3 +114,7 @@ services: $collectionProvider: '@api_platform.doctrine.orm.state.collection_provider' $itemProvider: '@api_platform.doctrine.orm.state.item_provider' + App\State\Provider\PxeBootFileProvider: + bind: + $collectionProvider: '@api_platform.doctrine.orm.state.collection_provider' + $itemProvider: '@api_platform.doctrine.orm.state.item_provider' diff --git a/src/Dto/Input/PxeBootFileInput.php b/src/Dto/Input/PxeBootFileInput.php new file mode 100644 index 0000000..936ba5f --- /dev/null +++ b/src/Dto/Input/PxeBootFileInput.php @@ -0,0 +1,55 @@ +template = $bootFile->getTemplate(); + if ($bootFile->getClients()) { + foreach ($bootFile->getClients() as $client) { + $this->clients[] = new ClientOutput($client); + } + } + } + + public function createOrUpdateEntity(?PxeBootFile $bootFile = null): PxeBootFile + { + if (!$bootFile) { + $bootFile = new PxeBootFile(); + } + + $bootFile->setTemplate($this->template); + + foreach ($this->clients as $client) { + $clientsToAdd[] = $client->getEntity(); + } + $bootFile->setClients( $clientsToAdd ?? [] ); + + return $bootFile; + } +} \ No newline at end of file diff --git a/src/Dto/Output/PxeBootFileOutput.php b/src/Dto/Output/PxeBootFileOutput.php new file mode 100644 index 0000000..9000eab --- /dev/null +++ b/src/Dto/Output/PxeBootFileOutput.php @@ -0,0 +1,38 @@ +template = new PxeTemplateOutput($bootFile->getTemplate()); + $this->clients = $bootFile->getClients()->map( + fn(Client $client) => new ClientOutput($client) + )->toArray(); + + $this->createdAt = $bootFile->getCreatedAt(); + $this->createdBy = $bootFile->getCreatedBy(); + } +} \ No newline at end of file diff --git a/src/Entity/PxeBootFile.php b/src/Entity/PxeBootFile.php index 7316351..1498caf 100644 --- a/src/Entity/PxeBootFile.php +++ b/src/Entity/PxeBootFile.php @@ -66,4 +66,15 @@ class PxeBootFile extends AbstractEntity return $this; } + + public function setClients(array $clients): static + { + $this->clients->clear(); + + foreach ($clients as $client){ + $this->addClient($client); + } + + return $this; + } } diff --git a/src/Repository/PxeBootFileRepository.php b/src/Repository/PxeBootFileRepository.php index f71c3c5..5733c80 100644 --- a/src/Repository/PxeBootFileRepository.php +++ b/src/Repository/PxeBootFileRepository.php @@ -9,35 +9,10 @@ use Doctrine\Persistence\ManagerRegistry; /** * @extends ServiceEntityRepository */ -class PxeBootFileRepository extends ServiceEntityRepository +class PxeBootFileRepository extends AbstractRepository { public function __construct(ManagerRegistry $registry) { parent::__construct($registry, PxeBootFile::class); } - - // /** - // * @return PxeBootFile[] Returns an array of PxeBootFile objects - // */ - // public function findByExampleField($value): array - // { - // return $this->createQueryBuilder('p') - // ->andWhere('p.exampleField = :val') - // ->setParameter('val', $value) - // ->orderBy('p.id', 'ASC') - // ->setMaxResults(10) - // ->getQuery() - // ->getResult() - // ; - // } - - // public function findOneBySomeField($value): ?PxeBootFile - // { - // return $this->createQueryBuilder('p') - // ->andWhere('p.exampleField = :val') - // ->setParameter('val', $value) - // ->getQuery() - // ->getOneOrNullResult() - // ; - // } } diff --git a/src/Service/OgBoot/PxeBootFile/PostService.php b/src/Service/OgBoot/PxeBootFile/PostService.php new file mode 100644 index 0000000..cc19f4d --- /dev/null +++ b/src/Service/OgBoot/PxeBootFile/PostService.php @@ -0,0 +1,70 @@ + false, // Ignorar la verificación del certificado SSL + 'verify_host' => false, // Ignorar la verificación del nombre del host + ]); + + foreach ($bootFile->getClients() as $client) { + $data = [ + 'template_name' => $bootFile->getTemplate()->getName(), + 'mac' => $client->getMac(), + 'ip' => $client->getIp(), + 'server_ip' => '', + 'router' => $client->getOrganizationalUnit()->getNetworkSettings()->getRouter(), + 'netmask' => $client->getOrganizationalUnit()->getNetworkSettings()->getNetmask(), + 'computer_name' => $client->getName(), + 'netiface' => $client->getNetiface(), + 'group' => $client->getOrganizationalUnit()->getName(), + 'ogrepo' => $client->getRepository() ? $client->getRepository()->getIpAddress() : $client->getOrganizationalUnit()->getNetworkSettings()->getRepository()->getIpAddress(), + 'oglive' => '', + 'ogdns' => $client->getOrganizationalUnit()->getNetworkSettings()->getDns(), + 'ogProxy' => $client->getOrganizationalUnit()->getNetworkSettings()->getProxy(), + 'ogunit' => '' + ]; + + try { + $response = $httpClient->request('POST', $this->ogBootApiUrl.'/ogboot/v1/pxes', [ + 'headers' => [ + 'accept' => 'application/json', + ], + 'json' => $data + ]); + } catch (TransportExceptionInterface $e) { + return new JsonResponse( data: 'An error occurred', status: Response::HTTP_INTERNAL_SERVER_ERROR); + } + + return json_decode($response->getContent(), true); + } + + return 1; + } +} \ No newline at end of file diff --git a/src/State/Processor/PxeBootFileProcessor.php b/src/State/Processor/PxeBootFileProcessor.php new file mode 100644 index 0000000..9b03645 --- /dev/null +++ b/src/State/Processor/PxeBootFileProcessor.php @@ -0,0 +1,72 @@ +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 = []): PxeBootFileOutput + { + if (!($data instanceof PxeBootFileInput)) { + throw new \Exception(sprintf('data is not instance of %s', PxeBootFileInput::class)); + } + + $entity = null; + if (isset($uriVariables['uuid'])) { + $entity = $this->bootFileRepository->findOneByUuid($uriVariables['uuid']); + } + + $pxeTemplate = $data->createOrUpdateEntity($entity); + $this->validator->validate($pxeTemplate); + $this->bootFileRepository->save($pxeTemplate); + + $this->postService->__invoke($pxeTemplate); + + return new PxeBootFileOutput($pxeTemplate); + } + + private function processDelete($data, Operation $operation, array $uriVariables = [], array $context = []): null + { + $bootFile = $this->bootFileRepository->findOneByUuid($uriVariables['uuid']); + $this->bootFileRepository->delete($bootFile); + + return null; + } +} diff --git a/src/State/Provider/PxeBootFileProvider.php b/src/State/Provider/PxeBootFileProvider.php new file mode 100644 index 0000000..f25bef9 --- /dev/null +++ b/src/State/Provider/PxeBootFileProvider.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 PxeBootFileOutput($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('PxeBootFile not found'); + } + + return new PxeBootFileOutput($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 PxeBootFileInput($item) : null; + } + + return new PxeBootFileInput(); + } +} -- 2.40.1 From b26b215ffcdcf6101eb7b76de930622b0bb91ac8 Mon Sep 17 00:00:00 2001 From: Manuel Aranda Date: Wed, 14 Aug 2024 15:01:15 +0200 Subject: [PATCH 009/157] refs #614. Itnegration ogDhcp --- .env | 2 + config/api_platform/PxeBootFile.yaml | 2 +- config/api_platform/Subnet.yaml | 31 ++++++++ config/services.yaml | 6 ++ .../OgDhcp/AbstractOgDhcpController.php | 20 ++++++ src/Controller/OgDhcp/Subnet/DeleteAction.php | 12 ++++ src/Controller/OgDhcp/Subnet/GetAction.php | 42 +++++++++++ .../OgDhcp/Subnet/GetCollectionAction.php | 41 +++++++++++ src/Controller/OgDhcp/Subnet/PostAction.php | 8 +++ src/Controller/OgDhcp/Subnet/PutAction.php | 8 +++ src/Dto/Input/SubnetInput.php | 57 +++++++++++++++ src/Dto/Output/SubnetOutput.php | 42 +++++++++++ src/Entity/Subnet.php | 70 ++++++++++++++++++ src/Factory/SubnetFactory.php | 58 +++++++++++++++ src/Repository/SubnetRepository.php | 18 +++++ src/State/Processor/SubnetProcessor.php | 69 ++++++++++++++++++ src/State/Provider/SubnetProvider.php | 71 +++++++++++++++++++ 17 files changed, 556 insertions(+), 1 deletion(-) create mode 100644 config/api_platform/Subnet.yaml create mode 100644 src/Controller/OgDhcp/AbstractOgDhcpController.php create mode 100644 src/Controller/OgDhcp/Subnet/DeleteAction.php create mode 100644 src/Controller/OgDhcp/Subnet/GetAction.php create mode 100644 src/Controller/OgDhcp/Subnet/GetCollectionAction.php create mode 100644 src/Controller/OgDhcp/Subnet/PostAction.php create mode 100644 src/Controller/OgDhcp/Subnet/PutAction.php create mode 100644 src/Dto/Input/SubnetInput.php create mode 100644 src/Dto/Output/SubnetOutput.php create mode 100644 src/Entity/Subnet.php create mode 100644 src/Factory/SubnetFactory.php create mode 100644 src/Repository/SubnetRepository.php create mode 100644 src/State/Processor/SubnetProcessor.php create mode 100644 src/State/Provider/SubnetProvider.php diff --git a/.env b/.env index 001e100..6a75f75 100644 --- a/.env +++ b/.env @@ -42,3 +42,5 @@ JWT_PASSPHRASE=8b9154df37ffa91ef9186ce095324e39e50ff3b023bb1ed34383abd019ba4515 ###< lexik/jwt-authentication-bundle ### OGBOOT_API_URL=http://localhost:8085 +OGDHCP_API_URL=http://localhost:8085 + diff --git a/config/api_platform/PxeBootFile.yaml b/config/api_platform/PxeBootFile.yaml index 6c5886d..034e707 100644 --- a/config/api_platform/PxeBootFile.yaml +++ b/config/api_platform/PxeBootFile.yaml @@ -9,7 +9,7 @@ resources: groups: ['pxe-boot-file:write'] operations: ApiPlatform\Metadata\GetCollection: - provider: App\State\Provider\PPxeBootFileProvider + provider: App\State\Provider\PxeBootFileProvider filters: - 'api_platform.filter.pxe_boot_file.order' - 'api_platform.filter.pxe_boot_file.search' diff --git a/config/api_platform/Subnet.yaml b/config/api_platform/Subnet.yaml new file mode 100644 index 0000000..7125a1a --- /dev/null +++ b/config/api_platform/Subnet.yaml @@ -0,0 +1,31 @@ +resources: + App\Entity\Subnet: + processor: App\State\Processor\SubnetProcessor + input: App\Dto\Input\SubnetInput + output: App\Dto\Output\SubnetOutput + normalizationContext: + groups: ['default', 'subnet:read'] + denormalizationContext: + groups: ['subnet:write'] + operations: + ApiPlatform\Metadata\GetCollection: + provider: App\State\Provider\SubnetProvider + filters: + - 'api_platform.filter.subnet.order' + - 'api_platform.filter.subnet.search' + + ApiPlatform\Metadata\Get: + provider: App\State\Provider\SubnetProvider + ApiPlatform\Metadata\Put: + provider: App\State\Provider\SubnetProvider + ApiPlatform\Metadata\Patch: + provider: App\State\Provider\SubnetProvider + ApiPlatform\Metadata\Post: ~ + ApiPlatform\Metadata\Delete: ~ + +properties: + App\Entity\Subnet: + id: + identifier: false + uuid: + identifier: true \ No newline at end of file diff --git a/config/services.yaml b/config/services.yaml index e03a116..50cfa8d 100644 --- a/config/services.yaml +++ b/config/services.yaml @@ -10,6 +10,7 @@ services: bind: $ogBootApiUrl: '%env(OGBOOT_API_URL)%' + $ogDhcpApiUrl: '%env(OGDHCP_API_URL)%' App\: resource: '../src/' @@ -118,3 +119,8 @@ services: bind: $collectionProvider: '@api_platform.doctrine.orm.state.collection_provider' $itemProvider: '@api_platform.doctrine.orm.state.item_provider' + + App\State\Provider\SubnetProvider: + bind: + $collectionProvider: '@api_platform.doctrine.orm.state.collection_provider' + $itemProvider: '@api_platform.doctrine.orm.state.item_provider' diff --git a/src/Controller/OgDhcp/AbstractOgDhcpController.php b/src/Controller/OgDhcp/AbstractOgDhcpController.php new file mode 100644 index 0000000..8874a9f --- /dev/null +++ b/src/Controller/OgDhcp/AbstractOgDhcpController.php @@ -0,0 +1,20 @@ +request('GET', $this->ogDhcpApiUrl.'/opengnsys3/rest/dhcp/subnets/'.$data->getChecksum(), [ + 'headers' => [ + 'accept' => 'application/json', + ], + ]); + } catch (TransportExceptionInterface $e) { + return new JsonResponse( data: 'An error occurred', status: Response::HTTP_INTERNAL_SERVER_ERROR); + } + + $data = json_decode($response->getContent(), true); + + return new JsonResponse( data: $data, status: Response::HTTP_OK); + } +} \ No newline at end of file diff --git a/src/Controller/OgDhcp/Subnet/GetCollectionAction.php b/src/Controller/OgDhcp/Subnet/GetCollectionAction.php new file mode 100644 index 0000000..d29c5d0 --- /dev/null +++ b/src/Controller/OgDhcp/Subnet/GetCollectionAction.php @@ -0,0 +1,41 @@ +request('GET', $this->ogDhcpApiUrl.'/opengnsys3/rest/subnets', [ + 'headers' => [ + 'accept' => 'application/json', + ], + ]); + } catch (TransportExceptionInterface $e) { + return new JsonResponse( data: 'An error occurred', status: Response::HTTP_INTERNAL_SERVER_ERROR); + } + + $data = json_decode($response->getContent(), true); + + return new JsonResponse( data: $data, status: Response::HTTP_OK); + } +} \ No newline at end of file diff --git a/src/Controller/OgDhcp/Subnet/PostAction.php b/src/Controller/OgDhcp/Subnet/PostAction.php new file mode 100644 index 0000000..c35a461 --- /dev/null +++ b/src/Controller/OgDhcp/Subnet/PostAction.php @@ -0,0 +1,8 @@ +netmask = $subnet->getNetmask(); + $this->ipAddress = $subnet->getIpAddress(); + $this->nextServer = $subnet->getNextServer(); + $this->bootFileName = $subnet->getBootFileName(); + } + + public function createOrUpdateEntity(?Subnet $subnet = null): Subnet + { + if (!$subnet) { + $subnet = new Subnet(); + } + + $subnet->setNetmask($this->netmask); + $subnet->setIpAddress($this->ipAddress); + $subnet->setNextServer($this->nextServer); + $subnet->setBootFileName($this->bootFileName); + + return $subnet; + } +} \ No newline at end of file diff --git a/src/Dto/Output/SubnetOutput.php b/src/Dto/Output/SubnetOutput.php new file mode 100644 index 0000000..567ff2f --- /dev/null +++ b/src/Dto/Output/SubnetOutput.php @@ -0,0 +1,42 @@ +netmask = $subnet->getNetmask(); + $this->ipAddress = $subnet->getIpAddress(); + $this->nextServer = $subnet->getNextServer(); + $this->bootFileName = $subnet->getBootFileName(); + $this->createdAt = $subnet->getCreatedAt(); + $this->createdBy = $subnet->getCreatedBy(); + } +} \ No newline at end of file diff --git a/src/Entity/Subnet.php b/src/Entity/Subnet.php new file mode 100644 index 0000000..29201dd --- /dev/null +++ b/src/Entity/Subnet.php @@ -0,0 +1,70 @@ +netmask; + } + + public function setNetmask(string $netmask): static + { + $this->netmask = $netmask; + + return $this; + } + + public function getIpAddress(): ?string + { + return $this->ipAddress; + } + + public function setIpAddress(string $ipAddress): static + { + $this->ipAddress = $ipAddress; + + return $this; + } + + public function getNextServer(): ?string + { + return $this->nextServer; + } + + public function setNextServer(string $nextServer): static + { + $this->nextServer = $nextServer; + + return $this; + } + + public function getBootFileName(): ?string + { + return $this->bootFileName; + } + + public function setBootFileName(string $bootFileName): static + { + $this->bootFileName = $bootFileName; + + return $this; + } +} diff --git a/src/Factory/SubnetFactory.php b/src/Factory/SubnetFactory.php new file mode 100644 index 0000000..e9e1c66 --- /dev/null +++ b/src/Factory/SubnetFactory.php @@ -0,0 +1,58 @@ + + */ +final class SubnetFactory extends ModelFactory +{ + /** + * @see https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#factories-as-services + * + * @todo inject services if required + */ + public function __construct() + { + parent::__construct(); + } + + /** + * @see https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#model-factories + * + * @todo add your default values here + */ + protected function getDefaults(): array + { + return [ + 'bootFileName' => self::faker()->text(255), + 'createdAt' => self::faker()->dateTime(), + 'ipAddress' => self::faker()->text(255), + 'netmask' => self::faker()->text(255), + 'nextServer' => self::faker()->text(255), + 'updatedAt' => self::faker()->dateTime() + ]; + } + + /** + * @see https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#initialization + */ + protected function initialize(): self + { + return $this + // ->afterInstantiate(function(Subnet $subnet): void {}) + ; + } + + protected static function getClass(): string + { + return Subnet::class; + } +} diff --git a/src/Repository/SubnetRepository.php b/src/Repository/SubnetRepository.php new file mode 100644 index 0000000..988dd99 --- /dev/null +++ b/src/Repository/SubnetRepository.php @@ -0,0 +1,18 @@ + + */ +class SubnetRepository extends AbstractRepository +{ + public function __construct(ManagerRegistry $registry) + { + parent::__construct($registry, Subnet::class); + } +} diff --git a/src/State/Processor/SubnetProcessor.php b/src/State/Processor/SubnetProcessor.php new file mode 100644 index 0000000..0fef5cf --- /dev/null +++ b/src/State/Processor/SubnetProcessor.php @@ -0,0 +1,69 @@ +processCreateOrUpdate($data, $operation, $uriVariables, $context); + case $operation instanceof Delete: + return $this->processDelete($data, $operation, $uriVariables, $context); + } + } + + /** + * @throws \Exception + */ + private function processCreateOrUpdate($data, Operation $operation, array $uriVariables = [], array $context = []): SubnetOutput + { + if (!($data instanceof SubnetInput)) { + throw new \Exception(sprintf('data is not instance of %s', SubnetInput::class)); + } + + $entity = null; + if (isset($uriVariables['uuid'])) { + $entity = $this->subnetRepository->findOneByUuid($uriVariables['uuid']); + } + + $subnet = $data->createOrUpdateEntity($entity); + $this->validator->validate($subnet); + $this->subnetRepository->save($subnet); + + return new SubnetOutput($subnet); + } + + private function processDelete($data, Operation $operation, array $uriVariables = [], array $context = []): null + { + $bootFile = $this->subnetRepository->findOneByUuid($uriVariables['uuid']); + $this->subnetRepository->delete($bootFile); + + return null; + } +} diff --git a/src/State/Provider/SubnetProvider.php b/src/State/Provider/SubnetProvider.php new file mode 100644 index 0000000..919c688 --- /dev/null +++ b/src/State/Provider/SubnetProvider.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 SubnetOutput($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('Subnet not found'); + } + + return new SubnetOutput($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 SubnetInput($item) : null; + } + + return new SubnetInput(); + } +} -- 2.40.1 From 159d38570ade4f96f5dd61577c6106ab9b895ddd Mon Sep 17 00:00:00 2001 From: Manuel Aranda Date: Wed, 14 Aug 2024 15:06:17 +0200 Subject: [PATCH 010/157] refs #638. Updated pxe template database field --- migrations/Version20240814130427.php | 31 ++++++++++++++++++++++++++++ src/Entity/PxeTemplate.php | 3 ++- 2 files changed, 33 insertions(+), 1 deletion(-) create mode 100644 migrations/Version20240814130427.php diff --git a/migrations/Version20240814130427.php b/migrations/Version20240814130427.php new file mode 100644 index 0000000..8e2cabc --- /dev/null +++ b/migrations/Version20240814130427.php @@ -0,0 +1,31 @@ +addSql('ALTER TABLE pxe_template CHANGE template_content template_content TINYTEXT NOT 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 CHANGE template_content template_content VARCHAR(255) NOT NULL'); + } +} diff --git a/src/Entity/PxeTemplate.php b/src/Entity/PxeTemplate.php index ef310ff..af0e7b3 100644 --- a/src/Entity/PxeTemplate.php +++ b/src/Entity/PxeTemplate.php @@ -3,6 +3,7 @@ namespace App\Entity; use App\Repository\PxeTemplateRepository; +use Doctrine\DBAL\Types\Types; use Doctrine\ORM\Mapping as ORM; use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity; @@ -13,7 +14,7 @@ class PxeTemplate extends AbstractEntity { use NameableTrait; - #[ORM\Column(length: 255)] + #[ORM\Column(length: 255, type: Types::TEXT)] private ?string $templateContent = null; public function getTemplateContent(): ?string -- 2.40.1 From f483b94a703d8fd41c03f2dba4481fd63c4934c6 Mon Sep 17 00:00:00 2001 From: Manuel Aranda Date: Mon, 19 Aug 2024 15:01:32 +0200 Subject: [PATCH 011/157] Improvements, and fixed pagination --- .env | 2 +- composer.json | 2 +- composer.lock | 624 +++++++++--------- config/api_platform/Client.yaml | 2 +- config/api_platform/OrganizationalUnit.yaml | 1 + config/packages/api_platform.yaml | 35 +- docker/default.conf | 10 +- migrations/Version20240819062421.php | 33 + src/Dto/Output/OgLiveOutput.php | 4 + src/Dto/Output/PxeTemplateOutput.php | 4 + src/Entity/OgLive.php | 1 + src/Entity/PxeTemplate.php | 3 +- src/Entity/SynchronizedTrait.php | 23 + src/OpenApi/OpenApiFactory.php | 51 +- .../Processor/OrganizationalUnitProcessor.php | 8 +- src/State/Provider/ClientProvider.php | 6 +- .../Provider/OrganizationalUnitProvider.php | 2 +- 17 files changed, 455 insertions(+), 356 deletions(-) create mode 100644 migrations/Version20240819062421.php create mode 100644 src/Entity/SynchronizedTrait.php diff --git a/.env b/.env index 001e100..340a072 100644 --- a/.env +++ b/.env @@ -24,7 +24,7 @@ APP_SECRET=e95c7f17da15ce1b03d77ad655379c34 # IMPORTANT: You MUST configure your server version, either here or in config/packages/doctrine.yaml # # DATABASE_URL="sqlite:///%kernel.project_dir%/var/data.db" -# DATABASE_URL="mysql://app:!ChangeMe!@127.0.0.1:3306/app?serverVersion=8.0.32&charset=utf8mb4" +#DATABASE_URL="mysql://root:root@127.0.0.1:3336/dimio?serverVersion=8.0.32&charset=utf8mb4" DATABASE_URL="mysql://root:root@ogcore-database:3306/ogcore?serverVersion=10.11.2-MariaDB&charset=utf8mb4" OG_1_DATABASE_URL="mysql://root:root@ogcore-database:3306/ogcore_old_og?serverVersion=10.11.2-MariaDB&charset=utf8mb4" diff --git a/composer.json b/composer.json index 7aa562a..1c7178a 100644 --- a/composer.json +++ b/composer.json @@ -7,7 +7,7 @@ "php": ">=8.1", "ext-ctype": "*", "ext-iconv": "*", - "api-platform/core": "^3.3", + "api-platform/core": "^3.2", "doctrine/dbal": "^3", "doctrine/doctrine-bundle": "^2.12", "doctrine/doctrine-migrations-bundle": "^3.3", diff --git a/composer.lock b/composer.lock index 05fab12..e2c3c61 100644 --- a/composer.lock +++ b/composer.lock @@ -4,20 +4,20 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "788f45c89f13c815d43700e8374bf655", + "content-hash": "696433794f1d706f6d16af7589d2c1ea", "packages": [ { "name": "api-platform/core", - "version": "v3.3.5", + "version": "v3.3.11", "source": { "type": "git", "url": "https://github.com/api-platform/core.git", - "reference": "b5a93fb0bb855273aabb0807505ba61b68813246" + "reference": "ea7d443376dfa1f9a05b53060049e7837d2ec7d4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/api-platform/core/zipball/b5a93fb0bb855273aabb0807505ba61b68813246", - "reference": "b5a93fb0bb855273aabb0807505ba61b68813246", + "url": "https://api.github.com/repos/api-platform/core/zipball/ea7d443376dfa1f9a05b53060049e7837d2ec7d4", + "reference": "ea7d443376dfa1f9a05b53060049e7837d2ec7d4", "shasum": "" }, "require": { @@ -190,9 +190,9 @@ ], "support": { "issues": "https://github.com/api-platform/core/issues", - "source": "https://github.com/api-platform/core/tree/v3.3.5" + "source": "https://github.com/api-platform/core/tree/v3.3.11" }, - "time": "2024-05-29T05:48:47+00:00" + "time": "2024-07-20T08:19:33+00:00" }, { "name": "behat/transliterator", @@ -575,16 +575,16 @@ }, { "name": "doctrine/dbal", - "version": "3.8.5", + "version": "3.9.0", "source": { "type": "git", "url": "https://github.com/doctrine/dbal.git", - "reference": "0e3536ba088a749985c8801105b6b3ac6c1280b6" + "reference": "d8f68ea6cc00912e5313237130b8c8decf4d28c6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/dbal/zipball/0e3536ba088a749985c8801105b6b3ac6c1280b6", - "reference": "0e3536ba088a749985c8801105b6b3ac6c1280b6", + "url": "https://api.github.com/repos/doctrine/dbal/zipball/d8f68ea6cc00912e5313237130b8c8decf4d28c6", + "reference": "d8f68ea6cc00912e5313237130b8c8decf4d28c6", "shasum": "" }, "require": { @@ -600,12 +600,12 @@ "doctrine/coding-standard": "12.0.0", "fig/log-test": "^1", "jetbrains/phpstorm-stubs": "2023.1", - "phpstan/phpstan": "1.11.1", + "phpstan/phpstan": "1.11.7", "phpstan/phpstan-strict-rules": "^1.6", - "phpunit/phpunit": "9.6.19", + "phpunit/phpunit": "9.6.20", "psalm/plugin-phpunit": "0.18.4", "slevomat/coding-standard": "8.13.1", - "squizlabs/php_codesniffer": "3.9.2", + "squizlabs/php_codesniffer": "3.10.2", "symfony/cache": "^5.4|^6.0|^7.0", "symfony/console": "^4.4|^5.4|^6.0|^7.0", "vimeo/psalm": "4.30.0" @@ -668,7 +668,7 @@ ], "support": { "issues": "https://github.com/doctrine/dbal/issues", - "source": "https://github.com/doctrine/dbal/tree/3.8.5" + "source": "https://github.com/doctrine/dbal/tree/3.9.0" }, "funding": [ { @@ -684,7 +684,7 @@ "type": "tidelift" } ], - "time": "2024-06-08T17:49:56+00:00" + "time": "2024-08-15T07:34:42+00:00" }, { "name": "doctrine/deprecations", @@ -1276,21 +1276,21 @@ }, { "name": "doctrine/migrations", - "version": "3.7.4", + "version": "3.8.0", "source": { "type": "git", "url": "https://github.com/doctrine/migrations.git", - "reference": "954e0a314c2f0eb9fb418210445111747de254a6" + "reference": "535a70dcbd88b8c6ba945be050977457f4f4c06c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/migrations/zipball/954e0a314c2f0eb9fb418210445111747de254a6", - "reference": "954e0a314c2f0eb9fb418210445111747de254a6", + "url": "https://api.github.com/repos/doctrine/migrations/zipball/535a70dcbd88b8c6ba945be050977457f4f4c06c", + "reference": "535a70dcbd88b8c6ba945be050977457f4f4c06c", "shasum": "" }, "require": { "composer-runtime-api": "^2", - "doctrine/dbal": "^3.5.1 || ^4", + "doctrine/dbal": "^3.6 || ^4", "doctrine/deprecations": "^0.5.3 || ^1", "doctrine/event-manager": "^1.2 || ^2.0", "php": "^8.1", @@ -1328,7 +1328,7 @@ "type": "library", "autoload": { "psr-4": { - "Doctrine\\Migrations\\": "lib/Doctrine/Migrations" + "Doctrine\\Migrations\\": "src" } }, "notification-url": "https://packagist.org/downloads/", @@ -1358,7 +1358,7 @@ ], "support": { "issues": "https://github.com/doctrine/migrations/issues", - "source": "https://github.com/doctrine/migrations/tree/3.7.4" + "source": "https://github.com/doctrine/migrations/tree/3.8.0" }, "funding": [ { @@ -1374,20 +1374,20 @@ "type": "tidelift" } ], - "time": "2024-03-06T13:41:11+00:00" + "time": "2024-06-26T14:12:46+00:00" }, { "name": "doctrine/orm", - "version": "2.19.5", + "version": "2.19.6", "source": { "type": "git", "url": "https://github.com/doctrine/orm.git", - "reference": "94986af28452da42a46a4489d1c958a2e5d710e5" + "reference": "c1bb2ccf4b19c845f91ff7c4c01dc7cbba7f4073" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/orm/zipball/94986af28452da42a46a4489d1c958a2e5d710e5", - "reference": "94986af28452da42a46a4489d1c958a2e5d710e5", + "url": "https://api.github.com/repos/doctrine/orm/zipball/c1bb2ccf4b19c845f91ff7c4c01dc7cbba7f4073", + "reference": "c1bb2ccf4b19c845f91ff7c4c01dc7cbba7f4073", "shasum": "" }, "require": { @@ -1416,14 +1416,14 @@ "doctrine/annotations": "^1.13 || ^2", "doctrine/coding-standard": "^9.0.2 || ^12.0", "phpbench/phpbench": "^0.16.10 || ^1.0", - "phpstan/phpstan": "~1.4.10 || 1.10.59", + "phpstan/phpstan": "~1.4.10 || 1.11.1", "phpunit/phpunit": "^7.5 || ^8.5 || ^9.6", "psr/log": "^1 || ^2 || ^3", "squizlabs/php_codesniffer": "3.7.2", "symfony/cache": "^4.4 || ^5.4 || ^6.4 || ^7.0", "symfony/var-exporter": "^4.4 || ^5.4 || ^6.2 || ^7.0", "symfony/yaml": "^3.4 || ^4.0 || ^5.0 || ^6.0 || ^7.0", - "vimeo/psalm": "4.30.0 || 5.22.2" + "vimeo/psalm": "4.30.0 || 5.24.0" }, "suggest": { "ext-dom": "Provides support for XSD validation for XML mapping files", @@ -1473,22 +1473,22 @@ ], "support": { "issues": "https://github.com/doctrine/orm/issues", - "source": "https://github.com/doctrine/orm/tree/2.19.5" + "source": "https://github.com/doctrine/orm/tree/2.19.6" }, - "time": "2024-04-30T06:49:54+00:00" + "time": "2024-06-26T17:24:40+00:00" }, { "name": "doctrine/persistence", - "version": "3.3.2", + "version": "3.3.3", "source": { "type": "git", "url": "https://github.com/doctrine/persistence.git", - "reference": "477da35bd0255e032826f440b94b3e37f2d56f42" + "reference": "b337726451f5d530df338fc7f68dee8781b49779" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/persistence/zipball/477da35bd0255e032826f440b94b3e37f2d56f42", - "reference": "477da35bd0255e032826f440b94b3e37f2d56f42", + "url": "https://api.github.com/repos/doctrine/persistence/zipball/b337726451f5d530df338fc7f68dee8781b49779", + "reference": "b337726451f5d530df338fc7f68dee8781b49779", "shasum": "" }, "require": { @@ -1500,15 +1500,14 @@ "doctrine/common": "<2.10" }, "require-dev": { - "composer/package-versions-deprecated": "^1.11", - "doctrine/coding-standard": "^11", + "doctrine/coding-standard": "^12", "doctrine/common": "^3.0", - "phpstan/phpstan": "1.9.4", + "phpstan/phpstan": "1.11.1", "phpstan/phpstan-phpunit": "^1", "phpstan/phpstan-strict-rules": "^1.1", "phpunit/phpunit": "^8.5 || ^9.5", "symfony/cache": "^4.4 || ^5.4 || ^6.0", - "vimeo/psalm": "4.30.0 || 5.3.0" + "vimeo/psalm": "4.30.0 || 5.24.0" }, "type": "library", "autoload": { @@ -1557,7 +1556,7 @@ ], "support": { "issues": "https://github.com/doctrine/persistence/issues", - "source": "https://github.com/doctrine/persistence/tree/3.3.2" + "source": "https://github.com/doctrine/persistence/tree/3.3.3" }, "funding": [ { @@ -1573,20 +1572,20 @@ "type": "tidelift" } ], - "time": "2024-03-12T14:54:36+00:00" + "time": "2024-06-20T10:14:30+00:00" }, { "name": "doctrine/sql-formatter", - "version": "1.4.0", + "version": "1.4.1", "source": { "type": "git", "url": "https://github.com/doctrine/sql-formatter.git", - "reference": "d1ac84aef745c69ea034929eb6d65a6908b675cc" + "reference": "7f83911cc5eba870de7ebb11283972483f7e2891" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/sql-formatter/zipball/d1ac84aef745c69ea034929eb6d65a6908b675cc", - "reference": "d1ac84aef745c69ea034929eb6d65a6908b675cc", + "url": "https://api.github.com/repos/doctrine/sql-formatter/zipball/7f83911cc5eba870de7ebb11283972483f7e2891", + "reference": "7f83911cc5eba870de7ebb11283972483f7e2891", "shasum": "" }, "require": { @@ -1626,22 +1625,22 @@ ], "support": { "issues": "https://github.com/doctrine/sql-formatter/issues", - "source": "https://github.com/doctrine/sql-formatter/tree/1.4.0" + "source": "https://github.com/doctrine/sql-formatter/tree/1.4.1" }, - "time": "2024-05-08T08:12:09+00:00" + "time": "2024-08-05T20:32:22+00:00" }, { "name": "gedmo/doctrine-extensions", - "version": "v3.15.0", + "version": "v3.16.1", "source": { "type": "git", "url": "https://github.com/doctrine-extensions/DoctrineExtensions.git", - "reference": "2a89103f4984d8970f3855284c8c04e6e6a63c0f" + "reference": "e85560ed96f977b8c29428a99222cb2ef2f0e80d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine-extensions/DoctrineExtensions/zipball/2a89103f4984d8970f3855284c8c04e6e6a63c0f", - "reference": "2a89103f4984d8970f3855284c8c04e6e6a63c0f", + "url": "https://api.github.com/repos/doctrine-extensions/DoctrineExtensions/zipball/e85560ed96f977b8c29428a99222cb2ef2f0e80d", + "reference": "e85560ed96f977b8c29428a99222cb2ef2f0e80d", "shasum": "" }, "require": { @@ -1660,7 +1659,7 @@ "doctrine/annotations": "<1.13 || >=3.0", "doctrine/dbal": "<3.2 || >=4.0", "doctrine/mongodb-odm": "<2.3 || >=3.0", - "doctrine/orm": "<2.14.0 || 2.16.0 || 2.16.1 || >=3.0" + "doctrine/orm": "<2.14.0 || 2.16.0 || 2.16.1 || >=4.0" }, "require-dev": { "doctrine/annotations": "^1.13 || ^2.0", @@ -1668,16 +1667,18 @@ "doctrine/dbal": "^3.2", "doctrine/doctrine-bundle": "^2.3", "doctrine/mongodb-odm": "^2.3", - "doctrine/orm": "^2.14.0", + "doctrine/orm": "^2.14.0 || ^3.0", "friendsofphp/php-cs-fixer": "^3.14.0", "nesbot/carbon": "^2.71 || ^3.0", - "phpstan/phpstan": "^1.10.2", - "phpstan/phpstan-doctrine": "^1.0", - "phpstan/phpstan-phpunit": "^1.0", + "phpstan/phpstan": "^1.11", + "phpstan/phpstan-doctrine": "^1.4", + "phpstan/phpstan-phpunit": "^1.4", "phpunit/phpunit": "^9.6", - "rector/rector": "^0.19", + "rector/rector": "^1.1", "symfony/console": "^5.4 || ^6.0 || ^7.0", + "symfony/doctrine-bridge": "^5.4 || ^6.0 || ^7.0", "symfony/phpunit-bridge": "^6.0 || ^7.0", + "symfony/uid": "^5.4 || ^6.0 || ^7.0", "symfony/yaml": "^5.4 || ^6.0 || ^7.0" }, "suggest": { @@ -1687,7 +1688,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "3.13-dev" + "dev-main": "3.x-dev" } }, "autoload": { @@ -1735,7 +1736,7 @@ "support": { "email": "gediminas.morkevicius@gmail.com", "issues": "https://github.com/doctrine-extensions/DoctrineExtensions/issues", - "source": "https://github.com/doctrine-extensions/DoctrineExtensions/tree/v3.15.0", + "source": "https://github.com/doctrine-extensions/DoctrineExtensions/tree/v3.16.1", "wiki": "https://github.com/Atlantic18/DoctrineExtensions/tree/main/doc" }, "funding": [ @@ -1756,7 +1757,7 @@ "type": "github" } ], - "time": "2024-02-12T15:17:22+00:00" + "time": "2024-06-25T16:22:14+00:00" }, { "name": "gesdinet/jwt-refresh-token-bundle", @@ -1977,16 +1978,16 @@ }, { "name": "lexik/jwt-authentication-bundle", - "version": "v3.0.0", + "version": "v3.1.0", "source": { "type": "git", "url": "https://github.com/lexik/LexikJWTAuthenticationBundle.git", - "reference": "b20c4ae7fdfe1d7422c7099a141cc9eb64627310" + "reference": "4f1a638289cf9282bad1b82b8df56d3bd4e0743c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/lexik/LexikJWTAuthenticationBundle/zipball/b20c4ae7fdfe1d7422c7099a141cc9eb64627310", - "reference": "b20c4ae7fdfe1d7422c7099a141cc9eb64627310", + "url": "https://api.github.com/repos/lexik/LexikJWTAuthenticationBundle/zipball/4f1a638289cf9282bad1b82b8df56d3bd4e0743c", + "reference": "4f1a638289cf9282bad1b82b8df56d3bd4e0743c", "shasum": "" }, "require": { @@ -2076,7 +2077,7 @@ ], "support": { "issues": "https://github.com/lexik/LexikJWTAuthenticationBundle/issues", - "source": "https://github.com/lexik/LexikJWTAuthenticationBundle/tree/v3.0.0" + "source": "https://github.com/lexik/LexikJWTAuthenticationBundle/tree/v3.1.0" }, "funding": [ { @@ -2088,20 +2089,20 @@ "type": "tidelift" } ], - "time": "2024-05-05T17:49:24+00:00" + "time": "2024-07-03T20:49:59+00:00" }, { "name": "nelmio/cors-bundle", - "version": "2.4.0", + "version": "2.5.0", "source": { "type": "git", "url": "https://github.com/nelmio/NelmioCorsBundle.git", - "reference": "78fcdb91f76b080a1008133def9c7f613833933d" + "reference": "3a526fe025cd20e04a6a11370cf5ab28dbb5a544" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nelmio/NelmioCorsBundle/zipball/78fcdb91f76b080a1008133def9c7f613833933d", - "reference": "78fcdb91f76b080a1008133def9c7f613833933d", + "url": "https://api.github.com/repos/nelmio/NelmioCorsBundle/zipball/3a526fe025cd20e04a6a11370cf5ab28dbb5a544", + "reference": "3a526fe025cd20e04a6a11370cf5ab28dbb5a544", "shasum": "" }, "require": { @@ -2148,9 +2149,9 @@ ], "support": { "issues": "https://github.com/nelmio/NelmioCorsBundle/issues", - "source": "https://github.com/nelmio/NelmioCorsBundle/tree/2.4.0" + "source": "https://github.com/nelmio/NelmioCorsBundle/tree/2.5.0" }, - "time": "2023-11-30T16:41:19+00:00" + "time": "2024-06-24T21:25:28+00:00" }, { "name": "phpdocumentor/reflection-common", @@ -2947,16 +2948,16 @@ }, { "name": "stof/doctrine-extensions-bundle", - "version": "v1.11.0", + "version": "v1.12.0", "source": { "type": "git", "url": "https://github.com/stof/StofDoctrineExtensionsBundle.git", - "reference": "9f7023e4c8a1c00a5627d41c1027a3f89e477630" + "reference": "473ae65598fa4160654c350e139e20ee75d9a91a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/stof/StofDoctrineExtensionsBundle/zipball/9f7023e4c8a1c00a5627d41c1027a3f89e477630", - "reference": "9f7023e4c8a1c00a5627d41c1027a3f89e477630", + "url": "https://api.github.com/repos/stof/StofDoctrineExtensionsBundle/zipball/473ae65598fa4160654c350e139e20ee75d9a91a", + "reference": "473ae65598fa4160654c350e139e20ee75d9a91a", "shasum": "" }, "require": { @@ -3021,9 +3022,9 @@ ], "support": { "issues": "https://github.com/stof/StofDoctrineExtensionsBundle/issues", - "source": "https://github.com/stof/StofDoctrineExtensionsBundle/tree/v1.11.0" + "source": "https://github.com/stof/StofDoctrineExtensionsBundle/tree/v1.12.0" }, - "time": "2024-02-13T14:43:20+00:00" + "time": "2024-06-10T12:27:27+00:00" }, { "name": "symfony/asset", @@ -3096,16 +3097,16 @@ }, { "name": "symfony/cache", - "version": "v6.4.8", + "version": "v6.4.10", "source": { "type": "git", "url": "https://github.com/symfony/cache.git", - "reference": "287142df5579ce223c485b3872df3efae8390984" + "reference": "6702d2d777260e6ff3451fee2d7d78ab5f715cdc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/cache/zipball/287142df5579ce223c485b3872df3efae8390984", - "reference": "287142df5579ce223c485b3872df3efae8390984", + "url": "https://api.github.com/repos/symfony/cache/zipball/6702d2d777260e6ff3451fee2d7d78ab5f715cdc", + "reference": "6702d2d777260e6ff3451fee2d7d78ab5f715cdc", "shasum": "" }, "require": { @@ -3172,7 +3173,7 @@ "psr6" ], "support": { - "source": "https://github.com/symfony/cache/tree/v6.4.8" + "source": "https://github.com/symfony/cache/tree/v6.4.10" }, "funding": [ { @@ -3188,7 +3189,7 @@ "type": "tidelift" } ], - "time": "2024-05-31T14:49:08+00:00" + "time": "2024-07-17T06:05:49+00:00" }, { "name": "symfony/cache-contracts", @@ -3417,16 +3418,16 @@ }, { "name": "symfony/console", - "version": "v6.4.8", + "version": "v6.4.10", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "be5854cee0e8c7b110f00d695d11debdfa1a2a91" + "reference": "504974cbe43d05f83b201d6498c206f16fc0cdbc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/be5854cee0e8c7b110f00d695d11debdfa1a2a91", - "reference": "be5854cee0e8c7b110f00d695d11debdfa1a2a91", + "url": "https://api.github.com/repos/symfony/console/zipball/504974cbe43d05f83b201d6498c206f16fc0cdbc", + "reference": "504974cbe43d05f83b201d6498c206f16fc0cdbc", "shasum": "" }, "require": { @@ -3491,7 +3492,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v6.4.8" + "source": "https://github.com/symfony/console/tree/v6.4.10" }, "funding": [ { @@ -3507,20 +3508,20 @@ "type": "tidelift" } ], - "time": "2024-05-31T14:49:08+00:00" + "time": "2024-07-26T12:30:32+00:00" }, { "name": "symfony/dependency-injection", - "version": "v6.4.8", + "version": "v6.4.10", "source": { "type": "git", "url": "https://github.com/symfony/dependency-injection.git", - "reference": "d3b618176e8c3a9e5772151c51eba0c52a0c771c" + "reference": "5caf9c5f6085f13b27d70a236b776c07e4a1c3eb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/d3b618176e8c3a9e5772151c51eba0c52a0c771c", - "reference": "d3b618176e8c3a9e5772151c51eba0c52a0c771c", + "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/5caf9c5f6085f13b27d70a236b776c07e4a1c3eb", + "reference": "5caf9c5f6085f13b27d70a236b776c07e4a1c3eb", "shasum": "" }, "require": { @@ -3572,7 +3573,7 @@ "description": "Allows you to standardize and centralize the way objects are constructed in your application", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/dependency-injection/tree/v6.4.8" + "source": "https://github.com/symfony/dependency-injection/tree/v6.4.10" }, "funding": [ { @@ -3588,7 +3589,7 @@ "type": "tidelift" } ], - "time": "2024-05-31T14:49:08+00:00" + "time": "2024-07-26T07:32:07+00:00" }, { "name": "symfony/deprecation-contracts", @@ -3659,16 +3660,16 @@ }, { "name": "symfony/doctrine-bridge", - "version": "v6.4.8", + "version": "v6.4.10", "source": { "type": "git", "url": "https://github.com/symfony/doctrine-bridge.git", - "reference": "afbf291ccaf595c8ff6f4ed3943aa0ea479e4d04" + "reference": "0de9662441bce4670506d0c371cc819a9d0a7607" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/doctrine-bridge/zipball/afbf291ccaf595c8ff6f4ed3943aa0ea479e4d04", - "reference": "afbf291ccaf595c8ff6f4ed3943aa0ea479e4d04", + "url": "https://api.github.com/repos/symfony/doctrine-bridge/zipball/0de9662441bce4670506d0c371cc819a9d0a7607", + "reference": "0de9662441bce4670506d0c371cc819a9d0a7607", "shasum": "" }, "require": { @@ -3747,7 +3748,7 @@ "description": "Provides integration for Doctrine with various Symfony components", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/doctrine-bridge/tree/v6.4.8" + "source": "https://github.com/symfony/doctrine-bridge/tree/v6.4.10" }, "funding": [ { @@ -3763,20 +3764,20 @@ "type": "tidelift" } ], - "time": "2024-05-31T14:49:08+00:00" + "time": "2024-07-26T12:30:32+00:00" }, { "name": "symfony/dotenv", - "version": "v6.4.8", + "version": "v6.4.10", "source": { "type": "git", "url": "https://github.com/symfony/dotenv.git", - "reference": "55aefa0029adff89ecffdb560820e945c7983f06" + "reference": "2ae0c84cc9be0dc1eeb86016970b63c764d8472e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/dotenv/zipball/55aefa0029adff89ecffdb560820e945c7983f06", - "reference": "55aefa0029adff89ecffdb560820e945c7983f06", + "url": "https://api.github.com/repos/symfony/dotenv/zipball/2ae0c84cc9be0dc1eeb86016970b63c764d8472e", + "reference": "2ae0c84cc9be0dc1eeb86016970b63c764d8472e", "shasum": "" }, "require": { @@ -3821,7 +3822,7 @@ "environment" ], "support": { - "source": "https://github.com/symfony/dotenv/tree/v6.4.8" + "source": "https://github.com/symfony/dotenv/tree/v6.4.10" }, "funding": [ { @@ -3837,20 +3838,20 @@ "type": "tidelift" } ], - "time": "2024-05-31T14:49:08+00:00" + "time": "2024-07-09T18:29:35+00:00" }, { "name": "symfony/error-handler", - "version": "v6.4.8", + "version": "v6.4.10", "source": { "type": "git", "url": "https://github.com/symfony/error-handler.git", - "reference": "ef836152bf13472dc5fb5b08b0c0c4cfeddc0fcc" + "reference": "231f1b2ee80f72daa1972f7340297d67439224f0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/error-handler/zipball/ef836152bf13472dc5fb5b08b0c0c4cfeddc0fcc", - "reference": "ef836152bf13472dc5fb5b08b0c0c4cfeddc0fcc", + "url": "https://api.github.com/repos/symfony/error-handler/zipball/231f1b2ee80f72daa1972f7340297d67439224f0", + "reference": "231f1b2ee80f72daa1972f7340297d67439224f0", "shasum": "" }, "require": { @@ -3896,7 +3897,7 @@ "description": "Provides tools to manage errors and ease debugging PHP code", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/error-handler/tree/v6.4.8" + "source": "https://github.com/symfony/error-handler/tree/v6.4.10" }, "funding": [ { @@ -3912,7 +3913,7 @@ "type": "tidelift" } ], - "time": "2024-05-31T14:49:08+00:00" + "time": "2024-07-26T12:30:32+00:00" }, { "name": "symfony/event-dispatcher", @@ -4136,16 +4137,16 @@ }, { "name": "symfony/filesystem", - "version": "v6.4.8", + "version": "v6.4.9", "source": { "type": "git", "url": "https://github.com/symfony/filesystem.git", - "reference": "4d37529150e7081c51b3c5d5718c55a04a9503f3" + "reference": "b51ef8059159330b74a4d52f68e671033c0fe463" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/4d37529150e7081c51b3c5d5718c55a04a9503f3", - "reference": "4d37529150e7081c51b3c5d5718c55a04a9503f3", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/b51ef8059159330b74a4d52f68e671033c0fe463", + "reference": "b51ef8059159330b74a4d52f68e671033c0fe463", "shasum": "" }, "require": { @@ -4182,7 +4183,7 @@ "description": "Provides basic utilities for the filesystem", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/filesystem/tree/v6.4.8" + "source": "https://github.com/symfony/filesystem/tree/v6.4.9" }, "funding": [ { @@ -4198,20 +4199,20 @@ "type": "tidelift" } ], - "time": "2024-05-31T14:49:08+00:00" + "time": "2024-06-28T09:49:33+00:00" }, { "name": "symfony/finder", - "version": "v6.4.8", + "version": "v6.4.10", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "3ef977a43883215d560a2cecb82ec8e62131471c" + "reference": "af29198d87112bebdd397bd7735fbd115997824c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/3ef977a43883215d560a2cecb82ec8e62131471c", - "reference": "3ef977a43883215d560a2cecb82ec8e62131471c", + "url": "https://api.github.com/repos/symfony/finder/zipball/af29198d87112bebdd397bd7735fbd115997824c", + "reference": "af29198d87112bebdd397bd7735fbd115997824c", "shasum": "" }, "require": { @@ -4246,7 +4247,7 @@ "description": "Finds files and directories via an intuitive fluent interface", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/finder/tree/v6.4.8" + "source": "https://github.com/symfony/finder/tree/v6.4.10" }, "funding": [ { @@ -4262,20 +4263,20 @@ "type": "tidelift" } ], - "time": "2024-05-31T14:49:08+00:00" + "time": "2024-07-24T07:06:38+00:00" }, { "name": "symfony/flex", - "version": "v2.4.5", + "version": "v2.4.6", "source": { "type": "git", "url": "https://github.com/symfony/flex.git", - "reference": "b0a405f40614c9f584b489d54f91091817b0e26e" + "reference": "4dc11919791f81d087a12db2ab4c7e044431ef6b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/flex/zipball/b0a405f40614c9f584b489d54f91091817b0e26e", - "reference": "b0a405f40614c9f584b489d54f91091817b0e26e", + "url": "https://api.github.com/repos/symfony/flex/zipball/4dc11919791f81d087a12db2ab4c7e044431ef6b", + "reference": "4dc11919791f81d087a12db2ab4c7e044431ef6b", "shasum": "" }, "require": { @@ -4311,7 +4312,7 @@ "description": "Composer plugin for Symfony", "support": { "issues": "https://github.com/symfony/flex/issues", - "source": "https://github.com/symfony/flex/tree/v2.4.5" + "source": "https://github.com/symfony/flex/tree/v2.4.6" }, "funding": [ { @@ -4327,20 +4328,20 @@ "type": "tidelift" } ], - "time": "2024-03-02T08:16:47+00:00" + "time": "2024-04-27T10:22:22+00:00" }, { "name": "symfony/framework-bundle", - "version": "v6.4.8", + "version": "v6.4.10", "source": { "type": "git", "url": "https://github.com/symfony/framework-bundle.git", - "reference": "7c7739f87f1a8be1c2f5e7d28addfe763a917acb" + "reference": "6cbdb0cc3ddbb63499262cd3036882b08ee2690b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/framework-bundle/zipball/7c7739f87f1a8be1c2f5e7d28addfe763a917acb", - "reference": "7c7739f87f1a8be1c2f5e7d28addfe763a917acb", + "url": "https://api.github.com/repos/symfony/framework-bundle/zipball/6cbdb0cc3ddbb63499262cd3036882b08ee2690b", + "reference": "6cbdb0cc3ddbb63499262cd3036882b08ee2690b", "shasum": "" }, "require": { @@ -4459,7 +4460,7 @@ "description": "Provides a tight integration between Symfony components and the Symfony full-stack framework", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/framework-bundle/tree/v6.4.8" + "source": "https://github.com/symfony/framework-bundle/tree/v6.4.10" }, "funding": [ { @@ -4475,7 +4476,7 @@ "type": "tidelift" } ], - "time": "2024-05-31T14:49:08+00:00" + "time": "2024-07-26T13:24:20+00:00" }, { "name": "symfony/http-client", @@ -4650,16 +4651,16 @@ }, { "name": "symfony/http-foundation", - "version": "v6.4.8", + "version": "v6.4.10", "source": { "type": "git", "url": "https://github.com/symfony/http-foundation.git", - "reference": "27de8cc95e11db7a50b027e71caaab9024545947" + "reference": "117f1f20a7ade7bcea28b861fb79160a21a1e37b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-foundation/zipball/27de8cc95e11db7a50b027e71caaab9024545947", - "reference": "27de8cc95e11db7a50b027e71caaab9024545947", + "url": "https://api.github.com/repos/symfony/http-foundation/zipball/117f1f20a7ade7bcea28b861fb79160a21a1e37b", + "reference": "117f1f20a7ade7bcea28b861fb79160a21a1e37b", "shasum": "" }, "require": { @@ -4707,7 +4708,7 @@ "description": "Defines an object-oriented layer for the HTTP specification", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/http-foundation/tree/v6.4.8" + "source": "https://github.com/symfony/http-foundation/tree/v6.4.10" }, "funding": [ { @@ -4723,20 +4724,20 @@ "type": "tidelift" } ], - "time": "2024-05-31T14:49:08+00:00" + "time": "2024-07-26T12:36:27+00:00" }, { "name": "symfony/http-kernel", - "version": "v6.4.8", + "version": "v6.4.10", "source": { "type": "git", "url": "https://github.com/symfony/http-kernel.git", - "reference": "6c519aa3f32adcfd1d1f18d923f6b227d9acf3c1" + "reference": "147e0daf618d7575b5007055340d09aece5cf068" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-kernel/zipball/6c519aa3f32adcfd1d1f18d923f6b227d9acf3c1", - "reference": "6c519aa3f32adcfd1d1f18d923f6b227d9acf3c1", + "url": "https://api.github.com/repos/symfony/http-kernel/zipball/147e0daf618d7575b5007055340d09aece5cf068", + "reference": "147e0daf618d7575b5007055340d09aece5cf068", "shasum": "" }, "require": { @@ -4821,7 +4822,7 @@ "description": "Provides a structured process for converting a Request into a Response", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/http-kernel/tree/v6.4.8" + "source": "https://github.com/symfony/http-kernel/tree/v6.4.10" }, "funding": [ { @@ -4837,7 +4838,7 @@ "type": "tidelift" } ], - "time": "2024-06-02T16:06:25+00:00" + "time": "2024-07-26T14:52:04+00:00" }, { "name": "symfony/password-hasher", @@ -4913,16 +4914,16 @@ }, { "name": "symfony/polyfill-intl-grapheme", - "version": "v1.29.0", + "version": "v1.30.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-grapheme.git", - "reference": "32a9da87d7b3245e09ac426c83d334ae9f06f80f" + "reference": "64647a7c30b2283f5d49b874d84a18fc22054b7a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/32a9da87d7b3245e09ac426c83d334ae9f06f80f", - "reference": "32a9da87d7b3245e09ac426c83d334ae9f06f80f", + "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/64647a7c30b2283f5d49b874d84a18fc22054b7a", + "reference": "64647a7c30b2283f5d49b874d84a18fc22054b7a", "shasum": "" }, "require": { @@ -4971,7 +4972,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.29.0" + "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.30.0" }, "funding": [ { @@ -4987,20 +4988,20 @@ "type": "tidelift" } ], - "time": "2024-01-29T20:11:03+00:00" + "time": "2024-05-31T15:07:36+00:00" }, { "name": "symfony/polyfill-intl-normalizer", - "version": "v1.29.0", + "version": "v1.30.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-normalizer.git", - "reference": "bc45c394692b948b4d383a08d7753968bed9a83d" + "reference": "a95281b0be0d9ab48050ebd988b967875cdb9fdb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/bc45c394692b948b4d383a08d7753968bed9a83d", - "reference": "bc45c394692b948b4d383a08d7753968bed9a83d", + "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/a95281b0be0d9ab48050ebd988b967875cdb9fdb", + "reference": "a95281b0be0d9ab48050ebd988b967875cdb9fdb", "shasum": "" }, "require": { @@ -5052,7 +5053,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.29.0" + "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.30.0" }, "funding": [ { @@ -5068,20 +5069,20 @@ "type": "tidelift" } ], - "time": "2024-01-29T20:11:03+00:00" + "time": "2024-05-31T15:07:36+00:00" }, { "name": "symfony/polyfill-mbstring", - "version": "v1.29.0", + "version": "v1.30.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "9773676c8a1bb1f8d4340a62efe641cf76eda7ec" + "reference": "fd22ab50000ef01661e2a31d850ebaa297f8e03c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/9773676c8a1bb1f8d4340a62efe641cf76eda7ec", - "reference": "9773676c8a1bb1f8d4340a62efe641cf76eda7ec", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/fd22ab50000ef01661e2a31d850ebaa297f8e03c", + "reference": "fd22ab50000ef01661e2a31d850ebaa297f8e03c", "shasum": "" }, "require": { @@ -5132,7 +5133,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.29.0" + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.30.0" }, "funding": [ { @@ -5148,25 +5149,24 @@ "type": "tidelift" } ], - "time": "2024-01-29T20:11:03+00:00" + "time": "2024-06-19T12:30:46+00:00" }, { "name": "symfony/polyfill-php83", - "version": "v1.29.0", + "version": "v1.30.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php83.git", - "reference": "86fcae159633351e5fd145d1c47de6c528f8caff" + "reference": "dbdcdf1a4dcc2743591f1079d0c35ab1e2dcbbc9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php83/zipball/86fcae159633351e5fd145d1c47de6c528f8caff", - "reference": "86fcae159633351e5fd145d1c47de6c528f8caff", + "url": "https://api.github.com/repos/symfony/polyfill-php83/zipball/dbdcdf1a4dcc2743591f1079d0c35ab1e2dcbbc9", + "reference": "dbdcdf1a4dcc2743591f1079d0c35ab1e2dcbbc9", "shasum": "" }, "require": { - "php": ">=7.1", - "symfony/polyfill-php80": "^1.14" + "php": ">=7.1" }, "type": "library", "extra": { @@ -5209,7 +5209,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php83/tree/v1.29.0" + "source": "https://github.com/symfony/polyfill-php83/tree/v1.30.0" }, "funding": [ { @@ -5225,7 +5225,7 @@ "type": "tidelift" } ], - "time": "2024-01-29T20:11:03+00:00" + "time": "2024-06-19T12:35:24+00:00" }, { "name": "symfony/property-access", @@ -5306,16 +5306,16 @@ }, { "name": "symfony/property-info", - "version": "v6.4.8", + "version": "v6.4.10", "source": { "type": "git", "url": "https://github.com/symfony/property-info.git", - "reference": "7f544bc6ceb1a6a2283c7af8e8621262c43b7ede" + "reference": "edaea9dcc723cb4a0ab6a00f7d6f8c07c0d8ff77" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/property-info/zipball/7f544bc6ceb1a6a2283c7af8e8621262c43b7ede", - "reference": "7f544bc6ceb1a6a2283c7af8e8621262c43b7ede", + "url": "https://api.github.com/repos/symfony/property-info/zipball/edaea9dcc723cb4a0ab6a00f7d6f8c07c0d8ff77", + "reference": "edaea9dcc723cb4a0ab6a00f7d6f8c07c0d8ff77", "shasum": "" }, "require": { @@ -5369,7 +5369,7 @@ "validator" ], "support": { - "source": "https://github.com/symfony/property-info/tree/v6.4.8" + "source": "https://github.com/symfony/property-info/tree/v6.4.10" }, "funding": [ { @@ -5385,20 +5385,20 @@ "type": "tidelift" } ], - "time": "2024-05-31T14:49:08+00:00" + "time": "2024-07-26T07:32:07+00:00" }, { "name": "symfony/routing", - "version": "v6.4.8", + "version": "v6.4.10", "source": { "type": "git", "url": "https://github.com/symfony/routing.git", - "reference": "8a40d0f9b01f0fbb80885d3ce0ad6714fb603a58" + "reference": "aad19fe10753ba842f0d653a8db819c4b3affa87" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/routing/zipball/8a40d0f9b01f0fbb80885d3ce0ad6714fb603a58", - "reference": "8a40d0f9b01f0fbb80885d3ce0ad6714fb603a58", + "url": "https://api.github.com/repos/symfony/routing/zipball/aad19fe10753ba842f0d653a8db819c4b3affa87", + "reference": "aad19fe10753ba842f0d653a8db819c4b3affa87", "shasum": "" }, "require": { @@ -5452,7 +5452,7 @@ "url" ], "support": { - "source": "https://github.com/symfony/routing/tree/v6.4.8" + "source": "https://github.com/symfony/routing/tree/v6.4.10" }, "funding": [ { @@ -5468,7 +5468,7 @@ "type": "tidelift" } ], - "time": "2024-05-31T14:49:08+00:00" + "time": "2024-07-15T09:26:24+00:00" }, { "name": "symfony/runtime", @@ -5551,16 +5551,16 @@ }, { "name": "symfony/security-bundle", - "version": "v6.4.8", + "version": "v6.4.10", "source": { "type": "git", "url": "https://github.com/symfony/security-bundle.git", - "reference": "dfb286069b0332e1f1c21962133d17c0fbc1e5e7" + "reference": "50007f4f76632741b62fa9604c5f65807f268b72" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/security-bundle/zipball/dfb286069b0332e1f1c21962133d17c0fbc1e5e7", - "reference": "dfb286069b0332e1f1c21962133d17c0fbc1e5e7", + "url": "https://api.github.com/repos/symfony/security-bundle/zipball/50007f4f76632741b62fa9604c5f65807f268b72", + "reference": "50007f4f76632741b62fa9604c5f65807f268b72", "shasum": "" }, "require": { @@ -5643,7 +5643,7 @@ "description": "Provides a tight integration of the Security component into the Symfony full-stack framework", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/security-bundle/tree/v6.4.8" + "source": "https://github.com/symfony/security-bundle/tree/v6.4.10" }, "funding": [ { @@ -5659,20 +5659,20 @@ "type": "tidelift" } ], - "time": "2024-05-31T14:49:08+00:00" + "time": "2024-07-17T10:49:44+00:00" }, { "name": "symfony/security-core", - "version": "v6.4.8", + "version": "v6.4.10", "source": { "type": "git", "url": "https://github.com/symfony/security-core.git", - "reference": "5fc7850ada5e8e03d78c1739c82c64d5e2f7d495" + "reference": "432dec55da108c471adcf58c351af01372a52164" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/security-core/zipball/5fc7850ada5e8e03d78c1739c82c64d5e2f7d495", - "reference": "5fc7850ada5e8e03d78c1739c82c64d5e2f7d495", + "url": "https://api.github.com/repos/symfony/security-core/zipball/432dec55da108c471adcf58c351af01372a52164", + "reference": "432dec55da108c471adcf58c351af01372a52164", "shasum": "" }, "require": { @@ -5729,7 +5729,7 @@ "description": "Symfony Security Component - Core Library", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/security-core/tree/v6.4.8" + "source": "https://github.com/symfony/security-core/tree/v6.4.10" }, "funding": [ { @@ -5745,7 +5745,7 @@ "type": "tidelift" } ], - "time": "2024-05-31T14:49:08+00:00" + "time": "2024-07-26T12:30:32+00:00" }, { "name": "symfony/security-csrf", @@ -5817,16 +5817,16 @@ }, { "name": "symfony/security-http", - "version": "v6.4.8", + "version": "v6.4.9", "source": { "type": "git", "url": "https://github.com/symfony/security-http.git", - "reference": "fb82ddec887dc67f3bcf4d6df3cb8efd529be104" + "reference": "8e70f39626ada36c5492c3aff9369c85d2840948" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/security-http/zipball/fb82ddec887dc67f3bcf4d6df3cb8efd529be104", - "reference": "fb82ddec887dc67f3bcf4d6df3cb8efd529be104", + "url": "https://api.github.com/repos/symfony/security-http/zipball/8e70f39626ada36c5492c3aff9369c85d2840948", + "reference": "8e70f39626ada36c5492c3aff9369c85d2840948", "shasum": "" }, "require": { @@ -5885,7 +5885,7 @@ "description": "Symfony Security Component - HTTP Integration", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/security-http/tree/v6.4.8" + "source": "https://github.com/symfony/security-http/tree/v6.4.9" }, "funding": [ { @@ -5901,20 +5901,20 @@ "type": "tidelift" } ], - "time": "2024-05-31T14:49:08+00:00" + "time": "2024-06-21T16:04:15+00:00" }, { "name": "symfony/serializer", - "version": "v6.4.8", + "version": "v6.4.10", "source": { "type": "git", "url": "https://github.com/symfony/serializer.git", - "reference": "d6eda9966a3e5d1823c1cedf41bf98f8ed969d7c" + "reference": "9a67fcf320561e96f94d62bbe0e169ac534a5718" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/serializer/zipball/d6eda9966a3e5d1823c1cedf41bf98f8ed969d7c", - "reference": "d6eda9966a3e5d1823c1cedf41bf98f8ed969d7c", + "url": "https://api.github.com/repos/symfony/serializer/zipball/9a67fcf320561e96f94d62bbe0e169ac534a5718", + "reference": "9a67fcf320561e96f94d62bbe0e169ac534a5718", "shasum": "" }, "require": { @@ -5983,7 +5983,7 @@ "description": "Handles serializing and deserializing data structures, including object graphs, into array structures or other formats like XML and JSON.", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/serializer/tree/v6.4.8" + "source": "https://github.com/symfony/serializer/tree/v6.4.10" }, "funding": [ { @@ -5999,7 +5999,7 @@ "type": "tidelift" } ], - "time": "2024-05-31T14:49:08+00:00" + "time": "2024-07-26T13:13:26+00:00" }, { "name": "symfony/service-contracts", @@ -6148,16 +6148,16 @@ }, { "name": "symfony/string", - "version": "v6.4.8", + "version": "v6.4.10", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "a147c0f826c4a1f3afb763ab8e009e37c877a44d" + "reference": "ccf9b30251719567bfd46494138327522b9a9446" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/a147c0f826c4a1f3afb763ab8e009e37c877a44d", - "reference": "a147c0f826c4a1f3afb763ab8e009e37c877a44d", + "url": "https://api.github.com/repos/symfony/string/zipball/ccf9b30251719567bfd46494138327522b9a9446", + "reference": "ccf9b30251719567bfd46494138327522b9a9446", "shasum": "" }, "require": { @@ -6214,7 +6214,7 @@ "utf8" ], "support": { - "source": "https://github.com/symfony/string/tree/v6.4.8" + "source": "https://github.com/symfony/string/tree/v6.4.10" }, "funding": [ { @@ -6230,20 +6230,20 @@ "type": "tidelift" } ], - "time": "2024-05-31T14:49:08+00:00" + "time": "2024-07-22T10:21:14+00:00" }, { "name": "symfony/translation", - "version": "v6.4.8", + "version": "v6.4.10", "source": { "type": "git", "url": "https://github.com/symfony/translation.git", - "reference": "a002933b13989fc4bd0b58e04bf7eec5210e438a" + "reference": "94041203f8ac200ae9e7c6a18fa6137814ccecc9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/translation/zipball/a002933b13989fc4bd0b58e04bf7eec5210e438a", - "reference": "a002933b13989fc4bd0b58e04bf7eec5210e438a", + "url": "https://api.github.com/repos/symfony/translation/zipball/94041203f8ac200ae9e7c6a18fa6137814ccecc9", + "reference": "94041203f8ac200ae9e7c6a18fa6137814ccecc9", "shasum": "" }, "require": { @@ -6309,7 +6309,7 @@ "description": "Provides tools to internationalize your application", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/translation/tree/v6.4.8" + "source": "https://github.com/symfony/translation/tree/v6.4.10" }, "funding": [ { @@ -6325,7 +6325,7 @@ "type": "tidelift" } ], - "time": "2024-05-31T14:49:08+00:00" + "time": "2024-07-26T12:30:32+00:00" }, { "name": "symfony/translation-contracts", @@ -6407,16 +6407,16 @@ }, { "name": "symfony/twig-bridge", - "version": "v6.4.8", + "version": "v6.4.9", "source": { "type": "git", "url": "https://github.com/symfony/twig-bridge.git", - "reference": "57de1b7d7499053a2c5beb9344751e8bfd332649" + "reference": "9bcb26445b9d4ef1087c389234bf33fb00e10ea6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/twig-bridge/zipball/57de1b7d7499053a2c5beb9344751e8bfd332649", - "reference": "57de1b7d7499053a2c5beb9344751e8bfd332649", + "url": "https://api.github.com/repos/symfony/twig-bridge/zipball/9bcb26445b9d4ef1087c389234bf33fb00e10ea6", + "reference": "9bcb26445b9d4ef1087c389234bf33fb00e10ea6", "shasum": "" }, "require": { @@ -6496,7 +6496,7 @@ "description": "Provides integration for Twig with various Symfony components", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/twig-bridge/tree/v6.4.8" + "source": "https://github.com/symfony/twig-bridge/tree/v6.4.9" }, "funding": [ { @@ -6512,7 +6512,7 @@ "type": "tidelift" } ], - "time": "2024-05-31T14:49:08+00:00" + "time": "2024-06-21T16:04:15+00:00" }, { "name": "symfony/twig-bundle", @@ -6600,16 +6600,16 @@ }, { "name": "symfony/validator", - "version": "v6.4.8", + "version": "v6.4.10", "source": { "type": "git", "url": "https://github.com/symfony/validator.git", - "reference": "dab2781371d54c86f6b25623ab16abb2dde2870c" + "reference": "bcf939a9d1acd7d2912e9474c0c3d7840a03cbcd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/validator/zipball/dab2781371d54c86f6b25623ab16abb2dde2870c", - "reference": "dab2781371d54c86f6b25623ab16abb2dde2870c", + "url": "https://api.github.com/repos/symfony/validator/zipball/bcf939a9d1acd7d2912e9474c0c3d7840a03cbcd", + "reference": "bcf939a9d1acd7d2912e9474c0c3d7840a03cbcd", "shasum": "" }, "require": { @@ -6677,7 +6677,7 @@ "description": "Provides tools to validate values", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/validator/tree/v6.4.8" + "source": "https://github.com/symfony/validator/tree/v6.4.10" }, "funding": [ { @@ -6693,20 +6693,20 @@ "type": "tidelift" } ], - "time": "2024-06-02T15:48:50+00:00" + "time": "2024-07-26T12:30:32+00:00" }, { "name": "symfony/var-dumper", - "version": "v6.4.8", + "version": "v6.4.10", "source": { "type": "git", "url": "https://github.com/symfony/var-dumper.git", - "reference": "ad23ca4312395f0a8a8633c831ef4c4ee542ed25" + "reference": "a71cc3374f5fb9759da1961d28c452373b343dd4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/var-dumper/zipball/ad23ca4312395f0a8a8633c831ef4c4ee542ed25", - "reference": "ad23ca4312395f0a8a8633c831ef4c4ee542ed25", + "url": "https://api.github.com/repos/symfony/var-dumper/zipball/a71cc3374f5fb9759da1961d28c452373b343dd4", + "reference": "a71cc3374f5fb9759da1961d28c452373b343dd4", "shasum": "" }, "require": { @@ -6762,7 +6762,7 @@ "dump" ], "support": { - "source": "https://github.com/symfony/var-dumper/tree/v6.4.8" + "source": "https://github.com/symfony/var-dumper/tree/v6.4.10" }, "funding": [ { @@ -6778,20 +6778,20 @@ "type": "tidelift" } ], - "time": "2024-05-31T14:49:08+00:00" + "time": "2024-07-26T12:30:32+00:00" }, { "name": "symfony/var-exporter", - "version": "v6.4.8", + "version": "v6.4.9", "source": { "type": "git", "url": "https://github.com/symfony/var-exporter.git", - "reference": "792ca836f99b340f2e9ca9497c7953948c49a504" + "reference": "f9a060622e0d93777b7f8687ec4860191e16802e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/var-exporter/zipball/792ca836f99b340f2e9ca9497c7953948c49a504", - "reference": "792ca836f99b340f2e9ca9497c7953948c49a504", + "url": "https://api.github.com/repos/symfony/var-exporter/zipball/f9a060622e0d93777b7f8687ec4860191e16802e", + "reference": "f9a060622e0d93777b7f8687ec4860191e16802e", "shasum": "" }, "require": { @@ -6839,7 +6839,7 @@ "serialize" ], "support": { - "source": "https://github.com/symfony/var-exporter/tree/v6.4.8" + "source": "https://github.com/symfony/var-exporter/tree/v6.4.9" }, "funding": [ { @@ -6855,7 +6855,7 @@ "type": "tidelift" } ], - "time": "2024-05-31T14:49:08+00:00" + "time": "2024-06-24T15:53:56+00:00" }, { "name": "symfony/web-link", @@ -7014,16 +7014,16 @@ }, { "name": "twig/twig", - "version": "v3.10.3", + "version": "v3.11.0", "source": { "type": "git", "url": "https://github.com/twigphp/Twig.git", - "reference": "67f29781ffafa520b0bbfbd8384674b42db04572" + "reference": "e80fb8ebba85c7341a97a9ebf825d7fd4b77708d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/twigphp/Twig/zipball/67f29781ffafa520b0bbfbd8384674b42db04572", - "reference": "67f29781ffafa520b0bbfbd8384674b42db04572", + "url": "https://api.github.com/repos/twigphp/Twig/zipball/e80fb8ebba85c7341a97a9ebf825d7fd4b77708d", + "reference": "e80fb8ebba85c7341a97a9ebf825d7fd4b77708d", "shasum": "" }, "require": { @@ -7031,7 +7031,8 @@ "symfony/deprecation-contracts": "^2.5|^3", "symfony/polyfill-ctype": "^1.8", "symfony/polyfill-mbstring": "^1.3", - "symfony/polyfill-php80": "^1.22" + "symfony/polyfill-php80": "^1.22", + "symfony/polyfill-php81": "^1.29" }, "require-dev": { "psr/container": "^1.0|^2.0", @@ -7077,7 +7078,7 @@ ], "support": { "issues": "https://github.com/twigphp/Twig/issues", - "source": "https://github.com/twigphp/Twig/tree/v3.10.3" + "source": "https://github.com/twigphp/Twig/tree/v3.11.0" }, "funding": [ { @@ -7089,7 +7090,7 @@ "type": "tidelift" } ], - "time": "2024-05-16T10:04:27+00:00" + "time": "2024-08-08T16:15:16+00:00" }, { "name": "webmozart/assert", @@ -7577,16 +7578,16 @@ }, { "name": "myclabs/deep-copy", - "version": "1.11.1", + "version": "1.12.0", "source": { "type": "git", "url": "https://github.com/myclabs/DeepCopy.git", - "reference": "7284c22080590fb39f2ffa3e9057f10a4ddd0e0c" + "reference": "3a6b9a42cd8f8771bd4295d13e1423fa7f3d942c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/7284c22080590fb39f2ffa3e9057f10a4ddd0e0c", - "reference": "7284c22080590fb39f2ffa3e9057f10a4ddd0e0c", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/3a6b9a42cd8f8771bd4295d13e1423fa7f3d942c", + "reference": "3a6b9a42cd8f8771bd4295d13e1423fa7f3d942c", "shasum": "" }, "require": { @@ -7594,11 +7595,12 @@ }, "conflict": { "doctrine/collections": "<1.6.8", - "doctrine/common": "<2.13.3 || >=3,<3.2.2" + "doctrine/common": "<2.13.3 || >=3 <3.2.2" }, "require-dev": { "doctrine/collections": "^1.6.8", "doctrine/common": "^2.13.3 || ^3.2.2", + "phpspec/prophecy": "^1.10", "phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13" }, "type": "library", @@ -7624,7 +7626,7 @@ ], "support": { "issues": "https://github.com/myclabs/DeepCopy/issues", - "source": "https://github.com/myclabs/DeepCopy/tree/1.11.1" + "source": "https://github.com/myclabs/DeepCopy/tree/1.12.0" }, "funding": [ { @@ -7632,20 +7634,20 @@ "type": "tidelift" } ], - "time": "2023-03-08T13:26:56+00:00" + "time": "2024-06-12T14:39:25+00:00" }, { "name": "nikic/php-parser", - "version": "v5.0.2", + "version": "v5.1.0", "source": { "type": "git", "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "139676794dc1e9231bf7bcd123cfc0c99182cb13" + "reference": "683130c2ff8c2739f4822ff7ac5c873ec529abd1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/139676794dc1e9231bf7bcd123cfc0c99182cb13", - "reference": "139676794dc1e9231bf7bcd123cfc0c99182cb13", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/683130c2ff8c2739f4822ff7ac5c873ec529abd1", + "reference": "683130c2ff8c2739f4822ff7ac5c873ec529abd1", "shasum": "" }, "require": { @@ -7656,7 +7658,7 @@ }, "require-dev": { "ircmaxell/php-yacc": "^0.0.7", - "phpunit/phpunit": "^7.0 || ^8.0 || ^9.0" + "phpunit/phpunit": "^9.0" }, "bin": [ "bin/php-parse" @@ -7688,9 +7690,9 @@ ], "support": { "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/v5.0.2" + "source": "https://github.com/nikic/PHP-Parser/tree/v5.1.0" }, - "time": "2024-03-05T20:51:40+00:00" + "time": "2024-07-01T20:03:41+00:00" }, { "name": "phar-io/manifest", @@ -8131,45 +8133,45 @@ }, { "name": "phpunit/phpunit", - "version": "9.6.19", + "version": "9.6.20", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "a1a54a473501ef4cdeaae4e06891674114d79db8" + "reference": "49d7820565836236411f5dc002d16dd689cde42f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/a1a54a473501ef4cdeaae4e06891674114d79db8", - "reference": "a1a54a473501ef4cdeaae4e06891674114d79db8", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/49d7820565836236411f5dc002d16dd689cde42f", + "reference": "49d7820565836236411f5dc002d16dd689cde42f", "shasum": "" }, "require": { - "doctrine/instantiator": "^1.3.1 || ^2", + "doctrine/instantiator": "^1.5.0 || ^2", "ext-dom": "*", "ext-json": "*", "ext-libxml": "*", "ext-mbstring": "*", "ext-xml": "*", "ext-xmlwriter": "*", - "myclabs/deep-copy": "^1.10.1", - "phar-io/manifest": "^2.0.3", - "phar-io/version": "^3.0.2", + "myclabs/deep-copy": "^1.12.0", + "phar-io/manifest": "^2.0.4", + "phar-io/version": "^3.2.1", "php": ">=7.3", - "phpunit/php-code-coverage": "^9.2.28", - "phpunit/php-file-iterator": "^3.0.5", + "phpunit/php-code-coverage": "^9.2.31", + "phpunit/php-file-iterator": "^3.0.6", "phpunit/php-invoker": "^3.1.1", - "phpunit/php-text-template": "^2.0.3", - "phpunit/php-timer": "^5.0.2", - "sebastian/cli-parser": "^1.0.1", - "sebastian/code-unit": "^1.0.6", + "phpunit/php-text-template": "^2.0.4", + "phpunit/php-timer": "^5.0.3", + "sebastian/cli-parser": "^1.0.2", + "sebastian/code-unit": "^1.0.8", "sebastian/comparator": "^4.0.8", - "sebastian/diff": "^4.0.3", - "sebastian/environment": "^5.1.3", - "sebastian/exporter": "^4.0.5", - "sebastian/global-state": "^5.0.1", - "sebastian/object-enumerator": "^4.0.3", - "sebastian/resource-operations": "^3.0.3", - "sebastian/type": "^3.2", + "sebastian/diff": "^4.0.6", + "sebastian/environment": "^5.1.5", + "sebastian/exporter": "^4.0.6", + "sebastian/global-state": "^5.0.7", + "sebastian/object-enumerator": "^4.0.4", + "sebastian/resource-operations": "^3.0.4", + "sebastian/type": "^3.2.1", "sebastian/version": "^3.0.2" }, "suggest": { @@ -8214,7 +8216,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", "security": "https://github.com/sebastianbergmann/phpunit/security/policy", - "source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.19" + "source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.20" }, "funding": [ { @@ -8230,7 +8232,7 @@ "type": "tidelift" } ], - "time": "2024-04-05T04:35:58+00:00" + "time": "2024-07-10T11:45:39+00:00" }, { "name": "sebastian/cli-parser", @@ -9489,16 +9491,16 @@ }, { "name": "symfony/phpunit-bridge", - "version": "v7.1.1", + "version": "v7.1.3", "source": { "type": "git", "url": "https://github.com/symfony/phpunit-bridge.git", - "reference": "3e1cb8c4dee341cfe96ae9fe29b1acda52a6bb16" + "reference": "e823122d31935eb711e2767c31f3d71cb0b87fb1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/phpunit-bridge/zipball/3e1cb8c4dee341cfe96ae9fe29b1acda52a6bb16", - "reference": "3e1cb8c4dee341cfe96ae9fe29b1acda52a6bb16", + "url": "https://api.github.com/repos/symfony/phpunit-bridge/zipball/e823122d31935eb711e2767c31f3d71cb0b87fb1", + "reference": "e823122d31935eb711e2767c31f3d71cb0b87fb1", "shasum": "" }, "require": { @@ -9551,7 +9553,7 @@ "description": "Provides utilities for PHPUnit, especially user deprecation notices management", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/phpunit-bridge/tree/v7.1.1" + "source": "https://github.com/symfony/phpunit-bridge/tree/v7.1.3" }, "funding": [ { @@ -9567,7 +9569,7 @@ "type": "tidelift" } ], - "time": "2024-06-04T06:50:37+00:00" + "time": "2024-07-26T12:41:01+00:00" }, { "name": "symfony/process", @@ -9632,16 +9634,16 @@ }, { "name": "symfony/web-profiler-bundle", - "version": "v6.4.8", + "version": "v6.4.10", "source": { "type": "git", "url": "https://github.com/symfony/web-profiler-bundle.git", - "reference": "bcc806d1360991de3bf78ac5ca0202db85de9bfc" + "reference": "370c9f1e3567cd4670d44311838e37d16182c3a7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/web-profiler-bundle/zipball/bcc806d1360991de3bf78ac5ca0202db85de9bfc", - "reference": "bcc806d1360991de3bf78ac5ca0202db85de9bfc", + "url": "https://api.github.com/repos/symfony/web-profiler-bundle/zipball/370c9f1e3567cd4670d44311838e37d16182c3a7", + "reference": "370c9f1e3567cd4670d44311838e37d16182c3a7", "shasum": "" }, "require": { @@ -9694,7 +9696,7 @@ "dev" ], "support": { - "source": "https://github.com/symfony/web-profiler-bundle/tree/v6.4.8" + "source": "https://github.com/symfony/web-profiler-bundle/tree/v6.4.10" }, "funding": [ { @@ -9710,7 +9712,7 @@ "type": "tidelift" } ], - "time": "2024-05-31T14:49:08+00:00" + "time": "2024-07-19T07:26:48+00:00" }, { "name": "theseer/tokenizer", @@ -9879,16 +9881,16 @@ }, { "name": "zenstruck/foundry", - "version": "v1.38.0", + "version": "v1.38.3", "source": { "type": "git", "url": "https://github.com/zenstruck/foundry.git", - "reference": "fd3c763de120445878077e54c608d8d75a3cc22d" + "reference": "112e3a6ce1d75627a3fbfd6cd009055994167edb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zenstruck/foundry/zipball/fd3c763de120445878077e54c608d8d75a3cc22d", - "reference": "fd3c763de120445878077e54c608d8d75a3cc22d", + "url": "https://api.github.com/repos/zenstruck/foundry/zipball/112e3a6ce1d75627a3fbfd6cd009055994167edb", + "reference": "112e3a6ce1d75627a3fbfd6cd009055994167edb", "shasum": "" }, "require": { @@ -9961,7 +9963,7 @@ ], "support": { "issues": "https://github.com/zenstruck/foundry/issues", - "source": "https://github.com/zenstruck/foundry/tree/v1.38.0" + "source": "https://github.com/zenstruck/foundry/tree/v1.38.3" }, "funding": [ { @@ -9969,7 +9971,7 @@ "type": "github" } ], - "time": "2024-06-07T23:04:21+00:00" + "time": "2024-07-04T19:59:49+00:00" } ], "aliases": [], diff --git a/config/api_platform/Client.yaml b/config/api_platform/Client.yaml index b61769b..4fca0a6 100644 --- a/config/api_platform/Client.yaml +++ b/config/api_platform/Client.yaml @@ -13,7 +13,7 @@ resources: filters: - 'api_platform.filter.client.order' - 'api_platform.filter.client.search' - - 'api_platform.filter.client.boolean' + ApiPlatform\Metadata\Get: provider: App\State\Provider\ClientProvider ApiPlatform\Metadata\Put: diff --git a/config/api_platform/OrganizationalUnit.yaml b/config/api_platform/OrganizationalUnit.yaml index ac90e14..e8f198b 100644 --- a/config/api_platform/OrganizationalUnit.yaml +++ b/config/api_platform/OrganizationalUnit.yaml @@ -13,6 +13,7 @@ resources: filters: - 'api_platform.filter.organizational_unit.order' - 'api_platform.filter.organizational_unit.search' + ApiPlatform\Metadata\Get: security: 'is_granted("ORGANIZATIONAL_UNIT_VIEW", object)' provider: App\State\Provider\OrganizationalUnitProvider diff --git a/config/packages/api_platform.yaml b/config/packages/api_platform.yaml index 352abc2..f449351 100644 --- a/config/packages/api_platform.yaml +++ b/config/packages/api_platform.yaml @@ -2,39 +2,20 @@ api_platform: title: 'OgCore Api' description: 'Api Documentation for OgCore' version: 1.0.0 - path_segment_name_generator: 'api_platform.path_segment_name_generator.dash' + path_segment_name_generator: api_platform.path_segment_name_generator.dash + defaults: + pagination_client_items_per_page: true + collection: + pagination: + items_per_page_parameter_name: 'itemsPerPage' formats: jsonld: [ 'application/ld+json' ] - jsonhal: [ 'application/hal+json' ] - jsonapi: [ 'application/vnd.api+json' ] - json: [ 'application/json' ] - xml: [ 'application/xml', 'text/xml' ] - yaml: [ 'application/x-yaml' ] - csv: [ 'text/csv' ] - html: [ 'text/html' ] + json: [ 'application/json' ] + csv: [ 'text/csv' ] patch_formats: jsonld: ['application/ld+json', 'application/json'] mapping: paths: ['%kernel.project_dir%/config/api_platform', '%kernel.project_dir%/src/Dto'] - use_symfony_listeners: true - collection: - pagination: - items_per_page_parameter_name: 'itemsPerPage' - defaults: - pagination_client_items_per_page: true - denormalization_context: - allow_extra_attributes: false - cache_headers: - vary: [ 'Content-Type', 'Authorization', 'Origin' ] - extra_properties: - standard_put: true - rfc_7807_compliant_errors: true - event_listeners_backward_compatibility_layer: false - keep_legacy_inflector: true - docs_formats: - jsonld: ['application/ld+json'] - jsonopenapi: ['application/vnd.openapi+json'] - html: ['text/html'] swagger: versions: [3] api_keys: diff --git a/docker/default.conf b/docker/default.conf index 654373e..7d7fc2c 100644 --- a/docker/default.conf +++ b/docker/default.conf @@ -2,19 +2,19 @@ server { listen 80; server_name localhost; root /var/www/html/public; + index index.html index.php; - location / { - try_files $uri /index.php?$is_args$args; + location / { + try_files $uri $uri/ /index.php?$args; } - location ~ ^/index.php(/|$) { + location ~ \.php$ { include fastcgi_params; fastcgi_pass php:9000; fastcgi_split_path_info ^(.+\.php)(/.*)$; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; fastcgi_param PATH_INFO $fastcgi_path_info; - fastcgi_param DOCUMENT_ROOT $document_root; - internal; + fastcgi_param PATH_TRANSLATED $document_root$fastcgi_script_name; } location ~ \.php$ { diff --git a/migrations/Version20240819062421.php b/migrations/Version20240819062421.php new file mode 100644 index 0000000..79b21f8 --- /dev/null +++ b/migrations/Version20240819062421.php @@ -0,0 +1,33 @@ +addSql('ALTER TABLE og_live ADD synchronized TINYINT(1) DEFAULT NULL'); + $this->addSql('ALTER TABLE pxe_template ADD synchronized 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 og_live DROP synchronized'); + $this->addSql('ALTER TABLE pxe_template DROP synchronized'); + } +} diff --git a/src/Dto/Output/OgLiveOutput.php b/src/Dto/Output/OgLiveOutput.php index 1b15405..c4462b5 100644 --- a/src/Dto/Output/OgLiveOutput.php +++ b/src/Dto/Output/OgLiveOutput.php @@ -13,6 +13,9 @@ final class OgLiveOutput extends AbstractOutput #[Groups(['og-live:read'])] public string $name; + #[Groups(['og-live:read'])] + public ?bool $synchronized = false; + #[Groups(['og-live:read'])] public ?string $downloadUrl = ''; @@ -27,6 +30,7 @@ final class OgLiveOutput extends AbstractOutput parent::__construct($ogLive); $this->name = $ogLive->getName(); + $this->synchronized = $ogLive->isSynchronized(); $this->downloadUrl = $ogLive->getDownloadUrl(); $this->createdAt = $ogLive->getCreatedAt(); $this->createdBy = $ogLive->getCreatedBy(); diff --git a/src/Dto/Output/PxeTemplateOutput.php b/src/Dto/Output/PxeTemplateOutput.php index 245e007..fd7b5e9 100644 --- a/src/Dto/Output/PxeTemplateOutput.php +++ b/src/Dto/Output/PxeTemplateOutput.php @@ -13,6 +13,9 @@ final class PxeTemplateOutput extends AbstractOutput #[Groups(['pxe-template:read'])] public string $name; + #[Groups(['pxe-template:read'])] + public ?bool $synchronized = null; + #[Groups(['pxe-template:read'])] public ?string $templateContent = ''; @@ -27,6 +30,7 @@ final class PxeTemplateOutput extends AbstractOutput parent::__construct($pxeTemplate); $this->name = $pxeTemplate->getName(); + $this->synchronized = $pxeTemplate->isSynchronized(); $this->templateContent = $pxeTemplate->getTemplateContent(); $this->createdAt = $pxeTemplate->getCreatedAt(); $this->createdBy = $pxeTemplate->getCreatedBy(); diff --git a/src/Entity/OgLive.php b/src/Entity/OgLive.php index e9c6e82..14decae 100644 --- a/src/Entity/OgLive.php +++ b/src/Entity/OgLive.php @@ -12,6 +12,7 @@ use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity; class OgLive extends AbstractEntity { use NameableTrait; + use SynchronizedTrait; #[ORM\Column(length: 255, nullable: true)] private ?string $downloadUrl = null; diff --git a/src/Entity/PxeTemplate.php b/src/Entity/PxeTemplate.php index af0e7b3..b2d3cfb 100644 --- a/src/Entity/PxeTemplate.php +++ b/src/Entity/PxeTemplate.php @@ -13,8 +13,9 @@ use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity; class PxeTemplate extends AbstractEntity { use NameableTrait; + use SynchronizedTrait; - #[ORM\Column(length: 255, type: Types::TEXT)] + #[ORM\Column(type: Types::TEXT, length: 255)] private ?string $templateContent = null; public function getTemplateContent(): ?string diff --git a/src/Entity/SynchronizedTrait.php b/src/Entity/SynchronizedTrait.php new file mode 100644 index 0000000..f4da200 --- /dev/null +++ b/src/Entity/SynchronizedTrait.php @@ -0,0 +1,23 @@ +synchronized; + } + + public function setSynchronized(?bool $synchronized): self + { + $this->synchronized = $synchronized; + + return $this; + } +} diff --git a/src/OpenApi/OpenApiFactory.php b/src/OpenApi/OpenApiFactory.php index 9e0ae6f..dc755c8 100644 --- a/src/OpenApi/OpenApiFactory.php +++ b/src/OpenApi/OpenApiFactory.php @@ -20,7 +20,7 @@ final readonly class OpenApiFactory implements OpenApiFactoryInterface $this->addRefreshToken($openApi); $this->addSearchEndpoint($openApi); - + $this->addStatusEndpoint($openApi); // Agregar el nuevo endpoint return $openApi; } @@ -126,4 +126,53 @@ final readonly class OpenApiFactory implements OpenApiFactoryInterface ]) )); } + + private function addStatusEndpoint(OpenApi $openApi): void + { + $openApi + ->getPaths() + ->addPath('/og-boot/status', (new Model\PathItem())->withGet( + (new Model\Operation('getStatus')) + ->withTags(['OgBoot']) + ->withResponses([ + Response::HTTP_OK => [ + 'description' => 'Service status', + 'content' => [ + 'application/json' => [ + 'schema' => [ + 'type' => 'object', + 'properties' => [ + 'status' => [ + 'type' => 'string', + 'example' => 'ok', + ], + 'uptime' => [ + 'type' => 'integer', + 'example' => 12345, + ], + ], + ], + ], + ], + ], + Response::HTTP_SERVICE_UNAVAILABLE => [ + 'description' => 'Service unavailable', + 'content' => [ + 'application/json' => [ + 'schema' => [ + 'type' => 'object', + 'properties' => [ + 'error' => [ + 'type' => 'string', + 'example' => 'Service is down', + ], + ], + ], + ], + ], + ], + ]) + ->withSummary('Get service status') + )); + } } \ No newline at end of file diff --git a/src/State/Processor/OrganizationalUnitProcessor.php b/src/State/Processor/OrganizationalUnitProcessor.php index 41d3974..468e410 100644 --- a/src/State/Processor/OrganizationalUnitProcessor.php +++ b/src/State/Processor/OrganizationalUnitProcessor.php @@ -21,12 +21,12 @@ use App\Repository\OrganizationalUnitRepository; use App\Service\ChangeClientNetworkSettingsService; use Doctrine\ORM\EntityManagerInterface; -class OrganizationalUnitProcessor implements ProcessorInterface +readonly class OrganizationalUnitProcessor implements ProcessorInterface { public function __construct( - private readonly OrganizationalUnitRepository $organizationalUnitRepository, - private readonly ValidatorInterface $validator, - private readonly ChangeClientNetworkSettingsService $changeClientNetworkSettingsService, + private OrganizationalUnitRepository $organizationalUnitRepository, + private ValidatorInterface $validator, + private ChangeClientNetworkSettingsService $changeClientNetworkSettingsService, ) { } diff --git a/src/State/Provider/ClientProvider.php b/src/State/Provider/ClientProvider.php index d398a0f..c5dd7d0 100644 --- a/src/State/Provider/ClientProvider.php +++ b/src/State/Provider/ClientProvider.php @@ -13,11 +13,11 @@ use App\Dto\Input\ClientInput; use App\Dto\Output\ClientOutput; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; -class ClientProvider implements ProviderInterface +readonly class ClientProvider implements ProviderInterface { public function __construct( - private readonly ProviderInterface $collectionProvider, - private readonly ProviderInterface $itemProvider + private ProviderInterface $collectionProvider, + private ProviderInterface $itemProvider ) { } diff --git a/src/State/Provider/OrganizationalUnitProvider.php b/src/State/Provider/OrganizationalUnitProvider.php index 7475e68..3aa0349 100644 --- a/src/State/Provider/OrganizationalUnitProvider.php +++ b/src/State/Provider/OrganizationalUnitProvider.php @@ -14,7 +14,7 @@ use App\Dto\Output\OrganizationalUnitOutput; use App\Entity\OrganizationalUnit; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; -readonly class OrganizationalUnitProvider implements ProviderInterface +final readonly class OrganizationalUnitProvider implements ProviderInterface { public function __construct( private ProviderInterface $collectionProvider, -- 2.40.1 From 36863bff7bfbde5f8c6ad11c943f890c285e5403 Mon Sep 17 00:00:00 2001 From: Manuel Aranda Date: Mon, 19 Aug 2024 16:03:37 +0200 Subject: [PATCH 012/157] refs #632. Create new fields and updated API --- config/api_platform/PxeBootFile.yaml | 17 +++++++--- migrations/Version20240819140045.php | 33 +++++++++++++++++++ .../OgBoot/OgLive/InstallAction.php | 9 +++-- .../OgBoot/OgLive/SetDefaultAction.php | 7 +++- ...{GetAction.php => GetCollectionAction.php} | 4 +-- .../OgBoot/PxeTemplate/PostAction.php | 7 +++- src/Dto/Output/OgLiveOutput.php | 8 +++++ src/Dto/Output/PxeBootFileOutput.php | 4 +++ src/Entity/OgLive.php | 15 +++++++++ src/Entity/PxeBootFile.php | 2 ++ 10 files changed, 96 insertions(+), 10 deletions(-) create mode 100644 migrations/Version20240819140045.php rename src/Controller/OgBoot/PxeBootFile/{GetAction.php => GetCollectionAction.php} (94%) diff --git a/config/api_platform/PxeBootFile.yaml b/config/api_platform/PxeBootFile.yaml index 6c5886d..f8cafaa 100644 --- a/config/api_platform/PxeBootFile.yaml +++ b/config/api_platform/PxeBootFile.yaml @@ -9,20 +9,29 @@ resources: groups: ['pxe-boot-file:write'] operations: ApiPlatform\Metadata\GetCollection: - provider: App\State\Provider\PPxeBootFileProvider + provider: App\State\Provider\PxeBootFileProvider filters: - 'api_platform.filter.pxe_boot_file.order' - 'api_platform.filter.pxe_boot_file.search' ApiPlatform\Metadata\Get: - provider: App\State\Provider\PxeTemplateProvider + provider: App\State\Provider\PxeBootFileProvider ApiPlatform\Metadata\Put: - provider: App\State\Provider\PxeTemplateProvider + provider: App\State\Provider\PxeBootFileProvider ApiPlatform\Metadata\Patch: - provider: App\State\Provider\PxeTemplateProvider + provider: App\State\Provider\PxeBootFileProvider ApiPlatform\Metadata\Post: ~ ApiPlatform\Metadata\Delete: ~ + get_collection: + shortName: PxeBootFile Server + description: Get collection of PxeBootFile + class: ApiPlatform\Metadata\GetCollection + method: GET + input: false + uriTemplate: /pxe-boot-files/server/get-collection + controller: App\Controller\OgBoot\PxeBootFile\GetCollectionAction + properties: App\Entity\PxeBootFile: id: diff --git a/migrations/Version20240819140045.php b/migrations/Version20240819140045.php new file mode 100644 index 0000000..01a67ef --- /dev/null +++ b/migrations/Version20240819140045.php @@ -0,0 +1,33 @@ +addSql('ALTER TABLE og_live ADD `default` TINYINT(1) DEFAULT NULL'); + $this->addSql('ALTER TABLE pxe_boot_file ADD synchronized 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 og_live DROP `default`'); + $this->addSql('ALTER TABLE pxe_boot_file DROP synchronized'); + } +} diff --git a/src/Controller/OgBoot/OgLive/InstallAction.php b/src/Controller/OgBoot/OgLive/InstallAction.php index 485cb4d..b2bcdac 100644 --- a/src/Controller/OgBoot/OgLive/InstallAction.php +++ b/src/Controller/OgBoot/OgLive/InstallAction.php @@ -4,6 +4,7 @@ namespace App\Controller\OgBoot\OgLive; use App\Controller\OgBoot\AbstractOgLiveController; use App\Entity\OgLive; +use Doctrine\ORM\EntityManagerInterface; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\Attribute\AsController; @@ -22,7 +23,7 @@ class InstallAction extends AbstractOgLiveController * @throws RedirectionExceptionInterface * @throws ClientExceptionInterface */ - public function __invoke(OgLive $data, HttpClientInterface $httpClient): JsonResponse + public function __invoke(OgLive $data, HttpClientInterface $httpClient, EntityManagerInterface $entityManager): JsonResponse { try { $response = $httpClient->request('POST', $this->ogBootApiUrl.'/ogboot/v1/oglives/install', [ @@ -31,13 +32,17 @@ class InstallAction extends AbstractOgLiveController 'Content-Type' => 'application/json', ], 'json' => [ - 'isoname' => $data->getName() + 'url' => $data->getDownloadUrl() ] ]); } catch (TransportExceptionInterface $e) { return new JsonResponse( data: 'An error occurred', status: Response::HTTP_INTERNAL_SERVER_ERROR); } + $data->setInstalled(true); + $entityManager->persist($data); + $entityManager->flush(); + $data = json_decode($response->getContent(), true); return new JsonResponse( data: $data, status: Response::HTTP_OK); diff --git a/src/Controller/OgBoot/OgLive/SetDefaultAction.php b/src/Controller/OgBoot/OgLive/SetDefaultAction.php index 0f96d13..ffdf033 100644 --- a/src/Controller/OgBoot/OgLive/SetDefaultAction.php +++ b/src/Controller/OgBoot/OgLive/SetDefaultAction.php @@ -4,6 +4,7 @@ namespace App\Controller\OgBoot\OgLive; use App\Controller\OgBoot\AbstractOgLiveController; use App\Entity\OgLive; +use Doctrine\ORM\EntityManagerInterface; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\Attribute\AsController; @@ -22,7 +23,7 @@ class SetDefaultAction extends AbstractOgLiveController * @throws RedirectionExceptionInterface * @throws ClientExceptionInterface */ - public function __invoke(OgLive $data, HttpClientInterface $httpClient): JsonResponse + public function __invoke(OgLive $data, HttpClientInterface $httpClient, EntityManagerInterface $entityManager): JsonResponse { try { $response = $httpClient->request('POST', $this->ogBootApiUrl.'/ogboot/v1/oglives/default', [ @@ -37,6 +38,10 @@ class SetDefaultAction extends AbstractOgLiveController return new JsonResponse( data: 'An error occurred', status: Response::HTTP_INTERNAL_SERVER_ERROR); } + $data->setDefault(true); + $entityManager->persist($data); + $entityManager->flush(); + $data = json_decode($response->getContent(), true); return new JsonResponse( data: $data, status: Response::HTTP_OK); diff --git a/src/Controller/OgBoot/PxeBootFile/GetAction.php b/src/Controller/OgBoot/PxeBootFile/GetCollectionAction.php similarity index 94% rename from src/Controller/OgBoot/PxeBootFile/GetAction.php rename to src/Controller/OgBoot/PxeBootFile/GetCollectionAction.php index 766da38..1b07fc6 100644 --- a/src/Controller/OgBoot/PxeBootFile/GetAction.php +++ b/src/Controller/OgBoot/PxeBootFile/GetCollectionAction.php @@ -16,7 +16,7 @@ use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface; use Symfony\Contracts\HttpClient\HttpClientInterface; #[AsController] -class GetAction extends AbstractOgLiveController +class GetCollectionAction extends AbstractOgLiveController { /** * @throws TransportExceptionInterface @@ -27,7 +27,7 @@ class GetAction extends AbstractOgLiveController public function __invoke(PxeBootFile $data, HttpClientInterface $httpClient): JsonResponse { try { - $response = $httpClient->request('GET', $this->ogBootApiUrl.'/ogboot/v1/pxes/'.$data->getName(), [ + $response = $httpClient->request('GET', $this->ogBootApiUrl.'/ogboot/v1/pxes', [ 'headers' => [ 'accept' => 'application/json', ], diff --git a/src/Controller/OgBoot/PxeTemplate/PostAction.php b/src/Controller/OgBoot/PxeTemplate/PostAction.php index a2f996c..6daf8d8 100644 --- a/src/Controller/OgBoot/PxeTemplate/PostAction.php +++ b/src/Controller/OgBoot/PxeTemplate/PostAction.php @@ -4,6 +4,7 @@ namespace App\Controller\OgBoot\PxeTemplate; use App\Controller\OgBoot\AbstractOgLiveController; use App\Entity\PxeTemplate; +use Doctrine\ORM\EntityManagerInterface; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Response; @@ -23,7 +24,7 @@ class PostAction extends AbstractOgLiveController * @throws RedirectionExceptionInterface * @throws ClientExceptionInterface */ - public function __invoke(PxeTemplate $data, HttpClientInterface $httpClient): JsonResponse + public function __invoke(PxeTemplate $data, HttpClientInterface $httpClient, EntityManagerInterface $entityManager): JsonResponse { try { $response = $httpClient->request('POST', $this->ogBootApiUrl.'/ogboot/v1/pxe-templates', [ @@ -37,6 +38,10 @@ class PostAction extends AbstractOgLiveController ], ]); + $data->setSynchronized(true); + $entityManager->persist($data); + $entityManager->flush(); + $data = json_decode($response->getContent(), true); return new JsonResponse($data, Response::HTTP_OK); diff --git a/src/Dto/Output/OgLiveOutput.php b/src/Dto/Output/OgLiveOutput.php index c4462b5..28e117e 100644 --- a/src/Dto/Output/OgLiveOutput.php +++ b/src/Dto/Output/OgLiveOutput.php @@ -16,6 +16,12 @@ final class OgLiveOutput extends AbstractOutput #[Groups(['og-live:read'])] public ?bool $synchronized = false; + #[Groups(['og-live:read'])] + public ?bool $installed = false; + + #[Groups(['og-live:read'])] + public ?bool $default = false; + #[Groups(['og-live:read'])] public ?string $downloadUrl = ''; @@ -31,6 +37,8 @@ final class OgLiveOutput extends AbstractOutput $this->name = $ogLive->getName(); $this->synchronized = $ogLive->isSynchronized(); + $this->installed = $ogLive->isInstalled(); + $this->default = $ogLive->isDefault(); $this->downloadUrl = $ogLive->getDownloadUrl(); $this->createdAt = $ogLive->getCreatedAt(); $this->createdBy = $ogLive->getCreatedBy(); diff --git a/src/Dto/Output/PxeBootFileOutput.php b/src/Dto/Output/PxeBootFileOutput.php index 9000eab..ff76bc6 100644 --- a/src/Dto/Output/PxeBootFileOutput.php +++ b/src/Dto/Output/PxeBootFileOutput.php @@ -17,6 +17,9 @@ final class PxeBootFileOutput extends AbstractOutput #[Groups(['pxe-boot-file:read'])] public array $clients; + #[Groups(['pxe-boot-file:read'])] + public ?bool $synchronized = null; + #[Groups(['pxe-boot-file:read'])] public \DateTime $createdAt; @@ -32,6 +35,7 @@ final class PxeBootFileOutput extends AbstractOutput fn(Client $client) => new ClientOutput($client) )->toArray(); + $this->synchronized = $bootFile->isSynchronized(); $this->createdAt = $bootFile->getCreatedAt(); $this->createdBy = $bootFile->getCreatedBy(); } diff --git a/src/Entity/OgLive.php b/src/Entity/OgLive.php index 14decae..2ecb106 100644 --- a/src/Entity/OgLive.php +++ b/src/Entity/OgLive.php @@ -41,6 +41,9 @@ class OgLive extends AbstractEntity #[ORM\Column(nullable: true)] private ?bool $installed = null; + #[ORM\Column(nullable: true)] + private ?bool $default = null; + public function getDownloadUrl(): ?string { return $this->downloadUrl; @@ -148,4 +151,16 @@ class OgLive extends AbstractEntity return $this; } + + public function isDefault(): ?bool + { + return $this->default; + } + + public function setDefault(?bool $default): static + { + $this->default = $default; + + return $this; + } } diff --git a/src/Entity/PxeBootFile.php b/src/Entity/PxeBootFile.php index 1498caf..b14dd3f 100644 --- a/src/Entity/PxeBootFile.php +++ b/src/Entity/PxeBootFile.php @@ -10,6 +10,8 @@ use Doctrine\ORM\Mapping as ORM; #[ORM\Entity(repositoryClass: PxeBootFileRepository::class)] class PxeBootFile extends AbstractEntity { + use SynchronizedTrait; + #[ORM\ManyToOne] private ?PxeTemplate $template = null; -- 2.40.1 From fa2ee36cb9263c3604401d461167e8bec8c56423 Mon Sep 17 00:00:00 2001 From: Manuel Aranda Date: Tue, 20 Aug 2024 08:44:28 +0200 Subject: [PATCH 013/157] refs #632. Create new fields and updated API --- migrations/Version20240820063513.php | 41 +++++++++++++++++++ migrations/Version20240820064106.php | 31 ++++++++++++++ src/Dto/Input/ClientInput.php | 12 ++++++ src/Dto/Input/NetworkSettingsInput.php | 13 ++++++ src/Dto/Output/ClientOutput.php | 5 +++ src/Dto/Output/NetworkSettingsOutput.php | 7 ++++ src/Dto/Output/OgLiveOutput.php | 4 +- src/Entity/Client.php | 15 +++++++ src/Entity/NetworkSettings.php | 15 +++++++ src/Entity/OgLive.php | 52 +++++++++++++++++++++--- 10 files changed, 188 insertions(+), 7 deletions(-) create mode 100644 migrations/Version20240820063513.php create mode 100644 migrations/Version20240820064106.php diff --git a/migrations/Version20240820063513.php b/migrations/Version20240820063513.php new file mode 100644 index 0000000..42803dd --- /dev/null +++ b/migrations/Version20240820063513.php @@ -0,0 +1,41 @@ +addSql('ALTER TABLE client ADD og_live_id INT DEFAULT NULL'); + $this->addSql('ALTER TABLE client ADD CONSTRAINT FK_C7440455F7E54CF3 FOREIGN KEY (og_live_id) REFERENCES og_live (id)'); + $this->addSql('CREATE INDEX IDX_C7440455F7E54CF3 ON client (og_live_id)'); + $this->addSql('ALTER TABLE network_settings ADD og_live_id INT DEFAULT NULL'); + $this->addSql('ALTER TABLE network_settings ADD CONSTRAINT FK_48869B54F7E54CF3 FOREIGN KEY (og_live_id) REFERENCES og_live (id)'); + $this->addSql('CREATE INDEX IDX_48869B54F7E54CF3 ON network_settings (og_live_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_48869B54F7E54CF3'); + $this->addSql('DROP INDEX IDX_48869B54F7E54CF3 ON network_settings'); + $this->addSql('ALTER TABLE network_settings DROP og_live_id'); + $this->addSql('ALTER TABLE client DROP FOREIGN KEY FK_C7440455F7E54CF3'); + $this->addSql('DROP INDEX IDX_C7440455F7E54CF3 ON client'); + $this->addSql('ALTER TABLE client DROP og_live_id'); + } +} diff --git a/migrations/Version20240820064106.php b/migrations/Version20240820064106.php new file mode 100644 index 0000000..2e387f6 --- /dev/null +++ b/migrations/Version20240820064106.php @@ -0,0 +1,31 @@ +addSql('ALTER TABLE og_live CHANGE `default` 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 og_live CHANGE is_default `default` TINYINT(1) DEFAULT NULL'); + } +} diff --git a/src/Dto/Input/ClientInput.php b/src/Dto/Input/ClientInput.php index 065e7a2..fbc5bf9 100644 --- a/src/Dto/Input/ClientInput.php +++ b/src/Dto/Input/ClientInput.php @@ -5,6 +5,7 @@ namespace App\Dto\Input; use ApiPlatform\Metadata\ApiProperty; use App\Dto\Output\HardwareProfileOutput; use App\Dto\Output\MenuOutput; +use App\Dto\Output\OgLiveOutput; use App\Dto\Output\OrganizationalUnitOutput; use App\Entity\Client; use App\Entity\OgRepository; @@ -76,6 +77,12 @@ final class ClientInput )] public ?HardwareProfileOutput $hardwareProfile = null; + #[Groups(['client:write'])] + #[ApiProperty( + description: 'OgLive del cliente' + )] + public ?OgLiveOutput $ogLive = null; + #[Groups(['client:write'])] #[ApiProperty( description: 'La posición del cliente dentro del aula' @@ -107,6 +114,10 @@ final class ClientInput $this->menu = new MenuOutput($client->getMenu()); } + if ($client->getOgLive()) { + $this->ogLive = new OgLiveOutput($client->getOgLive()); + } + if ($client->getHardwareProfile()) { $this->hardwareProfile = new HardwareProfileOutput($client->getHardwareProfile()); } @@ -126,6 +137,7 @@ final class ClientInput $client->setMac($this->mac); $client->setIp($this->ip); $client->setMenu($this->menu?->getEntity()); + $client->setOgLive($this->ogLive?->getEntity()); $client->setHardwareProfile($this->hardwareProfile?->getEntity()); $client->setPosition($this->position); diff --git a/src/Dto/Input/NetworkSettingsInput.php b/src/Dto/Input/NetworkSettingsInput.php index e32c732..899a495 100644 --- a/src/Dto/Input/NetworkSettingsInput.php +++ b/src/Dto/Input/NetworkSettingsInput.php @@ -2,8 +2,10 @@ namespace App\Dto\Input; +use ApiPlatform\Metadata\ApiProperty; use App\Dto\Output\HardwareProfileOutput; use App\Dto\Output\MenuOutput; +use App\Dto\Output\OgLiveOutput; use App\Entity\HardwareProfile; use App\Entity\Menu; use App\Entity\NetworkSettings; @@ -62,6 +64,9 @@ class NetworkSettingsInput #[Groups(['organizational-unit:write'])] public ?HardwareProfileOutput $hardwareProfile = null; + #[Groups(['organizational-unit:write'])] + public ?OgLiveOutput $ogLive = null; + #[Groups(['organizational-unit:write'])] public ?bool $validation = null; @@ -91,6 +96,10 @@ class NetworkSettingsInput $this->hardwareProfile = new HardwareProfileOutput($networkSettings->getHardwareProfile()); } + if ($networkSettings->getOgLive()) { + $this->ogLive = new OgLiveOutput($networkSettings->getOgLive()); + } + $this->validation = $networkSettings->getValidation(); } @@ -120,6 +129,10 @@ class NetworkSettingsInput $networkSettings->setHardwareProfile($this->hardwareProfile->getEntity()); } + if ($this->ogLive) { + $networkSettings->setOgLive($this->ogLive->getEntity()); + } + $networkSettings->setValidation($this->validation); return $networkSettings; diff --git a/src/Dto/Output/ClientOutput.php b/src/Dto/Output/ClientOutput.php index 97c0b5a..2fe5ff3 100644 --- a/src/Dto/Output/ClientOutput.php +++ b/src/Dto/Output/ClientOutput.php @@ -48,6 +48,10 @@ final class ClientOutput extends AbstractOutput #[ApiProperty(readableLink: true )] public ?HardwareProfileOutput $hardwareProfile = null; + #[Groups(['client:read'])] + #[ApiProperty(readableLink: true )] + public ?OgLiveOutput $ogLive = null; + #[Groups(['client:read'])] public ?array $position = ['x' => 0, 'y' => 0]; @@ -79,6 +83,7 @@ final class ClientOutput extends AbstractOutput $this->menu = $client->getMenu() ? new MenuOutput($client->getMenu()) : null; $this->position = $client->getPosition(); $this->hardwareProfile = $client->getHardwareProfile() ? new HardwareProfileOutput($client->getHardwareProfile()) : null; + $this->ogLive = $client->getOgLive() ? new OgLiveOutput($client->getOgLive()) : null; $this->createdAt = $client->getCreatedAt(); $this->createdBy = $client->getCreatedBy(); } diff --git a/src/Dto/Output/NetworkSettingsOutput.php b/src/Dto/Output/NetworkSettingsOutput.php index 9c0b66d..1b90e03 100644 --- a/src/Dto/Output/NetworkSettingsOutput.php +++ b/src/Dto/Output/NetworkSettingsOutput.php @@ -48,6 +48,9 @@ final class NetworkSettingsOutput extends AbstractOutput #[Groups(['network-settings:read', "organizational-unit:read", "client:read"])] public ?HardwareProfileOutput $hardwareProfile = null; + #[Groups(['network-settings:read', "organizational-unit:read", "client:read"])] + public ?OgLiveOutput $ogLive = null; + #[Groups(['network-settings:read', "organizational-unit:read", "client:read"])] public ?bool $validation = null; @@ -81,6 +84,10 @@ final class NetworkSettingsOutput extends AbstractOutput $this->hardwareProfile = new HardwareProfileOutput($networkSettings->getHardwareProfile()); } + if ($networkSettings->getOgLive()) { + $this->ogLive = new OgLiveOutput($networkSettings->getOgLive()); + } + $this->validation = $networkSettings->getValidation(); $this->createdAt = $networkSettings->getCreatedAt(); $this->createdBy = $networkSettings->getCreatedBy(); diff --git a/src/Dto/Output/OgLiveOutput.php b/src/Dto/Output/OgLiveOutput.php index 28e117e..bc9fc32 100644 --- a/src/Dto/Output/OgLiveOutput.php +++ b/src/Dto/Output/OgLiveOutput.php @@ -20,7 +20,7 @@ final class OgLiveOutput extends AbstractOutput public ?bool $installed = false; #[Groups(['og-live:read'])] - public ?bool $default = false; + public ?bool $isDefault = false; #[Groups(['og-live:read'])] public ?string $downloadUrl = ''; @@ -38,7 +38,7 @@ final class OgLiveOutput extends AbstractOutput $this->name = $ogLive->getName(); $this->synchronized = $ogLive->isSynchronized(); $this->installed = $ogLive->isInstalled(); - $this->default = $ogLive->isDefault(); + $this->isDefault = $ogLive->getIsDefault(); $this->downloadUrl = $ogLive->getDownloadUrl(); $this->createdAt = $ogLive->getCreatedAt(); $this->createdBy = $ogLive->getCreatedBy(); diff --git a/src/Entity/Client.php b/src/Entity/Client.php index 82f5cac..f0aa640 100644 --- a/src/Entity/Client.php +++ b/src/Entity/Client.php @@ -63,6 +63,9 @@ class Client extends AbstractEntity #[ORM\ManyToOne(inversedBy: 'clients')] private ?OgRepository $repository = null; + #[ORM\ManyToOne(inversedBy: 'clients')] + private ?OgLive $ogLive = null; + public function __construct() { parent::__construct(); @@ -254,4 +257,16 @@ class Client extends AbstractEntity return $this; } + + public function getOgLive(): ?OgLive + { + return $this->ogLive; + } + + public function setOgLive(?OgLive $ogLive): static + { + $this->ogLive = $ogLive; + + return $this; + } } diff --git a/src/Entity/NetworkSettings.php b/src/Entity/NetworkSettings.php index de3b9c9..586386a 100644 --- a/src/Entity/NetworkSettings.php +++ b/src/Entity/NetworkSettings.php @@ -63,6 +63,9 @@ class NetworkSettings extends AbstractEntity #[ORM\ManyToOne] private ?OgRepository $repository = null; + #[ORM\ManyToOne] + private ?OgLive $ogLive = null; + public function __construct() { parent::__construct(); @@ -283,4 +286,16 @@ class NetworkSettings extends AbstractEntity return $this; } + + public function getOgLive(): ?OgLive + { + return $this->ogLive; + } + + public function setOgLive(?OgLive $ogLive): static + { + $this->ogLive = $ogLive; + + return $this; + } } diff --git a/src/Entity/OgLive.php b/src/Entity/OgLive.php index 2ecb106..4375882 100644 --- a/src/Entity/OgLive.php +++ b/src/Entity/OgLive.php @@ -3,6 +3,8 @@ namespace App\Entity; use App\Repository\OgLiveRepository; +use Doctrine\Common\Collections\ArrayCollection; +use Doctrine\Common\Collections\Collection; use Doctrine\ORM\Mapping as ORM; use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity; @@ -42,7 +44,19 @@ class OgLive extends AbstractEntity private ?bool $installed = null; #[ORM\Column(nullable: true)] - private ?bool $default = null; + private ?bool $isDefault = null; + + /** + * @var Collection + */ + #[ORM\OneToMany(mappedBy: 'ogLive', targetEntity: Client::class)] + private Collection $clients; + + public function __construct() + { + parent::__construct(); + $this->clients = new ArrayCollection(); + } public function getDownloadUrl(): ?string { @@ -152,14 +166,42 @@ class OgLive extends AbstractEntity return $this; } - public function isDefault(): ?bool + public function getIsDefault(): ?bool { - return $this->default; + return $this->isDefault; } - public function setDefault(?bool $default): static + public function setIsDefault(?bool $isDefault): void { - $this->default = $default; + $this->isDefault = $isDefault; + } + + /** + * @return Collection + */ + public function getClients(): Collection + { + return $this->clients; + } + + public function addClient(Client $client): static + { + if (!$this->clients->contains($client)) { + $this->clients->add($client); + $client->setOgLive($this); + } + + return $this; + } + + public function removeClient(Client $client): static + { + if ($this->clients->removeElement($client)) { + // set the owning side to null (unless already changed) + if ($client->getOgLive() === $this) { + $client->setOgLive(null); + } + } return $this; } -- 2.40.1 From 75ff3ff11c6e37517e7143f58f79abb4b443297e Mon Sep 17 00:00:00 2001 From: Manuel Aranda Date: Tue, 20 Aug 2024 10:31:21 +0200 Subject: [PATCH 014/157] Updated ogBoot components view --- .env | 4 +-- config/api_platform/OgLive.yaml | 4 +-- config/api_platform/PxeBootFile.yaml | 2 +- .../OgBoot/OgLive/GetIsosAction.php | 29 ------------------- .../OgBoot/OgLive/InstallAction.php | 10 ++++--- .../OgBoot/OgLive/SetDefaultAction.php | 10 ++++--- 6 files changed, 17 insertions(+), 42 deletions(-) diff --git a/.env b/.env index 340a072..08e42ca 100644 --- a/.env +++ b/.env @@ -24,8 +24,8 @@ APP_SECRET=e95c7f17da15ce1b03d77ad655379c34 # IMPORTANT: You MUST configure your server version, either here or in config/packages/doctrine.yaml # # DATABASE_URL="sqlite:///%kernel.project_dir%/var/data.db" -#DATABASE_URL="mysql://root:root@127.0.0.1:3336/dimio?serverVersion=8.0.32&charset=utf8mb4" -DATABASE_URL="mysql://root:root@ogcore-database:3306/ogcore?serverVersion=10.11.2-MariaDB&charset=utf8mb4" +DATABASE_URL="mysql://root:root@127.0.0.1:3336/dimio?serverVersion=8.0.32&charset=utf8mb4" +#DATABASE_URL="mysql://root:root@ogcore-database:3306/ogcore?serverVersion=10.11.2-MariaDB&charset=utf8mb4" OG_1_DATABASE_URL="mysql://root:root@ogcore-database:3306/ogcore_old_og?serverVersion=10.11.2-MariaDB&charset=utf8mb4" #DATABASE_URL="postgresql://app:!ChangeMe!@127.0.0.1:5432/app?serverVersion=16&charset=utf8" diff --git a/config/api_platform/OgLive.yaml b/config/api_platform/OgLive.yaml index 0504100..4239240 100644 --- a/config/api_platform/OgLive.yaml +++ b/config/api_platform/OgLive.yaml @@ -23,7 +23,7 @@ resources: ApiPlatform\Metadata\Post: ~ ApiPlatform\Metadata\Delete: ~ - get_collection: + get_collection_oglives: shortName: OgLive Server description: Get collection of OgLive class: ApiPlatform\Metadata\GetCollection @@ -32,7 +32,7 @@ resources: uriTemplate: /og-lives/server/get-collection controller: App\Controller\OgBoot\OgLive\GetCollectionAction - get: + get_oglive: shortName: OgLive Server description: Get OgLive class: ApiPlatform\Metadata\Get diff --git a/config/api_platform/PxeBootFile.yaml b/config/api_platform/PxeBootFile.yaml index f8cafaa..62775c5 100644 --- a/config/api_platform/PxeBootFile.yaml +++ b/config/api_platform/PxeBootFile.yaml @@ -23,7 +23,7 @@ resources: ApiPlatform\Metadata\Post: ~ ApiPlatform\Metadata\Delete: ~ - get_collection: + get_all: shortName: PxeBootFile Server description: Get collection of PxeBootFile class: ApiPlatform\Metadata\GetCollection diff --git a/src/Controller/OgBoot/OgLive/GetIsosAction.php b/src/Controller/OgBoot/OgLive/GetIsosAction.php index 436f9d4..2ad93b4 100644 --- a/src/Controller/OgBoot/OgLive/GetIsosAction.php +++ b/src/Controller/OgBoot/OgLive/GetIsosAction.php @@ -38,35 +38,6 @@ class GetIsosAction extends AbstractOgLiveController $data = json_decode($response->getContent(), true); - if (!empty($data['downloads'])) { - $ogLivesInserted = $this->insertOglives($data); - } - return new JsonResponse( data: [ 'data' => $data, 'ogLivesInserted' => $ogLivesInserted], status: Response::HTTP_OK); } - - public function insertOglives(array $data): int - { - $count = 0; - - foreach ($data['downloads'] as $ogLive ) { - $ogLiveEntity = $this->entityManager->getRepository(OgLive::class)->findOneBy(['name' => $ogLive['filename']]); - - if ($ogLiveEntity) { - continue; - } - - $ogLiveEntity = new OgLive(); - $ogLiveEntity->setName($ogLive['filename']); - $ogLiveEntity->setInstalled($ogLive['installed']); - $ogLiveEntity->setFilename($ogLive['filename']); - - $this->entityManager->persist($ogLiveEntity); - $count++; - } - - $this->entityManager->flush(); - - return $count; - } } \ No newline at end of file diff --git a/src/Controller/OgBoot/OgLive/InstallAction.php b/src/Controller/OgBoot/OgLive/InstallAction.php index b2bcdac..7868240 100644 --- a/src/Controller/OgBoot/OgLive/InstallAction.php +++ b/src/Controller/OgBoot/OgLive/InstallAction.php @@ -36,12 +36,14 @@ class InstallAction extends AbstractOgLiveController ] ]); } catch (TransportExceptionInterface $e) { - return new JsonResponse( data: 'An error occurred', status: Response::HTTP_INTERNAL_SERVER_ERROR); + return new JsonResponse( data: $e->getMessage(), status: Response::HTTP_INTERNAL_SERVER_ERROR); } - $data->setInstalled(true); - $entityManager->persist($data); - $entityManager->flush(); + if ($response->getStatusCode() === Response::HTTP_OK) { + $data->setInstalled(true); + $entityManager->persist($data); + $entityManager->flush(); + } $data = json_decode($response->getContent(), true); diff --git a/src/Controller/OgBoot/OgLive/SetDefaultAction.php b/src/Controller/OgBoot/OgLive/SetDefaultAction.php index ffdf033..3864e67 100644 --- a/src/Controller/OgBoot/OgLive/SetDefaultAction.php +++ b/src/Controller/OgBoot/OgLive/SetDefaultAction.php @@ -26,7 +26,7 @@ class SetDefaultAction extends AbstractOgLiveController public function __invoke(OgLive $data, HttpClientInterface $httpClient, EntityManagerInterface $entityManager): JsonResponse { try { - $response = $httpClient->request('POST', $this->ogBootApiUrl.'/ogboot/v1/oglives/default', [ + $response = $httpClient->request('PUT', $this->ogBootApiUrl.'/ogboot/v1/oglives/default', [ 'headers' => [ 'accept' => 'application/json', ], @@ -38,9 +38,11 @@ class SetDefaultAction extends AbstractOgLiveController return new JsonResponse( data: 'An error occurred', status: Response::HTTP_INTERNAL_SERVER_ERROR); } - $data->setDefault(true); - $entityManager->persist($data); - $entityManager->flush(); + if ($response->getStatusCode() === Response::HTTP_OK) { + $data->setIsDefault(true); + $entityManager->persist($data); + $entityManager->flush(); + } $data = json_decode($response->getContent(), true); -- 2.40.1 From 8677fc0f9452783ebb95f8a18e33cd5cd0a034be Mon Sep 17 00:00:00 2001 From: Manuel Aranda Date: Tue, 20 Aug 2024 11:46:31 +0200 Subject: [PATCH 015/157] Updated PXEbootFiles input --- src/Controller/OgBoot/OgLive/SyncAction.php | 8 ++++++++ src/Dto/Input/PxeBootFileInput.php | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) create mode 100644 src/Controller/OgBoot/OgLive/SyncAction.php diff --git a/src/Controller/OgBoot/OgLive/SyncAction.php b/src/Controller/OgBoot/OgLive/SyncAction.php new file mode 100644 index 0000000..edaee84 --- /dev/null +++ b/src/Controller/OgBoot/OgLive/SyncAction.php @@ -0,0 +1,8 @@ +setTemplate($this->template); + $bootFile->setTemplate($this->template->getEntity()); foreach ($this->clients as $client) { $clientsToAdd[] = $client->getEntity(); -- 2.40.1 From 8e9db51cbc6ea6681106fef98d37f611c598334c Mon Sep 17 00:00:00 2001 From: Manuel Aranda Date: Tue, 20 Aug 2024 12:10:04 +0200 Subject: [PATCH 016/157] Updated .env --- .env | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.env b/.env index 08e42ca..340a072 100644 --- a/.env +++ b/.env @@ -24,8 +24,8 @@ APP_SECRET=e95c7f17da15ce1b03d77ad655379c34 # IMPORTANT: You MUST configure your server version, either here or in config/packages/doctrine.yaml # # DATABASE_URL="sqlite:///%kernel.project_dir%/var/data.db" -DATABASE_URL="mysql://root:root@127.0.0.1:3336/dimio?serverVersion=8.0.32&charset=utf8mb4" -#DATABASE_URL="mysql://root:root@ogcore-database:3306/ogcore?serverVersion=10.11.2-MariaDB&charset=utf8mb4" +#DATABASE_URL="mysql://root:root@127.0.0.1:3336/dimio?serverVersion=8.0.32&charset=utf8mb4" +DATABASE_URL="mysql://root:root@ogcore-database:3306/ogcore?serverVersion=10.11.2-MariaDB&charset=utf8mb4" OG_1_DATABASE_URL="mysql://root:root@ogcore-database:3306/ogcore_old_og?serverVersion=10.11.2-MariaDB&charset=utf8mb4" #DATABASE_URL="postgresql://app:!ChangeMe!@127.0.0.1:5432/app?serverVersion=16&charset=utf8" -- 2.40.1 From 750e36ec99c1d0226b6b340d71acbac3c66680f3 Mon Sep 17 00:00:00 2001 From: Manuel Aranda Date: Wed, 21 Aug 2024 09:17:48 +0200 Subject: [PATCH 017/157] refs #601. Integration API pxe-boot-file --- config/api_platform/OgLive.yaml | 12 +++- migrations/Version20240821065158.php | 31 +++++++++ src/Controller/OgBoot/OgLive/SyncAction.php | 66 ++++++++++++++++++- .../OgBoot/OgLive/UninstallAction.php | 16 +++-- .../PxeBootFile/GetCollectionAction.php | 2 +- .../OgBoot/PxeTemplate/PostAction.php | 8 ++- src/Dto/Output/PxeTemplateOutput.php | 2 +- src/Entity/PxeTemplate.php | 2 +- .../OgBoot/PxeBootFile/PostService.php | 21 ++++-- src/State/Processor/PxeBootFileProcessor.php | 8 ++- 10 files changed, 146 insertions(+), 22 deletions(-) create mode 100644 migrations/Version20240821065158.php diff --git a/config/api_platform/OgLive.yaml b/config/api_platform/OgLive.yaml index 4239240..d69ddad 100644 --- a/config/api_platform/OgLive.yaml +++ b/config/api_platform/OgLive.yaml @@ -23,6 +23,13 @@ resources: ApiPlatform\Metadata\Post: ~ ApiPlatform\Metadata\Delete: ~ + sync: + class: ApiPlatform\Metadata\Post + method: POST + input: false + uriTemplate: /og-lives/sync + controller: App\Controller\OgBoot\OgLive\SyncAction + get_collection_oglives: shortName: OgLive Server description: Get collection of OgLive @@ -80,9 +87,10 @@ resources: uninstall: shortName: OgLive Server description: Uninstall OgLive - class: ApiPlatform\Metadata\Get - method: GET + class: ApiPlatform\Metadata\Post + method: POST input: false + output: false uriTemplate: /og-lives/server/{uuid}/uninstall controller: App\Controller\OgBoot\OgLive\UninstallAction diff --git a/migrations/Version20240821065158.php b/migrations/Version20240821065158.php new file mode 100644 index 0000000..dfcd85a --- /dev/null +++ b/migrations/Version20240821065158.php @@ -0,0 +1,31 @@ +addSql('ALTER TABLE pxe_template CHANGE template_content template_content LONGTEXT NOT NULL'); + } + + public function down(Schema $schema): void + { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql('ALTER TABLE pxe_template CHANGE template_content template_content TINYTEXT NOT NULL'); + } +} diff --git a/src/Controller/OgBoot/OgLive/SyncAction.php b/src/Controller/OgBoot/OgLive/SyncAction.php index edaee84..6171cc8 100644 --- a/src/Controller/OgBoot/OgLive/SyncAction.php +++ b/src/Controller/OgBoot/OgLive/SyncAction.php @@ -2,7 +2,69 @@ namespace App\Controller\OgBoot\OgLive; -class SyncAction -{ +use App\Controller\OgBoot\AbstractOgLiveController; +use App\Entity\OgLive; +use Doctrine\ORM\EntityManagerInterface; +use Symfony\Component\HttpFoundation\JsonResponse; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpKernel\Attribute\AsController; +use Symfony\Contracts\HttpClient\Exception\ClientExceptionInterface; +use Symfony\Contracts\HttpClient\Exception\RedirectionExceptionInterface; +use Symfony\Contracts\HttpClient\Exception\ServerExceptionInterface; +use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface; +use Symfony\Contracts\HttpClient\HttpClientInterface; +#[AsController] +class SyncAction extends AbstractOgLiveController +{ + /** + * @throws TransportExceptionInterface + * @throws ServerExceptionInterface + * @throws RedirectionExceptionInterface + * @throws ClientExceptionInterface + */ + public function __invoke(HttpClientInterface $httpClient, EntityManagerInterface $entityManager): JsonResponse + { + try { + $response = $httpClient->request('GET', $this->ogBootApiUrl.'/ogboot/v1/oglives', [ + 'headers' => [ + 'accept' => 'application/json', + ], + ]); + + } catch (TransportExceptionInterface $e) { + return new JsonResponse( data: 'An error occurred', status: Response::HTTP_INTERNAL_SERVER_ERROR); + } + + $data = json_decode($response->getContent(), true); + + foreach ($data['installed_ogLives'] as $ogLive) { + $ogLiveEntity = $this->entityManager->getRepository(OgLive::class)->findOneBy(['checksum' => $ogLive['id']]); + if ($ogLiveEntity) { + $ogLiveEntity->setName($ogLive['filename']); + $ogLiveEntity->setInstalled(true); + $ogLiveEntity->setArchitecture($ogLive['architecture']); + $ogLiveEntity->setKernel($ogLive['kernel']); + $ogLiveEntity->setRevision($ogLive['revision']); + $ogLiveEntity->setDirectory($ogLive['directory']); + $ogLiveEntity->setChecksum($ogLive['id']); + $this->entityManager->persist($ogLiveEntity); + } else { + $ogLiveEntity = new OgLive(); + $ogLiveEntity->setName($ogLive['filename']); + $ogLiveEntity->setInstalled(true); + $ogLiveEntity->setArchitecture($ogLive['architecture']); + $ogLiveEntity->setKernel($ogLive['kernel']); + $ogLiveEntity->setRevision($ogLive['revision']); + $ogLiveEntity->setDirectory($ogLive['directory']); + $ogLiveEntity->setChecksum($ogLive['id']); + } + $this->entityManager->persist($ogLiveEntity); + + } + + $this->entityManager->flush(); + + return new JsonResponse( data: $data, status: Response::HTTP_OK); + } } \ No newline at end of file diff --git a/src/Controller/OgBoot/OgLive/UninstallAction.php b/src/Controller/OgBoot/OgLive/UninstallAction.php index e2981ec..ac9f047 100644 --- a/src/Controller/OgBoot/OgLive/UninstallAction.php +++ b/src/Controller/OgBoot/OgLive/UninstallAction.php @@ -4,6 +4,7 @@ namespace App\Controller\OgBoot\OgLive; use App\Controller\OgBoot\AbstractOgLiveController; use App\Entity\OgLive; +use Doctrine\ORM\EntityManagerInterface; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\Attribute\AsController; @@ -17,12 +18,12 @@ use Symfony\Contracts\HttpClient\HttpClientInterface; class UninstallAction extends AbstractOgLiveController { /** - * @throws TransportExceptionInterface * @throws ServerExceptionInterface * @throws RedirectionExceptionInterface * @throws ClientExceptionInterface + * @throws TransportExceptionInterface */ - public function __invoke(OgLive $data, HttpClientInterface $httpClient): JsonResponse + public function __invoke(OgLive $data, HttpClientInterface $httpClient, EntityManagerInterface $entityManager): JsonResponse { try { $response = $httpClient->request('DELETE', $this->ogBootApiUrl.'/ogboot/v1/oglives/'.$data->getChecksum(), [ @@ -30,12 +31,17 @@ class UninstallAction extends AbstractOgLiveController 'accept' => 'application/json', ], ]); + } catch (TransportExceptionInterface $e) { - return new JsonResponse( data: 'An error occurred', status: Response::HTTP_INTERNAL_SERVER_ERROR); + return new JsonResponse( data: $e->getMessage(), status: Response::HTTP_INTERNAL_SERVER_ERROR); } - $data = json_decode($response->getContent(), true); + if ($response->getStatusCode() === Response::HTTP_OK) { + $data->setInstalled(false); + $entityManager->persist($data); + $entityManager->flush(); + } - return new JsonResponse( data: $data, status: Response::HTTP_OK); + return new JsonResponse(status: Response::HTTP_OK); } } \ No newline at end of file diff --git a/src/Controller/OgBoot/PxeBootFile/GetCollectionAction.php b/src/Controller/OgBoot/PxeBootFile/GetCollectionAction.php index 1b07fc6..f758745 100644 --- a/src/Controller/OgBoot/PxeBootFile/GetCollectionAction.php +++ b/src/Controller/OgBoot/PxeBootFile/GetCollectionAction.php @@ -24,7 +24,7 @@ class GetCollectionAction extends AbstractOgLiveController * @throws RedirectionExceptionInterface * @throws ClientExceptionInterface */ - public function __invoke(PxeBootFile $data, HttpClientInterface $httpClient): JsonResponse + public function __invoke(HttpClientInterface $httpClient): JsonResponse { try { $response = $httpClient->request('GET', $this->ogBootApiUrl.'/ogboot/v1/pxes', [ diff --git a/src/Controller/OgBoot/PxeTemplate/PostAction.php b/src/Controller/OgBoot/PxeTemplate/PostAction.php index 6daf8d8..d07a016 100644 --- a/src/Controller/OgBoot/PxeTemplate/PostAction.php +++ b/src/Controller/OgBoot/PxeTemplate/PostAction.php @@ -38,9 +38,11 @@ class PostAction extends AbstractOgLiveController ], ]); - $data->setSynchronized(true); - $entityManager->persist($data); - $entityManager->flush(); + if ($response->getStatusCode() === Response::HTTP_OK) { + $data->setSynchronized(true); + $entityManager->persist($data); + $entityManager->flush(); + } $data = json_decode($response->getContent(), true); return new JsonResponse($data, Response::HTTP_OK); diff --git a/src/Dto/Output/PxeTemplateOutput.php b/src/Dto/Output/PxeTemplateOutput.php index fd7b5e9..e66ba17 100644 --- a/src/Dto/Output/PxeTemplateOutput.php +++ b/src/Dto/Output/PxeTemplateOutput.php @@ -10,7 +10,7 @@ use Symfony\Component\Serializer\Annotation\Groups; #[Get(shortName: 'PxeTemplate')] final class PxeTemplateOutput extends AbstractOutput { - #[Groups(['pxe-template:read'])] + #[Groups(['pxe-template:read', 'pxe-boot-file:read'])] public string $name; #[Groups(['pxe-template:read'])] diff --git a/src/Entity/PxeTemplate.php b/src/Entity/PxeTemplate.php index b2d3cfb..39a79b1 100644 --- a/src/Entity/PxeTemplate.php +++ b/src/Entity/PxeTemplate.php @@ -15,7 +15,7 @@ class PxeTemplate extends AbstractEntity use NameableTrait; use SynchronizedTrait; - #[ORM\Column(type: Types::TEXT, length: 255)] + #[ORM\Column(type: Types::TEXT)] private ?string $templateContent = null; public function getTemplateContent(): ?string diff --git a/src/Service/OgBoot/PxeBootFile/PostService.php b/src/Service/OgBoot/PxeBootFile/PostService.php index cc19f4d..e836af2 100644 --- a/src/Service/OgBoot/PxeBootFile/PostService.php +++ b/src/Service/OgBoot/PxeBootFile/PostService.php @@ -35,31 +35,40 @@ readonly class PostService foreach ($bootFile->getClients() as $client) { $data = [ - 'template_name' => $bootFile->getTemplate()->getName(), + 'template_name' => 'pxe_default', 'mac' => $client->getMac(), + 'lang' => 'es_ES.UTF_8', 'ip' => $client->getIp(), - 'server_ip' => '', + 'server_ip' => '92.168.2.1', 'router' => $client->getOrganizationalUnit()->getNetworkSettings()->getRouter(), 'netmask' => $client->getOrganizationalUnit()->getNetworkSettings()->getNetmask(), 'computer_name' => $client->getName(), 'netiface' => $client->getNetiface(), 'group' => $client->getOrganizationalUnit()->getName(), - 'ogrepo' => $client->getRepository() ? $client->getRepository()->getIpAddress() : $client->getOrganizationalUnit()->getNetworkSettings()->getRepository()->getIpAddress(), - 'oglive' => '', + 'ogrepo' => $client->getRepository() ? $client->getRepository()->getIpAddress() : '192.168.2.1', + 'oglive' => '127.0.0.1', + 'oglog' => '192.168.2.1', + 'ogshare' => '192.168.2.1', + 'oglivedir' => 'ogLive', + 'ogprof' => 'false', + 'hardprofile' => '', + 'ogntp' => $client->getOrganizationalUnit()->getNetworkSettings()->getNtp(), 'ogdns' => $client->getOrganizationalUnit()->getNetworkSettings()->getDns(), 'ogProxy' => $client->getOrganizationalUnit()->getNetworkSettings()->getProxy(), - 'ogunit' => '' + 'ogunit' => '', + 'resolution' => '768' ]; try { $response = $httpClient->request('POST', $this->ogBootApiUrl.'/ogboot/v1/pxes', [ 'headers' => [ 'accept' => 'application/json', + 'Content-Type' => 'application/json', ], 'json' => $data ]); } catch (TransportExceptionInterface $e) { - return new JsonResponse( data: 'An error occurred', status: Response::HTTP_INTERNAL_SERVER_ERROR); + return new JsonResponse( data: $e->getMessage(), status: Response::HTTP_INTERNAL_SERVER_ERROR); } return json_decode($response->getContent(), true); diff --git a/src/State/Processor/PxeBootFileProcessor.php b/src/State/Processor/PxeBootFileProcessor.php index 9b03645..b53e55b 100644 --- a/src/State/Processor/PxeBootFileProcessor.php +++ b/src/State/Processor/PxeBootFileProcessor.php @@ -13,6 +13,7 @@ use App\Dto\Input\PxeBootFileInput; use App\Dto\Output\PxeBootFileOutput; use App\Repository\PxeBootFileRepository; use App\Service\OgBoot\PxeBootFile\PostService; +use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface; readonly class PxeBootFileProcessor implements ProcessorInterface { @@ -41,6 +42,7 @@ readonly class PxeBootFileProcessor implements ProcessorInterface /** * @throws \Exception + * @throws TransportExceptionInterface */ private function processCreateOrUpdate($data, Operation $operation, array $uriVariables = [], array $context = []): PxeBootFileOutput { @@ -57,7 +59,11 @@ readonly class PxeBootFileProcessor implements ProcessorInterface $this->validator->validate($pxeTemplate); $this->bootFileRepository->save($pxeTemplate); - $this->postService->__invoke($pxeTemplate); + try { + $this->postService->__invoke($pxeTemplate); + } catch (\Exception $e) { + + } return new PxeBootFileOutput($pxeTemplate); } -- 2.40.1 From 6afce69f149948ffcfb5a75092bcb33296846240 Mon Sep 17 00:00:00 2001 From: Manuel Aranda Date: Fri, 23 Aug 2024 09:10:15 +0200 Subject: [PATCH 018/157] refs #658. Refactor endpoints --- config/api_platform/OgLive.yaml | 1 - .../OgBoot/AbstractOgBootController.php | 56 +++++++++++++++++++ .../OgBoot/AbstractOgLiveController.php | 20 ------- src/Controller/OgBoot/OgLive/GetAction.php | 19 +++---- .../OgBoot/OgLive/GetCollectionAction.php | 19 ++----- .../OgBoot/OgLive/GetDefaultAction.php | 18 ++---- .../OgBoot/OgLive/GetIsosAction.php | 20 ++----- .../OgBoot/OgLive/InstallAction.php | 37 ++++++------ .../OgBoot/OgLive/SetDefaultAction.php | 36 ++++++------ src/Controller/OgBoot/OgLive/SyncAction.php | 56 ++++++++----------- .../OgBoot/OgLive/UninstallAction.php | 26 ++++----- .../PxeBootFile/GetCollectionAction.php | 18 ++---- .../OgBoot/PxeTemplate/DeleteAction.php | 14 +++-- .../OgBoot/PxeTemplate/GetAction.php | 4 +- .../PxeTemplate/GetCollectionAction.php | 4 +- .../OgBoot/PxeTemplate/PostAction.php | 4 +- 16 files changed, 161 insertions(+), 191 deletions(-) create mode 100644 src/Controller/OgBoot/AbstractOgBootController.php delete mode 100644 src/Controller/OgBoot/AbstractOgLiveController.php diff --git a/config/api_platform/OgLive.yaml b/config/api_platform/OgLive.yaml index d69ddad..e24c61b 100644 --- a/config/api_platform/OgLive.yaml +++ b/config/api_platform/OgLive.yaml @@ -90,7 +90,6 @@ resources: class: ApiPlatform\Metadata\Post method: POST input: false - output: false uriTemplate: /og-lives/server/{uuid}/uninstall controller: App\Controller\OgBoot\OgLive\UninstallAction diff --git a/src/Controller/OgBoot/AbstractOgBootController.php b/src/Controller/OgBoot/AbstractOgBootController.php new file mode 100644 index 0000000..7e96b1c --- /dev/null +++ b/src/Controller/OgBoot/AbstractOgBootController.php @@ -0,0 +1,56 @@ + [ + 'accept' => 'application/json', + 'Content-Type' => 'application/json' + ], + ]); + + try { + $response = $httpClient->request($method, $url, $params); + + return json_decode($response->getContent(), true); + } catch (ClientExceptionInterface | ServerExceptionInterface $e) { + $response = $e->getResponse(); + $content = json_decode($response->getContent(false), true); + throw new HttpException($response->getStatusCode(), $content['error'] ?? 'An error occurred'); + } catch (TransportExceptionInterface $e) { + throw new HttpException(Response::HTTP_INTERNAL_SERVER_ERROR, $e->getMessage()); + } + } +} diff --git a/src/Controller/OgBoot/AbstractOgLiveController.php b/src/Controller/OgBoot/AbstractOgLiveController.php deleted file mode 100644 index 89cbb0c..0000000 --- a/src/Controller/OgBoot/AbstractOgLiveController.php +++ /dev/null @@ -1,20 +0,0 @@ -request('GET', $this->ogBootApiUrl.'/ogboot/v1/oglives/'.$data->getChecksum(), [ - 'headers' => [ - 'accept' => 'application/json', - ], - ]); - } catch (TransportExceptionInterface $e) { - return new JsonResponse( data: 'An error occurred', status: Response::HTTP_INTERNAL_SERVER_ERROR); + if (!$data->getChecksum()) { + throw new ValidatorException('Checksum is required'); } - $data = json_decode($response->getContent(), true); + $content = $this->createRequest($httpClient, 'GET', $this->ogBootApiUrl.'/ogboot/v1/oglives/'.$data->getChecksum()); - return new JsonResponse( data: $data, status: Response::HTTP_OK); + return new JsonResponse(data: $content, status: Response::HTTP_OK); } } \ No newline at end of file diff --git a/src/Controller/OgBoot/OgLive/GetCollectionAction.php b/src/Controller/OgBoot/OgLive/GetCollectionAction.php index 236a7fc..930b237 100644 --- a/src/Controller/OgBoot/OgLive/GetCollectionAction.php +++ b/src/Controller/OgBoot/OgLive/GetCollectionAction.php @@ -2,10 +2,11 @@ namespace App\Controller\OgBoot\OgLive; -use App\Controller\OgBoot\AbstractOgLiveController; +use App\Controller\OgBoot\AbstractOgBootController; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\Attribute\AsController; +use Symfony\Component\Validator\Exception\ValidatorException; use Symfony\Contracts\HttpClient\Exception\ClientExceptionInterface; use Symfony\Contracts\HttpClient\Exception\RedirectionExceptionInterface; use Symfony\Contracts\HttpClient\Exception\ServerExceptionInterface; @@ -13,7 +14,7 @@ use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface; use Symfony\Contracts\HttpClient\HttpClientInterface; #[AsController] -class GetCollectionAction extends AbstractOgLiveController +class GetCollectionAction extends AbstractOgBootController { /** * @throws TransportExceptionInterface @@ -23,18 +24,8 @@ class GetCollectionAction extends AbstractOgLiveController */ public function __invoke(HttpClientInterface $httpClient): JsonResponse { - try { - $response = $httpClient->request('GET', $this->ogBootApiUrl.'/ogboot/v1/oglives', [ - 'headers' => [ - 'accept' => 'application/json', - ], - ]); - } catch (TransportExceptionInterface $e) { - return new JsonResponse( data: 'An error occurred', status: Response::HTTP_INTERNAL_SERVER_ERROR); - } + $content = $this->createRequest($httpClient, 'GET', $this->ogBootApiUrl.'/ogboot/v1/oglives'); - $data = json_decode($response->getContent(), true); - - return new JsonResponse( data: $data, status: Response::HTTP_OK); + return new JsonResponse(data: $content, status: Response::HTTP_OK); } } \ No newline at end of file diff --git a/src/Controller/OgBoot/OgLive/GetDefaultAction.php b/src/Controller/OgBoot/OgLive/GetDefaultAction.php index 9d0ff93..94f3456 100644 --- a/src/Controller/OgBoot/OgLive/GetDefaultAction.php +++ b/src/Controller/OgBoot/OgLive/GetDefaultAction.php @@ -2,7 +2,7 @@ namespace App\Controller\OgBoot\OgLive; -use App\Controller\OgBoot\AbstractOgLiveController; +use App\Controller\OgBoot\AbstractOgBootController; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\Attribute\AsController; @@ -13,7 +13,7 @@ use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface; use Symfony\Contracts\HttpClient\HttpClientInterface; #[AsController] -class GetDefaultAction extends AbstractOgLiveController +class GetDefaultAction extends AbstractOgBootController { /** * @throws TransportExceptionInterface @@ -23,18 +23,8 @@ class GetDefaultAction extends AbstractOgLiveController */ public function __invoke(HttpClientInterface $httpClient): JsonResponse { - try { - $response = $httpClient->request('GET', $this->ogBootApiUrl.'/ogboot/v1/oglives/default', [ - 'headers' => [ - 'accept' => 'application/json', - ], - ]); - } catch (TransportExceptionInterface $e) { - return new JsonResponse( data: 'An error occurred', status: Response::HTTP_INTERNAL_SERVER_ERROR); - } + $content = $this->createRequest($httpClient, 'GET', $this->ogBootApiUrl.'/ogboot/v1/oglives/default'); - $data = json_decode($response->getContent(), true); - - return new JsonResponse( data: $data, status: Response::HTTP_OK); + return new JsonResponse(status: Response::HTTP_OK); } } \ No newline at end of file diff --git a/src/Controller/OgBoot/OgLive/GetIsosAction.php b/src/Controller/OgBoot/OgLive/GetIsosAction.php index 2ad93b4..90ddb19 100644 --- a/src/Controller/OgBoot/OgLive/GetIsosAction.php +++ b/src/Controller/OgBoot/OgLive/GetIsosAction.php @@ -2,7 +2,7 @@ namespace App\Controller\OgBoot\OgLive; -use App\Controller\OgBoot\AbstractOgLiveController; +use App\Controller\OgBoot\AbstractOgBootController; use App\Entity\OgLive; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Response; @@ -14,7 +14,7 @@ use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface; use Symfony\Contracts\HttpClient\HttpClientInterface; #[AsController] -class GetIsosAction extends AbstractOgLiveController +class GetIsosAction extends AbstractOgBootController { /** * @throws TransportExceptionInterface @@ -24,20 +24,8 @@ class GetIsosAction extends AbstractOgLiveController */ public function __invoke(HttpClientInterface $httpClient): JsonResponse { - $ogLivesInserted = 0; + $content = $this->createRequest($httpClient, 'GET', $this->ogBootApiUrl.'/ogboot/v1/oglives/isos'); - try { - $response = $httpClient->request('GET', $this->ogBootApiUrl.'/ogboot/v1/oglives/isos', [ - 'headers' => [ - 'accept' => 'application/json', - ], - ]); - } catch (TransportExceptionInterface $e) { - return new JsonResponse( data: 'An error occurred', status: Response::HTTP_INTERNAL_SERVER_ERROR); - } - - $data = json_decode($response->getContent(), true); - - return new JsonResponse( data: [ 'data' => $data, 'ogLivesInserted' => $ogLivesInserted], status: Response::HTTP_OK); + return new JsonResponse(data: $content, status: Response::HTTP_OK); } } \ No newline at end of file diff --git a/src/Controller/OgBoot/OgLive/InstallAction.php b/src/Controller/OgBoot/OgLive/InstallAction.php index 7868240..e9b871c 100644 --- a/src/Controller/OgBoot/OgLive/InstallAction.php +++ b/src/Controller/OgBoot/OgLive/InstallAction.php @@ -2,12 +2,13 @@ namespace App\Controller\OgBoot\OgLive; -use App\Controller\OgBoot\AbstractOgLiveController; +use App\Controller\OgBoot\AbstractOgBootController; use App\Entity\OgLive; use Doctrine\ORM\EntityManagerInterface; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\Attribute\AsController; +use Symfony\Component\Validator\Exception\ValidatorException; use Symfony\Contracts\HttpClient\Exception\ClientExceptionInterface; use Symfony\Contracts\HttpClient\Exception\RedirectionExceptionInterface; use Symfony\Contracts\HttpClient\Exception\ServerExceptionInterface; @@ -15,7 +16,7 @@ use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface; use Symfony\Contracts\HttpClient\HttpClientInterface; #[AsController] -class InstallAction extends AbstractOgLiveController +class InstallAction extends AbstractOgBootController { /** * @throws TransportExceptionInterface @@ -25,28 +26,22 @@ class InstallAction extends AbstractOgLiveController */ public function __invoke(OgLive $data, HttpClientInterface $httpClient, EntityManagerInterface $entityManager): JsonResponse { - try { - $response = $httpClient->request('POST', $this->ogBootApiUrl.'/ogboot/v1/oglives/install', [ - 'headers' => [ - 'accept' => 'application/json', - 'Content-Type' => 'application/json', - ], - 'json' => [ - 'url' => $data->getDownloadUrl() - ] - ]); - } catch (TransportExceptionInterface $e) { - return new JsonResponse( data: $e->getMessage(), status: Response::HTTP_INTERNAL_SERVER_ERROR); + if (!$data->getDownloadUrl()) { + throw new ValidatorException('Download URL is required'); } - if ($response->getStatusCode() === Response::HTTP_OK) { - $data->setInstalled(true); - $entityManager->persist($data); - $entityManager->flush(); - } + $params = [ + 'json' => [ + 'url' => $data->getDownloadUrl() + ] + ]; - $data = json_decode($response->getContent(), true); + $content = $this->createRequest($httpClient, 'POST', $this->ogBootApiUrl.'/ogboot/v1/oglives/install', $params); - return new JsonResponse( data: $data, status: Response::HTTP_OK); + $data->setInstalled(true); + $entityManager->persist($data); + $entityManager->flush(); + + return new JsonResponse(data: $content, status: Response::HTTP_OK); } } \ No newline at end of file diff --git a/src/Controller/OgBoot/OgLive/SetDefaultAction.php b/src/Controller/OgBoot/OgLive/SetDefaultAction.php index 3864e67..b0d9698 100644 --- a/src/Controller/OgBoot/OgLive/SetDefaultAction.php +++ b/src/Controller/OgBoot/OgLive/SetDefaultAction.php @@ -2,12 +2,13 @@ namespace App\Controller\OgBoot\OgLive; -use App\Controller\OgBoot\AbstractOgLiveController; +use App\Controller\OgBoot\AbstractOgBootController; use App\Entity\OgLive; use Doctrine\ORM\EntityManagerInterface; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\Attribute\AsController; +use Symfony\Component\Validator\Exception\ValidatorException; use Symfony\Contracts\HttpClient\Exception\ClientExceptionInterface; use Symfony\Contracts\HttpClient\Exception\RedirectionExceptionInterface; use Symfony\Contracts\HttpClient\Exception\ServerExceptionInterface; @@ -15,7 +16,7 @@ use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface; use Symfony\Contracts\HttpClient\HttpClientInterface; #[AsController] -class SetDefaultAction extends AbstractOgLiveController +class SetDefaultAction extends AbstractOgBootController { /** * @throws TransportExceptionInterface @@ -25,27 +26,22 @@ class SetDefaultAction extends AbstractOgLiveController */ public function __invoke(OgLive $data, HttpClientInterface $httpClient, EntityManagerInterface $entityManager): JsonResponse { - try { - $response = $httpClient->request('PUT', $this->ogBootApiUrl.'/ogboot/v1/oglives/default', [ - 'headers' => [ - 'accept' => 'application/json', - ], - 'json' => [ - 'checksum' => $data->getChecksum() - ] - ]); - } catch (TransportExceptionInterface $e) { - return new JsonResponse( data: 'An error occurred', status: Response::HTTP_INTERNAL_SERVER_ERROR); + if (!$data->getChecksum()) { + throw new ValidatorException('Checksum URL is required'); } - if ($response->getStatusCode() === Response::HTTP_OK) { - $data->setIsDefault(true); - $entityManager->persist($data); - $entityManager->flush(); - } + $params = [ + 'json' => [ + 'checksum' => $data->getChecksum() + ] + ]; - $data = json_decode($response->getContent(), true); + $content = $this->createRequest($httpClient, 'PUT', $this->ogBootApiUrl.'/ogboot/v1/oglives/default', $params); - return new JsonResponse( data: $data, status: Response::HTTP_OK); + $data->setIsDefault(true); + $entityManager->persist($data); + $entityManager->flush(); + + return new JsonResponse(status: Response::HTTP_OK); } } \ No newline at end of file diff --git a/src/Controller/OgBoot/OgLive/SyncAction.php b/src/Controller/OgBoot/OgLive/SyncAction.php index 6171cc8..dfabadb 100644 --- a/src/Controller/OgBoot/OgLive/SyncAction.php +++ b/src/Controller/OgBoot/OgLive/SyncAction.php @@ -2,12 +2,13 @@ namespace App\Controller\OgBoot\OgLive; -use App\Controller\OgBoot\AbstractOgLiveController; +use App\Controller\OgBoot\AbstractOgBootController; use App\Entity\OgLive; use Doctrine\ORM\EntityManagerInterface; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\Attribute\AsController; +use Symfony\Component\Validator\Exception\ValidatorException; use Symfony\Contracts\HttpClient\Exception\ClientExceptionInterface; use Symfony\Contracts\HttpClient\Exception\RedirectionExceptionInterface; use Symfony\Contracts\HttpClient\Exception\ServerExceptionInterface; @@ -15,7 +16,7 @@ use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface; use Symfony\Contracts\HttpClient\HttpClientInterface; #[AsController] -class SyncAction extends AbstractOgLiveController +class SyncAction extends AbstractOgBootController { /** * @throws TransportExceptionInterface @@ -25,46 +26,37 @@ class SyncAction extends AbstractOgLiveController */ public function __invoke(HttpClientInterface $httpClient, EntityManagerInterface $entityManager): JsonResponse { - try { - $response = $httpClient->request('GET', $this->ogBootApiUrl.'/ogboot/v1/oglives', [ - 'headers' => [ - 'accept' => 'application/json', - ], - ]); + $content = $this->createRequest($httpClient, 'GET', $this->ogBootApiUrl . '/ogboot/v1/oglives'); - } catch (TransportExceptionInterface $e) { - return new JsonResponse( data: 'An error occurred', status: Response::HTTP_INTERNAL_SERVER_ERROR); - } - - $data = json_decode($response->getContent(), true); - - foreach ($data['installed_ogLives'] as $ogLive) { + foreach ($content['installed_ogLives'] as $ogLive) { $ogLiveEntity = $this->entityManager->getRepository(OgLive::class)->findOneBy(['checksum' => $ogLive['id']]); if ($ogLiveEntity) { - $ogLiveEntity->setName($ogLive['filename']); - $ogLiveEntity->setInstalled(true); - $ogLiveEntity->setArchitecture($ogLive['architecture']); - $ogLiveEntity->setKernel($ogLive['kernel']); - $ogLiveEntity->setRevision($ogLive['revision']); - $ogLiveEntity->setDirectory($ogLive['directory']); - $ogLiveEntity->setChecksum($ogLive['id']); + $this->extracted($ogLiveEntity, $ogLive); $this->entityManager->persist($ogLiveEntity); } else { $ogLiveEntity = new OgLive(); - $ogLiveEntity->setName($ogLive['filename']); - $ogLiveEntity->setInstalled(true); - $ogLiveEntity->setArchitecture($ogLive['architecture']); - $ogLiveEntity->setKernel($ogLive['kernel']); - $ogLiveEntity->setRevision($ogLive['revision']); - $ogLiveEntity->setDirectory($ogLive['directory']); - $ogLiveEntity->setChecksum($ogLive['id']); + $this->extracted($ogLiveEntity, $ogLive); } $this->entityManager->persist($ogLiveEntity); - } - $this->entityManager->flush(); - return new JsonResponse( data: $data, status: Response::HTTP_OK); + return new JsonResponse(data: $content, status: Response::HTTP_OK); + } + + /** + * @param OgLive|null $ogLiveEntity + * @param mixed $ogLive + * @return void + */ + public function extracted(OgLive|null $ogLiveEntity, mixed $ogLive): void + { + $ogLiveEntity->setName($ogLive['filename']); + $ogLiveEntity->setInstalled(true); + $ogLiveEntity->setArchitecture($ogLive['architecture']); + $ogLiveEntity->setKernel($ogLive['kernel']); + $ogLiveEntity->setRevision($ogLive['revision']); + $ogLiveEntity->setDirectory($ogLive['directory']); + $ogLiveEntity->setChecksum($ogLive['id']); } } \ No newline at end of file diff --git a/src/Controller/OgBoot/OgLive/UninstallAction.php b/src/Controller/OgBoot/OgLive/UninstallAction.php index ac9f047..d0ea542 100644 --- a/src/Controller/OgBoot/OgLive/UninstallAction.php +++ b/src/Controller/OgBoot/OgLive/UninstallAction.php @@ -2,12 +2,14 @@ namespace App\Controller\OgBoot\OgLive; -use App\Controller\OgBoot\AbstractOgLiveController; +use App\Controller\OgBoot\AbstractOgBootController; use App\Entity\OgLive; use Doctrine\ORM\EntityManagerInterface; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\Attribute\AsController; +use Symfony\Component\HttpKernel\Exception\HttpException; +use Symfony\Component\Validator\Exception\ValidatorException; use Symfony\Contracts\HttpClient\Exception\ClientExceptionInterface; use Symfony\Contracts\HttpClient\Exception\RedirectionExceptionInterface; use Symfony\Contracts\HttpClient\Exception\ServerExceptionInterface; @@ -15,7 +17,7 @@ use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface; use Symfony\Contracts\HttpClient\HttpClientInterface; #[AsController] -class UninstallAction extends AbstractOgLiveController +class UninstallAction extends AbstractOgBootController { /** * @throws ServerExceptionInterface @@ -25,22 +27,14 @@ class UninstallAction extends AbstractOgLiveController */ public function __invoke(OgLive $data, HttpClientInterface $httpClient, EntityManagerInterface $entityManager): JsonResponse { - try { - $response = $httpClient->request('DELETE', $this->ogBootApiUrl.'/ogboot/v1/oglives/'.$data->getChecksum(), [ - 'headers' => [ - 'accept' => 'application/json', - ], - ]); - - } catch (TransportExceptionInterface $e) { - return new JsonResponse( data: $e->getMessage(), status: Response::HTTP_INTERNAL_SERVER_ERROR); + if (!$data->getChecksum()) { + throw new ValidatorException('Checksum is required'); } - if ($response->getStatusCode() === Response::HTTP_OK) { - $data->setInstalled(false); - $entityManager->persist($data); - $entityManager->flush(); - } + $content = $this->createRequest($httpClient, 'DELETE', $this->ogBootApiUrl.'/ogboot/v1/oglives/'.$data->getChecksum()); + + $entityManager->remove($data); + $entityManager->flush(); return new JsonResponse(status: Response::HTTP_OK); } diff --git a/src/Controller/OgBoot/PxeBootFile/GetCollectionAction.php b/src/Controller/OgBoot/PxeBootFile/GetCollectionAction.php index f758745..f870364 100644 --- a/src/Controller/OgBoot/PxeBootFile/GetCollectionAction.php +++ b/src/Controller/OgBoot/PxeBootFile/GetCollectionAction.php @@ -2,7 +2,7 @@ namespace App\Controller\OgBoot\PxeBootFile; -use App\Controller\OgBoot\AbstractOgLiveController; +use App\Controller\OgBoot\AbstractOgBootController; use App\Entity\PxeBootFile; use App\Entity\PxeTemplate; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; @@ -16,7 +16,7 @@ use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface; use Symfony\Contracts\HttpClient\HttpClientInterface; #[AsController] -class GetCollectionAction extends AbstractOgLiveController +class GetCollectionAction extends AbstractOgBootController { /** * @throws TransportExceptionInterface @@ -26,18 +26,8 @@ class GetCollectionAction extends AbstractOgLiveController */ public function __invoke(HttpClientInterface $httpClient): JsonResponse { - try { - $response = $httpClient->request('GET', $this->ogBootApiUrl.'/ogboot/v1/pxes', [ - 'headers' => [ - 'accept' => 'application/json', - ], - ]); - } catch (TransportExceptionInterface $e) { - return new JsonResponse( data: 'An error occurred', status: Response::HTTP_INTERNAL_SERVER_ERROR); - } + $content = $this->createRequest($httpClient, 'GET', $this->ogBootApiUrl.'/ogboot/v1/pxes'); - $data = json_decode($response->getContent(), true); - - return new JsonResponse( data: $data, status: Response::HTTP_OK); + return new JsonResponse(data: $content, status: Response::HTTP_OK); } } \ No newline at end of file diff --git a/src/Controller/OgBoot/PxeTemplate/DeleteAction.php b/src/Controller/OgBoot/PxeTemplate/DeleteAction.php index 4971708..ba6c49d 100644 --- a/src/Controller/OgBoot/PxeTemplate/DeleteAction.php +++ b/src/Controller/OgBoot/PxeTemplate/DeleteAction.php @@ -2,8 +2,9 @@ namespace App\Controller\OgBoot\PxeTemplate; -use App\Controller\OgBoot\AbstractOgLiveController; +use App\Controller\OgBoot\AbstractOgBootController; use App\Entity\PxeTemplate; +use Doctrine\ORM\EntityManagerInterface; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Response; @@ -15,7 +16,7 @@ use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface; use Symfony\Contracts\HttpClient\HttpClientInterface; #[AsController] -class DeleteAction extends AbstractOgLiveController +class DeleteAction extends AbstractOgBootController { /** * @throws TransportExceptionInterface @@ -23,7 +24,7 @@ class DeleteAction extends AbstractOgLiveController * @throws RedirectionExceptionInterface * @throws ClientExceptionInterface */ - public function __invoke(PxeTemplate $data, HttpClientInterface $httpClient): JsonResponse + public function __invoke(PxeTemplate $data, HttpClientInterface $httpClient, EntityManagerInterface $entityManager): JsonResponse { try { $response = $httpClient->request('DELETE', $this->ogBootApiUrl.'/ogboot/v1/pxe-templates/'.$data->getName(), [ @@ -35,8 +36,11 @@ class DeleteAction extends AbstractOgLiveController return new JsonResponse( data: 'An error occurred', status: Response::HTTP_INTERNAL_SERVER_ERROR); } - $data = json_decode($response->getContent(), true); + if ($response->getStatusCode() === Response::HTTP_OK) { + $entityManager->remove($data); + $entityManager->flush(); + } - return new JsonResponse( data: $data, status: Response::HTTP_OK); + return new JsonResponse(status: Response::HTTP_OK); } } \ No newline at end of file diff --git a/src/Controller/OgBoot/PxeTemplate/GetAction.php b/src/Controller/OgBoot/PxeTemplate/GetAction.php index 19e91a3..5648a55 100644 --- a/src/Controller/OgBoot/PxeTemplate/GetAction.php +++ b/src/Controller/OgBoot/PxeTemplate/GetAction.php @@ -2,7 +2,7 @@ namespace App\Controller\OgBoot\PxeTemplate; -use App\Controller\OgBoot\AbstractOgLiveController; +use App\Controller\OgBoot\AbstractOgBootController; use App\Entity\PxeTemplate; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\JsonResponse; @@ -15,7 +15,7 @@ use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface; use Symfony\Contracts\HttpClient\HttpClientInterface; #[AsController] -class GetAction extends AbstractOgLiveController +class GetAction extends AbstractOgBootController { /** * @throws TransportExceptionInterface diff --git a/src/Controller/OgBoot/PxeTemplate/GetCollectionAction.php b/src/Controller/OgBoot/PxeTemplate/GetCollectionAction.php index 6693311..c294cae 100644 --- a/src/Controller/OgBoot/PxeTemplate/GetCollectionAction.php +++ b/src/Controller/OgBoot/PxeTemplate/GetCollectionAction.php @@ -2,7 +2,7 @@ namespace App\Controller\OgBoot\PxeTemplate; -use App\Controller\OgBoot\AbstractOgLiveController; +use App\Controller\OgBoot\AbstractOgBootController; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Response; @@ -14,7 +14,7 @@ use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface; use Symfony\Contracts\HttpClient\HttpClientInterface; #[AsController] -class GetCollectionAction extends AbstractOgLiveController +class GetCollectionAction extends AbstractOgBootController { /** * @throws TransportExceptionInterface diff --git a/src/Controller/OgBoot/PxeTemplate/PostAction.php b/src/Controller/OgBoot/PxeTemplate/PostAction.php index d07a016..a448bf1 100644 --- a/src/Controller/OgBoot/PxeTemplate/PostAction.php +++ b/src/Controller/OgBoot/PxeTemplate/PostAction.php @@ -2,7 +2,7 @@ namespace App\Controller\OgBoot\PxeTemplate; -use App\Controller\OgBoot\AbstractOgLiveController; +use App\Controller\OgBoot\AbstractOgBootController; use App\Entity\PxeTemplate; use Doctrine\ORM\EntityManagerInterface; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; @@ -16,7 +16,7 @@ use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface; use Symfony\Contracts\HttpClient\HttpClientInterface; #[AsController] -class PostAction extends AbstractOgLiveController +class PostAction extends AbstractOgBootController { /** * @throws TransportExceptionInterface -- 2.40.1 From f88bb46f3e808bbc3e176129688feb6ec1d99078 Mon Sep 17 00:00:00 2001 From: Manuel Aranda Date: Fri, 23 Aug 2024 12:52:01 +0200 Subject: [PATCH 019/157] refs #614. Itnegration ogDhcp --- config/api_platform/Subnet.yaml | 36 ++++++++++++++ .../OgDhcp/AbstractOgDhcpController.php | 36 ++++++++++++++ .../OgDhcp/Subnet/AddHostAction.php | 48 +++++++++++++++++++ src/Controller/OgDhcp/Subnet/DeleteAction.php | 27 +++++++++++ .../OgDhcp/Subnet/DeleteHostAction.php | 8 ++++ src/Controller/OgDhcp/Subnet/GetAction.php | 20 +++----- .../OgDhcp/Subnet/GetCollectionAction.php | 15 +----- .../OgDhcp/Subnet/GetHostAction.php | 12 +++++ src/Controller/OgDhcp/Subnet/PostAction.php | 41 +++++++++++++++- .../OgDhcp/Subnet/PutHostAction.php | 8 ++++ 10 files changed, 223 insertions(+), 28 deletions(-) create mode 100644 src/Controller/OgDhcp/Subnet/AddHostAction.php create mode 100644 src/Controller/OgDhcp/Subnet/DeleteHostAction.php create mode 100644 src/Controller/OgDhcp/Subnet/GetHostAction.php create mode 100644 src/Controller/OgDhcp/Subnet/PutHostAction.php diff --git a/config/api_platform/Subnet.yaml b/config/api_platform/Subnet.yaml index 7125a1a..c9bd475 100644 --- a/config/api_platform/Subnet.yaml +++ b/config/api_platform/Subnet.yaml @@ -23,6 +23,42 @@ resources: ApiPlatform\Metadata\Post: ~ ApiPlatform\Metadata\Delete: ~ + get_collection: + shortName: Subnet Server + description: Get collection of Subnet + class: ApiPlatform\Metadata\GetCollection + method: GET + input: false + uriTemplate: /subnets/server/get-collection + controller: App\Controller\OgDhcp\Subnet\GetCollectionAction + + get: + shortName: Subnet Server + description: Get Subnet + class: ApiPlatform\Metadata\Get + method: GET + input: false + uriTemplate: /subnets/server/{uuid}/get + controller: App\Controller\OgDhcp\Subnet\GetAction + + post: + shortName: Subnet Server + description: Create Subnet + class: ApiPlatform\Metadata\Post + method: POST + input: false + uriTemplate: /subnets/server/{uuid}/post + controller: App\Controller\OgDhcp\Subnet\PostAction + + delete: + shortName: Subnet Server + description: Delete Subnet + class: ApiPlatform\Metadata\Get + method: GET + input: false + uriTemplate: /subnets/server/{uuid}/delete + controller: App\Controller\OgDhcp\Subnet\DeleteAction + properties: App\Entity\Subnet: id: diff --git a/src/Controller/OgDhcp/AbstractOgDhcpController.php b/src/Controller/OgDhcp/AbstractOgDhcpController.php index 8874a9f..cfd81b6 100644 --- a/src/Controller/OgDhcp/AbstractOgDhcpController.php +++ b/src/Controller/OgDhcp/AbstractOgDhcpController.php @@ -6,7 +6,15 @@ namespace App\Controller\OgDhcp; use Doctrine\ORM\EntityManagerInterface; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; +use Symfony\Component\HttpFoundation\JsonResponse; +use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\Attribute\AsController; +use Symfony\Component\HttpKernel\Exception\HttpException; +use Symfony\Contracts\HttpClient\Exception\ClientExceptionInterface; +use Symfony\Contracts\HttpClient\Exception\RedirectionExceptionInterface; +use Symfony\Contracts\HttpClient\Exception\ServerExceptionInterface; +use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface; +use Symfony\Contracts\HttpClient\HttpClientInterface; #[AsController] abstract class AbstractOgDhcpController extends AbstractController @@ -17,4 +25,32 @@ abstract class AbstractOgDhcpController extends AbstractController ) { } + + /** + * @throws TransportExceptionInterface + * @throws ServerExceptionInterface + * @throws RedirectionExceptionInterface + * @throws ClientExceptionInterface + */ + public function createRequest (HttpClientInterface $httpClient, string $method, string $url, array $params = []): JsonResponse|array + { + $params = array_merge($params, [ + 'headers' => [ + 'accept' => 'application/json', + 'Content-Type' => 'application/json' + ], + ]); + + try { + $response = $httpClient->request($method, $url, $params); + + return json_decode($response->getContent(), true); + } catch (ClientExceptionInterface | ServerExceptionInterface $e) { + $response = $e->getResponse(); + $content = json_decode($response->getContent(false), true); + throw new HttpException($response->getStatusCode(), $content['error'] ?? 'An error occurred'); + } catch (TransportExceptionInterface $e) { + throw new HttpException(Response::HTTP_INTERNAL_SERVER_ERROR, $e->getMessage()); + } + } } diff --git a/src/Controller/OgDhcp/Subnet/AddHostAction.php b/src/Controller/OgDhcp/Subnet/AddHostAction.php new file mode 100644 index 0000000..18cc882 --- /dev/null +++ b/src/Controller/OgDhcp/Subnet/AddHostAction.php @@ -0,0 +1,48 @@ + [ + 'host' => '', + 'macAddress' => '', + 'address' => '', + 'nextServer' => '', + ] + ]; + + $content = $this->createRequest($httpClient, 'POST', $this->ogDhcpApiUrl.'/opengnsys3/rest/subnets/'.$data->getId().'/hosts', $params); + + $this->entityManager->persist($data); + $this->entityManager->flush(); + + return new JsonResponse(data: $content, status: Response::HTTP_OK); + } +} \ No newline at end of file diff --git a/src/Controller/OgDhcp/Subnet/DeleteAction.php b/src/Controller/OgDhcp/Subnet/DeleteAction.php index 81080b8..462b412 100644 --- a/src/Controller/OgDhcp/Subnet/DeleteAction.php +++ b/src/Controller/OgDhcp/Subnet/DeleteAction.php @@ -3,10 +3,37 @@ namespace App\Controller\OgDhcp\Subnet; use App\Controller\OgDhcp\AbstractOgDhcpController; +use App\Entity\Subnet; +use Symfony\Component\HttpFoundation\JsonResponse; +use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\Attribute\AsController; +use Symfony\Component\Validator\Exception\ValidatorException; +use Symfony\Contracts\HttpClient\Exception\ClientExceptionInterface; +use Symfony\Contracts\HttpClient\Exception\RedirectionExceptionInterface; +use Symfony\Contracts\HttpClient\Exception\ServerExceptionInterface; +use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface; +use Symfony\Contracts\HttpClient\HttpClientInterface; #[AsController] class DeleteAction extends AbstractOgDhcpController { + /** + * @throws TransportExceptionInterface + * @throws ServerExceptionInterface + * @throws RedirectionExceptionInterface + * @throws ClientExceptionInterface + */ + public function __invoke(Subnet $data, HttpClientInterface $httpClient): JsonResponse + { + if (!$data->getChecksum()) { + throw new ValidatorException('Checksum is required'); + } + $content = $this->createRequest($httpClient, 'DELETE', $this->ogDhcpApiUrl.'/opengnsys3/rest/dhcp/subnets/'.$data->getChecksum()); + + $this->entityManager->remove($data); + $this->entityManager->flush(); + + return new JsonResponse(status: Response::HTTP_OK); + } } \ No newline at end of file diff --git a/src/Controller/OgDhcp/Subnet/DeleteHostAction.php b/src/Controller/OgDhcp/Subnet/DeleteHostAction.php new file mode 100644 index 0000000..95a3f1d --- /dev/null +++ b/src/Controller/OgDhcp/Subnet/DeleteHostAction.php @@ -0,0 +1,8 @@ +request('GET', $this->ogDhcpApiUrl.'/opengnsys3/rest/dhcp/subnets/'.$data->getChecksum(), [ - 'headers' => [ - 'accept' => 'application/json', - ], - ]); - } catch (TransportExceptionInterface $e) { - return new JsonResponse( data: 'An error occurred', status: Response::HTTP_INTERNAL_SERVER_ERROR); + if (!$data->getId()) { + throw new ValidatorException('Checksum is required'); } - $data = json_decode($response->getContent(), true); + $content = $this->createRequest($httpClient, 'GET', $this->ogDhcpApiUrl.'/opengnsys3/rest/dhcp/subnets/'.$data->getId()); - return new JsonResponse( data: $data, status: Response::HTTP_OK); + return new JsonResponse(data: $content, status: Response::HTTP_OK); } } \ No newline at end of file diff --git a/src/Controller/OgDhcp/Subnet/GetCollectionAction.php b/src/Controller/OgDhcp/Subnet/GetCollectionAction.php index d29c5d0..2b2ee50 100644 --- a/src/Controller/OgDhcp/Subnet/GetCollectionAction.php +++ b/src/Controller/OgDhcp/Subnet/GetCollectionAction.php @@ -2,7 +2,6 @@ namespace App\Controller\OgDhcp\Subnet; -use App\Controller\OgBoot\AbstractOgLiveController; use App\Controller\OgDhcp\AbstractOgDhcpController; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Response; @@ -24,18 +23,8 @@ class GetCollectionAction extends AbstractOgDhcpController */ public function __invoke(HttpClientInterface $httpClient): JsonResponse { - try { - $response = $httpClient->request('GET', $this->ogDhcpApiUrl.'/opengnsys3/rest/subnets', [ - 'headers' => [ - 'accept' => 'application/json', - ], - ]); - } catch (TransportExceptionInterface $e) { - return new JsonResponse( data: 'An error occurred', status: Response::HTTP_INTERNAL_SERVER_ERROR); - } + $content = $this->createRequest($httpClient, 'GET', $this->ogDhcpApiUrl . '/opengnsys3/rest/subnets'); - $data = json_decode($response->getContent(), true); - - return new JsonResponse( data: $data, status: Response::HTTP_OK); + return new JsonResponse(data: $content, status: Response::HTTP_OK); } } \ No newline at end of file diff --git a/src/Controller/OgDhcp/Subnet/GetHostAction.php b/src/Controller/OgDhcp/Subnet/GetHostAction.php new file mode 100644 index 0000000..1211d8a --- /dev/null +++ b/src/Controller/OgDhcp/Subnet/GetHostAction.php @@ -0,0 +1,12 @@ + [ + 'url' => '' + ] + ]; + + $content = $this->createRequest($httpClient, 'POST', $this->ogDhcpApiUrl.'/opengnsys3/rest/subnets', $params); + + $this->entityManager->persist($data); + $this->entityManager->flush(); + + return new JsonResponse(data: $content, status: Response::HTTP_OK); + } } \ No newline at end of file diff --git a/src/Controller/OgDhcp/Subnet/PutHostAction.php b/src/Controller/OgDhcp/Subnet/PutHostAction.php new file mode 100644 index 0000000..a5f540e --- /dev/null +++ b/src/Controller/OgDhcp/Subnet/PutHostAction.php @@ -0,0 +1,8 @@ + Date: Mon, 26 Aug 2024 11:40:21 +0200 Subject: [PATCH 020/157] refs #658. Params PXE boot files POST --- src/Service/OgBoot/PxeBootFile/PostService.php | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Service/OgBoot/PxeBootFile/PostService.php b/src/Service/OgBoot/PxeBootFile/PostService.php index e836af2..17ebe2e 100644 --- a/src/Service/OgBoot/PxeBootFile/PostService.php +++ b/src/Service/OgBoot/PxeBootFile/PostService.php @@ -41,7 +41,7 @@ readonly class PostService 'ip' => $client->getIp(), 'server_ip' => '92.168.2.1', 'router' => $client->getOrganizationalUnit()->getNetworkSettings()->getRouter(), - 'netmask' => $client->getOrganizationalUnit()->getNetworkSettings()->getNetmask(), + 'netmask' => $client->getOrganizationalUnit()->getNetworkSettings() ? $client->getOrganizationalUnit()->getNetworkSettings()->getNetmask() : '255.255.255.0', 'computer_name' => $client->getName(), 'netiface' => $client->getNetiface(), 'group' => $client->getOrganizationalUnit()->getName(), @@ -51,12 +51,12 @@ readonly class PostService 'ogshare' => '192.168.2.1', 'oglivedir' => 'ogLive', 'ogprof' => 'false', - 'hardprofile' => '', - 'ogntp' => $client->getOrganizationalUnit()->getNetworkSettings()->getNtp(), - 'ogdns' => $client->getOrganizationalUnit()->getNetworkSettings()->getDns(), - 'ogProxy' => $client->getOrganizationalUnit()->getNetworkSettings()->getProxy(), + 'hardprofile' => $client->getHardwareProfile() ? $client->getHardwareProfile()->getDescription() : 'default', + 'ogntp' => $client->getOrganizationalUnit()->getNetworkSettings()?->getNtp(), + 'ogdns' => $client->getOrganizationalUnit()->getNetworkSettings()?->getDns(), + 'ogProxy' => $client->getOrganizationalUnit()->getNetworkSettings()?->getProxy(), 'ogunit' => '', - 'resolution' => '768' + 'resolution' => '788' ]; try { -- 2.40.1 From 31b78736f2b757771715c34006194179cfe28b09 Mon Sep 17 00:00:00 2001 From: Manuel Aranda Date: Tue, 27 Aug 2024 10:55:32 +0200 Subject: [PATCH 021/157] refs #614. Itnegration ogDhcp --- config/api_platform/Subnet.yaml | 36 +++++++++++++++++++ .../OgDhcp/Subnet/DeleteHostAction.php | 7 ++-- .../OgDhcp/Subnet/GetHostAction.php | 12 ------- .../OgDhcp/Subnet/GetHostsAction.php | 36 +++++++++++++++++++ src/Controller/OgDhcp/Subnet/PostAction.php | 12 +++---- .../OgDhcp/Subnet/PutHostAction.php | 7 ++-- 6 files changed, 88 insertions(+), 22 deletions(-) delete mode 100644 src/Controller/OgDhcp/Subnet/GetHostAction.php create mode 100644 src/Controller/OgDhcp/Subnet/GetHostsAction.php diff --git a/config/api_platform/Subnet.yaml b/config/api_platform/Subnet.yaml index c9bd475..b240585 100644 --- a/config/api_platform/Subnet.yaml +++ b/config/api_platform/Subnet.yaml @@ -59,6 +59,42 @@ resources: uriTemplate: /subnets/server/{uuid}/delete controller: App\Controller\OgDhcp\Subnet\DeleteAction + add_host: + shortName: Subnet Server Hosts + description: Add Host to Subnet + class: ApiPlatform\Metadata\Post + method: POST + input: false + uriTemplate: /subnets/server/{uuid}/add-host + controller: App\Controller\OgDhcp\Subnet\AddHostAction + + get_hosts: + shortName: Subnet Server Hosts + description: Get Hosts of Subnet + class: ApiPlatform\Metadata\GetCollection + method: GET + input: false + uriTemplate: /subnets/server/{uuid}/get-hosts + controller: App\Controller\OgDhcp\Subnet\GetHostsAction + + put_host: + shortName: Subnet Server Hosts + description: Put Host of Subnet + class: ApiPlatform\Metadata\Put + method: PUT + input: false + uriTemplate: /subnets/server/{uuid}/put-host + controller: App\Controller\OgDhcp\Subnet\PutHostAction + + delete_host: + shortName: Subnet Server Hosts + description: Delete Host of Subnet + class: ApiPlatform\Metadata\Delete + method: DELETE + input: false + uriTemplate: /subnets/server/{uuid}/delete-host + controller: App\Controller\OgDhcp\Subnet\DeleteHostAction + properties: App\Entity\Subnet: id: diff --git a/src/Controller/OgDhcp/Subnet/DeleteHostAction.php b/src/Controller/OgDhcp/Subnet/DeleteHostAction.php index 95a3f1d..57aac51 100644 --- a/src/Controller/OgDhcp/Subnet/DeleteHostAction.php +++ b/src/Controller/OgDhcp/Subnet/DeleteHostAction.php @@ -2,7 +2,10 @@ namespace App\Controller\OgDhcp\Subnet; -class DeleteHostAction -{ +use App\Controller\OgDhcp\AbstractOgDhcpController; +use Symfony\Component\HttpKernel\Attribute\AsController; + +#[AsController] +class DeleteHostAction extends AbstractOgDhcpController{ } \ No newline at end of file diff --git a/src/Controller/OgDhcp/Subnet/GetHostAction.php b/src/Controller/OgDhcp/Subnet/GetHostAction.php deleted file mode 100644 index 1211d8a..0000000 --- a/src/Controller/OgDhcp/Subnet/GetHostAction.php +++ /dev/null @@ -1,12 +0,0 @@ -getId()) { + throw new ValidatorException('Checksum is required'); + } + + $content = $this->createRequest($httpClient, 'GET', $this->ogDhcpApiUrl.'/ogdhcp/v1/subnets/'.$data->getId()); + + return new JsonResponse(data: $content, status: Response::HTTP_OK); + } +} \ No newline at end of file diff --git a/src/Controller/OgDhcp/Subnet/PostAction.php b/src/Controller/OgDhcp/Subnet/PostAction.php index 2f16f5d..67f00eb 100644 --- a/src/Controller/OgDhcp/Subnet/PostAction.php +++ b/src/Controller/OgDhcp/Subnet/PostAction.php @@ -25,17 +25,17 @@ class PostAction extends AbstractOgDhcpController */ public function __invoke(Subnet $data, HttpClientInterface $httpClient): JsonResponse { - if (!$data) { - throw new ValidatorException('Data URL is required'); - } - $params = [ 'json' => [ - 'url' => '' + 'subnetId' => $data->getId(), + 'mask' => $data->getNetmask(), + 'address' => $data->getIpAddress(), + 'nextServer' => $data->getNextServer(), + 'bootFileName' => $data->getBootFileName(), ] ]; - $content = $this->createRequest($httpClient, 'POST', $this->ogDhcpApiUrl.'/opengnsys3/rest/subnets', $params); + $content = $this->createRequest($httpClient, 'POST', $this->ogDhcpApiUrl.'/ogdhcp/v1/subnets', $params); $this->entityManager->persist($data); $this->entityManager->flush(); diff --git a/src/Controller/OgDhcp/Subnet/PutHostAction.php b/src/Controller/OgDhcp/Subnet/PutHostAction.php index a5f540e..91146e9 100644 --- a/src/Controller/OgDhcp/Subnet/PutHostAction.php +++ b/src/Controller/OgDhcp/Subnet/PutHostAction.php @@ -2,7 +2,10 @@ namespace App\Controller\OgDhcp\Subnet; -class PutHostAction -{ +use App\Controller\OgDhcp\AbstractOgDhcpController; +use Symfony\Component\HttpKernel\Attribute\AsController; + +#[AsController] +class PutHostAction extends AbstractOgDhcpController{ } \ No newline at end of file -- 2.40.1 From 6d1a2a89ea7526d547c92a6fe9f050cf96cc2f9c Mon Sep 17 00:00:00 2001 From: Manuel Aranda Date: Tue, 27 Aug 2024 15:13:56 +0200 Subject: [PATCH 022/157] refs #614. Itnegration ogDhcp. New endpoints --- config/api_platform/Subnet.yaml | 29 +++++++----- migrations/Version20240827102833.php | 31 +++++++++++++ .../OgDhcp/Subnet/GetCollectionAction.php | 5 ++- src/Controller/OgDhcp/Subnet/PutAction.php | 44 ++++++++++++++++++- src/Dto/Input/SubnetInput.php | 7 +++ src/Dto/Output/SubnetOutput.php | 4 ++ src/Entity/Subnet.php | 2 + 7 files changed, 108 insertions(+), 14 deletions(-) create mode 100644 migrations/Version20240827102833.php diff --git a/config/api_platform/Subnet.yaml b/config/api_platform/Subnet.yaml index b240585..5dba0ef 100644 --- a/config/api_platform/Subnet.yaml +++ b/config/api_platform/Subnet.yaml @@ -29,7 +29,7 @@ resources: class: ApiPlatform\Metadata\GetCollection method: GET input: false - uriTemplate: /subnets/server/get-collection + uriTemplate: /og-dhcp/server/get-collection controller: App\Controller\OgDhcp\Subnet\GetCollectionAction get: @@ -38,7 +38,7 @@ resources: class: ApiPlatform\Metadata\Get method: GET input: false - uriTemplate: /subnets/server/{uuid}/get + uriTemplate: /og-dhcp/server/{uuid}/get controller: App\Controller\OgDhcp\Subnet\GetAction post: @@ -47,16 +47,25 @@ resources: class: ApiPlatform\Metadata\Post method: POST input: false - uriTemplate: /subnets/server/{uuid}/post + uriTemplate: /og-dhcp/server/{uuid}/post controller: App\Controller\OgDhcp\Subnet\PostAction + put: + shortName: Subnet Server + description: Create Subnet + class: ApiPlatform\Metadata\Put + method: PUT + input: false + uriTemplate: /og-dhcp/server/{uuid}/put + controller: App\Controller\OgDhcp\Subnet\PutAction + delete: shortName: Subnet Server description: Delete Subnet - class: ApiPlatform\Metadata\Get - method: GET + class: ApiPlatform\Metadata\Delete + method: DELETE input: false - uriTemplate: /subnets/server/{uuid}/delete + uriTemplate: /og-dhcp/server/{uuid}/delete controller: App\Controller\OgDhcp\Subnet\DeleteAction add_host: @@ -65,7 +74,7 @@ resources: class: ApiPlatform\Metadata\Post method: POST input: false - uriTemplate: /subnets/server/{uuid}/add-host + uriTemplate: /og-dhcp/server/{uuid}/add-host controller: App\Controller\OgDhcp\Subnet\AddHostAction get_hosts: @@ -74,7 +83,7 @@ resources: class: ApiPlatform\Metadata\GetCollection method: GET input: false - uriTemplate: /subnets/server/{uuid}/get-hosts + uriTemplate: /og-dhcp/server/{uuid}/get-hosts controller: App\Controller\OgDhcp\Subnet\GetHostsAction put_host: @@ -83,7 +92,7 @@ resources: class: ApiPlatform\Metadata\Put method: PUT input: false - uriTemplate: /subnets/server/{uuid}/put-host + uriTemplate: /og-dhcp/server/{uuid}/put-host controller: App\Controller\OgDhcp\Subnet\PutHostAction delete_host: @@ -92,7 +101,7 @@ resources: class: ApiPlatform\Metadata\Delete method: DELETE input: false - uriTemplate: /subnets/server/{uuid}/delete-host + uriTemplate: /og-dhcp/server/{uuid}/delete-host controller: App\Controller\OgDhcp\Subnet\DeleteHostAction properties: diff --git a/migrations/Version20240827102833.php b/migrations/Version20240827102833.php new file mode 100644 index 0000000..00ab457 --- /dev/null +++ b/migrations/Version20240827102833.php @@ -0,0 +1,31 @@ +addSql('CREATE TABLE subnet (id INT AUTO_INCREMENT NOT NULL, uuid CHAR(36) NOT NULL COMMENT \'(DC2Type:uuid)\', migration_id VARCHAR(255) DEFAULT NULL, created_at DATETIME NOT NULL, updated_at DATETIME NOT NULL, created_by VARCHAR(255) DEFAULT NULL, updated_by VARCHAR(255) DEFAULT NULL, netmask VARCHAR(255) NOT NULL, ip_address VARCHAR(255) NOT NULL, next_server VARCHAR(255) NOT NULL, boot_file_name VARCHAR(255) NOT NULL, name VARCHAR(255) NOT NULL, UNIQUE INDEX UNIQ_91C24216D17F50A6 (uuid), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB'); + } + + public function down(Schema $schema): void + { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql('DROP TABLE subnet'); + } +} diff --git a/src/Controller/OgDhcp/Subnet/GetCollectionAction.php b/src/Controller/OgDhcp/Subnet/GetCollectionAction.php index 2b2ee50..9f9324c 100644 --- a/src/Controller/OgDhcp/Subnet/GetCollectionAction.php +++ b/src/Controller/OgDhcp/Subnet/GetCollectionAction.php @@ -23,8 +23,9 @@ class GetCollectionAction extends AbstractOgDhcpController */ public function __invoke(HttpClientInterface $httpClient): JsonResponse { - $content = $this->createRequest($httpClient, 'GET', $this->ogDhcpApiUrl . '/opengnsys3/rest/subnets'); + $content = $this->createRequest($httpClient, 'GET', $this->ogDhcpApiUrl . '/ogdhcp/v1/subnets'); return new JsonResponse(data: $content, status: Response::HTTP_OK); } -} \ No newline at end of file +} + diff --git a/src/Controller/OgDhcp/Subnet/PutAction.php b/src/Controller/OgDhcp/Subnet/PutAction.php index 8e187a0..252343d 100644 --- a/src/Controller/OgDhcp/Subnet/PutAction.php +++ b/src/Controller/OgDhcp/Subnet/PutAction.php @@ -2,7 +2,47 @@ namespace App\Controller\OgDhcp\Subnet; -class PutAction -{ +use App\Controller\OgDhcp\AbstractOgDhcpController; +use App\Entity\Subnet; +use Symfony\Component\HttpFoundation\JsonResponse; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpKernel\Attribute\AsController; +use Symfony\Component\Validator\Exception\ValidatorException; +use Symfony\Contracts\HttpClient\Exception\ClientExceptionInterface; +use Symfony\Contracts\HttpClient\Exception\RedirectionExceptionInterface; +use Symfony\Contracts\HttpClient\Exception\ServerExceptionInterface; +use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface; +use Symfony\Contracts\HttpClient\HttpClientInterface; +#[AsController] +class PutAction extends AbstractOgDhcpController +{ + /** + * @throws TransportExceptionInterface + * @throws ServerExceptionInterface + * @throws RedirectionExceptionInterface + * @throws ClientExceptionInterface + */ + public function __invoke(Subnet $data, HttpClientInterface $httpClient): JsonResponse + { + if (null === $data->getId()) { + throw new ValidatorException('Id is required'); + } + + $params = [ + 'json' => [ + 'mask' => $data->getNetmask(), + 'address' => $data->getIpAddress(), + 'nextServer' => $data->getNextServer(), + 'bootFileName' => $data->getBootFileName(), + ] + ]; + + $content = $this->createRequest($httpClient, 'POST', $this->ogDhcpApiUrl.'/ogdhcp/v1/subnets/'.$data->getId(), $params); + + $this->entityManager->persist($data); + $this->entityManager->flush(); + + return new JsonResponse(data: $content, status: Response::HTTP_OK); + } } \ No newline at end of file diff --git a/src/Dto/Input/SubnetInput.php b/src/Dto/Input/SubnetInput.php index b7c233d..d536042 100644 --- a/src/Dto/Input/SubnetInput.php +++ b/src/Dto/Input/SubnetInput.php @@ -9,6 +9,11 @@ use Symfony\Component\Validator\Constraints as Assert; final class SubnetInput { + #[Assert\NotBlank(message: 'validators.subnet.name.not_blank')] + #[Groups(['subnet:write'])] + #[ApiProperty(description: 'The name of the subnet', example: "Subnet 1")] + public ?string $name = null; + #[Assert\NotBlank(message: 'validators.subnet.netmask.not_blank')] #[Groups(['subnet:write'])] #[ApiProperty(description: 'The netmask of the subnet', example: "")] @@ -35,6 +40,7 @@ final class SubnetInput return; } + $this->name = $subnet->getName(); $this->netmask = $subnet->getNetmask(); $this->ipAddress = $subnet->getIpAddress(); $this->nextServer = $subnet->getNextServer(); @@ -47,6 +53,7 @@ final class SubnetInput $subnet = new Subnet(); } + $subnet->setName($this->name); $subnet->setNetmask($this->netmask); $subnet->setIpAddress($this->ipAddress); $subnet->setNextServer($this->nextServer); diff --git a/src/Dto/Output/SubnetOutput.php b/src/Dto/Output/SubnetOutput.php index 567ff2f..92cf65d 100644 --- a/src/Dto/Output/SubnetOutput.php +++ b/src/Dto/Output/SubnetOutput.php @@ -10,6 +10,9 @@ use Symfony\Component\Serializer\Annotation\Groups; #[Get(shortName: 'Subnet')] final class SubnetOutput extends AbstractOutput { + #[Groups(['subnet:read'])] + public string $name; + #[Groups(['subnet:read'])] public string $netmask; @@ -32,6 +35,7 @@ final class SubnetOutput extends AbstractOutput { parent::__construct($subnet); + $this->name = $subnet->getName(); $this->netmask = $subnet->getNetmask(); $this->ipAddress = $subnet->getIpAddress(); $this->nextServer = $subnet->getNextServer(); diff --git a/src/Entity/Subnet.php b/src/Entity/Subnet.php index 29201dd..5693555 100644 --- a/src/Entity/Subnet.php +++ b/src/Entity/Subnet.php @@ -8,6 +8,8 @@ use Doctrine\ORM\Mapping as ORM; #[ORM\Entity(repositoryClass: SubnetRepository::class)] class Subnet extends AbstractEntity { + use NameableTrait; + #[ORM\Column(length: 255)] private ?string $netmask = null; -- 2.40.1 From a232d5cbd7de7725aa11dab0699d5e7d279d72f8 Mon Sep 17 00:00:00 2001 From: Manuel Aranda Date: Tue, 27 Aug 2024 16:03:22 +0200 Subject: [PATCH 023/157] refs #614. Itnegration ogDhcp. Change some params --- config/api_platform/PxeTemplate.yaml | 8 ++-- config/api_platform/Subnet.yaml | 20 +++++----- src/Controller/OgDhcp/Subnet/DeleteAction.php | 6 +-- .../OgDhcp/Subnet/DeleteHostAction.php | 36 ++++++++++++++++- .../OgDhcp/Subnet/GetHostsAction.php | 2 +- src/Controller/OgDhcp/Subnet/PostAction.php | 2 +- .../{AddHostAction.php => PostHostAction.php} | 40 ++++++++++--------- .../OgDhcp/Subnet/PutHostAction.php | 40 ++++++++++++++++++- src/Dto/Input/SubnetAddHostInput.php | 26 ++++++++++++ 9 files changed, 140 insertions(+), 40 deletions(-) rename src/Controller/OgDhcp/Subnet/{AddHostAction.php => PostHostAction.php} (53%) create mode 100644 src/Dto/Input/SubnetAddHostInput.php diff --git a/config/api_platform/PxeTemplate.yaml b/config/api_platform/PxeTemplate.yaml index 0e36c96..1c7f2e0 100644 --- a/config/api_platform/PxeTemplate.yaml +++ b/config/api_platform/PxeTemplate.yaml @@ -23,7 +23,7 @@ resources: ApiPlatform\Metadata\Post: ~ ApiPlatform\Metadata\Delete: ~ - get_collection: + get_collection_templates: shortName: PxeTemplate Server description: Get collection of PxeTemplate class: ApiPlatform\Metadata\GetCollection @@ -32,7 +32,7 @@ resources: uriTemplate: /pxe-templates/server/get-collection controller: App\Controller\OgBoot\PxeTemplate\GetCollectionAction - get: + get_template: shortName: PxeTemplate Server description: Get PxeTemplate class: ApiPlatform\Metadata\Get @@ -41,7 +41,7 @@ resources: uriTemplate: /pxe-templates/server/{uuid}/get controller: App\Controller\OgBoot\PxeTemplate\GetAction - post: + post_template: shortName: PxeTemplate Server description: Create PxeTemplate class: ApiPlatform\Metadata\Post @@ -50,7 +50,7 @@ resources: uriTemplate: /pxe-templates/server/{uuid}/post controller: App\Controller\OgBoot\PxeTemplate\PostAction - delete: + delete_template: shortName: PxeTemplate Server description: Delete PxeTemplate class: ApiPlatform\Metadata\Get diff --git a/config/api_platform/Subnet.yaml b/config/api_platform/Subnet.yaml index 5dba0ef..747efb9 100644 --- a/config/api_platform/Subnet.yaml +++ b/config/api_platform/Subnet.yaml @@ -23,7 +23,7 @@ resources: ApiPlatform\Metadata\Post: ~ ApiPlatform\Metadata\Delete: ~ - get_collection: + get_collection_subnets: shortName: Subnet Server description: Get collection of Subnet class: ApiPlatform\Metadata\GetCollection @@ -32,7 +32,7 @@ resources: uriTemplate: /og-dhcp/server/get-collection controller: App\Controller\OgDhcp\Subnet\GetCollectionAction - get: + get_subnet: shortName: Subnet Server description: Get Subnet class: ApiPlatform\Metadata\Get @@ -41,7 +41,7 @@ resources: uriTemplate: /og-dhcp/server/{uuid}/get controller: App\Controller\OgDhcp\Subnet\GetAction - post: + post_subnet: shortName: Subnet Server description: Create Subnet class: ApiPlatform\Metadata\Post @@ -50,7 +50,7 @@ resources: uriTemplate: /og-dhcp/server/{uuid}/post controller: App\Controller\OgDhcp\Subnet\PostAction - put: + put_subnet: shortName: Subnet Server description: Create Subnet class: ApiPlatform\Metadata\Put @@ -59,7 +59,7 @@ resources: uriTemplate: /og-dhcp/server/{uuid}/put controller: App\Controller\OgDhcp\Subnet\PutAction - delete: + delete_subnet: shortName: Subnet Server description: Delete Subnet class: ApiPlatform\Metadata\Delete @@ -68,14 +68,14 @@ resources: uriTemplate: /og-dhcp/server/{uuid}/delete controller: App\Controller\OgDhcp\Subnet\DeleteAction - add_host: + post_host: shortName: Subnet Server Hosts - description: Add Host to Subnet + description: Post Host to Subnet class: ApiPlatform\Metadata\Post method: POST - input: false - uriTemplate: /og-dhcp/server/{uuid}/add-host - controller: App\Controller\OgDhcp\Subnet\AddHostAction + input: App\Dto\Input\SubnetAddHostInput + uriTemplate: /og-dhcp/server/{uuid}/post-host + controller: App\Controller\OgDhcp\Subnet\PostHostAction get_hosts: shortName: Subnet Server Hosts diff --git a/src/Controller/OgDhcp/Subnet/DeleteAction.php b/src/Controller/OgDhcp/Subnet/DeleteAction.php index 462b412..0344240 100644 --- a/src/Controller/OgDhcp/Subnet/DeleteAction.php +++ b/src/Controller/OgDhcp/Subnet/DeleteAction.php @@ -25,11 +25,11 @@ class DeleteAction extends AbstractOgDhcpController */ public function __invoke(Subnet $data, HttpClientInterface $httpClient): JsonResponse { - if (!$data->getChecksum()) { - throw new ValidatorException('Checksum is required'); + if (!$data->getId()) { + throw new ValidatorException('Data Id is required'); } - $content = $this->createRequest($httpClient, 'DELETE', $this->ogDhcpApiUrl.'/opengnsys3/rest/dhcp/subnets/'.$data->getChecksum()); + $content = $this->createRequest($httpClient, 'DELETE', $this->ogDhcpApiUrl.'/ogdhcp/v1/subnets/'.$data->getId()); $this->entityManager->remove($data); $this->entityManager->flush(); diff --git a/src/Controller/OgDhcp/Subnet/DeleteHostAction.php b/src/Controller/OgDhcp/Subnet/DeleteHostAction.php index 57aac51..4544f45 100644 --- a/src/Controller/OgDhcp/Subnet/DeleteHostAction.php +++ b/src/Controller/OgDhcp/Subnet/DeleteHostAction.php @@ -3,9 +3,43 @@ namespace App\Controller\OgDhcp\Subnet; use App\Controller\OgDhcp\AbstractOgDhcpController; +use App\Entity\Subnet; +use Symfony\Component\HttpFoundation\JsonResponse; +use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\Attribute\AsController; +use Symfony\Component\Validator\Exception\ValidatorException; +use Symfony\Contracts\HttpClient\Exception\ClientExceptionInterface; +use Symfony\Contracts\HttpClient\Exception\RedirectionExceptionInterface; +use Symfony\Contracts\HttpClient\Exception\ServerExceptionInterface; +use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface; +use Symfony\Contracts\HttpClient\HttpClientInterface; #[AsController] -class DeleteHostAction extends AbstractOgDhcpController{ +class DeleteHostAction extends AbstractOgDhcpController +{ + /** + * @throws TransportExceptionInterface + * @throws ServerExceptionInterface + * @throws RedirectionExceptionInterface + * @throws ClientExceptionInterface + */ + public function __invoke(Subnet $data, HttpClientInterface $httpClient): JsonResponse + { + if (!$data->getId()) { + throw new ValidatorException('Data URL is required'); + } + $params = [ + 'json' => [ + 'host' => '', + ] + ]; + + $content = $this->createRequest($httpClient, 'POST', $this->ogDhcpApiUrl.'/ogdhcp/v1/subnets/'.$data->getId().'/hosts', $params); + + $this->entityManager->persist($data); + $this->entityManager->flush(); + + return new JsonResponse(data: $content, status: Response::HTTP_OK); + } } \ No newline at end of file diff --git a/src/Controller/OgDhcp/Subnet/GetHostsAction.php b/src/Controller/OgDhcp/Subnet/GetHostsAction.php index 79eb13d..b465466 100644 --- a/src/Controller/OgDhcp/Subnet/GetHostsAction.php +++ b/src/Controller/OgDhcp/Subnet/GetHostsAction.php @@ -29,7 +29,7 @@ class GetHostsAction extends AbstractOgDhcpController throw new ValidatorException('Checksum is required'); } - $content = $this->createRequest($httpClient, 'GET', $this->ogDhcpApiUrl.'/ogdhcp/v1/subnets/'.$data->getId()); + $content = $this->createRequest($httpClient, 'GET', $this->ogDhcpApiUrl.'/ogdhcp/v1/subnets/'.$data->getId().'/hosts'); return new JsonResponse(data: $content, status: Response::HTTP_OK); } diff --git a/src/Controller/OgDhcp/Subnet/PostAction.php b/src/Controller/OgDhcp/Subnet/PostAction.php index 67f00eb..e9e32e4 100644 --- a/src/Controller/OgDhcp/Subnet/PostAction.php +++ b/src/Controller/OgDhcp/Subnet/PostAction.php @@ -35,7 +35,7 @@ class PostAction extends AbstractOgDhcpController ] ]; - $content = $this->createRequest($httpClient, 'POST', $this->ogDhcpApiUrl.'/ogdhcp/v1/subnets', $params); + $content = $this->createRequest($httpClient, 'POST', $this->ogDhcpApiUrl.'/ogdhcp/v1/subnets' , $params); $this->entityManager->persist($data); $this->entityManager->flush(); diff --git a/src/Controller/OgDhcp/Subnet/AddHostAction.php b/src/Controller/OgDhcp/Subnet/PostHostAction.php similarity index 53% rename from src/Controller/OgDhcp/Subnet/AddHostAction.php rename to src/Controller/OgDhcp/Subnet/PostHostAction.php index 18cc882..d2bdbb3 100644 --- a/src/Controller/OgDhcp/Subnet/AddHostAction.php +++ b/src/Controller/OgDhcp/Subnet/PostHostAction.php @@ -3,6 +3,8 @@ namespace App\Controller\OgDhcp\Subnet; use App\Controller\OgDhcp\AbstractOgDhcpController; +use App\Dto\Input\SubnetAddHostInput; +use App\Entity\Client; use App\Entity\Subnet; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Response; @@ -15,7 +17,7 @@ use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface; use Symfony\Contracts\HttpClient\HttpClientInterface; #[AsController] -class AddHostAction extends AbstractOgDhcpController +class PostHostAction extends AbstractOgDhcpController { /** * @throws TransportExceptionInterface @@ -23,26 +25,26 @@ class AddHostAction extends AbstractOgDhcpController * @throws RedirectionExceptionInterface * @throws ClientExceptionInterface */ - public function __invoke(Subnet $data, HttpClientInterface $httpClient): JsonResponse + public function __invoke(SubnetAddHostInput $input, HttpClientInterface $httpClient): JsonResponse { - if (!$data) { - throw new ValidatorException('Data URL is required'); + $subnet = $input->subnet->getEntity(); + $clients = $input->clients; + + /** @var Client $client */ + foreach ($clients as $client) { + $data = [ + 'host' => $client->getName(), + 'macAddress' => $client->getMac(), + 'address' => $client->getIp(), + ]; + + $params = [ + 'json' => $data + ]; + + $content = $this->createRequest($httpClient, 'POST', $this->ogDhcpApiUrl.'/ogdhcp/v1/subnets/'.$subnet->getId().'/hosts', $params); } - $params = [ - 'json' => [ - 'host' => '', - 'macAddress' => '', - 'address' => '', - 'nextServer' => '', - ] - ]; - - $content = $this->createRequest($httpClient, 'POST', $this->ogDhcpApiUrl.'/opengnsys3/rest/subnets/'.$data->getId().'/hosts', $params); - - $this->entityManager->persist($data); - $this->entityManager->flush(); - - return new JsonResponse(data: $content, status: Response::HTTP_OK); + return new JsonResponse(status: Response::HTTP_OK); } } \ No newline at end of file diff --git a/src/Controller/OgDhcp/Subnet/PutHostAction.php b/src/Controller/OgDhcp/Subnet/PutHostAction.php index 91146e9..55d7f6f 100644 --- a/src/Controller/OgDhcp/Subnet/PutHostAction.php +++ b/src/Controller/OgDhcp/Subnet/PutHostAction.php @@ -3,9 +3,47 @@ namespace App\Controller\OgDhcp\Subnet; use App\Controller\OgDhcp\AbstractOgDhcpController; +use App\Dto\Input\SubnetAddHostInput; +use App\Entity\Client; +use Symfony\Component\HttpFoundation\JsonResponse; +use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\Attribute\AsController; +use Symfony\Contracts\HttpClient\Exception\ClientExceptionInterface; +use Symfony\Contracts\HttpClient\Exception\RedirectionExceptionInterface; +use Symfony\Contracts\HttpClient\Exception\ServerExceptionInterface; +use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface; +use Symfony\Contracts\HttpClient\HttpClientInterface; #[AsController] -class PutHostAction extends AbstractOgDhcpController{ +class PutHostAction extends AbstractOgDhcpController +{ + /** + * @throws TransportExceptionInterface + * @throws ServerExceptionInterface + * @throws RedirectionExceptionInterface + * @throws ClientExceptionInterface + */ + public function __invoke(SubnetAddHostInput $input, HttpClientInterface $httpClient): JsonResponse + { + $subnet = $input->subnet->getEntity(); + $clients = $input->clients; + /** @var Client $client */ + foreach ($clients as $client) { + $data = [ + 'host' => $client->getName(), + 'oldMacAddress' => '', + 'macAddress' => '', + 'address' => '', + ]; + + $params = [ + 'json' => $data + ]; + + $content = $this->createRequest($httpClient, 'PUT', $this->ogDhcpApiUrl.'/ogdhcp/v1/subnets/'.$subnet->getId().'/hosts', $params); + } + + return new JsonResponse(status: Response::HTTP_OK); + } } \ No newline at end of file diff --git a/src/Dto/Input/SubnetAddHostInput.php b/src/Dto/Input/SubnetAddHostInput.php new file mode 100644 index 0000000..c5b10aa --- /dev/null +++ b/src/Dto/Input/SubnetAddHostInput.php @@ -0,0 +1,26 @@ + Date: Thu, 29 Aug 2024 09:00:05 +0200 Subject: [PATCH 024/157] refs #614. Itnegration ogDhcp. Change some params --- config/api_platform/Subnet.yaml | 2 +- src/Controller/OgDhcp/Subnet/DeleteAction.php | 2 +- src/Controller/OgDhcp/Subnet/PostHostAction.php | 13 +++++++------ src/Controller/OgDhcp/Subnet/PutAction.php | 4 ++-- src/Controller/OgDhcp/Subnet/PutHostAction.php | 9 +++++---- src/Dto/Input/SubnetAddHostInput.php | 5 ----- 6 files changed, 16 insertions(+), 19 deletions(-) diff --git a/config/api_platform/Subnet.yaml b/config/api_platform/Subnet.yaml index 747efb9..1755518 100644 --- a/config/api_platform/Subnet.yaml +++ b/config/api_platform/Subnet.yaml @@ -80,7 +80,7 @@ resources: get_hosts: shortName: Subnet Server Hosts description: Get Hosts of Subnet - class: ApiPlatform\Metadata\GetCollection + class: ApiPlatform\Metadata\Get method: GET input: false uriTemplate: /og-dhcp/server/{uuid}/get-hosts diff --git a/src/Controller/OgDhcp/Subnet/DeleteAction.php b/src/Controller/OgDhcp/Subnet/DeleteAction.php index 0344240..8f687c5 100644 --- a/src/Controller/OgDhcp/Subnet/DeleteAction.php +++ b/src/Controller/OgDhcp/Subnet/DeleteAction.php @@ -34,6 +34,6 @@ class DeleteAction extends AbstractOgDhcpController $this->entityManager->remove($data); $this->entityManager->flush(); - return new JsonResponse(status: Response::HTTP_OK); + return new JsonResponse(data: $content, status: Response::HTTP_OK); } } \ No newline at end of file diff --git a/src/Controller/OgDhcp/Subnet/PostHostAction.php b/src/Controller/OgDhcp/Subnet/PostHostAction.php index d2bdbb3..d1c15c9 100644 --- a/src/Controller/OgDhcp/Subnet/PostHostAction.php +++ b/src/Controller/OgDhcp/Subnet/PostHostAction.php @@ -4,6 +4,7 @@ namespace App\Controller\OgDhcp\Subnet; use App\Controller\OgDhcp\AbstractOgDhcpController; use App\Dto\Input\SubnetAddHostInput; +use App\Dto\Output\ClientOutput; use App\Entity\Client; use App\Entity\Subnet; use Symfony\Component\HttpFoundation\JsonResponse; @@ -25,17 +26,17 @@ class PostHostAction extends AbstractOgDhcpController * @throws RedirectionExceptionInterface * @throws ClientExceptionInterface */ - public function __invoke(SubnetAddHostInput $input, HttpClientInterface $httpClient): JsonResponse + public function __invoke(SubnetAddHostInput $input, Subnet $subnet, HttpClientInterface $httpClient): JsonResponse { - $subnet = $input->subnet->getEntity(); $clients = $input->clients; - /** @var Client $client */ foreach ($clients as $client) { + /** @var Client $clientEntity */ + $clientEntity = $client->getEntity(); $data = [ - 'host' => $client->getName(), - 'macAddress' => $client->getMac(), - 'address' => $client->getIp(), + 'host' => $clientEntity->getName(), + 'macAddress' => $clientEntity->getMac(), + 'address' => $clientEntity->getIp(), ]; $params = [ diff --git a/src/Controller/OgDhcp/Subnet/PutAction.php b/src/Controller/OgDhcp/Subnet/PutAction.php index 252343d..f57b282 100644 --- a/src/Controller/OgDhcp/Subnet/PutAction.php +++ b/src/Controller/OgDhcp/Subnet/PutAction.php @@ -25,7 +25,7 @@ class PutAction extends AbstractOgDhcpController */ public function __invoke(Subnet $data, HttpClientInterface $httpClient): JsonResponse { - if (null === $data->getId()) { + if (!$data->getId()) { throw new ValidatorException('Id is required'); } @@ -38,7 +38,7 @@ class PutAction extends AbstractOgDhcpController ] ]; - $content = $this->createRequest($httpClient, 'POST', $this->ogDhcpApiUrl.'/ogdhcp/v1/subnets/'.$data->getId(), $params); + $content = $this->createRequest($httpClient, 'PUT', $this->ogDhcpApiUrl.'/ogdhcp/v1/subnets/'.$data->getId(), $params); $this->entityManager->persist($data); $this->entityManager->flush(); diff --git a/src/Controller/OgDhcp/Subnet/PutHostAction.php b/src/Controller/OgDhcp/Subnet/PutHostAction.php index 55d7f6f..3815ca4 100644 --- a/src/Controller/OgDhcp/Subnet/PutHostAction.php +++ b/src/Controller/OgDhcp/Subnet/PutHostAction.php @@ -5,6 +5,7 @@ namespace App\Controller\OgDhcp\Subnet; use App\Controller\OgDhcp\AbstractOgDhcpController; use App\Dto\Input\SubnetAddHostInput; use App\Entity\Client; +use App\Entity\Subnet; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\Attribute\AsController; @@ -23,15 +24,15 @@ class PutHostAction extends AbstractOgDhcpController * @throws RedirectionExceptionInterface * @throws ClientExceptionInterface */ - public function __invoke(SubnetAddHostInput $input, HttpClientInterface $httpClient): JsonResponse + public function __invoke(SubnetAddHostInput $input, Subnet $subnet, HttpClientInterface $httpClient): JsonResponse { - $subnet = $input->subnet->getEntity(); $clients = $input->clients; - /** @var Client $client */ foreach ($clients as $client) { + /** @var Client $clientEntity */ + $clientEntity = $client->getEntity(); $data = [ - 'host' => $client->getName(), + 'host' => $clientEntity->getName(), 'oldMacAddress' => '', 'macAddress' => '', 'address' => '', diff --git a/src/Dto/Input/SubnetAddHostInput.php b/src/Dto/Input/SubnetAddHostInput.php index c5b10aa..bc5015e 100644 --- a/src/Dto/Input/SubnetAddHostInput.php +++ b/src/Dto/Input/SubnetAddHostInput.php @@ -12,11 +12,6 @@ use Symfony\Component\Validator\Constraints as Assert; final class SubnetAddHostInput { - #[Assert\NotNull(message: 'validators.subnet_add_host.subnet.not_null')] - #[Groups(['subnet:write'])] - #[ApiProperty(description: 'The subnet of the pxeBootFile', example: "Subnet 1")] - public ?SubnetOutput $subnet = null; - /** * @var ClientOutput[] */ -- 2.40.1 From d70d2cf3f00644b7bb8a4bdce136d2c9e2dc2994 Mon Sep 17 00:00:00 2001 From: Manuel Aranda Date: Mon, 2 Sep 2024 10:09:36 +0200 Subject: [PATCH 025/157] Some improvements. Added filter search and refactor --- config/services/api_platform.yaml | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/config/services/api_platform.yaml b/config/services/api_platform.yaml index d8f0fa4..ee1a157 100644 --- a/config/services/api_platform.yaml +++ b/config/services/api_platform.yaml @@ -18,6 +18,18 @@ services: $orderParameterName: 'order' tags: [ 'api_platform.filter' ] + api_platform.filter.og_live.order: + parent: 'api_platform.doctrine.orm.order_filter' + arguments: + $properties: { 'id': ~, 'name': ~ } + $orderParameterName: 'order' + tags: [ 'api_platform.filter' ] + + api_platform.filter.og_live.search: + parent: 'api_platform.doctrine.orm.search_filter' + arguments: [ { 'id': 'exact', 'name': 'partial', } ] + tags: [ 'api_platform.filter' ] + api_platform.filter.hardware.search: parent: 'api_platform.doctrine.orm.search_filter' arguments: [ { 'id': 'exact', 'name': 'partial' } ] @@ -59,6 +71,18 @@ services: arguments: [ { 'id': 'exact', 'usage': 'exact', 'diskNumber': 'exact' } ] tags: [ 'api_platform.filter' ] + api_platform.filter.pxe_template.order: + parent: 'api_platform.doctrine.orm.order_filter' + arguments: + $properties: { 'id': ~, 'name': ~ } + $orderParameterName: 'order' + tags: [ 'api_platform.filter' ] + + api_platform.filter.pxe_template.search: + parent: 'api_platform.doctrine.orm.search_filter' + arguments: [ { 'id': 'exact', 'name': 'partial', } ] + tags: [ 'api_platform.filter' ] + api_platform.filter.user.order: parent: 'api_platform.doctrine.orm.order_filter' arguments: -- 2.40.1 From 68b460935f9cf6e496cebc1365194d968001faee Mon Sep 17 00:00:00 2001 From: Manuel Aranda Date: Mon, 2 Sep 2024 12:16:40 +0200 Subject: [PATCH 026/157] Change Output and added ogbott filters --- config/api_platform/OgLive.yaml | 1 + config/api_platform/PxeTemplate.yaml | 1 + config/services/api_platform.yaml | 15 +++++++++++++++ src/Dto/Output/ClientOutput.php | 6 +++--- src/Entity/OgLive.php | 4 ++-- 5 files changed, 22 insertions(+), 5 deletions(-) diff --git a/config/api_platform/OgLive.yaml b/config/api_platform/OgLive.yaml index e24c61b..e1b3327 100644 --- a/config/api_platform/OgLive.yaml +++ b/config/api_platform/OgLive.yaml @@ -13,6 +13,7 @@ resources: filters: - 'api_platform.filter.og_live.order' - 'api_platform.filter.og_live.search' + - 'api_platform.filter.og_live.boolean' ApiPlatform\Metadata\Get: provider: App\State\Provider\OgLiveProvider diff --git a/config/api_platform/PxeTemplate.yaml b/config/api_platform/PxeTemplate.yaml index 0e36c96..b990141 100644 --- a/config/api_platform/PxeTemplate.yaml +++ b/config/api_platform/PxeTemplate.yaml @@ -13,6 +13,7 @@ resources: filters: - 'api_platform.filter.pxe_template.order' - 'api_platform.filter.pxe_template.search' + - 'api_platform.filter.pxe_template.boolean' ApiPlatform\Metadata\Get: provider: App\State\Provider\PxeTemplateProvider diff --git a/config/services/api_platform.yaml b/config/services/api_platform.yaml index ee1a157..fb2af43 100644 --- a/config/services/api_platform.yaml +++ b/config/services/api_platform.yaml @@ -30,6 +30,11 @@ services: arguments: [ { 'id': 'exact', 'name': 'partial', } ] tags: [ 'api_platform.filter' ] + api_platform.filter.og_live.boolean: + parent: 'api_platform.doctrine.orm.boolean_filter' + arguments: [ { 'isDefault': ~, 'installed': ~ } ] + tags: [ 'api_platform.filter' ] + api_platform.filter.hardware.search: parent: 'api_platform.doctrine.orm.search_filter' arguments: [ { 'id': 'exact', 'name': 'partial' } ] @@ -78,11 +83,21 @@ services: $orderParameterName: 'order' tags: [ 'api_platform.filter' ] + api_platform.filter.pxe_boot_file.search: + parent: 'api_platform.doctrine.orm.search_filter' + arguments: [ { 'id': 'exact', 'template': exact } ] + tags: [ 'api_platform.filter' ] + api_platform.filter.pxe_template.search: parent: 'api_platform.doctrine.orm.search_filter' arguments: [ { 'id': 'exact', 'name': 'partial', } ] tags: [ 'api_platform.filter' ] + api_platform.filter.pxe_template.boolean: + parent: 'api_platform.doctrine.orm.boolean_filter' + arguments: [ { 'synchronized': ~ } ] + tags: [ 'api_platform.filter' ] + api_platform.filter.user.order: parent: 'api_platform.doctrine.orm.order_filter' arguments: diff --git a/src/Dto/Output/ClientOutput.php b/src/Dto/Output/ClientOutput.php index 2fe5ff3..07cb0a1 100644 --- a/src/Dto/Output/ClientOutput.php +++ b/src/Dto/Output/ClientOutput.php @@ -13,16 +13,16 @@ final class ClientOutput extends AbstractOutput { CONST string TYPE = 'client'; - #[Groups(['client:read', 'organizational-unit:read'])] + #[Groups(['client:read', 'organizational-unit:read', 'pxe-boot-file:read'])] public string $name; #[Groups(['client:read', 'organizational-unit:read'])] public string $type = self::TYPE; - #[Groups(['client:read', 'organizational-unit:read'])] + #[Groups(['client:read', 'organizational-unit:read', 'pxe-boot-file:read'])] public ?string $ip = ''; - #[Groups(['client:read', 'organizational-unit:read'])] + #[Groups(['client:read', 'organizational-unit:read', 'pxe-boot-file:read'])] public ?string $mac = ''; #[Groups(['client:read', 'organizational-unit:read'])] diff --git a/src/Entity/OgLive.php b/src/Entity/OgLive.php index 4375882..2983c79 100644 --- a/src/Entity/OgLive.php +++ b/src/Entity/OgLive.php @@ -41,10 +41,10 @@ class OgLive extends AbstractEntity private ?string $filename = null; #[ORM\Column(nullable: true)] - private ?bool $installed = null; + private ?bool $installed = false; #[ORM\Column(nullable: true)] - private ?bool $isDefault = null; + private ?bool $isDefault = false; /** * @var Collection -- 2.40.1 From 030d35f47e563855f0e18e0ee7cb69f7728dda50 Mon Sep 17 00:00:00 2001 From: Manuel Aranda Date: Mon, 2 Sep 2024 14:38:22 +0200 Subject: [PATCH 027/157] Partial commit --- src/Entity/Command.php | 48 ++++++++++++++++++++++++++++ src/Repository/CommandRepository.php | 43 +++++++++++++++++++++++++ 2 files changed, 91 insertions(+) create mode 100644 src/Entity/Command.php create mode 100644 src/Repository/CommandRepository.php diff --git a/src/Entity/Command.php b/src/Entity/Command.php new file mode 100644 index 0000000..f4cf530 --- /dev/null +++ b/src/Entity/Command.php @@ -0,0 +1,48 @@ +id; + } + + public function getScript(): ?string + { + return $this->script; + } + + public function setScript(string $script): static + { + $this->script = $script; + + return $this; + } + + public function getComments(): ?string + { + return $this->comments; + } + + public function setComments(?string $comments): static + { + $this->comments = $comments; + + return $this; + } +} diff --git a/src/Repository/CommandRepository.php b/src/Repository/CommandRepository.php new file mode 100644 index 0000000..5afe393 --- /dev/null +++ b/src/Repository/CommandRepository.php @@ -0,0 +1,43 @@ + + */ +class CommandRepository extends ServiceEntityRepository +{ + public function __construct(ManagerRegistry $registry) + { + parent::__construct($registry, Command::class); + } + + // /** + // * @return Command[] Returns an array of Command objects + // */ + // public function findByExampleField($value): array + // { + // return $this->createQueryBuilder('c') + // ->andWhere('c.exampleField = :val') + // ->setParameter('val', $value) + // ->orderBy('c.id', 'ASC') + // ->setMaxResults(10) + // ->getQuery() + // ->getResult() + // ; + // } + + // public function findOneBySomeField($value): ?Command + // { + // return $this->createQueryBuilder('c') + // ->andWhere('c.exampleField = :val') + // ->setParameter('val', $value) + // ->getQuery() + // ->getOneOrNullResult() + // ; + // } +} -- 2.40.1 From 2cf6730fbb06a5c89cda4716c3ea5d7f11b84e10 Mon Sep 17 00:00:00 2001 From: Manuel Aranda Date: Tue, 3 Sep 2024 10:37:41 +0200 Subject: [PATCH 028/157] refs #617. DHCP new changes in API --- migrations/Version20240902124157.php | 33 ++++++++++++++ migrations/Version20240903081001.php | 35 +++++++++++++++ src/Dto/Input/NetworkSettingsInput.php | 10 +++++ src/Dto/Input/SubnetInput.php | 20 +++++++++ src/Dto/Output/NetworkSettingsOutput.php | 8 ++++ src/Dto/Output/SubnetOutput.php | 10 +++++ src/Entity/NetworkSettings.php | 27 +++++++++++- src/Entity/OrganizationalUnit.php | 15 +++++++ src/Entity/Subnet.php | 55 ++++++++++++++++++++++++ 9 files changed, 211 insertions(+), 2 deletions(-) create mode 100644 migrations/Version20240902124157.php create mode 100644 migrations/Version20240903081001.php diff --git a/migrations/Version20240902124157.php b/migrations/Version20240902124157.php new file mode 100644 index 0000000..5c320b9 --- /dev/null +++ b/migrations/Version20240902124157.php @@ -0,0 +1,33 @@ +addSql('ALTER TABLE network_settings ADD next_server VARCHAR(255) DEFAULT NULL, ADD boot_file_name 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 network_settings ADD og_live_id INT DEFAULT NULL, DROP next_server, DROP boot_file_name'); + $this->addSql('ALTER TABLE network_settings ADD CONSTRAINT FK_48869B54F7E54CF3 FOREIGN KEY (og_live_id) REFERENCES og_live (id)'); + + } +} diff --git a/migrations/Version20240903081001.php b/migrations/Version20240903081001.php new file mode 100644 index 0000000..9202867 --- /dev/null +++ b/migrations/Version20240903081001.php @@ -0,0 +1,35 @@ +addSql('ALTER TABLE organizational_unit ADD subnet_id INT DEFAULT NULL'); + $this->addSql('ALTER TABLE organizational_unit ADD CONSTRAINT FK_749AEB2DC9CF9478 FOREIGN KEY (subnet_id) REFERENCES subnet (id)'); + $this->addSql('CREATE INDEX IDX_749AEB2DC9CF9478 ON organizational_unit (subnet_id)'); + } + + public function down(Schema $schema): void + { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql('ALTER TABLE organizational_unit DROP FOREIGN KEY FK_749AEB2DC9CF9478'); + $this->addSql('DROP INDEX IDX_749AEB2DC9CF9478 ON organizational_unit'); + $this->addSql('ALTER TABLE organizational_unit DROP subnet_id'); + } +} diff --git a/src/Dto/Input/NetworkSettingsInput.php b/src/Dto/Input/NetworkSettingsInput.php index e32c732..039e6f9 100644 --- a/src/Dto/Input/NetworkSettingsInput.php +++ b/src/Dto/Input/NetworkSettingsInput.php @@ -15,6 +15,12 @@ use Symfony\Component\Validator\Constraints as Assert; class NetworkSettingsInput { + #[Groups(['organizational-unit:write'])] + public ?string $nextServer = null; + + #[Groups(['organizational-unit:write'])] + public ?string $bootFileName = null; + #[Groups(['organizational-unit:write'])] public ?string $proxy = null; @@ -71,6 +77,8 @@ class NetworkSettingsInput return; } + $this->nextServer = $networkSettings->getNextServer(); + $this->bootFileName = $networkSettings->getBootFileName(); $this->proxy = $networkSettings->getProxy(); $this->dns = $networkSettings->getDns(); $this->netmask = $networkSettings->getNetmask(); @@ -100,6 +108,8 @@ class NetworkSettingsInput $networkSettings = new NetworkSettings(); } + $networkSettings->setNextServer($this->nextServer); + $networkSettings->setBootFileName($this->bootFileName); $networkSettings->setProxy($this->proxy); $networkSettings->setDns($this->dns); $networkSettings->setNetmask($this->netmask); diff --git a/src/Dto/Input/SubnetInput.php b/src/Dto/Input/SubnetInput.php index d536042..98fed68 100644 --- a/src/Dto/Input/SubnetInput.php +++ b/src/Dto/Input/SubnetInput.php @@ -3,6 +3,8 @@ namespace App\Dto\Input; use ApiPlatform\Metadata\ApiProperty; +use App\Dto\Output\OrganizationalUnitOutput; +use App\Dto\Output\SubnetOutput; use App\Entity\Subnet; use Symfony\Component\Serializer\Annotation\Groups; use Symfony\Component\Validator\Constraints as Assert; @@ -34,6 +36,13 @@ final class SubnetInput #[ApiProperty(description: 'The boot file name of the subnet', example: "")] public ?string $bootFileName = null; + /** + * @var OrganizationalUnitOutput[] + */ + #[Groups('user:write')] + #[ApiProperty(readableLink: false, writableLink: false)] + public array $organizationalUnits = []; + public function __construct(?Subnet $subnet = null) { if (!$subnet) { @@ -45,6 +54,12 @@ final class SubnetInput $this->ipAddress = $subnet->getIpAddress(); $this->nextServer = $subnet->getNextServer(); $this->bootFileName = $subnet->getBootFileName(); + + if ($subnet->getOrganizationalUnits()) { + foreach ($subnet->getOrganizationalUnits() as $organizationalUnit) { + $this->organizationalUnits[] = new OrganizationalUnitOutput($organizationalUnit); + } + } } public function createOrUpdateEntity(?Subnet $subnet = null): Subnet @@ -59,6 +74,11 @@ final class SubnetInput $subnet->setNextServer($this->nextServer); $subnet->setBootFileName($this->bootFileName); + foreach ($this->organizationalUnits as $organizationalUnit) { + $organizationalUnitToAdd[] = $organizationalUnit->getEntity(); + } + $subnet->setOrganizationalUnits($organizationalUnitToAdd ?? [] ); + return $subnet; } } \ No newline at end of file diff --git a/src/Dto/Output/NetworkSettingsOutput.php b/src/Dto/Output/NetworkSettingsOutput.php index 9c0b66d..d258489 100644 --- a/src/Dto/Output/NetworkSettingsOutput.php +++ b/src/Dto/Output/NetworkSettingsOutput.php @@ -9,6 +9,12 @@ use Symfony\Component\Serializer\Annotation\Groups; #[Get(shortName: 'NetworkSettings')] final class NetworkSettingsOutput extends AbstractOutput { + #[Groups(['network-settings:read', "organizational-unit:read", "client:read"])] + public ?string $nextServer = null; + + #[Groups(['network-settings:read', "organizational-unit:read", "client:read"])] + public ?string $bootFileName = null; + #[Groups(['network-settings:read', "organizational-unit:read", "client:read"])] public ?string $proxy = null; @@ -61,6 +67,8 @@ final class NetworkSettingsOutput extends AbstractOutput { parent::__construct($networkSettings); + $this->nextServer = $networkSettings->getNextServer(); + $this->bootFileName = $networkSettings->getBootFileName(); $this->proxy = $networkSettings->getProxy(); $this->dns = $networkSettings->getDns(); $this->netmask = $networkSettings->getNetmask(); diff --git a/src/Dto/Output/SubnetOutput.php b/src/Dto/Output/SubnetOutput.php index 92cf65d..9366f42 100644 --- a/src/Dto/Output/SubnetOutput.php +++ b/src/Dto/Output/SubnetOutput.php @@ -4,6 +4,7 @@ namespace App\Dto\Output; use ApiPlatform\Metadata\ApiProperty; use ApiPlatform\Metadata\Get; +use App\Entity\OrganizationalUnit; use App\Entity\Subnet; use Symfony\Component\Serializer\Annotation\Groups; @@ -25,6 +26,9 @@ final class SubnetOutput extends AbstractOutput #[Groups(['subnet:read'])] public string $bootFileName; + #[Groups(['subnet:read'])] + public array $organizationalUnits; + #[Groups(['subnet:read'])] public \DateTime $createdAt; @@ -40,6 +44,12 @@ final class SubnetOutput extends AbstractOutput $this->ipAddress = $subnet->getIpAddress(); $this->nextServer = $subnet->getNextServer(); $this->bootFileName = $subnet->getBootFileName(); + + + $this->organizationalUnits = $subnet->getOrganizationalUnits()->map( + fn(OrganizationalUnit $organizationalUnit) => new OrganizationalUnitOutput($organizationalUnit) + )->toArray(); + $this->createdAt = $subnet->getCreatedAt(); $this->createdBy = $subnet->getCreatedBy(); } diff --git a/src/Entity/NetworkSettings.php b/src/Entity/NetworkSettings.php index de3b9c9..77df412 100644 --- a/src/Entity/NetworkSettings.php +++ b/src/Entity/NetworkSettings.php @@ -10,6 +10,12 @@ use Doctrine\ORM\Mapping as ORM; #[ORM\Entity(repositoryClass: NetworkSettingsRepository::class)] class NetworkSettings extends AbstractEntity { + #[ORM\Column(length: 255, nullable: true)] + private ?string $nextServer = null; + + #[ORM\Column(length: 255, nullable: true)] + private ?string $bootFileName = null; + #[ORM\Column(length: 255, nullable: true)] private ?string $proxy = null; @@ -69,9 +75,26 @@ class NetworkSettings extends AbstractEntity $this->organizationalUnits = new ArrayCollection(); } - public function getId(): ?int + public function getNextServer(): ?string { - return $this->id; + return $this->nextServer; + } + + public function setNextServer(?string $nextServer): void + { + $this->nextServer = $nextServer; + } + + public function getBootFileName(): ?string + { + return $this->bootFileName; + } + + public function setBootFileName(?string $bootFileName): static + { + $this->bootFileName = $bootFileName; + + return $this; } public function getProxy(): ?string diff --git a/src/Entity/OrganizationalUnit.php b/src/Entity/OrganizationalUnit.php index 469a9f5..29bdd20 100644 --- a/src/Entity/OrganizationalUnit.php +++ b/src/Entity/OrganizationalUnit.php @@ -84,6 +84,9 @@ class OrganizationalUnit extends AbstractEntity #[ORM\OneToMany(mappedBy: 'organizationalUnit', targetEntity: SoftwareProfile::class)] private Collection $softwareProfiles; + #[ORM\ManyToOne(inversedBy: 'organizationalUnits')] + private ?Subnet $subnet = null; + public function __construct() { parent::__construct(); @@ -368,4 +371,16 @@ class OrganizationalUnit extends AbstractEntity return $this; } + + public function getSubnet(): ?Subnet + { + return $this->subnet; + } + + public function setSubnet(?Subnet $subnet): static + { + $this->subnet = $subnet; + + return $this; + } } diff --git a/src/Entity/Subnet.php b/src/Entity/Subnet.php index 5693555..6fc7c15 100644 --- a/src/Entity/Subnet.php +++ b/src/Entity/Subnet.php @@ -3,6 +3,8 @@ namespace App\Entity; use App\Repository\SubnetRepository; +use Doctrine\Common\Collections\ArrayCollection; +use Doctrine\Common\Collections\Collection; use Doctrine\ORM\Mapping as ORM; #[ORM\Entity(repositoryClass: SubnetRepository::class)] @@ -22,6 +24,18 @@ class Subnet extends AbstractEntity #[ORM\Column(length: 255)] private ?string $bootFileName = null; + /** + * @var Collection + */ + #[ORM\OneToMany(mappedBy: 'subnet', targetEntity: OrganizationalUnit::class)] + private Collection $organizationalUnits; + + public function __construct() + { + parent::__construct(); + $this->organizationalUnits = new ArrayCollection(); + } + public function getNetmask(): ?string { return $this->netmask; @@ -69,4 +83,45 @@ class Subnet extends AbstractEntity return $this; } + + /** + * @return Collection + */ + public function getOrganizationalUnits(): Collection + { + return $this->organizationalUnits; + } + + public function setOrganizationalUnits(array $organizationalUnits): static + { + $this->organizationalUnits->clear(); + + foreach ($organizationalUnits as $organizationalUnit){ + $this->addOrganizationalUnit($organizationalUnit); + } + + return $this; + } + + public function addOrganizationalUnit(OrganizationalUnit $organizationalUnit): static + { + if (!$this->organizationalUnits->contains($organizationalUnit)) { + $this->organizationalUnits->add($organizationalUnit); + $organizationalUnit->setSubnet($this); + } + + return $this; + } + + public function removeOrganizationalUnit(OrganizationalUnit $organizationalUnit): static + { + if ($this->organizationalUnits->removeElement($organizationalUnit)) { + // set the owning side to null (unless already changed) + if ($organizationalUnit->getSubnet() === $this) { + $organizationalUnit->setSubnet(null); + } + } + + return $this; + } } -- 2.40.1 From ebd3d7e539a6f00c2018a405bc8b80824fecdf15 Mon Sep 17 00:00:00 2001 From: Manuel Aranda Date: Wed, 4 Sep 2024 10:14:52 +0200 Subject: [PATCH 029/157] refs #658. OgBoot Webhook --- config/packages/security.yaml | 1 + .../Webhook/InstallOgLiveResponseAction.php | 36 ++++++++++ src/OpenApi/OpenApiFactory.php | 67 ++++++++++++++++++- 3 files changed, 103 insertions(+), 1 deletion(-) create mode 100644 src/Controller/OgBoot/OgLive/Webhook/InstallOgLiveResponseAction.php diff --git a/config/packages/security.yaml b/config/packages/security.yaml index 5e2315b..3bf853d 100644 --- a/config/packages/security.yaml +++ b/config/packages/security.yaml @@ -29,6 +29,7 @@ security: - { path: ^/$, roles: PUBLIC_ACCESS } # Allows accessing the Swagger UI - { path: ^/docs, roles: PUBLIC_ACCESS } # Allows accessing the Swagger UI docs - { path: ^/auth/login, roles: PUBLIC_ACCESS } + - { path: ^/og-lives/install/webhook, roles: PUBLIC_ACCESS } - { path: ^/auth/refresh, roles: PUBLIC_ACCESS } - { path: ^/, roles: IS_AUTHENTICATED_FULLY } diff --git a/src/Controller/OgBoot/OgLive/Webhook/InstallOgLiveResponseAction.php b/src/Controller/OgBoot/OgLive/Webhook/InstallOgLiveResponseAction.php new file mode 100644 index 0000000..caf97e6 --- /dev/null +++ b/src/Controller/OgBoot/OgLive/Webhook/InstallOgLiveResponseAction.php @@ -0,0 +1,36 @@ +getContent(), true); + + return new JsonResponse(data: ['hola caracola'], status: Response::HTTP_OK); + } +} \ No newline at end of file diff --git a/src/OpenApi/OpenApiFactory.php b/src/OpenApi/OpenApiFactory.php index dc755c8..7b8a744 100644 --- a/src/OpenApi/OpenApiFactory.php +++ b/src/OpenApi/OpenApiFactory.php @@ -20,7 +20,8 @@ final readonly class OpenApiFactory implements OpenApiFactoryInterface $this->addRefreshToken($openApi); $this->addSearchEndpoint($openApi); - $this->addStatusEndpoint($openApi); // Agregar el nuevo endpoint + $this->addStatusEndpoint($openApi); + $this->addInstallOgLiveWebhookEndpoint($openApi); // Añadir aquí return $openApi; } @@ -175,4 +176,68 @@ final readonly class OpenApiFactory implements OpenApiFactoryInterface ->withSummary('Get service status') )); } + + private function addInstallOgLiveWebhookEndpoint(OpenApi $openApi): void + { + $openApi + ->getPaths() + ->addPath('/og-lives/install/webhook', (new Model\PathItem())->withPost( + (new Model\Operation('postInstallOgLiveWebhook')) + ->withTags(['OgLive']) + ->withResponses([ + Response::HTTP_OK => [ + 'description' => 'Webhook installation successful', + 'content' => [ + 'application/json' => [ + 'schema' => [ + 'type' => 'object', + 'properties' => [ + 'message' => [ + 'type' => 'string', + 'example' => 'Webhook installed successfully', + ], + ], + ], + ], + ], + ], + Response::HTTP_BAD_REQUEST => [ + 'description' => 'Invalid request data', + 'content' => [ + 'application/json' => [ + 'schema' => [ + 'type' => 'object', + 'properties' => [ + 'error' => [ + 'type' => 'string', + 'example' => 'Invalid input data', + ], + ], + ], + ], + ], + ], + ]) + ->withSummary('Install a webhook for OgLive') + ->withRequestBody( + (new Model\RequestBody()) + ->withDescription('Data required for installing the webhook') + ->withContent(new \ArrayObject([ + 'application/json' => new Model\MediaType(new \ArrayObject([ + 'type' => 'object', + 'properties' => [ + 'da' => [ + 'type' => 'string', + 'description' => 'The URL to set for the webhook', + 'example' => 'https://example.com/webhook', + ], + ], + 'required' => ['url'], + ])) + ])) + ->withRequired(true) + ) + )); + } + } \ No newline at end of file -- 2.40.1 From 07b419016e96c7414a848a443452716cc6fc4cb1 Mon Sep 17 00:00:00 2001 From: Manuel Aranda Date: Wed, 4 Sep 2024 16:11:58 +0200 Subject: [PATCH 030/157] refs #617. Added single host integration --- config/api_platform/Subnet.yaml | 9 ++++ migrations/Version20240904134540.php | 35 +++++++++++++ .../OgDhcp/Subnet/AddSingleHostAction.php | 52 +++++++++++++++++++ .../OgDhcp/Subnet/PostHostAction.php | 6 +++ src/Dto/Input/SubnetAddSingleHostInput.php | 15 ++++++ src/Entity/Client.php | 15 ++++++ src/Entity/Subnet.php | 48 +++++++++++++++++ 7 files changed, 180 insertions(+) create mode 100644 migrations/Version20240904134540.php create mode 100644 src/Controller/OgDhcp/Subnet/AddSingleHostAction.php create mode 100644 src/Dto/Input/SubnetAddSingleHostInput.php diff --git a/config/api_platform/Subnet.yaml b/config/api_platform/Subnet.yaml index 1755518..2222cda 100644 --- a/config/api_platform/Subnet.yaml +++ b/config/api_platform/Subnet.yaml @@ -68,6 +68,15 @@ resources: uriTemplate: /og-dhcp/server/{uuid}/delete controller: App\Controller\OgDhcp\Subnet\DeleteAction + add_single_host: + shortName: Subnet Server Hosts + description: Add Single Host to Subnet + class: ApiPlatform\Metadata\Post + method: POST + input: App\Dto\Input\SubnetAddSingleHostInput + uriTemplate: /og-dhcp/server/{uuid}/add-single-host + controller: App\Controller\OgDhcp\Subnet\AddSingleHostAction + post_host: shortName: Subnet Server Hosts description: Post Host to Subnet diff --git a/migrations/Version20240904134540.php b/migrations/Version20240904134540.php new file mode 100644 index 0000000..df9dd35 --- /dev/null +++ b/migrations/Version20240904134540.php @@ -0,0 +1,35 @@ +addSql('ALTER TABLE client CHANGE og_live_id subnet_id INT DEFAULT NULL'); + $this->addSql('ALTER TABLE client ADD CONSTRAINT FK_C7440455C9CF9478 FOREIGN KEY (subnet_id) REFERENCES subnet (id)'); + $this->addSql('CREATE INDEX IDX_C7440455C9CF9478 ON client (subnet_id)'); + } + + 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_C7440455C9CF9478'); + $this->addSql('DROP INDEX IDX_C7440455C9CF9478 ON client'); + $this->addSql('ALTER TABLE client CHANGE subnet_id og_live_id INT DEFAULT NULL'); + } +} diff --git a/src/Controller/OgDhcp/Subnet/AddSingleHostAction.php b/src/Controller/OgDhcp/Subnet/AddSingleHostAction.php new file mode 100644 index 0000000..dcaf665 --- /dev/null +++ b/src/Controller/OgDhcp/Subnet/AddSingleHostAction.php @@ -0,0 +1,52 @@ +client; + /** @var Client $clientEntity */ + $clientEntity = $client->getEntity(); + + $params = [ + 'json' => [ + 'host' => $clientEntity->getName(), + 'macAddress' => $clientEntity->getMac(), + 'address' => $clientEntity->getIp(), + ] + ]; + + $content = $this->createRequest($httpClient, 'POST', $this->ogDhcpApiUrl.'/ogdhcp/v1/subnets/'.$subnet->getId().'/hosts', $params); + + if ($content->getStatusCode() === 200) { + $subnet->addClient($clientEntity); + $this->entityManager->persist($subnet); + $this->entityManager->flush(); + } + + return new JsonResponse(data: $content, status: Response::HTTP_OK); + } +} \ No newline at end of file diff --git a/src/Controller/OgDhcp/Subnet/PostHostAction.php b/src/Controller/OgDhcp/Subnet/PostHostAction.php index d1c15c9..319025e 100644 --- a/src/Controller/OgDhcp/Subnet/PostHostAction.php +++ b/src/Controller/OgDhcp/Subnet/PostHostAction.php @@ -20,6 +20,7 @@ use Symfony\Contracts\HttpClient\HttpClientInterface; #[AsController] class PostHostAction extends AbstractOgDhcpController { + /** * @throws TransportExceptionInterface * @throws ServerExceptionInterface @@ -30,9 +31,14 @@ class PostHostAction extends AbstractOgDhcpController { $clients = $input->clients; + $subnet->setClients($clients); + $this->entityManager->persist($subnet); + $this->entityManager->flush(); + foreach ($clients as $client) { /** @var Client $clientEntity */ $clientEntity = $client->getEntity(); + $data = [ 'host' => $clientEntity->getName(), 'macAddress' => $clientEntity->getMac(), diff --git a/src/Dto/Input/SubnetAddSingleHostInput.php b/src/Dto/Input/SubnetAddSingleHostInput.php new file mode 100644 index 0000000..855261b --- /dev/null +++ b/src/Dto/Input/SubnetAddSingleHostInput.php @@ -0,0 +1,15 @@ +subnet; + } + + public function setSubnet(?Subnet $subnet): static + { + $this->subnet = $subnet; + + return $this; + } } diff --git a/src/Entity/Subnet.php b/src/Entity/Subnet.php index 6fc7c15..719de65 100644 --- a/src/Entity/Subnet.php +++ b/src/Entity/Subnet.php @@ -30,10 +30,17 @@ class Subnet extends AbstractEntity #[ORM\OneToMany(mappedBy: 'subnet', targetEntity: OrganizationalUnit::class)] private Collection $organizationalUnits; + /** + * @var Collection + */ + #[ORM\OneToMany(mappedBy: 'subnet', targetEntity: Client::class)] + private Collection $clients; + public function __construct() { parent::__construct(); $this->organizationalUnits = new ArrayCollection(); + $this->clients = new ArrayCollection(); } public function getNetmask(): ?string @@ -124,4 +131,45 @@ class Subnet extends AbstractEntity return $this; } + + /** + * @return Collection + */ + public function getClients(): Collection + { + return $this->clients; + } + + public function setClients(array $clients): static + { + $this->clients->clear(); + + foreach ($clients as $client){ + $this->addClient($client); + } + + return $this; + } + + public function addClient(Client $client): static + { + if (!$this->clients->contains($client)) { + $this->clients->add($client); + $client->setSubnet($this); + } + + return $this; + } + + public function removeClient(Client $client): static + { + if ($this->clients->removeElement($client)) { + // set the owning side to null (unless already changed) + if ($client->getSubnet() === $this) { + $client->setSubnet(null); + } + } + + return $this; + } } -- 2.40.1 From 5d9b34709850cf6dfe232f9f968635f9bdfda384 Mon Sep 17 00:00:00 2001 From: Manuel Aranda Date: Wed, 4 Sep 2024 16:20:48 +0200 Subject: [PATCH 031/157] refs #617. Added single classroom integration --- config/api_platform/Subnet.yaml | 9 ++++ .../AddSingleOrganizationalUnitAction.php | 54 +++++++++++++++++++ src/Dto/Input/SubnetAddSingleHostInput.php | 2 +- ...SubnetAddSingleOrganizationalUnitInput.php | 15 ++++++ 4 files changed, 79 insertions(+), 1 deletion(-) create mode 100644 src/Controller/OgDhcp/Subnet/AddSingleOrganizationalUnitAction.php create mode 100644 src/Dto/Input/SubnetAddSingleOrganizationalUnitInput.php diff --git a/config/api_platform/Subnet.yaml b/config/api_platform/Subnet.yaml index 2222cda..3f19ae8 100644 --- a/config/api_platform/Subnet.yaml +++ b/config/api_platform/Subnet.yaml @@ -113,6 +113,15 @@ resources: uriTemplate: /og-dhcp/server/{uuid}/delete-host controller: App\Controller\OgDhcp\Subnet\DeleteHostAction + add_single_organizational_unit: + shortName: Subnet Server Organizational Units + description: Add Single Organizational Unit to Subnet + class: ApiPlatform\Metadata\Post + method: POST + input: App\Dto\Input\SubnetAddSingleOrganizationalUnitInput + uriTemplate: /og-dhcp/server/{uuid}/add-single-organizational-unit + controller: App\Controller\OgDhcp\Subnet\AddSingleOrganizationalUnitAction + properties: App\Entity\Subnet: id: diff --git a/src/Controller/OgDhcp/Subnet/AddSingleOrganizationalUnitAction.php b/src/Controller/OgDhcp/Subnet/AddSingleOrganizationalUnitAction.php new file mode 100644 index 0000000..ced5db2 --- /dev/null +++ b/src/Controller/OgDhcp/Subnet/AddSingleOrganizationalUnitAction.php @@ -0,0 +1,54 @@ +organizationalUnitOutput; + /** @var OrganizationalUnit $organizationalUnitEntity */ + $organizationalUnitEntity = $ou->getEntity(); + + $params = [ + 'json' => [ + 'name' => $organizationalUnitEntity->getId(), + 'nextServer' => $organizationalUnitEntity->getNetworkSettings()?->getNextServer(), + 'bootFileName' => $organizationalUnitEntity->getNetworkSettings()?->getBootFileName(), + ] + ]; + + $content = $this->createRequest($httpClient, 'POST', $this->ogDhcpApiUrl.'/ogdhcp/v1/subnets/'.$subnet->getId().'/classroom', $params); + + if ($content->getStatusCode() === 200) { + $subnet->addOrganizationalUnit($organizationalUnitEntity); + $this->entityManager->persist($subnet); + $this->entityManager->flush(); + } + + return new JsonResponse(data: $content, status: Response::HTTP_OK); + } +} \ No newline at end of file diff --git a/src/Dto/Input/SubnetAddSingleHostInput.php b/src/Dto/Input/SubnetAddSingleHostInput.php index 855261b..5075161 100644 --- a/src/Dto/Input/SubnetAddSingleHostInput.php +++ b/src/Dto/Input/SubnetAddSingleHostInput.php @@ -11,5 +11,5 @@ final class SubnetAddSingleHostInput { #[Assert\NotNull] #[Groups(['subnet:write'])] - public ClientOutput $client; + public ?ClientOutput $client = null; } \ No newline at end of file diff --git a/src/Dto/Input/SubnetAddSingleOrganizationalUnitInput.php b/src/Dto/Input/SubnetAddSingleOrganizationalUnitInput.php new file mode 100644 index 0000000..7576e1b --- /dev/null +++ b/src/Dto/Input/SubnetAddSingleOrganizationalUnitInput.php @@ -0,0 +1,15 @@ + Date: Thu, 5 Sep 2024 10:17:10 +0200 Subject: [PATCH 032/157] refs #658. Added install fully integration --- config/services/api_platform.yaml | 2 +- migrations/Version20240905080435.php | 32 +++++++++++++++++ .../OgBoot/OgLive/InstallAction.php | 6 ++-- .../Webhook/InstallOgLiveResponseAction.php | 31 +++++++++++++++- src/Dto/Input/OgLiveInput.php | 2 ++ src/Dto/Output/OgLiveOutput.php | 4 +++ src/Entity/OgLive.php | 15 ++++++++ src/Model/OgLiveStatus.php | 36 +++++++++++++++++++ 8 files changed, 124 insertions(+), 4 deletions(-) create mode 100644 migrations/Version20240905080435.php create mode 100644 src/Model/OgLiveStatus.php diff --git a/config/services/api_platform.yaml b/config/services/api_platform.yaml index fb2af43..00d855e 100644 --- a/config/services/api_platform.yaml +++ b/config/services/api_platform.yaml @@ -8,7 +8,7 @@ services: api_platform.filter.client.search: parent: 'api_platform.doctrine.orm.search_filter' - arguments: [ { 'id': 'exact', 'name': 'partial', 'serialNumber': 'exact', organizationalUnit.id: 'exact' } ] + arguments: [ { 'id': 'exact', 'name': 'partial', 'serialNumber': 'exact', organizationalUnit.id: 'exact', 'ip': exact, 'mac': exact } ] tags: [ 'api_platform.filter' ] api_platform.filter.hardware.order: diff --git a/migrations/Version20240905080435.php b/migrations/Version20240905080435.php new file mode 100644 index 0000000..82c72f8 --- /dev/null +++ b/migrations/Version20240905080435.php @@ -0,0 +1,32 @@ +addSql('ALTER TABLE og_live ADD status 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 og_live DROP status'); + + } +} diff --git a/src/Controller/OgBoot/OgLive/InstallAction.php b/src/Controller/OgBoot/OgLive/InstallAction.php index e9b871c..2572499 100644 --- a/src/Controller/OgBoot/OgLive/InstallAction.php +++ b/src/Controller/OgBoot/OgLive/InstallAction.php @@ -4,6 +4,7 @@ namespace App\Controller\OgBoot\OgLive; use App\Controller\OgBoot\AbstractOgBootController; use App\Entity\OgLive; +use App\Model\OgLiveStatus; use Doctrine\ORM\EntityManagerInterface; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Response; @@ -32,13 +33,14 @@ class InstallAction extends AbstractOgBootController $params = [ 'json' => [ - 'url' => $data->getDownloadUrl() + 'url' => $data->getDownloadUrl(), + 'ogCoreId' => $data->getUuid() ] ]; $content = $this->createRequest($httpClient, 'POST', $this->ogBootApiUrl.'/ogboot/v1/oglives/install', $params); - $data->setInstalled(true); + $data->setStatus(OgLiveStatus::PENDING); $entityManager->persist($data); $entityManager->flush(); diff --git a/src/Controller/OgBoot/OgLive/Webhook/InstallOgLiveResponseAction.php b/src/Controller/OgBoot/OgLive/Webhook/InstallOgLiveResponseAction.php index caf97e6..61f185f 100644 --- a/src/Controller/OgBoot/OgLive/Webhook/InstallOgLiveResponseAction.php +++ b/src/Controller/OgBoot/OgLive/Webhook/InstallOgLiveResponseAction.php @@ -3,6 +3,8 @@ namespace App\Controller\OgBoot\OgLive\Webhook; use App\Controller\OgBoot\AbstractOgBootController; +use App\Entity\OgLive; +use App\Model\OgLiveStatus; use Doctrine\ORM\EntityManagerInterface; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\JsonResponse; @@ -31,6 +33,33 @@ class InstallOgLiveResponseAction extends AbstractController { $data = json_decode($request->getContent(), true); - return new JsonResponse(data: ['hola caracola'], status: Response::HTTP_OK); + if ($data === null || !isset($data['message'], $data['ogCoreId'], $data['details']['result'])) { + return new JsonResponse(['error' => 'Invalid or incomplete JSON data'], Response::HTTP_BAD_REQUEST); + } + + $message = $data['message']; + $ogCoreId = $data['ogCoreId']; + $details = $data['details']; + $result = $details['result']; + + $ogLive = $this->entityManager->getRepository(OgLive::class)->findOneBy(['uuid' => $ogCoreId]); + + if (!$ogLive) { + return new JsonResponse(['error' => 'OgLive not found'], Response::HTTP_NOT_FOUND); + } + + $ogLive->setStatus($details['status'] === 'success' ? OgLiveStatus::ACTIVE : OgLiveStatus::FAILED); + $ogLive->setInstalled($details['status'] === 'success'); + $ogLive->setChecksum($result['id']); + $ogLive->setDistribution($result['distribution']); + $ogLive->setKernel($result['kernel']); + $ogLive->setArchitecture($result['architecture']); + $ogLive->setRevision($result['revision']); + $ogLive->setDirectory($result['directory']); + + $this->entityManager->persist($ogLive); + $this->entityManager->flush(); + + return new JsonResponse(data: 'Oglive updated successfully', status: Response::HTTP_OK); } } \ No newline at end of file diff --git a/src/Dto/Input/OgLiveInput.php b/src/Dto/Input/OgLiveInput.php index d9e86df..2c86228 100644 --- a/src/Dto/Input/OgLiveInput.php +++ b/src/Dto/Input/OgLiveInput.php @@ -4,6 +4,7 @@ namespace App\Dto\Input; use ApiPlatform\Metadata\ApiProperty; use App\Entity\OgLive; +use App\Model\OgLiveStatus; use Symfony\Component\Serializer\Annotation\Groups; use Symfony\Component\Validator\Constraints as Assert; @@ -36,6 +37,7 @@ final class OgLiveInput $ogLive->setName($this->name); $ogLive->setDownloadUrl($this->downloadUrl); + $ogLive->setStatus(OgLiveStatus::INACTIVE); return $ogLive; } diff --git a/src/Dto/Output/OgLiveOutput.php b/src/Dto/Output/OgLiveOutput.php index bc9fc32..60b0d3d 100644 --- a/src/Dto/Output/OgLiveOutput.php +++ b/src/Dto/Output/OgLiveOutput.php @@ -25,6 +25,9 @@ final class OgLiveOutput extends AbstractOutput #[Groups(['og-live:read'])] public ?string $downloadUrl = ''; + #[Groups(['og-live:read'])] + public ?string $status = ''; + #[Groups(['og-live:read'])] public \DateTime $createdAt; @@ -40,6 +43,7 @@ final class OgLiveOutput extends AbstractOutput $this->installed = $ogLive->isInstalled(); $this->isDefault = $ogLive->getIsDefault(); $this->downloadUrl = $ogLive->getDownloadUrl(); + $this->status = $ogLive->getStatus(); $this->createdAt = $ogLive->getCreatedAt(); $this->createdBy = $ogLive->getCreatedBy(); } diff --git a/src/Entity/OgLive.php b/src/Entity/OgLive.php index 2983c79..df0f872 100644 --- a/src/Entity/OgLive.php +++ b/src/Entity/OgLive.php @@ -52,6 +52,9 @@ class OgLive extends AbstractEntity #[ORM\OneToMany(mappedBy: 'ogLive', targetEntity: Client::class)] private Collection $clients; + #[ORM\Column(length: 255)] + private ?string $status = null; + public function __construct() { parent::__construct(); @@ -205,4 +208,16 @@ class OgLive extends AbstractEntity return $this; } + + public function getStatus(): ?string + { + return $this->status; + } + + public function setStatus(string $status): static + { + $this->status = $status; + + return $this; + } } diff --git a/src/Model/OgLiveStatus.php b/src/Model/OgLiveStatus.php new file mode 100644 index 0000000..2b9e744 --- /dev/null +++ b/src/Model/OgLiveStatus.php @@ -0,0 +1,36 @@ + 'Pendiente', + self::ACTIVE => 'Activo', + self::INACTIVE => 'Inactivo', + self::DELETED => 'Eliminado', + self::FAILED => 'Fallido', + ]; + + public static function getOgLiveStatuses(): array + { + return self::OG_LIVE_STATUSES; + } + + public static function getOgLiveStatus(string $ogLiveStatus): ?string + { + return self::OG_LIVE_STATUSES[$ogLiveStatus] ?? null; + } + + public static function getOgLiveStatusKeys(): array + { + return array_keys(self::OG_LIVE_STATUSES); + } +} \ No newline at end of file -- 2.40.1 From aa1da83a701e2fb0208eac1944a406621ec90f74 Mon Sep 17 00:00:00 2001 From: Manuel Aranda Date: Thu, 5 Sep 2024 18:00:44 +0200 Subject: [PATCH 033/157] refs #658. Added install fully integration --- .../OgBoot/OgLive/Webhook/InstallOgLiveResponseAction.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/Controller/OgBoot/OgLive/Webhook/InstallOgLiveResponseAction.php b/src/Controller/OgBoot/OgLive/Webhook/InstallOgLiveResponseAction.php index 61f185f..336e957 100644 --- a/src/Controller/OgBoot/OgLive/Webhook/InstallOgLiveResponseAction.php +++ b/src/Controller/OgBoot/OgLive/Webhook/InstallOgLiveResponseAction.php @@ -48,6 +48,10 @@ class InstallOgLiveResponseAction extends AbstractController return new JsonResponse(['error' => 'OgLive not found'], Response::HTTP_NOT_FOUND); } + if ($ogLive->getStatus() === OgLiveStatus::ACTIVE) { + return new JsonResponse(['error' => 'OgLive is already active'], Response::HTTP_BAD_REQUEST); + } + $ogLive->setStatus($details['status'] === 'success' ? OgLiveStatus::ACTIVE : OgLiveStatus::FAILED); $ogLive->setInstalled($details['status'] === 'success'); $ogLive->setChecksum($result['id']); @@ -60,6 +64,6 @@ class InstallOgLiveResponseAction extends AbstractController $this->entityManager->persist($ogLive); $this->entityManager->flush(); - return new JsonResponse(data: 'Oglive updated successfully', status: Response::HTTP_OK); + return new JsonResponse(data: sprintf('OgLive %s updated successfully', $ogLive->getChecksum()), status: Response::HTTP_OK); } } \ No newline at end of file -- 2.40.1 From 8bfc47114464635b2f5d367785445630ff31ce11 Mon Sep 17 00:00:00 2001 From: Manuel Aranda Date: Wed, 11 Sep 2024 11:43:12 +0200 Subject: [PATCH 034/157] refs #658. Updated endpoints with failure and success logic --- .../OgBoot/OgLive/InstallAction.php | 2 +- .../Webhook/InstallOgLiveResponseAction.php | 42 +++++++++++++------ 2 files changed, 30 insertions(+), 14 deletions(-) diff --git a/src/Controller/OgBoot/OgLive/InstallAction.php b/src/Controller/OgBoot/OgLive/InstallAction.php index 2572499..e2bbe17 100644 --- a/src/Controller/OgBoot/OgLive/InstallAction.php +++ b/src/Controller/OgBoot/OgLive/InstallAction.php @@ -34,7 +34,7 @@ class InstallAction extends AbstractOgBootController $params = [ 'json' => [ 'url' => $data->getDownloadUrl(), - 'ogCoreId' => $data->getUuid() + 'id' => $data->getUuid() ] ]; diff --git a/src/Controller/OgBoot/OgLive/Webhook/InstallOgLiveResponseAction.php b/src/Controller/OgBoot/OgLive/Webhook/InstallOgLiveResponseAction.php index 336e957..f5e7cbc 100644 --- a/src/Controller/OgBoot/OgLive/Webhook/InstallOgLiveResponseAction.php +++ b/src/Controller/OgBoot/OgLive/Webhook/InstallOgLiveResponseAction.php @@ -22,6 +22,11 @@ use Symfony\Contracts\HttpClient\HttpClientInterface; #[AsController] class InstallOgLiveResponseAction extends AbstractController { + public CONST string OG_LIVE_INSTALL_SUCCESS = 'success'; + public CONST string OG_LIVE_INSTALL_FAILED = 'failure'; + + + public function __construct( protected readonly EntityManagerInterface $entityManager ) @@ -33,14 +38,15 @@ class InstallOgLiveResponseAction extends AbstractController { $data = json_decode($request->getContent(), true); - if ($data === null || !isset($data['message'], $data['ogCoreId'], $data['details']['result'])) { + if ($data === null || !isset($data['message'], $data['ogCoreId'], $data['details'])) { return new JsonResponse(['error' => 'Invalid or incomplete JSON data'], Response::HTTP_BAD_REQUEST); } $message = $data['message']; $ogCoreId = $data['ogCoreId']; $details = $data['details']; - $result = $details['result']; + $status = $data['status']; + $result = $status === self::OG_LIVE_INSTALL_SUCCESS ? $details['result'] : []; $ogLive = $this->entityManager->getRepository(OgLive::class)->findOneBy(['uuid' => $ogCoreId]); @@ -52,18 +58,28 @@ class InstallOgLiveResponseAction extends AbstractController return new JsonResponse(['error' => 'OgLive is already active'], Response::HTTP_BAD_REQUEST); } - $ogLive->setStatus($details['status'] === 'success' ? OgLiveStatus::ACTIVE : OgLiveStatus::FAILED); - $ogLive->setInstalled($details['status'] === 'success'); - $ogLive->setChecksum($result['id']); - $ogLive->setDistribution($result['distribution']); - $ogLive->setKernel($result['kernel']); - $ogLive->setArchitecture($result['architecture']); - $ogLive->setRevision($result['revision']); - $ogLive->setDirectory($result['directory']); - - $this->entityManager->persist($ogLive); - $this->entityManager->flush(); + $this->updateOgLive($ogLive, $details, $result, $status); return new JsonResponse(data: sprintf('OgLive %s updated successfully', $ogLive->getChecksum()), status: Response::HTTP_OK); } + + private function updateOgLive (OgLive $ogLive, array $details, array $result, string $status): void + { + if ($status === self::OG_LIVE_INSTALL_SUCCESS) { + $ogLive->setInstalled(true); + $ogLive->setSynchronized(true); + $ogLive->setChecksum($result['id']); + $ogLive->setDistribution($result['distribution']); + $ogLive->setKernel($result['kernel']); + $ogLive->setArchitecture($result['architecture']); + $ogLive->setRevision($result['revision']); + $ogLive->setDirectory($result['directory']); + } + + $ogLive->setStatus($status === self::OG_LIVE_INSTALL_SUCCESS ? OgLiveStatus::ACTIVE : OgLiveStatus::FAILED); + $ogLive->setInstalled($status === self::OG_LIVE_INSTALL_SUCCESS); + + $this->entityManager->persist($ogLive); + $this->entityManager->flush(); + } } \ No newline at end of file -- 2.40.1 From efdf60778a92a2548aac9e52538f4cbf60e588b5 Mon Sep 17 00:00:00 2001 From: Manuel Aranda Date: Fri, 13 Sep 2024 09:52:13 +0200 Subject: [PATCH 035/157] refs #658. Updated sync endpoint ogBoot --- src/Controller/OgBoot/OgLive/SyncAction.php | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/src/Controller/OgBoot/OgLive/SyncAction.php b/src/Controller/OgBoot/OgLive/SyncAction.php index dfabadb..4083309 100644 --- a/src/Controller/OgBoot/OgLive/SyncAction.php +++ b/src/Controller/OgBoot/OgLive/SyncAction.php @@ -4,6 +4,7 @@ namespace App\Controller\OgBoot\OgLive; use App\Controller\OgBoot\AbstractOgBootController; use App\Entity\OgLive; +use App\Model\OgLiveStatus; use Doctrine\ORM\EntityManagerInterface; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Response; @@ -40,6 +41,7 @@ class SyncAction extends AbstractOgBootController $this->entityManager->persist($ogLiveEntity); } $this->entityManager->flush(); + $this->serDefaultOgLive($content['default_oglive']); return new JsonResponse(data: $content, status: Response::HTTP_OK); } @@ -49,14 +51,30 @@ class SyncAction extends AbstractOgBootController * @param mixed $ogLive * @return void */ - public function extracted(OgLive|null $ogLiveEntity, mixed $ogLive): void + private function extracted(OgLive|null $ogLiveEntity, mixed $ogLive): void { $ogLiveEntity->setName($ogLive['filename']); $ogLiveEntity->setInstalled(true); $ogLiveEntity->setArchitecture($ogLive['architecture']); + $ogLiveEntity->setDistribution($ogLive['distribution']); + $ogLiveEntity->setFilename($ogLive['filename']); $ogLiveEntity->setKernel($ogLive['kernel']); $ogLiveEntity->setRevision($ogLive['revision']); $ogLiveEntity->setDirectory($ogLive['directory']); $ogLiveEntity->setChecksum($ogLive['id']); + $ogLiveEntity->setStatus(OgLiveStatus::ACTIVE); + } + + private function serDefaultOgLive(string $defaultOgLive): void + { + $ogLiveEntity = $this->entityManager->getRepository(OgLive::class)->findOneBy(['name' => $defaultOgLive]); + + if (!$ogLiveEntity) { + return; + } + + $ogLiveEntity->setIsDefault(true); + $this->entityManager->persist($ogLiveEntity); + $this->entityManager->flush(); } } \ No newline at end of file -- 2.40.1 From 8d67ccd78a555d5d8049794157a63e265816259c Mon Sep 17 00:00:00 2001 From: Manuel Aranda Date: Mon, 16 Sep 2024 08:40:08 +0200 Subject: [PATCH 036/157] refs #723. Updated command entity with new fields --- config/api_platform/Command.yaml | 0 src/Dto/Input/CommandInput.php | 8 ++++++++ src/Dto/Output/CommandOutput.php | 8 ++++++++ src/Entity/Command.php | 15 +++++++++++++++ src/State/Processor/CommandProcessor.php | 8 ++++++++ src/State/Provider/CommandProvider.php | 8 ++++++++ 6 files changed, 47 insertions(+) create mode 100644 config/api_platform/Command.yaml create mode 100644 src/Dto/Input/CommandInput.php create mode 100644 src/Dto/Output/CommandOutput.php create mode 100644 src/State/Processor/CommandProcessor.php create mode 100644 src/State/Provider/CommandProvider.php diff --git a/config/api_platform/Command.yaml b/config/api_platform/Command.yaml new file mode 100644 index 0000000..e69de29 diff --git a/src/Dto/Input/CommandInput.php b/src/Dto/Input/CommandInput.php new file mode 100644 index 0000000..03d64f2 --- /dev/null +++ b/src/Dto/Input/CommandInput.php @@ -0,0 +1,8 @@ +id; @@ -45,4 +48,16 @@ class Command extends AbstractEntity return $this; } + + public function isReadOnly(): ?bool + { + return $this->readOnly; + } + + public function setReadOnly(bool $readOnly): static + { + $this->readOnly = $readOnly; + + return $this; + } } diff --git a/src/State/Processor/CommandProcessor.php b/src/State/Processor/CommandProcessor.php new file mode 100644 index 0000000..355a27b --- /dev/null +++ b/src/State/Processor/CommandProcessor.php @@ -0,0 +1,8 @@ + Date: Mon, 16 Sep 2024 08:41:32 +0200 Subject: [PATCH 037/157] refs #659. Command API --- config/api_platform/Command.yaml | 31 +++++++++++ config/services.yaml | 5 ++ src/Dto/Input/CommandInput.php | 63 +++++++++++++++++++++- src/Dto/Output/CommandOutput.php | 37 ++++++++++++- src/Repository/CommandRepository.php | 27 +--------- src/State/Processor/CommandProcessor.php | 66 +++++++++++++++++++++-- src/State/Provider/CommandProvider.php | 69 ++++++++++++++++++++++-- 7 files changed, 262 insertions(+), 36 deletions(-) diff --git a/config/api_platform/Command.yaml b/config/api_platform/Command.yaml index e69de29..fd204fe 100644 --- a/config/api_platform/Command.yaml +++ b/config/api_platform/Command.yaml @@ -0,0 +1,31 @@ +resources: + App\Entity\Command: + processor: App\State\Processor\CommandProcessor + input: App\Dto\Input\CommandInput + output: App\Dto\Output\CommandOutput + normalizationContext: + groups: ['default', 'command:read'] + denormalizationContext: + groups: ['command:write'] + operations: + ApiPlatform\Metadata\GetCollection: + provider: App\State\Provider\CommandProvider + filters: + - 'api_platform.filter.command.order' + - 'api_platform.filter.command.search' + + ApiPlatform\Metadata\Get: + provider: App\State\Provider\CommandProvider + ApiPlatform\Metadata\Put: + provider: App\State\Provider\CommandProvider + ApiPlatform\Metadata\Patch: + provider: App\State\Provider\CommandProvider + ApiPlatform\Metadata\Post: ~ + ApiPlatform\Metadata\Delete: ~ + +properties: + App\Entity\Command: + id: + identifier: false + uuid: + identifier: true \ No newline at end of file diff --git a/config/services.yaml b/config/services.yaml index beee276..99d1d6c 100644 --- a/config/services.yaml +++ b/config/services.yaml @@ -101,3 +101,8 @@ services: bind: $collectionProvider: '@api_platform.doctrine.orm.state.collection_provider' $itemProvider: '@api_platform.doctrine.orm.state.item_provider' + + App\State\Provider\CommandProvider: + bind: + $collectionProvider: '@api_platform.doctrine.orm.state.collection_provider' + $itemProvider: '@api_platform.doctrine.orm.state.item_provider' diff --git a/src/Dto/Input/CommandInput.php b/src/Dto/Input/CommandInput.php index 03d64f2..a9d36af 100644 --- a/src/Dto/Input/CommandInput.php +++ b/src/Dto/Input/CommandInput.php @@ -2,7 +2,66 @@ namespace App\Dto\Input; -class CommandInput -{ +use ApiPlatform\Metadata\ApiProperty; +use App\Entity\Command; +use phpDocumentor\Reflection\Types\Boolean; +use Symfony\Component\Serializer\Annotation\Groups; +use Symfony\Component\Validator\Constraints as Assert; +final class CommandInput +{ + #[Assert\NotBlank(message: 'validators.command.name.not_blank')] + #[Groups(['command:write'])] + #[ApiProperty( + description: 'El nombre del comando', + example: 'Command 1', + )] + public ?string $name = null; + + #[Groups(['command:write'])] + #[ApiProperty( + description: 'El script del comando', + example: 'echo "Hello World"', + )] + public ?string $script = null; + + #[Groups(['command:write'])] + #[ApiProperty( + description: 'Es interno el comando?', + example: 'true', + )] + public ?bool $readOnly = false; + + #[Groups(['command:write'])] + #[ApiProperty( + description: 'Los comentarios del comando', + example: 'Comentarios del comando', + )] + public ?string $comments = null; + + public function __construct(?Command $command = null) + { + if (!$command) { + return; + } + + $this->name = $command->getName(); + $this->script = $command->getScript(); + $this->readOnly = $command->isReadOnly(); + $this->comments = $command->getComments(); + } + + public function createOrUpdateEntity(?Command $command = null): Command + { + if (!$command) { + $command = new Command(); + } + + $command->setName($this->name); + $command->setScript($this->script); + $command->setReadOnly($this->readOnly); + $command->setComments($this->comments); + + return $command; + } } \ No newline at end of file diff --git a/src/Dto/Output/CommandOutput.php b/src/Dto/Output/CommandOutput.php index 7eda910..d8598f5 100644 --- a/src/Dto/Output/CommandOutput.php +++ b/src/Dto/Output/CommandOutput.php @@ -2,7 +2,40 @@ namespace App\Dto\Output; -class CommandOutput -{ +use ApiPlatform\Metadata\Get; +use App\Entity\Command; +use Symfony\Component\Serializer\Annotation\Groups; +#[Get(shortName: 'Client')] +final class CommandOutput extends AbstractOutput +{ + #[Groups(['command:read'])] + public string $name; + + #[Groups(['command:read'])] + public ?string $script = ''; + + #[Groups(['command:read'])] + public ?bool $readOnly = false; + + #[Groups(['command:read'])] + public ?string $comments = ''; + + #[Groups(['command:read'])] + public \DateTime $createdAt; + + #[Groups(['command:read'])] + public ?string $createdBy = null; + + public function __construct(Command $command) + { + parent::__construct($command); + + $this->name = $command->getName(); + $this->script = $command->getScript(); + $this->readOnly = $command->isReadOnly(); + $this->comments = $command->getComments(); + $this->createdAt = $command->getCreatedAt(); + $this->createdBy = $command->getCreatedBy(); + } } \ No newline at end of file diff --git a/src/Repository/CommandRepository.php b/src/Repository/CommandRepository.php index 5afe393..544b708 100644 --- a/src/Repository/CommandRepository.php +++ b/src/Repository/CommandRepository.php @@ -9,35 +9,10 @@ use Doctrine\Persistence\ManagerRegistry; /** * @extends ServiceEntityRepository */ -class CommandRepository extends ServiceEntityRepository +class CommandRepository extends AbstractRepository { public function __construct(ManagerRegistry $registry) { parent::__construct($registry, Command::class); } - - // /** - // * @return Command[] Returns an array of Command objects - // */ - // public function findByExampleField($value): array - // { - // return $this->createQueryBuilder('c') - // ->andWhere('c.exampleField = :val') - // ->setParameter('val', $value) - // ->orderBy('c.id', 'ASC') - // ->setMaxResults(10) - // ->getQuery() - // ->getResult() - // ; - // } - - // public function findOneBySomeField($value): ?Command - // { - // return $this->createQueryBuilder('c') - // ->andWhere('c.exampleField = :val') - // ->setParameter('val', $value) - // ->getQuery() - // ->getOneOrNullResult() - // ; - // } } diff --git a/src/State/Processor/CommandProcessor.php b/src/State/Processor/CommandProcessor.php index 355a27b..fffae4f 100644 --- a/src/State/Processor/CommandProcessor.php +++ b/src/State/Processor/CommandProcessor.php @@ -2,7 +2,67 @@ namespace App\State\Processor; -class CommandProcessor -{ +use ApiPlatform\Metadata\Delete; +use ApiPlatform\Metadata\Operation; +use ApiPlatform\Metadata\Patch; +use ApiPlatform\Metadata\Post; +use ApiPlatform\Metadata\Put; +use ApiPlatform\State\ProcessorInterface; +use ApiPlatform\Validator\ValidatorInterface; +use App\Dto\Input\CommandInput; +use App\Dto\Output\CommandOutput; +use App\Repository\CommandRepository; -} \ No newline at end of file +readonly class CommandProcessor implements ProcessorInterface +{ + public function __construct( + private CommandRepository $commandRepository, + private ValidatorInterface $validator + ) + { + } + + /** + * @throws \Exception + */ + public function process(mixed $data, Operation $operation, array $uriVariables = [], array $context = []): CommandOutput|null + { + switch ($operation){ + case $operation instanceof Post: + case $operation instanceof Put: + case $operation instanceof Patch: + return $this->processCreateOrUpdate($data, $operation, $uriVariables, $context); + case $operation instanceof Delete: + return $this->processDelete($data, $operation, $uriVariables, $context); + } + } + + /** + * @throws \Exception + */ + private function processCreateOrUpdate($data, Operation $operation, array $uriVariables = [], array $context = []): CommandOutput + { + if (!($data instanceof CommandInput)) { + throw new \Exception(sprintf('data is not instance of %s', CommandInput::class)); + } + + $entity = null; + if (isset($uriVariables['uuid'])) { + $entity = $this->commandRepository->findOneByUuid($uriVariables['uuid']); + } + + $command = $data->createOrUpdateEntity($entity); + $this->validator->validate($command); + $this->commandRepository->save($command); + + return new CommandOutput($command); + } + + private function processDelete($data, Operation $operation, array $uriVariables = [], array $context = []): null + { + $user = $this->commandRepository->findOneByUuid($uriVariables['uuid']); + $this->commandRepository->delete($user); + + return null; + } +} diff --git a/src/State/Provider/CommandProvider.php b/src/State/Provider/CommandProvider.php index a32b218..ac2de21 100644 --- a/src/State/Provider/CommandProvider.php +++ b/src/State/Provider/CommandProvider.php @@ -2,7 +2,70 @@ namespace App\State\Provider; -class CommandProvider -{ +use ApiPlatform\Metadata\Get; +use ApiPlatform\Metadata\GetCollection; +use ApiPlatform\Metadata\Operation; +use ApiPlatform\Metadata\Patch; +use ApiPlatform\Metadata\Put; +use ApiPlatform\State\Pagination\TraversablePaginator; +use ApiPlatform\State\ProviderInterface; +use App\Dto\Input\CommandInput; +use App\Dto\Output\CommandOutput; +use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; -} \ No newline at end of file +readonly class CommandProvider implements ProviderInterface +{ + public function __construct( + private ProviderInterface $collectionProvider, + private ProviderInterface $itemProvider + ) + { + } + + public function provide(Operation $operation, array $uriVariables = [], array $context = []): object|array|null + { + switch ($operation){ + case $operation instanceof GetCollection: + return $this->provideCollection($operation, $uriVariables, $context); + case $operation instanceof Patch: + case $operation instanceof Put: + return $this->provideInput($operation, $uriVariables, $context); + case $operation instanceof Get: + return $this->provideItem($operation, $uriVariables, $context); + } + } + + private function provideCollection(Operation $operation, array $uriVariables = [], array $context = []): object + { + $paginator = $this->collectionProvider->provide($operation, $uriVariables, $context); + + $items = new \ArrayObject(); + foreach ($paginator->getIterator() as $item){ + $items[] = new CommandOutput($item); + } + + return new TraversablePaginator($items, $paginator->getCurrentPage(), $paginator->getItemsPerPage(), $paginator->getTotalItems()); + } + + public function provideItem(Operation $operation, array $uriVariables = [], array $context = []): object|array|null + { + $item = $this->itemProvider->provide($operation, $uriVariables, $context); + + if (!$item) { + throw new NotFoundHttpException('Hardware profile not found'); + } + + return new CommandOutput($item); + } + + public function provideInput(Operation $operation, array $uriVariables = [], array $context = []): object|array|null + { + if (isset($uriVariables['uuid'])) { + $item = $this->itemProvider->provide($operation, $uriVariables, $context); + + return $item !== null ? new CommandInput($item) : null; + } + + return new CommandInput(); + } +} -- 2.40.1 From 1883478baeecaa4e07f565f07638f2486568092b Mon Sep 17 00:00:00 2001 From: Manuel Aranda Date: Mon, 16 Sep 2024 09:18:17 +0200 Subject: [PATCH 038/157] refs #758. Testing command API --- src/Dto/Input/CommandInput.php | 1 + src/Dto/Output/CommandOutput.php | 2 +- src/Factory/CommandFactory.php | 59 +++++++++++++++ tests/Functional/CommandTest.php | 122 +++++++++++++++++++++++++++++++ 4 files changed, 183 insertions(+), 1 deletion(-) create mode 100644 src/Factory/CommandFactory.php create mode 100644 tests/Functional/CommandTest.php diff --git a/src/Dto/Input/CommandInput.php b/src/Dto/Input/CommandInput.php index a9d36af..7ddf219 100644 --- a/src/Dto/Input/CommandInput.php +++ b/src/Dto/Input/CommandInput.php @@ -18,6 +18,7 @@ final class CommandInput )] public ?string $name = null; + #[Assert\NotBlank(message: 'validators.command.script.not_blank')] #[Groups(['command:write'])] #[ApiProperty( description: 'El script del comando', diff --git a/src/Dto/Output/CommandOutput.php b/src/Dto/Output/CommandOutput.php index d8598f5..75f7d5e 100644 --- a/src/Dto/Output/CommandOutput.php +++ b/src/Dto/Output/CommandOutput.php @@ -6,7 +6,7 @@ use ApiPlatform\Metadata\Get; use App\Entity\Command; use Symfony\Component\Serializer\Annotation\Groups; -#[Get(shortName: 'Client')] +#[Get(shortName: 'Command')] final class CommandOutput extends AbstractOutput { #[Groups(['command:read'])] diff --git a/src/Factory/CommandFactory.php b/src/Factory/CommandFactory.php new file mode 100644 index 0000000..2e318ed --- /dev/null +++ b/src/Factory/CommandFactory.php @@ -0,0 +1,59 @@ + + */ +final class CommandFactory extends ModelFactory +{ + /** + * @see https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#factories-as-services + * + * @todo inject services if required + */ + public function __construct() + { + parent::__construct(); + } + + /** + * @see https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#model-factories + * + * @todo add your default values here + */ + protected function getDefaults(): array + { + return [ + 'createdAt' => self::faker()->dateTime(), + 'enabled' => self::faker()->boolean(), + 'name' => self::faker()->text(255), + 'readOnly' => self::faker()->boolean(), + 'script' => self::faker()->text(255), + 'updatedAt' => self::faker()->dateTime() + ]; + } + + /** + * @see https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#initialization + */ + protected function initialize(): self + { + return $this + // ->afterInstantiate(function(Command $command): void {}) + ; + } + + + public static function getClass(): string + { + return Command::class; + } +} diff --git a/tests/Functional/CommandTest.php b/tests/Functional/CommandTest.php new file mode 100644 index 0000000..68a55ad --- /dev/null +++ b/tests/Functional/CommandTest.php @@ -0,0 +1,122 @@ + self::USER_ADMIN, 'roles'=> [UserGroupPermissions::ROLE_SUPER_ADMIN]]); + + CommandFactory::createMany(10); + + $this->createClientWithCredentials()->request('GET', '/commands'); + $this->assertResponseStatusCodeSame(Response::HTTP_OK); + $this->assertResponseHeaderSame('content-type', 'application/ld+json; charset=utf-8'); + $this->assertJsonContains([ + '@context' => '/contexts/Command', + '@id' => '/commands', + '@type' => 'hydra:Collection', + 'hydra:totalItems' => 10, + ]); + } + + /** + * @throws RedirectionExceptionInterface + * @throws DecodingExceptionInterface + * @throws ClientExceptionInterface + * @throws TransportExceptionInterface + * @throws ServerExceptionInterface + */ + public function testCreateCommand(): void + { + UserFactory::createOne(['username' => self::USER_ADMIN, 'roles'=> [UserGroupPermissions::ROLE_SUPER_ADMIN]]); + + $this->createClientWithCredentials()->request('POST', '/commands',['json' => [ + 'name' => self::CMD_CREATE, + 'script' => 'echo "Hello World!"', + ]]); + + $this->assertResponseStatusCodeSame(201); + $this->assertResponseHeaderSame('content-type', 'application/ld+json; charset=utf-8'); + $this->assertJsonContains([ + '@context' => '/contexts/CommandOutput', + '@type' => 'Command', + 'name' => self::CMD_CREATE, + 'script' => 'echo "Hello World!"' + ]); + } + + /** + * @throws RedirectionExceptionInterface + * @throws DecodingExceptionInterface + * @throws ClientExceptionInterface + * @throws TransportExceptionInterface + * @throws ServerExceptionInterface + */ + public function testUpdateCommand(): void + { + UserFactory::createOne(['username' => self::USER_ADMIN, 'roles'=> [UserGroupPermissions::ROLE_SUPER_ADMIN]]); + + CommandFactory::createOne(['name' => self::CMD_CREATE]); + $iri = $this->findIriBy(Command::class, ['name' => self::CMD_CREATE]); + + $this->createClientWithCredentials()->request('PUT', $iri, ['json' => [ + 'name' => self::CMD_UPDATE, + 'readOnly' => true + ]]); + + $this->assertResponseIsSuccessful(); + $this->assertJsonContains([ + '@id' => $iri, + 'name' => self::CMD_UPDATE, + 'readOnly' => true + ]); + } + + /** + * @throws TransportExceptionInterface + * @throws ServerExceptionInterface + * @throws RedirectionExceptionInterface + * @throws DecodingExceptionInterface + * @throws ClientExceptionInterface + */ + public function testDeleteCommand(): void + { + UserFactory::createOne(['username' => self::USER_ADMIN, 'roles'=> [UserGroupPermissions::ROLE_SUPER_ADMIN]]); + + CommandFactory::createOne(['name' => self::CMD_DELETE]); + $iri = $this->findIriBy(Command::class, ['name' => self::CMD_DELETE]); + + $this->createClientWithCredentials()->request('DELETE', $iri); + $this->assertResponseStatusCodeSame(204); + $this->assertNull( + static::getContainer()->get('doctrine')->getRepository(Command::class)->findOneBy(['name' => self::CMD_DELETE]) + ); + } +} \ No newline at end of file -- 2.40.1 From d955a7f4340c387c4d4a39d6f32c61a0ca8e2540 Mon Sep 17 00:00:00 2001 From: Manuel Aranda Date: Mon, 16 Sep 2024 10:51:02 +0200 Subject: [PATCH 039/157] refs #723. Added migration command --- migrations/Version20240916073039.php | 31 ++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 migrations/Version20240916073039.php diff --git a/migrations/Version20240916073039.php b/migrations/Version20240916073039.php new file mode 100644 index 0000000..ba99c0b --- /dev/null +++ b/migrations/Version20240916073039.php @@ -0,0 +1,31 @@ +addSql('CREATE TABLE command (id INT AUTO_INCREMENT NOT NULL, uuid CHAR(36) NOT NULL COMMENT \'(DC2Type:uuid)\', migration_id VARCHAR(255) DEFAULT NULL, created_at DATETIME NOT NULL, updated_at DATETIME NOT NULL, created_by VARCHAR(255) DEFAULT NULL, updated_by VARCHAR(255) DEFAULT NULL, script VARCHAR(255) NOT NULL, comments VARCHAR(255) DEFAULT NULL, read_only TINYINT(1) NOT NULL, name VARCHAR(255) NOT NULL, enabled TINYINT(1) NOT NULL, UNIQUE INDEX UNIQ_8ECAEAD4D17F50A6 (uuid), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB'); + } + + public function down(Schema $schema): void + { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql('DROP TABLE command'); + } +} -- 2.40.1 From 05214bbd2ed731b748cef4e1d4171a7a3c1f398a Mon Sep 17 00:00:00 2001 From: Manuel Aranda Date: Mon, 16 Sep 2024 11:19:52 +0200 Subject: [PATCH 040/157] refs #723. First version commandGroup --- migrations/Version20240916091601.php | 37 +++++++++++++ src/Entity/Command.php | 40 +++++++++++++- src/Entity/CommandGroup.php | 66 +++++++++++++++++++++++ src/Repository/CommandGroupRepository.php | 43 +++++++++++++++ 4 files changed, 184 insertions(+), 2 deletions(-) create mode 100644 migrations/Version20240916091601.php create mode 100644 src/Entity/CommandGroup.php create mode 100644 src/Repository/CommandGroupRepository.php diff --git a/migrations/Version20240916091601.php b/migrations/Version20240916091601.php new file mode 100644 index 0000000..9f3304d --- /dev/null +++ b/migrations/Version20240916091601.php @@ -0,0 +1,37 @@ +addSql('CREATE TABLE command_group (id INT AUTO_INCREMENT NOT NULL, uuid CHAR(36) NOT NULL COMMENT \'(DC2Type:uuid)\', migration_id VARCHAR(255) DEFAULT NULL, created_at DATETIME NOT NULL, updated_at DATETIME NOT NULL, created_by VARCHAR(255) DEFAULT NULL, updated_by VARCHAR(255) DEFAULT NULL, position INT NOT NULL, name VARCHAR(255) NOT NULL, enabled TINYINT(1) NOT NULL, UNIQUE INDEX UNIQ_FE6811F6D17F50A6 (uuid), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB'); + $this->addSql('CREATE TABLE command_group_command (command_group_id INT NOT NULL, command_id INT NOT NULL, INDEX IDX_118CE215C7B800D6 (command_group_id), INDEX IDX_118CE21533E1689A (command_id), PRIMARY KEY(command_group_id, command_id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB'); + $this->addSql('ALTER TABLE command_group_command ADD CONSTRAINT FK_118CE215C7B800D6 FOREIGN KEY (command_group_id) REFERENCES command_group (id) ON DELETE CASCADE'); + $this->addSql('ALTER TABLE command_group_command ADD CONSTRAINT FK_118CE21533E1689A FOREIGN KEY (command_id) REFERENCES command (id) ON DELETE CASCADE'); + } + + public function down(Schema $schema): void + { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql('ALTER TABLE command_group_command DROP FOREIGN KEY FK_118CE215C7B800D6'); + $this->addSql('ALTER TABLE command_group_command DROP FOREIGN KEY FK_118CE21533E1689A'); + $this->addSql('DROP TABLE command_group'); + $this->addSql('DROP TABLE command_group_command'); + } +} diff --git a/src/Entity/Command.php b/src/Entity/Command.php index 45db55a..97964f2 100644 --- a/src/Entity/Command.php +++ b/src/Entity/Command.php @@ -3,6 +3,8 @@ namespace App\Entity; use App\Repository\CommandRepository; +use Doctrine\Common\Collections\ArrayCollection; +use Doctrine\Common\Collections\Collection; use Doctrine\ORM\Mapping as ORM; #[ORM\Entity(repositoryClass: CommandRepository::class)] @@ -20,9 +22,16 @@ class Command extends AbstractEntity #[ORM\Column] private ?bool $readOnly = null; - public function getId(): ?int + /** + * @var Collection + */ + #[ORM\ManyToMany(targetEntity: CommandGroup::class, mappedBy: 'commands')] + private Collection $commandGroups; + + public function __construct() { - return $this->id; + parent::__construct(); + $this->commandGroups = new ArrayCollection(); } public function getScript(): ?string @@ -60,4 +69,31 @@ class Command extends AbstractEntity return $this; } + + /** + * @return Collection + */ + public function getCommandGroups(): Collection + { + return $this->commandGroups; + } + + public function addCommandGroup(CommandGroup $commandGroup): static + { + if (!$this->commandGroups->contains($commandGroup)) { + $this->commandGroups->add($commandGroup); + $commandGroup->addCommand($this); + } + + return $this; + } + + public function removeCommandGroup(CommandGroup $commandGroup): static + { + if ($this->commandGroups->removeElement($commandGroup)) { + $commandGroup->removeCommand($this); + } + + return $this; + } } diff --git a/src/Entity/CommandGroup.php b/src/Entity/CommandGroup.php new file mode 100644 index 0000000..aa1173d --- /dev/null +++ b/src/Entity/CommandGroup.php @@ -0,0 +1,66 @@ + + */ + #[ORM\ManyToMany(targetEntity: Command::class, inversedBy: 'commandGroups')] + private Collection $commands; + + #[ORM\Column] + private ?int $position = null; + + public function __construct() + { + parent::__construct(); + $this->commands = new ArrayCollection(); + } + + /** + * @return Collection + */ + public function getCommands(): Collection + { + return $this->commands; + } + + 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; + } + + public function getPosition(): ?int + { + return $this->position; + } + + public function setPosition(int $position): static + { + $this->position = $position; + + return $this; + } +} diff --git a/src/Repository/CommandGroupRepository.php b/src/Repository/CommandGroupRepository.php new file mode 100644 index 0000000..b669b14 --- /dev/null +++ b/src/Repository/CommandGroupRepository.php @@ -0,0 +1,43 @@ + + */ +class CommandGroupRepository extends ServiceEntityRepository +{ + public function __construct(ManagerRegistry $registry) + { + parent::__construct($registry, CommandGroup::class); + } + + // /** + // * @return CommandGroup[] Returns an array of CommandGroup objects + // */ + // public function findByExampleField($value): array + // { + // return $this->createQueryBuilder('c') + // ->andWhere('c.exampleField = :val') + // ->setParameter('val', $value) + // ->orderBy('c.id', 'ASC') + // ->setMaxResults(10) + // ->getQuery() + // ->getResult() + // ; + // } + + // public function findOneBySomeField($value): ?CommandGroup + // { + // return $this->createQueryBuilder('c') + // ->andWhere('c.exampleField = :val') + // ->setParameter('val', $value) + // ->getQuery() + // ->getOneOrNullResult() + // ; + // } +} -- 2.40.1 From ad02456de69eb6cbc7d37d51d1a198f3040ac6b1 Mon Sep 17 00:00:00 2001 From: Manuel Aranda Date: Mon, 16 Sep 2024 13:31:37 +0200 Subject: [PATCH 041/157] refs #659. CommandGroup API --- config/api_platform/Command.yaml | 1 + config/api_platform/CommandGroup.yaml | 32 +++++++ config/services.yaml | 5 ++ config/services/api_platform.yaml | 17 ++++ src/Dto/Input/CommandGroupInput.php | 83 +++++++++++++++++++ src/Dto/Input/CommandInput.php | 9 ++ src/Dto/Output/CommandGroupOutput.php | 42 ++++++++++ src/Dto/Output/CommandOutput.php | 4 + src/Entity/CommandGroup.php | 12 +++ src/Repository/CommandGroupRepository.php | 27 +----- src/State/Processor/CommandGroupProcessor.php | 69 +++++++++++++++ src/State/Provider/CommandGroupProvider.php | 72 ++++++++++++++++ src/State/Provider/CommandProvider.php | 2 +- translations/validators.en.yaml | 12 +++ translations/validators.es.yaml | 12 +++ 15 files changed, 372 insertions(+), 27 deletions(-) create mode 100644 config/api_platform/CommandGroup.yaml create mode 100644 src/Dto/Input/CommandGroupInput.php create mode 100644 src/Dto/Output/CommandGroupOutput.php create mode 100644 src/State/Processor/CommandGroupProcessor.php create mode 100644 src/State/Provider/CommandGroupProvider.php diff --git a/config/api_platform/Command.yaml b/config/api_platform/Command.yaml index fd204fe..f8480ce 100644 --- a/config/api_platform/Command.yaml +++ b/config/api_platform/Command.yaml @@ -13,6 +13,7 @@ resources: filters: - 'api_platform.filter.command.order' - 'api_platform.filter.command.search' + - 'api_platform.filter.command.boolean' ApiPlatform\Metadata\Get: provider: App\State\Provider\CommandProvider diff --git a/config/api_platform/CommandGroup.yaml b/config/api_platform/CommandGroup.yaml new file mode 100644 index 0000000..6e0cda9 --- /dev/null +++ b/config/api_platform/CommandGroup.yaml @@ -0,0 +1,32 @@ +resources: + App\Entity\CommandGroup: + processor: App\State\Processor\CommandGroupProcessor + input: App\Dto\Input\CommandGroupInput + output: App\Dto\Output\CommandGroupOutput + normalizationContext: + groups: ['default', 'command-group:read'] + denormalizationContext: + groups: ['command-group:write'] + operations: + ApiPlatform\Metadata\GetCollection: + provider: App\State\Provider\CommandGroupProvider + filters: + - 'api_platform.filter.command.order' + - 'api_platform.filter.command.search' + - 'api_platform.filter.command.boolean' + + ApiPlatform\Metadata\Get: + provider: App\State\Provider\CommandGroupProvider + ApiPlatform\Metadata\Put: + provider: App\State\Provider\CommandGroupProvider + ApiPlatform\Metadata\Patch: + provider: App\State\Provider\CommandGroupProvider + ApiPlatform\Metadata\Post: ~ + ApiPlatform\Metadata\Delete: ~ + +properties: + App\Entity\CommandGroup: + id: + identifier: false + uuid: + identifier: true \ No newline at end of file diff --git a/config/services.yaml b/config/services.yaml index 99d1d6c..be56306 100644 --- a/config/services.yaml +++ b/config/services.yaml @@ -106,3 +106,8 @@ services: bind: $collectionProvider: '@api_platform.doctrine.orm.state.collection_provider' $itemProvider: '@api_platform.doctrine.orm.state.item_provider' + + App\State\Provider\CommandGroupProvider: + bind: + $collectionProvider: '@api_platform.doctrine.orm.state.collection_provider' + $itemProvider: '@api_platform.doctrine.orm.state.item_provider' \ No newline at end of file diff --git a/config/services/api_platform.yaml b/config/services/api_platform.yaml index d8f0fa4..466d458 100644 --- a/config/services/api_platform.yaml +++ b/config/services/api_platform.yaml @@ -11,6 +11,23 @@ services: arguments: [ { 'id': 'exact', 'name': 'partial', 'serialNumber': 'exact', organizationalUnit.id: 'exact' } ] tags: [ 'api_platform.filter' ] + api_platform.filter.command.order: + parent: 'api_platform.doctrine.orm.order_filter' + arguments: + $properties: { 'id': ~, 'name': ~ } + $orderParameterName: 'order' + tags: [ 'api_platform.filter' ] + + api_platform.filter.command.search: + parent: 'api_platform.doctrine.orm.search_filter' + arguments: [ { 'id': 'exact', 'name': 'exact'} ] + tags: [ 'api_platform.filter' ] + + api_platform.filter.command.boolean: + parent: 'api_platform.doctrine.orm.boolean_filter' + arguments: [ { 'enabled': ~ } ] + tags: [ 'api_platform.filter' ] + api_platform.filter.hardware.order: parent: 'api_platform.doctrine.orm.order_filter' arguments: diff --git a/src/Dto/Input/CommandGroupInput.php b/src/Dto/Input/CommandGroupInput.php new file mode 100644 index 0000000..2a3fe64 --- /dev/null +++ b/src/Dto/Input/CommandGroupInput.php @@ -0,0 +1,83 @@ +name = $commandGroup->getName(); + + if ($commandGroup->getCommands()) { + foreach ($commandGroup->getCommands() as $command) { + $this->commands[] = new CommandOutput($command); + } + } + + $this->position = $commandGroup->getPosition(); + $this->enabled = $commandGroup->isEnabled(); + } + + public function createOrUpdateEntity(?CommandGroup $commandGroup = null): CommandGroup + { + if (!$commandGroup) { + $commandGroup = new CommandGroup(); + } + + $commandGroup->setName($this->name); + + foreach ($this->commands as $command) { + $commandsToAdd[] = $command->getEntity(); + } + + $commandGroup->setCommands( $commandsToAdd ?? [] ); + $commandGroup->setPosition($this->position); + + return $commandGroup; + } +} diff --git a/src/Dto/Input/CommandInput.php b/src/Dto/Input/CommandInput.php index 7ddf219..10697d8 100644 --- a/src/Dto/Input/CommandInput.php +++ b/src/Dto/Input/CommandInput.php @@ -33,6 +33,13 @@ final class CommandInput )] public ?bool $readOnly = false; + #[Groups(['command:write'])] + #[ApiProperty( + description: 'Esta activo?', + example: 'true', + )] + public ?bool $enabled = true; + #[Groups(['command:write'])] #[ApiProperty( description: 'Los comentarios del comando', @@ -48,6 +55,7 @@ final class CommandInput $this->name = $command->getName(); $this->script = $command->getScript(); + $this->enabled = $command->isEnabled(); $this->readOnly = $command->isReadOnly(); $this->comments = $command->getComments(); } @@ -60,6 +68,7 @@ final class CommandInput $command->setName($this->name); $command->setScript($this->script); + $command->setEnabled($this->enabled); $command->setReadOnly($this->readOnly); $command->setComments($this->comments); diff --git a/src/Dto/Output/CommandGroupOutput.php b/src/Dto/Output/CommandGroupOutput.php new file mode 100644 index 0000000..636ca8e --- /dev/null +++ b/src/Dto/Output/CommandGroupOutput.php @@ -0,0 +1,42 @@ +name = $commandGroup->getName(); + + $this->commands = $commandGroup->getCommands()->map( + fn(Command $command) => new CommandOutput($command) + )->toArray(); + + $this->position = $commandGroup->getPosition(); + $this->createdAt = $commandGroup->getCreatedAt(); + $this->createdBy = $commandGroup->getCreatedBy(); + } +} \ No newline at end of file diff --git a/src/Dto/Output/CommandOutput.php b/src/Dto/Output/CommandOutput.php index 75f7d5e..5d92345 100644 --- a/src/Dto/Output/CommandOutput.php +++ b/src/Dto/Output/CommandOutput.php @@ -18,6 +18,9 @@ final class CommandOutput extends AbstractOutput #[Groups(['command:read'])] public ?bool $readOnly = false; + #[Groups(['command:read'])] + public ?bool $enabled = true; + #[Groups(['command:read'])] public ?string $comments = ''; @@ -34,6 +37,7 @@ final class CommandOutput extends AbstractOutput $this->name = $command->getName(); $this->script = $command->getScript(); $this->readOnly = $command->isReadOnly(); + $this->enabled = $command->isEnabled(); $this->comments = $command->getComments(); $this->createdAt = $command->getCreatedAt(); $this->createdBy = $command->getCreatedBy(); diff --git a/src/Entity/CommandGroup.php b/src/Entity/CommandGroup.php index aa1173d..f0061c3 100644 --- a/src/Entity/CommandGroup.php +++ b/src/Entity/CommandGroup.php @@ -36,6 +36,18 @@ class CommandGroup extends AbstractEntity return $this->commands; } + public function setCommands(array $commands): static + { + $this->commands->clear(); + + foreach ($commands as $command){ + $this->addCommand($command); + } + + return $this; + } + + public function addCommand(Command $command): static { if (!$this->commands->contains($command)) { diff --git a/src/Repository/CommandGroupRepository.php b/src/Repository/CommandGroupRepository.php index b669b14..f8cf8b2 100644 --- a/src/Repository/CommandGroupRepository.php +++ b/src/Repository/CommandGroupRepository.php @@ -9,35 +9,10 @@ use Doctrine\Persistence\ManagerRegistry; /** * @extends ServiceEntityRepository */ -class CommandGroupRepository extends ServiceEntityRepository +class CommandGroupRepository extends AbstractRepository { public function __construct(ManagerRegistry $registry) { parent::__construct($registry, CommandGroup::class); } - - // /** - // * @return CommandGroup[] Returns an array of CommandGroup objects - // */ - // public function findByExampleField($value): array - // { - // return $this->createQueryBuilder('c') - // ->andWhere('c.exampleField = :val') - // ->setParameter('val', $value) - // ->orderBy('c.id', 'ASC') - // ->setMaxResults(10) - // ->getQuery() - // ->getResult() - // ; - // } - - // public function findOneBySomeField($value): ?CommandGroup - // { - // return $this->createQueryBuilder('c') - // ->andWhere('c.exampleField = :val') - // ->setParameter('val', $value) - // ->getQuery() - // ->getOneOrNullResult() - // ; - // } } diff --git a/src/State/Processor/CommandGroupProcessor.php b/src/State/Processor/CommandGroupProcessor.php new file mode 100644 index 0000000..65ffacd --- /dev/null +++ b/src/State/Processor/CommandGroupProcessor.php @@ -0,0 +1,69 @@ +processCreateOrUpdate($data, $operation, $uriVariables, $context); + case $operation instanceof Delete: + return $this->processDelete($data, $operation, $uriVariables, $context); + } + } + + /** + * @throws \Exception + */ + private function processCreateOrUpdate($data, Operation $operation, array $uriVariables = [], array $context = []): CommandGroupOutput + { + if (!($data instanceof CommandGroupInput)) { + throw new \Exception(sprintf('data is not instance of %s', CommandGroupInput::class)); + } + + $entity = null; + if (isset($uriVariables['uuid'])) { + $entity = $this->commandGroupRepository->findOneByUuid($uriVariables['uuid']); + } + + $command = $data->createOrUpdateEntity($entity); + $this->validator->validate($command); + $this->commandGroupRepository->save($command); + + return new CommandGroupOutput($command); + } + + private function processDelete($data, Operation $operation, array $uriVariables = [], array $context = []): null + { + $user = $this->commandGroupRepository->findOneByUuid($uriVariables['uuid']); + $this->commandGroupRepository->delete($user); + + return null; + } +} diff --git a/src/State/Provider/CommandGroupProvider.php b/src/State/Provider/CommandGroupProvider.php new file mode 100644 index 0000000..c3092db --- /dev/null +++ b/src/State/Provider/CommandGroupProvider.php @@ -0,0 +1,72 @@ +provideCollection($operation, $uriVariables, $context); + case $operation instanceof Patch: + case $operation instanceof Put: + return $this->provideInput($operation, $uriVariables, $context); + case $operation instanceof Get: + return $this->provideItem($operation, $uriVariables, $context); + } + } + + private function provideCollection(Operation $operation, array $uriVariables = [], array $context = []): object + { + $paginator = $this->collectionProvider->provide($operation, $uriVariables, $context); + + $items = new \ArrayObject(); + foreach ($paginator->getIterator() as $item){ + $items[] = new CommandGroupOutput($item); + } + + return new TraversablePaginator($items, $paginator->getCurrentPage(), $paginator->getItemsPerPage(), $paginator->getTotalItems()); + } + + public function provideItem(Operation $operation, array $uriVariables = [], array $context = []): object|array|null + { + $item = $this->itemProvider->provide($operation, $uriVariables, $context); + + if (!$item) { + throw new NotFoundHttpException('Command group not found'); + } + + return new CommandGroupOutput($item); + } + + public function provideInput(Operation $operation, array $uriVariables = [], array $context = []): object|array|null + { + if (isset($uriVariables['uuid'])) { + $item = $this->itemProvider->provide($operation, $uriVariables, $context); + + return $item !== null ? new CommandGroupInput($item) : null; + } + + return new CommandGroupInput(); + } +} diff --git a/src/State/Provider/CommandProvider.php b/src/State/Provider/CommandProvider.php index ac2de21..b5854b1 100644 --- a/src/State/Provider/CommandProvider.php +++ b/src/State/Provider/CommandProvider.php @@ -52,7 +52,7 @@ readonly class CommandProvider implements ProviderInterface $item = $this->itemProvider->provide($operation, $uriVariables, $context); if (!$item) { - throw new NotFoundHttpException('Hardware profile not found'); + throw new NotFoundHttpException('Command not found'); } return new CommandOutput($item); diff --git a/translations/validators.en.yaml b/translations/validators.en.yaml index e5b6e69..9f0f516 100644 --- a/translations/validators.en.yaml +++ b/translations/validators.en.yaml @@ -8,6 +8,18 @@ validators: organizational_unit: not_null: 'The organizational unit should not be null.' + command: + name: + not_blank: 'The name should not be blank.' + script: + not_blank: 'The script should not be blank.' + + command_group: + name: + not_blank: 'The name should not be blank.' + position: + not_blank: 'The position should not be blank.' + view: name: not_blank: 'The name should not be blank.' diff --git a/translations/validators.es.yaml b/translations/validators.es.yaml index 9bf6d15..5a2984d 100644 --- a/translations/validators.es.yaml +++ b/translations/validators.es.yaml @@ -8,6 +8,18 @@ validators: organizational_unit: not_null: 'La unidad organizativa no debería estar vacía.' + command: + name: + not_blank: 'El nombre no debería estar vacío.' + script: + not_blank: 'El script no debería estar vacío.' + + command_group: + name: + not_blank: 'El nombre no debería estar vacío.' + position: + not_blank: 'La posición no debería estar vacía.' + view: name: not_blank: 'El nombre no debería estar vacío.' -- 2.40.1 From 1afe73bd0937f4a002bca5b9fb334a9f1f45445c Mon Sep 17 00:00:00 2001 From: Manuel Aranda Date: Mon, 16 Sep 2024 13:55:57 +0200 Subject: [PATCH 042/157] refs #659. CommandGroup. Added 'add-command' endpoint --- config/api_platform/CommandGroup.yaml | 7 ++++ .../CommandGroupAddCommandsAction.php | 35 +++++++++++++++++++ .../Input/CommandGroupAddCommandsInput.php | 17 +++++++++ src/State/Provider/CommandGroupProvider.php | 2 +- 4 files changed, 60 insertions(+), 1 deletion(-) create mode 100644 src/Controller/CommandGroupAddCommandsAction.php create mode 100644 src/Dto/Input/CommandGroupAddCommandsInput.php diff --git a/config/api_platform/CommandGroup.yaml b/config/api_platform/CommandGroup.yaml index 6e0cda9..40ede0b 100644 --- a/config/api_platform/CommandGroup.yaml +++ b/config/api_platform/CommandGroup.yaml @@ -24,6 +24,13 @@ resources: ApiPlatform\Metadata\Post: ~ ApiPlatform\Metadata\Delete: ~ + add_commands: + class: ApiPlatform\Metadata\Post + method: POST + input: App\Dto\Input\CommandGroupAddCommandsInput + uriTemplate: /command-groups/{uuid}/add-commands + controller: App\Controller\CommandGroupAddCommandsAction + properties: App\Entity\CommandGroup: id: diff --git a/src/Controller/CommandGroupAddCommandsAction.php b/src/Controller/CommandGroupAddCommandsAction.php new file mode 100644 index 0000000..e779057 --- /dev/null +++ b/src/Controller/CommandGroupAddCommandsAction.php @@ -0,0 +1,35 @@ +commands; + + /** @var Command $command */ + foreach ($commands as $command) { + $commandGroup->addCommand($command->getEntity()); + } + + $this->entityManager->persist($commandGroup); + $this->entityManager->flush(); + + return new JsonResponse(data: 'Commands added to command group successfully', status: Response::HTTP_OK); + } +} \ No newline at end of file diff --git a/src/Dto/Input/CommandGroupAddCommandsInput.php b/src/Dto/Input/CommandGroupAddCommandsInput.php new file mode 100644 index 0000000..2b14a7b --- /dev/null +++ b/src/Dto/Input/CommandGroupAddCommandsInput.php @@ -0,0 +1,17 @@ +collectionProvider->provide($operation, $uriVariables, $context); -- 2.40.1 From e7b1444446ca08f11214d9a5a1cddcab4c7d3b24 Mon Sep 17 00:00:00 2001 From: Manuel Aranda Date: Mon, 16 Sep 2024 14:30:37 +0200 Subject: [PATCH 043/157] refs #723. Included script to migrate commands --- src/Command/LoadDefaultCommandsCommand.php | 95 ++++++++++++++++++++++ 1 file changed, 95 insertions(+) create mode 100644 src/Command/LoadDefaultCommandsCommand.php diff --git a/src/Command/LoadDefaultCommandsCommand.php b/src/Command/LoadDefaultCommandsCommand.php new file mode 100644 index 0000000..2c9a92e --- /dev/null +++ b/src/Command/LoadDefaultCommandsCommand.php @@ -0,0 +1,95 @@ + 'Encender', + 'enabled' => true, + 'readOnly' => true, + ], + [ + 'name' => 'Apagar', + 'enabled' => true, + 'readOnly' => true, + ], + [ + 'name' => 'Restaurar Imagen', + 'enabled' => true, + 'readOnly' => true, + ], + [ + 'name' => 'Crear Imagen', + 'enabled' => true, + 'readOnly' => true, + ], + [ + 'name' => 'Reiniciar', + 'enabled' => true, + 'readOnly' => true, + ], + [ + 'name' => 'Inventario Hardware', + 'enabled' => true, + 'readOnly' => true, + ], + [ + 'name' => 'Inventario Software', + 'enabled' => true, + 'readOnly' => true, + ], + [ + 'name' => 'Ejecutar Script', + 'enabled' => true, + 'readOnly' => true, + ], + [ + 'name' => 'Iniciar Sesion', + 'enabled' => true, + 'readOnly' => true, + ], + [ + 'name' => 'Particionar y Formatear', + 'enabled' => true, + 'readOnly' => true, + ], + [ + 'name' => 'Eliminar Imagen Cache', + 'enabled' => true, + 'readOnly' => true, + ], + ]; + + foreach ($commands as $command) { + $entity = new \App\Entity\Command(); + $entity->setName($command['name']); + $entity->setScript(''); + $entity->setEnabled($command['enabled']); + $entity->setReadOnly($command['readOnly']); + + $this->entityManager->persist($entity); + } + + $this->entityManager->flush(); + + return 1; + + } + +} \ No newline at end of file -- 2.40.1 From 8141d581c84251a4fcdc21c3d979ee7d18bd1e47 Mon Sep 17 00:00:00 2001 From: Manuel Aranda Date: Mon, 16 Sep 2024 14:31:52 +0200 Subject: [PATCH 044/157] refs #723. Included script to migrate commands --- README.md | 2 ++ src/Command/LoadDefaultCommandsCommand.php | 1 - 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 1539804..6fdb775 100644 --- a/README.md +++ b/README.md @@ -69,6 +69,8 @@ docker exec ogcore-php php bin/console doctrine:migrations:migrate --no-interact ```sh docker exec ogcore-php php bin/console doctrine:fixtures:load --no-interaction docker exec ogcore-php php bin/console app:load-default-user-groups #cargamos los grupos por defecto +docker exec ogcore-php php bin/console app:load-default commands #cargamos los commands por defecto + ``` ## UX Api Platform diff --git a/src/Command/LoadDefaultCommandsCommand.php b/src/Command/LoadDefaultCommandsCommand.php index 2c9a92e..8069f4e 100644 --- a/src/Command/LoadDefaultCommandsCommand.php +++ b/src/Command/LoadDefaultCommandsCommand.php @@ -89,7 +89,6 @@ class LoadDefaultCommandsCommand extends Command $this->entityManager->flush(); return 1; - } } \ No newline at end of file -- 2.40.1 From d4101cda809d0852190a2148f8a41cb2b925e435 Mon Sep 17 00:00:00 2001 From: Manuel Aranda Date: Mon, 16 Sep 2024 14:39:48 +0200 Subject: [PATCH 045/157] refs #723. Little bug in script name --- README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index 6fdb775..54d5c90 100644 --- a/README.md +++ b/README.md @@ -69,8 +69,7 @@ docker exec ogcore-php php bin/console doctrine:migrations:migrate --no-interact ```sh docker exec ogcore-php php bin/console doctrine:fixtures:load --no-interaction docker exec ogcore-php php bin/console app:load-default-user-groups #cargamos los grupos por defecto -docker exec ogcore-php php bin/console app:load-default commands #cargamos los commands por defecto - +docker exec ogcore-php php bin/console app:load-default-commands #cargamos los commands por defecto ``` ## UX Api Platform -- 2.40.1 From 0f0b047c85832bcc5be96fbef94920896e98a08f Mon Sep 17 00:00:00 2001 From: Manuel Aranda Date: Tue, 17 Sep 2024 08:50:17 +0200 Subject: [PATCH 046/157] refs #723. Added new entity CommandTask --- migrations/Version20240917064754.php | 43 ++++++++ src/Entity/Command.php | 34 ++++++ src/Entity/CommandGroup.php | 34 ++++++ src/Entity/CommandTask.php | 126 +++++++++++++++++++++++ src/Model/CommandTaskStatus.php | 33 ++++++ src/Repository/CommandTaskRepository.php | 43 ++++++++ 6 files changed, 313 insertions(+) create mode 100644 migrations/Version20240917064754.php create mode 100644 src/Entity/CommandTask.php create mode 100644 src/Model/CommandTaskStatus.php create mode 100644 src/Repository/CommandTaskRepository.php diff --git a/migrations/Version20240917064754.php b/migrations/Version20240917064754.php new file mode 100644 index 0000000..64f1f9b --- /dev/null +++ b/migrations/Version20240917064754.php @@ -0,0 +1,43 @@ +addSql('CREATE TABLE command_task (id INT AUTO_INCREMENT NOT NULL, uuid CHAR(36) NOT NULL COMMENT \'(DC2Type:uuid)\', migration_id VARCHAR(255) DEFAULT NULL, created_at DATETIME NOT NULL, updated_at DATETIME NOT NULL, created_by VARCHAR(255) DEFAULT NULL, updated_by VARCHAR(255) DEFAULT NULL, datetime DATETIME NOT NULL, notes VARCHAR(255) DEFAULT NULL, status VARCHAR(255) NOT NULL, UNIQUE INDEX UNIQ_F3D475A8D17F50A6 (uuid), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB'); + $this->addSql('CREATE TABLE command_task_command (command_task_id INT NOT NULL, command_id INT NOT NULL, INDEX IDX_BB417CA862DC5265 (command_task_id), INDEX IDX_BB417CA833E1689A (command_id), PRIMARY KEY(command_task_id, command_id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB'); + $this->addSql('CREATE TABLE command_task_command_group (command_task_id INT NOT NULL, command_group_id INT NOT NULL, INDEX IDX_C43618BD62DC5265 (command_task_id), INDEX IDX_C43618BDC7B800D6 (command_group_id), PRIMARY KEY(command_task_id, command_group_id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB'); + $this->addSql('ALTER TABLE command_task_command ADD CONSTRAINT FK_BB417CA862DC5265 FOREIGN KEY (command_task_id) REFERENCES command_task (id) ON DELETE CASCADE'); + $this->addSql('ALTER TABLE command_task_command ADD CONSTRAINT FK_BB417CA833E1689A FOREIGN KEY (command_id) REFERENCES command (id) ON DELETE CASCADE'); + $this->addSql('ALTER TABLE command_task_command_group ADD CONSTRAINT FK_C43618BD62DC5265 FOREIGN KEY (command_task_id) REFERENCES command_task (id) ON DELETE CASCADE'); + $this->addSql('ALTER TABLE command_task_command_group ADD CONSTRAINT FK_C43618BDC7B800D6 FOREIGN KEY (command_group_id) REFERENCES command_group (id) ON DELETE CASCADE'); + } + + public function down(Schema $schema): void + { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql('ALTER TABLE command_task_command DROP FOREIGN KEY FK_BB417CA862DC5265'); + $this->addSql('ALTER TABLE command_task_command DROP FOREIGN KEY FK_BB417CA833E1689A'); + $this->addSql('ALTER TABLE command_task_command_group DROP FOREIGN KEY FK_C43618BD62DC5265'); + $this->addSql('ALTER TABLE command_task_command_group DROP FOREIGN KEY FK_C43618BDC7B800D6'); + $this->addSql('DROP TABLE command_task'); + $this->addSql('DROP TABLE command_task_command'); + $this->addSql('DROP TABLE command_task_command_group'); + } +} diff --git a/src/Entity/Command.php b/src/Entity/Command.php index 97964f2..14e485e 100644 --- a/src/Entity/Command.php +++ b/src/Entity/Command.php @@ -28,10 +28,17 @@ class Command extends AbstractEntity #[ORM\ManyToMany(targetEntity: CommandGroup::class, mappedBy: 'commands')] private Collection $commandGroups; + /** + * @var Collection + */ + #[ORM\ManyToMany(targetEntity: CommandTask::class, mappedBy: 'command')] + private Collection $commandTasks; + public function __construct() { parent::__construct(); $this->commandGroups = new ArrayCollection(); + $this->commandTasks = new ArrayCollection(); } public function getScript(): ?string @@ -96,4 +103,31 @@ class Command extends AbstractEntity return $this; } + + /** + * @return Collection + */ + public function getCommandTasks(): Collection + { + return $this->commandTasks; + } + + public function addCommandTask(CommandTask $commandTask): static + { + if (!$this->commandTasks->contains($commandTask)) { + $this->commandTasks->add($commandTask); + $commandTask->addCommand($this); + } + + return $this; + } + + public function removeCommandTask(CommandTask $commandTask): static + { + if ($this->commandTasks->removeElement($commandTask)) { + $commandTask->removeCommand($this); + } + + return $this; + } } diff --git a/src/Entity/CommandGroup.php b/src/Entity/CommandGroup.php index f0061c3..30befcb 100644 --- a/src/Entity/CommandGroup.php +++ b/src/Entity/CommandGroup.php @@ -22,10 +22,17 @@ class CommandGroup extends AbstractEntity #[ORM\Column] private ?int $position = null; + /** + * @var Collection + */ + #[ORM\ManyToMany(targetEntity: CommandTask::class, mappedBy: 'commandGroup')] + private Collection $commandTasks; + public function __construct() { parent::__construct(); $this->commands = new ArrayCollection(); + $this->commandTasks = new ArrayCollection(); } /** @@ -75,4 +82,31 @@ class CommandGroup extends AbstractEntity return $this; } + + /** + * @return Collection + */ + public function getCommandTasks(): Collection + { + return $this->commandTasks; + } + + public function addCommandTask(CommandTask $commandTask): static + { + if (!$this->commandTasks->contains($commandTask)) { + $this->commandTasks->add($commandTask); + $commandTask->addCommandGroup($this); + } + + return $this; + } + + public function removeCommandTask(CommandTask $commandTask): static + { + if ($this->commandTasks->removeElement($commandTask)) { + $commandTask->removeCommandGroup($this); + } + + return $this; + } } diff --git a/src/Entity/CommandTask.php b/src/Entity/CommandTask.php new file mode 100644 index 0000000..3684eb4 --- /dev/null +++ b/src/Entity/CommandTask.php @@ -0,0 +1,126 @@ + + */ + #[ORM\ManyToMany(targetEntity: Command::class, inversedBy: 'commandTasks')] + private Collection $commands; + + /** + * @var Collection + */ + #[ORM\ManyToMany(targetEntity: CommandGroup::class, inversedBy: 'commandTasks')] + private Collection $commandGroups; + + #[ORM\Column(type: Types::DATETIME_MUTABLE)] + private ?\DateTimeInterface $datetime = null; + + #[ORM\Column(length: 255, nullable: true)] + private ?string $notes = null; + + #[ORM\Column(length: 255)] + private ?string $status = null; + + public function __construct() + { + parent::__construct(); + + $this->commands = new ArrayCollection(); + $this->commandGroups = new ArrayCollection(); + } + + /** + * @return Collection + */ + public function getCommands(): Collection + { + return $this->commands; + } + + 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 addCommandGroup(CommandGroup $commandGroup): static + { + if (!$this->commandGroups->contains($commandGroup)) { + $this->commandGroups->add($commandGroup); + } + + return $this; + } + + public function removeCommandGroup(CommandGroup $commandGroup): static + { + $this->commandGroups->removeElement($commandGroup); + + return $this; + } + + public function getDatetime(): ?\DateTimeInterface + { + return $this->datetime; + } + + public function setDatetime(\DateTimeInterface $datetime): static + { + $this->datetime = $datetime; + + return $this; + } + + public function getNotes(): ?string + { + return $this->notes; + } + + public function setNotes(?string $notes): static + { + $this->notes = $notes; + + return $this; + } + + public function getStatus(): ?string + { + return $this->status; + } + + public function setStatus(string $status): static + { + $this->status = $status; + + return $this; + } +} diff --git a/src/Model/CommandTaskStatus.php b/src/Model/CommandTaskStatus.php new file mode 100644 index 0000000..9c1cab1 --- /dev/null +++ b/src/Model/CommandTaskStatus.php @@ -0,0 +1,33 @@ + 'Pendiente', + self::IN_PROGRESS => 'En progreso', + self::COMPLETED => 'Completado', + self::FAILED => 'Fallido', + ]; + + public static function getStatus(): array + { + return self::STATUS; + } + + public static function getCommandTaskStatus(string $status): ?string + { + return self::STATUS[$status] ?? null; + } + + public static function getStatusKeys(): array + { + return array_keys(self::STATUS); + } +} \ No newline at end of file diff --git a/src/Repository/CommandTaskRepository.php b/src/Repository/CommandTaskRepository.php new file mode 100644 index 0000000..e7b8cf7 --- /dev/null +++ b/src/Repository/CommandTaskRepository.php @@ -0,0 +1,43 @@ + + */ +class CommandTaskRepository extends ServiceEntityRepository +{ + public function __construct(ManagerRegistry $registry) + { + parent::__construct($registry, CommandTask::class); + } + + // /** + // * @return CommandTask[] Returns an array of CommandTask objects + // */ + // public function findByExampleField($value): array + // { + // return $this->createQueryBuilder('c') + // ->andWhere('c.exampleField = :val') + // ->setParameter('val', $value) + // ->orderBy('c.id', 'ASC') + // ->setMaxResults(10) + // ->getQuery() + // ->getResult() + // ; + // } + + // public function findOneBySomeField($value): ?CommandTask + // { + // return $this->createQueryBuilder('c') + // ->andWhere('c.exampleField = :val') + // ->setParameter('val', $value) + // ->getQuery() + // ->getOneOrNullResult() + // ; + // } +} -- 2.40.1 From 794237dd90274a867036895b19ff30a2a57e9dc3 Mon Sep 17 00:00:00 2001 From: Manuel Aranda Date: Tue, 17 Sep 2024 10:00:32 +0200 Subject: [PATCH 047/157] refs #659. CommandTask API --- config/api_platform/CommandTask.yaml | 32 +++++++ config/services.yaml | 5 + src/Dto/Input/CommandTaskInput.php | 96 ++++++++++++++++++++ src/Dto/Output/CommandGroupOutput.php | 6 +- src/Dto/Output/CommandOutput.php | 4 +- src/Dto/Output/CommandTaskOutput.php | 54 +++++++++++ src/Entity/CommandGroup.php | 1 - src/Entity/CommandTask.php | 22 +++++ src/Repository/CommandTaskRepository.php | 27 +----- src/State/Processor/CommandTaskProcessor.php | 68 ++++++++++++++ src/State/Provider/CommandTaskProvider.php | 71 +++++++++++++++ 11 files changed, 354 insertions(+), 32 deletions(-) create mode 100644 config/api_platform/CommandTask.yaml create mode 100644 src/Dto/Input/CommandTaskInput.php create mode 100644 src/Dto/Output/CommandTaskOutput.php create mode 100644 src/State/Processor/CommandTaskProcessor.php create mode 100644 src/State/Provider/CommandTaskProvider.php diff --git a/config/api_platform/CommandTask.yaml b/config/api_platform/CommandTask.yaml new file mode 100644 index 0000000..d008252 --- /dev/null +++ b/config/api_platform/CommandTask.yaml @@ -0,0 +1,32 @@ +resources: + App\Entity\CommandTask: + processor: App\State\Processor\CommandTaskProcessor + input: App\Dto\Input\CommandTaskInput + output: App\Dto\Output\CommandTaskOutput + normalizationContext: + groups: ['default', 'command-task:read'] + denormalizationContext: + groups: ['command-task:write'] + operations: + ApiPlatform\Metadata\GetCollection: + provider: App\State\Provider\CommandTaskProvider + filters: + - 'api_platform.filter.command_task.order' + - 'api_platform.filter.command_task.search' + - 'api_platform.filter.command_task.boolean' + + ApiPlatform\Metadata\Get: + provider: App\State\Provider\CommandTaskProvider + ApiPlatform\Metadata\Put: + provider: App\State\Provider\CommandTaskProvider + ApiPlatform\Metadata\Patch: + provider: App\State\Provider\CommandTaskProvider + ApiPlatform\Metadata\Post: ~ + ApiPlatform\Metadata\Delete: ~ + +properties: + App\Entity\CommandTask: + id: + identifier: false + uuid: + identifier: true \ No newline at end of file diff --git a/config/services.yaml b/config/services.yaml index be56306..4c0ac26 100644 --- a/config/services.yaml +++ b/config/services.yaml @@ -108,6 +108,11 @@ services: $itemProvider: '@api_platform.doctrine.orm.state.item_provider' App\State\Provider\CommandGroupProvider: + bind: + $collectionProvider: '@api_platform.doctrine.orm.state.collection_provider' + $itemProvider: '@api_platform.doctrine.orm.state.item_provider' + + App\State\Provider\CommandTaskProvider: bind: $collectionProvider: '@api_platform.doctrine.orm.state.collection_provider' $itemProvider: '@api_platform.doctrine.orm.state.item_provider' \ No newline at end of file diff --git a/src/Dto/Input/CommandTaskInput.php b/src/Dto/Input/CommandTaskInput.php new file mode 100644 index 0000000..dd6b89a --- /dev/null +++ b/src/Dto/Input/CommandTaskInput.php @@ -0,0 +1,96 @@ +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->dateTime = $commandTask->getDatetime(); + $this->notes = $commandTask->getNotes(); + } + + public function createOrUpdateEntity(?CommandTask $commandTask = null): CommandTask + { + if (!$commandTask) { + $commandTask = new CommandTask(); + } + + foreach ($this->commands as $command) { + $commandsToAdd[] = $command->getEntity(); + } + + $commandTask->setCommands( $commandsToAdd ?? [] ); + + foreach ($this->commandGroups as $commandGroup) { + $commandGroupsToAdd[] = $commandGroup->getEntity(); + } + + $commandTask->setCommandGroups( $commandGroupsToAdd ?? [] ); + $commandTask->setDatetime($this->dateTime); + $commandTask->setStatus(CommandTaskStatus::PENDING); + $commandTask->setNotes($this->notes); + + return $commandTask; + } +} diff --git a/src/Dto/Output/CommandGroupOutput.php b/src/Dto/Output/CommandGroupOutput.php index 636ca8e..8e6b645 100644 --- a/src/Dto/Output/CommandGroupOutput.php +++ b/src/Dto/Output/CommandGroupOutput.php @@ -10,13 +10,13 @@ use Symfony\Component\Serializer\Annotation\Groups; #[Get(shortName: 'CommandGroup')] final class CommandGroupOutput extends AbstractOutput { - #[Groups(['command-group:read'])] + #[Groups(['command-group:read', 'command-task:read'])] public string $name; - #[Groups(['command-group:read'])] + #[Groups(['command-group:read', 'command-task:read'])] public array $commands = []; - #[Groups(['command-group:read'])] + #[Groups(['command-group:read', 'command-task:read'])] public ?int $position = null; #[Groups(['command-group:read'])] diff --git a/src/Dto/Output/CommandOutput.php b/src/Dto/Output/CommandOutput.php index 5d92345..b65ee8d 100644 --- a/src/Dto/Output/CommandOutput.php +++ b/src/Dto/Output/CommandOutput.php @@ -9,10 +9,10 @@ use Symfony\Component\Serializer\Annotation\Groups; #[Get(shortName: 'Command')] final class CommandOutput extends AbstractOutput { - #[Groups(['command:read'])] + #[Groups(['command:read', 'command-group:read', 'command-task:read'])] public string $name; - #[Groups(['command:read'])] + #[Groups(['command:read', 'command-group:read', 'command-task:read'])] public ?string $script = ''; #[Groups(['command:read'])] diff --git a/src/Dto/Output/CommandTaskOutput.php b/src/Dto/Output/CommandTaskOutput.php new file mode 100644 index 0000000..c5852fe --- /dev/null +++ b/src/Dto/Output/CommandTaskOutput.php @@ -0,0 +1,54 @@ +commands = $commandTask->getCommands()->map( + fn(Command $command) => new CommandOutput($command) + )->toArray(); + + $this->commandGroups = $commandTask->getCommandGroups()->map( + fn(CommandGroup $commandGroup) => new CommandGroupOutput($commandGroup) + )->toArray(); + + $this->dateTime = $commandTask->getDateTime(); + $this->notes = $commandTask->getNotes(); + $this->status = $commandTask->getStatus(); + $this->createdAt = $commandTask->getCreatedAt(); + $this->createdBy = $commandTask->getCreatedBy(); + } +} \ No newline at end of file diff --git a/src/Entity/CommandGroup.php b/src/Entity/CommandGroup.php index 30befcb..6cf5271 100644 --- a/src/Entity/CommandGroup.php +++ b/src/Entity/CommandGroup.php @@ -54,7 +54,6 @@ class CommandGroup extends AbstractEntity return $this; } - public function addCommand(Command $command): static { if (!$this->commands->contains($command)) { diff --git a/src/Entity/CommandTask.php b/src/Entity/CommandTask.php index 3684eb4..8335067 100644 --- a/src/Entity/CommandTask.php +++ b/src/Entity/CommandTask.php @@ -48,6 +48,17 @@ class CommandTask extends AbstractEntity return $this->commands; } + public function setCommands(array $commands): static + { + $this->commands->clear(); + + foreach ($commands as $command){ + $this->addCommand($command); + } + + return $this; + } + public function addCommand(Command $command): static { if (!$this->commands->contains($command)) { @@ -72,6 +83,17 @@ class CommandTask extends AbstractEntity 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)) { diff --git a/src/Repository/CommandTaskRepository.php b/src/Repository/CommandTaskRepository.php index e7b8cf7..fc859f8 100644 --- a/src/Repository/CommandTaskRepository.php +++ b/src/Repository/CommandTaskRepository.php @@ -9,35 +9,10 @@ use Doctrine\Persistence\ManagerRegistry; /** * @extends ServiceEntityRepository */ -class CommandTaskRepository extends ServiceEntityRepository +class CommandTaskRepository extends AbstractRepository { public function __construct(ManagerRegistry $registry) { parent::__construct($registry, CommandTask::class); } - - // /** - // * @return CommandTask[] Returns an array of CommandTask objects - // */ - // public function findByExampleField($value): array - // { - // return $this->createQueryBuilder('c') - // ->andWhere('c.exampleField = :val') - // ->setParameter('val', $value) - // ->orderBy('c.id', 'ASC') - // ->setMaxResults(10) - // ->getQuery() - // ->getResult() - // ; - // } - - // public function findOneBySomeField($value): ?CommandTask - // { - // return $this->createQueryBuilder('c') - // ->andWhere('c.exampleField = :val') - // ->setParameter('val', $value) - // ->getQuery() - // ->getOneOrNullResult() - // ; - // } } diff --git a/src/State/Processor/CommandTaskProcessor.php b/src/State/Processor/CommandTaskProcessor.php new file mode 100644 index 0000000..bad348b --- /dev/null +++ b/src/State/Processor/CommandTaskProcessor.php @@ -0,0 +1,68 @@ +processCreateOrUpdate($data, $operation, $uriVariables, $context); + case $operation instanceof Delete: + return $this->processDelete($data, $operation, $uriVariables, $context); + } + } + + /** + * @throws \Exception + */ + private function processCreateOrUpdate($data, Operation $operation, array $uriVariables = [], array $context = []): CommandTaskOutput + { + if (!($data instanceof CommandTaskInput)) { + throw new \Exception(sprintf('data is not instance of %s', CommandTaskInput::class)); + } + + $entity = null; + if (isset($uriVariables['uuid'])) { + $entity = $this->commandTaskRepository->findOneByUuid($uriVariables['uuid']); + } + + $command = $data->createOrUpdateEntity($entity); + $this->validator->validate($command); + $this->commandTaskRepository->save($command); + + return new CommandTaskOutput($command); + } + + private function processDelete($data, Operation $operation, array $uriVariables = [], array $context = []): null + { + $user = $this->commandTaskRepository->findOneByUuid($uriVariables['uuid']); + $this->commandTaskRepository->delete($user); + + return null; + } +} diff --git a/src/State/Provider/CommandTaskProvider.php b/src/State/Provider/CommandTaskProvider.php new file mode 100644 index 0000000..49ef3d3 --- /dev/null +++ b/src/State/Provider/CommandTaskProvider.php @@ -0,0 +1,71 @@ +provideCollection($operation, $uriVariables, $context); + case $operation instanceof Patch: + case $operation instanceof Put: + return $this->provideInput($operation, $uriVariables, $context); + case $operation instanceof Get: + return $this->provideItem($operation, $uriVariables, $context); + } + } + + private function provideCollection(Operation $operation, array $uriVariables = [], array $context = []): object|array|null + { + $paginator = $this->collectionProvider->provide($operation, $uriVariables, $context); + + $items = new \ArrayObject(); + foreach ($paginator->getIterator() as $item){ + $items[] = new CommandTaskOutput($item); + } + + return new TraversablePaginator($items, $paginator->getCurrentPage(), $paginator->getItemsPerPage(), $paginator->getTotalItems()); + } + + public function provideItem(Operation $operation, array $uriVariables = [], array $context = []): object|array|null + { + $item = $this->itemProvider->provide($operation, $uriVariables, $context); + + if (!$item) { + throw new NotFoundHttpException('Command task not found'); + } + + return new CommandTaskOutput($item); + } + + public function provideInput(Operation $operation, array $uriVariables = [], array $context = []): object|array|null + { + if (isset($uriVariables['uuid'])) { + $item = $this->itemProvider->provide($operation, $uriVariables, $context); + + return $item !== null ? new CommandTaskInput($item) : null; + } + + return new CommandTaskInput(); + } +} -- 2.40.1 From b4f9f1e9bb29031782df01027758a0c1e7cae54e Mon Sep 17 00:00:00 2001 From: Manuel Aranda Date: Tue, 17 Sep 2024 10:37:20 +0200 Subject: [PATCH 048/157] refs #723. Added new entity Trace --- src/Entity/Trace.php | 103 +++++++++++++++++++++++++++++ src/Repository/TraceRepository.php | 19 ++++++ 2 files changed, 122 insertions(+) create mode 100644 src/Entity/Trace.php create mode 100644 src/Repository/TraceRepository.php diff --git a/src/Entity/Trace.php b/src/Entity/Trace.php new file mode 100644 index 0000000..2010979 --- /dev/null +++ b/src/Entity/Trace.php @@ -0,0 +1,103 @@ +client; + } + + public function setClient(?Client $client): static + { + $this->client = $client; + + return $this; + } + + public function getCommand(): ?Command + { + return $this->command; + } + + public function setCommand(?Command $command): static + { + $this->command = $command; + + return $this; + } + + public function getStatus(): ?string + { + return $this->status; + } + + public function setStatus(string $status): static + { + $this->status = $status; + + return $this; + } + + public function getOutput(): ?string + { + return $this->output; + } + + public function setOutput(?string $output): static + { + $this->output = $output; + + return $this; + } + + public function getExecutedAt(): ?\DateTimeImmutable + { + return $this->executedAt; + } + + public function setExecutedAt(\DateTimeImmutable $executedAt): static + { + $this->executedAt = $executedAt; + + return $this; + } + + public function getFinishedAt(): ?\DateTimeImmutable + { + return $this->finishedAt; + } + + public function setFinishedAt(\DateTimeImmutable $finishedAt): static + { + $this->finishedAt = $finishedAt; + + return $this; + } +} diff --git a/src/Repository/TraceRepository.php b/src/Repository/TraceRepository.php new file mode 100644 index 0000000..102fa7b --- /dev/null +++ b/src/Repository/TraceRepository.php @@ -0,0 +1,19 @@ + + */ +class TraceRepository extends AbstractRepository +{ + public function __construct(ManagerRegistry $registry) + { + parent::__construct($registry, Trace::class); + } + +} -- 2.40.1 From d1105a1e89d3e9a240444174ab582352fb2860e1 Mon Sep 17 00:00:00 2001 From: Manuel Aranda Date: Tue, 17 Sep 2024 11:28:02 +0200 Subject: [PATCH 049/157] refs #659. Trace API. New endpoint execute in command --- config/api_platform/Command.yaml | 7 ++++ config/api_platform/Trace.yaml | 20 +++++++++ config/services.yaml | 5 +++ migrations/Version20240917091950.php | 35 ++++++++++++++++ migrations/Version20240917092207.php | 31 ++++++++++++++ src/Controller/CommandExecuteAction.php | 44 +++++++++++++++++++ src/Dto/Input/CommandExecuteInput.php | 17 ++++++++ src/Dto/Output/ClientOutput.php | 8 ++-- src/Dto/Output/CommandOutput.php | 4 +- src/Dto/Output/TraceOutput.php | 50 ++++++++++++++++++++++ src/Entity/Trace.php | 14 +++---- src/Model/TraceStatus.php | 33 +++++++++++++++ src/State/Provider/TraceProvider.php | 56 +++++++++++++++++++++++++ 13 files changed, 311 insertions(+), 13 deletions(-) create mode 100644 config/api_platform/Trace.yaml create mode 100644 migrations/Version20240917091950.php create mode 100644 migrations/Version20240917092207.php create mode 100644 src/Controller/CommandExecuteAction.php create mode 100644 src/Dto/Input/CommandExecuteInput.php create mode 100644 src/Dto/Output/TraceOutput.php create mode 100644 src/Model/TraceStatus.php create mode 100644 src/State/Provider/TraceProvider.php diff --git a/config/api_platform/Command.yaml b/config/api_platform/Command.yaml index f8480ce..fa14e2b 100644 --- a/config/api_platform/Command.yaml +++ b/config/api_platform/Command.yaml @@ -24,6 +24,13 @@ resources: ApiPlatform\Metadata\Post: ~ ApiPlatform\Metadata\Delete: ~ + execute: + class: ApiPlatform\Metadata\Post + method: POST + input: App\Dto\Input\CommandExecuteInput + uriTemplate: /commands/{uuid}/execute + controller: App\Controller\CommandExecuteAction + properties: App\Entity\Command: id: diff --git a/config/api_platform/Trace.yaml b/config/api_platform/Trace.yaml new file mode 100644 index 0000000..c7e74db --- /dev/null +++ b/config/api_platform/Trace.yaml @@ -0,0 +1,20 @@ +resources: + App\Entity\Trace: + output: App\Dto\Output\TraceOutput + normalizationContext: + groups: ['default', 'trace:read'] + operations: + ApiPlatform\Metadata\GetCollection: + provider: App\State\Provider\TraceProvider + filters: + - 'api_platform.filter.trace.order' + - 'api_platform.filter.trace.search' + ApiPlatform\Metadata\Get: + provider: App\State\Provider\TraceProvider + +properties: + App\Entity\Trace: + id: + identifier: false + uuid: + identifier: true \ No newline at end of file diff --git a/config/services.yaml b/config/services.yaml index 4c0ac26..c9d2a91 100644 --- a/config/services.yaml +++ b/config/services.yaml @@ -113,6 +113,11 @@ services: $itemProvider: '@api_platform.doctrine.orm.state.item_provider' App\State\Provider\CommandTaskProvider: + bind: + $collectionProvider: '@api_platform.doctrine.orm.state.collection_provider' + $itemProvider: '@api_platform.doctrine.orm.state.item_provider' + + App\State\Provider\TraceProvider: bind: $collectionProvider: '@api_platform.doctrine.orm.state.collection_provider' $itemProvider: '@api_platform.doctrine.orm.state.item_provider' \ No newline at end of file diff --git a/migrations/Version20240917091950.php b/migrations/Version20240917091950.php new file mode 100644 index 0000000..7645fa2 --- /dev/null +++ b/migrations/Version20240917091950.php @@ -0,0 +1,35 @@ +addSql('CREATE TABLE trace (id INT AUTO_INCREMENT NOT NULL, client_id INT NOT NULL, command_id INT NOT NULL, uuid CHAR(36) NOT NULL COMMENT \'(DC2Type:uuid)\', migration_id VARCHAR(255) DEFAULT NULL, created_at DATETIME NOT NULL, updated_at DATETIME NOT NULL, created_by VARCHAR(255) DEFAULT NULL, updated_by VARCHAR(255) DEFAULT NULL, status VARCHAR(255) NOT NULL, output VARCHAR(255) DEFAULT NULL, executed_at DATETIME NOT NULL, finished_at DATETIME NOT NULL, UNIQUE INDEX UNIQ_315BD5A1D17F50A6 (uuid), INDEX IDX_315BD5A119EB6921 (client_id), INDEX IDX_315BD5A133E1689A (command_id), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB'); + $this->addSql('ALTER TABLE trace ADD CONSTRAINT FK_315BD5A119EB6921 FOREIGN KEY (client_id) REFERENCES client (id)'); + $this->addSql('ALTER TABLE trace ADD CONSTRAINT FK_315BD5A133E1689A FOREIGN KEY (command_id) REFERENCES command (id)'); + } + + public function down(Schema $schema): void + { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql('ALTER TABLE trace DROP FOREIGN KEY FK_315BD5A119EB6921'); + $this->addSql('ALTER TABLE trace DROP FOREIGN KEY FK_315BD5A133E1689A'); + $this->addSql('DROP TABLE trace'); + } +} diff --git a/migrations/Version20240917092207.php b/migrations/Version20240917092207.php new file mode 100644 index 0000000..f291bd8 --- /dev/null +++ b/migrations/Version20240917092207.php @@ -0,0 +1,31 @@ +addSql('ALTER TABLE trace CHANGE finished_at finished_at DATETIME DEFAULT NULL'); + } + + public function down(Schema $schema): void + { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql('ALTER TABLE trace CHANGE finished_at finished_at DATETIME NOT NULL'); + } +} diff --git a/src/Controller/CommandExecuteAction.php b/src/Controller/CommandExecuteAction.php new file mode 100644 index 0000000..fbdfda9 --- /dev/null +++ b/src/Controller/CommandExecuteAction.php @@ -0,0 +1,44 @@ +clients; + + /** @var Client $client */ + foreach ($clients as $client) { + $trace = new Trace(); + $trace->setClient($client->getEntity()); + $trace->setCommand($command); + $trace->setStatus(TraceStatus::IN_PROGRESS); + $trace->setExecutedAt(new \DateTimeImmutable()); + + $this->entityManager->persist($trace); + } + + $this->entityManager->flush(); + + return new JsonResponse(data: 'Command executed successfully', status: Response::HTTP_OK); + } +} \ No newline at end of file diff --git a/src/Dto/Input/CommandExecuteInput.php b/src/Dto/Input/CommandExecuteInput.php new file mode 100644 index 0000000..4630a47 --- /dev/null +++ b/src/Dto/Input/CommandExecuteInput.php @@ -0,0 +1,17 @@ +command = new CommandOutput($trace->getCommand()); + $this->client = new ClientOutput($trace->getClient()); + $this->status = $trace->getStatus(); + $this->executedAt = $trace->getExecutedAt(); + $this->output = $trace->getOutput(); + $this->finishedAt = $trace->getFinishedAt(); + $this->createdAt = $trace->getCreatedAt(); + $this->createdBy = $trace->getCreatedBy(); + } +} \ No newline at end of file diff --git a/src/Entity/Trace.php b/src/Entity/Trace.php index 2010979..1f2f318 100644 --- a/src/Entity/Trace.php +++ b/src/Entity/Trace.php @@ -24,10 +24,10 @@ class Trace extends AbstractEntity private ?string $output = null; #[ORM\Column(type: Types::DATETIME_MUTABLE)] - private ?\DateTimeImmutable $executedAt = null; + private ?\DateTimeInterface $executedAt = null; - #[ORM\Column(type: Types::DATETIME_MUTABLE)] - private ?\DateTimeImmutable $finishedAt = null; + #[ORM\Column(type: Types::DATETIME_MUTABLE, nullable: true)] + private ?\DateTimeInterface $finishedAt = null; public function getClient(): ?Client { @@ -77,24 +77,24 @@ class Trace extends AbstractEntity return $this; } - public function getExecutedAt(): ?\DateTimeImmutable + public function getExecutedAt(): ?\DateTimeInterface { return $this->executedAt; } - public function setExecutedAt(\DateTimeImmutable $executedAt): static + public function setExecutedAt(\DateTimeInterface $executedAt): static { $this->executedAt = $executedAt; return $this; } - public function getFinishedAt(): ?\DateTimeImmutable + public function getFinishedAt(): ?\DateTimeInterface { return $this->finishedAt; } - public function setFinishedAt(\DateTimeImmutable $finishedAt): static + public function setFinishedAt(\DateTimeInterface $finishedAt): static { $this->finishedAt = $finishedAt; diff --git a/src/Model/TraceStatus.php b/src/Model/TraceStatus.php new file mode 100644 index 0000000..fc790b2 --- /dev/null +++ b/src/Model/TraceStatus.php @@ -0,0 +1,33 @@ + 'Pendiente', + self::IN_PROGRESS => 'En progreso', + self::COMPLETED => 'Completado', + self::FAILED => 'Fallido', + ]; + + public static function getStatus(): array + { + return self::STATUS; + } + + public static function getTraceStatus(string $status): ?string + { + return self::STATUS[$status] ?? null; + } + + public static function getStatusKeys(): array + { + return array_keys(self::STATUS); + } +} \ No newline at end of file diff --git a/src/State/Provider/TraceProvider.php b/src/State/Provider/TraceProvider.php new file mode 100644 index 0000000..aeefd56 --- /dev/null +++ b/src/State/Provider/TraceProvider.php @@ -0,0 +1,56 @@ +provideCollection($operation, $uriVariables, $context); + case $operation instanceof Get: + return $this->provideItem($operation, $uriVariables, $context); + } + } + + private function provideCollection(Operation $operation, array $uriVariables = [], array $context = []): object + { + $paginator = $this->collectionProvider->provide($operation, $uriVariables, $context); + + $items = new \ArrayObject(); + foreach ($paginator->getIterator() as $item){ + $items[] = new TraceOutput($item); + } + + return new TraversablePaginator($items, $paginator->getCurrentPage(), $paginator->getItemsPerPage(), $paginator->getTotalItems()); + } + + public function provideItem(Operation $operation, array $uriVariables = [], array $context = []): object|array|null + { + $item = $this->itemProvider->provide($operation, $uriVariables, $context); + + if (!$item) { + throw new NotFoundHttpException('Trace not found'); + } + + return new TraceOutput($item); + } +} -- 2.40.1 From 34c2144ffd973bbdab395e58a382d78df57809eb Mon Sep 17 00:00:00 2001 From: Manuel Aranda Date: Tue, 24 Sep 2024 09:09:29 +0200 Subject: [PATCH 050/157] refs #659. CommandGroup execute endpoint --- README.md | 2 - config/api_platform/Command.yaml | 2 +- config/api_platform/CommandGroup.yaml | 7 +++ config/packages/api_platform.yaml | 4 +- src/Controller/CommandGroupExecuteAction.php | 46 ++++++++++++++++++++ src/Dto/Input/CommandGroupExecuteInput.php | 17 ++++++++ src/Entity/Command.php | 2 +- 7 files changed, 75 insertions(+), 5 deletions(-) create mode 100644 src/Controller/CommandGroupExecuteAction.php create mode 100644 src/Dto/Input/CommandGroupExecuteInput.php diff --git a/README.md b/README.md index 54d5c90..ad2d3fa 100644 --- a/README.md +++ b/README.md @@ -134,8 +134,6 @@ docker exec ogcore-php php bin/console opengnsys:migration:hardware-profiles #ca docker exec ogcore-php php bin/console opengnsys:migration:clients #cargamos los clientes docker exec ogcore-php php bin/console opengnsys:migration:os #cargamos los sistemas operativos docker exec ogcore-php php bin/console opengnsys:migration:partition #cargamos las particiones - - ``` ## Objetos de interés diff --git a/config/api_platform/Command.yaml b/config/api_platform/Command.yaml index fa14e2b..425259b 100644 --- a/config/api_platform/Command.yaml +++ b/config/api_platform/Command.yaml @@ -24,7 +24,7 @@ resources: ApiPlatform\Metadata\Post: ~ ApiPlatform\Metadata\Delete: ~ - execute: + command_execute: class: ApiPlatform\Metadata\Post method: POST input: App\Dto\Input\CommandExecuteInput diff --git a/config/api_platform/CommandGroup.yaml b/config/api_platform/CommandGroup.yaml index 40ede0b..aa09ccc 100644 --- a/config/api_platform/CommandGroup.yaml +++ b/config/api_platform/CommandGroup.yaml @@ -31,6 +31,13 @@ resources: uriTemplate: /command-groups/{uuid}/add-commands controller: App\Controller\CommandGroupAddCommandsAction + command_group_execute: + class: ApiPlatform\Metadata\Post + method: POST + input: App\Dto\Input\CommandGroupExecuteInput + uriTemplate: /command-groups/{uuid}/execute + controller: App\Controller\CommandGroupExecuteAction + properties: App\Entity\CommandGroup: id: diff --git a/config/packages/api_platform.yaml b/config/packages/api_platform.yaml index 352abc2..ee6fc53 100644 --- a/config/packages/api_platform.yaml +++ b/config/packages/api_platform.yaml @@ -40,4 +40,6 @@ api_platform: api_keys: apiKey: name: Authorization - type: header \ No newline at end of file + type: header + exception_to_status: + Doctrine\DBAL\Exception\ForeignKeyConstraintViolationException: 409 \ No newline at end of file diff --git a/src/Controller/CommandGroupExecuteAction.php b/src/Controller/CommandGroupExecuteAction.php new file mode 100644 index 0000000..563a488 --- /dev/null +++ b/src/Controller/CommandGroupExecuteAction.php @@ -0,0 +1,46 @@ +clients; + + foreach ($commandGroup->getCommands() as $command) { + /** @var Client $client */ + foreach ($clients as $client) { + $trace = new Trace(); + $trace->setClient($client->getEntity()); + $trace->setCommand($command); + $trace->setStatus(TraceStatus::IN_PROGRESS); + $trace->setExecutedAt(new \DateTimeImmutable()); + + $this->entityManager->persist($trace); + } + } + + $this->entityManager->flush(); + + return new JsonResponse(data: 'Command group executed successfully', status: Response::HTTP_OK); + } +} \ No newline at end of file diff --git a/src/Dto/Input/CommandGroupExecuteInput.php b/src/Dto/Input/CommandGroupExecuteInput.php new file mode 100644 index 0000000..19dd317 --- /dev/null +++ b/src/Dto/Input/CommandGroupExecuteInput.php @@ -0,0 +1,17 @@ + */ - #[ORM\ManyToMany(targetEntity: CommandTask::class, mappedBy: 'command')] + #[ORM\ManyToMany(targetEntity: CommandTask::class, mappedBy: 'commands')] private Collection $commandTasks; public function __construct() -- 2.40.1 From 1394f3855b66f284f4dd448884182b922e740903 Mon Sep 17 00:00:00 2001 From: Manuel Aranda Date: Tue, 24 Sep 2024 09:20:03 +0200 Subject: [PATCH 051/157] refs #723. CommandEntity. Updated 'script' longtext' --- migrations/Version20240924071858.php | 31 ++++++++++++++++++++++++++++ src/Entity/Command.php | 3 ++- 2 files changed, 33 insertions(+), 1 deletion(-) create mode 100644 migrations/Version20240924071858.php diff --git a/migrations/Version20240924071858.php b/migrations/Version20240924071858.php new file mode 100644 index 0000000..d758402 --- /dev/null +++ b/migrations/Version20240924071858.php @@ -0,0 +1,31 @@ +addSql('ALTER TABLE command CHANGE script script LONGTEXT NOT NULL'); + } + + public function down(Schema $schema): void + { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql('ALTER TABLE command CHANGE script script VARCHAR(255) NOT NULL'); + } +} diff --git a/src/Entity/Command.php b/src/Entity/Command.php index 974730a..f43aa30 100644 --- a/src/Entity/Command.php +++ b/src/Entity/Command.php @@ -3,6 +3,7 @@ namespace App\Entity; use App\Repository\CommandRepository; +use Doctrine\DBAL\Types\Types; use Doctrine\Common\Collections\ArrayCollection; use Doctrine\Common\Collections\Collection; use Doctrine\ORM\Mapping as ORM; @@ -13,7 +14,7 @@ class Command extends AbstractEntity use NameableTrait; use ToggleableTrait; - #[ORM\Column(length: 255)] + #[ORM\Column(type: Types::TEXT)] private ?string $script = null; #[ORM\Column(length: 255, nullable: true)] -- 2.40.1 From bdd5093a6a268055904738ef16a3bbe478648b4f Mon Sep 17 00:00:00 2001 From: Manuel Aranda Date: Tue, 24 Sep 2024 11:07:33 +0200 Subject: [PATCH 052/157] refs #724. Added RemoteCalendar and RemoteCalendarRule entities --- migrations/Version20240924090429.php | 41 ++++++ src/Entity/OrganizationalUnit.php | 4 + src/Entity/RemoteCalendar.php | 72 ++++++++++ src/Entity/RemoteCalendarRule.php | 131 ++++++++++++++++++ src/Repository/RemoteCalendarRepository.php | 43 ++++++ .../RemoteCalendarRuleRepository.php | 43 ++++++ 6 files changed, 334 insertions(+) create mode 100644 migrations/Version20240924090429.php create mode 100644 src/Entity/RemoteCalendar.php create mode 100644 src/Entity/RemoteCalendarRule.php create mode 100644 src/Repository/RemoteCalendarRepository.php create mode 100644 src/Repository/RemoteCalendarRuleRepository.php diff --git a/migrations/Version20240924090429.php b/migrations/Version20240924090429.php new file mode 100644 index 0000000..ba9563c --- /dev/null +++ b/migrations/Version20240924090429.php @@ -0,0 +1,41 @@ +addSql('CREATE TABLE remote_calendar (id INT AUTO_INCREMENT NOT NULL, uuid CHAR(36) NOT NULL COMMENT \'(DC2Type:uuid)\', migration_id VARCHAR(255) DEFAULT NULL, created_at DATETIME NOT NULL, updated_at DATETIME NOT NULL, created_by VARCHAR(255) DEFAULT NULL, updated_by VARCHAR(255) DEFAULT NULL, name VARCHAR(255) NOT NULL, UNIQUE INDEX UNIQ_BD3BDE0AD17F50A6 (uuid), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB'); + $this->addSql('CREATE TABLE remote_calendar_rule (id INT AUTO_INCREMENT NOT NULL, remote_calendar_id INT DEFAULT 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, busy_weekdays JSON NOT NULL COMMENT \'(DC2Type:json)\', busy_from_hour DATETIME NOT NULL, busy_to_hour DATETIME NOT NULL, is_remote_available TINYINT(1) NOT NULL, available_from_date DATE NOT NULL, available_to_date DATE NOT NULL, available_reason VARCHAR(255) NOT NULL, UNIQUE INDEX UNIQ_EE93D058D17F50A6 (uuid), INDEX IDX_EE93D058C56641EE (remote_calendar_id), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB'); + $this->addSql('ALTER TABLE remote_calendar_rule ADD CONSTRAINT FK_EE93D058C56641EE FOREIGN KEY (remote_calendar_id) REFERENCES remote_calendar (id)'); + $this->addSql('ALTER TABLE organizational_unit ADD remote_calendar_id INT DEFAULT NULL'); + $this->addSql('ALTER TABLE organizational_unit ADD CONSTRAINT FK_749AEB2DC56641EE FOREIGN KEY (remote_calendar_id) REFERENCES remote_calendar (id)'); + $this->addSql('CREATE INDEX IDX_749AEB2DC56641EE ON organizational_unit (remote_calendar_id)'); + } + + public function down(Schema $schema): void + { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql('ALTER TABLE organizational_unit DROP FOREIGN KEY FK_749AEB2DC56641EE'); + $this->addSql('ALTER TABLE remote_calendar_rule DROP FOREIGN KEY FK_EE93D058C56641EE'); + $this->addSql('DROP TABLE remote_calendar'); + $this->addSql('DROP TABLE remote_calendar_rule'); + $this->addSql('DROP INDEX IDX_749AEB2DC56641EE ON organizational_unit'); + $this->addSql('ALTER TABLE organizational_unit DROP remote_calendar_id'); + } +} diff --git a/src/Entity/OrganizationalUnit.php b/src/Entity/OrganizationalUnit.php index 469a9f5..11cfa89 100644 --- a/src/Entity/OrganizationalUnit.php +++ b/src/Entity/OrganizationalUnit.php @@ -84,6 +84,10 @@ class OrganizationalUnit extends AbstractEntity #[ORM\OneToMany(mappedBy: 'organizationalUnit', targetEntity: SoftwareProfile::class)] private Collection $softwareProfiles; + #[ORM\ManyToOne(targetEntity: RemoteCalendar::class, inversedBy: 'organizationalUnits', cascade: ['persist'])] + #[ORM\JoinColumn(nullable: true)] + private ?RemoteCalendar $remoteCalendar = null; + public function __construct() { parent::__construct(); diff --git a/src/Entity/RemoteCalendar.php b/src/Entity/RemoteCalendar.php new file mode 100644 index 0000000..d15e499 --- /dev/null +++ b/src/Entity/RemoteCalendar.php @@ -0,0 +1,72 @@ + + */ + #[ORM\OneToMany(mappedBy: 'remoteCalendar', targetEntity: OrganizationalUnit::class)] + private Collection $organizationalUnits; + + /** + * @var Collection + */ + #[ORM\OneToMany(mappedBy: 'remoteCalendar', targetEntity: RemoteCalendarRule::class)] + private Collection $rules; + + public function __construct() + { + parent::__construct(); + $this->rules = new ArrayCollection(); + } + + public function getOrganizationalUnits(): Collection + { + return $this->organizationalUnits; + } + + public function setOrganizationalUnits(Collection $organizationalUnits): void + { + $this->organizationalUnits = $organizationalUnits; + } + + /** + * @return Collection + */ + public function getRules(): Collection + { + return $this->rules; + } + + public function addRule(RemoteCalendarRule $rule): static + { + if (!$this->rules->contains($rule)) { + $this->rules->add($rule); + $rule->setRemoteCalendar($this); + } + + return $this; + } + + public function removeRule(RemoteCalendarRule $rule): static + { + if ($this->rules->removeElement($rule)) { + // set the owning side to null (unless already changed) + if ($rule->getRemoteCalendar() === $this) { + $rule->setRemoteCalendar(null); + } + } + + return $this; + } +} diff --git a/src/Entity/RemoteCalendarRule.php b/src/Entity/RemoteCalendarRule.php new file mode 100644 index 0000000..92c4b1b --- /dev/null +++ b/src/Entity/RemoteCalendarRule.php @@ -0,0 +1,131 @@ +busyWeekdays; + } + + public function setBusyWeekdays(array $busyWeekdays): static + { + $this->busyWeekdays = $busyWeekdays; + + return $this; + } + + public function getBusyFromHour(): ?\DateTimeInterface + { + return $this->busyFromHour; + } + + public function setBusyFromHour(\DateTimeInterface $busyFromHour): static + { + $this->busyFromHour = $busyFromHour; + + return $this; + } + + public function getBusyToHour(): ?\DateTimeInterface + { + return $this->busyToHour; + } + + public function setBusyToHour(\DateTimeInterface $busyToHour): static + { + $this->busyToHour = $busyToHour; + + return $this; + } + + public function isRemoteAvailable(): ?bool + { + return $this->isRemoteAvailable; + } + + public function setRemoteAvailable(bool $isRemoteAvailable): static + { + $this->isRemoteAvailable = $isRemoteAvailable; + + return $this; + } + + public function getAvailableFromDate(): ?\DateTimeInterface + { + return $this->availableFromDate; + } + + public function setAvailableFromDate(\DateTimeInterface $availableFromDate): static + { + $this->availableFromDate = $availableFromDate; + + return $this; + } + + public function getAvailableToDate(): ?\DateTimeInterface + { + return $this->availableToDate; + } + + public function setAvailableToDate(\DateTimeInterface $availableToDate): static + { + $this->availableToDate = $availableToDate; + + return $this; + } + + public function getAvailableReason(): ?string + { + return $this->availableReason; + } + + public function setAvailableReason(string $availableReason): static + { + $this->availableReason = $availableReason; + + return $this; + } + + public function getRemoteCalendar(): ?RemoteCalendar + { + return $this->remoteCalendar; + } + + public function setRemoteCalendar(?RemoteCalendar $remoteCalendar): static + { + $this->remoteCalendar = $remoteCalendar; + + return $this; + } +} diff --git a/src/Repository/RemoteCalendarRepository.php b/src/Repository/RemoteCalendarRepository.php new file mode 100644 index 0000000..331c150 --- /dev/null +++ b/src/Repository/RemoteCalendarRepository.php @@ -0,0 +1,43 @@ + + */ +class RemoteCalendarRepository extends ServiceEntityRepository +{ + public function __construct(ManagerRegistry $registry) + { + parent::__construct($registry, RemoteCalendar::class); + } + +// /** +// * @return RemoteCalendar[] Returns an array of RemoteCalendar objects +// */ +// public function findByExampleField($value): array +// { +// return $this->createQueryBuilder('r') +// ->andWhere('r.exampleField = :val') +// ->setParameter('val', $value) +// ->orderBy('r.id', 'ASC') +// ->setMaxResults(10) +// ->getQuery() +// ->getResult() +// ; +// } + +// public function findOneBySomeField($value): ?RemoteCalendar +// { +// return $this->createQueryBuilder('r') +// ->andWhere('r.exampleField = :val') +// ->setParameter('val', $value) +// ->getQuery() +// ->getOneOrNullResult() +// ; +// } +} diff --git a/src/Repository/RemoteCalendarRuleRepository.php b/src/Repository/RemoteCalendarRuleRepository.php new file mode 100644 index 0000000..1af754c --- /dev/null +++ b/src/Repository/RemoteCalendarRuleRepository.php @@ -0,0 +1,43 @@ + + */ +class RemoteCalendarRuleRepository extends ServiceEntityRepository +{ + public function __construct(ManagerRegistry $registry) + { + parent::__construct($registry, RemoteCalendarRule::class); + } + +// /** +// * @return RemoteCalendarRule[] Returns an array of RemoteCalendarRule objects +// */ +// public function findByExampleField($value): array +// { +// return $this->createQueryBuilder('r') +// ->andWhere('r.exampleField = :val') +// ->setParameter('val', $value) +// ->orderBy('r.id', 'ASC') +// ->setMaxResults(10) +// ->getQuery() +// ->getResult() +// ; +// } + +// public function findOneBySomeField($value): ?RemoteCalendarRule +// { +// return $this->createQueryBuilder('r') +// ->andWhere('r.exampleField = :val') +// ->setParameter('val', $value) +// ->getQuery() +// ->getOneOrNullResult() +// ; +// } +} -- 2.40.1 From 083ac514a8df627b7c01167cf72d94302ecea0ad Mon Sep 17 00:00:00 2001 From: Manuel Aranda Date: Tue, 24 Sep 2024 15:31:18 +0200 Subject: [PATCH 053/157] refs #725. Added RemoteCalendar and RemoteCalendarRule APIs --- config/api_platform/RemoteCalendar.yaml | 31 +++++++ config/api_platform/RemoteCalendarRule.yaml | 31 +++++++ config/services.yaml | 10 +++ migrations/Version20240924095558.php | 31 +++++++ migrations/Version20240924100335.php | 31 +++++++ migrations/Version20240924102357.php | 31 +++++++ src/Dto/Input/RemoteCalendarInput.php | 55 +++++++++++++ src/Dto/Input/RemoteCalendarRuleInput.php | 82 +++++++++++++++++++ src/Dto/Output/RemoteCalendarOutput.php | 32 ++++++++ src/Dto/Output/RemoteCalendarRuleOutput.php | 45 ++++++++++ src/Entity/RemoteCalendar.php | 14 ++++ src/Entity/RemoteCalendarRule.php | 42 ++++++---- src/Repository/RemoteCalendarRepository.php | 27 +----- .../RemoteCalendarRuleRepository.php | 27 +----- .../Processor/RemoteCalendarProcessor.php | 68 +++++++++++++++ .../Processor/RemoteCalendarRuleProcessor.php | 68 +++++++++++++++ src/State/Provider/RemoteCalendarProvider.php | 71 ++++++++++++++++ .../Provider/RemoteCalendarRuleProvider.php | 71 ++++++++++++++++ 18 files changed, 697 insertions(+), 70 deletions(-) create mode 100644 config/api_platform/RemoteCalendar.yaml create mode 100644 config/api_platform/RemoteCalendarRule.yaml create mode 100644 migrations/Version20240924095558.php create mode 100644 migrations/Version20240924100335.php create mode 100644 migrations/Version20240924102357.php create mode 100644 src/Dto/Input/RemoteCalendarInput.php create mode 100644 src/Dto/Input/RemoteCalendarRuleInput.php create mode 100644 src/Dto/Output/RemoteCalendarOutput.php create mode 100644 src/Dto/Output/RemoteCalendarRuleOutput.php create mode 100644 src/State/Processor/RemoteCalendarProcessor.php create mode 100644 src/State/Processor/RemoteCalendarRuleProcessor.php create mode 100644 src/State/Provider/RemoteCalendarProvider.php create mode 100644 src/State/Provider/RemoteCalendarRuleProvider.php diff --git a/config/api_platform/RemoteCalendar.yaml b/config/api_platform/RemoteCalendar.yaml new file mode 100644 index 0000000..9585ffe --- /dev/null +++ b/config/api_platform/RemoteCalendar.yaml @@ -0,0 +1,31 @@ +resources: + App\Entity\RemoteCalendar: + processor: App\State\Processor\RemoteCalendarProcessor + input: App\Dto\Input\RemoteCalendarInput + output: App\Dto\Output\RemoteCalendarOutput + normalizationContext: + groups: ['default', 'remote-calendar:read'] + denormalizationContext: + groups: ['remote-calendar:write'] + operations: + ApiPlatform\Metadata\GetCollection: + provider: App\State\Provider\RemoteCalendarProvider + filters: + - 'api_platform.filter.remote_calendar.order' + - 'api_platform.filter.remote_calendar.search' + + ApiPlatform\Metadata\Get: + provider: App\State\Provider\RemoteCalendarProvider + ApiPlatform\Metadata\Put: + provider: App\State\Provider\RemoteCalendarProvider + ApiPlatform\Metadata\Patch: + provider: App\State\Provider\RemoteCalendarProvider + ApiPlatform\Metadata\Post: ~ + ApiPlatform\Metadata\Delete: ~ + +properties: + App\Entity\RemoteCalendar: + id: + identifier: false + uuid: + identifier: true \ No newline at end of file diff --git a/config/api_platform/RemoteCalendarRule.yaml b/config/api_platform/RemoteCalendarRule.yaml new file mode 100644 index 0000000..89f02eb --- /dev/null +++ b/config/api_platform/RemoteCalendarRule.yaml @@ -0,0 +1,31 @@ +resources: + App\Entity\RemoteCalendarRule: + processor: App\State\Processor\RemoteCalendarRuleProcessor + input: App\Dto\Input\RemoteCalendarRuleInput + output: App\Dto\Output\RemoteCalendarRuleOutput + normalizationContext: + groups: ['default', 'remote-calendar-rule:read'] + denormalizationContext: + groups: ['remote-calendar-rule:write'] + operations: + ApiPlatform\Metadata\GetCollection: + provider: App\State\Provider\RemoteCalendarRuleProvider + filters: + - 'api_platform.filter.remote_calendar_rule.order' + - 'api_platform.filter.remote_calendar_rule.search' + + ApiPlatform\Metadata\Get: + provider: App\State\Provider\RemoteCalendarRuleProvider + ApiPlatform\Metadata\Put: + provider: App\State\Provider\RemoteCalendarRuleProvider + ApiPlatform\Metadata\Patch: + provider: App\State\Provider\RemoteCalendarRuleProvider + ApiPlatform\Metadata\Post: ~ + ApiPlatform\Metadata\Delete: ~ + +properties: + App\Entity\RemoteCalendarRule: + id: + identifier: false + uuid: + identifier: true \ No newline at end of file diff --git a/config/services.yaml b/config/services.yaml index beee276..52a86a4 100644 --- a/config/services.yaml +++ b/config/services.yaml @@ -101,3 +101,13 @@ services: bind: $collectionProvider: '@api_platform.doctrine.orm.state.collection_provider' $itemProvider: '@api_platform.doctrine.orm.state.item_provider' + + App\State\Provider\RemoteCalendarProvider: + bind: + $collectionProvider: '@api_platform.doctrine.orm.state.collection_provider' + $itemProvider: '@api_platform.doctrine.orm.state.item_provider' + + App\State\Provider\RemoteCalendarRuleProvider: + bind: + $collectionProvider: '@api_platform.doctrine.orm.state.collection_provider' + $itemProvider: '@api_platform.doctrine.orm.state.item_provider' \ No newline at end of file diff --git a/migrations/Version20240924095558.php b/migrations/Version20240924095558.php new file mode 100644 index 0000000..b8b2d3b --- /dev/null +++ b/migrations/Version20240924095558.php @@ -0,0 +1,31 @@ +addSql('ALTER TABLE remote_calendar_rule CHANGE busy_from_hour busy_from_hour TIME NOT NULL, CHANGE busy_to_hour busy_to_hour TIME NOT NULL'); + } + + public function down(Schema $schema): void + { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql('ALTER TABLE remote_calendar_rule CHANGE busy_from_hour busy_from_hour DATETIME NOT NULL, CHANGE busy_to_hour busy_to_hour DATETIME NOT NULL'); + } +} diff --git a/migrations/Version20240924100335.php b/migrations/Version20240924100335.php new file mode 100644 index 0000000..9edf05f --- /dev/null +++ b/migrations/Version20240924100335.php @@ -0,0 +1,31 @@ +addSql('CREATE UNIQUE INDEX UNIQ_IDENTIFIER_NAME ON remote_calendar (name)'); + } + + public function down(Schema $schema): void + { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql('DROP INDEX UNIQ_IDENTIFIER_NAME ON remote_calendar'); + } +} diff --git a/migrations/Version20240924102357.php b/migrations/Version20240924102357.php new file mode 100644 index 0000000..069d3d3 --- /dev/null +++ b/migrations/Version20240924102357.php @@ -0,0 +1,31 @@ +addSql('ALTER TABLE remote_calendar_rule CHANGE busy_weekdays busy_weekdays JSON DEFAULT NULL COMMENT \'(DC2Type:json)\', CHANGE busy_from_hour busy_from_hour TIME DEFAULT NULL, CHANGE busy_to_hour busy_to_hour TIME DEFAULT NULL, CHANGE available_from_date available_from_date DATE DEFAULT NULL, CHANGE available_to_date available_to_date DATE DEFAULT NULL, CHANGE available_reason available_reason 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 remote_calendar_rule CHANGE busy_weekdays busy_weekdays JSON NOT NULL COMMENT \'(DC2Type:json)\', CHANGE busy_from_hour busy_from_hour TIME NOT NULL, CHANGE busy_to_hour busy_to_hour TIME NOT NULL, CHANGE available_from_date available_from_date DATE NOT NULL, CHANGE available_to_date available_to_date DATE NOT NULL, CHANGE available_reason available_reason VARCHAR(255) NOT NULL'); + } +} diff --git a/src/Dto/Input/RemoteCalendarInput.php b/src/Dto/Input/RemoteCalendarInput.php new file mode 100644 index 0000000..512f157 --- /dev/null +++ b/src/Dto/Input/RemoteCalendarInput.php @@ -0,0 +1,55 @@ +name = $remoteCalendar->getName(); + + if ($remoteCalendar->getRules()) { + foreach ($remoteCalendar->getRules() as $rule) { + $this->remoteCalendarRules[] = new RemoteCalendarRuleOutput($rule); + } + } + } + + public function createOrUpdateEntity(?RemoteCalendar $remoteCalendar = null): RemoteCalendar + { + if (!$remoteCalendar) { + $remoteCalendar = new RemoteCalendar(); + } + + $remoteCalendar->setName($this->name); + + foreach ($this->remoteCalendarRules as $rule) { + $ruleToAdd[] = $rule->getEntity(); + } + + $remoteCalendar->setRules( $ruleToAdd ?? [] ); + + return $remoteCalendar; + } +} \ No newline at end of file diff --git a/src/Dto/Input/RemoteCalendarRuleInput.php b/src/Dto/Input/RemoteCalendarRuleInput.php new file mode 100644 index 0000000..c0883c4 --- /dev/null +++ b/src/Dto/Input/RemoteCalendarRuleInput.php @@ -0,0 +1,82 @@ +remoteCalendar = new RemoteCalendarOutput($remoteCalendarRule->getRemoteCalendar()); + $this->busyWeekDays = $remoteCalendarRule->getBusyWeekDays(); + $this->busyFromHour = $remoteCalendarRule->getBusyFromHour(); + $this->busyToHour = $remoteCalendarRule->getBusyToHour(); + $this->isRemoteAvailable = $remoteCalendarRule->isRemoteAvailable(); + $this->availableFromDate = $remoteCalendarRule->getAvailableFromDate(); + $this->availableToDate = $remoteCalendarRule->getAvailableToDate(); + $this->availableReason = $remoteCalendarRule->getAvailableReason(); + } + + /** + * @throws \Exception + */ + public function createOrUpdateEntity(?RemoteCalendarRule $remoteCalendarRule = null): RemoteCalendarRule + { + if (!$remoteCalendarRule) { + $remoteCalendarRule = new RemoteCalendarRule(); + } + + $remoteCalendarRule->setRemoteCalendar($this->remoteCalendar->getEntity()); + $remoteCalendarRule->setBusyWeekDays($this->busyWeekDays); + $remoteCalendarRule->setBusyFromHour($this->busyFromHour); + $remoteCalendarRule->setBusyToHour($this->busyToHour); + $remoteCalendarRule->setRemoteAvailable($this->isRemoteAvailable); + $remoteCalendarRule->setAvailableFromDate($this->availableFromDate); + $remoteCalendarRule->setAvailableToDate($this->availableToDate); + $remoteCalendarRule->setAvailableReason($this->availableReason); + + return $remoteCalendarRule; + } +} \ No newline at end of file diff --git a/src/Dto/Output/RemoteCalendarOutput.php b/src/Dto/Output/RemoteCalendarOutput.php new file mode 100644 index 0000000..807dbc8 --- /dev/null +++ b/src/Dto/Output/RemoteCalendarOutput.php @@ -0,0 +1,32 @@ +name = $remoteCalendar->getName(); + + $this->remoteCalendarRules = $remoteCalendar->getRules()->map( + fn(RemoteCalendarRule $rule) => new RemoteCalendarRuleOutput($rule) + )->toArray(); + } +} \ No newline at end of file diff --git a/src/Dto/Output/RemoteCalendarRuleOutput.php b/src/Dto/Output/RemoteCalendarRuleOutput.php new file mode 100644 index 0000000..9617599 --- /dev/null +++ b/src/Dto/Output/RemoteCalendarRuleOutput.php @@ -0,0 +1,45 @@ +busyWeekDays = $remoteCalendarRule->getBusyWeekDays(); + $this->busyFromHour = $remoteCalendarRule->getBusyFromHour(); + $this->busyToHour = $remoteCalendarRule->getBusyToHour(); + $this->isRemoteAvailable = $remoteCalendarRule->isRemoteAvailable(); + $this->availableFromDate = $remoteCalendarRule->getAvailableFromDate(); + $this->availableToDate = $remoteCalendarRule->getAvailableToDate(); + $this->availableReason = $remoteCalendarRule->getAvailableReason(); + } +} \ No newline at end of file diff --git a/src/Entity/RemoteCalendar.php b/src/Entity/RemoteCalendar.php index d15e499..a0773e5 100644 --- a/src/Entity/RemoteCalendar.php +++ b/src/Entity/RemoteCalendar.php @@ -6,8 +6,11 @@ use App\Repository\RemoteCalendarRepository; use Doctrine\Common\Collections\ArrayCollection; use Doctrine\Common\Collections\Collection; use Doctrine\ORM\Mapping as ORM; +use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity; #[ORM\Entity(repositoryClass: RemoteCalendarRepository::class)] +#[ORM\UniqueConstraint(name: 'UNIQ_IDENTIFIER_NAME', fields: ['name'])] +#[UniqueEntity(fields: ['name'], message: 'This name is already in use.')] class RemoteCalendar extends AbstractEntity { use NameableTrait; @@ -48,6 +51,17 @@ class RemoteCalendar extends AbstractEntity return $this->rules; } + public function setRules(array $rules): static + { + $this->rules->clear(); + + foreach ($rules as $rule){ + $this->addRule($rule); + } + + return $this; + } + public function addRule(RemoteCalendarRule $rule): static { if (!$this->rules->contains($rule)) { diff --git a/src/Entity/RemoteCalendarRule.php b/src/Entity/RemoteCalendarRule.php index 92c4b1b..6b5a05f 100644 --- a/src/Entity/RemoteCalendarRule.php +++ b/src/Entity/RemoteCalendarRule.php @@ -12,25 +12,25 @@ class RemoteCalendarRule extends AbstractEntity #[ORM\ManyToOne(inversedBy: 'rules')] private ?RemoteCalendar $remoteCalendar = null; - #[ORM\Column(type: Types::JSON)] + #[ORM\Column(type: Types::JSON, nullable: true)] private array $busyWeekdays = []; - #[ORM\Column(type: Types::DATETIME_MUTABLE)] + #[ORM\Column(type: Types::TIME_MUTABLE, nullable: true)] private ?\DateTimeInterface $busyFromHour = null; - #[ORM\Column(type: Types::DATETIME_MUTABLE)] + #[ORM\Column(type: Types::TIME_MUTABLE, nullable: true)] private ?\DateTimeInterface $busyToHour = null; #[ORM\Column] private ?bool $isRemoteAvailable = null; - #[ORM\Column(type: Types::DATE_MUTABLE)] + #[ORM\Column(type: Types::DATE_MUTABLE, nullable: true)] private ?\DateTimeInterface $availableFromDate = null; - #[ORM\Column(type: Types::DATE_MUTABLE)] + #[ORM\Column(type: Types::DATE_MUTABLE, nullable: true)] private ?\DateTimeInterface $availableToDate = null; - #[ORM\Column(length: 255)] + #[ORM\Column(length: 255, nullable: true)] private ?string $availableReason = null; public function getBusyWeekdays(): array @@ -38,33 +38,39 @@ class RemoteCalendarRule extends AbstractEntity return $this->busyWeekdays; } - public function setBusyWeekdays(array $busyWeekdays): static + public function setBusyWeekdays(?array $busyWeekdays): static { $this->busyWeekdays = $busyWeekdays; return $this; } - public function getBusyFromHour(): ?\DateTimeInterface + public function getBusyFromHour(): string { - return $this->busyFromHour; + return $this->busyFromHour->format('H:i'); } - public function setBusyFromHour(\DateTimeInterface $busyFromHour): static + /** + * @throws \Exception + */ + public function setBusyFromHour(?string $busyFromHour): static { - $this->busyFromHour = $busyFromHour; + $this->busyFromHour = new \DateTimeImmutable($busyFromHour); return $this; } - public function getBusyToHour(): ?\DateTimeInterface + public function getBusyToHour(): string { - return $this->busyToHour; + return $this->busyToHour->format('H:i'); } - public function setBusyToHour(\DateTimeInterface $busyToHour): static + /** + * @throws \Exception + */ + public function setBusyToHour(?string $busyToHour): static { - $this->busyToHour = $busyToHour; + $this->busyToHour = new \DateTimeImmutable($busyToHour); return $this; } @@ -86,7 +92,7 @@ class RemoteCalendarRule extends AbstractEntity return $this->availableFromDate; } - public function setAvailableFromDate(\DateTimeInterface $availableFromDate): static + public function setAvailableFromDate(?\DateTimeInterface $availableFromDate): static { $this->availableFromDate = $availableFromDate; @@ -98,7 +104,7 @@ class RemoteCalendarRule extends AbstractEntity return $this->availableToDate; } - public function setAvailableToDate(\DateTimeInterface $availableToDate): static + public function setAvailableToDate(?\DateTimeInterface $availableToDate): static { $this->availableToDate = $availableToDate; @@ -110,7 +116,7 @@ class RemoteCalendarRule extends AbstractEntity return $this->availableReason; } - public function setAvailableReason(string $availableReason): static + public function setAvailableReason(?string $availableReason): static { $this->availableReason = $availableReason; diff --git a/src/Repository/RemoteCalendarRepository.php b/src/Repository/RemoteCalendarRepository.php index 331c150..2401d29 100644 --- a/src/Repository/RemoteCalendarRepository.php +++ b/src/Repository/RemoteCalendarRepository.php @@ -9,35 +9,10 @@ use Doctrine\Persistence\ManagerRegistry; /** * @extends ServiceEntityRepository */ -class RemoteCalendarRepository extends ServiceEntityRepository +class RemoteCalendarRepository extends AbstractRepository { public function __construct(ManagerRegistry $registry) { parent::__construct($registry, RemoteCalendar::class); } - -// /** -// * @return RemoteCalendar[] Returns an array of RemoteCalendar objects -// */ -// public function findByExampleField($value): array -// { -// return $this->createQueryBuilder('r') -// ->andWhere('r.exampleField = :val') -// ->setParameter('val', $value) -// ->orderBy('r.id', 'ASC') -// ->setMaxResults(10) -// ->getQuery() -// ->getResult() -// ; -// } - -// public function findOneBySomeField($value): ?RemoteCalendar -// { -// return $this->createQueryBuilder('r') -// ->andWhere('r.exampleField = :val') -// ->setParameter('val', $value) -// ->getQuery() -// ->getOneOrNullResult() -// ; -// } } diff --git a/src/Repository/RemoteCalendarRuleRepository.php b/src/Repository/RemoteCalendarRuleRepository.php index 1af754c..1238954 100644 --- a/src/Repository/RemoteCalendarRuleRepository.php +++ b/src/Repository/RemoteCalendarRuleRepository.php @@ -9,35 +9,10 @@ use Doctrine\Persistence\ManagerRegistry; /** * @extends ServiceEntityRepository */ -class RemoteCalendarRuleRepository extends ServiceEntityRepository +class RemoteCalendarRuleRepository extends AbstractRepository { public function __construct(ManagerRegistry $registry) { parent::__construct($registry, RemoteCalendarRule::class); } - -// /** -// * @return RemoteCalendarRule[] Returns an array of RemoteCalendarRule objects -// */ -// public function findByExampleField($value): array -// { -// return $this->createQueryBuilder('r') -// ->andWhere('r.exampleField = :val') -// ->setParameter('val', $value) -// ->orderBy('r.id', 'ASC') -// ->setMaxResults(10) -// ->getQuery() -// ->getResult() -// ; -// } - -// public function findOneBySomeField($value): ?RemoteCalendarRule -// { -// return $this->createQueryBuilder('r') -// ->andWhere('r.exampleField = :val') -// ->setParameter('val', $value) -// ->getQuery() -// ->getOneOrNullResult() -// ; -// } } diff --git a/src/State/Processor/RemoteCalendarProcessor.php b/src/State/Processor/RemoteCalendarProcessor.php new file mode 100644 index 0000000..f5ab68f --- /dev/null +++ b/src/State/Processor/RemoteCalendarProcessor.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 = []): RemoteCalendarOutput + { + if (!($data instanceof RemoteCalendarInput)) { + throw new \Exception(sprintf('data is not instance of %s', RemoteCalendarInput::class)); + } + + $entity = null; + if (isset($uriVariables['uuid'])) { + $entity = $this->remoteCalendarRepository->findOneByUuid($uriVariables['uuid']); + } + + $partition = $data->createOrUpdateEntity($entity); + $this->validator->validate($partition); + $this->remoteCalendarRepository->save($partition); + + return new RemoteCalendarOutput($partition); + } + + private function processDelete($data, Operation $operation, array $uriVariables = [], array $context = []): null + { + $user = $this->remoteCalendarRepository->findOneByUuid($uriVariables['uuid']); + $this->remoteCalendarRepository->delete($user); + + return null; + } +} diff --git a/src/State/Processor/RemoteCalendarRuleProcessor.php b/src/State/Processor/RemoteCalendarRuleProcessor.php new file mode 100644 index 0000000..631c755 --- /dev/null +++ b/src/State/Processor/RemoteCalendarRuleProcessor.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 = []): RemoteCalendarRuleOutput + { + if (!($data instanceof RemoteCalendarRuleInput)) { + throw new \Exception(sprintf('data is not instance of %s', RemoteCalendarRuleInput::class)); + } + + $entity = null; + if (isset($uriVariables['uuid'])) { + $entity = $this->remoteCalendarRuleRepository->findOneByUuid($uriVariables['uuid']); + } + + $partition = $data->createOrUpdateEntity($entity); + $this->validator->validate($partition); + $this->remoteCalendarRuleRepository->save($partition); + + return new RemoteCalendarRuleOutput($partition); + } + + private function processDelete($data, Operation $operation, array $uriVariables = [], array $context = []): null + { + $user = $this->remoteCalendarRuleRepository->findOneByUuid($uriVariables['uuid']); + $this->remoteCalendarRuleRepository->delete($user); + + return null; + } +} diff --git a/src/State/Provider/RemoteCalendarProvider.php b/src/State/Provider/RemoteCalendarProvider.php new file mode 100644 index 0000000..1cb9c98 --- /dev/null +++ b/src/State/Provider/RemoteCalendarProvider.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 RemoteCalendarOutput($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('RemoteCalendar not found'); + } + + return new RemoteCalendarOutput($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 RemoteCalendarInput($item) : null; + } + + return new RemoteCalendarInput(); + } +} diff --git a/src/State/Provider/RemoteCalendarRuleProvider.php b/src/State/Provider/RemoteCalendarRuleProvider.php new file mode 100644 index 0000000..4417635 --- /dev/null +++ b/src/State/Provider/RemoteCalendarRuleProvider.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 RemoteCalendarRuleOutput($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('RemoteCalendarRule not found'); + } + + return new RemoteCalendarRuleOutput($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 RemoteCalendarRuleInput($item) : null; + } + + return new RemoteCalendarRuleInput(); + } +} -- 2.40.1 From f928a1db11be08bc00470ed954eec6ef37a9f37e Mon Sep 17 00:00:00 2001 From: Manuel Aranda Date: Wed, 25 Sep 2024 17:30:28 +0200 Subject: [PATCH 054/157] refs #725. Added RemoteCalendar and RemoteCalendarRule APIs --- src/Dto/Output/RemoteCalendarOutput.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Dto/Output/RemoteCalendarOutput.php b/src/Dto/Output/RemoteCalendarOutput.php index 807dbc8..97b260c 100644 --- a/src/Dto/Output/RemoteCalendarOutput.php +++ b/src/Dto/Output/RemoteCalendarOutput.php @@ -19,6 +19,9 @@ final class RemoteCalendarOutput extends AbstractOutput #[Groups(['remote-calendar:read'])] public array $remoteCalendarRules = []; + #[Groups(['remote-calendar:read'])] + public ?\DateTimeInterface $createdAt = null; + public function __construct(RemoteCalendar $remoteCalendar) { parent::__construct($remoteCalendar); @@ -28,5 +31,7 @@ final class RemoteCalendarOutput extends AbstractOutput $this->remoteCalendarRules = $remoteCalendar->getRules()->map( fn(RemoteCalendarRule $rule) => new RemoteCalendarRuleOutput($rule) )->toArray(); + + $this->createdAt = $remoteCalendar->getCreatedAt(); } } \ No newline at end of file -- 2.40.1 From fc5fd7421b5523806f06830cd4a51769159f3b5a Mon Sep 17 00:00:00 2001 From: Manuel Aranda Date: Thu, 26 Sep 2024 10:54:31 +0200 Subject: [PATCH 055/157] refs #724. Updated onDelete logic --- migrations/Version20240926085224.php | 35 ++++++++++++++++++++++++++++ src/Entity/RemoteCalendarRule.php | 1 + 2 files changed, 36 insertions(+) create mode 100644 migrations/Version20240926085224.php diff --git a/migrations/Version20240926085224.php b/migrations/Version20240926085224.php new file mode 100644 index 0000000..b05ce37 --- /dev/null +++ b/migrations/Version20240926085224.php @@ -0,0 +1,35 @@ +addSql('ALTER TABLE remote_calendar_rule DROP FOREIGN KEY FK_EE93D058C56641EE'); + $this->addSql('ALTER TABLE remote_calendar_rule CHANGE remote_calendar_id remote_calendar_id INT NOT NULL'); + $this->addSql('ALTER TABLE remote_calendar_rule ADD CONSTRAINT FK_EE93D058C56641EE FOREIGN KEY (remote_calendar_id) REFERENCES remote_calendar (id) ON DELETE CASCADE'); + } + + public function down(Schema $schema): void + { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql('ALTER TABLE remote_calendar_rule DROP FOREIGN KEY FK_EE93D058C56641EE'); + $this->addSql('ALTER TABLE remote_calendar_rule CHANGE remote_calendar_id remote_calendar_id INT DEFAULT NULL'); + $this->addSql('ALTER TABLE remote_calendar_rule ADD CONSTRAINT FK_EE93D058C56641EE FOREIGN KEY (remote_calendar_id) REFERENCES remote_calendar (id)'); + } +} diff --git a/src/Entity/RemoteCalendarRule.php b/src/Entity/RemoteCalendarRule.php index 6b5a05f..419fa56 100644 --- a/src/Entity/RemoteCalendarRule.php +++ b/src/Entity/RemoteCalendarRule.php @@ -10,6 +10,7 @@ use Doctrine\ORM\Mapping as ORM; class RemoteCalendarRule extends AbstractEntity { #[ORM\ManyToOne(inversedBy: 'rules')] + #[ORM\JoinColumn(nullable: false, onDelete: 'CASCADE')] private ?RemoteCalendar $remoteCalendar = null; #[ORM\Column(type: Types::JSON, nullable: true)] -- 2.40.1 From 792eb8f7b95dda186411ac0bbbd191ed4c9c13eb Mon Sep 17 00:00:00 2001 From: Manuel Aranda Date: Thu, 26 Sep 2024 12:00:50 +0200 Subject: [PATCH 056/157] refs #724. Added calendar filters --- config/services/api_platform.yaml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/config/services/api_platform.yaml b/config/services/api_platform.yaml index d8f0fa4..679f66c 100644 --- a/config/services/api_platform.yaml +++ b/config/services/api_platform.yaml @@ -1,4 +1,16 @@ services: + api_platform.filter.calendar.order: + parent: 'api_platform.doctrine.orm.order_filter' + arguments: + $properties: { 'id': ~, 'name': ~,} + $orderParameterName: 'order' + tags: [ 'api_platform.filter' ] + + api_platform.filter.calendar.search: + parent: 'api_platform.doctrine.orm.search_filter' + arguments: [ { 'id': 'exact', 'name': 'exact'} ] + tags: [ 'api_platform.filter' ] + api_platform.filter.client.order: parent: 'api_platform.doctrine.orm.order_filter' arguments: -- 2.40.1 From 38674081d9da0818294e9ba9432b1bbd2167377a Mon Sep 17 00:00:00 2001 From: Manuel Aranda Date: Thu, 26 Sep 2024 12:44:16 +0200 Subject: [PATCH 057/157] refs #723. Fixed bug delete commandGroup --- src/Entity/CommandGroup.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Entity/CommandGroup.php b/src/Entity/CommandGroup.php index 6cf5271..c50bc82 100644 --- a/src/Entity/CommandGroup.php +++ b/src/Entity/CommandGroup.php @@ -25,7 +25,7 @@ class CommandGroup extends AbstractEntity /** * @var Collection */ - #[ORM\ManyToMany(targetEntity: CommandTask::class, mappedBy: 'commandGroup')] + #[ORM\ManyToMany(targetEntity: CommandTask::class, mappedBy: 'commandGroups')] private Collection $commandTasks; public function __construct() -- 2.40.1 From a7c70a287f79e107063418263b3810f5c3859bb5 Mon Sep 17 00:00:00 2001 From: Manuel Aranda Date: Thu, 26 Sep 2024 12:48:50 +0200 Subject: [PATCH 058/157] refs #723. Delete attribute position --- migrations/Version20240926104532.php | 37 +++++++++++++++++++++++++++ src/Dto/Input/CommandGroupInput.php | 10 -------- src/Dto/Output/CommandGroupOutput.php | 4 --- src/Entity/CommandGroup.php | 15 ----------- 4 files changed, 37 insertions(+), 29 deletions(-) create mode 100644 migrations/Version20240926104532.php diff --git a/migrations/Version20240926104532.php b/migrations/Version20240926104532.php new file mode 100644 index 0000000..62d220e --- /dev/null +++ b/migrations/Version20240926104532.php @@ -0,0 +1,37 @@ +addSql('ALTER TABLE organizational_unit DROP FOREIGN KEY FK_749AEB2DC56641EE'); + $this->addSql('ALTER TABLE command_group DROP position'); + $this->addSql('DROP INDEX IDX_749AEB2DC56641EE ON organizational_unit'); + $this->addSql('ALTER TABLE organizational_unit DROP remote_calendar_id'); + } + + public function down(Schema $schema): void + { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql('ALTER TABLE organizational_unit ADD remote_calendar_id INT DEFAULT NULL'); + $this->addSql('ALTER TABLE organizational_unit ADD CONSTRAINT FK_749AEB2DC56641EE FOREIGN KEY (remote_calendar_id) REFERENCES remote_calendar (id)'); + $this->addSql('CREATE INDEX IDX_749AEB2DC56641EE ON organizational_unit (remote_calendar_id)'); + $this->addSql('ALTER TABLE command_group ADD position INT NOT NULL'); + } +} diff --git a/src/Dto/Input/CommandGroupInput.php b/src/Dto/Input/CommandGroupInput.php index 2a3fe64..502467c 100644 --- a/src/Dto/Input/CommandGroupInput.php +++ b/src/Dto/Input/CommandGroupInput.php @@ -30,14 +30,6 @@ final class CommandGroupInput )] public array $commands = []; - #[Assert\NotBlank(message: 'validators.command_group.position.not_blank')] - #[Groups(['command-group:write'])] - #[ApiProperty( - description: 'La posición del grupo de comandos', - example: '1', - )] - public ?int $position = null; - #[Groups(['command-group:write'])] #[ApiProperty( description: '¿Está habilitado el grupo de comandos?', @@ -59,7 +51,6 @@ final class CommandGroupInput } } - $this->position = $commandGroup->getPosition(); $this->enabled = $commandGroup->isEnabled(); } @@ -76,7 +67,6 @@ final class CommandGroupInput } $commandGroup->setCommands( $commandsToAdd ?? [] ); - $commandGroup->setPosition($this->position); return $commandGroup; } diff --git a/src/Dto/Output/CommandGroupOutput.php b/src/Dto/Output/CommandGroupOutput.php index 8e6b645..617bf79 100644 --- a/src/Dto/Output/CommandGroupOutput.php +++ b/src/Dto/Output/CommandGroupOutput.php @@ -16,9 +16,6 @@ final class CommandGroupOutput extends AbstractOutput #[Groups(['command-group:read', 'command-task:read'])] public array $commands = []; - #[Groups(['command-group:read', 'command-task:read'])] - public ?int $position = null; - #[Groups(['command-group:read'])] public \DateTime $createdAt; @@ -35,7 +32,6 @@ final class CommandGroupOutput extends AbstractOutput fn(Command $command) => new CommandOutput($command) )->toArray(); - $this->position = $commandGroup->getPosition(); $this->createdAt = $commandGroup->getCreatedAt(); $this->createdBy = $commandGroup->getCreatedBy(); } diff --git a/src/Entity/CommandGroup.php b/src/Entity/CommandGroup.php index c50bc82..90bec8e 100644 --- a/src/Entity/CommandGroup.php +++ b/src/Entity/CommandGroup.php @@ -19,9 +19,6 @@ class CommandGroup extends AbstractEntity #[ORM\ManyToMany(targetEntity: Command::class, inversedBy: 'commandGroups')] private Collection $commands; - #[ORM\Column] - private ?int $position = null; - /** * @var Collection */ @@ -70,18 +67,6 @@ class CommandGroup extends AbstractEntity return $this; } - public function getPosition(): ?int - { - return $this->position; - } - - public function setPosition(int $position): static - { - $this->position = $position; - - return $this; - } - /** * @return Collection */ -- 2.40.1 From 1750cbd60f27cda1e5764105f41c747757a71b1a Mon Sep 17 00:00:00 2001 From: Manuel Aranda Date: Thu, 26 Sep 2024 16:11:31 +0200 Subject: [PATCH 059/157] refs #758. Testing commandTask and commandGroups --- src/Factory/CommandGroupFactory.php | 56 +++++++++++ src/Factory/CommandTaskFactory.php | 56 +++++++++++ src/Factory/TraceFactory.php | 58 ++++++++++++ tests/Functional/CommandGroupTest.php | 127 +++++++++++++++++++++++++ tests/Functional/CommandTaskTest.php | 129 ++++++++++++++++++++++++++ 5 files changed, 426 insertions(+) create mode 100644 src/Factory/CommandGroupFactory.php create mode 100644 src/Factory/CommandTaskFactory.php create mode 100644 src/Factory/TraceFactory.php create mode 100644 tests/Functional/CommandGroupTest.php create mode 100644 tests/Functional/CommandTaskTest.php diff --git a/src/Factory/CommandGroupFactory.php b/src/Factory/CommandGroupFactory.php new file mode 100644 index 0000000..4608586 --- /dev/null +++ b/src/Factory/CommandGroupFactory.php @@ -0,0 +1,56 @@ + + */ +final class CommandGroupFactory extends ModelFactory +{ + /** + * @see https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#factories-as-services + * + * @todo inject services if required + */ + public function __construct() + { + parent::__construct(); + } + + public static function getClass(): string + { + return CommandGroup::class; + } + + /** + * @see https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#model-factories + * + * @todo add your default values here + */ + protected function getDefaults(): array + { + return [ + 'createdAt' => self::faker()->dateTime(), + 'enabled' => self::faker()->boolean(), + 'name' => self::faker()->text(255), + 'updatedAt' => self::faker()->dateTime() + ]; + } + + /** + * @see https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#initialization + */ + protected function initialize(): self + { + return $this + // ->afterInstantiate(function(CommandGroup $commandGroup): void {}) + ; + } +} diff --git a/src/Factory/CommandTaskFactory.php b/src/Factory/CommandTaskFactory.php new file mode 100644 index 0000000..91d2d09 --- /dev/null +++ b/src/Factory/CommandTaskFactory.php @@ -0,0 +1,56 @@ + + */ +final class CommandTaskFactory extends ModelFactory +{ + /** + * @see https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#factories-as-services + * + * @todo inject services if required + */ + public function __construct() + { + parent::__construct(); + } + + public static function getClass(): string + { + return CommandTask::class; + } + + /** + * @see https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#model-factories + * + * @todo add your default values here + */ + protected function getDefaults(): array + { + return [ + 'createdAt' => self::faker()->dateTime(), + 'datetime' => self::faker()->dateTime(), + 'status' => self::faker()->text(255), + 'updatedAt' => self::faker()->dateTime() + ]; + } + + /** + * @see https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#initialization + */ + protected function initialize(): self + { + return $this + // ->afterInstantiate(function(CommandTask $commandTask): void {}) + ; + } +} diff --git a/src/Factory/TraceFactory.php b/src/Factory/TraceFactory.php new file mode 100644 index 0000000..14aec4b --- /dev/null +++ b/src/Factory/TraceFactory.php @@ -0,0 +1,58 @@ + + */ +final class TraceFactory extends ModelFactory +{ + /** + * @see https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#factories-as-services + * + * @todo inject services if required + */ + public function __construct() + { + parent::__construct(); + } + + public static function getClass(): string + { + return Trace::class; + } + + /** + * @see https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#model-factories + * + * @todo add your default values here + */ + protected function getDefaults(): array + { + return [ + 'client' => ClientFactory::new(), + 'command' => CommandFactory::new(), + 'createdAt' => self::faker()->dateTime(), + 'executedAt' => self::faker()->dateTime(), + 'status' => self::faker()->text(255), + 'updatedAt' => self::faker()->dateTime() + ]; + } + + /** + * @see https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#initialization + */ + protected function initialize(): static + { + return $this + // ->afterInstantiate(function(Trace $trace): void {}) + ; + } +} diff --git a/tests/Functional/CommandGroupTest.php b/tests/Functional/CommandGroupTest.php new file mode 100644 index 0000000..de5faef --- /dev/null +++ b/tests/Functional/CommandGroupTest.php @@ -0,0 +1,127 @@ + self::USER_ADMIN, 'roles'=> [UserGroupPermissions::ROLE_SUPER_ADMIN]]); + + CommandGroupFactory::createMany(10); + + $this->createClientWithCredentials()->request('GET', '/command-groups'); + $this->assertResponseStatusCodeSame(Response::HTTP_OK); + $this->assertResponseHeaderSame('content-type', 'application/ld+json; charset=utf-8'); + $this->assertJsonContains([ + '@context' => '/contexts/CommandGroup', + '@id' => '/command-groups', + '@type' => 'hydra:Collection', + 'hydra:totalItems' => 10, + ]); + } + + /** + * @throws RedirectionExceptionInterface + * @throws DecodingExceptionInterface + * @throws ClientExceptionInterface + * @throws TransportExceptionInterface + * @throws ServerExceptionInterface + */ + public function testCreateCommandGroup(): void + { + UserFactory::createOne(['username' => self::USER_ADMIN, 'roles'=> [UserGroupPermissions::ROLE_SUPER_ADMIN]]); + + CommandFactory::createOne(['name' => self::CMD_CREATE]); + $commandIri = $this->findIriBy(Command::class, ['name' => self::CMD_CREATE]); + + $this->createClientWithCredentials()->request('POST', '/command-groups',['json' => [ + 'name' => self::CMD_GROUP_CREATE, + 'commands' => [ + $commandIri + ] + ]]); + + $this->assertResponseStatusCodeSame(201); + $this->assertResponseHeaderSame('content-type', 'application/ld+json; charset=utf-8'); + $this->assertJsonContains([ + '@context' => '/contexts/CommandGroupOutput', + '@type' => 'CommandGroup', + 'name' => self::CMD_GROUP_CREATE, + ]); + } + + /** + * @throws RedirectionExceptionInterface + * @throws DecodingExceptionInterface + * @throws ClientExceptionInterface + * @throws TransportExceptionInterface + * @throws ServerExceptionInterface + */ + public function testUpdateCommandGroup(): void + { + UserFactory::createOne(['username' => self::USER_ADMIN, 'roles'=> [UserGroupPermissions::ROLE_SUPER_ADMIN]]); + + CommandGroupFactory::createOne(['name' => self::CMD_GROUP_CREATE]); + $iri = $this->findIriBy(CommandGroup::class, ['name' => self::CMD_GROUP_CREATE]); + + $this->createClientWithCredentials()->request('PUT', $iri, ['json' => [ + 'name' => self::CMD_GROUP_UPDATE, + ]]); + + $this->assertResponseIsSuccessful(); + $this->assertJsonContains([ + '@id' => $iri, + 'name' => self::CMD_GROUP_UPDATE + ]); + } + + /** + * @throws TransportExceptionInterface + * @throws ServerExceptionInterface + * @throws RedirectionExceptionInterface + * @throws DecodingExceptionInterface + * @throws ClientExceptionInterface + */ + public function testDeleteCommandGroup(): void + { + UserFactory::createOne(['username' => self::USER_ADMIN, 'roles'=> [UserGroupPermissions::ROLE_SUPER_ADMIN]]); + + CommandGroupFactory::createOne(['name' => self::CMD_GROUP_DELETE]); + $iri = $this->findIriBy(CommandGroup::class, ['name' => self::CMD_GROUP_DELETE]); + + $this->createClientWithCredentials()->request('DELETE', $iri); + $this->assertResponseStatusCodeSame(204); + $this->assertNull( + static::getContainer()->get('doctrine')->getRepository(CommandGroup::class)->findOneBy(['name' => self::CMD_GROUP_DELETE]) + ); + } +} \ No newline at end of file diff --git a/tests/Functional/CommandTaskTest.php b/tests/Functional/CommandTaskTest.php new file mode 100644 index 0000000..49b9555 --- /dev/null +++ b/tests/Functional/CommandTaskTest.php @@ -0,0 +1,129 @@ + self::USER_ADMIN, 'roles'=> [UserGroupPermissions::ROLE_SUPER_ADMIN]]); + + CommandTaskFactory::createMany(10); + + $this->createClientWithCredentials()->request('GET', '/command-tasks'); + $this->assertResponseStatusCodeSame(Response::HTTP_OK); + $this->assertResponseHeaderSame('content-type', 'application/ld+json; charset=utf-8'); + $this->assertJsonContains([ + '@context' => '/contexts/CommandTask', + '@id' => '/command-tasks', + '@type' => 'hydra:Collection', + 'hydra:totalItems' => 10, + ]); + } + + /** + * @throws RedirectionExceptionInterface + * @throws DecodingExceptionInterface + * @throws ClientExceptionInterface + * @throws TransportExceptionInterface + * @throws ServerExceptionInterface + */ + public function testCreateCommandTask(): void + { + UserFactory::createOne(['username' => self::USER_ADMIN, 'roles'=> [UserGroupPermissions::ROLE_SUPER_ADMIN]]); + + CommandFactory::createOne(['name' => self::CMD_CREATE]); + $commandIri = $this->findIriBy(Command::class, ['name' => self::CMD_CREATE]); + + $date = new \DateTimeImmutable(); + + $this->createClientWithCredentials()->request('POST', '/command-tasks',['json' => [ + 'dateTime' => $date->format('Y-m-d H:i:s'), + 'notes' => self::CMD_TASK_CREATE, + 'commands' => [ + $commandIri + ] + ]]); + + $this->assertResponseStatusCodeSame(201); + $this->assertResponseHeaderSame('content-type', 'application/ld+json; charset=utf-8'); + $this->assertJsonContains([ + '@context' => '/contexts/CommandTaskOutput', + '@type' => 'CommandTask', + 'notes' => self::CMD_TASK_CREATE, + ]); + } + + /** + * @throws RedirectionExceptionInterface + * @throws DecodingExceptionInterface + * @throws ClientExceptionInterface + * @throws TransportExceptionInterface + * @throws ServerExceptionInterface + */ + public function testUpdateCommandTask(): void + { + UserFactory::createOne(['username' => self::USER_ADMIN, 'roles'=> [UserGroupPermissions::ROLE_SUPER_ADMIN]]); + + CommandTaskFactory::createOne(['notes' => self::CMD_TASK_CREATE]); + $iri = $this->findIriBy(CommandTask::class, ['notes' => self::CMD_TASK_CREATE]); + + $this->createClientWithCredentials()->request('PUT', $iri, ['json' => [ + 'notes' => self::CMD_TASK_UPDATE, + ]]); + + $this->assertResponseIsSuccessful(); + $this->assertJsonContains([ + '@id' => $iri, + 'notes' => self::CMD_TASK_UPDATE + ]); + } + + /** + * @throws TransportExceptionInterface + * @throws ServerExceptionInterface + * @throws RedirectionExceptionInterface + * @throws DecodingExceptionInterface + * @throws ClientExceptionInterface + */ + public function testDeleteCommandTask(): void + { + UserFactory::createOne(['username' => self::USER_ADMIN, 'roles'=> [UserGroupPermissions::ROLE_SUPER_ADMIN]]); + + CommandTaskFactory::createOne(['notes' => self::CMD_TASK_DELETE]); + $iri = $this->findIriBy(CommandTask::class, ['notes' => self::CMD_TASK_DELETE]); + + $this->createClientWithCredentials()->request('DELETE', $iri); + $this->assertResponseStatusCodeSame(204); + $this->assertNull( + static::getContainer()->get('doctrine')->getRepository(CommandTask::class)->findOneBy(['notes' => self::CMD_TASK_DELETE]) + ); + } +} \ No newline at end of file -- 2.40.1 From b0189d8d9ea6139d1aa8debea17fac4eb130dcdb Mon Sep 17 00:00:00 2001 From: Manuel Aranda Date: Fri, 27 Sep 2024 10:51:53 +0200 Subject: [PATCH 060/157] refs #758. Testing commandTask and commandGroups --- migrations/Version20240926104532.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/migrations/Version20240926104532.php b/migrations/Version20240926104532.php index 62d220e..3c743da 100644 --- a/migrations/Version20240926104532.php +++ b/migrations/Version20240926104532.php @@ -20,9 +20,7 @@ final class Version20240926104532 extends AbstractMigration public function up(Schema $schema): void { // this up() migration is auto-generated, please modify it to your needs - $this->addSql('ALTER TABLE organizational_unit DROP FOREIGN KEY FK_749AEB2DC56641EE'); $this->addSql('ALTER TABLE command_group DROP position'); - $this->addSql('DROP INDEX IDX_749AEB2DC56641EE ON organizational_unit'); $this->addSql('ALTER TABLE organizational_unit DROP remote_calendar_id'); } -- 2.40.1 From ecd559391e566e3fe6e0754bb4211c209c4ccc38 Mon Sep 17 00:00:00 2001 From: Manuel Aranda Date: Fri, 27 Sep 2024 10:58:57 +0200 Subject: [PATCH 061/157] refs #758. Testing commandTask and commandGroups --- migrations/Version20240926104532.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/migrations/Version20240926104532.php b/migrations/Version20240926104532.php index 3c743da..c7d34fc 100644 --- a/migrations/Version20240926104532.php +++ b/migrations/Version20240926104532.php @@ -20,14 +20,14 @@ final class Version20240926104532 extends AbstractMigration public function up(Schema $schema): void { // this up() migration is auto-generated, please modify it to your needs + $this->addSql('ALTER TABLE organizational_unit DROP FOREIGN KEY FK_749AEB2DC56641EE'); $this->addSql('ALTER TABLE command_group DROP position'); - $this->addSql('ALTER TABLE organizational_unit DROP remote_calendar_id'); + $this->addSql('DROP INDEX IDX_749AEB2DC56641EE ON organizational_unit'); } public function down(Schema $schema): void { // this down() migration is auto-generated, please modify it to your needs - $this->addSql('ALTER TABLE organizational_unit ADD remote_calendar_id INT DEFAULT NULL'); $this->addSql('ALTER TABLE organizational_unit ADD CONSTRAINT FK_749AEB2DC56641EE FOREIGN KEY (remote_calendar_id) REFERENCES remote_calendar (id)'); $this->addSql('CREATE INDEX IDX_749AEB2DC56641EE ON organizational_unit (remote_calendar_id)'); $this->addSql('ALTER TABLE command_group ADD position INT NOT NULL'); -- 2.40.1 From 04cee6c4c041ae412dfd7001b9b5358f7533b5f3 Mon Sep 17 00:00:00 2001 From: Manuel Aranda Date: Fri, 27 Sep 2024 11:01:20 +0200 Subject: [PATCH 062/157] refs #758. Testing commandTask and commandGroups --- migrations/Version20240926104532.php | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/migrations/Version20240926104532.php b/migrations/Version20240926104532.php index c7d34fc..125bc93 100644 --- a/migrations/Version20240926104532.php +++ b/migrations/Version20240926104532.php @@ -20,16 +20,13 @@ final class Version20240926104532 extends AbstractMigration public function up(Schema $schema): void { // this up() migration is auto-generated, please modify it to your needs - $this->addSql('ALTER TABLE organizational_unit DROP FOREIGN KEY FK_749AEB2DC56641EE'); $this->addSql('ALTER TABLE command_group DROP position'); - $this->addSql('DROP INDEX IDX_749AEB2DC56641EE ON organizational_unit'); } public function down(Schema $schema): void { // this down() migration is auto-generated, please modify it to your needs - $this->addSql('ALTER TABLE organizational_unit ADD CONSTRAINT FK_749AEB2DC56641EE FOREIGN KEY (remote_calendar_id) REFERENCES remote_calendar (id)'); - $this->addSql('CREATE INDEX IDX_749AEB2DC56641EE ON organizational_unit (remote_calendar_id)'); + $this->addSql('ALTER TABLE organizational_unit ADD FK_749AEB2DC56641EE FOREIGN KEY (remote_calendar_id) REFERENCES remote_calendar (id)'); $this->addSql('ALTER TABLE command_group ADD position INT NOT NULL'); } } -- 2.40.1 From 4ff77d7e2b950a9febc656cff4c145a4b24b0f94 Mon Sep 17 00:00:00 2001 From: Manuel Aranda Date: Fri, 27 Sep 2024 12:43:38 +0200 Subject: [PATCH 063/157] refs #723. Updated CommandTask. Added clients --- config/services/api_platform.yaml | 2 +- src/Dto/Input/CommandTaskInput.php | 24 ++++++++++++++++ src/Dto/Output/CommandTaskOutput.php | 8 ++++++ src/Entity/CommandTask.php | 42 ++++++++++++++++++++++++++++ 4 files changed, 75 insertions(+), 1 deletion(-) diff --git a/config/services/api_platform.yaml b/config/services/api_platform.yaml index 466d458..4441b4e 100644 --- a/config/services/api_platform.yaml +++ b/config/services/api_platform.yaml @@ -8,7 +8,7 @@ services: api_platform.filter.client.search: parent: 'api_platform.doctrine.orm.search_filter' - arguments: [ { 'id': 'exact', 'name': 'partial', 'serialNumber': 'exact', organizationalUnit.id: 'exact' } ] + arguments: [ { 'id': 'exact', 'name': 'partial', 'serialNumber': 'exact', organizationalUnit.id: 'exact', mac: 'exact', ip: 'exact' } ] tags: [ 'api_platform.filter' ] api_platform.filter.command.order: diff --git a/src/Dto/Input/CommandTaskInput.php b/src/Dto/Input/CommandTaskInput.php index dd6b89a..c7e08e7 100644 --- a/src/Dto/Input/CommandTaskInput.php +++ b/src/Dto/Input/CommandTaskInput.php @@ -3,6 +3,7 @@ namespace App\Dto\Input; use ApiPlatform\Metadata\ApiProperty; +use App\Dto\Output\ClientOutput; use App\Dto\Output\CommandGroupOutput; use App\Dto\Output\CommandOutput; use App\Entity\CommandTask; @@ -33,6 +34,16 @@ final class CommandTaskInput )] public array $commandGroups = []; + /** + * @var ClientOutput[] + */ + #[Groups(['command-task:write'])] + #[ApiProperty( + description: 'Los clientes de la tarea', + example: 'Clientes de la tarea', + )] + public array $clients = []; + #[Assert\NotBlank(message: 'validators.command_task.datetime.not_blank')] #[Groups(['command-task:write'])] #[ApiProperty( @@ -66,6 +77,12 @@ final class CommandTaskInput } } + if ($commandTask->getClients()) { + foreach ($commandTask->getClients() as $client) { + $this->clients[] = new ClientOutput($client); + } + } + $this->dateTime = $commandTask->getDatetime(); $this->notes = $commandTask->getNotes(); } @@ -87,6 +104,13 @@ final class CommandTaskInput } $commandTask->setCommandGroups( $commandGroupsToAdd ?? [] ); + + foreach ($this->clients as $client) { + $clientsToAdd[] = $client->getEntity(); + } + + $commandTask->setClients( $clientsToAdd ?? [] ); + $commandTask->setDatetime($this->dateTime); $commandTask->setStatus(CommandTaskStatus::PENDING); $commandTask->setNotes($this->notes); diff --git a/src/Dto/Output/CommandTaskOutput.php b/src/Dto/Output/CommandTaskOutput.php index c5852fe..76c519c 100644 --- a/src/Dto/Output/CommandTaskOutput.php +++ b/src/Dto/Output/CommandTaskOutput.php @@ -3,6 +3,7 @@ namespace App\Dto\Output; use ApiPlatform\Metadata\Get; +use App\Entity\Client; use App\Entity\Command; use App\Entity\CommandGroup; use App\Entity\CommandTask; @@ -17,6 +18,9 @@ final class CommandTaskOutput extends AbstractOutput #[Groups(['command-task:read'])] public array $commandGroups = []; + #[Groups(['command-task:read'])] + public array $clients = []; + #[Groups(['command-task:read'])] public \DateTimeInterface $dateTime; @@ -45,6 +49,10 @@ final class CommandTaskOutput extends AbstractOutput fn(CommandGroup $commandGroup) => new CommandGroupOutput($commandGroup) )->toArray(); + $this->clients = $commandTask->getClients()->map( + fn(Client $client) => new ClientOutput($client) + )->toArray(); + $this->dateTime = $commandTask->getDateTime(); $this->notes = $commandTask->getNotes(); $this->status = $commandTask->getStatus(); diff --git a/src/Entity/CommandTask.php b/src/Entity/CommandTask.php index 8335067..b742212 100644 --- a/src/Entity/CommandTask.php +++ b/src/Entity/CommandTask.php @@ -32,12 +32,19 @@ class CommandTask extends AbstractEntity #[ORM\Column(length: 255)] private ?string $status = null; + /** + * @var Collection + */ + #[ORM\ManyToMany(targetEntity: Client::class)] + private Collection $clients; + public function __construct() { parent::__construct(); $this->commands = new ArrayCollection(); $this->commandGroups = new ArrayCollection(); + $this->clients = new ArrayCollection(); } /** @@ -145,4 +152,39 @@ class CommandTask extends AbstractEntity return $this; } + + /** + * @return Collection + */ + public function getClients(): Collection + { + return $this->clients; + } + + public function addClient(Client $client): static + { + if (!$this->clients->contains($client)) { + $this->clients->add($client); + } + + return $this; + } + + public function removeClient(Client $client): static + { + $this->clients->removeElement($client); + + return $this; + } + + public function setClients(array $clients): static + { + $this->clients->clear(); + + foreach ($clients as $client){ + $this->addClient($client); + } + + return $this; + } } -- 2.40.1 From f6649231b65adfd15dded6db0fb47bdb34ec8022 Mon Sep 17 00:00:00 2001 From: Manuel Aranda Date: Mon, 30 Sep 2024 11:11:20 +0200 Subject: [PATCH 064/157] refs #726. Temporal commit. Added UDS Service logic --- .env | 7 +++ config/services.yaml | 6 +++ src/Service/UDS/UDSClient.php | 88 +++++++++++++++++++++++++++++++++++ 3 files changed, 101 insertions(+) create mode 100644 src/Service/UDS/UDSClient.php diff --git a/.env b/.env index 2d1c753..bc50ff4 100644 --- a/.env +++ b/.env @@ -40,3 +40,10 @@ JWT_SECRET_KEY=%kernel.project_dir%/config/jwt/private.pem JWT_PUBLIC_KEY=%kernel.project_dir%/config/jwt/public.pem JWT_PASSPHRASE=8b9154df37ffa91ef9186ce095324e39e50ff3b023bb1ed34383abd019ba4515 ###< lexik/jwt-authentication-bundle ### + +###> UDS ### +UDS_AUTH_LOGIN="Usuarios locales" +UDS_AUTH_USERNAME="natiqindel" +UDS_AUTH_PASSWORD="correct horse battery staple" +UDS_URL=https://localhost:8087/uds/rest/ +###< UDS ### \ No newline at end of file diff --git a/config/services.yaml b/config/services.yaml index 52a86a4..31634f6 100644 --- a/config/services.yaml +++ b/config/services.yaml @@ -9,6 +9,12 @@ services: autowire: true # Automatically injects dependencies in your services. autoconfigure: true # Automatically registers your services as commands, event subscribers, etc. + bind: + $udsAPIurl: '%env(UDS_URL)%' + $udsAuthLogin: '%env(UDS_AUTH_LOGIN)%' + $udsAuthUsername: '%env(UDS_AUTH_USERNAME)%' + $udsAuthPassword: '%env(UDS_AUTH_PASSWORD)%' + App\: resource: '../src/' exclude: diff --git a/src/Service/UDS/UDSClient.php b/src/Service/UDS/UDSClient.php new file mode 100644 index 0000000..fa7fde2 --- /dev/null +++ b/src/Service/UDS/UDSClient.php @@ -0,0 +1,88 @@ +login(); + } + + /** + * @throws TransportExceptionInterface + */ + public function login(): void + { + $response = $this->httpClient->request('POST', $this->udsAPIurl . '/auth/login', [ + 'json' => [ + 'login' => $this->udsAuthLogin, + 'username' => $this->udsAuthUsername, + 'password' => $this->udsAuthPassword + ] + ]); + + $data = json_decode($response->getContent(), true); + $this->token = $data['token']; + $this->scrambler = $data['scrambler']; + } + + /** + * @throws TransportExceptionInterface + */ + public function getServicePools(): array + { + $response = $this->httpClient->request('GET', $this->udsAPIurl . '/servicespools/overview', [ + 'headers' => [ + 'X-Auth-Token' => $this->token, + 'Content-Type' => 'application/json', + 'Scrambler' => $this->scrambler + ] + ]); + } + + public function getServicePool(string $providerId, string $serviceId): array + { + $response = $this->httpClient->request('GET', $this->udsAPIurl . '/providers/' . $providerId .'/services/'. $serviceId, [ + 'headers' => [ + 'X-Auth-Token' => $this->token, + 'Content-Type' => 'application/json', + 'Scrambler' => $this->scrambler + ] + ]); + } + + public function setServicePool(string $serviceId, array $payload): void + { + $response = $this->httpClient->request('PUT', $this->udsAPIurl . '/servicespools/' . $serviceId, [ + 'headers' => [ + 'X-Auth-Token' => $this->token, + 'Content-Type' => 'application/json', + 'Scrambler' => $this->scrambler + ], + 'json' => $payload + ]); + } +} \ No newline at end of file -- 2.40.1 From 4f1bfa04f63732e89c41159c6419942d19f5d60f Mon Sep 17 00:00:00 2001 From: Manuel Aranda Date: Mon, 30 Sep 2024 11:37:36 +0200 Subject: [PATCH 065/157] Fixed conflicts and solved DTO bugs --- config/services/api_platform.yaml | 4 ---- src/Dto/Output/ClientOutput.php | 15 ++------------- src/Entity/Client.php | 11 +++++------ src/Factory/OgLiveFactory.php | 2 ++ tests/Functional/OgLiveTest.php | 6 ++++-- 5 files changed, 13 insertions(+), 25 deletions(-) diff --git a/config/services/api_platform.yaml b/config/services/api_platform.yaml index 91d360a..c7f7ea4 100644 --- a/config/services/api_platform.yaml +++ b/config/services/api_platform.yaml @@ -8,7 +8,6 @@ services: api_platform.filter.client.search: parent: 'api_platform.doctrine.orm.search_filter' -<<<<<<< HEAD arguments: [ { 'id': 'exact', 'name': 'partial', 'serialNumber': 'exact', organizationalUnit.id: 'exact', mac: 'exact', ip: 'exact' } ] tags: [ 'api_platform.filter' ] @@ -27,9 +26,6 @@ services: api_platform.filter.command.boolean: parent: 'api_platform.doctrine.orm.boolean_filter' arguments: [ { 'enabled': ~ } ] -======= - arguments: [ { 'id': 'exact', 'name': 'partial', 'serialNumber': 'exact', organizationalUnit.id: 'exact', 'ip': exact, 'mac': exact } ] ->>>>>>> feature/integration-ogboot tags: [ 'api_platform.filter' ] api_platform.filter.hardware.order: diff --git a/src/Dto/Output/ClientOutput.php b/src/Dto/Output/ClientOutput.php index 2579c80..f1a33dd 100644 --- a/src/Dto/Output/ClientOutput.php +++ b/src/Dto/Output/ClientOutput.php @@ -13,27 +13,16 @@ final class ClientOutput extends AbstractOutput { CONST string TYPE = 'client'; -<<<<<<< HEAD - #[Groups(['client:read', 'organizational-unit:read', 'trace:read'])] -======= - #[Groups(['client:read', 'organizational-unit:read', 'pxe-boot-file:read'])] ->>>>>>> feature/integration-ogboot + #[Groups(['client:read', 'organizational-unit:read', 'pxe-boot-file:read', 'trace:read'])] public string $name; #[Groups(['client:read', 'organizational-unit:read'])] public string $type = self::TYPE; -<<<<<<< HEAD - #[Groups(['client:read', 'organizational-unit:read', 'trace:read'])] - public ?string $ip = ''; - - #[Groups(['client:read', 'organizational-unit:read', 'trace:read'])] -======= - #[Groups(['client:read', 'organizational-unit:read', 'pxe-boot-file:read'])] + #[Groups(['client:read', 'organizational-unit:read', 'pxe-boot-file:read', 'trace:read'])] public ?string $ip = ''; #[Groups(['client:read', 'organizational-unit:read', 'pxe-boot-file:read'])] ->>>>>>> feature/integration-ogboot public ?string $mac = ''; #[Groups(['client:read', 'organizational-unit:read', 'trace:read'])] diff --git a/src/Entity/Client.php b/src/Entity/Client.php index e51c56f..0c7a20f 100644 --- a/src/Entity/Client.php +++ b/src/Entity/Client.php @@ -64,11 +64,9 @@ class Client extends AbstractEntity private ?OgRepository $repository = null; #[ORM\ManyToOne(inversedBy: 'clients')] -<<<<<<< HEAD private ?Subnet $subnet = null; -======= + #[ORM\ManyToOne(inversedBy: 'clients')] private ?OgLive $ogLive = null; ->>>>>>> feature/integration-ogboot public function __construct() { @@ -262,7 +260,6 @@ class Client extends AbstractEntity return $this; } -<<<<<<< HEAD public function getSubnet(): ?Subnet { return $this->subnet; @@ -271,7 +268,10 @@ class Client extends AbstractEntity public function setSubnet(?Subnet $subnet): static { $this->subnet = $subnet; -======= + + return $this; + } + public function getOgLive(): ?OgLive { return $this->ogLive; @@ -280,7 +280,6 @@ class Client extends AbstractEntity public function setOgLive(?OgLive $ogLive): static { $this->ogLive = $ogLive; ->>>>>>> feature/integration-ogboot return $this; } diff --git a/src/Factory/OgLiveFactory.php b/src/Factory/OgLiveFactory.php index cb90dcd..bfe05a8 100644 --- a/src/Factory/OgLiveFactory.php +++ b/src/Factory/OgLiveFactory.php @@ -3,6 +3,7 @@ namespace App\Factory; use App\Entity\OgLive; +use App\Model\OgLiveStatus; use App\Repository\OgLiveRepository; use Zenstruck\Foundry\ModelFactory; use Zenstruck\Foundry\Persistence\PersistentProxyObjectFactory; @@ -35,6 +36,7 @@ final class OgLiveFactory extends ModelFactory 'createdAt' => self::faker()->dateTime(), 'name' => self::faker()->text(255), 'downloadUrl' => self::faker()->text(255), + 'status' => OgLiveStatus::ACTIVE, 'updatedAt' => self::faker()->dateTime(), ]; } diff --git a/tests/Functional/OgLiveTest.php b/tests/Functional/OgLiveTest.php index bb6c58a..95b2490 100644 --- a/tests/Functional/OgLiveTest.php +++ b/tests/Functional/OgLiveTest.php @@ -9,6 +9,7 @@ use App\Entity\User; use App\Factory\HardwareProfileFactory; use App\Factory\OgLiveFactory; use App\Factory\UserFactory; +use App\Model\OgLiveStatus; use App\Model\UserGroupPermissions; use Symfony\Component\HttpFoundation\Response; use Symfony\Contracts\HttpClient\Exception\ClientExceptionInterface; @@ -61,7 +62,7 @@ class OgLiveTest extends AbstractTest $this->createClientWithCredentials()->request('POST', '/og-lives',['json' => [ 'name' => self::OGLIVE_CREATE, - 'downloadUrl' => 'http://example.com' + 'downloadUrl' => 'http://example.com', ]]); $this->assertResponseStatusCodeSame(201); @@ -70,7 +71,8 @@ class OgLiveTest extends AbstractTest '@context' => '/contexts/OgLiveOutput', '@type' => 'OgLive', 'name' => self::OGLIVE_CREATE, - 'downloadUrl' => 'http://example.com' + 'downloadUrl' => 'http://example.com', + 'status' => OgLiveStatus::INACTIVE ]); } -- 2.40.1 From d75712428d933ce4119805b42b017145b450ae2d Mon Sep 17 00:00:00 2001 From: Manuel Aranda Date: Mon, 30 Sep 2024 11:45:25 +0200 Subject: [PATCH 066/157] refs #725. Added RemoteCalendar into OrganizationalUnit --- src/Dto/Input/OrganizationalUnitInput.php | 12 ++++++++++++ src/Dto/Output/OrganizationalUnitOutput.php | 7 ++++++- src/Entity/OrganizationalUnit.php | 12 ++++++++++++ 3 files changed, 30 insertions(+), 1 deletion(-) diff --git a/src/Dto/Input/OrganizationalUnitInput.php b/src/Dto/Input/OrganizationalUnitInput.php index 1b83437..aa641f9 100644 --- a/src/Dto/Input/OrganizationalUnitInput.php +++ b/src/Dto/Input/OrganizationalUnitInput.php @@ -4,6 +4,7 @@ namespace App\Dto\Input; use ApiPlatform\Metadata\ApiProperty; use App\Dto\Output\OrganizationalUnitOutput; +use App\Dto\Output\RemoteCalendarOutput; use App\Entity\OrganizationalUnit; use App\Validator\Constraints\OrganizationalUnitMulticastMode; use App\Validator\Constraints\OrganizationalUnitMulticastPort; @@ -50,6 +51,9 @@ class OrganizationalUnitInput #[Assert\Valid()] public ?NetworkSettingsInput $networkSettings = null; + #[Groups(['organizational-unit:write'])] + public ?RemoteCalendarOutput $remoteCalendar = null; + public function __construct(?OrganizationalUnit $organizationalUnit = null) { if (!$organizationalUnit) { @@ -70,6 +74,10 @@ class OrganizationalUnitInput if ($organizationalUnit->getNetworkSettings()){ $this->networkSettings = new NetworkSettingsInput($organizationalUnit->getNetworkSettings()); } + + if ($organizationalUnit->getRemoteCalendar()) { + $this->remoteCalendar = new RemoteCalendarOutput($organizationalUnit->getRemoteCalendar()); + } } public function createOrUpdateEntity(?OrganizationalUnit $organizationalUnit = null): OrganizationalUnit @@ -94,6 +102,10 @@ class OrganizationalUnitInput $organizationalUnit->setNetworkSettings($this->networkSettings->createOrUpdateEntity($organizationalUnit->getNetworkSettings())); } + if ($this->remoteCalendar) { + $organizationalUnit->setRemoteCalendar($this->remoteCalendar->getEntity()); + } + return $organizationalUnit; } } \ No newline at end of file diff --git a/src/Dto/Output/OrganizationalUnitOutput.php b/src/Dto/Output/OrganizationalUnitOutput.php index a51a5c0..ab850e0 100644 --- a/src/Dto/Output/OrganizationalUnitOutput.php +++ b/src/Dto/Output/OrganizationalUnitOutput.php @@ -51,6 +51,10 @@ final class OrganizationalUnitOutput extends AbstractOutput #[Groups(['organizational-unit:read'])] public array $clients = []; + #[Groups(['organizational-unit:read', "client:read"])] + #[ApiProperty(readableLink: true)] + public ?RemoteCalendarOutput $remoteCalendar = null; + #[Groups(['organizational-unit:read'])] public \DateTime $createdAt; @@ -70,7 +74,7 @@ final class OrganizationalUnitOutput extends AbstractOutput $this->capacity = $organizationalUnit->getCapacity(); $this->type = $organizationalUnit->getType(); $this->networkSettings = $organizationalUnit->getNetworkSettings() ? new NetworkSettingsOutput($organizationalUnit->getNetworkSettings()) : null; - + $this->remoteCalendar = $organizationalUnit->getRemoteCalendar() ? new RemoteCalendarOutput($organizationalUnit->getRemoteCalendar()) : null; if ($organizationalUnit->getParent()) { $this->parent = new self($organizationalUnit->getParent()); } @@ -87,6 +91,7 @@ final class OrganizationalUnitOutput extends AbstractOutput )->toArray(); } + $this->path = $organizationalUnit->getPath(); $this->createdAt = $organizationalUnit->getCreatedAt(); $this->createdBy = $organizationalUnit->getCreatedBy(); diff --git a/src/Entity/OrganizationalUnit.php b/src/Entity/OrganizationalUnit.php index 11cfa89..7bc4c0c 100644 --- a/src/Entity/OrganizationalUnit.php +++ b/src/Entity/OrganizationalUnit.php @@ -372,4 +372,16 @@ class OrganizationalUnit extends AbstractEntity return $this; } + + public function getRemoteCalendar(): ?RemoteCalendar + { + return $this->remoteCalendar; + } + + public function setRemoteCalendar(?RemoteCalendar $remoteCalendar): static + { + $this->remoteCalendar = $remoteCalendar; + + return $this; + } } -- 2.40.1 From 4b3f55f631135f5aba404d1a49c3e895095d59fb Mon Sep 17 00:00:00 2001 From: Manuel Aranda Date: Mon, 30 Sep 2024 15:10:48 +0200 Subject: [PATCH 067/157] Added new migration --- migrations/Version20240930131003.php | 45 ++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 migrations/Version20240930131003.php diff --git a/migrations/Version20240930131003.php b/migrations/Version20240930131003.php new file mode 100644 index 0000000..68b2308 --- /dev/null +++ b/migrations/Version20240930131003.php @@ -0,0 +1,45 @@ +addSql('CREATE TABLE command_task_client (command_task_id INT NOT NULL, client_id INT NOT NULL, INDEX IDX_F97A827D62DC5265 (command_task_id), INDEX IDX_F97A827D19EB6921 (client_id), PRIMARY KEY(command_task_id, client_id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB'); + $this->addSql('ALTER TABLE command_task_client ADD CONSTRAINT FK_F97A827D62DC5265 FOREIGN KEY (command_task_id) REFERENCES command_task (id) ON DELETE CASCADE'); + $this->addSql('ALTER TABLE command_task_client ADD CONSTRAINT FK_F97A827D19EB6921 FOREIGN KEY (client_id) REFERENCES client (id) ON DELETE CASCADE'); + $this->addSql('ALTER TABLE client DROP FOREIGN KEY FK_C7440455F7E54CF3'); + $this->addSql('DROP INDEX IDX_C7440455F7E54CF3 ON client'); + $this->addSql('ALTER TABLE client ADD og_live_id INT DEFAULT NULL'); + $this->addSql('ALTER TABLE client ADD CONSTRAINT FK_C7440455F7E54CF3 FOREIGN KEY (og_live_id) REFERENCES og_live (id)'); + $this->addSql('CREATE INDEX IDX_C7440455F7E54CF3 ON client (og_live_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_client DROP FOREIGN KEY FK_F97A827D62DC5265'); + $this->addSql('ALTER TABLE command_task_client DROP FOREIGN KEY FK_F97A827D19EB6921'); + $this->addSql('DROP TABLE command_task_client'); + $this->addSql('ALTER TABLE client DROP FOREIGN KEY FK_C7440455F7E54CF3'); + $this->addSql('DROP INDEX IDX_C7440455F7E54CF3 ON client'); + $this->addSql('ALTER TABLE client DROP og_live_id'); + $this->addSql('ALTER TABLE client ADD CONSTRAINT FK_C7440455F7E54CF3 FOREIGN KEY (subnet_id) REFERENCES og_live (id)'); + $this->addSql('CREATE INDEX IDX_C7440455F7E54CF3 ON client (subnet_id)'); + } +} -- 2.40.1 From d135c74f8ad5e8631da524c2ed6cddee1f809bfd Mon Sep 17 00:00:00 2001 From: Manuel Aranda Date: Tue, 1 Oct 2024 15:05:25 +0200 Subject: [PATCH 068/157] refs #726. Integration UDS. Success Put info --- config/api_platform/RemoteCalendar.yaml | 6 + .../UDS/RemoteCalendarSyncUdsAction.php | 34 ++++ src/Entity/RemoteCalendar.php | 17 ++ src/Service/UDS/UDSClient.php | 165 +++++++++++++----- 4 files changed, 179 insertions(+), 43 deletions(-) create mode 100644 src/Controller/UDS/RemoteCalendarSyncUdsAction.php diff --git a/config/api_platform/RemoteCalendar.yaml b/config/api_platform/RemoteCalendar.yaml index 9585ffe..4bb8e36 100644 --- a/config/api_platform/RemoteCalendar.yaml +++ b/config/api_platform/RemoteCalendar.yaml @@ -23,6 +23,12 @@ resources: ApiPlatform\Metadata\Post: ~ ApiPlatform\Metadata\Delete: ~ + sync_uds: + class: ApiPlatform\Metadata\Post + method: POST + uriTemplate: /remote-calendars/{uuid}/sync-uds + controller: App\Controller\UDS\RemoteCalendarSyncUdsAction + properties: App\Entity\RemoteCalendar: id: diff --git a/src/Controller/UDS/RemoteCalendarSyncUdsAction.php b/src/Controller/UDS/RemoteCalendarSyncUdsAction.php new file mode 100644 index 0000000..019534b --- /dev/null +++ b/src/Controller/UDS/RemoteCalendarSyncUdsAction.php @@ -0,0 +1,34 @@ +udsClient->__invoke(); + + return new JsonResponse($data, Response::HTTP_OK); + + } +} \ No newline at end of file diff --git a/src/Entity/RemoteCalendar.php b/src/Entity/RemoteCalendar.php index a0773e5..c06e599 100644 --- a/src/Entity/RemoteCalendar.php +++ b/src/Entity/RemoteCalendar.php @@ -83,4 +83,21 @@ class RemoteCalendar extends AbstractEntity return $this; } + + public function isRemoteAvailable(): bool + { + $now = new \DateTime(); + + foreach ($this->getRules() as $rule) { + $dayOfWeek = (int) $now->format('w'); + $adjustedDayOfWeek = ($dayOfWeek + 6) % 7; + if (!in_array($adjustedDayOfWeek, $rule->getBusyWeekdays())) { + return false; + } + + $currentTime = $now->format('H:i'); + + return ($currentTime >= $rule->getBusyFromHour() && $currentTime <= $rule->getBusyToHour()); + } + } } diff --git a/src/Service/UDS/UDSClient.php b/src/Service/UDS/UDSClient.php index fa7fde2..364c62f 100644 --- a/src/Service/UDS/UDSClient.php +++ b/src/Service/UDS/UDSClient.php @@ -3,22 +3,31 @@ namespace App\Service\UDS; use AllowDynamicProperties; +use App\Entity\OrganizationalUnit; use Doctrine\ORM\EntityManagerInterface; +use Symfony\Component\HttpClient\Exception\TransportException; +use Symfony\Component\HttpFoundation\JsonResponse; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Contracts\HttpClient\Exception\ClientExceptionInterface; +use Symfony\Contracts\HttpClient\Exception\RedirectionExceptionInterface; +use Symfony\Contracts\HttpClient\Exception\ServerExceptionInterface; use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface; use Symfony\Contracts\HttpClient\HttpClientInterface; #[AllowDynamicProperties] class UDSClient { + private string $token; + + private string $scrambler; + public function __construct( - private HttpClientInterface $httpClient, - EntityManagerInterface $entityManager, - private string $udsAPIurl, - private string $udsAuthLogin, - private string $udsAuthUsername, - private string $udsAuthPassword, - string $token, - string $scrambler + private HttpClientInterface $httpClient, + private readonly EntityManagerInterface $entityManager, + private string $udsAPIurl, + private string $udsAuthLogin, + private string $udsAuthUsername, + private string $udsAuthPassword, ) { } @@ -26,9 +35,35 @@ class UDSClient /** * @throws TransportExceptionInterface */ - public function __invoke(): void + public function __invoke(): array { $this->login(); + $servicePools = $this->getServicePools(); + + foreach ($servicePools as $servicePool) { + $servicePoolInfo = $this->getServicePool($servicePool['provider_id'], $servicePool['service_id']); + + if (!$servicePoolInfo) { + continue; + } + + $classroom = $servicePoolInfo['ou']; + $maxAvailableSeats = $this->getMaxAvailableSeats($classroom); + $servicePool['max_srvs'] = 10; + $servicePool['osmanager_id'] = null; + + $response = $this->httpClient->request('PUT', $this->udsAPIurl . 'servicespools/' . $servicePool['id'], [ + 'verify_host' => false, + 'headers' => [ + 'X-Auth-Token' => $this->token, + 'Content-Type' => 'application/json', + 'Scrambler' => $this->scrambler + ], + 'body' => json_encode($servicePool) + ]); + + return json_decode($response->getContent(false), true); + } } /** @@ -36,17 +71,28 @@ class UDSClient */ public function login(): void { - $response = $this->httpClient->request('POST', $this->udsAPIurl . '/auth/login', [ - 'json' => [ - 'login' => $this->udsAuthLogin, - 'username' => $this->udsAuthUsername, - 'password' => $this->udsAuthPassword - ] - ]); + try { + $response = $this->httpClient->request('POST', $this->udsAPIurl . 'auth/login', [ + 'verify_host' => false, + 'headers' => [ + 'Content-Type' => 'application/json' + ], + 'json' => [ + 'auth' => $this->udsAuthLogin, + 'username' => $this->udsAuthUsername, + 'password' => $this->udsAuthPassword + ] + ]); - $data = json_decode($response->getContent(), true); - $this->token = $data['token']; - $this->scrambler = $data['scrambler']; + $data = json_decode($response->getContent(false), true); + + $this->token = $data['token']; + $this->scrambler = $data['scrambler']; + + } catch (TransportExceptionInterface $e) { + throw new TransportException('Error while logging in'); + } catch (ClientExceptionInterface|ServerExceptionInterface|RedirectionExceptionInterface $e) { + } } /** @@ -54,35 +100,68 @@ class UDSClient */ public function getServicePools(): array { - $response = $this->httpClient->request('GET', $this->udsAPIurl . '/servicespools/overview', [ - 'headers' => [ - 'X-Auth-Token' => $this->token, - 'Content-Type' => 'application/json', - 'Scrambler' => $this->scrambler - ] - ]); + try { + $response = $this->httpClient->request('GET', $this->udsAPIurl . '/servicespools/overview', [ + 'verify_host' => false, + 'headers' => [ + 'X-Auth-Token' => $this->token, + 'Content-Type' => 'application/json', + 'Scrambler' => $this->scrambler + ] + ]); + return json_decode($response->getContent(), true); + } catch (TransportExceptionInterface $e) { + throw new TransportException('Error while fetching service pools'); + } catch (ClientExceptionInterface|ServerExceptionInterface|RedirectionExceptionInterface $e) { + } } - public function getServicePool(string $providerId, string $serviceId): array + /** + * @throws TransportExceptionInterface + */ + public function getServicePool(string $providerId, string $serviceId): ?array { - $response = $this->httpClient->request('GET', $this->udsAPIurl . '/providers/' . $providerId .'/services/'. $serviceId, [ - 'headers' => [ - 'X-Auth-Token' => $this->token, - 'Content-Type' => 'application/json', - 'Scrambler' => $this->scrambler - ] - ]); + try { + $response = $this->httpClient->request('GET', $this->udsAPIurl . 'providers/' . $providerId .'/services/'. $serviceId, [ + 'verify_host' => false, + 'headers' => [ + 'X-Auth-Token' => $this->token, + 'Content-Type' => 'application/json', + 'Scrambler' => $this->scrambler + ] + ]); + + return json_decode($response->getContent(), true); + } catch (TransportExceptionInterface $e) { + throw new TransportException('Error while fetching service pool'); + } catch (ClientExceptionInterface|ServerExceptionInterface|RedirectionExceptionInterface $e) { + } + + return null; } - public function setServicePool(string $serviceId, array $payload): void + public function getMaxAvailableSeats(int $organizationalUnitId): int { - $response = $this->httpClient->request('PUT', $this->udsAPIurl . '/servicespools/' . $serviceId, [ - 'headers' => [ - 'X-Auth-Token' => $this->token, - 'Content-Type' => 'application/json', - 'Scrambler' => $this->scrambler - ], - 'json' => $payload - ]); + $organizationalUnit = $this->entityManager->getRepository(OrganizationalUnit::class)->find($organizationalUnitId); + + if (!$organizationalUnit) { + return 0; + } + + $remoteCalendar = $organizationalUnit->getRemoteCalendar(); + if (!$remoteCalendar || !$remoteCalendar->isRemoteAvailable()) { + return 0; + } + + $totalAvailableClients = 0; + + foreach ($organizationalUnit->getClients() as $client) { + $status = $client->getStatus(); + + $clientAvailable = in_array($status, ['active', 'windows', 'linux']); + $totalAvailableClients += $clientAvailable ? 1 : 0; + } + + return $totalAvailableClients; } } \ No newline at end of file -- 2.40.1 From feb239f3de26b45ddd6f33f9b4086a96c22dff95 Mon Sep 17 00:00:00 2001 From: Manuel Aranda Date: Tue, 1 Oct 2024 16:19:40 +0200 Subject: [PATCH 069/157] refs #726. Added new fields OU ddbb integration UDS --- src/Dto/Input/OrganizationalUnitInput.php | 5 ++++ src/Dto/Output/OrganizationalUnitOutput.php | 8 ++++++ src/Entity/OrganizationalUnit.php | 32 ++++++++++++++++++++- src/Factory/OrganizationalUnitFactory.php | 1 + src/Service/UDS/UDSClient.php | 2 +- tests/Functional/OrganizationalUnitTest.php | 2 ++ 6 files changed, 48 insertions(+), 2 deletions(-) diff --git a/src/Dto/Input/OrganizationalUnitInput.php b/src/Dto/Input/OrganizationalUnitInput.php index aa641f9..022f994 100644 --- a/src/Dto/Input/OrganizationalUnitInput.php +++ b/src/Dto/Input/OrganizationalUnitInput.php @@ -54,6 +54,9 @@ class OrganizationalUnitInput #[Groups(['organizational-unit:write'])] public ?RemoteCalendarOutput $remoteCalendar = null; + #[Groups(['organizational-unit:write'])] + public ?bool $remotePc = null; + public function __construct(?OrganizationalUnit $organizationalUnit = null) { if (!$organizationalUnit) { @@ -71,6 +74,7 @@ class OrganizationalUnitInput $this->capacity = $organizationalUnit->getCapacity(); $this->comments = $organizationalUnit->getComments(); $this->type = $organizationalUnit->getType(); + $this->remotePc = $organizationalUnit->isRemotePc(); if ($organizationalUnit->getNetworkSettings()){ $this->networkSettings = new NetworkSettingsInput($organizationalUnit->getNetworkSettings()); } @@ -97,6 +101,7 @@ class OrganizationalUnitInput $organizationalUnit->setCapacity($this->capacity); $organizationalUnit->setComments($this->comments); $organizationalUnit->setType($this->type); + $organizationalUnit->setRemotePc($this->remotePc); if ($this->networkSettings){ $organizationalUnit->setNetworkSettings($this->networkSettings->createOrUpdateEntity($organizationalUnit->getNetworkSettings())); diff --git a/src/Dto/Output/OrganizationalUnitOutput.php b/src/Dto/Output/OrganizationalUnitOutput.php index ab850e0..d5c00bc 100644 --- a/src/Dto/Output/OrganizationalUnitOutput.php +++ b/src/Dto/Output/OrganizationalUnitOutput.php @@ -55,6 +55,12 @@ final class OrganizationalUnitOutput extends AbstractOutput #[ApiProperty(readableLink: true)] public ?RemoteCalendarOutput $remoteCalendar = null; + #[Groups(['organizational-unit:read'])] + public ?bool $remotePc = null; + + #[Groups(['organizational-unit:read'])] + public ?bool $reserved = null; + #[Groups(['organizational-unit:read'])] public \DateTime $createdAt; @@ -73,6 +79,8 @@ final class OrganizationalUnitOutput extends AbstractOutput $this->board = $organizationalUnit->isBoard(); $this->capacity = $organizationalUnit->getCapacity(); $this->type = $organizationalUnit->getType(); + $this->remotePc = $organizationalUnit->isRemotePc(); + $this->reserved = $organizationalUnit->isReserved(); $this->networkSettings = $organizationalUnit->getNetworkSettings() ? new NetworkSettingsOutput($organizationalUnit->getNetworkSettings()) : null; $this->remoteCalendar = $organizationalUnit->getRemoteCalendar() ? new RemoteCalendarOutput($organizationalUnit->getRemoteCalendar()) : null; if ($organizationalUnit->getParent()) { diff --git a/src/Entity/OrganizationalUnit.php b/src/Entity/OrganizationalUnit.php index c1f9435..b0d5596 100644 --- a/src/Entity/OrganizationalUnit.php +++ b/src/Entity/OrganizationalUnit.php @@ -87,10 +87,16 @@ class OrganizationalUnit extends AbstractEntity #[ORM\ManyToOne(inversedBy: 'organizationalUnits')] private ?Subnet $subnet = null; - #[ORM\ManyToOne(targetEntity: RemoteCalendar::class, inversedBy: 'organizationalUnits', cascade: ['persist'])] + #[ORM\ManyToOne(targetEntity: RemoteCalendar::class, cascade: ['persist'], inversedBy: 'organizationalUnits')] #[ORM\JoinColumn(nullable: true)] private ?RemoteCalendar $remoteCalendar = null; + #[ORM\Column] + private ?bool $remotePc = false; + + #[ORM\Column] + private ?bool $reserved = false; + public function __construct() { parent::__construct(); @@ -399,4 +405,28 @@ class OrganizationalUnit extends AbstractEntity return $this; } + + public function isRemotePc(): ?bool + { + return $this->remotePc; + } + + public function setRemotePc(bool $remotePc): static + { + $this->remotePc = $remotePc; + + return $this; + } + + public function isReserved(): ?bool + { + return $this->reserved; + } + + public function setReserved(bool $reserved): static + { + $this->reserved = $reserved; + + return $this; + } } diff --git a/src/Factory/OrganizationalUnitFactory.php b/src/Factory/OrganizationalUnitFactory.php index c4be743..12bd17b 100644 --- a/src/Factory/OrganizationalUnitFactory.php +++ b/src/Factory/OrganizationalUnitFactory.php @@ -34,6 +34,7 @@ final class OrganizationalUnitFactory extends ModelFactory 'createdAt' => self::faker()->dateTime(), 'name' => self::faker()->text(255), 'updatedAt' => self::faker()->dateTime(), + 'remotePc' => self::faker()->boolean(), ]; } diff --git a/src/Service/UDS/UDSClient.php b/src/Service/UDS/UDSClient.php index 364c62f..e70fdb1 100644 --- a/src/Service/UDS/UDSClient.php +++ b/src/Service/UDS/UDSClient.php @@ -49,7 +49,7 @@ class UDSClient $classroom = $servicePoolInfo['ou']; $maxAvailableSeats = $this->getMaxAvailableSeats($classroom); - $servicePool['max_srvs'] = 10; + $servicePool['max_srvs'] = $maxAvailableSeats; $servicePool['osmanager_id'] = null; $response = $this->httpClient->request('PUT', $this->udsAPIurl . 'servicespools/' . $servicePool['id'], [ diff --git a/tests/Functional/OrganizationalUnitTest.php b/tests/Functional/OrganizationalUnitTest.php index 899093b..bcb4a97 100644 --- a/tests/Functional/OrganizationalUnitTest.php +++ b/tests/Functional/OrganizationalUnitTest.php @@ -63,6 +63,7 @@ class OrganizationalUnitTest extends AbstractTest $this->createClientWithCredentials()->request('POST', '/organizational-units',['json' => [ 'name' => self::ORGANIZATIONAL_UNIT_CREATE, 'type' => OrganizationalUnitTypes::ORGANIZATIONAL_UNIT, + 'remotePc' => true, ]]); $this->assertResponseStatusCodeSame(201); @@ -72,6 +73,7 @@ class OrganizationalUnitTest extends AbstractTest '@type' => 'OrganizationalUnit', 'name' => self::ORGANIZATIONAL_UNIT_CREATE, 'type' => OrganizationalUnitTypes::ORGANIZATIONAL_UNIT, + 'remotePc' => true, ]); } -- 2.40.1 From ca23d41241808cc923ee9b50db06cdae6bbf2486 Mon Sep 17 00:00:00 2001 From: Manuel Aranda Date: Thu, 3 Oct 2024 08:57:56 +0200 Subject: [PATCH 070/157] refs #724. New field . RemotePc --- config/api_platform/Trace.yaml | 3 +++ config/services/api_platform.yaml | 12 +++++++++++ migrations/Version20241002062742.php | 31 ++++++++++++++++++++++++++++ src/Entity/RemoteCalendar.php | 31 ++++++++++++++++++++-------- 4 files changed, 68 insertions(+), 9 deletions(-) create mode 100644 migrations/Version20241002062742.php diff --git a/config/api_platform/Trace.yaml b/config/api_platform/Trace.yaml index c7e74db..115c1f0 100644 --- a/config/api_platform/Trace.yaml +++ b/config/api_platform/Trace.yaml @@ -12,6 +12,9 @@ resources: ApiPlatform\Metadata\Get: provider: App\State\Provider\TraceProvider + order: + createdAt: DESC + properties: App\Entity\Trace: id: diff --git a/config/services/api_platform.yaml b/config/services/api_platform.yaml index 07306b4..8b1dc40 100644 --- a/config/services/api_platform.yaml +++ b/config/services/api_platform.yaml @@ -127,6 +127,18 @@ services: arguments: [ { 'synchronized': ~ } ] tags: [ 'api_platform.filter' ] + api_platform.filter.remote_calendar.order: + parent: 'api_platform.doctrine.orm.order_filter' + arguments: + $properties: { 'id': ~, 'name': ~ } + $orderParameterName: 'order' + tags: [ 'api_platform.filter' ] + + api_platform.filter.remote_calendar.search: + parent: 'api_platform.doctrine.orm.search_filter' + arguments: [ { 'id': 'exact', 'name': 'partial', } ] + tags: [ 'api_platform.filter' ] + api_platform.filter.user.order: parent: 'api_platform.doctrine.orm.order_filter' arguments: diff --git a/migrations/Version20241002062742.php b/migrations/Version20241002062742.php new file mode 100644 index 0000000..d867007 --- /dev/null +++ b/migrations/Version20241002062742.php @@ -0,0 +1,31 @@ +addSql('ALTER TABLE organizational_unit ADD remote_pc TINYINT(1) NOT NULL, ADD reserved 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 organizational_unit DROP remote_pc, DROP reserved'); + } +} diff --git a/src/Entity/RemoteCalendar.php b/src/Entity/RemoteCalendar.php index c06e599..66348bc 100644 --- a/src/Entity/RemoteCalendar.php +++ b/src/Entity/RemoteCalendar.php @@ -84,20 +84,33 @@ class RemoteCalendar extends AbstractEntity return $this; } - public function isRemoteAvailable(): bool + public function isAvailable(): bool { $now = new \DateTime(); foreach ($this->getRules() as $rule) { - $dayOfWeek = (int) $now->format('w'); - $adjustedDayOfWeek = ($dayOfWeek + 6) % 7; - if (!in_array($adjustedDayOfWeek, $rule->getBusyWeekdays())) { - return false; + if ($rule->isRemoteAvailable()) { + $fromDate = $rule->getAvailableFromDate(); + $toDate = $rule->getAvailableToDate(); + + if ($fromDate && $toDate && $fromDate <= $now && $now <= $toDate) { + return true; + } + } else { + $dayOfWeek = (int) $now->format('w'); + $adjustedDayOfWeek = ($dayOfWeek + 6) % 7; + + if (!in_array($adjustedDayOfWeek, $rule->getBusyWeekdays())) { + return true; + } + + $currentTime = $now->format('H:i'); + if (!($currentTime >= $rule->getBusyFromHour() && $currentTime <= $rule->getBusyToHour())) { + return true; + } } - - $currentTime = $now->format('H:i'); - - return ($currentTime >= $rule->getBusyFromHour() && $currentTime <= $rule->getBusyToHour()); } + + return false; } } -- 2.40.1 From 179f36ebef5ae9ae861cae1c3419ef8280686ffe Mon Sep 17 00:00:00 2001 From: Manuel Aranda Date: Thu, 3 Oct 2024 08:58:25 +0200 Subject: [PATCH 071/157] refs #725. Updated API Dto --- src/Dto/Input/ClientInput.php | 9 +++++++++ src/Dto/Input/OrganizationalUnitInput.php | 2 +- src/Dto/Output/ClientOutput.php | 4 ++++ src/Dto/Output/OrganizationalUnitOutput.php | 1 - src/Dto/Output/RemoteCalendarOutput.php | 2 +- src/Service/UDS/UDSClient.php | 7 ++++--- 6 files changed, 19 insertions(+), 6 deletions(-) diff --git a/src/Dto/Input/ClientInput.php b/src/Dto/Input/ClientInput.php index fbc5bf9..32abc29 100644 --- a/src/Dto/Input/ClientInput.php +++ b/src/Dto/Input/ClientInput.php @@ -58,6 +58,13 @@ final class ClientInput )] public ?string $ip = null; + #[Groups(['client:write'])] + #[ApiProperty( + description: 'El estado del cliente', + example: 'active' + )] + public ?string $status = 'power-off'; + #[Assert\NotNull(message: 'validators.organizational_unit.not_null')] #[Groups(['client:write', 'client:patch'])] #[ApiProperty( @@ -109,6 +116,7 @@ final class ClientInput $this->mac = $client->getMac(); $this->ip = $client->getIp(); $this->position = $client->getPosition(); + $this->status = $client->getStatus(); if ($client->getMenu()) { $this->menu = new MenuOutput($client->getMenu()); @@ -140,6 +148,7 @@ final class ClientInput $client->setOgLive($this->ogLive?->getEntity()); $client->setHardwareProfile($this->hardwareProfile?->getEntity()); $client->setPosition($this->position); + $client->setStatus($this->status); return $client; } diff --git a/src/Dto/Input/OrganizationalUnitInput.php b/src/Dto/Input/OrganizationalUnitInput.php index 022f994..d06e337 100644 --- a/src/Dto/Input/OrganizationalUnitInput.php +++ b/src/Dto/Input/OrganizationalUnitInput.php @@ -55,7 +55,7 @@ class OrganizationalUnitInput public ?RemoteCalendarOutput $remoteCalendar = null; #[Groups(['organizational-unit:write'])] - public ?bool $remotePc = null; + public ?bool $remotePc = false; public function __construct(?OrganizationalUnit $organizationalUnit = null) { diff --git a/src/Dto/Output/ClientOutput.php b/src/Dto/Output/ClientOutput.php index f1a33dd..78ee199 100644 --- a/src/Dto/Output/ClientOutput.php +++ b/src/Dto/Output/ClientOutput.php @@ -55,6 +55,9 @@ final class ClientOutput extends AbstractOutput #[Groups(['client:read'])] public ?array $position = ['x' => 0, 'y' => 0]; + #[Groups(['client:read'])] + public ?string $status = ''; + #[Groups(['client:read'])] public \DateTime $createdAt; @@ -84,6 +87,7 @@ final class ClientOutput extends AbstractOutput $this->position = $client->getPosition(); $this->hardwareProfile = $client->getHardwareProfile() ? new HardwareProfileOutput($client->getHardwareProfile()) : null; $this->ogLive = $client->getOgLive() ? new OgLiveOutput($client->getOgLive()) : null; + $this->status = $client->getStatus(); $this->createdAt = $client->getCreatedAt(); $this->createdBy = $client->getCreatedBy(); } diff --git a/src/Dto/Output/OrganizationalUnitOutput.php b/src/Dto/Output/OrganizationalUnitOutput.php index d5c00bc..98a5dc9 100644 --- a/src/Dto/Output/OrganizationalUnitOutput.php +++ b/src/Dto/Output/OrganizationalUnitOutput.php @@ -99,7 +99,6 @@ final class OrganizationalUnitOutput extends AbstractOutput )->toArray(); } - $this->path = $organizationalUnit->getPath(); $this->createdAt = $organizationalUnit->getCreatedAt(); $this->createdBy = $organizationalUnit->getCreatedBy(); diff --git a/src/Dto/Output/RemoteCalendarOutput.php b/src/Dto/Output/RemoteCalendarOutput.php index 97b260c..b079f8a 100644 --- a/src/Dto/Output/RemoteCalendarOutput.php +++ b/src/Dto/Output/RemoteCalendarOutput.php @@ -10,7 +10,7 @@ use Symfony\Component\Serializer\Annotation\Groups; #[Get(shortName: 'RemoteCalendar')] final class RemoteCalendarOutput extends AbstractOutput { - #[Groups(['remote-calendar:read'])] + #[Groups(['remote-calendar:read', 'organizational-unit:read'])] public ?string $name = null; /** diff --git a/src/Service/UDS/UDSClient.php b/src/Service/UDS/UDSClient.php index e70fdb1..95ee9c6 100644 --- a/src/Service/UDS/UDSClient.php +++ b/src/Service/UDS/UDSClient.php @@ -90,7 +90,7 @@ class UDSClient $this->scrambler = $data['scrambler']; } catch (TransportExceptionInterface $e) { - throw new TransportException('Error while logging in'); + throw new TransportException('Error while logging in to UDS'); } catch (ClientExceptionInterface|ServerExceptionInterface|RedirectionExceptionInterface $e) { } } @@ -142,14 +142,15 @@ class UDSClient public function getMaxAvailableSeats(int $organizationalUnitId): int { - $organizationalUnit = $this->entityManager->getRepository(OrganizationalUnit::class)->find($organizationalUnitId); + $organizationalUnit = $this->entityManager->getRepository(OrganizationalUnit::class)->findOneBy(['migrationId' => $organizationalUnitId]); if (!$organizationalUnit) { return 0; } $remoteCalendar = $organizationalUnit->getRemoteCalendar(); - if (!$remoteCalendar || !$remoteCalendar->isRemoteAvailable()) { + $aux = $remoteCalendar->isAvailable(); + if (!$remoteCalendar || !$aux) { return 0; } -- 2.40.1 From c14002e220c6211b2287ee6d4b1064bfffda910f Mon Sep 17 00:00:00 2001 From: Manuel Aranda Date: Thu, 3 Oct 2024 20:20:37 +0200 Subject: [PATCH 072/157] refs #759. Adding listener Trace --- config/services/api_platform.yaml | 14 +++++- src/Dto/Output/OrganizationalUnitOutput.php | 4 +- src/Service/CreateTraceService.php | 46 ++++++++++++++++++++ src/Service/UDS/UDSClient.php | 3 +- src/State/Processor/CommandTaskProcessor.php | 13 +++--- 5 files changed, 70 insertions(+), 10 deletions(-) create mode 100644 src/Service/CreateTraceService.php diff --git a/config/services/api_platform.yaml b/config/services/api_platform.yaml index 8b1dc40..e4111f6 100644 --- a/config/services/api_platform.yaml +++ b/config/services/api_platform.yaml @@ -32,7 +32,7 @@ services: api_platform.filter.command.search: parent: 'api_platform.doctrine.orm.search_filter' - arguments: [ { 'id': 'exact', 'name': 'exact'} ] + arguments: [ { 'id': 'exact', 'name': 'partial'} ] tags: [ 'api_platform.filter' ] api_platform.filter.command.boolean: @@ -139,6 +139,18 @@ services: arguments: [ { 'id': 'exact', 'name': 'partial', } ] tags: [ 'api_platform.filter' ] + api_platform.filter.trace.search: + parent: 'api_platform.doctrine.orm.search_filter' + arguments: [ { 'id': 'exact', 'command.id': 'exact', 'client.id': 'exact' } ] + tags: [ 'api_platform.filter' ] + + api_platform.filter.trace.order: + parent: 'api_platform.doctrine.orm.order_filter' + arguments: + $properties: { 'id': ~, 'command': ~ } + $orderParameterName: 'order' + tags: [ 'api_platform.filter' ] + api_platform.filter.user.order: parent: 'api_platform.doctrine.orm.order_filter' arguments: diff --git a/src/Dto/Output/OrganizationalUnitOutput.php b/src/Dto/Output/OrganizationalUnitOutput.php index 98a5dc9..7be1acc 100644 --- a/src/Dto/Output/OrganizationalUnitOutput.php +++ b/src/Dto/Output/OrganizationalUnitOutput.php @@ -59,7 +59,7 @@ final class OrganizationalUnitOutput extends AbstractOutput public ?bool $remotePc = null; #[Groups(['organizational-unit:read'])] - public ?bool $reserved = null; + public ?bool $available = null; #[Groups(['organizational-unit:read'])] public \DateTime $createdAt; @@ -80,7 +80,7 @@ final class OrganizationalUnitOutput extends AbstractOutput $this->capacity = $organizationalUnit->getCapacity(); $this->type = $organizationalUnit->getType(); $this->remotePc = $organizationalUnit->isRemotePc(); - $this->reserved = $organizationalUnit->isReserved(); + $this->available = $organizationalUnit->getRemoteCalendar() && $organizationalUnit->getRemoteCalendar()->isAvailable(); $this->networkSettings = $organizationalUnit->getNetworkSettings() ? new NetworkSettingsOutput($organizationalUnit->getNetworkSettings()) : null; $this->remoteCalendar = $organizationalUnit->getRemoteCalendar() ? new RemoteCalendarOutput($organizationalUnit->getRemoteCalendar()) : null; if ($organizationalUnit->getParent()) { diff --git a/src/Service/CreateTraceService.php b/src/Service/CreateTraceService.php new file mode 100644 index 0000000..cb747d1 --- /dev/null +++ b/src/Service/CreateTraceService.php @@ -0,0 +1,46 @@ +getCommands() as $command) { + foreach ($commandTask->getClients() as $client) { + $trace = new Trace(); + $trace->setClient($client); + $trace->setCommand($command); + $trace->setStatus(TraceStatus::PENDING); + $trace->setExecutedAt($commandTask->getDatetime()); + $this->entityManager->persist($trace); + } + } + + foreach ($commandTask->getCommandGroups() as $commandGroup) { + foreach ($commandTask->getCommands() as $command) { + foreach ($commandTask->getClients() as $client) { + $trace = new Trace(); + $trace->setClient($client); + $trace->setCommand($command); + $trace->setStatus(TraceStatus::PENDING); + $trace->setExecutedAt($commandTask->getDatetime()); + $this->entityManager->persist($trace); + } + } + } + + $this->entityManager->flush(); + } +} \ No newline at end of file diff --git a/src/Service/UDS/UDSClient.php b/src/Service/UDS/UDSClient.php index 95ee9c6..071cc56 100644 --- a/src/Service/UDS/UDSClient.php +++ b/src/Service/UDS/UDSClient.php @@ -149,8 +149,7 @@ class UDSClient } $remoteCalendar = $organizationalUnit->getRemoteCalendar(); - $aux = $remoteCalendar->isAvailable(); - if (!$remoteCalendar || !$aux) { + if (!$remoteCalendar || !$remoteCalendar->isAvailable()) { return 0; } diff --git a/src/State/Processor/CommandTaskProcessor.php b/src/State/Processor/CommandTaskProcessor.php index bad348b..fe39109 100644 --- a/src/State/Processor/CommandTaskProcessor.php +++ b/src/State/Processor/CommandTaskProcessor.php @@ -12,12 +12,14 @@ use ApiPlatform\Validator\ValidatorInterface; use App\Dto\Input\CommandTaskInput; use App\Dto\Output\CommandTaskOutput; use App\Repository\CommandTaskRepository; +use App\Service\CreateTraceService; readonly class CommandTaskProcessor implements ProcessorInterface { public function __construct( private CommandTaskRepository $commandTaskRepository, - private ValidatorInterface $validator + private ValidatorInterface $validator, + private CreateTraceService $createTraceService ) { } @@ -51,11 +53,12 @@ readonly class CommandTaskProcessor implements ProcessorInterface $entity = $this->commandTaskRepository->findOneByUuid($uriVariables['uuid']); } - $command = $data->createOrUpdateEntity($entity); - $this->validator->validate($command); - $this->commandTaskRepository->save($command); + $task = $data->createOrUpdateEntity($entity); + $this->validator->validate($task); + $this->commandTaskRepository->save($task); + $this->createTraceService->__invoke($task); - return new CommandTaskOutput($command); + return new CommandTaskOutput($task); } private function processDelete($data, Operation $operation, array $uriVariables = [], array $context = []): null -- 2.40.1 From cdfab436d8bd278bd77826081fadbc95f4b80b2f Mon Sep 17 00:00:00 2001 From: Manuel Aranda Date: Fri, 4 Oct 2024 10:32:27 +0200 Subject: [PATCH 073/157] New validator text --- README.md | 8 ++------ translations/validators.en.yaml | 12 ++++++++++++ translations/validators.es.yaml | 14 +++++++++++++- 3 files changed, 27 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index ad2d3fa..c79fdfa 100644 --- a/README.md +++ b/README.md @@ -129,12 +129,8 @@ Una vez tengamos la base de datos cargada, podremos ejecutar las migraciones de ```sh --- Migraciones de OpenGnsys. --- -docker exec ogcore-php php bin/console opengnsys:migration:organizational-unit #cargamos las unidades organizativas -docker exec ogcore-php php bin/console opengnsys:migration:hardware-profiles #cargamos los perfiles de hardware -docker exec ogcore-php php bin/console opengnsys:migration:clients #cargamos los clientes -docker exec ogcore-php php bin/console opengnsys:migration:os #cargamos los sistemas operativos -docker exec ogcore-php php bin/console opengnsys:migration:partition #cargamos las particiones -``` +Proximamente +``` ## Objetos de interés diff --git a/translations/validators.en.yaml b/translations/validators.en.yaml index 5281532..7881069 100644 --- a/translations/validators.en.yaml +++ b/translations/validators.en.yaml @@ -54,3 +54,15 @@ validators: name: not_blank: 'The name should not be blank.' unique: 'The name should be unique.' + + subnet: + name: + not_blank: 'The name should not be blank.' + ip_address: + not_blank: 'The IP should not be blank.' + netmask: + not_blank: 'The netmask should not be blank.' + boot_file_name: + not_blank: 'The boot file name should not be blank.' + next_server: + not_blank: 'The next server should not be blank.' \ No newline at end of file diff --git a/translations/validators.es.yaml b/translations/validators.es.yaml index 5a2984d..9f11c35 100644 --- a/translations/validators.es.yaml +++ b/translations/validators.es.yaml @@ -48,4 +48,16 @@ validators: operative_system: name: - not_blank: 'El nombre no debería estar vacío.' \ No newline at end of file + not_blank: 'El nombre no debería estar vacío.' + + subnet: + name: + not_blank: 'El nombre no debería estar vacío.' + ip_address: + not_blank: 'La dirección IP no debería estar vacía.' + netmask: + not_blank: 'La máscara de red no debería estar vacía.' + boot_file_name: + not_blank: 'El nombre del archivo de arranque no debería estar vacío.' + next_server: + not_blank: 'El servidor no debería estar vacío.' \ No newline at end of file -- 2.40.1 From ea35ddd1e447f25a0f581277694efeba759ad365 Mon Sep 17 00:00:00 2001 From: Manuel Aranda Date: Mon, 7 Oct 2024 11:30:44 +0200 Subject: [PATCH 074/157] Updated command script migration --- README.md | 8 ++++++-- config/api_platform/OrganizationalUnit.yaml | 1 + config/services/api_platform.yaml | 5 +++++ src/Command/LoadDefaultCommandsCommand.php | 1 + .../MigrateHardwareAndHardwareProfileCommand.php | 7 ++++++- src/Dto/Output/AbstractOutput.php | 2 +- src/Dto/Output/OrganizationalUnitOutput.php | 4 ++-- 7 files changed, 22 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index c79fdfa..e8fc13a 100644 --- a/README.md +++ b/README.md @@ -129,8 +129,12 @@ Una vez tengamos la base de datos cargada, podremos ejecutar las migraciones de ```sh --- Migraciones de OpenGnsys. --- -Proximamente -``` +docker exec ogcore-php php bin/console opengnsys:migration:organizational-unit #cargamos las unidades organizativas +docker exec ogcore-php php bin/console opengnsys:migration:hardware-profile #cargamos los perfiles de hardware +docker exec ogcore-php php bin/console opengnsys:migration:clients #cargamos los clientes +docker exec ogcore-php php bin/console opengnsys:migration:os #cargamos los sistemas operativos +docker exec ogcore-php php bin/console opengnsys:migration:partition #cargamos las particiones +``` ## Objetos de interés diff --git a/config/api_platform/OrganizationalUnit.yaml b/config/api_platform/OrganizationalUnit.yaml index e8f198b..d3793b0 100644 --- a/config/api_platform/OrganizationalUnit.yaml +++ b/config/api_platform/OrganizationalUnit.yaml @@ -13,6 +13,7 @@ resources: filters: - 'api_platform.filter.organizational_unit.order' - 'api_platform.filter.organizational_unit.search' + - 'api_platform.filter.organizational_unit.group_filter' ApiPlatform\Metadata\Get: security: 'is_granted("ORGANIZATIONAL_UNIT_VIEW", object)' diff --git a/config/services/api_platform.yaml b/config/services/api_platform.yaml index e4111f6..cd8bc48 100644 --- a/config/services/api_platform.yaml +++ b/config/services/api_platform.yaml @@ -93,6 +93,11 @@ services: arguments: [ { id: 'exact', name: 'partial', type: 'exact', parent.id: 'exact'} ] tags: [ 'api_platform.filter' ] + api_platform.filter.organizational_unit.group_filter: + parent: 'api_platform.serializer.group_filter' + arguments: [ 'groups', true, ['organizational-unit:read:collection:short'] ] + tags: [ 'api_platform.filter' ] + api_platform.filter.partition.order: parent: 'api_platform.doctrine.orm.order_filter' arguments: diff --git a/src/Command/LoadDefaultCommandsCommand.php b/src/Command/LoadDefaultCommandsCommand.php index 8069f4e..e0f8c72 100644 --- a/src/Command/LoadDefaultCommandsCommand.php +++ b/src/Command/LoadDefaultCommandsCommand.php @@ -79,6 +79,7 @@ class LoadDefaultCommandsCommand extends Command foreach ($commands as $command) { $entity = new \App\Entity\Command(); $entity->setName($command['name']); + $entity->setScript(''); $entity->setEnabled($command['enabled']); $entity->setReadOnly($command['readOnly']); diff --git a/src/Command/Migration/MigrateHardwareAndHardwareProfileCommand.php b/src/Command/Migration/MigrateHardwareAndHardwareProfileCommand.php index e71b417..7e078ac 100644 --- a/src/Command/Migration/MigrateHardwareAndHardwareProfileCommand.php +++ b/src/Command/Migration/MigrateHardwareAndHardwareProfileCommand.php @@ -6,6 +6,7 @@ use App\Entity\Hardware; use App\Entity\HardwareProfile; use App\Entity\HardwareType; use App\Entity\OrganizationalUnit; +use App\Model\OrganizationalUnitTypes; use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\Query\ResultSetMapping; use Doctrine\Persistence\ManagerRegistry; @@ -118,7 +119,11 @@ class MigrateHardwareAndHardwareProfileCommand extends Command $hardwareProfileEntity->setComments($hardwareProfile['perfilhard.comentarios']); } - $migrationId = $hardwareProfile['perfilhard.grupoid'] === 0 ? $hardwareProfile['perfilhard.idcentro'] : $hardwareProfile['perfilhard.grupoid']; + $migrationId = match ($hardwareProfile['perfilhard.grupoid']) { + 0 => OrganizationalUnitTypes::ORGANIZATIONAL_UNIT . '-' . $hardwareProfile['perfilhard.idcentro'], + default => OrganizationalUnitTypes::CLASSROOMS_GROUP . '-' . $hardwareProfile['perfilhard.grupoid'], + }; + $organizationalUnit = $organizationalUnitRepository->findOneBy(['migrationId' => $migrationId]); if ($organizationalUnit){ diff --git a/src/Dto/Output/AbstractOutput.php b/src/Dto/Output/AbstractOutput.php index 93f7e50..1992875 100644 --- a/src/Dto/Output/AbstractOutput.php +++ b/src/Dto/Output/AbstractOutput.php @@ -14,7 +14,7 @@ abstract class AbstractOutput public UuidInterface $uuid; #[ApiProperty(identifier: false)] - #[Groups('default')] + #[Groups(groups: ['default', 'organizational-unit:read:collection:short'])] public int $id; public function __construct(private readonly AbstractEntity $entity) diff --git a/src/Dto/Output/OrganizationalUnitOutput.php b/src/Dto/Output/OrganizationalUnitOutput.php index 7be1acc..9d9ca7c 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"])] + #[Groups(['organizational-unit:read', "client:read", "user:read", 'organizational-unit:read:collection:short'])] public string $name; #[Groups(['organizational-unit:read'])] @@ -38,7 +38,7 @@ final class OrganizationalUnitOutput extends AbstractOutput #[Groups(['organizational-unit:read'])] public ?self $parent = null; - #[Groups(['organizational-unit:read'])] + #[Groups(['organizational-unit:read', 'organizational-unit:read:collection:short'])] public string $path; #[Groups(['organizational-unit:read', "client:read"])] -- 2.40.1 From c32363c974679d5aba81ded72adcc9d2e2309721 Mon Sep 17 00:00:00 2001 From: Manuel Aranda Date: Tue, 8 Oct 2024 12:29:51 +0200 Subject: [PATCH 075/157] Updated Partition and Image. Added UDS endpoints --- config/packages/security.yaml | 2 +- migrations/Version20241008080902.php | 35 +++++++ migrations/Version20241008081013.php | 31 +++++++ migrations/Version20241008092247.php | 31 +++++++ migrations/Version20241008092849.php | 35 +++++++ .../Migration/MigrateImagesCommand.php | 91 +++++++++++++++++++ .../UDS/OrganizationalUnitController.php | 87 ++++++++++++++++++ src/Dto/Input/ClientInput.php | 8 ++ src/Dto/Input/ImageInput.php | 13 +++ src/Dto/Input/PartitionInput.php | 7 ++ src/Dto/Output/ClientOutput.php | 4 + src/Dto/Output/ImageOutput.php | 6 +- src/Entity/Client.php | 15 +++ src/Entity/Image.php | 74 +++++++++++++-- src/Entity/OrganizationalUnit.php | 37 ++++++++ src/Entity/Partition.php | 16 ++++ src/Factory/ClientFactory.php | 1 + src/Factory/ImageFactory.php | 3 +- src/Factory/PartitionFactory.php | 1 + src/OpenApi/OpenApiFactory.php | 84 +++++++++++++++++ src/Service/UDS/UDSClient.php | 5 +- tests/Functional/ImageTest.php | 9 +- tests/Functional/PartitionTest.php | 6 ++ 23 files changed, 588 insertions(+), 13 deletions(-) create mode 100644 migrations/Version20241008080902.php create mode 100644 migrations/Version20241008081013.php create mode 100644 migrations/Version20241008092247.php create mode 100644 migrations/Version20241008092849.php create mode 100644 src/Command/Migration/MigrateImagesCommand.php create mode 100644 src/Controller/UDS/OrganizationalUnitController.php diff --git a/config/packages/security.yaml b/config/packages/security.yaml index 1e9928e..568ba93 100644 --- a/config/packages/security.yaml +++ b/config/packages/security.yaml @@ -29,7 +29,7 @@ security: - { path: ^/$, roles: PUBLIC_ACCESS } # Allows accessing the Swagger UI - { path: ^/docs, roles: PUBLIC_ACCESS } # Allows accessing the Swagger UI docs - { path: ^/auth/login, roles: PUBLIC_ACCESS } - - { path: ^/opengnsys/rest/, roles: PUBLIC_ACCESS } + - { path: ^/opengnsys/rest, roles: PUBLIC_ACCESS } - { path: ^/og-lives/install/webhook, roles: PUBLIC_ACCESS } - { path: ^/auth/refresh, roles: PUBLIC_ACCESS } - { path: ^/, roles: IS_AUTHENTICATED_FULLY } diff --git a/migrations/Version20241008080902.php b/migrations/Version20241008080902.php new file mode 100644 index 0000000..dc61c00 --- /dev/null +++ b/migrations/Version20241008080902.php @@ -0,0 +1,35 @@ +addSql('ALTER TABLE `partition` ADD image_id INT NOT NULL'); + $this->addSql('ALTER TABLE `partition` ADD CONSTRAINT FK_9EB910E43DA5256D FOREIGN KEY (image_id) REFERENCES image (id)'); + $this->addSql('CREATE INDEX IDX_9EB910E43DA5256D ON `partition` (image_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('DROP INDEX IDX_9EB910E43DA5256D ON `partition`'); + $this->addSql('ALTER TABLE `partition` DROP image_id'); + } +} diff --git a/migrations/Version20241008081013.php b/migrations/Version20241008081013.php new file mode 100644 index 0000000..87b726c --- /dev/null +++ b/migrations/Version20241008081013.php @@ -0,0 +1,31 @@ +addSql('ALTER TABLE client ADD maintenance 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 client DROP maintenance'); + } +} diff --git a/migrations/Version20241008092247.php b/migrations/Version20241008092247.php new file mode 100644 index 0000000..033c6bc --- /dev/null +++ b/migrations/Version20241008092247.php @@ -0,0 +1,31 @@ +addSql('ALTER TABLE image CHANGE software_profile_id software_profile_id INT DEFAULT NULL, CHANGE path path VARCHAR(255) DEFAULT NULL, CHANGE type type VARCHAR(255) DEFAULT NULL, CHANGE size size 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 image CHANGE software_profile_id software_profile_id INT NOT NULL, CHANGE path path VARCHAR(255) NOT NULL, CHANGE type type VARCHAR(255) NOT NULL, CHANGE size size INT NOT NULL'); + } +} diff --git a/migrations/Version20241008092849.php b/migrations/Version20241008092849.php new file mode 100644 index 0000000..5c39be2 --- /dev/null +++ b/migrations/Version20241008092849.php @@ -0,0 +1,35 @@ +addSql('ALTER TABLE image ADD organizational_unit_id INT NOT NULL'); + $this->addSql('ALTER TABLE image ADD CONSTRAINT FK_C53D045FFB84408A FOREIGN KEY (organizational_unit_id) REFERENCES organizational_unit (id)'); + $this->addSql('CREATE INDEX IDX_C53D045FFB84408A ON image (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 image DROP FOREIGN KEY FK_C53D045FFB84408A'); + $this->addSql('DROP INDEX IDX_C53D045FFB84408A ON image'); + $this->addSql('ALTER TABLE image DROP organizational_unit_id'); + } +} diff --git a/src/Command/Migration/MigrateImagesCommand.php b/src/Command/Migration/MigrateImagesCommand.php new file mode 100644 index 0000000..17de59c --- /dev/null +++ b/src/Command/Migration/MigrateImagesCommand.php @@ -0,0 +1,91 @@ +doctrine->getManager('og_1'); + + $imagesRepository = $this->entityManager->getRepository(Image::class); + $clientRepository = $this->entityManager->getRepository(Client::class); + $ouRepository = $this->entityManager->getRepository(OrganizationalUnit::class); + + /** Obtener las imágenes de la base de datos antigua **/ + $rsmOperativeSystems = new ResultSetMapping(); + $rsmOperativeSystems->addScalarResult('idimagen', 'idimagen'); + $rsmOperativeSystems->addScalarResult('nombreca', 'nombreca'); + $rsmOperativeSystems->addScalarResult('idcentro', 'centros.idcentro'); + $rsmOperativeSystems->addScalarResult('idordenador', 'ordenadores.idordenador'); + $rsmOperativeSystems->addScalarResult('numdisk', 'numdisk'); + $rsmOperativeSystems->addScalarResult('ruta', 'ruta'); + $rsmOperativeSystems->addScalarResult('numpar', 'numpar'); + $rsmOperativeSystems->addScalarResult('revision', 'revision'); + $rsmOperativeSystems->addScalarResult('descripcion', 'descripcion'); + $rsmOperativeSystems->addScalarResult('comentarios', 'comentarios'); + + $imagesQuery = $oldDatabaseEntityManager->createNativeQuery('SELECT idimagen, nombreca, ruta, ordenadores.idordenador, centros.idcentro, numdisk, numpar, revision, descripcion, imagenes.comentarios FROM imagenes LEFT JOIN ordenadores ON imagenes.idordenador = ordenadores.idordenador LEFT JOIN centros ON imagenes.idcentro = imagenes.idcentro', $rsmOperativeSystems); + $images = $imagesQuery->getResult(); + + /** Imágenes **/ + $output->writeln("IMÁGENES TOTAL: ". count($images)); + + foreach ($images as $image){ + $ouEntity = $ouRepository->findOneBy(['migrationId' => OrganizationalUnitTypes::ORGANIZATIONAL_UNIT.'-'.$image['centros.idcentro']]); + + if (!$ouEntity) { + $output->writeln("No se ha encontrado la OU con id: ". $image['centros.idcentro']); + continue; + } + + $clientEntity = $clientRepository->findOneBy(['migrationId' => $image['ordenadores.idordenador']]); + if(!$clientEntity){ + $output->writeln("No se ha encontrado el cliente con id: ". $image['ordenadores.idordenador']); + continue; + } + + $imageEntity = $imagesRepository->findOneBy(['migrationId' => $image['idimagen']]); + if(!$imageEntity) { + $imageEntity = new Image(); + $imageEntity->setMigrationId((string) $image['idimagen']); + $imageEntity->setName($image['nombreca']); + $imageEntity->setClient($clientEntity); + $imageEntity->setOrganizationalUnit($ouEntity); + $imageEntity->setRevision((string) $image['revision']); + $imageEntity->setDescription($image['descripcion']); + $imageEntity->setComments($image['comentarios']); + } + + $this->entityManager->persist($imageEntity); + } + + $this->entityManager->flush(); + + return Command::SUCCESS; + } +} diff --git a/src/Controller/UDS/OrganizationalUnitController.php b/src/Controller/UDS/OrganizationalUnitController.php new file mode 100644 index 0000000..e68c0e8 --- /dev/null +++ b/src/Controller/UDS/OrganizationalUnitController.php @@ -0,0 +1,87 @@ +entityManager + ->getRepository(OrganizationalUnit::class) + ->findBy(['type' => OrganizationalUnitTypes::ORGANIZATIONAL_UNIT], ['name' => 'ASC']); + + $data = []; + foreach ($organizationalUnits as $center) { + $data[] = [ + 'id' => $center->getId(), + 'name' => $center->getName(), + 'description' => $center->getDescription(), + ]; + } + + return new JsonResponse($data, Response::HTTP_OK); + } + + #[Route('/opengnsys/rest//ous/{centerId}/labs', methods: ['GET'])] + public function getClassrooms(Request $request, int $centerId): JsonResponse + { + $classrooms = $this->entityManager + ->getRepository(OrganizationalUnit::class) + ->findBy([ + 'type' => OrganizationalUnitTypes::CLASSROOM, + 'parent' => $centerId + ], ['name' => 'ASC']); + + $data = []; + + foreach ($classrooms as $classroom) { + $data[] = [ + 'id' => $classroom->getId(), + 'name' => $classroom->getName(), + 'description' => $classroom->getDescription(), + ]; + } + + return new JsonResponse($data, Response::HTTP_OK); + } + + #[Route('/opengnsys/rest//ous/{centerId}/images', methods: ['GET'])] + public function getImages(Request $request, int $centerId): JsonResponse + { + $images = $this->entityManager + ->getRepository(Image::class) + ->findBy([ + 'organizationalUnit' => $centerId, + ], ['name' => 'ASC']); + + $data = []; + + foreach ($images as $image) { + $data[] = [ + 'id' => $image->getId(), + 'name' => $image->getName(), + ]; + } + + return new JsonResponse($data, Response::HTTP_OK); + } +} \ No newline at end of file diff --git a/src/Dto/Input/ClientInput.php b/src/Dto/Input/ClientInput.php index 32abc29..9d3a98f 100644 --- a/src/Dto/Input/ClientInput.php +++ b/src/Dto/Input/ClientInput.php @@ -102,6 +102,13 @@ final class ClientInput )] public ?OgRepository $repository = null; + #[Groups(['client:write'])] + #[ApiProperty( + description: 'descriptions.client.validation' + )] + public ?bool $maintenance = false; + + public function __construct(?Client $client = null) { if (!$client) { @@ -149,6 +156,7 @@ final class ClientInput $client->setHardwareProfile($this->hardwareProfile?->getEntity()); $client->setPosition($this->position); $client->setStatus($this->status); + $client->setMaintenance($this->maintenance); return $client; } diff --git a/src/Dto/Input/ImageInput.php b/src/Dto/Input/ImageInput.php index 10c46a5..7bab121 100644 --- a/src/Dto/Input/ImageInput.php +++ b/src/Dto/Input/ImageInput.php @@ -4,8 +4,10 @@ namespace App\Dto\Input; use ApiPlatform\Metadata\ApiProperty; use App\Dto\Output\ClientOutput; +use App\Dto\Output\OrganizationalUnitOutput; use App\Dto\Output\SoftwareProfileOutput; use App\Entity\Image; +use App\Entity\OrganizationalUnit; use Symfony\Component\Serializer\Annotation\Groups; use Symfony\Component\Validator\Constraints as Assert; @@ -52,6 +54,10 @@ final class ImageInput #[ApiProperty(description: 'The software profile of the image')] public ?SoftwareProfileOutput $softwareProfile = null; + #[Groups(['image:write'])] + #[ApiProperty(description: 'The organizational unit of the image')] + public ?OrganizationalUnitOutput $organizationalUnit = null; + public function __construct(?Image $image = null) { if (!$image) { @@ -68,6 +74,7 @@ final class ImageInput $this->size = $image->getSize(); $this->client = new ClientOutput($image->getClient()); $this->softwareProfile = new SoftwareProfileOutput($image->getSoftwareProfile()); + $this->organizationalUnit = new OrganizationalUnitOutput($image->getOrganizationalUnit()); } public function createOrUpdateEntity(?Image $image = null): Image @@ -87,6 +94,12 @@ final class ImageInput $image->setClient($this->client->getEntity()); $image->setSoftwareProfile($this->softwareProfile->getEntity()); + if ($this->organizationalUnit) { + $image->setOrganizationalUnit($this->organizationalUnit->getEntity()); + } else { + $image->setOrganizationalUnit($this->client->getEntity()->getOrganizationalUnit()); + } + return $image; } } \ No newline at end of file diff --git a/src/Dto/Input/PartitionInput.php b/src/Dto/Input/PartitionInput.php index 03d633a..c7477a5 100644 --- a/src/Dto/Input/PartitionInput.php +++ b/src/Dto/Input/PartitionInput.php @@ -4,6 +4,7 @@ namespace App\Dto\Input; use ApiPlatform\Metadata\ApiProperty; use App\Dto\Output\ClientOutput; +use App\Dto\Output\ImageOutput; use App\Dto\Output\OperativeSystemOutput; use App\Dto\Output\OrganizationalUnitOutput; use App\Entity\HardwareProfile; @@ -54,6 +55,10 @@ final class PartitionInput #[ApiProperty(description: 'The memory usage of the partition', example: 100)] public ?int $memoryUsage = null; + #[Groups(['partition:write'])] + #[ApiProperty(description: 'The image of the partition')] + public ?ImageOutput $image = null; + public function __construct(?Partition $partition = null) { if (!$partition) { @@ -69,6 +74,7 @@ final class PartitionInput $this->operativeSystem = new OperativeSystemOutput($partition->getOperativeSystem()); $this->client = new ClientOutput($partition->getClient()); $this->memoryUsage = $partition->getMemoryUsage(); + $this->image = new ImageOutput($partition->getImage()); } public function createOrUpdateEntity(?Partition $partition = null): Partition @@ -86,6 +92,7 @@ final class PartitionInput $partition->setOperativeSystem($this->operativeSystem->getEntity()); $partition->setClient($this->client->getEntity()); $partition->setMemoryUsage($this->memoryUsage * 100); + $partition->setImage($this->image->getEntity()); return $partition; } diff --git a/src/Dto/Output/ClientOutput.php b/src/Dto/Output/ClientOutput.php index 78ee199..a43f212 100644 --- a/src/Dto/Output/ClientOutput.php +++ b/src/Dto/Output/ClientOutput.php @@ -64,6 +64,9 @@ final class ClientOutput extends AbstractOutput #[Groups(['client:read'])] public ?string $createdBy = null; + #[Groups(['client:read'])] + public ?bool $maintenance = false; + public function __construct(Client $client) { parent::__construct($client); @@ -90,5 +93,6 @@ final class ClientOutput extends AbstractOutput $this->status = $client->getStatus(); $this->createdAt = $client->getCreatedAt(); $this->createdBy = $client->getCreatedBy(); + $this->maintenance = $client->isMaintenance(); } } \ No newline at end of file diff --git a/src/Dto/Output/ImageOutput.php b/src/Dto/Output/ImageOutput.php index f14e16a..acbdff7 100644 --- a/src/Dto/Output/ImageOutput.php +++ b/src/Dto/Output/ImageOutput.php @@ -19,7 +19,7 @@ final class ImageOutput extends AbstractOutput public ?string $comments = ''; #[Groups(['image:read'])] - public ?string $type = ''; + public ?string $type = null; #[Groups(['image:read'])] public ?string $path = ''; @@ -39,6 +39,9 @@ final class ImageOutput extends AbstractOutput #[Groups(['image:read'])] public ?SoftwareProfileOutput $softwareProfile = null; + #[Groups(['image:read'])] + public ?OrganizationalUnitOutput $organizationalUnit = null; + public function __construct(Image $image) { parent::__construct($image); @@ -53,5 +56,6 @@ final class ImageOutput extends AbstractOutput $this->size = $image->getSize(); $this->clientOutput = new ClientOutput($image->getClient()); $this->softwareProfile = new SoftwareProfileOutput($image->getSoftwareProfile()); + $this->organizationalUnit = new OrganizationalUnitOutput($image->getOrganizationalUnit()); } } \ No newline at end of file diff --git a/src/Entity/Client.php b/src/Entity/Client.php index 0c7a20f..4c4a2ce 100644 --- a/src/Entity/Client.php +++ b/src/Entity/Client.php @@ -68,6 +68,9 @@ class Client extends AbstractEntity #[ORM\ManyToOne(inversedBy: 'clients')] private ?OgLive $ogLive = null; + #[ORM\Column] + private ?bool $maintenance = null; + public function __construct() { parent::__construct(); @@ -283,4 +286,16 @@ class Client extends AbstractEntity return $this; } + + public function isMaintenance(): ?bool + { + return $this->maintenance; + } + + public function setMaintenance(bool $maintenance): static + { + $this->maintenance = $maintenance; + + return $this; + } } diff --git a/src/Entity/Image.php b/src/Entity/Image.php index 5d41052..bd25a0a 100644 --- a/src/Entity/Image.php +++ b/src/Entity/Image.php @@ -3,6 +3,8 @@ namespace App\Entity; use App\Repository\ImageRepository; +use Doctrine\Common\Collections\ArrayCollection; +use Doctrine\Common\Collections\Collection; use Doctrine\ORM\Mapping as ORM; #[ORM\Entity(repositoryClass: ImageRepository::class)] @@ -16,10 +18,10 @@ class Image extends AbstractEntity #[ORM\Column(length: 255, nullable: true)] private ?string $comments = null; - #[ORM\Column(length: 255)] + #[ORM\Column(length: 255, nullable: true)] private ?string $path = null; - #[ORM\Column(length: 255)] + #[ORM\Column(length: 255, nullable: true)] private ?string $type = null; #[ORM\Column(length: 255, nullable: true)] @@ -28,7 +30,7 @@ class Image extends AbstractEntity #[ORM\Column(length: 255, nullable: true)] private ?string $info = null; - #[ORM\Column] + #[ORM\Column(length: 255, nullable: true)] private ?int $size = null; #[ORM\ManyToOne] @@ -36,9 +38,25 @@ class Image extends AbstractEntity private ?Client $client = null; #[ORM\ManyToOne] - #[ORM\JoinColumn(nullable: false)] + #[ORM\JoinColumn(nullable: true)] private ?SoftwareProfile $softwareProfile = null; + /** + * @var Collection + */ + #[ORM\OneToMany(mappedBy: 'image', targetEntity: Partition::class)] + private Collection $partitions; + + #[ORM\ManyToOne(inversedBy: 'images')] + #[ORM\JoinColumn(nullable: false)] + private ?OrganizationalUnit $organizationalUnit = null; + + public function __construct() + { + parent::__construct(); + $this->partitions = new ArrayCollection(); + } + public function getDescription(): ?string { return $this->description; @@ -68,7 +86,7 @@ class Image extends AbstractEntity return $this->path; } - public function setPath(string $path): static + public function setPath(?string $path): static { $this->path = $path; @@ -80,7 +98,7 @@ class Image extends AbstractEntity return $this->type; } - public function setType(string $type): static + public function setType(?string $type): static { $this->type = $type; @@ -116,7 +134,7 @@ class Image extends AbstractEntity return $this->size; } - public function setSize(int $size): static + public function setSize(?int $size): static { $this->size = $size; @@ -146,4 +164,46 @@ class Image extends AbstractEntity return $this; } + + /** + * @return Collection + */ + public function getPartitions(): Collection + { + return $this->partitions; + } + + public function addPartition(Partition $partition): static + { + if (!$this->partitions->contains($partition)) { + $this->partitions->add($partition); + $partition->setImage($this); + } + + return $this; + } + + public function removePartition(Partition $partition): static + { + if ($this->partitions->removeElement($partition)) { + // set the owning side to null (unless already changed) + if ($partition->getImage() === $this) { + $partition->setImage(null); + } + } + + return $this; + } + + public function getOrganizationalUnit(): ?OrganizationalUnit + { + return $this->organizationalUnit; + } + + public function setOrganizationalUnit(?OrganizationalUnit $organizationalUnit): static + { + $this->organizationalUnit = $organizationalUnit; + + return $this; + } } diff --git a/src/Entity/OrganizationalUnit.php b/src/Entity/OrganizationalUnit.php index b0d5596..a7d2756 100644 --- a/src/Entity/OrganizationalUnit.php +++ b/src/Entity/OrganizationalUnit.php @@ -97,6 +97,12 @@ class OrganizationalUnit extends AbstractEntity #[ORM\Column] private ?bool $reserved = false; + /** + * @var Collection + */ + #[ORM\OneToMany(mappedBy: 'organizationalUnit', targetEntity: Image::class)] + private Collection $images; + public function __construct() { parent::__construct(); @@ -104,6 +110,7 @@ class OrganizationalUnit extends AbstractEntity $this->users = new ArrayCollection(); $this->clients = new ArrayCollection(); $this->softwareProfiles = new ArrayCollection(); + $this->images = new ArrayCollection(); } public function getDescription(): ?string @@ -429,4 +436,34 @@ class OrganizationalUnit extends AbstractEntity return $this; } + + /** + * @return Collection + */ + public function getImages(): Collection + { + return $this->images; + } + + public function addImage(Image $image): static + { + if (!$this->images->contains($image)) { + $this->images->add($image); + $image->setOrganizationalUnit($this); + } + + return $this; + } + + public function removeImage(Image $image): static + { + if ($this->images->removeElement($image)) { + // set the owning side to null (unless already changed) + if ($image->getOrganizationalUnit() === $this) { + $image->setOrganizationalUnit(null); + } + } + + return $this; + } } diff --git a/src/Entity/Partition.php b/src/Entity/Partition.php index 7795315..d96409b 100644 --- a/src/Entity/Partition.php +++ b/src/Entity/Partition.php @@ -37,6 +37,10 @@ class Partition extends AbstractEntity #[ORM\ManyToOne(inversedBy: 'partitions')] private ?OperativeSystem $operativeSystem = null; + #[ORM\ManyToOne(inversedBy: 'partitions')] + #[ORM\JoinColumn(nullable: false)] + private ?Image $image = null; + public function getDiskNumber(): ?int { return $this->diskNumber; @@ -144,4 +148,16 @@ class Partition extends AbstractEntity return $this; } + + public function getImage(): ?Image + { + return $this->image; + } + + public function setImage(?Image $image): static + { + $this->image = $image; + + return $this; + } } diff --git a/src/Factory/ClientFactory.php b/src/Factory/ClientFactory.php index 56e28da..b8f4b89 100644 --- a/src/Factory/ClientFactory.php +++ b/src/Factory/ClientFactory.php @@ -32,6 +32,7 @@ final class ClientFactory extends ModelFactory 'createdAt' => self::faker()->dateTime(), 'name' => self::faker()->text(255), 'updatedAt' => self::faker()->dateTime(), + 'maintenance' => self::faker()->boolean(), ]; } diff --git a/src/Factory/ImageFactory.php b/src/Factory/ImageFactory.php index 3a4962d..7e068af 100644 --- a/src/Factory/ImageFactory.php +++ b/src/Factory/ImageFactory.php @@ -3,6 +3,7 @@ namespace App\Factory; use App\Entity\Image; +use App\Model\OrganizationalUnitTypes; use App\Repository\ImageRepository; use Zenstruck\Foundry\ModelFactory; use Zenstruck\Foundry\Persistence\PersistentProxyObjectFactory; @@ -38,7 +39,7 @@ final class ImageFactory extends ModelFactory 'path' => self::faker()->text(255), 'size' => self::faker()->randomNumber(), 'softwareProfile' => SoftwareProfileFactory::new(), - 'type' => self::faker()->text(255), + 'organizationalUnit' => OrganizationalUnitFactory::createOne(['type' => OrganizationalUnitTypes::ORGANIZATIONAL_UNIT])->_save(), 'updatedAt' => self::faker()->dateTime(), ]; } diff --git a/src/Factory/PartitionFactory.php b/src/Factory/PartitionFactory.php index bb3d736..d791b7c 100644 --- a/src/Factory/PartitionFactory.php +++ b/src/Factory/PartitionFactory.php @@ -43,6 +43,7 @@ final class PartitionFactory extends ModelFactory 'updatedAt' => self::faker()->dateTime(), 'operativeSystem' => OperativeSystemFactory::new(), 'client' => ClientFactory::new(), + 'image' => ImageFactory::new(), ]; } diff --git a/src/OpenApi/OpenApiFactory.php b/src/OpenApi/OpenApiFactory.php index 8889728..76165c1 100644 --- a/src/OpenApi/OpenApiFactory.php +++ b/src/OpenApi/OpenApiFactory.php @@ -20,6 +20,7 @@ final readonly class OpenApiFactory implements OpenApiFactoryInterface $this->addRefreshToken($openApi); $this->addOgAgentEndpoints($openApi); + $this->addUDsEndpoints($openApi); $this->addStatusEndpoint($openApi); $this->addInstallOgLiveWebhookEndpoint($openApi); @@ -704,6 +705,89 @@ final readonly class OpenApiFactory implements OpenApiFactoryInterface )); } + private function addUDsEndpoints(OpenApi $openApi): void + { + $openApi->getPaths()->addPath('/opengnsys/rest//ous', (new Model\PathItem())->withGet( + (new Model\Operation('getOUs')) + ->withTags(['Organizational Unit']) + ->withSummary('Obtener todas las Unidades Organizacionales') + ->withResponses([ + Response::HTTP_OK => [ + 'description' => 'Lista de Unidades Organizacionales', + 'content' => [ + 'application/json' => [ + 'schema' => [ + 'type' => 'array', + 'items' => [ + 'type' => 'object', + 'properties' => [ + 'id' => [ + 'type' => 'integer', + 'example' => 1, + ], + 'name' => [ + 'type' => 'string', + 'example' => 'Nombre de la Unidad Organizacional', + ], + 'description' => [ + 'type' => 'string', + 'example' => 'Descripción de la Unidad Organizacional', + ] + ] + ] + ] + ] + ] + ] + ]) + )); + + $openApi->getPaths()->addPath('/opengnsys/rest//ous/{centerId}/labs', (new Model\PathItem())->withGet( + (new Model\Operation('getClassrooms')) + ->withTags(['Organizational Unit']) + ->withSummary('Obtener los laboratorios de una Unidad Organizacional específica') + ->withParameters([ + new Model\Parameter( + 'centerId', + 'path', + 'El ID de la Unidad Organizacional', + true, + false, + ) + ]) + ->withResponses([ + Response::HTTP_OK => [ + 'description' => 'Lista de Laboratorios', + 'content' => [ + 'application/json' => [ + 'schema' => [ + 'type' => 'array', + 'items' => [ + 'type' => 'object', + 'properties' => [ + 'id' => [ + 'type' => 'integer', + 'example' => 1, + ], + 'name' => [ + 'type' => 'string', + 'example' => 'Nombre del Laboratorio', + ], + 'description' => [ + 'type' => 'string', + 'example' => 'Descripción del Laboratorio', + ] + ] + ] + ] + ] + ] + ] + ]) + )); + } + + private function addInstallOgLiveWebhookEndpoint(OpenApi $openApi): void { $openApi diff --git a/src/Service/UDS/UDSClient.php b/src/Service/UDS/UDSClient.php index 071cc56..23f9286 100644 --- a/src/Service/UDS/UDSClient.php +++ b/src/Service/UDS/UDSClient.php @@ -47,7 +47,7 @@ class UDSClient continue; } - $classroom = $servicePoolInfo['ou']; + $classroom = $servicePoolInfo['lab']; $maxAvailableSeats = $this->getMaxAvailableSeats($classroom); $servicePool['max_srvs'] = $maxAvailableSeats; $servicePool['osmanager_id'] = null; @@ -156,6 +156,9 @@ class UDSClient $totalAvailableClients = 0; foreach ($organizationalUnit->getClients() as $client) { + if ($client->isMaintenance()) { + continue; + } $status = $client->getStatus(); $clientAvailable = in_array($status, ['active', 'windows', 'linux']); diff --git a/tests/Functional/ImageTest.php b/tests/Functional/ImageTest.php index b4e95fc..93eb45d 100644 --- a/tests/Functional/ImageTest.php +++ b/tests/Functional/ImageTest.php @@ -8,8 +8,10 @@ use App\Entity\OrganizationalUnit; use App\Entity\SoftwareProfile; use App\Factory\ClientFactory; use App\Factory\ImageFactory; +use App\Factory\OrganizationalUnitFactory; use App\Factory\SoftwareProfileFactory; use App\Factory\UserFactory; +use App\Model\OrganizationalUnitTypes; use App\Model\UserGroupPermissions; use Symfony\Component\HttpFoundation\Response; use Symfony\Contracts\HttpClient\Exception\ClientExceptionInterface; @@ -68,13 +70,16 @@ class ImageTest extends AbstractTest SoftwareProfileFactory::createOne(['description' => self::SOFTWARE_PROFILE]); $swPIri = $this->findIriBy(SoftwareProfile::class, ['description' => self::SOFTWARE_PROFILE]); + OrganizationalUnitFactory::createOne(['type' => OrganizationalUnitTypes::ORGANIZATIONAL_UNIT]); + $ouIri = $this->findIriBy(OrganizationalUnit::class, ['type' => OrganizationalUnitTypes::ORGANIZATIONAL_UNIT]); + $this->createClientWithCredentials()->request('POST', '/images',['json' => [ 'name' => self::IMAGE_CREATE, 'size' => 123, 'path' => '/path/to/image', - 'type' => 'type', 'client' => $clientIri, - 'softwareProfile' => $swPIri + 'softwareProfile' => $swPIri, + 'organizationalUnit' => $ouIri, ]]); $this->assertResponseStatusCodeSame(201); diff --git a/tests/Functional/PartitionTest.php b/tests/Functional/PartitionTest.php index 39debe4..e587484 100644 --- a/tests/Functional/PartitionTest.php +++ b/tests/Functional/PartitionTest.php @@ -3,11 +3,13 @@ namespace Functional; use App\Entity\Client; +use App\Entity\Image; use App\Entity\Menu; use App\Entity\OperativeSystem; use App\Entity\Partition; use App\Factory\ClientFactory; use App\Factory\HardwareProfileFactory; +use App\Factory\ImageFactory; use App\Factory\MenuFactory; use App\Factory\OperativeSystemFactory; use App\Factory\OrganizationalUnitFactory; @@ -73,9 +75,13 @@ class PartitionTest extends AbstractTest OperativeSystemFactory::createOne(['name' => 'Ubuntu']); $osIri = $this->findIriBy(OperativeSystem::class, ['name' => 'Ubuntu']); + ImageFactory::createOne(['name' => 'Image 1']); + $imageIri = $this->findIriBy(Image::class, ['name' => 'Image 1']); + $this->createClientWithCredentials()->request('POST', '/partitions',['json' => [ 'size' => 100, 'operativeSystem' => $osIri, + 'image' => $imageIri, 'client' => $iri, 'memoryUsage' => 100 ]]); -- 2.40.1 From 7c9032547140aff2ab45f529f7c2fea52f988f8d Mon Sep 17 00:00:00 2001 From: Manuel Aranda Date: Tue, 8 Oct 2024 12:32:16 +0200 Subject: [PATCH 076/157] Added MigrateImage in readme --- README.md | 2 +- src/Command/Migration/MigrateClientsCommand.php | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index e8fc13a..0078882 100644 --- a/README.md +++ b/README.md @@ -133,7 +133,7 @@ docker exec ogcore-php php bin/console opengnsys:migration:organizational-unit # docker exec ogcore-php php bin/console opengnsys:migration:hardware-profile #cargamos los perfiles de hardware docker exec ogcore-php php bin/console opengnsys:migration:clients #cargamos los clientes docker exec ogcore-php php bin/console opengnsys:migration:os #cargamos los sistemas operativos -docker exec ogcore-php php bin/console opengnsys:migration:partition #cargamos las particiones +docker exec ogcore-php php bin/console opengnsys:migration:image #cargamos las imagenes ``` ## Objetos de interés diff --git a/src/Command/Migration/MigrateClientsCommand.php b/src/Command/Migration/MigrateClientsCommand.php index f4c2fc1..cee6b8e 100644 --- a/src/Command/Migration/MigrateClientsCommand.php +++ b/src/Command/Migration/MigrateClientsCommand.php @@ -63,6 +63,7 @@ class MigrateClientsCommand extends Command $clientEntity->setNetdriver($client['netdriver']); $clientEntity->setMac($client['mac']); $clientEntity->setIp($client['ip']); + $clientEntity->setMaintenance(false); $clientEntity->setPosition(['x' => 0, 'y' => 0]); } -- 2.40.1 From 37b21c85a44813752a95459ea3e1938991c0fa52 Mon Sep 17 00:00:00 2001 From: Manuel Aranda Date: Tue, 8 Oct 2024 16:20:43 +0200 Subject: [PATCH 077/157] Added TLS docker --- docker-compose.yaml | 2 ++ docker/Dockerfile-nginx | 7 ++++++- docker/default.conf | 17 ++++++++++++++--- entrypoint.sh | 6 ++++++ 4 files changed, 28 insertions(+), 4 deletions(-) create mode 100644 entrypoint.sh diff --git a/docker-compose.yaml b/docker-compose.yaml index aad1b74..8a5bd62 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -23,8 +23,10 @@ services: - php ports: - 8080:80 + - 8443:443 # Añadir el puerto 443 volumes: - ./public:/var/www/html/public:cached + - ./docker/certs:/etc/nginx/certs # Montar certificados en Nginx networks: - ogcore-network diff --git a/docker/Dockerfile-nginx b/docker/Dockerfile-nginx index d8d753a..7c8881b 100644 --- a/docker/Dockerfile-nginx +++ b/docker/Dockerfile-nginx @@ -1,2 +1,7 @@ FROM nginx:latest -COPY ./docker/default.conf /etc/nginx/conf.d/default.conf \ No newline at end of file + +# Copiar el archivo de configuración de Nginx +COPY ./docker/default.conf /etc/nginx/conf.d/default.conf + +# Copiar los certificados SSL +COPY ./docker/certs /etc/nginx/certs diff --git a/docker/default.conf b/docker/default.conf index 7d7fc2c..0e0059d 100644 --- a/docker/default.conf +++ b/docker/default.conf @@ -1,10 +1,21 @@ server { listen 80; server_name localhost; + + # Redirigir todo el tráfico HTTP a HTTPS + return 301 https://$host$request_uri; +} + +server { + listen 443 ssl; + server_name localhost; root /var/www/html/public; index index.html index.php; - location / { + ssl_certificate /etc/nginx/certs/server.crt; # Ruta al certificado + ssl_certificate_key /etc/nginx/certs/server.key; # Ruta a la clave + + location / { try_files $uri $uri/ /index.php?$args; } @@ -13,7 +24,7 @@ server { fastcgi_pass php:9000; fastcgi_split_path_info ^(.+\.php)(/.*)$; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; - fastcgi_param PATH_INFO $fastcgi_path_info; + fastcgi_param PATH_INFO $request_uri; fastcgi_param PATH_TRANSLATED $document_root$fastcgi_script_name; } @@ -23,4 +34,4 @@ server { error_log /var/log/nginx/error.log; access_log /var/log/nginx/access.log; -} \ No newline at end of file +} diff --git a/entrypoint.sh b/entrypoint.sh new file mode 100644 index 0000000..51159ab --- /dev/null +++ b/entrypoint.sh @@ -0,0 +1,6 @@ +#!/bin/sh + +# Instalar certificados CA de Symfony si no existen +if [ ! -f /root/.symfony*/cacert.pem ]; then + symfony server:ca:install +fi -- 2.40.1 From 1e2947b183067859a926f2f01261863753de9395 Mon Sep 17 00:00:00 2001 From: Manuel Aranda Date: Tue, 8 Oct 2024 16:39:10 +0200 Subject: [PATCH 078/157] Added UDS info endpoint --- .../UDS/OrganizationalUnitController.php | 28 ++++++++++++++++++ src/OpenApi/OpenApiFactory.php | 29 ++++++++++++++++++- 2 files changed, 56 insertions(+), 1 deletion(-) diff --git a/src/Controller/UDS/OrganizationalUnitController.php b/src/Controller/UDS/OrganizationalUnitController.php index e68c0e8..4657bdf 100644 --- a/src/Controller/UDS/OrganizationalUnitController.php +++ b/src/Controller/UDS/OrganizationalUnitController.php @@ -84,4 +84,32 @@ class OrganizationalUnitController extends AbstractController return new JsonResponse($data, Response::HTTP_OK); } + + #[Route('/opengnsys/rest//info', methods: ['GET'])] + public function getOpengnsysInfo(): JsonResponse + { + $data = [ + "project" => "OpenGnsys", + "version" => "1.1.1d", + "codename" => "Espeto", + "definition" => "http://www.andalucia.com/gastronomy/espeto.htm", + "release" => "", + "ogagent" => "1.1.2", + "services" => [ + "server", + "repository" + ], + "oglive" => + [ + "distribution" => "focal", + "kernel" => "5.13.0-27-beta", + "architecture" => "amd64", + "revision" => "r20210706", + "directory" => "ogLive-5.13.0-r20210706", + "iso" => "ogLive-focal-5.13.0-27-beta-amd64-r20210706.5b4bf5f.iso" + ] + ]; + + return new JsonResponse($data, Response::HTTP_OK); + } } \ No newline at end of file diff --git a/src/OpenApi/OpenApiFactory.php b/src/OpenApi/OpenApiFactory.php index 76165c1..5587cd3 100644 --- a/src/OpenApi/OpenApiFactory.php +++ b/src/OpenApi/OpenApiFactory.php @@ -785,8 +785,35 @@ final readonly class OpenApiFactory implements OpenApiFactoryInterface ] ]) )); - } + $openApi->getPaths()->addPath('/opengnsys/rest//info', (new Model\PathItem())->withGet( + (new Model\Operation('getOpengnsysInfo')) + ->withTags(['General']) + ->withSummary('Obtener información general de OpenGnsys') + ->withResponses([ + Response::HTTP_OK => [ + 'description' => 'Información general de OpenGnsys', + 'content' => [ + 'application/json' => [ + 'schema' => [ + 'type' => 'object', + 'properties' => [ + 'project' => [ + 'type' => 'string', + 'example' => 'OpenGnsys', + ], + 'version' => [ + 'type' => 'string', + 'example' => '1.1.1d', + ], + ], + ], + ], + ], + ] + ]) + )); + } private function addInstallOgLiveWebhookEndpoint(OpenApi $openApi): void { -- 2.40.1 From d3c43f4f81279c45becb3612a2b25ed4b9010804 Mon Sep 17 00:00:00 2001 From: Manuel Aranda Date: Tue, 8 Oct 2024 17:00:12 +0200 Subject: [PATCH 079/157] Added default command loader OUs --- .../LoadOrganizationalUnitDefaultCommand.php | 144 ++++++++++++++++++ 1 file changed, 144 insertions(+) create mode 100644 src/Command/LoadOrganizationalUnitDefaultCommand.php diff --git a/src/Command/LoadOrganizationalUnitDefaultCommand.php b/src/Command/LoadOrganizationalUnitDefaultCommand.php new file mode 100644 index 0000000..42ed7f4 --- /dev/null +++ b/src/Command/LoadOrganizationalUnitDefaultCommand.php @@ -0,0 +1,144 @@ + 'Universidad Test', + 'type' => OrganizationalUnitTypes::ORGANIZATIONAL_UNIT + ] + ]; + + $classrooms = [ + [ + 'name' => 'Aula Test 1', + 'type' => OrganizationalUnitTypes::CLASSROOM + ], + [ + 'name' => 'Aula Test 2', + 'type' => OrganizationalUnitTypes::CLASSROOM + ], + [ + 'name' => 'Aula Test 3', + 'type' => OrganizationalUnitTypes::CLASSROOM + ] + ]; + + $clientsA = [ + [ + 'name' => 'Cliente Test 1a', + 'ip' => '1.1.1.1', + 'mac' => '00:1A:2B:3C:4D:5E', + ], + [ + 'name' => 'Cliente Test 1b', + 'ip' => '2.2.2.2', + 'mac' => '00:1B:3C:4D:5E:6F' + ], + [ + 'name' => 'Cliente Test 1c', + 'ip' => '3.3.3.3', + 'mac' => '00:1C:4D:5E:6F:7A' + ] + ]; + + $clientsB = [ + [ + 'name' => 'Cliente Test 2a', + 'ip' => '4.4.4.4', + 'mac' => '00:1D:5E:6F:7A:8B' + ], + [ + 'name' => 'Cliente Test 2b', + 'ip' => '5.5.5.5', + 'mac' => '00:1E:6F:7A:8B:9C' + ], + [ + 'name' => 'Cliente Test 2c', + 'ip' => '6.6.6.6', + 'mac' => '00:1F:7A:8B:9C:AD' + ], + ]; + + $clientsC = [ + [ + 'name' => 'Cliente Test 3a', + 'ip' => '7.7.7.7.', + 'mac' => '00:2A:8B:9C:AD:BE' + ], + [ + 'name' => 'Cliente Test 3b', + 'ip' => '8.8.8.8', + 'mac' => '00:2B:9C:AD:BE:CF' + ], + [ + 'name' => 'Cliente Test 3c', + 'ip' => '9.9.9.9', + 'mac' => '00:2C:AD:BE:CF:D0' + ] + ]; + + + foreach ($organizationalUnits as $organizationalUnit) { + $organizationalUnitEntity = new OrganizationalUnit(); + $organizationalUnitEntity->setName($organizationalUnit['name']); + $organizationalUnitEntity->setType($organizationalUnit['type']); + + $this->entityManager->persist($organizationalUnitEntity); + + foreach ($classrooms as $classroom) { + $classroomEntity = new OrganizationalUnit(); + $classroomEntity->setName($classroom['name']); + $classroomEntity->setType($classroom['type']); + $classroomEntity->setParent($organizationalUnitEntity); + $this->entityManager->persist($classroomEntity); + + + if ($classroomEntity->getName() === 'Aula Test 1') { + $clients = $clientsA; + } elseif ($classroomEntity->getName() === 'Aula Test 2') { + $clients = $clientsB; + } elseif ($classroomEntity->getName() === 'Aula Test 3') { + $clients = $clientsC; + } + + foreach ($clients as $client) { + $clientEntity = new Client(); + $clientEntity->setName($client['name']); + $clientEntity->setIp($client['ip']); + $clientEntity->setMac($client['mac']); + $clientEntity->setMaintenance(false); + $clientEntity->setOrganizationalUnit($classroomEntity); + $this->entityManager->persist($clientEntity); + + } + } + + $this->entityManager->flush(); + } + + return Command::SUCCESS; + } +} -- 2.40.1 From 29a222bc965c3c6a405d6f0829ac7e06230726b7 Mon Sep 17 00:00:00 2001 From: Manuel Aranda Date: Tue, 8 Oct 2024 17:04:11 +0200 Subject: [PATCH 080/157] Updated Open APi docu --- .../LoadOrganizationalUnitDefaultCommand.php | 1 - src/OpenApi/OpenApiFactory.php | 48 ++++++++++++++++++- 2 files changed, 46 insertions(+), 3 deletions(-) diff --git a/src/Command/LoadOrganizationalUnitDefaultCommand.php b/src/Command/LoadOrganizationalUnitDefaultCommand.php index 42ed7f4..57ecae8 100644 --- a/src/Command/LoadOrganizationalUnitDefaultCommand.php +++ b/src/Command/LoadOrganizationalUnitDefaultCommand.php @@ -132,7 +132,6 @@ class LoadOrganizationalUnitDefaultCommand extends Command $clientEntity->setMaintenance(false); $clientEntity->setOrganizationalUnit($classroomEntity); $this->entityManager->persist($clientEntity); - } } diff --git a/src/OpenApi/OpenApiFactory.php b/src/OpenApi/OpenApiFactory.php index 5587cd3..61763a5 100644 --- a/src/OpenApi/OpenApiFactory.php +++ b/src/OpenApi/OpenApiFactory.php @@ -744,7 +744,7 @@ final readonly class OpenApiFactory implements OpenApiFactoryInterface $openApi->getPaths()->addPath('/opengnsys/rest//ous/{centerId}/labs', (new Model\PathItem())->withGet( (new Model\Operation('getClassrooms')) - ->withTags(['Organizational Unit']) + ->withTags(['UDS']) ->withSummary('Obtener los laboratorios de una Unidad Organizacional específica') ->withParameters([ new Model\Parameter( @@ -786,9 +786,53 @@ final readonly class OpenApiFactory implements OpenApiFactoryInterface ]) )); + $openApi->getPaths()->addPath('/opengnsys/rest//ous/{centerId}/images', (new Model\PathItem())->withGet( + (new Model\Operation('getImages')) + ->withTags(['UDS']) + ->withSummary('Obtener las imágenes de una Unidad Organizacional específica') + ->withParameters([ + new Model\Parameter( + 'centerId', + 'path', + 'El ID de la Unidad Organizacional', + true, + false, + ) + ]) + ->withResponses([ + Response::HTTP_OK => [ + 'description' => 'Lista de Imagenes', + 'content' => [ + 'application/json' => [ + 'schema' => [ + 'type' => 'array', + 'items' => [ + 'type' => 'object', + 'properties' => [ + 'id' => [ + 'type' => 'integer', + 'example' => 1, + ], + 'name' => [ + 'type' => 'string', + 'example' => 'Nombre del Laboratorio', + ], + 'description' => [ + 'type' => 'string', + 'example' => 'Descripción del Laboratorio', + ] + ] + ] + ] + ] + ] + ] + ]) + )); + $openApi->getPaths()->addPath('/opengnsys/rest//info', (new Model\PathItem())->withGet( (new Model\Operation('getOpengnsysInfo')) - ->withTags(['General']) + ->withTags(['UDS']) ->withSummary('Obtener información general de OpenGnsys') ->withResponses([ Response::HTTP_OK => [ -- 2.40.1 From 07b6f55310a8a1690a40be28108eb61c8dc7b795 Mon Sep 17 00:00:00 2001 From: Manuel Aranda Date: Tue, 8 Oct 2024 17:27:48 +0200 Subject: [PATCH 081/157] Added image loader --- .../LoadOrganizationalUnitDefaultCommand.php | 29 +++++++++++++++++++ .../UDS/OrganizationalUnitController.php | 8 +++-- src/OpenApi/OpenApiFactory.php | 2 +- 3 files changed, 36 insertions(+), 3 deletions(-) diff --git a/src/Command/LoadOrganizationalUnitDefaultCommand.php b/src/Command/LoadOrganizationalUnitDefaultCommand.php index 57ecae8..673e6da 100644 --- a/src/Command/LoadOrganizationalUnitDefaultCommand.php +++ b/src/Command/LoadOrganizationalUnitDefaultCommand.php @@ -5,6 +5,7 @@ declare(strict_types=1); namespace App\Command; use App\Entity\Client; +use App\Entity\Image; use App\Entity\OrganizationalUnit; use App\Model\OrganizationalUnitTypes; use App\Model\UserGroupPermissions; @@ -100,6 +101,24 @@ class LoadOrganizationalUnitDefaultCommand extends Command ] ]; + $images = [ + [ + 'name' => 'Imagen Test 1', + 'type' => 'ISO', + 'path' => '/path/to/imagen1.iso' + ], + [ + 'name' => 'Imagen Test 2', + 'type' => 'ISO', + 'path' => '/path/to/imagen2.iso' + ], + [ + 'name' => 'Imagen Test 3', + 'type' => 'ISO', + 'path' => '/path/to/imagen3.iso' + ] + ]; + foreach ($organizationalUnits as $organizationalUnit) { $organizationalUnitEntity = new OrganizationalUnit(); @@ -132,6 +151,16 @@ class LoadOrganizationalUnitDefaultCommand extends Command $clientEntity->setMaintenance(false); $clientEntity->setOrganizationalUnit($classroomEntity); $this->entityManager->persist($clientEntity); + + foreach ($images as $image) { + $imageEntity = new Image(); + $imageEntity->setName($image['name']); + $imageEntity->setType($image['type']); + $imageEntity->setPath($image['path']); + $imageEntity->setOrganizationalUnit($organizationalUnitEntity); + $imageEntity->setClient($clientEntity); + $this->entityManager->persist($imageEntity); + } } } diff --git a/src/Controller/UDS/OrganizationalUnitController.php b/src/Controller/UDS/OrganizationalUnitController.php index 4657bdf..3b1f502 100644 --- a/src/Controller/UDS/OrganizationalUnitController.php +++ b/src/Controller/UDS/OrganizationalUnitController.php @@ -67,10 +67,14 @@ class OrganizationalUnitController extends AbstractController #[Route('/opengnsys/rest//ous/{centerId}/images', methods: ['GET'])] public function getImages(Request $request, int $centerId): JsonResponse { + $parent = $this->entityManager + ->getRepository(OrganizationalUnit::class) + ->find($centerId); + $images = $this->entityManager ->getRepository(Image::class) ->findBy([ - 'organizationalUnit' => $centerId, + 'organizationalUnit' => $parent, ], ['name' => 'ASC']); $data = []; @@ -86,7 +90,7 @@ class OrganizationalUnitController extends AbstractController } #[Route('/opengnsys/rest//info', methods: ['GET'])] - public function getOpengnsysInfo(): JsonResponse + public function getOpengnsysInfo(Request $request, int $centerId): JsonResponse { $data = [ "project" => "OpenGnsys", diff --git a/src/OpenApi/OpenApiFactory.php b/src/OpenApi/OpenApiFactory.php index 61763a5..0d53a84 100644 --- a/src/OpenApi/OpenApiFactory.php +++ b/src/OpenApi/OpenApiFactory.php @@ -709,7 +709,7 @@ final readonly class OpenApiFactory implements OpenApiFactoryInterface { $openApi->getPaths()->addPath('/opengnsys/rest//ous', (new Model\PathItem())->withGet( (new Model\Operation('getOUs')) - ->withTags(['Organizational Unit']) + ->withTags(['UDS']) ->withSummary('Obtener todas las Unidades Organizacionales') ->withResponses([ Response::HTTP_OK => [ -- 2.40.1 From 2ddaba51d9008861076e79fb908088cf0531f379 Mon Sep 17 00:00:00 2001 From: Manuel Aranda Date: Tue, 8 Oct 2024 17:42:51 +0200 Subject: [PATCH 082/157] Added image loader --- docker/certs/server.crt | 19 +++++++++++++ docker/certs/server.key | 28 +++++++++++++++++++ ogcore.crt | 21 ++++++++++++++ ogcore.key | 28 +++++++++++++++++++ .../UDS/OrganizationalUnitController.php | 2 +- src/OpenApi/OpenApiFactory.php | 2 +- 6 files changed, 98 insertions(+), 2 deletions(-) create mode 100644 docker/certs/server.crt create mode 100644 docker/certs/server.key create mode 100644 ogcore.crt create mode 100644 ogcore.key diff --git a/docker/certs/server.crt b/docker/certs/server.crt new file mode 100644 index 0000000..39e705b --- /dev/null +++ b/docker/certs/server.crt @@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIIDCTCCAfGgAwIBAgIUCESVK0sEqcpz40+nh0GMitq9VfIwDQYJKoZIhvcNAQEL +BQAwFDESMBAGA1UEAwwJbG9jYWxob3N0MB4XDTI0MTAwODExNTIyNFoXDTI1MTAw +ODExNTIyNFowFDESMBAGA1UEAwwJbG9jYWxob3N0MIIBIjANBgkqhkiG9w0BAQEF +AAOCAQ8AMIIBCgKCAQEAmtdlS7M7iB8HMPtwlHlXTegiGWL/xpvCvyb56kbrIjUk +2MsKzz/vhfdI+Jd/I8JKcAd+IN1bqyAUl9MjwFhFyIgFghNP6jLsN3n2C8ZIZl3u +CzTP4nlIHC5/MqwclxavxMM8uGMj2Ci9cub5RSJ2XTEmiEcV67hXdG+j3m8cBsVw +D2L9y9Fl4f+1BauiVtGotBRVQuaLlkqwPJPRCUDiLSOoIX43ZbHn7bbPJRG2eQtb +7fdhGkYUI4KqR7rvExR7E/EShp1k5m5pX8V0WW0pxigLVhQ6FSRudIyGoZ3PFgHp +5y4UA5jMjJtha4TeqYAy3+TyuoiU3T6cGsox9VmGhwIDAQABo1MwUTAdBgNVHQ4E +FgQUqSam/uMA2D2znG+yD4SSPwxfmOgwHwYDVR0jBBgwFoAUqSam/uMA2D2znG+y +D4SSPwxfmOgwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAevFz +ayQz7V/OlspIewLNC1msTcDmJEBDNYoQNzpLzzTR0+vww1O99nixCCJZm4UdaMbI +AL2mAbI94Hq2IewLbhLSTkzP6OpwE13rAZglDe8UFQweppDY4DzENdcHY4J2awS2 +frs9UFErcEqFktUH9PLVUOinmls1HGPKHUcEeLrIgbqM+S/4H4hWxfxaQIdFbL6b +K22fIXdagsc9//PsVf9zWaiUzkITjs8ue3XgXXNZioX/PL40UAMkimVdDDeg5vUK +Xtr5rVWKBxsrMCZrlvI0xpFX6VbCjMwG3x3wYRqsn0wmCTqqVCXzg4qYq8UZAcZM +WQplbeu2ShNvdToXVQ== +-----END CERTIFICATE----- diff --git a/docker/certs/server.key b/docker/certs/server.key new file mode 100644 index 0000000..1e2ccca --- /dev/null +++ b/docker/certs/server.key @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCa12VLszuIHwcw ++3CUeVdN6CIZYv/Gm8K/JvnqRusiNSTYywrPP++F90j4l38jwkpwB34g3VurIBSX +0yPAWEXIiAWCE0/qMuw3efYLxkhmXe4LNM/ieUgcLn8yrByXFq/Ewzy4YyPYKL1y +5vlFInZdMSaIRxXruFd0b6PebxwGxXAPYv3L0WXh/7UFq6JW0ai0FFVC5ouWSrA8 +k9EJQOItI6ghfjdlseftts8lEbZ5C1vt92EaRhQjgqpHuu8TFHsT8RKGnWTmbmlf +xXRZbSnGKAtWFDoVJG50jIahnc8WAennLhQDmMyMm2FrhN6pgDLf5PK6iJTdPpwa +yjH1WYaHAgMBAAECggEACQGMkD3GsXul25j8VX5wmq+Xms9nHheoLkhnkn35JikI +0XKPLeUY8DtPq2SFuMA6Znq2a4CZ2NcJq5epxYG83qresHqhaXdNxZ+aioPIE+rU +XM1/u+X0UO35aFVEvRr3h+PChTJVqY3GZcepFEwqre/yaaHCKiQziKpc/Hk3nEMf +ZfPpEqQqRh4tMwcG1BoPqj6q2E5h3XQZ2bNKPUa+mk28Xa4DsrBdWlVV2uIuIH0H +qqzoVcKds0mDcOgiYwCnYCgXK141fks6DS+1iesonzcPiNNtMzvM2bFtYRsUul9j +dcwNGmKtK10vCnb/q/YhhIu5mrZAhQtollJmOh4HIQKBgQDP7L4YQV/qgsaOaJh7 +OmrLTAz7DrImaq8vbbCnd4avXiLGZJ4uyHSWga7seqlK4WF0ZfIbjJ4+YojPsbni +0IoxNFgCtu1kz8t8KjpEyMuu5gZJYtI9O+QM9eRqXzUxBBOculm5e8k38v7DfpFJ +DycLEcjti9rJTvmtQYpxnZEv4QKBgQC+pJlX8XMcUpQHlV6xuvMtIFM2xJqwjnT9 +Qac0UkvlwFQrmKaWS6METHQLQosvimw7B7Ux2mwFZgzjriGwol+vJrMQaEU3F3jq +654NuwGX6HKFtGvzBXyvKjgERSdWrs6f7N7ng2eDcwcGpohG7bHrSGEHC24Ofl5Y +DKNSqNOjZwKBgQCpRClcxZu35c6OkEfRybS226qVl1kgPSlwivOM4Zkbmp7ks3Sy +0S1YeSpWCWVYODKF95gpJUiNsNl/e0fAhdR6Sgp76z9HGoKlCht1c6GdOFVV2MUe +ZGs6wdK5Zdh/lroAlRQGE+ezTHZp39eWimdWc5wHBhuOVmSbzwdNhuBzAQKBgE/C +l7FWgD16QxqFINAlEjZwDsCcMsfpYgLPJABc6goelFiFDjVVQce7oNVRKuBh/nQX +/nP0GTuM/ZmIMeovlO63tzUHGuY4uMs4drDx63G9u6Q63usXAZpbhIIs2I+IpUP9 +myirLnStfGQI6n0aV1TDvGxxGMORlTErYG1l2t3DAoGAZ8xdgLlXiND2vBYDT8WN +NozyhHYdCP5zP1WJLP6i4C6uLHJZ/HV7eSZ/aFlG5E+c9A53VF/J5BP1v3oob7Ul +eMOIpnlM8Velp6BeCAVKx/5IfaWDYIwdqxcEMHw5uFg84cCh7iGSaDcQaAgoTDd8 +8N9WV8piwfcZERkFbdFI70g= +-----END PRIVATE KEY----- diff --git a/ogcore.crt b/ogcore.crt new file mode 100644 index 0000000..072269f --- /dev/null +++ b/ogcore.crt @@ -0,0 +1,21 @@ +-----BEGIN CERTIFICATE----- +MIIDYTCCAkmgAwIBAgIUZ2upHKcDRZdr6cW4FfCAe114LtgwDQYJKoZIhvcNAQEL +BQAwQDELMAkGA1UEBhMCRVMxDzANBgNVBAgMBk1hZHJpZDEPMA0GA1UEBwwGTWFk +cmlkMQ8wDQYDVQQKDAZRaW5kZWwwHhcNMjQxMDA4MTExMTMxWhcNMjUxMDA1MTEx +MTMxWjBAMQswCQYDVQQGEwJFUzEPMA0GA1UECAwGTWFkcmlkMQ8wDQYDVQQHDAZN +YWRyaWQxDzANBgNVBAoMBlFpbmRlbDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC +AQoCggEBAM9NWMylshHt6EZTyeZCWoCsuVYoKSP2+EgCn8ujPvA2K9dh2StccI7w +y2SsUEZN8pesDWit1Kt/2nXY1ec3RV/JQzF6h8qGgSST+pg02PbhTSRzEBi1DX+r +FRey/M/Wl5Uf2PtUyqVsbbt7+TFaymjdV2/pR5CFP5auVrZKe/KYoq8BNY+Lusl+ +rBa7YPDNKqfhwBDm2YL4ppIhAv17RgWvgBO2ZcXoFLF8PH0Qk1Wb0wQlJJWdmlyH +XF292Egl/Re1W8ci0T/3r/UidTypDSKPLJ2AXNlf5ZUSgbrzUItsXD/++Irnv9gG +LvqH+12u8HP2lQ5OAo1A6mrcEWW56l0CAwEAAaNTMFEwHQYDVR0OBBYEFEpxe7x9 +7/KeASrLnQiz5Jok/XC2MB8GA1UdIwQYMBaAFEpxe7x97/KeASrLnQiz5Jok/XC2 +MA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBALRGOWaJqAl3AUvT +WVRK3P7PNXkD37ZXwAcqtf8G5GA/n5jMj9t8uwvH3Acq+UqSLf5OpynME5ZoI0gc +hzDDGOWp9+24VTnX2Sl8OmPbUvTuh7XMLY/A5CkKoNTEkpntPhYsxDSgJAKAgxGZ +LJ4cqbI0NZFMgUUWgb+ge4DE+MIX5VfLrak/pWGAoKzqXTobZ4+q9buvloGfkg6d +sQ9Rj72mpWdiwmL+AJ4A3IzpdcIphBtV5Jtqd2CgNGoid6/WLGuuSCHeIKTEqXiL +UczwgP1Hxu1z7kGC2iZiy46KVMnan6JJCyZUNsxk6LA6ECKOVlekNQV9pcs2vB2M +UAfYocI= +-----END CERTIFICATE----- diff --git a/ogcore.key b/ogcore.key new file mode 100644 index 0000000..9e5573c --- /dev/null +++ b/ogcore.key @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDPTVjMpbIR7ehG +U8nmQlqArLlWKCkj9vhIAp/Loz7wNivXYdkrXHCO8MtkrFBGTfKXrA1ordSrf9p1 +2NXnN0VfyUMxeofKhoEkk/qYNNj24U0kcxAYtQ1/qxUXsvzP1peVH9j7VMqlbG27 +e/kxWspo3Vdv6UeQhT+Wrla2SnvymKKvATWPi7rJfqwWu2DwzSqn4cAQ5tmC+KaS +IQL9e0YFr4ATtmXF6BSxfDx9EJNVm9MEJSSVnZpch1xdvdhIJf0XtVvHItE/96/1 +InU8qQ0ijyydgFzZX+WVEoG681CLbFw//viK57/YBi76h/tdrvBz9pUOTgKNQOpq +3BFluepdAgMBAAECggEAJYdqqo3auW05DjZQCFv912qtmN/nZsfotdynwi15EPK1 +qCXiM3vmMrJ8F+ssEzY8U2IGAWvq4utgCNKrEtNKP2eHvxbgK3WDPrR9ev2iQ7uR +BCbChL6ORY0a4fH428WNJqWf2gxFLYJ+MSFmAR3fI/HiSPPSDWNaGjTQpOrCGq5c +57MpJ+7yPBV5lf+f0Z8MNgY1JnQ1dZ0AqcamkuP+/k6NETdolC7AF33M46EkVgDu +OSqNcRqmmMjYLAjAfVgtjBQ5XtH0Wp3tNa8W64GXGDMtOjiywjeMI7T8LHRgXaSH +HP6dGsYGDNWEvZpwpv67gi/xW+kj/vFx/QSe/DwRoQKBgQDn9JN11whTTOw2eztl +KTvYmSDEEA3CNZKzCrD8vuyKvmSeQsaZxMTMhfTtlzLt76SaqkIQqcXe1S5v3s3c +MCjzsX+0qisBSQaQpv7tFIc810rxmL0+o137eCY5HxW0kCuaClPyHjYq9kk/dO4k +M6TWkhxLckwAvdHGygqbmfwxrQKBgQDkyouXzSOAc1Un0rB1ysOQ7m7sY44PGJxq +mHdBg/KU3WyTxvq9p17ZmrpRmLd3ZJ9HPLWBQlqEW5csoA9K273tKQWwMjQa/ztv +51Qvib7/7gFHXOJXHz96CWSkfuXCLBsD1eTOW/gAd7fUwMWalph/jf6tFRkK4bCE +1VAH1PmRcQKBgFFwe72qDBBDN8RBLC9udMatrOVpxYrkiGc4X7+9wQtNocI3K8ed +YIN2nPI2XflQZHixQpTbJtAABVfd4GVou1gkf/QMsyC2ZQmwuFzBEmJ4LKX6vKcb +cMnz+2ee1F4bKFPjQGoH1XjsE/eltF8disiTcD/FF45DFz42fdb4D6XZAoGATlXH +O9+CA0353xu1TW9yTGJG2msvzZ1omNrBIk5yNWQm8+YSHfN7d1gR6TQ3xQpPus1f +e/VjFqRhe2cGNWRNgDEmgHCABKpVCHiVC33HRsuP5oY3/Qp26mri6auFMVl2mnaW +080LeeWDrLWgMtDTQAE3LL3niCFEFgcPUs8p8BECgYEAhQEHqCcdkdJ4Dlqrr/A2 +XkwLiOxr+KGfLYxNx4SCtmYREoP5CHgEj1QkFJyu2hiAprq0fNCTYqraHJU/gnWa ++MFGJLQnRAao/3jzYK9/KMEAHPHQC3MXFdlsbq2GIY3tfbHXusRPkfrHk0tbWT5V +Mo3wGFKMv2PfxKZ6Q+dxb4o= +-----END PRIVATE KEY----- diff --git a/src/Controller/UDS/OrganizationalUnitController.php b/src/Controller/UDS/OrganizationalUnitController.php index 3b1f502..af46e7d 100644 --- a/src/Controller/UDS/OrganizationalUnitController.php +++ b/src/Controller/UDS/OrganizationalUnitController.php @@ -42,7 +42,7 @@ class OrganizationalUnitController extends AbstractController } #[Route('/opengnsys/rest//ous/{centerId}/labs', methods: ['GET'])] - public function getClassrooms(Request $request, int $centerId): JsonResponse + public function getClassrooms(Request $request, string $centerId): JsonResponse { $classrooms = $this->entityManager ->getRepository(OrganizationalUnit::class) diff --git a/src/OpenApi/OpenApiFactory.php b/src/OpenApi/OpenApiFactory.php index 0d53a84..f15bec0 100644 --- a/src/OpenApi/OpenApiFactory.php +++ b/src/OpenApi/OpenApiFactory.php @@ -20,7 +20,7 @@ final readonly class OpenApiFactory implements OpenApiFactoryInterface $this->addRefreshToken($openApi); $this->addOgAgentEndpoints($openApi); - $this->addUDsEndpoints($openApi); + //$this->addUDsEndpoints($openApi); $this->addStatusEndpoint($openApi); $this->addInstallOgLiveWebhookEndpoint($openApi); -- 2.40.1 From 8ff975743afeacb0247807f2bfded45bd58468c8 Mon Sep 17 00:00:00 2001 From: Manuel Aranda Date: Tue, 8 Oct 2024 18:24:53 +0200 Subject: [PATCH 083/157] Test endpoint --- src/Controller/UDS/OrganizationalUnitController.php | 8 +++++--- src/OpenApi/OpenApiFactory.php | 6 +++--- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/Controller/UDS/OrganizationalUnitController.php b/src/Controller/UDS/OrganizationalUnitController.php index af46e7d..4896ddd 100644 --- a/src/Controller/UDS/OrganizationalUnitController.php +++ b/src/Controller/UDS/OrganizationalUnitController.php @@ -11,7 +11,7 @@ use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\Attribute\AsController; -use Symfony\Component\Routing\Annotation\Route; +use Symfony\Component\Routing\Attribute\Route; #[AsController] class OrganizationalUnitController extends AbstractController @@ -41,8 +41,9 @@ class OrganizationalUnitController extends AbstractController return new JsonResponse($data, Response::HTTP_OK); } + #[Route('/opengnsys/rest/ous/{centerId}/labs', name: 'get_classrooms', methods: ['GET'])] #[Route('/opengnsys/rest//ous/{centerId}/labs', methods: ['GET'])] - public function getClassrooms(Request $request, string $centerId): JsonResponse + public function getClassrooms(int $centerId): JsonResponse { $classrooms = $this->entityManager ->getRepository(OrganizationalUnit::class) @@ -64,8 +65,9 @@ class OrganizationalUnitController extends AbstractController return new JsonResponse($data, Response::HTTP_OK); } + #[Route('/opengnsys/rest/ous/{centerId}/images', name: 'getImages', methods: ['GET'])] #[Route('/opengnsys/rest//ous/{centerId}/images', methods: ['GET'])] - public function getImages(Request $request, int $centerId): JsonResponse + public function getImages(int $centerId): JsonResponse { $parent = $this->entityManager ->getRepository(OrganizationalUnit::class) diff --git a/src/OpenApi/OpenApiFactory.php b/src/OpenApi/OpenApiFactory.php index f15bec0..2513802 100644 --- a/src/OpenApi/OpenApiFactory.php +++ b/src/OpenApi/OpenApiFactory.php @@ -20,7 +20,7 @@ final readonly class OpenApiFactory implements OpenApiFactoryInterface $this->addRefreshToken($openApi); $this->addOgAgentEndpoints($openApi); - //$this->addUDsEndpoints($openApi); + $this->addUDsEndpoints($openApi); $this->addStatusEndpoint($openApi); $this->addInstallOgLiveWebhookEndpoint($openApi); @@ -742,7 +742,7 @@ final readonly class OpenApiFactory implements OpenApiFactoryInterface ]) )); - $openApi->getPaths()->addPath('/opengnsys/rest//ous/{centerId}/labs', (new Model\PathItem())->withGet( + $openApi->getPaths()->addPath('/opengnsys/rest/ous/{centerId}/labs', (new Model\PathItem())->withGet( (new Model\Operation('getClassrooms')) ->withTags(['UDS']) ->withSummary('Obtener los laboratorios de una Unidad Organizacional específica') @@ -786,7 +786,7 @@ final readonly class OpenApiFactory implements OpenApiFactoryInterface ]) )); - $openApi->getPaths()->addPath('/opengnsys/rest//ous/{centerId}/images', (new Model\PathItem())->withGet( + $openApi->getPaths()->addPath('/opengnsys/rest/ous/{centerId}/images', (new Model\PathItem())->withGet( (new Model\Operation('getImages')) ->withTags(['UDS']) ->withSummary('Obtener las imágenes de una Unidad Organizacional específica') -- 2.40.1 From 1cfd1fdfeec8f1f1f3bd71d44317f26d203816ac Mon Sep 17 00:00:00 2001 From: Manuel Aranda Date: Tue, 8 Oct 2024 18:40:52 +0200 Subject: [PATCH 084/157] Test ngin rewrite rule --- docker/default.conf | 6 ++++++ src/Controller/UDS/OrganizationalUnitController.php | 1 - 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/docker/default.conf b/docker/default.conf index 0e0059d..23c1e7c 100644 --- a/docker/default.conf +++ b/docker/default.conf @@ -15,6 +15,12 @@ server { ssl_certificate /etc/nginx/certs/server.crt; # Ruta al certificado ssl_certificate_key /etc/nginx/certs/server.key; # Ruta a la clave + # Reescritura para eliminar la doble barra en la URL para images + location /opengnsys/rest/ous/ { + rewrite ^/opengnsys/rest/ous/(.*?)/images$ /opengnsys/rest/ous/$1/images break; + rewrite ^/opengnsys/rest/ous/(.*?)/labs$ /opengnsys/rest/ous/$1/labs break; + } + location / { try_files $uri $uri/ /index.php?$args; } diff --git a/src/Controller/UDS/OrganizationalUnitController.php b/src/Controller/UDS/OrganizationalUnitController.php index 4896ddd..d68588e 100644 --- a/src/Controller/UDS/OrganizationalUnitController.php +++ b/src/Controller/UDS/OrganizationalUnitController.php @@ -66,7 +66,6 @@ class OrganizationalUnitController extends AbstractController } #[Route('/opengnsys/rest/ous/{centerId}/images', name: 'getImages', methods: ['GET'])] - #[Route('/opengnsys/rest//ous/{centerId}/images', methods: ['GET'])] public function getImages(int $centerId): JsonResponse { $parent = $this->entityManager -- 2.40.1 From 8806f0449c20277b35b781bf5c1c7dfea5eec66e Mon Sep 17 00:00:00 2001 From: Manuel Aranda Date: Wed, 9 Oct 2024 08:44:44 +0200 Subject: [PATCH 085/157] Test nginx conf --- docker/default.conf | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/docker/default.conf b/docker/default.conf index 23c1e7c..fbe3c6b 100644 --- a/docker/default.conf +++ b/docker/default.conf @@ -15,16 +15,26 @@ server { ssl_certificate /etc/nginx/certs/server.crt; # Ruta al certificado ssl_certificate_key /etc/nginx/certs/server.key; # Ruta a la clave - # Reescritura para eliminar la doble barra en la URL para images + # Reescritura para corregir las URLs con doble barra, pero sin cambiar el path de los archivos location /opengnsys/rest/ous/ { - rewrite ^/opengnsys/rest/ous/(.*?)/images$ /opengnsys/rest/ous/$1/images break; - rewrite ^/opengnsys/rest/ous/(.*?)/labs$ /opengnsys/rest/ous/$1/labs break; + # Solo eliminar la doble barra en las URLs para estos endpoints + rewrite ^/opengnsys/rest/ous/([0-9]+)/images/?$ /opengnsys/rest/ous/$1/images break; + rewrite ^/opengnsys/rest/ous/([0-9]+)/labs/?$ /opengnsys/rest/ous/$1/labs break; + + # Proxy hacia el backend o cualquier otra configuración necesaria + proxy_pass http://127.0.0.1:8443; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; } + # Bloque principal para archivos location / { try_files $uri $uri/ /index.php?$args; } + # Manejo de PHP location ~ \.php$ { include fastcgi_params; fastcgi_pass php:9000; @@ -34,6 +44,7 @@ server { fastcgi_param PATH_TRANSLATED $document_root$fastcgi_script_name; } + # Bloque para errores PHP location ~ \.php$ { return 404; } -- 2.40.1 From b8efa6a19f7d5c90519462b3c4a303a33fc0bbea Mon Sep 17 00:00:00 2001 From: Manuel Aranda Date: Wed, 9 Oct 2024 08:52:50 +0200 Subject: [PATCH 086/157] Test nginx conf --- docker/default.conf | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/docker/default.conf b/docker/default.conf index fbe3c6b..3932f0c 100644 --- a/docker/default.conf +++ b/docker/default.conf @@ -16,13 +16,13 @@ server { ssl_certificate_key /etc/nginx/certs/server.key; # Ruta a la clave # Reescritura para corregir las URLs con doble barra, pero sin cambiar el path de los archivos - location /opengnsys/rest/ous/ { - # Solo eliminar la doble barra en las URLs para estos endpoints - rewrite ^/opengnsys/rest/ous/([0-9]+)/images/?$ /opengnsys/rest/ous/$1/images break; - rewrite ^/opengnsys/rest/ous/([0-9]+)/labs/?$ /opengnsys/rest/ous/$1/labs break; + # Reescribir URL con doble barra + location /opengnsys/rest/ { + # Eliminar la doble barra cuando exista entre /rest/ y /ous/ + rewrite ^(/opengnsys/rest)//(.*)$ $1/$2 break; - # Proxy hacia el backend o cualquier otra configuración necesaria - proxy_pass http://127.0.0.1:8443; + # Proxy hacia el backend + proxy_pass https://127.0.0.1:8443; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; -- 2.40.1 From 9dab268a806e81e44ebedefc4af0e3b979160d57 Mon Sep 17 00:00:00 2001 From: Manuel Aranda Date: Wed, 9 Oct 2024 08:59:27 +0200 Subject: [PATCH 087/157] Test nginx conf --- docker/default.conf | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/docker/default.conf b/docker/default.conf index 3932f0c..d35647c 100644 --- a/docker/default.conf +++ b/docker/default.conf @@ -15,20 +15,6 @@ server { ssl_certificate /etc/nginx/certs/server.crt; # Ruta al certificado ssl_certificate_key /etc/nginx/certs/server.key; # Ruta a la clave - # Reescritura para corregir las URLs con doble barra, pero sin cambiar el path de los archivos - # Reescribir URL con doble barra - location /opengnsys/rest/ { - # Eliminar la doble barra cuando exista entre /rest/ y /ous/ - rewrite ^(/opengnsys/rest)//(.*)$ $1/$2 break; - - # Proxy hacia el backend - proxy_pass https://127.0.0.1:8443; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - } - # Bloque principal para archivos location / { try_files $uri $uri/ /index.php?$args; -- 2.40.1 From 5726fb0bae1df4bd1f71bef814fa6ddc1b29fe1d Mon Sep 17 00:00:00 2001 From: Manuel Aranda Date: Wed, 9 Oct 2024 09:15:16 +0200 Subject: [PATCH 088/157] Test nginx conf --- docker/default.conf | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docker/default.conf b/docker/default.conf index d35647c..8beb81f 100644 --- a/docker/default.conf +++ b/docker/default.conf @@ -15,6 +15,11 @@ server { ssl_certificate /etc/nginx/certs/server.crt; # Ruta al certificado ssl_certificate_key /etc/nginx/certs/server.key; # Ruta a la clave + location /opengnsys/rest/ous// { + rewrite ^/opengnsys/rest/ous//([0-9]+)/images /opengnsys/rest/ous/$1/images; + rewrite ^/opengnsys/rest/ous//([0-9]+)/labs /opengnsys/rest/ous/$1/labs; + } + # Bloque principal para archivos location / { try_files $uri $uri/ /index.php?$args; -- 2.40.1 From c58afe873fd5df23ab989c680a137ab00b241c38 Mon Sep 17 00:00:00 2001 From: Manuel Aranda Date: Wed, 9 Oct 2024 09:32:14 +0200 Subject: [PATCH 089/157] Fixed controller info uds --- docker/default.conf | 2 +- src/Controller/UDS/OrganizationalUnitController.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docker/default.conf b/docker/default.conf index 8beb81f..4df9f66 100644 --- a/docker/default.conf +++ b/docker/default.conf @@ -40,6 +40,6 @@ server { return 404; } - error_log /var/log/nginx/error.log; + error_log /var/log/nginx/error.log debug; access_log /var/log/nginx/access.log; } diff --git a/src/Controller/UDS/OrganizationalUnitController.php b/src/Controller/UDS/OrganizationalUnitController.php index d68588e..3d03e11 100644 --- a/src/Controller/UDS/OrganizationalUnitController.php +++ b/src/Controller/UDS/OrganizationalUnitController.php @@ -91,7 +91,7 @@ class OrganizationalUnitController extends AbstractController } #[Route('/opengnsys/rest//info', methods: ['GET'])] - public function getOpengnsysInfo(Request $request, int $centerId): JsonResponse + public function getOpengnsysInfo(Request $request): JsonResponse { $data = [ "project" => "OpenGnsys", -- 2.40.1 From 8fba590e9c0d8f4d07b8df1876ed4a2b2c6dbd0a Mon Sep 17 00:00:00 2001 From: Manuel Aranda Date: Wed, 9 Oct 2024 09:43:15 +0200 Subject: [PATCH 090/157] Created mock up login --- .../UDS/OrganizationalUnitController.php | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/Controller/UDS/OrganizationalUnitController.php b/src/Controller/UDS/OrganizationalUnitController.php index 3d03e11..e48c446 100644 --- a/src/Controller/UDS/OrganizationalUnitController.php +++ b/src/Controller/UDS/OrganizationalUnitController.php @@ -6,6 +6,7 @@ use App\Entity\Image; use App\Entity\OrganizationalUnit; use App\Model\OrganizationalUnitTypes; use Doctrine\ORM\EntityManagerInterface; +use Random\RandomException; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Request; @@ -22,6 +23,21 @@ class OrganizationalUnitController extends AbstractController { } + /** + * @throws RandomException + */ + #[Route('/opengnsys/rest//login', methods: ['POST'])] + public function login(Request $request): JsonResponse + { + $data = [ + 'userid' => 1, + 'apikey' => bin2hex(random_bytes(32)) + ]; + + return new JsonResponse($data, Response::HTTP_OK); + } + + #[Route('/opengnsys/rest//ous', methods: ['GET'])] public function getOUs(Request $request): JsonResponse { -- 2.40.1 From 5da12033e8b9d9a088491239ee3bed01ed147c29 Mon Sep 17 00:00:00 2001 From: Manuel Aranda Date: Wed, 9 Oct 2024 09:44:47 +0200 Subject: [PATCH 091/157] Created mock up login --- src/Controller/UDS/OrganizationalUnitController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Controller/UDS/OrganizationalUnitController.php b/src/Controller/UDS/OrganizationalUnitController.php index e48c446..dbd6090 100644 --- a/src/Controller/UDS/OrganizationalUnitController.php +++ b/src/Controller/UDS/OrganizationalUnitController.php @@ -31,7 +31,7 @@ class OrganizationalUnitController extends AbstractController { $data = [ 'userid' => 1, - 'apikey' => bin2hex(random_bytes(32)) + 'apikey' => bin2hex(random_bytes(16)) ]; return new JsonResponse($data, Response::HTTP_OK); -- 2.40.1 From f39abdc2ba526351bf90e0311818d6d0d51594ae Mon Sep 17 00:00:00 2001 From: Manuel Aranda Date: Wed, 9 Oct 2024 09:46:33 +0200 Subject: [PATCH 092/157] Created mock up login --- src/Controller/UDS/OrganizationalUnitController.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Controller/UDS/OrganizationalUnitController.php b/src/Controller/UDS/OrganizationalUnitController.php index dbd6090..8777ed0 100644 --- a/src/Controller/UDS/OrganizationalUnitController.php +++ b/src/Controller/UDS/OrganizationalUnitController.php @@ -82,6 +82,7 @@ class OrganizationalUnitController extends AbstractController } #[Route('/opengnsys/rest/ous/{centerId}/images', name: 'getImages', methods: ['GET'])] + #[Route('/opengnsys/rest//ous/{centerId}/images', methods: ['GET'])] public function getImages(int $centerId): JsonResponse { $parent = $this->entityManager -- 2.40.1 From ecf406609ac7758547fba7610b1093c43381bcc5 Mon Sep 17 00:00:00 2001 From: Manuel Aranda Date: Wed, 9 Oct 2024 10:15:36 +0200 Subject: [PATCH 093/157] Created mock up login --- src/Controller/UDS/OrganizationalUnitController.php | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/Controller/UDS/OrganizationalUnitController.php b/src/Controller/UDS/OrganizationalUnitController.php index 8777ed0..5767326 100644 --- a/src/Controller/UDS/OrganizationalUnitController.php +++ b/src/Controller/UDS/OrganizationalUnitController.php @@ -74,7 +74,11 @@ class OrganizationalUnitController extends AbstractController $data[] = [ 'id' => $classroom->getId(), 'name' => $classroom->getName(), - 'description' => $classroom->getDescription(), + 'inremotepc' => true, + 'ou' => [ + 'id' => 1 + ], + 'classroom' => [] ]; } @@ -101,6 +105,10 @@ class OrganizationalUnitController extends AbstractController $data[] = [ 'id' => $image->getId(), 'name' => $image->getName(), + 'inremotepc' => true, + 'ou' => [ + 'id' => 1 + ] ]; } -- 2.40.1 From 4ef98924720f40632891fca651106ae10efae913 Mon Sep 17 00:00:00 2001 From: Manuel Aranda Date: Wed, 9 Oct 2024 11:09:40 +0200 Subject: [PATCH 094/157] Updated CORS open to test --- .env | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.env b/.env index 8589cab..3a0cfeb 100644 --- a/.env +++ b/.env @@ -32,7 +32,7 @@ OG_1_DATABASE_URL="mysql://root:root@ogcore-database:3306/ogcore_old_og?serverVe ###< doctrine/doctrine-bundle ### ###> nelmio/cors-bundle ### -CORS_ALLOW_ORIGIN='^https?://(localhost|127\.0\.0\.1)(:[0-9]+)?$' +CORS_ALLOW_ORIGIN='*' ###< nelmio/cors-bundle ### ###> lexik/jwt-authentication-bundle ### -- 2.40.1 From 0ccecb786ebbc0c048685fe9a452441a69a7d303 Mon Sep 17 00:00:00 2001 From: Manuel Aranda Date: Wed, 9 Oct 2024 15:31:16 +0200 Subject: [PATCH 095/157] Change logic migration UDS --- src/Service/UDS/UDSClient.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Service/UDS/UDSClient.php b/src/Service/UDS/UDSClient.php index 23f9286..3d3f161 100644 --- a/src/Service/UDS/UDSClient.php +++ b/src/Service/UDS/UDSClient.php @@ -142,7 +142,7 @@ class UDSClient public function getMaxAvailableSeats(int $organizationalUnitId): int { - $organizationalUnit = $this->entityManager->getRepository(OrganizationalUnit::class)->findOneBy(['migrationId' => $organizationalUnitId]); + $organizationalUnit = $this->entityManager->getRepository(OrganizationalUnit::class)->findOneBy(['id' => $organizationalUnitId]); if (!$organizationalUnit) { return 0; -- 2.40.1 From 3c4d56727a60ef53ee44212c8f0b7bcf6959d4db Mon Sep 17 00:00:00 2001 From: Manuel Aranda Date: Wed, 9 Oct 2024 16:43:07 +0200 Subject: [PATCH 096/157] Added new certs --- docker/certs/ogcore.uds-test.net.crt.pem | 28 ++++++++++++++++++++++++ docker/certs/ogcore.uds-test.net.key.pem | 28 ++++++++++++++++++++++++ docker/certs/server.crt | 19 ---------------- docker/certs/server.key | 28 ------------------------ docker/default.conf | 4 ++-- 5 files changed, 58 insertions(+), 49 deletions(-) create mode 100644 docker/certs/ogcore.uds-test.net.crt.pem create mode 100644 docker/certs/ogcore.uds-test.net.key.pem delete mode 100644 docker/certs/server.crt delete mode 100644 docker/certs/server.key diff --git a/docker/certs/ogcore.uds-test.net.crt.pem b/docker/certs/ogcore.uds-test.net.crt.pem new file mode 100644 index 0000000..1541b24 --- /dev/null +++ b/docker/certs/ogcore.uds-test.net.crt.pem @@ -0,0 +1,28 @@ +-----BEGIN CERTIFICATE----- +MIIExTCCAq2gAwIBAgICEAEwDQYJKoZIhvcNAQELBQAwSTELMAkGA1UEBhMCRVMx +DzANBgNVBAgMBk1hZHJpZDEPMA0GA1UEBwwGTWFkcmlkMRgwFgYDVQQDDA9jYS51 +ZHMtdGVzdC5uZXQwHhcNMjQxMDA5MTQyODM3WhcNMjUxMDE5MTQyODM3WjBNMQsw +CQYDVQQGEwJFUzEPMA0GA1UECAwGTWFkcmlkMQ8wDQYDVQQHDAZNYWRyaWQxHDAa +BgNVBAMME29nY29yZS51ZHMtdGVzdC5uZXQwggEiMA0GCSqGSIb3DQEBAQUAA4IB +DwAwggEKAoIBAQDaT0uiHcCwxUtRiJAhMI1VBuUohIzQgBQ1pwOa8gfwJZGn+p5p +T6qVrDb2RGWL8kJyR0tohQ6BxwVirAYTs0Az2EzZrh26gAlMhEsmQdWjQuWeHiRk +tp+6ELfATSd97LwMe5KgJl80JYQDVHxryPxPTgbxB3tjmp8ErcrhQ58Omq2D6bnd +xrKCbgfSoZP+ZyqKY3sNbHIX3632zSwHnu8on2ltZiYbbs1I29onysM4Zj2eAjZP +ot3pTzt4uIYV+i0fyY3+STvBda10bgUsoFWAVcdG310oVsginkFbpnhZPPueUklw +YtsXPq/yPJwn/tIcbEZ7TO7Pvtlh9RqTne9VAgMBAAGjgbIwga8wHgYDVR0RBBcw +FYITb2djb3JlLnVkcy10ZXN0Lm5ldDAdBgNVHQ4EFgQUS+4OUtWxVvTVc1odUFUO +UR3dURswbgYDVR0jBGcwZaFNpEswSTELMAkGA1UEBhMCRVMxDzANBgNVBAgMBk1h +ZHJpZDEPMA0GA1UEBwwGTWFkcmlkMRgwFgYDVQQDDA9jYS51ZHMtdGVzdC5uZXSC +FECd/NYwzaJTHtQ002YnOD3ZKLs0MA0GCSqGSIb3DQEBCwUAA4ICAQAeHA6/lJIv +hQTySWOOLhnuWcej1DmQhbDzyrylLUfJe86qV7QCLpasXabDpOQzTK5yBkjCWtV2 +YiXNx6eT1iGbs70+5fITjj2vhAT9bxi4WH49xU4q+vfxlfxTkA4/ZXTEEmb+B91Q +BVEF/7f13UiGV2yu4xbDptr98v/55OeycBgwLdNN7uw7EP6WK8ryLxlxvF+nqt1n +YHof/QqRJze3FKHlGhGvx1I3SEE+VwWW5hVbde3HkwlORf5ABr4fxbvudL+kwtMi +HmYV2oYvkYQZK0Vfcua0WAn9vKVBgnF6tXdqJTPG7p91dVe7bIbUdFgNBVBdh/md +SdESFWCghPQ6WYoB7/1WfPKVQ/0IBe7l8Yx+piNNl4WW2M7lOGf8mbBWUHNAgJjD +2u3440PfsPJgBniUuV4ILNhRNGbAjdk86oU8w2Vg6WR7xsQIBcukrHEm5wEW2RkW +bZMclPyUOzHh1l4dQZTyOp2LxYNqtfYXQuPDT6tvZV5hLuLMqQfdLVxuoi3KOgo2 +GfaolX2sW/sA4fx1FAvEvEq7zEvchpocL3EYa/aUNySxMrgjKMc3AtyLYF48D8M7 +1LZMj61TaPWUUalM/u32fEHhqOXJ1o0VX3gCY7c+hKhGFee8Aiyk/hrB2ED/Q/vR +cssM+sHHNwSYI/L+bjEkJDQtVZEztcQODQ== +-----END CERTIFICATE----- diff --git a/docker/certs/ogcore.uds-test.net.key.pem b/docker/certs/ogcore.uds-test.net.key.pem new file mode 100644 index 0000000..5aa9d43 --- /dev/null +++ b/docker/certs/ogcore.uds-test.net.key.pem @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQDaT0uiHcCwxUtR +iJAhMI1VBuUohIzQgBQ1pwOa8gfwJZGn+p5pT6qVrDb2RGWL8kJyR0tohQ6BxwVi +rAYTs0Az2EzZrh26gAlMhEsmQdWjQuWeHiRktp+6ELfATSd97LwMe5KgJl80JYQD +VHxryPxPTgbxB3tjmp8ErcrhQ58Omq2D6bndxrKCbgfSoZP+ZyqKY3sNbHIX3632 +zSwHnu8on2ltZiYbbs1I29onysM4Zj2eAjZPot3pTzt4uIYV+i0fyY3+STvBda10 +bgUsoFWAVcdG310oVsginkFbpnhZPPueUklwYtsXPq/yPJwn/tIcbEZ7TO7Pvtlh +9RqTne9VAgMBAAECggEADthSi9EbH8oiv4YaSu96xNvlrFYrHyh+d1GGmLw5lvZv +C62qpP2iW3AtAp0PDK+qHgxED/TwUhne/2E0PpWzWXMtcqX45ow7VaUvWQgkB2iO +paxmDCUxAl2NqTl15IW7GdwzUcmaMrkUW4ecuFWf9qKXLT+1f8XtZ0uMHrpS8BKr +bDM5oeushD6/ES32ecBIG3eJafSMESUux1fq/frzHEajS0fkYqFxi2uLYJdPLXC/ +YdVan7sIxOU3gxRROFDYLJ1uaksvUMM31oIujuxngdLTrf/K61RJGBFMeu1UIfrO +v6/9tqZgLIPTaC1nYMxjD+AtkwlY4C5vsPa8+jKtcQKBgQDz16/d/YSPxoa1QyPD +Ae/z+l13DGdk2+PdDavoyb1j11qwl4pgSGIA2uR3XeXg/dtmZaFk+KXUC+mHPsiM +TvxjoqjzP8LMXPJkRRzzTAwMjYzI8/ZAmCW+4q4Sxv3Dw4UQd0tCYi9ruMtXhnqp +agMKT2CNAikmbWdIu4S5uR6GDQKBgQDlMbfB5yJQsanW29VEJkUv+JBKZvxC7v59 +2EmZEbW1hqYHR7PA2qvSnNXvsQKRIcJtmrzf3koDhXN3mw59deBgT5FsUQCGxoK8 +ALMYMKiYcCNLp1rpxoz63lFnSzpD+f2dLBj0sac5Ufw17O6Fjs5+ZqND32UaEXFW +CLrjBpPEaQKBgAjXIQEjV9L+l5Oqw3kmcNSflxPh1z3I2xIAlOLzrXIZNKiCVfn3 +pdXyGaZaOSNXEMU9mgRXH2v4opbMp+iuVGgoVYe8IAvYstD/0HThpO4vk5MVhTAC +VBv/i+ASZtDaHdDjAk99z8pQAM9DiN7rgQC2sAFsuqEyBjSU19MD6x9hAoGAOSW1 +cObF2qMB+y3jNlPoinaK29Jj8fiPgids6nrM+Q8y1LvfKSYdE63BdjuHrVJinVuo +3pUZlVkwGlGSQlwi70DHvN9Rp0lWDbSK82wmjaPgWRvIgmPcgSzv1Taft5Vc1FTL +gC/Px24W2gdSzgB2onPLH8BTADX7MX2Jw9O/AokCgYBK7kvg1/cmikj176JFn1AM +MBCwVKS1fvUyh0bctadVyM+RA22cVvLB5PEbPB+LbyK1PnHB3jivEa954bOuYo39 +frRrRYZW4iP+oTqx8arcioaMW2K5urFtsqNrYVgkE5KDBAqFGSAyuKTAjftMxGqy +tORwgZ9jzgbBC0V8td6fqg== +-----END PRIVATE KEY----- diff --git a/docker/certs/server.crt b/docker/certs/server.crt deleted file mode 100644 index 39e705b..0000000 --- a/docker/certs/server.crt +++ /dev/null @@ -1,19 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIDCTCCAfGgAwIBAgIUCESVK0sEqcpz40+nh0GMitq9VfIwDQYJKoZIhvcNAQEL -BQAwFDESMBAGA1UEAwwJbG9jYWxob3N0MB4XDTI0MTAwODExNTIyNFoXDTI1MTAw -ODExNTIyNFowFDESMBAGA1UEAwwJbG9jYWxob3N0MIIBIjANBgkqhkiG9w0BAQEF -AAOCAQ8AMIIBCgKCAQEAmtdlS7M7iB8HMPtwlHlXTegiGWL/xpvCvyb56kbrIjUk -2MsKzz/vhfdI+Jd/I8JKcAd+IN1bqyAUl9MjwFhFyIgFghNP6jLsN3n2C8ZIZl3u -CzTP4nlIHC5/MqwclxavxMM8uGMj2Ci9cub5RSJ2XTEmiEcV67hXdG+j3m8cBsVw -D2L9y9Fl4f+1BauiVtGotBRVQuaLlkqwPJPRCUDiLSOoIX43ZbHn7bbPJRG2eQtb -7fdhGkYUI4KqR7rvExR7E/EShp1k5m5pX8V0WW0pxigLVhQ6FSRudIyGoZ3PFgHp -5y4UA5jMjJtha4TeqYAy3+TyuoiU3T6cGsox9VmGhwIDAQABo1MwUTAdBgNVHQ4E -FgQUqSam/uMA2D2znG+yD4SSPwxfmOgwHwYDVR0jBBgwFoAUqSam/uMA2D2znG+y -D4SSPwxfmOgwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAevFz -ayQz7V/OlspIewLNC1msTcDmJEBDNYoQNzpLzzTR0+vww1O99nixCCJZm4UdaMbI -AL2mAbI94Hq2IewLbhLSTkzP6OpwE13rAZglDe8UFQweppDY4DzENdcHY4J2awS2 -frs9UFErcEqFktUH9PLVUOinmls1HGPKHUcEeLrIgbqM+S/4H4hWxfxaQIdFbL6b -K22fIXdagsc9//PsVf9zWaiUzkITjs8ue3XgXXNZioX/PL40UAMkimVdDDeg5vUK -Xtr5rVWKBxsrMCZrlvI0xpFX6VbCjMwG3x3wYRqsn0wmCTqqVCXzg4qYq8UZAcZM -WQplbeu2ShNvdToXVQ== ------END CERTIFICATE----- diff --git a/docker/certs/server.key b/docker/certs/server.key deleted file mode 100644 index 1e2ccca..0000000 --- a/docker/certs/server.key +++ /dev/null @@ -1,28 +0,0 @@ ------BEGIN PRIVATE KEY----- -MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCa12VLszuIHwcw -+3CUeVdN6CIZYv/Gm8K/JvnqRusiNSTYywrPP++F90j4l38jwkpwB34g3VurIBSX -0yPAWEXIiAWCE0/qMuw3efYLxkhmXe4LNM/ieUgcLn8yrByXFq/Ewzy4YyPYKL1y -5vlFInZdMSaIRxXruFd0b6PebxwGxXAPYv3L0WXh/7UFq6JW0ai0FFVC5ouWSrA8 -k9EJQOItI6ghfjdlseftts8lEbZ5C1vt92EaRhQjgqpHuu8TFHsT8RKGnWTmbmlf -xXRZbSnGKAtWFDoVJG50jIahnc8WAennLhQDmMyMm2FrhN6pgDLf5PK6iJTdPpwa -yjH1WYaHAgMBAAECggEACQGMkD3GsXul25j8VX5wmq+Xms9nHheoLkhnkn35JikI -0XKPLeUY8DtPq2SFuMA6Znq2a4CZ2NcJq5epxYG83qresHqhaXdNxZ+aioPIE+rU -XM1/u+X0UO35aFVEvRr3h+PChTJVqY3GZcepFEwqre/yaaHCKiQziKpc/Hk3nEMf -ZfPpEqQqRh4tMwcG1BoPqj6q2E5h3XQZ2bNKPUa+mk28Xa4DsrBdWlVV2uIuIH0H -qqzoVcKds0mDcOgiYwCnYCgXK141fks6DS+1iesonzcPiNNtMzvM2bFtYRsUul9j -dcwNGmKtK10vCnb/q/YhhIu5mrZAhQtollJmOh4HIQKBgQDP7L4YQV/qgsaOaJh7 -OmrLTAz7DrImaq8vbbCnd4avXiLGZJ4uyHSWga7seqlK4WF0ZfIbjJ4+YojPsbni -0IoxNFgCtu1kz8t8KjpEyMuu5gZJYtI9O+QM9eRqXzUxBBOculm5e8k38v7DfpFJ -DycLEcjti9rJTvmtQYpxnZEv4QKBgQC+pJlX8XMcUpQHlV6xuvMtIFM2xJqwjnT9 -Qac0UkvlwFQrmKaWS6METHQLQosvimw7B7Ux2mwFZgzjriGwol+vJrMQaEU3F3jq -654NuwGX6HKFtGvzBXyvKjgERSdWrs6f7N7ng2eDcwcGpohG7bHrSGEHC24Ofl5Y -DKNSqNOjZwKBgQCpRClcxZu35c6OkEfRybS226qVl1kgPSlwivOM4Zkbmp7ks3Sy -0S1YeSpWCWVYODKF95gpJUiNsNl/e0fAhdR6Sgp76z9HGoKlCht1c6GdOFVV2MUe -ZGs6wdK5Zdh/lroAlRQGE+ezTHZp39eWimdWc5wHBhuOVmSbzwdNhuBzAQKBgE/C -l7FWgD16QxqFINAlEjZwDsCcMsfpYgLPJABc6goelFiFDjVVQce7oNVRKuBh/nQX -/nP0GTuM/ZmIMeovlO63tzUHGuY4uMs4drDx63G9u6Q63usXAZpbhIIs2I+IpUP9 -myirLnStfGQI6n0aV1TDvGxxGMORlTErYG1l2t3DAoGAZ8xdgLlXiND2vBYDT8WN -NozyhHYdCP5zP1WJLP6i4C6uLHJZ/HV7eSZ/aFlG5E+c9A53VF/J5BP1v3oob7Ul -eMOIpnlM8Velp6BeCAVKx/5IfaWDYIwdqxcEMHw5uFg84cCh7iGSaDcQaAgoTDd8 -8N9WV8piwfcZERkFbdFI70g= ------END PRIVATE KEY----- diff --git a/docker/default.conf b/docker/default.conf index 4df9f66..53b5966 100644 --- a/docker/default.conf +++ b/docker/default.conf @@ -12,8 +12,8 @@ server { root /var/www/html/public; index index.html index.php; - ssl_certificate /etc/nginx/certs/server.crt; # Ruta al certificado - ssl_certificate_key /etc/nginx/certs/server.key; # Ruta a la clave + ssl_certificate /etc/nginx/certs/ogcore.uds-test.crt.pem; + ssl_certificate_key /etc/nginx/certs/ogcore.uds-test.key.pem; location /opengnsys/rest/ous// { rewrite ^/opengnsys/rest/ous//([0-9]+)/images /opengnsys/rest/ous/$1/images; -- 2.40.1 From 5b7998d47b68bf2aa078e6ff7419389efce7a1ea Mon Sep 17 00:00:00 2001 From: Manuel Aranda Date: Wed, 9 Oct 2024 16:47:10 +0200 Subject: [PATCH 097/157] Added new certs --- docker/default.conf | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docker/default.conf b/docker/default.conf index 53b5966..0cb5a45 100644 --- a/docker/default.conf +++ b/docker/default.conf @@ -12,8 +12,8 @@ server { root /var/www/html/public; index index.html index.php; - ssl_certificate /etc/nginx/certs/ogcore.uds-test.crt.pem; - ssl_certificate_key /etc/nginx/certs/ogcore.uds-test.key.pem; + ssl_certificate /etc/nginx/certs/ogcore.uds-test.net.crt.pem; + ssl_certificate_key /etc/nginx/certs/ogcore.uds-test.net.key.pem; location /opengnsys/rest/ous// { rewrite ^/opengnsys/rest/ous//([0-9]+)/images /opengnsys/rest/ous/$1/images; -- 2.40.1 From 401898b26229183b882b589fe2e55f1eb341fc2a Mon Sep 17 00:00:00 2001 From: Manuel Aranda Date: Thu, 10 Oct 2024 09:30:44 +0200 Subject: [PATCH 098/157] Updated version. Removed test crt/key --- composer.json | 1 + config/packages/api_platform.yaml | 2 +- ogcore.crt | 21 --------------------- ogcore.key | 28 ---------------------------- 4 files changed, 2 insertions(+), 50 deletions(-) delete mode 100644 ogcore.crt delete mode 100644 ogcore.key diff --git a/composer.json b/composer.json index 1c7178a..b7550df 100644 --- a/composer.json +++ b/composer.json @@ -2,6 +2,7 @@ "type": "project", "license": "proprietary", "minimum-stability": "stable", + "version": "0.5.0", "prefer-stable": true, "require": { "php": ">=8.1", diff --git a/config/packages/api_platform.yaml b/config/packages/api_platform.yaml index f3290ad..7adc4f3 100644 --- a/config/packages/api_platform.yaml +++ b/config/packages/api_platform.yaml @@ -1,7 +1,7 @@ api_platform: title: 'OgCore Api' description: 'Api Documentation for OgCore' - version: 1.0.0 + version: 0.5.0 path_segment_name_generator: api_platform.path_segment_name_generator.dash defaults: pagination_client_items_per_page: true diff --git a/ogcore.crt b/ogcore.crt deleted file mode 100644 index 072269f..0000000 --- a/ogcore.crt +++ /dev/null @@ -1,21 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIDYTCCAkmgAwIBAgIUZ2upHKcDRZdr6cW4FfCAe114LtgwDQYJKoZIhvcNAQEL -BQAwQDELMAkGA1UEBhMCRVMxDzANBgNVBAgMBk1hZHJpZDEPMA0GA1UEBwwGTWFk -cmlkMQ8wDQYDVQQKDAZRaW5kZWwwHhcNMjQxMDA4MTExMTMxWhcNMjUxMDA1MTEx -MTMxWjBAMQswCQYDVQQGEwJFUzEPMA0GA1UECAwGTWFkcmlkMQ8wDQYDVQQHDAZN -YWRyaWQxDzANBgNVBAoMBlFpbmRlbDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC -AQoCggEBAM9NWMylshHt6EZTyeZCWoCsuVYoKSP2+EgCn8ujPvA2K9dh2StccI7w -y2SsUEZN8pesDWit1Kt/2nXY1ec3RV/JQzF6h8qGgSST+pg02PbhTSRzEBi1DX+r -FRey/M/Wl5Uf2PtUyqVsbbt7+TFaymjdV2/pR5CFP5auVrZKe/KYoq8BNY+Lusl+ -rBa7YPDNKqfhwBDm2YL4ppIhAv17RgWvgBO2ZcXoFLF8PH0Qk1Wb0wQlJJWdmlyH -XF292Egl/Re1W8ci0T/3r/UidTypDSKPLJ2AXNlf5ZUSgbrzUItsXD/++Irnv9gG -LvqH+12u8HP2lQ5OAo1A6mrcEWW56l0CAwEAAaNTMFEwHQYDVR0OBBYEFEpxe7x9 -7/KeASrLnQiz5Jok/XC2MB8GA1UdIwQYMBaAFEpxe7x97/KeASrLnQiz5Jok/XC2 -MA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBALRGOWaJqAl3AUvT -WVRK3P7PNXkD37ZXwAcqtf8G5GA/n5jMj9t8uwvH3Acq+UqSLf5OpynME5ZoI0gc -hzDDGOWp9+24VTnX2Sl8OmPbUvTuh7XMLY/A5CkKoNTEkpntPhYsxDSgJAKAgxGZ -LJ4cqbI0NZFMgUUWgb+ge4DE+MIX5VfLrak/pWGAoKzqXTobZ4+q9buvloGfkg6d -sQ9Rj72mpWdiwmL+AJ4A3IzpdcIphBtV5Jtqd2CgNGoid6/WLGuuSCHeIKTEqXiL -UczwgP1Hxu1z7kGC2iZiy46KVMnan6JJCyZUNsxk6LA6ECKOVlekNQV9pcs2vB2M -UAfYocI= ------END CERTIFICATE----- diff --git a/ogcore.key b/ogcore.key deleted file mode 100644 index 9e5573c..0000000 --- a/ogcore.key +++ /dev/null @@ -1,28 +0,0 @@ ------BEGIN PRIVATE KEY----- -MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDPTVjMpbIR7ehG -U8nmQlqArLlWKCkj9vhIAp/Loz7wNivXYdkrXHCO8MtkrFBGTfKXrA1ordSrf9p1 -2NXnN0VfyUMxeofKhoEkk/qYNNj24U0kcxAYtQ1/qxUXsvzP1peVH9j7VMqlbG27 -e/kxWspo3Vdv6UeQhT+Wrla2SnvymKKvATWPi7rJfqwWu2DwzSqn4cAQ5tmC+KaS -IQL9e0YFr4ATtmXF6BSxfDx9EJNVm9MEJSSVnZpch1xdvdhIJf0XtVvHItE/96/1 -InU8qQ0ijyydgFzZX+WVEoG681CLbFw//viK57/YBi76h/tdrvBz9pUOTgKNQOpq -3BFluepdAgMBAAECggEAJYdqqo3auW05DjZQCFv912qtmN/nZsfotdynwi15EPK1 -qCXiM3vmMrJ8F+ssEzY8U2IGAWvq4utgCNKrEtNKP2eHvxbgK3WDPrR9ev2iQ7uR -BCbChL6ORY0a4fH428WNJqWf2gxFLYJ+MSFmAR3fI/HiSPPSDWNaGjTQpOrCGq5c -57MpJ+7yPBV5lf+f0Z8MNgY1JnQ1dZ0AqcamkuP+/k6NETdolC7AF33M46EkVgDu -OSqNcRqmmMjYLAjAfVgtjBQ5XtH0Wp3tNa8W64GXGDMtOjiywjeMI7T8LHRgXaSH -HP6dGsYGDNWEvZpwpv67gi/xW+kj/vFx/QSe/DwRoQKBgQDn9JN11whTTOw2eztl -KTvYmSDEEA3CNZKzCrD8vuyKvmSeQsaZxMTMhfTtlzLt76SaqkIQqcXe1S5v3s3c -MCjzsX+0qisBSQaQpv7tFIc810rxmL0+o137eCY5HxW0kCuaClPyHjYq9kk/dO4k -M6TWkhxLckwAvdHGygqbmfwxrQKBgQDkyouXzSOAc1Un0rB1ysOQ7m7sY44PGJxq -mHdBg/KU3WyTxvq9p17ZmrpRmLd3ZJ9HPLWBQlqEW5csoA9K273tKQWwMjQa/ztv -51Qvib7/7gFHXOJXHz96CWSkfuXCLBsD1eTOW/gAd7fUwMWalph/jf6tFRkK4bCE -1VAH1PmRcQKBgFFwe72qDBBDN8RBLC9udMatrOVpxYrkiGc4X7+9wQtNocI3K8ed -YIN2nPI2XflQZHixQpTbJtAABVfd4GVou1gkf/QMsyC2ZQmwuFzBEmJ4LKX6vKcb -cMnz+2ee1F4bKFPjQGoH1XjsE/eltF8disiTcD/FF45DFz42fdb4D6XZAoGATlXH -O9+CA0353xu1TW9yTGJG2msvzZ1omNrBIk5yNWQm8+YSHfN7d1gR6TQ3xQpPus1f -e/VjFqRhe2cGNWRNgDEmgHCABKpVCHiVC33HRsuP5oY3/Qp26mri6auFMVl2mnaW -080LeeWDrLWgMtDTQAE3LL3niCFEFgcPUs8p8BECgYEAhQEHqCcdkdJ4Dlqrr/A2 -XkwLiOxr+KGfLYxNx4SCtmYREoP5CHgEj1QkFJyu2hiAprq0fNCTYqraHJU/gnWa -+MFGJLQnRAao/3jzYK9/KMEAHPHQC3MXFdlsbq2GIY3tfbHXusRPkfrHk0tbWT5V -Mo3wGFKMv2PfxKZ6Q+dxb4o= ------END PRIVATE KEY----- -- 2.40.1 From 8c57e61a57cddb706363246b3b786c30492675d3 Mon Sep 17 00:00:00 2001 From: Manuel Aranda Date: Mon, 14 Oct 2024 06:43:34 +0200 Subject: [PATCH 099/157] refs #614. Integration DHCP --- config/api_platform/Subnet.yaml | 17 ------ .../OgDhcp/Subnet/AddSingleHostAction.php | 52 ------------------ .../AddSingleOrganizationalUnitAction.php | 54 ------------------- src/Controller/OgDhcp/Subnet/GetAction.php | 2 +- .../OgDhcp/Subnet/PostHostAction.php | 38 +++++++------ src/Dto/Input/SubnetAddHostInput.php | 7 +-- src/Dto/Input/SubnetAddSingleHostInput.php | 15 ------ src/Dto/Output/ClientOutput.php | 6 +-- src/Dto/Output/SubnetOutput.php | 7 +-- 9 files changed, 28 insertions(+), 170 deletions(-) delete mode 100644 src/Controller/OgDhcp/Subnet/AddSingleHostAction.php delete mode 100644 src/Controller/OgDhcp/Subnet/AddSingleOrganizationalUnitAction.php delete mode 100644 src/Dto/Input/SubnetAddSingleHostInput.php diff --git a/config/api_platform/Subnet.yaml b/config/api_platform/Subnet.yaml index 3f19ae8..6f384de 100644 --- a/config/api_platform/Subnet.yaml +++ b/config/api_platform/Subnet.yaml @@ -68,15 +68,6 @@ resources: uriTemplate: /og-dhcp/server/{uuid}/delete controller: App\Controller\OgDhcp\Subnet\DeleteAction - add_single_host: - shortName: Subnet Server Hosts - description: Add Single Host to Subnet - class: ApiPlatform\Metadata\Post - method: POST - input: App\Dto\Input\SubnetAddSingleHostInput - uriTemplate: /og-dhcp/server/{uuid}/add-single-host - controller: App\Controller\OgDhcp\Subnet\AddSingleHostAction - post_host: shortName: Subnet Server Hosts description: Post Host to Subnet @@ -113,14 +104,6 @@ resources: uriTemplate: /og-dhcp/server/{uuid}/delete-host controller: App\Controller\OgDhcp\Subnet\DeleteHostAction - add_single_organizational_unit: - shortName: Subnet Server Organizational Units - description: Add Single Organizational Unit to Subnet - class: ApiPlatform\Metadata\Post - method: POST - input: App\Dto\Input\SubnetAddSingleOrganizationalUnitInput - uriTemplate: /og-dhcp/server/{uuid}/add-single-organizational-unit - controller: App\Controller\OgDhcp\Subnet\AddSingleOrganizationalUnitAction properties: App\Entity\Subnet: diff --git a/src/Controller/OgDhcp/Subnet/AddSingleHostAction.php b/src/Controller/OgDhcp/Subnet/AddSingleHostAction.php deleted file mode 100644 index dcaf665..0000000 --- a/src/Controller/OgDhcp/Subnet/AddSingleHostAction.php +++ /dev/null @@ -1,52 +0,0 @@ -client; - /** @var Client $clientEntity */ - $clientEntity = $client->getEntity(); - - $params = [ - 'json' => [ - 'host' => $clientEntity->getName(), - 'macAddress' => $clientEntity->getMac(), - 'address' => $clientEntity->getIp(), - ] - ]; - - $content = $this->createRequest($httpClient, 'POST', $this->ogDhcpApiUrl.'/ogdhcp/v1/subnets/'.$subnet->getId().'/hosts', $params); - - if ($content->getStatusCode() === 200) { - $subnet->addClient($clientEntity); - $this->entityManager->persist($subnet); - $this->entityManager->flush(); - } - - return new JsonResponse(data: $content, status: Response::HTTP_OK); - } -} \ No newline at end of file diff --git a/src/Controller/OgDhcp/Subnet/AddSingleOrganizationalUnitAction.php b/src/Controller/OgDhcp/Subnet/AddSingleOrganizationalUnitAction.php deleted file mode 100644 index ced5db2..0000000 --- a/src/Controller/OgDhcp/Subnet/AddSingleOrganizationalUnitAction.php +++ /dev/null @@ -1,54 +0,0 @@ -organizationalUnitOutput; - /** @var OrganizationalUnit $organizationalUnitEntity */ - $organizationalUnitEntity = $ou->getEntity(); - - $params = [ - 'json' => [ - 'name' => $organizationalUnitEntity->getId(), - 'nextServer' => $organizationalUnitEntity->getNetworkSettings()?->getNextServer(), - 'bootFileName' => $organizationalUnitEntity->getNetworkSettings()?->getBootFileName(), - ] - ]; - - $content = $this->createRequest($httpClient, 'POST', $this->ogDhcpApiUrl.'/ogdhcp/v1/subnets/'.$subnet->getId().'/classroom', $params); - - if ($content->getStatusCode() === 200) { - $subnet->addOrganizationalUnit($organizationalUnitEntity); - $this->entityManager->persist($subnet); - $this->entityManager->flush(); - } - - return new JsonResponse(data: $content, status: Response::HTTP_OK); - } -} \ No newline at end of file diff --git a/src/Controller/OgDhcp/Subnet/GetAction.php b/src/Controller/OgDhcp/Subnet/GetAction.php index c9b5133..b17698c 100644 --- a/src/Controller/OgDhcp/Subnet/GetAction.php +++ b/src/Controller/OgDhcp/Subnet/GetAction.php @@ -29,7 +29,7 @@ class GetAction extends AbstractOgDhcpController throw new ValidatorException('Checksum is required'); } - $content = $this->createRequest($httpClient, 'GET', $this->ogDhcpApiUrl.'/opengnsys3/rest/dhcp/subnets/'.$data->getId()); + $content = $this->createRequest($httpClient, 'GET', $this->ogDhcpApiUrl.'/ogdhcp/v1/subnets/'.$data->getId()); return new JsonResponse(data: $content, status: Response::HTTP_OK); } diff --git a/src/Controller/OgDhcp/Subnet/PostHostAction.php b/src/Controller/OgDhcp/Subnet/PostHostAction.php index 319025e..1b900e8 100644 --- a/src/Controller/OgDhcp/Subnet/PostHostAction.php +++ b/src/Controller/OgDhcp/Subnet/PostHostAction.php @@ -29,29 +29,27 @@ class PostHostAction extends AbstractOgDhcpController */ public function __invoke(SubnetAddHostInput $input, Subnet $subnet, HttpClientInterface $httpClient): JsonResponse { - $clients = $input->clients; + $client = $input->client; - $subnet->setClients($clients); + /** @var Client $clientEntity */ + $clientEntity = $client->getEntity(); + + $data = [ + 'host' => $clientEntity->getName(), + 'macAddress' => strtolower($clientEntity->getMac()), + 'address' => $clientEntity->getIp(), + ]; + + $params = [ + 'json' => $data + ]; + + $content = $this->createRequest($httpClient, 'POST', $this->ogDhcpApiUrl.'/ogdhcp/v1/subnets/'.$subnet->getId().'/hosts', $params); + + $subnet->addClient($clientEntity); $this->entityManager->persist($subnet); $this->entityManager->flush(); - foreach ($clients as $client) { - /** @var Client $clientEntity */ - $clientEntity = $client->getEntity(); - - $data = [ - 'host' => $clientEntity->getName(), - 'macAddress' => $clientEntity->getMac(), - 'address' => $clientEntity->getIp(), - ]; - - $params = [ - 'json' => $data - ]; - - $content = $this->createRequest($httpClient, 'POST', $this->ogDhcpApiUrl.'/ogdhcp/v1/subnets/'.$subnet->getId().'/hosts', $params); - } - - return new JsonResponse(status: Response::HTTP_OK); + return new JsonResponse(data: $content, status: Response::HTTP_OK); } } \ No newline at end of file diff --git a/src/Dto/Input/SubnetAddHostInput.php b/src/Dto/Input/SubnetAddHostInput.php index bc5015e..0129a86 100644 --- a/src/Dto/Input/SubnetAddHostInput.php +++ b/src/Dto/Input/SubnetAddHostInput.php @@ -12,10 +12,7 @@ use Symfony\Component\Validator\Constraints as Assert; final class SubnetAddHostInput { - /** - * @var ClientOutput[] - */ + #[Assert\NotNull] #[Groups(['subnet:write'])] - #[ApiProperty(description: 'The clients of the subnet', readableLink: false, writableLink: false, example: "Client 1")] - public array $clients = []; + public ?ClientOutput $client = null; } \ No newline at end of file diff --git a/src/Dto/Input/SubnetAddSingleHostInput.php b/src/Dto/Input/SubnetAddSingleHostInput.php deleted file mode 100644 index 5075161..0000000 --- a/src/Dto/Input/SubnetAddSingleHostInput.php +++ /dev/null @@ -1,15 +0,0 @@ -bootFileName = $subnet->getBootFileName(); - $this->organizationalUnits = $subnet->getOrganizationalUnits()->map( - fn(OrganizationalUnit $organizationalUnit) => new OrganizationalUnitOutput($organizationalUnit) + $this->clients = $subnet->getClients()->map( + fn(Client $client) => new ClientOutput($client) )->toArray(); $this->createdAt = $subnet->getCreatedAt(); -- 2.40.1 From 4759f3e24f6a7711615e52d59650af8003007d37 Mon Sep 17 00:00:00 2001 From: Manuel Aranda Date: Mon, 14 Oct 2024 13:18:19 +0200 Subject: [PATCH 100/157] refs #614. Integration DHCP --- config/api_platform/Client.yaml | 1 + config/api_platform/OgLive.yaml | 2 +- config/api_platform/Subnet.yaml | 10 ++- config/services/api_platform.yaml | 17 ++++ migrations/Version20241014053130.php | 31 +++++++ migrations/Version20241014082029.php | 33 +++++++ migrations/Version20241014102105.php | 33 +++++++ src/Controller/OgBoot/OgBootController.php | 17 ---- .../OgDhcp/AbstractOgDhcpController.php | 4 +- src/Controller/OgDhcp/OgDhcpController.php | 41 +++++++++ src/Controller/OgDhcp/Subnet/DeleteAction.php | 2 +- .../OgDhcp/Subnet/DeleteHostAction.php | 14 ++- src/Controller/OgDhcp/Subnet/GetAction.php | 2 +- .../OgDhcp/Subnet/GetHostsAction.php | 2 +- src/Controller/OgDhcp/Subnet/PostAction.php | 2 + .../OgDhcp/Subnet/PostHostAction.php | 2 +- src/Controller/OgDhcp/Subnet/PutAction.php | 2 +- src/Controller/OgDhcp/Subnet/SyncAction.php | 90 +++++++++++++++++++ src/Dto/Output/ClientOutput.php | 4 + src/Dto/Output/SubnetOutput.php | 9 +- src/Entity/Client.php | 1 + src/Entity/Subnet.php | 17 +++- .../StatusService.php} | 13 ++- .../GetIpAddressAndNetmaskFromCIDRService.php | 18 ++++ 24 files changed, 330 insertions(+), 37 deletions(-) create mode 100644 migrations/Version20241014053130.php create mode 100644 migrations/Version20241014082029.php create mode 100644 migrations/Version20241014102105.php create mode 100644 src/Controller/OgDhcp/OgDhcpController.php create mode 100644 src/Controller/OgDhcp/Subnet/SyncAction.php rename src/Service/{OgBoot/ConfigService.php => OgDhcp/StatusService.php} (72%) create mode 100644 src/Service/Utils/GetIpAddressAndNetmaskFromCIDRService.php diff --git a/config/api_platform/Client.yaml b/config/api_platform/Client.yaml index 4fca0a6..641ba9a 100644 --- a/config/api_platform/Client.yaml +++ b/config/api_platform/Client.yaml @@ -13,6 +13,7 @@ resources: filters: - 'api_platform.filter.client.order' - 'api_platform.filter.client.search' + - 'api_platform.filter.client.exist' ApiPlatform\Metadata\Get: provider: App\State\Provider\ClientProvider diff --git a/config/api_platform/OgLive.yaml b/config/api_platform/OgLive.yaml index e1b3327..a40fa2a 100644 --- a/config/api_platform/OgLive.yaml +++ b/config/api_platform/OgLive.yaml @@ -24,7 +24,7 @@ resources: ApiPlatform\Metadata\Post: ~ ApiPlatform\Metadata\Delete: ~ - sync: + oglives_sync: class: ApiPlatform\Metadata\Post method: POST input: false diff --git a/config/api_platform/Subnet.yaml b/config/api_platform/Subnet.yaml index 6f384de..72ef541 100644 --- a/config/api_platform/Subnet.yaml +++ b/config/api_platform/Subnet.yaml @@ -23,6 +23,13 @@ resources: ApiPlatform\Metadata\Post: ~ ApiPlatform\Metadata\Delete: ~ + subnet_sync: + class: ApiPlatform\Metadata\Post + method: POST + input: false + uriTemplate: /subnets/sync + controller: App\Controller\OgDhcp\Subnet\SyncAction + get_collection_subnets: shortName: Subnet Server description: Get collection of Subnet @@ -101,7 +108,8 @@ resources: class: ApiPlatform\Metadata\Delete method: DELETE input: false - uriTemplate: /og-dhcp/server/{uuid}/delete-host + read: false + uriTemplate: /og-dhcp/server/{uuid}/delete-host/{clientUuid} controller: App\Controller\OgDhcp\Subnet\DeleteHostAction diff --git a/config/services/api_platform.yaml b/config/services/api_platform.yaml index cd8bc48..19567b8 100644 --- a/config/services/api_platform.yaml +++ b/config/services/api_platform.yaml @@ -23,6 +23,11 @@ services: arguments: [ { 'id': 'exact', 'name': 'partial', 'serialNumber': 'exact', organizationalUnit.id: 'exact', mac: 'exact', ip: 'exact' } ] tags: [ 'api_platform.filter' ] + api_platform.filter.client.exist: + parent: 'api_platform.doctrine.orm.exists_filter' + arguments: [{'subnet': ~ }] + tags: [ 'api_platform.filter' ] + api_platform.filter.command.order: parent: 'api_platform.doctrine.orm.order_filter' arguments: @@ -144,6 +149,18 @@ services: arguments: [ { 'id': 'exact', 'name': 'partial', } ] tags: [ 'api_platform.filter' ] + api_platform.filter.subnet.order: + parent: 'api_platform.doctrine.orm.order_filter' + arguments: + $properties: { 'id': ~, 'name': ~ } + $orderParameterName: 'order' + tags: [ 'api_platform.filter' ] + + api_platform.filter.subnet.search: + parent: 'api_platform.doctrine.orm.search_filter' + arguments: [ { 'id': 'exact', 'name': 'partial', ip: 'exact', nextServer: 'exact', netmask: 'exact', bootFileName: 'partial'} ] + tags: [ 'api_platform.filter' ] + api_platform.filter.trace.search: parent: 'api_platform.doctrine.orm.search_filter' arguments: [ { 'id': 'exact', 'command.id': 'exact', 'client.id': 'exact' } ] diff --git a/migrations/Version20241014053130.php b/migrations/Version20241014053130.php new file mode 100644 index 0000000..5abd23e --- /dev/null +++ b/migrations/Version20241014053130.php @@ -0,0 +1,31 @@ +addSql('ALTER TABLE subnet ADD server_id INT DEFAULT NULL, ADD synchronized 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 subnet DROP server_id, DROP synchronized'); + } +} diff --git a/migrations/Version20241014082029.php b/migrations/Version20241014082029.php new file mode 100644 index 0000000..7777645 --- /dev/null +++ b/migrations/Version20241014082029.php @@ -0,0 +1,33 @@ +addSql('ALTER TABLE client DROP FOREIGN KEY FK_C7440455C9CF9478'); + $this->addSql('ALTER TABLE client ADD CONSTRAINT FK_C7440455C9CF9478 FOREIGN KEY (subnet_id) REFERENCES subnet (id) ON DELETE CASCADE'); + } + + public function down(Schema $schema): void + { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql('ALTER TABLE client DROP FOREIGN KEY FK_C7440455C9CF9478'); + $this->addSql('ALTER TABLE client ADD CONSTRAINT FK_C7440455C9CF9478 FOREIGN KEY (subnet_id) REFERENCES subnet (id)'); + } +} diff --git a/migrations/Version20241014102105.php b/migrations/Version20241014102105.php new file mode 100644 index 0000000..6015cff --- /dev/null +++ b/migrations/Version20241014102105.php @@ -0,0 +1,33 @@ +addSql('ALTER TABLE client DROP FOREIGN KEY FK_C7440455C9CF9478'); + $this->addSql('ALTER TABLE client ADD CONSTRAINT FK_C7440455C9CF9478 FOREIGN KEY (subnet_id) REFERENCES subnet (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_C7440455C9CF9478'); + $this->addSql('ALTER TABLE client ADD CONSTRAINT FK_C7440455C9CF9478 FOREIGN KEY (subnet_id) REFERENCES subnet (id) ON DELETE CASCADE'); + } +} diff --git a/src/Controller/OgBoot/OgBootController.php b/src/Controller/OgBoot/OgBootController.php index f9e26f3..eaa71be 100644 --- a/src/Controller/OgBoot/OgBootController.php +++ b/src/Controller/OgBoot/OgBootController.php @@ -4,7 +4,6 @@ declare(strict_types=1); namespace App\Controller\OgBoot; -use App\Service\OgBoot\ConfigService; use App\Service\OgBoot\StatusService; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\JsonResponse; @@ -23,7 +22,6 @@ class OgBootController extends AbstractController { public function __construct( private readonly StatusService $ogbootStatusService, - private readonly ConfigService $ogbootConfigService, ) { } @@ -41,19 +39,4 @@ class OgBootController extends AbstractController return new JsonResponse( data: $data, status: Response::HTTP_OK); } - - - /** - * @throws TransportExceptionInterface - * @throws ServerExceptionInterface - * @throws RedirectionExceptionInterface - * @throws ClientExceptionInterface - */ - #[Route('/config', name: 'ogboot_config', methods: ['GET'])] - public function config(): JsonResponse - { - $data = $this->ogbootConfigService->__invoke(); - - return new JsonResponse( data: $data, status: Response::HTTP_OK); - } } diff --git a/src/Controller/OgDhcp/AbstractOgDhcpController.php b/src/Controller/OgDhcp/AbstractOgDhcpController.php index cfd81b6..2e4e6f5 100644 --- a/src/Controller/OgDhcp/AbstractOgDhcpController.php +++ b/src/Controller/OgDhcp/AbstractOgDhcpController.php @@ -4,6 +4,7 @@ declare(strict_types=1); namespace App\Controller\OgDhcp; +use App\Service\Utils\GetIpAddressAndNetmaskFromCIDRService; use Doctrine\ORM\EntityManagerInterface; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\JsonResponse; @@ -21,7 +22,8 @@ abstract class AbstractOgDhcpController extends AbstractController { public function __construct( protected readonly string $ogDhcpApiUrl, - protected readonly EntityManagerInterface $entityManager + protected readonly EntityManagerInterface $entityManager, + protected readonly GetIpAddressAndNetmaskFromCIDRService $getIpAddressAndNetmaskFromCIDRService ) { } diff --git a/src/Controller/OgDhcp/OgDhcpController.php b/src/Controller/OgDhcp/OgDhcpController.php new file mode 100644 index 0000000..f67f01f --- /dev/null +++ b/src/Controller/OgDhcp/OgDhcpController.php @@ -0,0 +1,41 @@ +ogdhcpStatusService->__invoke(); + + return new JsonResponse( data: $data, status: Response::HTTP_OK); + } +} diff --git a/src/Controller/OgDhcp/Subnet/DeleteAction.php b/src/Controller/OgDhcp/Subnet/DeleteAction.php index 8f687c5..a00555a 100644 --- a/src/Controller/OgDhcp/Subnet/DeleteAction.php +++ b/src/Controller/OgDhcp/Subnet/DeleteAction.php @@ -29,7 +29,7 @@ class DeleteAction extends AbstractOgDhcpController throw new ValidatorException('Data Id is required'); } - $content = $this->createRequest($httpClient, 'DELETE', $this->ogDhcpApiUrl.'/ogdhcp/v1/subnets/'.$data->getId()); + $content = $this->createRequest($httpClient, 'DELETE', $this->ogDhcpApiUrl.'/ogdhcp/v1/subnets/'.$data->getServerId()); $this->entityManager->remove($data); $this->entityManager->flush(); diff --git a/src/Controller/OgDhcp/Subnet/DeleteHostAction.php b/src/Controller/OgDhcp/Subnet/DeleteHostAction.php index 4544f45..cf32b80 100644 --- a/src/Controller/OgDhcp/Subnet/DeleteHostAction.php +++ b/src/Controller/OgDhcp/Subnet/DeleteHostAction.php @@ -3,6 +3,7 @@ namespace App\Controller\OgDhcp\Subnet; use App\Controller\OgDhcp\AbstractOgDhcpController; +use App\Entity\Client; use App\Entity\Subnet; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Response; @@ -23,20 +24,27 @@ class DeleteHostAction extends AbstractOgDhcpController * @throws RedirectionExceptionInterface * @throws ClientExceptionInterface */ - public function __invoke(Subnet $data, HttpClientInterface $httpClient): JsonResponse + public function __invoke(Subnet $data, string $clientUuid, HttpClientInterface $httpClient): JsonResponse { + $client = $this->entityManager->getRepository(Client::class)->findOneBy(['uuid' => $clientUuid]); + + if (!$client || $client->getSubnet() !== $data) { + throw new ValidatorException('Client not found'); + } + if (!$data->getId()) { throw new ValidatorException('Data URL is required'); } $params = [ 'json' => [ - 'host' => '', + 'macAddress' => strtolower($client->getMac()), ] ]; - $content = $this->createRequest($httpClient, 'POST', $this->ogDhcpApiUrl.'/ogdhcp/v1/subnets/'.$data->getId().'/hosts', $params); + $content = $this->createRequest($httpClient, 'DELETE', $this->ogDhcpApiUrl.'/ogdhcp/v1/subnets/'.$data->getServerId().'/hosts', $params); + $data->removeClient($client); $this->entityManager->persist($data); $this->entityManager->flush(); diff --git a/src/Controller/OgDhcp/Subnet/GetAction.php b/src/Controller/OgDhcp/Subnet/GetAction.php index b17698c..77f1bce 100644 --- a/src/Controller/OgDhcp/Subnet/GetAction.php +++ b/src/Controller/OgDhcp/Subnet/GetAction.php @@ -29,7 +29,7 @@ class GetAction extends AbstractOgDhcpController throw new ValidatorException('Checksum is required'); } - $content = $this->createRequest($httpClient, 'GET', $this->ogDhcpApiUrl.'/ogdhcp/v1/subnets/'.$data->getId()); + $content = $this->createRequest($httpClient, 'GET', $this->ogDhcpApiUrl.'/ogdhcp/v1/subnets/'.$data->getServerId()); return new JsonResponse(data: $content, status: Response::HTTP_OK); } diff --git a/src/Controller/OgDhcp/Subnet/GetHostsAction.php b/src/Controller/OgDhcp/Subnet/GetHostsAction.php index b465466..52ca307 100644 --- a/src/Controller/OgDhcp/Subnet/GetHostsAction.php +++ b/src/Controller/OgDhcp/Subnet/GetHostsAction.php @@ -29,7 +29,7 @@ class GetHostsAction extends AbstractOgDhcpController throw new ValidatorException('Checksum is required'); } - $content = $this->createRequest($httpClient, 'GET', $this->ogDhcpApiUrl.'/ogdhcp/v1/subnets/'.$data->getId().'/hosts'); + $content = $this->createRequest($httpClient, 'GET', $this->ogDhcpApiUrl.'/ogdhcp/v1/subnets/'.$data->getServerId().'/hosts'); return new JsonResponse(data: $content, status: Response::HTTP_OK); } diff --git a/src/Controller/OgDhcp/Subnet/PostAction.php b/src/Controller/OgDhcp/Subnet/PostAction.php index e9e32e4..a4824f1 100644 --- a/src/Controller/OgDhcp/Subnet/PostAction.php +++ b/src/Controller/OgDhcp/Subnet/PostAction.php @@ -37,6 +37,8 @@ class PostAction extends AbstractOgDhcpController $content = $this->createRequest($httpClient, 'POST', $this->ogDhcpApiUrl.'/ogdhcp/v1/subnets' , $params); + $data->setServerId($content['message']['id']); + $data->setSynchronized(true); $this->entityManager->persist($data); $this->entityManager->flush(); diff --git a/src/Controller/OgDhcp/Subnet/PostHostAction.php b/src/Controller/OgDhcp/Subnet/PostHostAction.php index 1b900e8..4330f8d 100644 --- a/src/Controller/OgDhcp/Subnet/PostHostAction.php +++ b/src/Controller/OgDhcp/Subnet/PostHostAction.php @@ -44,7 +44,7 @@ class PostHostAction extends AbstractOgDhcpController 'json' => $data ]; - $content = $this->createRequest($httpClient, 'POST', $this->ogDhcpApiUrl.'/ogdhcp/v1/subnets/'.$subnet->getId().'/hosts', $params); + $content = $this->createRequest($httpClient, 'POST', $this->ogDhcpApiUrl.'/ogdhcp/v1/subnets/'.$subnet->getServerId().'/hosts', $params); $subnet->addClient($clientEntity); $this->entityManager->persist($subnet); diff --git a/src/Controller/OgDhcp/Subnet/PutAction.php b/src/Controller/OgDhcp/Subnet/PutAction.php index f57b282..9e3004c 100644 --- a/src/Controller/OgDhcp/Subnet/PutAction.php +++ b/src/Controller/OgDhcp/Subnet/PutAction.php @@ -38,7 +38,7 @@ class PutAction extends AbstractOgDhcpController ] ]; - $content = $this->createRequest($httpClient, 'PUT', $this->ogDhcpApiUrl.'/ogdhcp/v1/subnets/'.$data->getId(), $params); + $content = $this->createRequest($httpClient, 'PUT', $this->ogDhcpApiUrl.'/ogdhcp/v1/subnets/'.$data->getServerId(), $params); $this->entityManager->persist($data); $this->entityManager->flush(); diff --git a/src/Controller/OgDhcp/Subnet/SyncAction.php b/src/Controller/OgDhcp/Subnet/SyncAction.php new file mode 100644 index 0000000..29c86dc --- /dev/null +++ b/src/Controller/OgDhcp/Subnet/SyncAction.php @@ -0,0 +1,90 @@ +createRequest($httpClient, 'GET', $this->ogDhcpApiUrl . '/ogdhcp/v1/subnets'); + + $arraySync = []; + + foreach ($content['message'] as $subnet) { + $subnetEntity = $this->entityManager->getRepository(Subnet::class)->findOneBy(['serverId' => $subnet['id']]); + if ($subnetEntity) { + $this->extracted($subnetEntity, $subnet); + $this->entityManager->persist($subnetEntity); + } else { + $subnetEntity = new Subnet(); + $this->extracted($subnetEntity, $subnet); + } + + $arraySync[] = $subnet['id']; + + $subnetEntity->setServerId($subnet['id']); + $subnetEntity->setSynchronized(true); + $this->entityManager->persist($subnetEntity); + } + + $this->setSynchronized($arraySync); + + $this->entityManager->flush(); + + return new JsonResponse(data: $content, status: Response::HTTP_OK); + } + + /** + * @param Subnet|null $subnetEntity + * @param mixed $subnet + * @return void + */ + private function extracted(Subnet|null $subnetEntity, mixed $subnet): void + { + $getParsedData = $this->getIpAddressAndNetmaskFromCIDRService->__invoke($subnet['subnet']); + + $subnetEntity->setName($subnet['boot-file-name']); + $subnetEntity->setBootFileName($subnet['boot-file-name']); + $subnetEntity->setIpAddress($getParsedData['ip']); + $subnetEntity->setNetmask($getParsedData['mask']); + $subnetEntity->setNextServer($subnet['next-server']); + + foreach ($subnet['reservations'] as $host) { + $hostEntity = $this->entityManager->getRepository(Client::class)->findOneBy(['mac' => $host['hw-address']]); + if ($hostEntity){ + $subnetEntity->addClient($hostEntity); + } + } + } + + public function setSynchronized(array $array): void + { + $this->entityManager->createQueryBuilder() + ->update(Subnet::class, 's') + ->set('s.synchronized', 'false') + ->where('s.serverId NOT IN (:array)') + ->setParameter('array', array_values($array)) + ->getQuery() + ->getResult(); + } +} \ No newline at end of file diff --git a/src/Dto/Output/ClientOutput.php b/src/Dto/Output/ClientOutput.php index 2bed44b..ca46c88 100644 --- a/src/Dto/Output/ClientOutput.php +++ b/src/Dto/Output/ClientOutput.php @@ -52,6 +52,9 @@ final class ClientOutput extends AbstractOutput #[ApiProperty(readableLink: true )] public ?OgLiveOutput $ogLive = null; + #[Groups(['client:read'])] + public ?string $subnet = null; + #[Groups(['client:read'])] public ?array $position = ['x' => 0, 'y' => 0]; @@ -89,6 +92,7 @@ final class ClientOutput extends AbstractOutput $this->menu = $client->getMenu() ? new MenuOutput($client->getMenu()) : null; $this->position = $client->getPosition(); $this->hardwareProfile = $client->getHardwareProfile() ? new HardwareProfileOutput($client->getHardwareProfile()) : null; + $this->subnet = $client->getSubnet() ? $client->getSubnet()->getIpAddress(). '-' . $client->getSubnet()->getNetmask() : null; $this->ogLive = $client->getOgLive() ? new OgLiveOutput($client->getOgLive()) : null; $this->status = $client->getStatus(); $this->createdAt = $client->getCreatedAt(); diff --git a/src/Dto/Output/SubnetOutput.php b/src/Dto/Output/SubnetOutput.php index f542562..e112d82 100644 --- a/src/Dto/Output/SubnetOutput.php +++ b/src/Dto/Output/SubnetOutput.php @@ -30,6 +30,12 @@ final class SubnetOutput extends AbstractOutput #[Groups(['subnet:read'])] public array $clients; + #[Groups(['subnet:read'])] + public ?bool $synchronized = false; + + #[Groups(['subnet:read'])] + public ?int $serverId; + #[Groups(['subnet:read'])] public \DateTime $createdAt; @@ -45,7 +51,8 @@ final class SubnetOutput extends AbstractOutput $this->ipAddress = $subnet->getIpAddress(); $this->nextServer = $subnet->getNextServer(); $this->bootFileName = $subnet->getBootFileName(); - + $this->synchronized = $subnet->isSynchronized(); + $this->serverId = $subnet->getServerId(); $this->clients = $subnet->getClients()->map( fn(Client $client) => new ClientOutput($client) diff --git a/src/Entity/Client.php b/src/Entity/Client.php index 4c4a2ce..28b6e72 100644 --- a/src/Entity/Client.php +++ b/src/Entity/Client.php @@ -64,6 +64,7 @@ class Client extends AbstractEntity private ?OgRepository $repository = null; #[ORM\ManyToOne(inversedBy: 'clients')] + #[ORM\JoinColumn( onDelete: 'SET NULL')] private ?Subnet $subnet = null; #[ORM\ManyToOne(inversedBy: 'clients')] private ?OgLive $ogLive = null; diff --git a/src/Entity/Subnet.php b/src/Entity/Subnet.php index 719de65..fe9dd2d 100644 --- a/src/Entity/Subnet.php +++ b/src/Entity/Subnet.php @@ -11,6 +11,7 @@ use Doctrine\ORM\Mapping as ORM; class Subnet extends AbstractEntity { use NameableTrait; + use SynchronizedTrait; #[ORM\Column(length: 255)] private ?string $netmask = null; @@ -36,6 +37,9 @@ class Subnet extends AbstractEntity #[ORM\OneToMany(mappedBy: 'subnet', targetEntity: Client::class)] private Collection $clients; + #[ORM\Column(nullable: true)] + private ?int $serverId = null; + public function __construct() { parent::__construct(); @@ -164,7 +168,6 @@ class Subnet extends AbstractEntity public function removeClient(Client $client): static { if ($this->clients->removeElement($client)) { - // set the owning side to null (unless already changed) if ($client->getSubnet() === $this) { $client->setSubnet(null); } @@ -172,4 +175,16 @@ class Subnet extends AbstractEntity return $this; } + + public function getServerId(): ?int + { + return $this->serverId; + } + + public function setServerId(?int $serverId): static + { + $this->serverId = $serverId; + + return $this; + } } diff --git a/src/Service/OgBoot/ConfigService.php b/src/Service/OgDhcp/StatusService.php similarity index 72% rename from src/Service/OgBoot/ConfigService.php rename to src/Service/OgDhcp/StatusService.php index 8b247b9..6e434bf 100644 --- a/src/Service/OgBoot/ConfigService.php +++ b/src/Service/OgDhcp/StatusService.php @@ -1,6 +1,6 @@ false, // Ignorar la verificación del certificado SSL - 'verify_host' => false, // Ignorar la verificación del nombre del host + 'verify_peer' => false, + 'verify_host' => false, ]); try { - $response = $httpClient->request('GET', $this->ogBootApiUrl.'/ogboot/v1/config', [ + $response = $httpClient->request('GET', $this->ogDhcpApiUrl.'/ogdhcp/v1/status', [ 'headers' => [ 'accept' => 'application/json', ], diff --git a/src/Service/Utils/GetIpAddressAndNetmaskFromCIDRService.php b/src/Service/Utils/GetIpAddressAndNetmaskFromCIDRService.php new file mode 100644 index 0000000..67e1c37 --- /dev/null +++ b/src/Service/Utils/GetIpAddressAndNetmaskFromCIDRService.php @@ -0,0 +1,18 @@ + $ip, + 'mask' => $mask + ]; + } +} \ No newline at end of file -- 2.40.1 From f8bfffebf110897b775bcbff8d7ca8f28af5ef40 Mon Sep 17 00:00:00 2001 From: Manuel Aranda Date: Tue, 15 Oct 2024 07:37:05 +0200 Subject: [PATCH 101/157] Updated partition and image input --- src/Dto/Input/ImageInput.php | 15 ++++++++++++--- src/Dto/Input/PartitionInput.php | 16 ++++++++++++---- src/Dto/Output/ImageOutput.php | 6 +++--- 3 files changed, 27 insertions(+), 10 deletions(-) diff --git a/src/Dto/Input/ImageInput.php b/src/Dto/Input/ImageInput.php index 7bab121..badbdc0 100644 --- a/src/Dto/Input/ImageInput.php +++ b/src/Dto/Input/ImageInput.php @@ -72,9 +72,18 @@ final class ImageInput $this->revision = $image->getRevision(); $this->info = $image->getInfo(); $this->size = $image->getSize(); - $this->client = new ClientOutput($image->getClient()); - $this->softwareProfile = new SoftwareProfileOutput($image->getSoftwareProfile()); - $this->organizationalUnit = new OrganizationalUnitOutput($image->getOrganizationalUnit()); + + if ($image->getClient()) { + $this->client = new ClientOutput($image->getClient()); + } + + if ($image->getSoftwareProfile()) { + $this->softwareProfile = new SoftwareProfileOutput($image->getSoftwareProfile()); + } + + if ($image->getOrganizationalUnit()) { + $this->organizationalUnit = new OrganizationalUnitOutput($image->getOrganizationalUnit()); + } } public function createOrUpdateEntity(?Image $image = null): Image diff --git a/src/Dto/Input/PartitionInput.php b/src/Dto/Input/PartitionInput.php index c7477a5..7bc7538 100644 --- a/src/Dto/Input/PartitionInput.php +++ b/src/Dto/Input/PartitionInput.php @@ -40,7 +40,6 @@ final class PartitionInput #[ApiProperty(description: 'The filesystem of the partition', example: "filesystem")] public ?string $filesystem = null; - #[Assert\NotNull()] #[Groups(['partition:write'])] #[ApiProperty(description: 'The operative system name of the partition', example: "Ubuntu")] public ?OperativeSystemOutput $operativeSystem = null; @@ -71,10 +70,19 @@ final class PartitionInput $this->size = $partition->getSize(); $this->cacheContent = $partition->getCacheContent(); $this->filesystem = $partition->getFilesystem(); - $this->operativeSystem = new OperativeSystemOutput($partition->getOperativeSystem()); - $this->client = new ClientOutput($partition->getClient()); + + if ($partition->getOperativeSystem()) { + $this->operativeSystem = new OperativeSystemOutput($partition->getOperativeSystem()); + } + + if ($partition->getClient()) { + $this->client = new ClientOutput($partition->getClient()); + } $this->memoryUsage = $partition->getMemoryUsage(); - $this->image = new ImageOutput($partition->getImage()); + + if ($partition->getImage()) { + $this->image = new ImageOutput($partition->getImage()); + } } public function createOrUpdateEntity(?Partition $partition = null): Partition diff --git a/src/Dto/Output/ImageOutput.php b/src/Dto/Output/ImageOutput.php index acbdff7..3f7df0c 100644 --- a/src/Dto/Output/ImageOutput.php +++ b/src/Dto/Output/ImageOutput.php @@ -54,8 +54,8 @@ final class ImageOutput extends AbstractOutput $this->revision = $image->getRevision(); $this->info = $image->getInfo(); $this->size = $image->getSize(); - $this->clientOutput = new ClientOutput($image->getClient()); - $this->softwareProfile = new SoftwareProfileOutput($image->getSoftwareProfile()); - $this->organizationalUnit = new OrganizationalUnitOutput($image->getOrganizationalUnit()); + $this->clientOutput = $image->getClient() ? new ClientOutput($image->getClient()) : null; + $this->softwareProfile = $image->getSoftwareProfile() ? new SoftwareProfileOutput($image->getSoftwareProfile()) : null; + $this->organizationalUnit = $image->getOrganizationalUnit() ? new OrganizationalUnitOutput($image->getOrganizationalUnit()) : null; } } \ No newline at end of file -- 2.40.1 From d5e36c6657235547353827e2baf9cd47a30aadc8 Mon Sep 17 00:00:00 2001 From: Manuel Aranda Date: Wed, 16 Oct 2024 09:12:03 +0200 Subject: [PATCH 102/157] refs #955. New database fields --- README.md | 1 + migrations/Version20241015154123.php | 31 +++++ migrations/Version20241016063657.php | 37 ++++++ migrations/Version20241016065729.php | 31 +++++ ...grateSoftwareAndSoftwareProfileCommand.php | 118 ++++++++++++++++-- .../OgAgent/OgAdmClientController.php | 62 +++++++-- src/Controller/OgBoot/OgLive/SyncAction.php | 9 +- src/Dto/Input/SoftwareInput.php | 7 ++ src/Dto/Output/PartitionOutput.php | 12 +- src/Dto/Output/SoftwareOutput.php | 4 + src/Entity/Partition.php | 3 +- src/Entity/Software.php | 15 +++ src/Entity/SoftwareProfile.php | 16 +++ src/Factory/SoftwareFactory.php | 1 + src/Factory/SoftwareProfileFactory.php | 1 + src/OpenApi/OpenApiFactory.php | 10 +- tests/Functional/SoftwareTest.php | 5 +- 17 files changed, 326 insertions(+), 37 deletions(-) create mode 100644 migrations/Version20241015154123.php create mode 100644 migrations/Version20241016063657.php create mode 100644 migrations/Version20241016065729.php diff --git a/README.md b/README.md index 0078882..31e23ec 100644 --- a/README.md +++ b/README.md @@ -134,6 +134,7 @@ docker exec ogcore-php php bin/console opengnsys:migration:hardware-profile #car docker exec ogcore-php php bin/console opengnsys:migration:clients #cargamos los clientes docker exec ogcore-php php bin/console opengnsys:migration:os #cargamos los sistemas operativos docker exec ogcore-php php bin/console opengnsys:migration:image #cargamos las imagenes +docker exec ogcore-php php bin/console opengnsys:migration:software-profile #cargamos los software profiles ``` ## Objetos de interés diff --git a/migrations/Version20241015154123.php b/migrations/Version20241015154123.php new file mode 100644 index 0000000..8a87c52 --- /dev/null +++ b/migrations/Version20241015154123.php @@ -0,0 +1,31 @@ +addSql('ALTER TABLE `partition` CHANGE image_id image_id 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 `partition` CHANGE image_id image_id INT NOT NULL'); + } +} diff --git a/migrations/Version20241016063657.php b/migrations/Version20241016063657.php new file mode 100644 index 0000000..340af96 --- /dev/null +++ b/migrations/Version20241016063657.php @@ -0,0 +1,37 @@ +addSql('ALTER TABLE software ADD type VARCHAR(255) NOT NULL'); + $this->addSql('ALTER TABLE software_profile ADD operative_system_id INT NOT NULL'); + $this->addSql('ALTER TABLE software_profile ADD CONSTRAINT FK_B70C3C9BF1E9F66E FOREIGN KEY (operative_system_id) REFERENCES operative_system (id)'); + $this->addSql('CREATE INDEX IDX_B70C3C9BF1E9F66E ON software_profile (operative_system_id)'); + } + + public function down(Schema $schema): void + { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql('ALTER TABLE software DROP type'); + $this->addSql('ALTER TABLE software_profile DROP FOREIGN KEY FK_B70C3C9BF1E9F66E'); + $this->addSql('DROP INDEX IDX_B70C3C9BF1E9F66E ON software_profile'); + $this->addSql('ALTER TABLE software_profile DROP operative_system_id'); + } +} diff --git a/migrations/Version20241016065729.php b/migrations/Version20241016065729.php new file mode 100644 index 0000000..a33c8e2 --- /dev/null +++ b/migrations/Version20241016065729.php @@ -0,0 +1,31 @@ +addSql('ALTER TABLE software_profile CHANGE operative_system_id operative_system_id 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 software_profile CHANGE operative_system_id operative_system_id INT NOT NULL'); + } +} diff --git a/src/Command/Migration/MigrateSoftwareAndSoftwareProfileCommand.php b/src/Command/Migration/MigrateSoftwareAndSoftwareProfileCommand.php index 667de64..a7c239b 100644 --- a/src/Command/Migration/MigrateSoftwareAndSoftwareProfileCommand.php +++ b/src/Command/Migration/MigrateSoftwareAndSoftwareProfileCommand.php @@ -2,9 +2,12 @@ namespace App\Command\Migration; +use App\Entity\OperativeSystem; use App\Entity\OrganizationalUnit; use App\Entity\Software; use App\Entity\SoftwareProfile; +use App\Model\OrganizationalUnitTypes; +use App\Model\SoftwareTypes; use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\Query\ResultSetMapping; use Doctrine\Persistence\ManagerRegistry; @@ -24,34 +27,131 @@ class MigrateSoftwareAndSoftwareProfileCommand extends Command parent::__construct(); } - protected function execute(InputInterface $input, OutputInterface $output): void + protected function execute(InputInterface $input, OutputInterface $output): int { + ini_set('memory_limit', '-1'); + + /** @var EntityManagerInterface $oldDatabaseEntityManager */ $oldDatabaseEntityManager = $this->doctrine->getManager('og_1'); $organizationalUnitRepository = $this->entityManager->getRepository(OrganizationalUnit::class); + $operativeSystemRepository = $this->entityManager->getRepository(OperativeSystem::class); $softwareProfileRepository = $this->entityManager->getRepository(SoftwareProfile::class); $softwareRepository = $this->entityManager->getRepository(Software::class); - /** Obtener los perfiles software de la base de datos antigua **/ + /** Obtener los software de la base de datos antigua **/ $rsmSoftware = new ResultSetMapping(); - $rsmSoftware->addScalarResult('idtiposoftware', 'idtiposoftware'); + $rsmSoftware->addScalarResult('idsoftware', 'idsoftware'); $rsmSoftware->addScalarResult('descripcion', 'descripcion'); + $rsmSoftware->addScalarResult('grupoid', 'softwares.grupoid'); + $rsmSoftware->addScalarResult('idcentro', 'softwares.idcentro'); + $rsmSoftware->addScalarResult('idtiposoftware', 'softwares.idtiposoftware'); - $softwareQuery = $oldDatabaseEntityManager->createNativeQuery('SELECT idtiposoftware, descripcion FROM softwares', $rsmSoftware); + $softwareQuery = $oldDatabaseEntityManager->createNativeQuery('SELECT idsoftware, softwares.idtiposoftware, softwares.descripcion, softwares.grupoid, softwares.idcentro FROM softwares ', $rsmSoftware); $softwareCollection = $softwareQuery->getResult(); - foreach ($softwareCollection as $software) { + $output->writeln("SOFTWARE TOTAL: ". count($softwareCollection)); + foreach ($softwareCollection as $software){ $softwareEntity = null; - $softwareEntity = $softwareRepository->findOneBy(['migrationId' => $software['idtiposoftware']]); - if (!$softwareEntity) { + $softwareEntity = $softwareRepository->findOneBy(['migrationId' => $software['idsoftware']]); + if(!$softwareEntity){ + + $type = match ($software['softwares.idtiposoftware']) { + 1 => SoftwareTypes::OPERATIVE_SYSTEM, + 2 => SoftwareTypes::APPLICATION, + 3 => SoftwareTypes::FILE, + default => SoftwareTypes::APPLICATION, + }; + $softwareEntity = new Software(); - $softwareEntity->setMigrationId($software['idtiposoftware']); - $softwareEntity->setName($software['descripcion']); + $softwareEntity->setMigrationId($software['idsoftware']); $softwareEntity->setDescription($software['descripcion']); + $softwareEntity->setName($software['descripcion']); + $softwareEntity->setType($type); } + $migrationId = match ($software['softwares.grupoid']) { + 0 => OrganizationalUnitTypes::ORGANIZATIONAL_UNIT . '-' . $software['softwares.idcentro'], + default => OrganizationalUnitTypes::CLASSROOMS_GROUP . '-' . $software['softwares.grupoid'], + }; + + $organizationalUnit = $organizationalUnitRepository->findOneBy(['migrationId' => $migrationId]); + + /* + if ($organizationalUnit){ + $softwareEntity->setOrganizationalUnit($organizationalUnit); + }*/ + $this->entityManager->persist($softwareEntity); } + + /** Obtener los perfiles software de la base de datos antigua **/ + $rsmSoftwareProfiles = new ResultSetMapping(); + $rsmSoftwareProfiles->addScalarResult('idperfilsoft', 'idperfilsoft'); + $rsmSoftwareProfiles->addScalarResult('descripcion', 'descripcion'); + $rsmSoftwareProfiles->addScalarResult('comentarios', 'perfilessoft.comentarios'); + $rsmSoftwareProfiles->addScalarResult('grupoid', 'perfilessoft.grupoid'); + $rsmSoftwareProfiles->addScalarResult('idcentro', 'perfilessoft.idcentro'); + $rsmSoftwareProfiles->addScalarResult('idnombreso', 'perfilessoft.idnombreso'); + + + $softwareProfilesQuery = $oldDatabaseEntityManager->createNativeQuery('SELECT idperfilsoft, descripcion, grupos.comentarios, perfilessoft.grupoid, perfilessoft.idcentro, perfilessoft.idnombreso FROM perfilessoft LEFT JOIN grupos ON perfilessoft.grupoid = grupos.idgrupo', $rsmSoftwareProfiles); + $softwareProfiles = $softwareProfilesQuery->getResult(); + + /** Perfiles software **/ + $output->writeln("PERFILES SOFTWARE TOTAL: ". count($softwareProfiles)); + foreach ($softwareProfiles as $softwareProfile){ + $softwareProfileEntity = null; + $softwareProfileEntity = $softwareProfileRepository->findOneBy(['migrationId' => $softwareProfile['idperfilsoft']]); + if(!$softwareProfileEntity){ + $softwareProfileEntity = new SoftwareProfile(); + $softwareProfileEntity->setMigrationId($softwareProfile['idperfilsoft']); + $softwareProfileEntity->setDescription($softwareProfile['descripcion']); + $softwareProfileEntity->setComments($softwareProfile['perfilessoft.comentarios']); + } + + $migrationId = match ($softwareProfile['perfilessoft.grupoid']) { + 0 => OrganizationalUnitTypes::ORGANIZATIONAL_UNIT . '-' . $softwareProfile['perfilessoft.idcentro'], + default => OrganizationalUnitTypes::CLASSROOMS_GROUP . '-' . $softwareProfile['perfilessoft.grupoid'], + }; + + $organizationalUnit = $organizationalUnitRepository->findOneBy(['migrationId' => $migrationId]); + + if ($organizationalUnit){ + $softwareProfileEntity->setOrganizationalUnit($organizationalUnit); + } + + $operativeSystem = $operativeSystemRepository->findOneBy(['migrationId' => $softwareProfile['perfilessoft.idnombreso']]); + + if ($operativeSystem){ + $softwareProfileEntity->setOperativeSystem($operativeSystem); + } + + $this->entityManager->persist($softwareProfileEntity); + } + + /** Obtener los software, y asignarselos a los perfiles software **/ + $rsmSoftwareProfilesRelation = new ResultSetMapping(); + $rsmSoftwareProfilesRelation->addScalarResult('idperfilsoft', 'idperfilsoft'); + $rsmSoftwareProfilesRelation->addScalarResult('idsoftware', 'idsoftware'); + + $softwareProfilesQuery = $oldDatabaseEntityManager->createNativeQuery('SELECT idperfilsoft, idsoftware FROM perfilessoft_softwares', $rsmSoftwareProfilesRelation); + $softwareProfileRelations = $softwareProfilesQuery->getResult(); + + $output->writeln("PERFILES SOFTWARE RELACIONES TOTAL: ". count($softwareProfileRelations)); + foreach ($softwareProfileRelations as $softwareProfileRelation){ + $softwareProfileEntity = $softwareProfileRepository->findOneBy(['migrationId' => $softwareProfileRelation['idperfilsoft']]); + $softwareEntity = $softwareProfileRepository->findOneBy(['migrationId' => $softwareProfileRelation['idsoftware']]); + + if ($softwareProfileEntity && $softwareEntity){ + $softwareProfileEntity->addHardwareCollection($softwareEntity); + $this->entityManager->persist($softwareProfileEntity); + } + } + + $this->entityManager->flush(); + + return Command::SUCCESS; } } \ No newline at end of file diff --git a/src/Controller/OgAgent/OgAdmClientController.php b/src/Controller/OgAgent/OgAdmClientController.php index 0b50352..3050e63 100644 --- a/src/Controller/OgAgent/OgAdmClientController.php +++ b/src/Controller/OgAgent/OgAdmClientController.php @@ -4,6 +4,11 @@ declare(strict_types=1); namespace App\Controller\OgAgent; +use App\Entity\Client; +use App\Entity\OrganizationalUnit; +use App\Entity\Partition; +use App\Model\OrganizationalUnitTypes; +use Doctrine\ORM\EntityManagerInterface; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Request; @@ -14,8 +19,15 @@ use Symfony\Component\Routing\Attribute\Route; #[AsController] class OgAdmClientController extends AbstractController { - #[Route('/opengnsys/rest/__ogAdmClient/InclusionCliente', methods: ['POST'])] - public function inclusionCliente(Request $request): JsonResponse + public function __construct( + protected readonly EntityManagerInterface $entityManager + ) + { + } + + + #[Route('/opengnsys/rest/ogAdmClient/InclusionCliente', methods: ['POST'])] + public function processClient(Request $request): JsonResponse { $data = $request->toArray(); $requiredFields = ['iph', 'cfg']; @@ -26,21 +38,49 @@ class OgAdmClientController extends AbstractController } } + $clientEntity = $this->entityManager->getRepository(Client::class)->findOneBy(['ip' => $data['iph']]); + + if (!$clientEntity) { + return new JsonResponse(['message' => 'Client not found'], Response::HTTP_NOT_FOUND); + } + + foreach ($data['cfg'] as $cfg) { + $partitionEntity = $this->entityManager->getRepository(Partition::class) + ->findOneBy(['client' => $clientEntity, 'diskNumber' => $cfg['disk'], 'partitionNumber' => $cfg['par']]); + + if (!$partitionEntity) { + $partitionEntity = new Partition(); + } + + $partitionEntity->setClient($clientEntity); + $partitionEntity->setDiskNumber($cfg['disk']); + //$partitionEntity->setPartitionCode($cfg['codpar']); + $partitionEntity->setPartitionNumber($cfg['par']); + $partitionEntity->setSize($cfg['tam']); + $partitionEntity->setMemoryUsage($cfg['uso']); + //$partitionEntity->setFilesystem($cfg['idsistemafichero']); + $this->entityManager->persist($partitionEntity); + $this->entityManager->flush(); + } + + $center = $this->entityManager->getRepository(OrganizationalUnit::class)->find($clientEntity->getOrganizationalUnit()->getId()); + $root = $this->entityManager->getRepository(OrganizationalUnit::class)->getRootNodes(); + $responseData = [ 'res' => 1, - 'ido' => $data['ido'] ?? 42, - 'npc' => $data['npc'] ?? 42, - 'che' => 42, + 'ido' => $clientEntity->getId(), + 'npc' => $clientEntity->getName(), + 'che' => 1, 'exe' => 42, - 'ida' => $data['ida'] ?? 42, - 'idc' => $data['idc'] ?? 42, + 'ida' => $clientEntity->getOrganizationalUnit()?->getId(), + 'idc' => $root[0]->getId(), ]; return new JsonResponse($responseData, Response::HTTP_OK); } - #[Route('/opengnsys/rest/__ogAdmClient/AutoexecCliente', methods: ['POST'])] + #[Route('/opengnsys/rest/ogAdmClient/AutoexecCliente', methods: ['POST'])] public function autoexecCliente(Request $request): JsonResponse { $data = $request->toArray(); @@ -71,7 +111,7 @@ class OgAdmClientController extends AbstractController return new JsonResponse($responseData, Response::HTTP_OK); } - #[Route('/opengnsys/rest/__ogAdmClient/enviaArchivo', methods: ['POST'])] + #[Route('/opengnsys/rest/ogAdmClient/enviaArchivo', methods: ['POST'])] public function enviaArchivo(Request $request): JsonResponse { $data = $request->toArray(); @@ -95,7 +135,7 @@ class OgAdmClientController extends AbstractController return new JsonResponse(['contents' => base64_encode($contents)], Response::HTTP_OK); } - #[Route('/opengnsys/rest/__ogAdmClient/ComandosPendientes', methods: ['POST'])] + #[Route('/opengnsys/rest/ogAdmClient/ComandosPendientes', methods: ['POST'])] public function comandosPendientes(Request $request): JsonResponse { $data = $request->toArray(); @@ -122,7 +162,7 @@ class OgAdmClientController extends AbstractController return new JsonResponse($param, Response::HTTP_OK); } - #[Route('/opengnsys/rest/__ogAdmClient/DisponibilidadComandos', methods: ['POST'])] + #[Route('/opengnsys/rest/ogAdmClient/DisponibilidadComandos', methods: ['POST'])] public function disponibilidadComandos(Request $request): JsonResponse { $data = $request->toArray(); diff --git a/src/Controller/OgBoot/OgLive/SyncAction.php b/src/Controller/OgBoot/OgLive/SyncAction.php index 4083309..198e9e0 100644 --- a/src/Controller/OgBoot/OgLive/SyncAction.php +++ b/src/Controller/OgBoot/OgLive/SyncAction.php @@ -29,7 +29,8 @@ class SyncAction extends AbstractOgBootController { $content = $this->createRequest($httpClient, 'GET', $this->ogBootApiUrl . '/ogboot/v1/oglives'); - foreach ($content['installed_ogLives'] as $ogLive) { + foreach ($content['message'] as $ogLive) { + var_dump($ogLive); $ogLiveEntity = $this->entityManager->getRepository(OgLive::class)->findOneBy(['checksum' => $ogLive['id']]); if ($ogLiveEntity) { $this->extracted($ogLiveEntity, $ogLive); @@ -41,7 +42,7 @@ class SyncAction extends AbstractOgBootController $this->entityManager->persist($ogLiveEntity); } $this->entityManager->flush(); - $this->serDefaultOgLive($content['default_oglive']); + //$this->serDefaultOgLive($content['default_oglive']); return new JsonResponse(data: $content, status: Response::HTTP_OK); } @@ -53,11 +54,11 @@ class SyncAction extends AbstractOgBootController */ private function extracted(OgLive|null $ogLiveEntity, mixed $ogLive): void { - $ogLiveEntity->setName($ogLive['filename']); + $ogLiveEntity->setName($ogLive['directory']); $ogLiveEntity->setInstalled(true); $ogLiveEntity->setArchitecture($ogLive['architecture']); $ogLiveEntity->setDistribution($ogLive['distribution']); - $ogLiveEntity->setFilename($ogLive['filename']); + $ogLiveEntity->setFilename($ogLive['directory']); $ogLiveEntity->setKernel($ogLive['kernel']); $ogLiveEntity->setRevision($ogLive['revision']); $ogLiveEntity->setDirectory($ogLive['directory']); diff --git a/src/Dto/Input/SoftwareInput.php b/src/Dto/Input/SoftwareInput.php index 624a94c..6835562 100644 --- a/src/Dto/Input/SoftwareInput.php +++ b/src/Dto/Input/SoftwareInput.php @@ -18,6 +18,11 @@ final class SoftwareInput #[ApiProperty(description: 'The description of the software', example: "Software 1 description")] public ?string $description = null; + #[Groups(['software:write'])] + #[ApiProperty(description: 'The type of the software', example: "Software 1 type")] + public ?string $type = null; + + public function __construct(?Software $software = null) { if (!$software) { @@ -26,6 +31,7 @@ final class SoftwareInput $this->name = $software->getName(); $this->description = $software->getDescription(); + $this->type = $software->getType(); } public function createOrUpdateEntity(?Software $software = null): Software @@ -36,6 +42,7 @@ final class SoftwareInput $software->setName($this->name); $software->setDescription($this->description); + $software->setType($this->type); return $software; } diff --git a/src/Dto/Output/PartitionOutput.php b/src/Dto/Output/PartitionOutput.php index f6eaadf..f9c1181 100644 --- a/src/Dto/Output/PartitionOutput.php +++ b/src/Dto/Output/PartitionOutput.php @@ -12,25 +12,25 @@ class PartitionOutput extends AbstractOutput #[Groups(['partition:read', 'client:read'])] public ?int $diskNumber = null; - #[Groups(['partition:read'])] - public ?string $partitionNumber = null; + #[Groups(['partition:read', 'client:read'])] + public ?int $partitionNumber = null; - #[Groups(['partition:read'])] + #[Groups(['partition:read', 'client:read'])] public ?string $partitionCode = null; #[Groups(['partition:read', 'client:read'])] public ?int $size = null; - #[Groups(['partition:read'])] + #[Groups(['partition:read', 'client:read'])] public ?string $cacheContent = null; - #[Groups(['partition:read'])] + #[Groups(['partition:read', 'client:read'])] public ?string $filesystem = null; #[Groups(['partition:read', 'client:read'])] public ?OperativeSystemOutput $operativeSystem = null; - #[Groups(['partition:read'])] + #[Groups(['partition:read', 'client:read'])] public ?int $memoryUsage = null; public function __construct(Partition $partition) diff --git a/src/Dto/Output/SoftwareOutput.php b/src/Dto/Output/SoftwareOutput.php index 07a4fee..3c75152 100644 --- a/src/Dto/Output/SoftwareOutput.php +++ b/src/Dto/Output/SoftwareOutput.php @@ -16,6 +16,9 @@ final class SoftwareOutput extends AbstractOutput #[Groups(['software:read'])] public ?string $description = ''; + #[Groups(['software:read'])] + public ?string $type = ''; + #[Groups(['software:read'])] public \DateTime $createdAt; @@ -28,6 +31,7 @@ final class SoftwareOutput extends AbstractOutput $this->name = $software->getName(); $this->description = $software->getDescription(); + $this->type = $software->getType(); $this->createdAt = $software->getCreatedAt(); $this->createdBy = $software->getCreatedBy(); } diff --git a/src/Entity/Partition.php b/src/Entity/Partition.php index d96409b..554763d 100644 --- a/src/Entity/Partition.php +++ b/src/Entity/Partition.php @@ -35,10 +35,11 @@ class Partition extends AbstractEntity private ?int $memoryUsage = null; #[ORM\ManyToOne(inversedBy: 'partitions')] + #[ORM\JoinColumn(nullable: true)] private ?OperativeSystem $operativeSystem = null; #[ORM\ManyToOne(inversedBy: 'partitions')] - #[ORM\JoinColumn(nullable: false)] + #[ORM\JoinColumn(nullable: true)] private ?Image $image = null; public function getDiskNumber(): ?int diff --git a/src/Entity/Software.php b/src/Entity/Software.php index 1723483..891b514 100644 --- a/src/Entity/Software.php +++ b/src/Entity/Software.php @@ -21,6 +21,9 @@ class Software extends AbstractEntity #[ORM\ManyToMany(targetEntity: SoftwareProfile::class, mappedBy: 'softwareCollection')] private Collection $softwareProfiles; + #[ORM\Column(length: 255)] + private ?string $type = null; + public function __construct() { parent::__construct(); @@ -66,4 +69,16 @@ class Software extends AbstractEntity 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/SoftwareProfile.php b/src/Entity/SoftwareProfile.php index bc0f968..38eac0f 100644 --- a/src/Entity/SoftwareProfile.php +++ b/src/Entity/SoftwareProfile.php @@ -26,6 +26,10 @@ class SoftwareProfile extends AbstractEntity #[ORM\ManyToMany(targetEntity: Software::class, inversedBy: 'softwareProfiles')] private Collection $softwareCollection; + #[ORM\ManyToOne] + #[ORM\JoinColumn(nullable: true)] + private ?OperativeSystem $operativeSystem = null; + public function __construct() { parent::__construct(); @@ -97,4 +101,16 @@ class SoftwareProfile extends AbstractEntity return $this; } + + public function getOperativeSystem(): ?OperativeSystem + { + return $this->operativeSystem; + } + + public function setOperativeSystem(?OperativeSystem $operativeSystem): static + { + $this->operativeSystem = $operativeSystem; + + return $this; + } } diff --git a/src/Factory/SoftwareFactory.php b/src/Factory/SoftwareFactory.php index 7d59325..4982727 100644 --- a/src/Factory/SoftwareFactory.php +++ b/src/Factory/SoftwareFactory.php @@ -39,6 +39,7 @@ final class SoftwareFactory extends ModelFactory return [ 'createdAt' => self::faker()->dateTime(), 'name' => self::faker()->text(255), + 'type' => self::faker()->text(255), 'updatedAt' => self::faker()->dateTime(), ]; } diff --git a/src/Factory/SoftwareProfileFactory.php b/src/Factory/SoftwareProfileFactory.php index 5a05ae4..d3d370c 100644 --- a/src/Factory/SoftwareProfileFactory.php +++ b/src/Factory/SoftwareProfileFactory.php @@ -36,6 +36,7 @@ final class SoftwareProfileFactory extends ModelFactory 'createdAt' => self::faker()->dateTime(), 'description' => self::faker()->text(255), 'updatedAt' => self::faker()->dateTime(), + 'operativeSystem' => OperativeSystemFactory::createOne()->_save(), 'organizationalUnit' => OrganizationalUnitFactory::createOne(['type' => OrganizationalUnitTypes::ORGANIZATIONAL_UNIT])->_save(), ]; } diff --git a/src/OpenApi/OpenApiFactory.php b/src/OpenApi/OpenApiFactory.php index 2513802..eb23de8 100644 --- a/src/OpenApi/OpenApiFactory.php +++ b/src/OpenApi/OpenApiFactory.php @@ -276,7 +276,7 @@ final readonly class OpenApiFactory implements OpenApiFactoryInterface )); $openApi ->getPaths() - ->addPath('/opengnsys/rest/__ogAdmClient/InclusionCliente', (new Model\PathItem())->withPost( + ->addPath('/opengnsys/rest/ogAdmClient/InclusionCliente', (new Model\PathItem())->withPost( (new Model\Operation('postInclusionCliente')) ->withTags(['OgLive agent']) ->withSummary('Add client to the list of known ones') @@ -324,7 +324,7 @@ final readonly class OpenApiFactory implements OpenApiFactoryInterface )); $openApi ->getPaths() - ->addPath('/opengnsys/rest/__ogAdmClient/AutoexecCliente', (new Model\PathItem())->withPost( + ->addPath('/opengnsys/rest/ogAdmClient/AutoexecCliente', (new Model\PathItem())->withPost( (new Model\Operation('postAutoexecCliente')) ->withTags(['OgLive agent']) ->withSummary('Create and return autoexec file for client') @@ -403,7 +403,7 @@ final readonly class OpenApiFactory implements OpenApiFactoryInterface )); $openApi ->getPaths() - ->addPath('/opengnsys/rest/__ogAdmClient/enviaArchivo', (new Model\PathItem())->withPost( + ->addPath('/opengnsys/rest/ogAdmClient/enviaArchivo', (new Model\PathItem())->withPost( (new Model\Operation('postEnviaArchivo')) ->withTags(['OgLive agent']) ->withSummary('Send the contents of a file') @@ -489,7 +489,7 @@ final readonly class OpenApiFactory implements OpenApiFactoryInterface )); $openApi ->getPaths() - ->addPath('/opengnsys/rest/__ogAdmClient/ComandosPendientes', (new Model\PathItem())->withPost( + ->addPath('/opengnsys/rest/ogAdmClient/ComandosPendientes', (new Model\PathItem())->withPost( (new Model\Operation('postComandosPendientes')) ->withTags(['OgLive agent']) ->withSummary('Retrieve pending commands for a client') @@ -578,7 +578,7 @@ final readonly class OpenApiFactory implements OpenApiFactoryInterface )); $openApi ->getPaths() - ->addPath('/opengnsys/rest/__ogAdmClient/DisponibilidadComandos', (new Model\PathItem())->withPost( + ->addPath('/opengnsys/rest/ogAdmClient/DisponibilidadComandos', (new Model\PathItem())->withPost( (new Model\Operation('postDisponibilidadComandos')) ->withTags(['OgLive agent']) ->withSummary('Check command availability for a client') diff --git a/tests/Functional/SoftwareTest.php b/tests/Functional/SoftwareTest.php index 07f41dd..f48a299 100644 --- a/tests/Functional/SoftwareTest.php +++ b/tests/Functional/SoftwareTest.php @@ -7,6 +7,7 @@ use App\Entity\Software; use App\Factory\OrganizationalUnitFactory; use App\Factory\SoftwareFactory; use App\Factory\UserFactory; +use App\Model\SoftwareTypes; use App\Model\UserGroupPermissions; use Symfony\Component\HttpFoundation\Response; use Symfony\Contracts\HttpClient\Exception\ClientExceptionInterface; @@ -59,6 +60,7 @@ class SoftwareTest extends AbstractTest $this->createClientWithCredentials()->request('POST', '/software',['json' => [ 'name' => self::SW_CREATE, + 'type' => SoftwareTypes::FILE ]]); $this->assertResponseStatusCodeSame(201); @@ -66,7 +68,8 @@ class SoftwareTest extends AbstractTest $this->assertJsonContains([ '@context' => '/contexts/SoftwareOutput', '@type' => 'Software', - 'name' => self::SW_CREATE + 'name' => self::SW_CREATE, + 'type' => SoftwareTypes::FILE ]); } -- 2.40.1 From 5173dc81f09afb4a7fa400dfd8b71ce0b91d3453 Mon Sep 17 00:00:00 2001 From: Manuel Aranda Date: Thu, 17 Oct 2024 09:04:34 +0200 Subject: [PATCH 103/157] refs #955. New database fields --- config/api_platform/SoftwareProfile.yaml | 1 + config/services/api_platform.yaml | 24 +++++++++++++ ...grateSoftwareAndSoftwareProfileCommand.php | 4 +-- src/Dto/Input/PartitionArrayInput.php | 20 +++++++++++ src/Dto/Output/OperativeSystemOutput.php | 10 +++++- src/Dto/Output/SoftwareOutput.php | 2 +- src/Dto/Output/SoftwareProfileOutput.php | 8 +++++ src/OpenApi/OpenApiFactory.php | 35 ++++++++++++++++++- .../Processor/OrganizationalUnitProcessor.php | 4 --- .../Provider/SoftwareProfileProvider.php | 2 +- 10 files changed, 100 insertions(+), 10 deletions(-) create mode 100644 src/Dto/Input/PartitionArrayInput.php diff --git a/config/api_platform/SoftwareProfile.yaml b/config/api_platform/SoftwareProfile.yaml index f5c544c..b969510 100644 --- a/config/api_platform/SoftwareProfile.yaml +++ b/config/api_platform/SoftwareProfile.yaml @@ -13,6 +13,7 @@ resources: filters: - 'api_platform.filter.software.order' - 'api_platform.filter.software.search' + ApiPlatform\Metadata\Get: provider: App\State\Provider\SoftwareProfileProvider ApiPlatform\Metadata\Put: diff --git a/config/services/api_platform.yaml b/config/services/api_platform.yaml index 19567b8..395822c 100644 --- a/config/services/api_platform.yaml +++ b/config/services/api_platform.yaml @@ -86,6 +86,18 @@ services: arguments: [ { 'id': 'exact', 'name': 'exact', 'title': 'exact' } ] tags: [ 'api_platform.filter' ] + api_platform.filter.operative_system.order: + parent: 'api_platform.doctrine.orm.order_filter' + arguments: + $properties: { 'id': ~, 'name': ~ } + $orderParameterName: 'order' + tags: [ 'api_platform.filter' ] + + api_platform.filter.operative_system.search: + parent: 'api_platform.doctrine.orm.search_filter' + arguments: [ { 'id': 'exact', 'name': 'partial' } ] + tags: [ 'api_platform.filter' ] + api_platform.filter.organizational_unit.order: parent: 'api_platform.doctrine.orm.order_filter' arguments: @@ -149,6 +161,18 @@ services: arguments: [ { 'id': 'exact', 'name': 'partial', } ] tags: [ 'api_platform.filter' ] + api_platform.filter.software.order: + parent: 'api_platform.doctrine.orm.order_filter' + arguments: + $properties: { 'id': ~, 'name': ~ } + $orderParameterName: 'order' + tags: [ 'api_platform.filter' ] + + api_platform.filter.software.search: + parent: 'api_platform.doctrine.orm.search_filter' + arguments: [ { 'id': 'exact', 'name': 'partial', type: 'exact'} ] + tags: [ 'api_platform.filter' ] + api_platform.filter.subnet.order: parent: 'api_platform.doctrine.orm.order_filter' arguments: diff --git a/src/Command/Migration/MigrateSoftwareAndSoftwareProfileCommand.php b/src/Command/Migration/MigrateSoftwareAndSoftwareProfileCommand.php index a7c239b..8439278 100644 --- a/src/Command/Migration/MigrateSoftwareAndSoftwareProfileCommand.php +++ b/src/Command/Migration/MigrateSoftwareAndSoftwareProfileCommand.php @@ -142,10 +142,10 @@ class MigrateSoftwareAndSoftwareProfileCommand extends Command $output->writeln("PERFILES SOFTWARE RELACIONES TOTAL: ". count($softwareProfileRelations)); foreach ($softwareProfileRelations as $softwareProfileRelation){ $softwareProfileEntity = $softwareProfileRepository->findOneBy(['migrationId' => $softwareProfileRelation['idperfilsoft']]); - $softwareEntity = $softwareProfileRepository->findOneBy(['migrationId' => $softwareProfileRelation['idsoftware']]); + $softwareEntity = $softwareRepository->findOneBy(['migrationId' => $softwareProfileRelation['idsoftware']]); if ($softwareProfileEntity && $softwareEntity){ - $softwareProfileEntity->addHardwareCollection($softwareEntity); + $softwareProfileEntity->addSoftwareCollection($softwareEntity); $this->entityManager->persist($softwareProfileEntity); } } diff --git a/src/Dto/Input/PartitionArrayInput.php b/src/Dto/Input/PartitionArrayInput.php new file mode 100644 index 0000000..f4303d7 --- /dev/null +++ b/src/Dto/Input/PartitionArrayInput.php @@ -0,0 +1,20 @@ +name = $operativeSystem->getName(); + $this->createdAt = $operativeSystem->getCreatedAt(); + $this->createdBy = $operativeSystem->getCreatedBy(); } } \ No newline at end of file diff --git a/src/Dto/Output/SoftwareOutput.php b/src/Dto/Output/SoftwareOutput.php index 3c75152..20da642 100644 --- a/src/Dto/Output/SoftwareOutput.php +++ b/src/Dto/Output/SoftwareOutput.php @@ -16,7 +16,7 @@ final class SoftwareOutput extends AbstractOutput #[Groups(['software:read'])] public ?string $description = ''; - #[Groups(['software:read'])] + #[Groups(['software:read', 'software-profile:read'])] public ?string $type = ''; #[Groups(['software:read'])] diff --git a/src/Dto/Output/SoftwareProfileOutput.php b/src/Dto/Output/SoftwareProfileOutput.php index 5dc21e0..e306127 100644 --- a/src/Dto/Output/SoftwareProfileOutput.php +++ b/src/Dto/Output/SoftwareProfileOutput.php @@ -20,6 +20,9 @@ final class SoftwareProfileOutput extends AbstractOutput #[Groups(['software-profile:read'])] public ?OrganizationalUnitOutput $organizationalUnit = null; + #[Groups(['software-profile:read'])] + public ?OperativeSystemOutput $operativeSystem = null; + #[Groups(['software-profile:read'])] public ?array $softwareCollection = []; @@ -35,10 +38,15 @@ final class SoftwareProfileOutput extends AbstractOutput $this->description = $softwareProfile->getDescription(); $this->comments = $softwareProfile->getComments(); + if($softwareProfile->getOrganizationalUnit()) { $this->organizationalUnit = new OrganizationalUnitOutput($softwareProfile->getOrganizationalUnit()); } + if ($softwareProfile->getOperativeSystem()) { + $this->operativeSystem = new OperativeSystemOutput($softwareProfile->getOperativeSystem()); + } + $this->softwareCollection = $softwareProfile->getSoftwareCollection()->map( fn(Software $hardware) => new SoftwareOutput($hardware) )->toArray(); diff --git a/src/OpenApi/OpenApiFactory.php b/src/OpenApi/OpenApiFactory.php index eb23de8..2f4935e 100644 --- a/src/OpenApi/OpenApiFactory.php +++ b/src/OpenApi/OpenApiFactory.php @@ -316,7 +316,40 @@ final readonly class OpenApiFactory implements OpenApiFactoryInterface 'description' => 'Success', 'content' => [ 'application/json' => [ - 'schema' => ['$ref' => '#/components/schemas/InclusionClienteRes'], + 'schema' => [ + 'type' => 'object', + 'properties' => [ + 'res' => [ + 'type' => 'integer', + 'example' => 1, + ], + 'ido' => [ + 'type' => 'integer', + 'example' => 1, + ], + 'npc' => [ + 'type' => 'string', + 'example' => 'Nombre del cliente', + ], + 'che' => [ + 'type' => 'integer', + 'example' => 1, + ], + 'exe' => [ + 'type' => 'integer', + 'example' => 42, + ], + 'ida' => [ + 'type' => 'integer', + 'example' => 1, + ], + 'idc' => [ + 'type' => 'integer', + 'example' => 1, + ], + ], + 'required' => ['res', 'ido', 'npc', 'che', 'exe', 'ida', 'idc'], + ], ], ], ], diff --git a/src/State/Processor/OrganizationalUnitProcessor.php b/src/State/Processor/OrganizationalUnitProcessor.php index 468e410..e4d2244 100644 --- a/src/State/Processor/OrganizationalUnitProcessor.php +++ b/src/State/Processor/OrganizationalUnitProcessor.php @@ -11,11 +11,7 @@ use ApiPlatform\State\ProcessorInterface; use ApiPlatform\Validator\ValidatorInterface; use App\Dto\Input\ChangeOrganizationalUnitInput; use App\Dto\Input\MenuInput; -use App\Dto\Input\OrganizationalUnitClassroomGroupInput; -use App\Dto\Input\OrganizationalUnitClassroomInput; -use App\Dto\Input\OrganizationalUnitClientGroupInput; use App\Dto\Input\OrganizationalUnitInput; -use App\Dto\Input\OrganizationalUnitRootInput; use App\Dto\Output\OrganizationalUnitOutput; use App\Repository\OrganizationalUnitRepository; use App\Service\ChangeClientNetworkSettingsService; diff --git a/src/State/Provider/SoftwareProfileProvider.php b/src/State/Provider/SoftwareProfileProvider.php index 94f9aed..8d34140 100644 --- a/src/State/Provider/SoftwareProfileProvider.php +++ b/src/State/Provider/SoftwareProfileProvider.php @@ -41,7 +41,7 @@ readonly class SoftwareProfileProvider implements ProviderInterface $items = new \ArrayObject(); foreach ($paginator->getIterator() as $item){ - $items[] = new SoftwareProfileInput($item); + $items[] = new SoftwareProfileOutput($item); } return new TraversablePaginator($items, $paginator->getCurrentPage(), $paginator->getItemsPerPage(), $paginator->getTotalItems()); -- 2.40.1 From 7e6a0a2a1be9d18c6f3da6e386cbae9c56dc9b16 Mon Sep 17 00:00:00 2001 From: Manuel Aranda Date: Thu, 17 Oct 2024 13:36:33 +0200 Subject: [PATCH 104/157] refs #955. Updated Partition API --- config/api_platform/SoftwareProfile.yaml | 4 ++-- config/services/api_platform.yaml | 12 +++++++++++ .../Webhook/InstallOgLiveResponseAction.php | 20 +++++++++---------- src/Dto/Input/PartitionArrayInput.php | 20 ------------------- src/Dto/Input/PartitionInput.php | 11 +++++++--- src/Dto/Input/SoftwareProfileInput.php | 14 +++++++++++++ src/State/Provider/ImageProvider.php | 2 +- 7 files changed, 46 insertions(+), 37 deletions(-) delete mode 100644 src/Dto/Input/PartitionArrayInput.php diff --git a/config/api_platform/SoftwareProfile.yaml b/config/api_platform/SoftwareProfile.yaml index b969510..aaf6a12 100644 --- a/config/api_platform/SoftwareProfile.yaml +++ b/config/api_platform/SoftwareProfile.yaml @@ -11,8 +11,8 @@ resources: ApiPlatform\Metadata\GetCollection: provider: App\State\Provider\SoftwareProfileProvider filters: - - 'api_platform.filter.software.order' - - 'api_platform.filter.software.search' + - 'api_platform.filter.software_profile.order' + - 'api_platform.filter.software_profile.search' ApiPlatform\Metadata\Get: provider: App\State\Provider\SoftwareProfileProvider diff --git a/config/services/api_platform.yaml b/config/services/api_platform.yaml index 395822c..d27362a 100644 --- a/config/services/api_platform.yaml +++ b/config/services/api_platform.yaml @@ -173,6 +173,18 @@ services: arguments: [ { 'id': 'exact', 'name': 'partial', type: 'exact'} ] tags: [ 'api_platform.filter' ] + api_platform.filter.software_profile.order: + parent: 'api_platform.doctrine.orm.order_filter' + arguments: + $properties: { 'id': ~, 'description': ~ } + $orderParameterName: 'order' + tags: [ 'api_platform.filter' ] + + api_platform.filter.software_profile.search: + parent: 'api_platform.doctrine.orm.search_filter' + arguments: [ { 'id': 'exact', 'description': 'partial' } ] + tags: [ 'api_platform.filter' ] + api_platform.filter.subnet.order: parent: 'api_platform.doctrine.orm.order_filter' arguments: diff --git a/src/Controller/OgBoot/OgLive/Webhook/InstallOgLiveResponseAction.php b/src/Controller/OgBoot/OgLive/Webhook/InstallOgLiveResponseAction.php index f5e7cbc..3c06b6c 100644 --- a/src/Controller/OgBoot/OgLive/Webhook/InstallOgLiveResponseAction.php +++ b/src/Controller/OgBoot/OgLive/Webhook/InstallOgLiveResponseAction.php @@ -38,15 +38,13 @@ class InstallOgLiveResponseAction extends AbstractController { $data = json_decode($request->getContent(), true); - if ($data === null || !isset($data['message'], $data['ogCoreId'], $data['details'])) { + if ($data === null || !isset($data['message'], $data['ogCoreId'], $data['status'])) { return new JsonResponse(['error' => 'Invalid or incomplete JSON data'], Response::HTTP_BAD_REQUEST); } $message = $data['message']; $ogCoreId = $data['ogCoreId']; - $details = $data['details']; $status = $data['status']; - $result = $status === self::OG_LIVE_INSTALL_SUCCESS ? $details['result'] : []; $ogLive = $this->entityManager->getRepository(OgLive::class)->findOneBy(['uuid' => $ogCoreId]); @@ -58,22 +56,22 @@ class InstallOgLiveResponseAction extends AbstractController return new JsonResponse(['error' => 'OgLive is already active'], Response::HTTP_BAD_REQUEST); } - $this->updateOgLive($ogLive, $details, $result, $status); + $this->updateOgLive($ogLive, $message, $status); return new JsonResponse(data: sprintf('OgLive %s updated successfully', $ogLive->getChecksum()), status: Response::HTTP_OK); } - private function updateOgLive (OgLive $ogLive, array $details, array $result, string $status): void + private function updateOgLive (OgLive $ogLive, array $details, string $status): void { if ($status === self::OG_LIVE_INSTALL_SUCCESS) { $ogLive->setInstalled(true); $ogLive->setSynchronized(true); - $ogLive->setChecksum($result['id']); - $ogLive->setDistribution($result['distribution']); - $ogLive->setKernel($result['kernel']); - $ogLive->setArchitecture($result['architecture']); - $ogLive->setRevision($result['revision']); - $ogLive->setDirectory($result['directory']); + $ogLive->setChecksum($details['id']); + $ogLive->setDistribution($details['distribution']); + $ogLive->setKernel($details['kernel']); + $ogLive->setArchitecture($details['architecture']); + $ogLive->setRevision($details['revision']); + $ogLive->setDirectory($details['directory']); } $ogLive->setStatus($status === self::OG_LIVE_INSTALL_SUCCESS ? OgLiveStatus::ACTIVE : OgLiveStatus::FAILED); diff --git a/src/Dto/Input/PartitionArrayInput.php b/src/Dto/Input/PartitionArrayInput.php deleted file mode 100644 index f4303d7..0000000 --- a/src/Dto/Input/PartitionArrayInput.php +++ /dev/null @@ -1,20 +0,0 @@ -setSize($this->size * 100); $partition->setCacheContent($this->cacheContent); $partition->setFilesystem($this->filesystem); - $partition->setOperativeSystem($this->operativeSystem->getEntity()); + + if ($this->operativeSystem) { + $partition->setOperativeSystem($this->operativeSystem->getEntity()); + } $partition->setClient($this->client->getEntity()); $partition->setMemoryUsage($this->memoryUsage * 100); - $partition->setImage($this->image->getEntity()); + + if ($this->image) { + $partition->setImage($this->image->getEntity()); + } return $partition; } diff --git a/src/Dto/Input/SoftwareProfileInput.php b/src/Dto/Input/SoftwareProfileInput.php index c803d82..87ee072 100644 --- a/src/Dto/Input/SoftwareProfileInput.php +++ b/src/Dto/Input/SoftwareProfileInput.php @@ -3,6 +3,7 @@ namespace App\Dto\Input; use ApiPlatform\Metadata\ApiProperty; +use App\Dto\Output\OperativeSystemOutput; use App\Dto\Output\OrganizationalUnitOutput; use App\Entity\SoftwareProfile; use Symfony\Component\Serializer\Annotation\Groups; @@ -21,6 +22,11 @@ final class SoftwareProfileInput #[ApiProperty(description: 'The organizational unit of the software profile')] public ?OrganizationalUnitOutput $organizationalUnit = null; + #[Groups(['software-profile:write'])] + #[ApiProperty(description: 'The operative system of the software profile')] + public ?OperativeSystemOutput $operativeSystem = null; + + public function __construct(?SoftwareProfile $softwareProfile = null) { if (!$softwareProfile) { @@ -32,6 +38,10 @@ final class SoftwareProfileInput if($softwareProfile->getOrganizationalUnit()) { $this->organizationalUnit = new OrganizationalUnitOutput($softwareProfile->getOrganizationalUnit()); } + + if($softwareProfile->getOperativeSystem()) { + $this->operativeSystem = new OperativeSystemOutput($softwareProfile->getOperativeSystem()); + } } public function createOrUpdateEntity(?SoftwareProfile $softwareProfile = null): SoftwareProfile @@ -46,6 +56,10 @@ final class SoftwareProfileInput $softwareProfile->setOrganizationalUnit($this->organizationalUnit->getEntity()); } + if ($this->operativeSystem) { + $softwareProfile->setOperativeSystem($this->operativeSystem->getEntity()); + } + return $softwareProfile; } } \ No newline at end of file diff --git a/src/State/Provider/ImageProvider.php b/src/State/Provider/ImageProvider.php index d807b4a..be46ddc 100644 --- a/src/State/Provider/ImageProvider.php +++ b/src/State/Provider/ImageProvider.php @@ -52,7 +52,7 @@ readonly class ImageProvider implements ProviderInterface $item = $this->itemProvider->provide($operation, $uriVariables, $context); if (!$item) { - throw new NotFoundHttpException('Menu not found'); + throw new NotFoundHttpException('Image not found'); } return new ImageOutput($item); -- 2.40.1 From 88b3d8aaa9a567765f20d637f744373bc76b0b76 Mon Sep 17 00:00:00 2001 From: Manuel Aranda Date: Thu, 17 Oct 2024 14:28:36 +0200 Subject: [PATCH 105/157] refs #658. Updated webhook data --- .../OgBoot/OgLive/Webhook/InstallOgLiveResponseAction.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/Controller/OgBoot/OgLive/Webhook/InstallOgLiveResponseAction.php b/src/Controller/OgBoot/OgLive/Webhook/InstallOgLiveResponseAction.php index 3c06b6c..71e8a2c 100644 --- a/src/Controller/OgBoot/OgLive/Webhook/InstallOgLiveResponseAction.php +++ b/src/Controller/OgBoot/OgLive/Webhook/InstallOgLiveResponseAction.php @@ -38,6 +38,12 @@ class InstallOgLiveResponseAction extends AbstractController { $data = json_decode($request->getContent(), true); + if (!is_array($data)) { + return new JsonResponse(['error' => 'Invalid JSON data'], Response::HTTP_BAD_REQUEST); + } + + $data = $data['webhookData']; + if ($data === null || !isset($data['message'], $data['ogCoreId'], $data['status'])) { return new JsonResponse(['error' => 'Invalid or incomplete JSON data'], Response::HTTP_BAD_REQUEST); } -- 2.40.1 From ef3bda1322f759f6fb6ef5ba2e8b106d627157e5 Mon Sep 17 00:00:00 2001 From: Manuel Aranda Date: Fri, 18 Oct 2024 09:19:11 +0200 Subject: [PATCH 106/157] refs #658. Update obBoot sync parametrs --- src/Controller/OgBoot/OgLive/SyncAction.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Controller/OgBoot/OgLive/SyncAction.php b/src/Controller/OgBoot/OgLive/SyncAction.php index 198e9e0..074ed1e 100644 --- a/src/Controller/OgBoot/OgLive/SyncAction.php +++ b/src/Controller/OgBoot/OgLive/SyncAction.php @@ -30,7 +30,6 @@ class SyncAction extends AbstractOgBootController $content = $this->createRequest($httpClient, 'GET', $this->ogBootApiUrl . '/ogboot/v1/oglives'); foreach ($content['message'] as $ogLive) { - var_dump($ogLive); $ogLiveEntity = $this->entityManager->getRepository(OgLive::class)->findOneBy(['checksum' => $ogLive['id']]); if ($ogLiveEntity) { $this->extracted($ogLiveEntity, $ogLive); -- 2.40.1 From a2171ceb479450db77899c289ea4f08bd22a1e6d Mon Sep 17 00:00:00 2001 From: Manuel Aranda Date: Fri, 18 Oct 2024 09:20:33 +0200 Subject: [PATCH 107/157] refs #978. Image API updates --- config/services/api_platform.yaml | 12 +++++++++ migrations/Version20241018055534.php | 39 ++++++++++++++++++++++++++++ migrations/Version20241018060155.php | 31 ++++++++++++++++++++++ src/Dto/Input/ImageInput.php | 24 +++-------------- src/Dto/Output/ImageOutput.php | 12 ++++++--- src/Entity/Image.php | 29 +++++---------------- src/Factory/ImageFactory.php | 3 +-- tests/Functional/ImageTest.php | 8 ------ 8 files changed, 100 insertions(+), 58 deletions(-) create mode 100644 migrations/Version20241018055534.php create mode 100644 migrations/Version20241018060155.php diff --git a/config/services/api_platform.yaml b/config/services/api_platform.yaml index d27362a..82f3fd0 100644 --- a/config/services/api_platform.yaml +++ b/config/services/api_platform.yaml @@ -52,6 +52,18 @@ services: $orderParameterName: 'order' tags: [ 'api_platform.filter' ] + api_platform.filter.image.order: + parent: 'api_platform.doctrine.orm.order_filter' + arguments: + $properties: { 'id': ~, 'name': ~ } + $orderParameterName: 'order' + tags: [ 'api_platform.filter' ] + + api_platform.filter.image.search: + parent: 'api_platform.doctrine.orm.search_filter' + arguments: [ { 'id': 'exact', 'name': 'partial', } ] + tags: [ 'api_platform.filter' ] + api_platform.filter.og_live.order: parent: 'api_platform.doctrine.orm.order_filter' arguments: diff --git a/migrations/Version20241018055534.php b/migrations/Version20241018055534.php new file mode 100644 index 0000000..0c1d5ab --- /dev/null +++ b/migrations/Version20241018055534.php @@ -0,0 +1,39 @@ +addSql('ALTER TABLE image DROP FOREIGN KEY FK_C53D045FFB84408A'); + $this->addSql('ALTER TABLE image DROP FOREIGN KEY FK_C53D045F19EB6921'); + $this->addSql('DROP INDEX IDX_C53D045F19EB6921 ON image'); + $this->addSql('DROP INDEX IDX_C53D045FFB84408A ON image'); + $this->addSql('ALTER TABLE image DROP client_id, DROP 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 image ADD client_id INT NOT NULL, ADD organizational_unit_id INT NOT NULL'); + $this->addSql('ALTER TABLE image ADD CONSTRAINT FK_C53D045FFB84408A FOREIGN KEY (organizational_unit_id) REFERENCES organizational_unit (id)'); + $this->addSql('ALTER TABLE image ADD CONSTRAINT FK_C53D045F19EB6921 FOREIGN KEY (client_id) REFERENCES client (id)'); + $this->addSql('CREATE INDEX IDX_C53D045F19EB6921 ON image (client_id)'); + $this->addSql('CREATE INDEX IDX_C53D045FFB84408A ON image (organizational_unit_id)'); + } +} diff --git a/migrations/Version20241018060155.php b/migrations/Version20241018060155.php new file mode 100644 index 0000000..48d75fb --- /dev/null +++ b/migrations/Version20241018060155.php @@ -0,0 +1,31 @@ +addSql('ALTER TABLE image ADD remote_pc 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 image DROP remote_pc'); + } +} diff --git a/src/Dto/Input/ImageInput.php b/src/Dto/Input/ImageInput.php index badbdc0..c1b2e84 100644 --- a/src/Dto/Input/ImageInput.php +++ b/src/Dto/Input/ImageInput.php @@ -46,17 +46,12 @@ final class ImageInput #[ApiProperty(description: 'The size of the image', example: 1024)] public ?int $size = null; - #[Groups(['image:write'])] - #[ApiProperty(description: 'The client of the image')] - public ?ClientOutput $client = null; - #[Groups(['image:write'])] #[ApiProperty(description: 'The software profile of the image')] public ?SoftwareProfileOutput $softwareProfile = null; #[Groups(['image:write'])] - #[ApiProperty(description: 'The organizational unit of the image')] - public ?OrganizationalUnitOutput $organizationalUnit = null; + public ?bool $remotePc = false; public function __construct(?Image $image = null) { @@ -72,18 +67,11 @@ final class ImageInput $this->revision = $image->getRevision(); $this->info = $image->getInfo(); $this->size = $image->getSize(); - - if ($image->getClient()) { - $this->client = new ClientOutput($image->getClient()); - } + $this->remotePc = $image->isRemotePc(); if ($image->getSoftwareProfile()) { $this->softwareProfile = new SoftwareProfileOutput($image->getSoftwareProfile()); } - - if ($image->getOrganizationalUnit()) { - $this->organizationalUnit = new OrganizationalUnitOutput($image->getOrganizationalUnit()); - } } public function createOrUpdateEntity(?Image $image = null): Image @@ -100,14 +88,8 @@ final class ImageInput $image->setRevision($this->revision); $image->setInfo($this->info); $image->setSize($this->size); - $image->setClient($this->client->getEntity()); $image->setSoftwareProfile($this->softwareProfile->getEntity()); - - if ($this->organizationalUnit) { - $image->setOrganizationalUnit($this->organizationalUnit->getEntity()); - } else { - $image->setOrganizationalUnit($this->client->getEntity()->getOrganizationalUnit()); - } + $image->setRemotePc($this->remotePc); return $image; } diff --git a/src/Dto/Output/ImageOutput.php b/src/Dto/Output/ImageOutput.php index 3f7df0c..fe91429 100644 --- a/src/Dto/Output/ImageOutput.php +++ b/src/Dto/Output/ImageOutput.php @@ -34,13 +34,16 @@ final class ImageOutput extends AbstractOutput public ?int $size = null; #[Groups(['image:read'])] - public ?ClientOutput $clientOutput = null; + public ?bool $remotePc = null; #[Groups(['image:read'])] public ?SoftwareProfileOutput $softwareProfile = null; #[Groups(['image:read'])] - public ?OrganizationalUnitOutput $organizationalUnit = null; + public \DateTime $createdAt; + + #[Groups(['image:read'])] + public ?string $createdBy = null; public function __construct(Image $image) { @@ -54,8 +57,9 @@ final class ImageOutput extends AbstractOutput $this->revision = $image->getRevision(); $this->info = $image->getInfo(); $this->size = $image->getSize(); - $this->clientOutput = $image->getClient() ? new ClientOutput($image->getClient()) : null; $this->softwareProfile = $image->getSoftwareProfile() ? new SoftwareProfileOutput($image->getSoftwareProfile()) : null; - $this->organizationalUnit = $image->getOrganizationalUnit() ? new OrganizationalUnitOutput($image->getOrganizationalUnit()) : null; + $this->remotePc = $image->isRemotePc(); + $this->createdAt = $image->getCreatedAt(); + $this->createdBy = $image->getCreatedBy(); } } \ No newline at end of file diff --git a/src/Entity/Image.php b/src/Entity/Image.php index bd25a0a..7dc8c05 100644 --- a/src/Entity/Image.php +++ b/src/Entity/Image.php @@ -33,10 +33,6 @@ class Image extends AbstractEntity #[ORM\Column(length: 255, nullable: true)] private ?int $size = null; - #[ORM\ManyToOne] - #[ORM\JoinColumn(nullable: false)] - private ?Client $client = null; - #[ORM\ManyToOne] #[ORM\JoinColumn(nullable: true)] private ?SoftwareProfile $softwareProfile = null; @@ -47,9 +43,8 @@ class Image extends AbstractEntity #[ORM\OneToMany(mappedBy: 'image', targetEntity: Partition::class)] private Collection $partitions; - #[ORM\ManyToOne(inversedBy: 'images')] - #[ORM\JoinColumn(nullable: false)] - private ?OrganizationalUnit $organizationalUnit = null; + #[ORM\Column] + private ?bool $remotePc = null; public function __construct() { @@ -141,18 +136,6 @@ class Image extends AbstractEntity return $this; } - public function getClient(): ?Client - { - return $this->client; - } - - public function setClient(?Client $client): static - { - $this->client = $client; - - return $this; - } - public function getSoftwareProfile(): ?SoftwareProfile { return $this->softwareProfile; @@ -195,14 +178,14 @@ class Image extends AbstractEntity return $this; } - public function getOrganizationalUnit(): ?OrganizationalUnit + public function isRemotePc(): ?bool { - return $this->organizationalUnit; + return $this->remotePc; } - public function setOrganizationalUnit(?OrganizationalUnit $organizationalUnit): static + public function setRemotePc(bool $remotePc): static { - $this->organizationalUnit = $organizationalUnit; + $this->remotePc = $remotePc; return $this; } diff --git a/src/Factory/ImageFactory.php b/src/Factory/ImageFactory.php index 7e068af..52822df 100644 --- a/src/Factory/ImageFactory.php +++ b/src/Factory/ImageFactory.php @@ -33,14 +33,13 @@ final class ImageFactory extends ModelFactory protected function getDefaults(): array { return [ - 'client' => ClientFactory::new(), 'createdAt' => self::faker()->dateTime(), 'name' => self::faker()->text(255), 'path' => self::faker()->text(255), 'size' => self::faker()->randomNumber(), 'softwareProfile' => SoftwareProfileFactory::new(), - 'organizationalUnit' => OrganizationalUnitFactory::createOne(['type' => OrganizationalUnitTypes::ORGANIZATIONAL_UNIT])->_save(), 'updatedAt' => self::faker()->dateTime(), + 'remotePc' => self::faker()->boolean(), ]; } diff --git a/tests/Functional/ImageTest.php b/tests/Functional/ImageTest.php index 93eb45d..eee855f 100644 --- a/tests/Functional/ImageTest.php +++ b/tests/Functional/ImageTest.php @@ -64,22 +64,14 @@ class ImageTest extends AbstractTest { UserFactory::createOne(['username' => self::USER_ADMIN, 'roles'=> [UserGroupPermissions::ROLE_SUPER_ADMIN]]); - ClientFactory::createOne(['name' => self::CLIENT]); - $clientIri = $this->findIriBy(Client::class, ['name' => self::CLIENT]); - SoftwareProfileFactory::createOne(['description' => self::SOFTWARE_PROFILE]); $swPIri = $this->findIriBy(SoftwareProfile::class, ['description' => self::SOFTWARE_PROFILE]); - OrganizationalUnitFactory::createOne(['type' => OrganizationalUnitTypes::ORGANIZATIONAL_UNIT]); - $ouIri = $this->findIriBy(OrganizationalUnit::class, ['type' => OrganizationalUnitTypes::ORGANIZATIONAL_UNIT]); - $this->createClientWithCredentials()->request('POST', '/images',['json' => [ 'name' => self::IMAGE_CREATE, 'size' => 123, 'path' => '/path/to/image', - 'client' => $clientIri, 'softwareProfile' => $swPIri, - 'organizationalUnit' => $ouIri, ]]); $this->assertResponseStatusCodeSame(201); -- 2.40.1 From de5f71911170d2eb84eda835ed1493b3b5ecc763 Mon Sep 17 00:00:00 2001 From: Manuel Aranda Date: Fri, 18 Oct 2024 09:21:44 +0200 Subject: [PATCH 108/157] refs #955. Updates Partition/SW-P/SW --- config/api_platform/SoftwareProfile.yaml | 2 ++ src/Dto/Input/PartitionInput.php | 2 +- src/Dto/Output/OgLiveOutput.php | 2 +- src/Dto/Output/PartitionOutput.php | 2 +- src/Dto/Output/SoftwareProfileOutput.php | 4 ++-- 5 files changed, 7 insertions(+), 5 deletions(-) diff --git a/config/api_platform/SoftwareProfile.yaml b/config/api_platform/SoftwareProfile.yaml index aaf6a12..899cad8 100644 --- a/config/api_platform/SoftwareProfile.yaml +++ b/config/api_platform/SoftwareProfile.yaml @@ -16,6 +16,8 @@ resources: ApiPlatform\Metadata\Get: provider: App\State\Provider\SoftwareProfileProvider + normalizationContext: + groups: ['software-profile:item:get', 'software-profile:read:collection:short'] ApiPlatform\Metadata\Put: provider: App\State\Provider\SoftwareProfileProvider ApiPlatform\Metadata\Patch: diff --git a/src/Dto/Input/PartitionInput.php b/src/Dto/Input/PartitionInput.php index a9a7a45..635149f 100644 --- a/src/Dto/Input/PartitionInput.php +++ b/src/Dto/Input/PartitionInput.php @@ -93,7 +93,7 @@ final class PartitionInput $partition->setDiskNumber($this->diskNumber); $partition->setPartitionNumber($this->partitionNumber); $partition->setPartitionCode($this->partitionCode); - $partition->setSize($this->size * 100); + $partition->setSize($this->size * 1024); $partition->setCacheContent($this->cacheContent); $partition->setFilesystem($this->filesystem); diff --git a/src/Dto/Output/OgLiveOutput.php b/src/Dto/Output/OgLiveOutput.php index 60b0d3d..b881f35 100644 --- a/src/Dto/Output/OgLiveOutput.php +++ b/src/Dto/Output/OgLiveOutput.php @@ -10,7 +10,7 @@ use Symfony\Component\Serializer\Annotation\Groups; #[Get(shortName: 'OgLive')] final class OgLiveOutput extends AbstractOutput { - #[Groups(['og-live:read'])] + #[Groups(['og-live:read', 'client:read'])] public string $name; #[Groups(['og-live:read'])] diff --git a/src/Dto/Output/PartitionOutput.php b/src/Dto/Output/PartitionOutput.php index f9c1181..a9bb3d7 100644 --- a/src/Dto/Output/PartitionOutput.php +++ b/src/Dto/Output/PartitionOutput.php @@ -40,7 +40,7 @@ class PartitionOutput extends AbstractOutput $this->diskNumber = $partition->getDiskNumber(); $this->partitionNumber = $partition->getPartitionNumber(); $this->partitionCode = $partition->getPartitionCode(); - $this->size = $partition->getSize() / 100; + $this->size = $partition->getSize() / 1024 ; $this->cacheContent = $partition->getCacheContent(); $this->filesystem = $partition->getFilesystem(); if ($partition->getOperativeSystem()) { diff --git a/src/Dto/Output/SoftwareProfileOutput.php b/src/Dto/Output/SoftwareProfileOutput.php index e306127..1b2f423 100644 --- a/src/Dto/Output/SoftwareProfileOutput.php +++ b/src/Dto/Output/SoftwareProfileOutput.php @@ -11,7 +11,7 @@ use Symfony\Component\Serializer\Annotation\Groups; #[Get(shortName: 'SoftwareProfile')] final class SoftwareProfileOutput extends AbstractOutput { - #[Groups(['software-profile:read'])] + #[Groups(['software-profile:read', 'image:read'])] public ?string $description = ''; #[Groups(['software-profile:read'])] @@ -23,7 +23,7 @@ final class SoftwareProfileOutput extends AbstractOutput #[Groups(['software-profile:read'])] public ?OperativeSystemOutput $operativeSystem = null; - #[Groups(['software-profile:read'])] + #[Groups(['software-profile:item:get'])] public ?array $softwareCollection = []; #[Groups(['software-profile:read'])] -- 2.40.1 From ba65454c38b9a6612a422b19aa41ec93b0f9f7fb Mon Sep 17 00:00:00 2001 From: Manuel Aranda Date: Tue, 22 Oct 2024 07:34:34 +0200 Subject: [PATCH 109/157] refs #649. Integration ogBoot.Demo --- config/api_platform/Client.yaml | 8 ++ config/api_platform/PxeBootFile.yaml | 40 --------- config/api_platform/PxeTemplate.yaml | 39 ++++++++- config/services.yaml | 5 -- config/services/api_platform.yaml | 4 +- migrations/Version20241019073142.php | 31 +++++++ migrations/Version20241021061008.php | 33 ++++++++ migrations/Version20241021071250.php | 43 ++++++++++ migrations/Version20241021201438.php | 31 +++++++ src/Controller/OgAgent/StatusAction.php | 8 ++ .../OgAgent/Webhook/GetStatusAction.php | 8 ++ src/Controller/OgBoot/OgLive/SyncAction.php | 2 +- .../OgBoot/PxeBootFile/PostAction.php | 80 ++++++++++++++++++ .../OgBoot/PxeTemplate/AddClientAction.php | 44 ++++++++++ .../OgBoot/PxeTemplate/DeleteClientAction.php | 43 ++++++++++ .../OgBoot/PxeTemplate/GetAction.php | 11 ++- .../OgBoot/PxeTemplate/SyncAction.php | 48 +++++++++++ src/Dto/Input/ClientInput.php | 12 +++ src/Dto/Input/PxeBootFileInput.php | 55 ------------- src/Dto/Input/PxeTemplateAddClientsInput.php | 20 +++++ .../Input/PxeTemplateDeleteClientInput.php | 17 ++++ ...put.php => PxeTemplateSyncClientInput.php} | 8 +- src/Dto/Output/ClientOutput.php | 17 +++- src/Dto/Output/OgLiveOutput.php | 24 ++++++ src/Dto/Output/PxeBootFileOutput.php | 42 ---------- src/Dto/Output/PxeTemplateOutput.php | 7 +- src/Entity/Client.php | 42 ++++++++-- src/Entity/PxeBootFile.php | 82 ------------------- src/Entity/PxeTemplate.php | 46 +++++++++++ src/Repository/PxeBootFileRepository.php | 18 ---- src/State/Processor/PxeBootFileProcessor.php | 78 ------------------ src/State/Provider/PxeBootFileProvider.php | 71 ---------------- 32 files changed, 605 insertions(+), 412 deletions(-) delete mode 100644 config/api_platform/PxeBootFile.yaml create mode 100644 migrations/Version20241019073142.php create mode 100644 migrations/Version20241021061008.php create mode 100644 migrations/Version20241021071250.php create mode 100644 migrations/Version20241021201438.php create mode 100644 src/Controller/OgAgent/StatusAction.php create mode 100644 src/Controller/OgAgent/Webhook/GetStatusAction.php create mode 100644 src/Controller/OgBoot/PxeBootFile/PostAction.php create mode 100644 src/Controller/OgBoot/PxeTemplate/AddClientAction.php create mode 100644 src/Controller/OgBoot/PxeTemplate/DeleteClientAction.php create mode 100644 src/Controller/OgBoot/PxeTemplate/SyncAction.php delete mode 100644 src/Dto/Input/PxeBootFileInput.php create mode 100644 src/Dto/Input/PxeTemplateAddClientsInput.php create mode 100644 src/Dto/Input/PxeTemplateDeleteClientInput.php rename src/Dto/Input/{SubnetAddSingleOrganizationalUnitInput.php => PxeTemplateSyncClientInput.php} (50%) delete mode 100644 src/Dto/Output/PxeBootFileOutput.php delete mode 100644 src/Entity/PxeBootFile.php delete mode 100644 src/Repository/PxeBootFileRepository.php delete mode 100644 src/State/Processor/PxeBootFileProcessor.php delete mode 100644 src/State/Provider/PxeBootFileProvider.php diff --git a/config/api_platform/Client.yaml b/config/api_platform/Client.yaml index 641ba9a..ee2b36c 100644 --- a/config/api_platform/Client.yaml +++ b/config/api_platform/Client.yaml @@ -34,6 +34,14 @@ resources: uriTemplate: /clients/change-organizational-units controller: App\Controller\ChangeOrganizationalUnitAction + agent_status: + provider: App\State\Provider\ClientProvider + class: ApiPlatform\Metadata\Post + method: POST + input: false + uriTemplate: /clients/{uuid}/agent/status + controller: App\Controller\OgAgent\StatusAction + properties: App\Entity\Client: id: diff --git a/config/api_platform/PxeBootFile.yaml b/config/api_platform/PxeBootFile.yaml deleted file mode 100644 index 62775c5..0000000 --- a/config/api_platform/PxeBootFile.yaml +++ /dev/null @@ -1,40 +0,0 @@ -resources: - App\Entity\PxeBootFile: - processor: App\State\Processor\PxeBootFileProcessor - input: App\Dto\Input\PxeBootFileInput - output: App\Dto\Output\PxeBootFileOutput - normalizationContext: - groups: ['default', 'pxe-boot-file:read'] - denormalizationContext: - groups: ['pxe-boot-file:write'] - operations: - ApiPlatform\Metadata\GetCollection: - provider: App\State\Provider\PxeBootFileProvider - filters: - - 'api_platform.filter.pxe_boot_file.order' - - 'api_platform.filter.pxe_boot_file.search' - - ApiPlatform\Metadata\Get: - provider: App\State\Provider\PxeBootFileProvider - ApiPlatform\Metadata\Put: - provider: App\State\Provider\PxeBootFileProvider - ApiPlatform\Metadata\Patch: - provider: App\State\Provider\PxeBootFileProvider - ApiPlatform\Metadata\Post: ~ - ApiPlatform\Metadata\Delete: ~ - - get_all: - shortName: PxeBootFile Server - description: Get collection of PxeBootFile - class: ApiPlatform\Metadata\GetCollection - method: GET - input: false - uriTemplate: /pxe-boot-files/server/get-collection - controller: App\Controller\OgBoot\PxeBootFile\GetCollectionAction - -properties: - App\Entity\PxeBootFile: - id: - identifier: false - uuid: - identifier: true \ No newline at end of file diff --git a/config/api_platform/PxeTemplate.yaml b/config/api_platform/PxeTemplate.yaml index 0f311af..ce4ae4d 100644 --- a/config/api_platform/PxeTemplate.yaml +++ b/config/api_platform/PxeTemplate.yaml @@ -24,6 +24,13 @@ resources: ApiPlatform\Metadata\Post: ~ ApiPlatform\Metadata\Delete: ~ + pxe_template_sync: + class: ApiPlatform\Metadata\Post + method: POST + input: false + uriTemplate: /pxe-templates/sync + controller: App\Controller\OgBoot\PxeTemplate\SyncAction + get_collection_templates: shortName: PxeTemplate Server description: Get collection of PxeTemplate @@ -54,12 +61,40 @@ resources: delete_template: shortName: PxeTemplate Server description: Delete PxeTemplate - class: ApiPlatform\Metadata\Get - method: GET + class: ApiPlatform\Metadata\Post + method: POST input: false uriTemplate: /pxe-templates/server/{uuid}/delete controller: App\Controller\OgBoot\PxeTemplate\DeleteAction + template_add_clients: + shortName: PxeTemplate Server + description: Add Client to PxeTemplate + class: ApiPlatform\Metadata\Post + method: POST + input: App\Dto\Input\PxeTemplateAddClientsInput + uriTemplate: /pxe-templates/{uuid}/add-clients + controller: App\Controller\OgBoot\PxeTemplate\AddClientAction + + template_sync_client: + shortName: PxeTemplate Server + description: Sync Client to PxeTemplate + class: ApiPlatform\Metadata\Post + method: POST + input: App\Dto\Input\PxeTemplateSyncClientInput + uriTemplate: /pxe-templates/{uuid}/sync-client + controller: App\Controller\OgBoot\PxeBootFile\PostAction + + template_delete_client: + shortName: PxeTemplate Server + description: Add Client to PxeTemplate + class: ApiPlatform\Metadata\Post + method: POST + input: App\Dto\Input\PxeTemplateDeleteClientInput + uriTemplate: /pxe-templates/{uuid}/delete-client + controller: App\Controller\OgBoot\PxeTemplate\DeleteClientAction + + properties: App\Entity\PxeTemplate: id: diff --git a/config/services.yaml b/config/services.yaml index dfa9740..294e4b8 100644 --- a/config/services.yaml +++ b/config/services.yaml @@ -134,11 +134,6 @@ services: $collectionProvider: '@api_platform.doctrine.orm.state.collection_provider' $itemProvider: '@api_platform.doctrine.orm.state.item_provider' - App\State\Provider\PxeBootFileProvider: - bind: - $collectionProvider: '@api_platform.doctrine.orm.state.collection_provider' - $itemProvider: '@api_platform.doctrine.orm.state.item_provider' - App\State\Provider\CommandTaskProvider: bind: $collectionProvider: '@api_platform.doctrine.orm.state.collection_provider' diff --git a/config/services/api_platform.yaml b/config/services/api_platform.yaml index 82f3fd0..96d9926 100644 --- a/config/services/api_platform.yaml +++ b/config/services/api_platform.yaml @@ -20,12 +20,12 @@ services: api_platform.filter.client.search: parent: 'api_platform.doctrine.orm.search_filter' - arguments: [ { 'id': 'exact', 'name': 'partial', 'serialNumber': 'exact', organizationalUnit.id: 'exact', mac: 'exact', ip: 'exact' } ] + arguments: [ { 'id': 'exact', 'name': 'partial', 'serialNumber': 'exact', 'template.id': 'exact', organizationalUnit.id: 'exact', mac: 'exact', ip: 'exact' } ] tags: [ 'api_platform.filter' ] api_platform.filter.client.exist: parent: 'api_platform.doctrine.orm.exists_filter' - arguments: [{'subnet': ~ }] + arguments: [{'subnet': ~, 'template': ~ }] tags: [ 'api_platform.filter' ] api_platform.filter.command.order: diff --git a/migrations/Version20241019073142.php b/migrations/Version20241019073142.php new file mode 100644 index 0000000..645dac2 --- /dev/null +++ b/migrations/Version20241019073142.php @@ -0,0 +1,31 @@ +addSql('ALTER TABLE client ADD agent_job_id 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 client DROP agent_job_id'); + } +} diff --git a/migrations/Version20241021061008.php b/migrations/Version20241021061008.php new file mode 100644 index 0000000..84ba8f8 --- /dev/null +++ b/migrations/Version20241021061008.php @@ -0,0 +1,33 @@ +addSql('ALTER TABLE client DROP FOREIGN KEY FK_C7440455F7E54CF3'); + $this->addSql('ALTER TABLE client ADD CONSTRAINT FK_C7440455F7E54CF3 FOREIGN KEY (og_live_id) REFERENCES og_live (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_C7440455F7E54CF3'); + $this->addSql('ALTER TABLE client ADD CONSTRAINT FK_C7440455F7E54CF3 FOREIGN KEY (og_live_id) REFERENCES og_live (id)'); + } +} diff --git a/migrations/Version20241021071250.php b/migrations/Version20241021071250.php new file mode 100644 index 0000000..054ddd6 --- /dev/null +++ b/migrations/Version20241021071250.php @@ -0,0 +1,43 @@ +addSql('ALTER TABLE client DROP FOREIGN KEY FK_C7440455D4CBF752'); + $this->addSql('ALTER TABLE pxe_boot_file DROP FOREIGN KEY FK_7FD1F34B5DA0FB8'); + $this->addSql('DROP TABLE pxe_boot_file'); + $this->addSql('DROP INDEX IDX_C7440455D4CBF752 ON client'); + $this->addSql('ALTER TABLE client CHANGE pxe_boot_file_id template_id INT DEFAULT NULL'); + $this->addSql('ALTER TABLE client ADD CONSTRAINT FK_C74404555DA0FB8 FOREIGN KEY (template_id) REFERENCES pxe_template (id)'); + $this->addSql('CREATE INDEX IDX_C74404555DA0FB8 ON client (template_id)'); + } + + public function down(Schema $schema): void + { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql('CREATE TABLE pxe_boot_file (id INT AUTO_INCREMENT NOT NULL, template_id INT DEFAULT 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`, synchronized TINYINT(1) DEFAULT NULL, INDEX IDX_7FD1F34B5DA0FB8 (template_id), UNIQUE INDEX UNIQ_7FD1F34BD17F50A6 (uuid), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB COMMENT = \'\' '); + $this->addSql('ALTER TABLE pxe_boot_file ADD CONSTRAINT FK_7FD1F34B5DA0FB8 FOREIGN KEY (template_id) REFERENCES pxe_template (id)'); + $this->addSql('ALTER TABLE client DROP FOREIGN KEY FK_C74404555DA0FB8'); + $this->addSql('DROP INDEX IDX_C74404555DA0FB8 ON client'); + $this->addSql('ALTER TABLE client CHANGE template_id pxe_boot_file_id INT DEFAULT NULL'); + $this->addSql('ALTER TABLE client ADD CONSTRAINT FK_C7440455D4CBF752 FOREIGN KEY (pxe_boot_file_id) REFERENCES pxe_boot_file (id)'); + $this->addSql('CREATE INDEX IDX_C7440455D4CBF752 ON client (pxe_boot_file_id)'); + } +} diff --git a/migrations/Version20241021201438.php b/migrations/Version20241021201438.php new file mode 100644 index 0000000..7c1dc59 --- /dev/null +++ b/migrations/Version20241021201438.php @@ -0,0 +1,31 @@ +addSql('ALTER TABLE client ADD pxe_sync 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 client DROP pxe_sync'); + } +} diff --git a/src/Controller/OgAgent/StatusAction.php b/src/Controller/OgAgent/StatusAction.php new file mode 100644 index 0000000..12f8b92 --- /dev/null +++ b/src/Controller/OgAgent/StatusAction.php @@ -0,0 +1,8 @@ +createRequest($httpClient, 'GET', $this->ogBootApiUrl . '/ogboot/v1/oglives'); - foreach ($content['message'] as $ogLive) { + foreach ($content['message']['installed_ogLives'] as $ogLive) { $ogLiveEntity = $this->entityManager->getRepository(OgLive::class)->findOneBy(['checksum' => $ogLive['id']]); if ($ogLiveEntity) { $this->extracted($ogLiveEntity, $ogLive); diff --git a/src/Controller/OgBoot/PxeBootFile/PostAction.php b/src/Controller/OgBoot/PxeBootFile/PostAction.php new file mode 100644 index 0000000..eeba300 --- /dev/null +++ b/src/Controller/OgBoot/PxeBootFile/PostAction.php @@ -0,0 +1,80 @@ +client->getEntity(); + + $data = [ + 'template_name' => $pxeTemplate->getName(), + 'mac' => strtolower($client->getMac()), + 'lang' => 'es_ES.UTF_8', + 'ip' => $client->getIp(), + 'server_ip' => '92.168.2.1', + 'router' => $client->getOrganizationalUnit()->getNetworkSettings()->getRouter(), + 'netmask' => $client->getOrganizationalUnit()->getNetworkSettings() ? $client->getOrganizationalUnit()->getNetworkSettings()->getNetmask() : '255.255.255.0', + 'computer_name' => $client->getName(), + 'netiface' => $client->getNetiface(), + 'group' => $client->getOrganizationalUnit()->getName(), + 'ogrepo' => $client->getRepository() ? $client->getRepository()->getIpAddress() : '192.168.2.1', + 'oglive' => $client->getOgLive() ? $client->getOgLive()->getDownloadUrl() : '', + 'oglog' => '192.168.2.1', + 'ogshare' => '192.168.2.1', + 'oglivedir' => 'ogLive', + 'ogprof' => 'false', + 'hardprofile' => $client->getHardwareProfile() ? $client->getHardwareProfile()->getDescription() : 'default', + 'ogntp' => $client->getOrganizationalUnit()->getNetworkSettings()?->getNtp(), + 'ogdns' => $client->getOrganizationalUnit()->getNetworkSettings()?->getDns(), + 'ogProxy' => $client->getOrganizationalUnit()->getNetworkSettings()?->getProxy(), + 'ogunit' => '', + 'resolution' => '788' + ]; + + try { + $response = $httpClient->request('POST', $this->ogBootApiUrl.'/ogboot/v1/pxes', [ + 'headers' => [ + 'accept' => 'application/json', + 'Content-Type' => 'application/json', + ], + 'json' => $data + ]); + } catch (TransportExceptionInterface $e) { + $client->setPxeSync(false); + $this->entityManager->persist($client); + $this->entityManager->flush(); + return new JsonResponse( data: $e->getMessage(), status: Response::HTTP_INTERNAL_SERVER_ERROR); + } + + $client->setPxeSync(true); + $this->entityManager->persist($client); + $this->entityManager->flush(); + + return new JsonResponse(data: json_decode($response->getContent(), true), status: Response::HTTP_OK); + } +} \ No newline at end of file diff --git a/src/Controller/OgBoot/PxeTemplate/AddClientAction.php b/src/Controller/OgBoot/PxeTemplate/AddClientAction.php new file mode 100644 index 0000000..9ebc38c --- /dev/null +++ b/src/Controller/OgBoot/PxeTemplate/AddClientAction.php @@ -0,0 +1,44 @@ +clients; + + foreach ($clients as $client) { + /** @var Client $clientEntity */ + $clientEntity = $client->getEntity(); + $clientEntity->setTemplate($pxeTemplate); + + $this->entityManager->persist($clientEntity); + } + + $this->entityManager->flush(); + + return new JsonResponse(status: Response::HTTP_OK); + } +} \ No newline at end of file diff --git a/src/Controller/OgBoot/PxeTemplate/DeleteClientAction.php b/src/Controller/OgBoot/PxeTemplate/DeleteClientAction.php new file mode 100644 index 0000000..77e07f3 --- /dev/null +++ b/src/Controller/OgBoot/PxeTemplate/DeleteClientAction.php @@ -0,0 +1,43 @@ +client; + + /** @var Client $clientEntity */ + $clientEntity = $client->getEntity(); + + $pxeTemplate->removeClient($clientEntity); + + $this->entityManager->persist($clientEntity); + $this->entityManager->flush(); + + return new JsonResponse(status: Response::HTTP_OK); + } +} \ No newline at end of file diff --git a/src/Controller/OgBoot/PxeTemplate/GetAction.php b/src/Controller/OgBoot/PxeTemplate/GetAction.php index 5648a55..5bbc2c0 100644 --- a/src/Controller/OgBoot/PxeTemplate/GetAction.php +++ b/src/Controller/OgBoot/PxeTemplate/GetAction.php @@ -23,10 +23,10 @@ class GetAction extends AbstractOgBootController * @throws RedirectionExceptionInterface * @throws ClientExceptionInterface */ - public function __invoke(PxeTemplate $data, HttpClientInterface $httpClient): JsonResponse + public function __invoke(PxeTemplate $template, HttpClientInterface $httpClient): JsonResponse { try { - $response = $httpClient->request('GET', $this->ogBootApiUrl.'/ogboot/v1/pxe-templates/'.$data->getName(), [ + $response = $httpClient->request('GET', $this->ogBootApiUrl.'/ogboot/v1/pxe-templates/'.$template->getName(), [ 'headers' => [ 'accept' => 'application/json', ], @@ -37,6 +37,13 @@ class GetAction extends AbstractOgBootController $data = json_decode($response->getContent(), true); + $template->setName($data['template_name']); + $template->setTemplateContent($data['template_content']); + $template->setSynchronized(true); + + $this->entityManager->persist($template); + $this->entityManager->flush(); + return new JsonResponse( data: $data, 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 new file mode 100644 index 0000000..50746bd --- /dev/null +++ b/src/Controller/OgBoot/PxeTemplate/SyncAction.php @@ -0,0 +1,48 @@ +createRequest($httpClient, 'GET', $this->ogBootApiUrl . '/ogboot/v1/pxe-templates'); + + foreach ($content['message'] as $template) { + $templateEntity = $this->entityManager->getRepository(PxeTemplate::class)->findOneBy(['name' => $template]); + if ($templateEntity) { + $this->entityManager->persist($templateEntity); + } else { + $templateEntity = new PxeTemplate(); + $templateEntity->setName($template); + $templateEntity->setTemplateContent(''); + } + $this->entityManager->persist($templateEntity); + } + $this->entityManager->flush(); + + return new JsonResponse(data: $content, status: Response::HTTP_OK); + } +} \ No newline at end of file diff --git a/src/Dto/Input/ClientInput.php b/src/Dto/Input/ClientInput.php index 9d3a98f..e4bbc7b 100644 --- a/src/Dto/Input/ClientInput.php +++ b/src/Dto/Input/ClientInput.php @@ -7,6 +7,7 @@ use App\Dto\Output\HardwareProfileOutput; use App\Dto\Output\MenuOutput; use App\Dto\Output\OgLiveOutput; use App\Dto\Output\OrganizationalUnitOutput; +use App\Dto\Output\PxeTemplateOutput; use App\Entity\Client; use App\Entity\OgRepository; use Symfony\Component\Serializer\Annotation\Groups; @@ -78,6 +79,12 @@ final class ClientInput )] public ?MenuOutput $menu = null; + #[Groups(['client:write'])] + #[ApiProperty( + description: 'La plantilla PXE del cliente' + )] + public ?PxeTemplateOutput $template = null; + #[Groups(['client:write'])] #[ApiProperty( description: 'El perfil de hardware del cliente' @@ -133,6 +140,10 @@ final class ClientInput $this->ogLive = new OgLiveOutput($client->getOgLive()); } + if ($client->getTemplate()) { + $this->template = new PxeTemplateOutput($client->getTemplate()); + } + if ($client->getHardwareProfile()) { $this->hardwareProfile = new HardwareProfileOutput($client->getHardwareProfile()); } @@ -154,6 +165,7 @@ final class ClientInput $client->setMenu($this->menu?->getEntity()); $client->setOgLive($this->ogLive?->getEntity()); $client->setHardwareProfile($this->hardwareProfile?->getEntity()); + $client->setTemplate($this->template?->getEntity()); $client->setPosition($this->position); $client->setStatus($this->status); $client->setMaintenance($this->maintenance); diff --git a/src/Dto/Input/PxeBootFileInput.php b/src/Dto/Input/PxeBootFileInput.php deleted file mode 100644 index 3b0831d..0000000 --- a/src/Dto/Input/PxeBootFileInput.php +++ /dev/null @@ -1,55 +0,0 @@ -template = $bootFile->getTemplate(); - if ($bootFile->getClients()) { - foreach ($bootFile->getClients() as $client) { - $this->clients[] = new ClientOutput($client); - } - } - } - - public function createOrUpdateEntity(?PxeBootFile $bootFile = null): PxeBootFile - { - if (!$bootFile) { - $bootFile = new PxeBootFile(); - } - - $bootFile->setTemplate($this->template->getEntity()); - - foreach ($this->clients as $client) { - $clientsToAdd[] = $client->getEntity(); - } - $bootFile->setClients( $clientsToAdd ?? [] ); - - return $bootFile; - } -} \ No newline at end of file diff --git a/src/Dto/Input/PxeTemplateAddClientsInput.php b/src/Dto/Input/PxeTemplateAddClientsInput.php new file mode 100644 index 0000000..28cf393 --- /dev/null +++ b/src/Dto/Input/PxeTemplateAddClientsInput.php @@ -0,0 +1,20 @@ +menu = $client->getMenu() ? new MenuOutput($client->getMenu()) : null; $this->position = $client->getPosition(); + $this->template = $client->getTemplate() ? new PxeTemplateOutput($client->getTemplate()) : null; $this->hardwareProfile = $client->getHardwareProfile() ? new HardwareProfileOutput($client->getHardwareProfile()) : null; - $this->subnet = $client->getSubnet() ? $client->getSubnet()->getIpAddress(). '-' . $client->getSubnet()->getNetmask() : null; + $this->subnet = $client->getSubnet()?->getIpAddress(); $this->ogLive = $client->getOgLive() ? new OgLiveOutput($client->getOgLive()) : null; $this->status = $client->getStatus(); $this->createdAt = $client->getCreatedAt(); $this->createdBy = $client->getCreatedBy(); $this->maintenance = $client->isMaintenance(); + $this->pxeSync = $client->isPxeSync(); } } \ No newline at end of file diff --git a/src/Dto/Output/OgLiveOutput.php b/src/Dto/Output/OgLiveOutput.php index b881f35..45a9a57 100644 --- a/src/Dto/Output/OgLiveOutput.php +++ b/src/Dto/Output/OgLiveOutput.php @@ -28,6 +28,24 @@ final class OgLiveOutput extends AbstractOutput #[Groups(['og-live:read'])] public ?string $status = ''; + #[Groups(['og-live:read'])] + public ?string $checksum = ''; + + #[Groups(['og-live:read'])] + public ?string $distribution = ''; + + #[Groups(['og-live:read'])] + public ?string $revision = ''; + + #[Groups(['og-live:read'])] + public ?string $filename = ''; + + #[Groups(['og-live:read'])] + public ?string $kernel = ''; + + #[Groups(['og-live:read'])] + public ?string $architecture = ''; + #[Groups(['og-live:read'])] public \DateTime $createdAt; @@ -43,6 +61,12 @@ final class OgLiveOutput extends AbstractOutput $this->installed = $ogLive->isInstalled(); $this->isDefault = $ogLive->getIsDefault(); $this->downloadUrl = $ogLive->getDownloadUrl(); + $this->distribution = $ogLive->getDistribution(); + $this->revision = $ogLive->getRevision(); + $this->filename = $ogLive->getFilename(); + $this->checksum = $ogLive->getChecksum(); + $this->kernel = $ogLive->getKernel(); + $this->architecture = $ogLive->getArchitecture(); $this->status = $ogLive->getStatus(); $this->createdAt = $ogLive->getCreatedAt(); $this->createdBy = $ogLive->getCreatedBy(); diff --git a/src/Dto/Output/PxeBootFileOutput.php b/src/Dto/Output/PxeBootFileOutput.php deleted file mode 100644 index ff76bc6..0000000 --- a/src/Dto/Output/PxeBootFileOutput.php +++ /dev/null @@ -1,42 +0,0 @@ -template = new PxeTemplateOutput($bootFile->getTemplate()); - $this->clients = $bootFile->getClients()->map( - fn(Client $client) => new ClientOutput($client) - )->toArray(); - - $this->synchronized = $bootFile->isSynchronized(); - $this->createdAt = $bootFile->getCreatedAt(); - $this->createdBy = $bootFile->getCreatedBy(); - } -} \ No newline at end of file diff --git a/src/Dto/Output/PxeTemplateOutput.php b/src/Dto/Output/PxeTemplateOutput.php index e66ba17..a625e91 100644 --- a/src/Dto/Output/PxeTemplateOutput.php +++ b/src/Dto/Output/PxeTemplateOutput.php @@ -4,13 +4,14 @@ namespace App\Dto\Output; use ApiPlatform\Metadata\ApiProperty; use ApiPlatform\Metadata\Get; +use App\Entity\Client; use App\Entity\PxeTemplate; use Symfony\Component\Serializer\Annotation\Groups; #[Get(shortName: 'PxeTemplate')] final class PxeTemplateOutput extends AbstractOutput { - #[Groups(['pxe-template:read', 'pxe-boot-file:read'])] + #[Groups(['pxe-template:read', 'client:read'])] public string $name; #[Groups(['pxe-template:read'])] @@ -19,6 +20,9 @@ final class PxeTemplateOutput extends AbstractOutput #[Groups(['pxe-template:read'])] public ?string $templateContent = ''; + #[Groups(['pxe-template:read'])] + public ?int $clientsLength = 0; + #[Groups(['pxe-template:read'])] public \DateTime $createdAt; @@ -32,6 +36,7 @@ final class PxeTemplateOutput extends AbstractOutput $this->name = $pxeTemplate->getName(); $this->synchronized = $pxeTemplate->isSynchronized(); $this->templateContent = $pxeTemplate->getTemplateContent(); + $this->clientsLength = $pxeTemplate->getClients()->count(); $this->createdAt = $pxeTemplate->getCreatedAt(); $this->createdBy = $pxeTemplate->getCreatedBy(); } diff --git a/src/Entity/Client.php b/src/Entity/Client.php index 28b6e72..37546ed 100644 --- a/src/Entity/Client.php +++ b/src/Entity/Client.php @@ -58,7 +58,7 @@ class Client extends AbstractEntity private ?array $position = ['x' => 0, 'y' => 0]; #[ORM\ManyToOne(inversedBy: 'clients')] - private ?PxeBootFile $pxeBootFile = null; + private ?PxeTemplate $template = null; #[ORM\ManyToOne(inversedBy: 'clients')] private ?OgRepository $repository = null; @@ -67,11 +67,18 @@ class Client extends AbstractEntity #[ORM\JoinColumn( onDelete: 'SET NULL')] private ?Subnet $subnet = null; #[ORM\ManyToOne(inversedBy: 'clients')] + #[ORM\JoinColumn( onDelete: 'SET NULL')] private ?OgLive $ogLive = null; #[ORM\Column] private ?bool $maintenance = null; + #[ORM\Column(length: 255, nullable: true)] + private ?string $agentJobId = null; + + #[ORM\Column(nullable: true)] + private ?bool $pxeSync = null; + public function __construct() { parent::__construct(); @@ -240,18 +247,19 @@ class Client extends AbstractEntity return $this; } - public function getPxeBootFile(): ?PxeBootFile + public function getTemplate(): ?PxeTemplate { - return $this->pxeBootFile; + return $this->template; } - public function setPxeBootFile(?PxeBootFile $pxeBootFile): static + public function setTemplate(?PxeTemplate $template): static { - $this->pxeBootFile = $pxeBootFile; + $this->template = $template; return $this; } + public function getRepository(): ?OgRepository { return $this->repository; @@ -299,4 +307,28 @@ class Client extends AbstractEntity return $this; } + + public function getAgentJobId(): ?string + { + return $this->agentJobId; + } + + public function setAgentJobId(?string $agentJobId): static + { + $this->agentJobId = $agentJobId; + + return $this; + } + + public function isPxeSync(): ?bool + { + return $this->pxeSync; + } + + public function setPxeSync(?bool $pxeSync): static + { + $this->pxeSync = $pxeSync; + + return $this; + } } diff --git a/src/Entity/PxeBootFile.php b/src/Entity/PxeBootFile.php deleted file mode 100644 index b14dd3f..0000000 --- a/src/Entity/PxeBootFile.php +++ /dev/null @@ -1,82 +0,0 @@ - - */ - #[ORM\OneToMany(mappedBy: 'pxeBootFile', targetEntity: Client::class)] - private Collection $clients; - - public function __construct() - { - parent::__construct(); - $this->clients = new ArrayCollection(); - } - - public function getTemplate(): ?PxeTemplate - { - return $this->template; - } - - public function setTemplate(?PxeTemplate $template): static - { - $this->template = $template; - - return $this; - } - - /** - * @return Collection - */ - public function getClients(): Collection - { - return $this->clients; - } - - public function addClient(Client $client): static - { - if (!$this->clients->contains($client)) { - $this->clients->add($client); - $client->setPxeBootFile($this); - } - - return $this; - } - - public function removeClient(Client $client): static - { - if ($this->clients->removeElement($client)) { - // set the owning side to null (unless already changed) - if ($client->getPxeBootFile() === $this) { - $client->setPxeBootFile(null); - } - } - - return $this; - } - - public function setClients(array $clients): static - { - $this->clients->clear(); - - foreach ($clients as $client){ - $this->addClient($client); - } - - return $this; - } -} diff --git a/src/Entity/PxeTemplate.php b/src/Entity/PxeTemplate.php index 39a79b1..87b74b0 100644 --- a/src/Entity/PxeTemplate.php +++ b/src/Entity/PxeTemplate.php @@ -3,6 +3,7 @@ namespace App\Entity; use App\Repository\PxeTemplateRepository; +use Doctrine\Common\Collections\Collection; use Doctrine\DBAL\Types\Types; use Doctrine\ORM\Mapping as ORM; use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity; @@ -18,6 +19,12 @@ class PxeTemplate extends AbstractEntity #[ORM\Column(type: Types::TEXT)] private ?string $templateContent = null; + /** + * @var Collection + */ + #[ORM\OneToMany(mappedBy: 'template', targetEntity: Client::class)] + private Collection $clients; + public function getTemplateContent(): ?string { return $this->templateContent; @@ -29,4 +36,43 @@ class PxeTemplate extends AbstractEntity return $this; } + + /** + * @return Collection + */ + public function getClients(): Collection + { + return $this->clients; + } + + public function setClients(array $clients): static + { + $this->clients->clear(); + + foreach ($clients as $client){ + $this->addClient($client); + } + + return $this; + } + + public function addClient(Client $client): static + { + if (!$this->clients->contains($client)) { + $this->clients->add($client); + } + + return $this; + } + + public function removeClient(Client $client): static + { + if ($this->clients->removeElement($client)) { + if ($client->getTemplate() === $this) { + $client->setTemplate(null); + } + } + + return $this; + } } diff --git a/src/Repository/PxeBootFileRepository.php b/src/Repository/PxeBootFileRepository.php deleted file mode 100644 index 5733c80..0000000 --- a/src/Repository/PxeBootFileRepository.php +++ /dev/null @@ -1,18 +0,0 @@ - - */ -class PxeBootFileRepository extends AbstractRepository -{ - public function __construct(ManagerRegistry $registry) - { - parent::__construct($registry, PxeBootFile::class); - } -} diff --git a/src/State/Processor/PxeBootFileProcessor.php b/src/State/Processor/PxeBootFileProcessor.php deleted file mode 100644 index b53e55b..0000000 --- a/src/State/Processor/PxeBootFileProcessor.php +++ /dev/null @@ -1,78 +0,0 @@ -processCreateOrUpdate($data, $operation, $uriVariables, $context); - case $operation instanceof Delete: - return $this->processDelete($data, $operation, $uriVariables, $context); - } - } - - /** - * @throws \Exception - * @throws TransportExceptionInterface - */ - private function processCreateOrUpdate($data, Operation $operation, array $uriVariables = [], array $context = []): PxeBootFileOutput - { - if (!($data instanceof PxeBootFileInput)) { - throw new \Exception(sprintf('data is not instance of %s', PxeBootFileInput::class)); - } - - $entity = null; - if (isset($uriVariables['uuid'])) { - $entity = $this->bootFileRepository->findOneByUuid($uriVariables['uuid']); - } - - $pxeTemplate = $data->createOrUpdateEntity($entity); - $this->validator->validate($pxeTemplate); - $this->bootFileRepository->save($pxeTemplate); - - try { - $this->postService->__invoke($pxeTemplate); - } catch (\Exception $e) { - - } - - return new PxeBootFileOutput($pxeTemplate); - } - - private function processDelete($data, Operation $operation, array $uriVariables = [], array $context = []): null - { - $bootFile = $this->bootFileRepository->findOneByUuid($uriVariables['uuid']); - $this->bootFileRepository->delete($bootFile); - - return null; - } -} diff --git a/src/State/Provider/PxeBootFileProvider.php b/src/State/Provider/PxeBootFileProvider.php deleted file mode 100644 index f25bef9..0000000 --- a/src/State/Provider/PxeBootFileProvider.php +++ /dev/null @@ -1,71 +0,0 @@ -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 PxeBootFileOutput($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('PxeBootFile not found'); - } - - return new PxeBootFileOutput($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 PxeBootFileInput($item) : null; - } - - return new PxeBootFileInput(); - } -} -- 2.40.1 From 0ac01b8c4a1f2222b99903b9eef5ebfcbc59f009 Mon Sep 17 00:00:00 2001 From: Manuel Aranda Date: Tue, 22 Oct 2024 07:35:19 +0200 Subject: [PATCH 110/157] refs #952. First revision status endpoint ogAgent --- .../OgAgent/OgAdmClientController.php | 3 - src/Controller/OgAgent/StatusAction.php | 57 ++++++++++++++++++- .../OgAgent/Webhook/GetStatusAction.php | 40 ++++++++++++- 3 files changed, 93 insertions(+), 7 deletions(-) diff --git a/src/Controller/OgAgent/OgAdmClientController.php b/src/Controller/OgAgent/OgAdmClientController.php index 3050e63..f8fe811 100644 --- a/src/Controller/OgAgent/OgAdmClientController.php +++ b/src/Controller/OgAgent/OgAdmClientController.php @@ -25,7 +25,6 @@ class OgAdmClientController extends AbstractController { } - #[Route('/opengnsys/rest/ogAdmClient/InclusionCliente', methods: ['POST'])] public function processClient(Request $request): JsonResponse { @@ -54,11 +53,9 @@ class OgAdmClientController extends AbstractController $partitionEntity->setClient($clientEntity); $partitionEntity->setDiskNumber($cfg['disk']); - //$partitionEntity->setPartitionCode($cfg['codpar']); $partitionEntity->setPartitionNumber($cfg['par']); $partitionEntity->setSize($cfg['tam']); $partitionEntity->setMemoryUsage($cfg['uso']); - //$partitionEntity->setFilesystem($cfg['idsistemafichero']); $this->entityManager->persist($partitionEntity); $this->entityManager->flush(); } diff --git a/src/Controller/OgAgent/StatusAction.php b/src/Controller/OgAgent/StatusAction.php index 12f8b92..8de1498 100644 --- a/src/Controller/OgAgent/StatusAction.php +++ b/src/Controller/OgAgent/StatusAction.php @@ -2,7 +2,60 @@ namespace App\Controller\OgAgent; -class StatusAction -{ +use App\Entity\Client; +use App\Model\OgLiveStatus; +use Doctrine\ORM\EntityManagerInterface; +use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; +use Symfony\Component\HttpClient\HttpClient; +use Symfony\Component\HttpFoundation\JsonResponse; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpKernel\Attribute\AsController; +use Symfony\Component\Validator\Exception\ValidatorException; +use Symfony\Contracts\HttpClient\Exception\ClientExceptionInterface; +use Symfony\Contracts\HttpClient\Exception\RedirectionExceptionInterface; +use Symfony\Contracts\HttpClient\Exception\ServerExceptionInterface; +use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface; +use Symfony\Contracts\HttpClient\HttpClientInterface; +#[AsController] +class StatusAction extends AbstractController +{ + public function __construct( + protected readonly EntityManagerInterface $entityManager, + protected readonly HttpClientInterface $httpClient + ) + { + $httpClient = HttpClient::create([ + 'verify_peer' => false, + 'verify_host' => false, + ]); + } + + /** + * @throws TransportExceptionInterface + * @throws ServerExceptionInterface + * @throws RedirectionExceptionInterface + * @throws ClientExceptionInterface + */ + public function __invoke(Client $client): JsonResponse + { + if (!$client->getIp()) { + throw new ValidatorException('IP is required'); + } + + + try { + $response = $this->httpClient->request('POST', 'https://'.$client->getIp().':8000/ogAdmClient/status'); + } catch (TransportExceptionInterface $e) { + return new JsonResponse( data: 'An error occurred', status: Response::HTTP_INTERNAL_SERVER_ERROR); + } + + $data = json_decode($response->getContent(), true); + + $client->setAgentJobId($data['job_id']); + $this->entityManager->persist($data); + $this->entityManager->flush(); + + return new JsonResponse(data: $data, status: Response::HTTP_OK); + } } \ No newline at end of file diff --git a/src/Controller/OgAgent/Webhook/GetStatusAction.php b/src/Controller/OgAgent/Webhook/GetStatusAction.php index 7c63b23..fc256f8 100644 --- a/src/Controller/OgAgent/Webhook/GetStatusAction.php +++ b/src/Controller/OgAgent/Webhook/GetStatusAction.php @@ -2,7 +2,43 @@ namespace App\Controller\OgAgent\Webhook; -class GetStatusAction -{ +use App\Controller\OgBoot\AbstractOgBootController; +use App\Entity\OgLive; +use App\Model\OgLiveStatus; +use Doctrine\ORM\EntityManagerInterface; +use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; +use Symfony\Component\HttpFoundation\JsonResponse; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpKernel\Attribute\AsController; +use Symfony\Component\Routing\Annotation\Route; +use Symfony\Component\Validator\Exception\ValidatorException; +use Symfony\Contracts\HttpClient\Exception\ClientExceptionInterface; +use Symfony\Contracts\HttpClient\Exception\RedirectionExceptionInterface; +use Symfony\Contracts\HttpClient\Exception\ServerExceptionInterface; +use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface; +use Symfony\Contracts\HttpClient\HttpClientInterface; +#[AsController] +class GetStatusAction extends AbstractController +{ + public function __construct( + protected readonly EntityManagerInterface $entityManager + ) + { + } + + #[Route('/clients/status/webhook', name: 'status', methods: ['POST'])] + public function installWebhook(Request $request): JsonResponse + { + $data = json_decode($request->getContent(), true); + + if (!is_array($data)) { + return new JsonResponse(['error' => 'Invalid JSON data'], Response::HTTP_BAD_REQUEST); + } + + + + return new JsonResponse(data: $data, status: Response::HTTP_OK); + } } \ No newline at end of file -- 2.40.1 From 3f933db43eff7b366c16b8427eb24421d8d75f04 Mon Sep 17 00:00:00 2001 From: Manuel Aranda Date: Tue, 22 Oct 2024 09:42:07 +0200 Subject: [PATCH 111/157] Improvement APi DTO output --- src/Dto/Output/ClientOutput.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Dto/Output/ClientOutput.php b/src/Dto/Output/ClientOutput.php index 953c912..2724822 100644 --- a/src/Dto/Output/ClientOutput.php +++ b/src/Dto/Output/ClientOutput.php @@ -48,7 +48,7 @@ final class ClientOutput extends AbstractOutput #[ApiProperty(readableLink: true )] public ?HardwareProfileOutput $hardwareProfile = null; - #[Groups(['client:read'])] + #[Groups(['client:read', 'organizational-unit:read'])] #[ApiProperty(readableLink: true )] public ?PxeTemplateOutput $template = null; -- 2.40.1 From 31750fe663235c481023a13cb6bf1bd9fae89bb1 Mon Sep 17 00:00:00 2001 From: Manuel Aranda Date: Tue, 22 Oct 2024 18:26:51 +0200 Subject: [PATCH 112/157] refs #649. Integration ogBoot.Demo --- config/api_platform/Client.yaml | 9 +++- src/Controller/OgAgent/StatusAction.php | 12 +++--- .../OgBoot/PxeBootFile/GetAction.php | 43 +++++++++++++++++++ 3 files changed, 58 insertions(+), 6 deletions(-) create mode 100644 src/Controller/OgBoot/PxeBootFile/GetAction.php diff --git a/config/api_platform/Client.yaml b/config/api_platform/Client.yaml index ee2b36c..d1e1d84 100644 --- a/config/api_platform/Client.yaml +++ b/config/api_platform/Client.yaml @@ -35,13 +35,20 @@ resources: controller: App\Controller\ChangeOrganizationalUnitAction agent_status: - provider: App\State\Provider\ClientProvider class: ApiPlatform\Metadata\Post method: POST input: false uriTemplate: /clients/{uuid}/agent/status controller: App\Controller\OgAgent\StatusAction + get_pxe: + class: ApiPlatform\Metadata\Post + method: POST + input: false + uriTemplate: /clients/server/{uuid}/get-pxe + controller: App\Controller\OgBoot\PxeBootFile\GetAction + + properties: App\Entity\Client: id: diff --git a/src/Controller/OgAgent/StatusAction.php b/src/Controller/OgAgent/StatusAction.php index 8de1498..b11af50 100644 --- a/src/Controller/OgAgent/StatusAction.php +++ b/src/Controller/OgAgent/StatusAction.php @@ -7,6 +7,7 @@ use App\Model\OgLiveStatus; use Doctrine\ORM\EntityManagerInterface; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpClient\HttpClient; +use Symfony\Component\HttpClient\Internal\ClientState; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\Attribute\AsController; @@ -43,19 +44,20 @@ class StatusAction extends AbstractController throw new ValidatorException('IP is required'); } - try { $response = $this->httpClient->request('POST', 'https://'.$client->getIp().':8000/ogAdmClient/status'); + $client->setStatus('off'); + $this->entityManager->persist($client); + $this->entityManager->flush(); } catch (TransportExceptionInterface $e) { return new JsonResponse( data: 'An error occurred', status: Response::HTTP_INTERNAL_SERVER_ERROR); } - $data = json_decode($response->getContent(), true); - $client->setAgentJobId($data['job_id']); - $this->entityManager->persist($data); + $client->setStatus('active'); + $this->entityManager->persist($client); $this->entityManager->flush(); - return new JsonResponse(data: $data, status: Response::HTTP_OK); + return new JsonResponse(status: Response::HTTP_OK); } } \ No newline at end of file diff --git a/src/Controller/OgBoot/PxeBootFile/GetAction.php b/src/Controller/OgBoot/PxeBootFile/GetAction.php new file mode 100644 index 0000000..7f51e2b --- /dev/null +++ b/src/Controller/OgBoot/PxeBootFile/GetAction.php @@ -0,0 +1,43 @@ +request('GET', $this->ogBootApiUrl.'/ogboot/v1/pxes/'.$client->getMac(), [ + 'headers' => [ + 'accept' => 'application/json', + ], + ]); + } catch (TransportExceptionInterface $e) { + return new JsonResponse( data: 'An error occurred', status: Response::HTTP_INTERNAL_SERVER_ERROR); + } + + $data = json_decode($response->getContent(), true); + + return new JsonResponse( data: $data, status: Response::HTTP_OK); + } +} \ No newline at end of file -- 2.40.1 From 4aa03d643e485fa3d75e58518c84886edf4a8891 Mon Sep 17 00:00:00 2001 From: Manuel Aranda Date: Tue, 22 Oct 2024 21:36:38 +0200 Subject: [PATCH 113/157] refs #952. First revision status endpoint ogAgent --- src/Controller/OgAgent/StatusAction.php | 3 +-- .../OgLive/Webhook/InstallOgLiveResponseAction.php | 9 +++++++++ src/Controller/OgBoot/PxeBootFile/PostAction.php | 10 +++++----- 3 files changed, 15 insertions(+), 7 deletions(-) diff --git a/src/Controller/OgAgent/StatusAction.php b/src/Controller/OgAgent/StatusAction.php index b11af50..345c379 100644 --- a/src/Controller/OgAgent/StatusAction.php +++ b/src/Controller/OgAgent/StatusAction.php @@ -46,14 +46,13 @@ class StatusAction extends AbstractController try { $response = $this->httpClient->request('POST', 'https://'.$client->getIp().':8000/ogAdmClient/status'); + } catch (TransportExceptionInterface $e) { $client->setStatus('off'); $this->entityManager->persist($client); $this->entityManager->flush(); - } catch (TransportExceptionInterface $e) { return new JsonResponse( data: 'An error occurred', status: Response::HTTP_INTERNAL_SERVER_ERROR); } - $client->setStatus('active'); $this->entityManager->persist($client); $this->entityManager->flush(); diff --git a/src/Controller/OgBoot/OgLive/Webhook/InstallOgLiveResponseAction.php b/src/Controller/OgBoot/OgLive/Webhook/InstallOgLiveResponseAction.php index 71e8a2c..2f4df6a 100644 --- a/src/Controller/OgBoot/OgLive/Webhook/InstallOgLiveResponseAction.php +++ b/src/Controller/OgBoot/OgLive/Webhook/InstallOgLiveResponseAction.php @@ -82,8 +82,17 @@ class InstallOgLiveResponseAction extends AbstractController $ogLive->setStatus($status === self::OG_LIVE_INSTALL_SUCCESS ? OgLiveStatus::ACTIVE : OgLiveStatus::FAILED); $ogLive->setInstalled($status === self::OG_LIVE_INSTALL_SUCCESS); + $ogLive->setIsDefault(true); $this->entityManager->persist($ogLive); + + $oldDefaultOgLive = $this->entityManager->getRepository(OgLive::class)->findBy(['isDefault' => true]); + + foreach ($oldDefaultOgLive as $oldOgLive) { + $oldOgLive->setIsDefault(false); + $this->entityManager->persist($oldOgLive); + } + $this->entityManager->flush(); } } \ No newline at end of file diff --git a/src/Controller/OgBoot/PxeBootFile/PostAction.php b/src/Controller/OgBoot/PxeBootFile/PostAction.php index eeba300..506b4b9 100644 --- a/src/Controller/OgBoot/PxeBootFile/PostAction.php +++ b/src/Controller/OgBoot/PxeBootFile/PostAction.php @@ -36,16 +36,16 @@ class PostAction extends AbstractOgBootController 'mac' => strtolower($client->getMac()), 'lang' => 'es_ES.UTF_8', 'ip' => $client->getIp(), - 'server_ip' => '92.168.2.1', + 'server_ip' => '192.168.2.4', 'router' => $client->getOrganizationalUnit()->getNetworkSettings()->getRouter(), 'netmask' => $client->getOrganizationalUnit()->getNetworkSettings() ? $client->getOrganizationalUnit()->getNetworkSettings()->getNetmask() : '255.255.255.0', 'computer_name' => $client->getName(), 'netiface' => $client->getNetiface(), 'group' => $client->getOrganizationalUnit()->getName(), - 'ogrepo' => $client->getRepository() ? $client->getRepository()->getIpAddress() : '192.168.2.1', - 'oglive' => $client->getOgLive() ? $client->getOgLive()->getDownloadUrl() : '', - 'oglog' => '192.168.2.1', - 'ogshare' => '192.168.2.1', + 'ogrepo' => $client->getRepository() ? $client->getRepository()->getIpAddress() : '192.168.2.4', + 'oglive' => '192.168.2.4', + 'oglog' => '192.168.2.4', + 'ogshare' => '192.168.2.4', 'oglivedir' => 'ogLive', 'ogprof' => 'false', 'hardprofile' => $client->getHardwareProfile() ? $client->getHardwareProfile()->getDescription() : 'default', -- 2.40.1 From 14ff759309d31142bda36a5cc4d6dc4b02c4efc5 Mon Sep 17 00:00:00 2001 From: Manuel Aranda Date: Tue, 22 Oct 2024 22:07:27 +0200 Subject: [PATCH 114/157] refs #952. First revision status endpoint ogAgent --- src/Controller/OgAgent/StatusAction.php | 11 +++++++---- src/Dto/Input/ClientInput.php | 2 +- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/Controller/OgAgent/StatusAction.php b/src/Controller/OgAgent/StatusAction.php index 345c379..3a25429 100644 --- a/src/Controller/OgAgent/StatusAction.php +++ b/src/Controller/OgAgent/StatusAction.php @@ -46,17 +46,20 @@ class StatusAction extends AbstractController try { $response = $this->httpClient->request('POST', 'https://'.$client->getIp().':8000/ogAdmClient/status'); + + $client->setStatus('active'); + $this->entityManager->persist($client); + $this->entityManager->flush(); + } catch (TransportExceptionInterface $e) { + $client->setStatus('off'); $this->entityManager->persist($client); $this->entityManager->flush(); + return new JsonResponse( data: 'An error occurred', status: Response::HTTP_INTERNAL_SERVER_ERROR); } - $client->setStatus('active'); - $this->entityManager->persist($client); - $this->entityManager->flush(); - return new JsonResponse(status: Response::HTTP_OK); } } \ No newline at end of file diff --git a/src/Dto/Input/ClientInput.php b/src/Dto/Input/ClientInput.php index e4bbc7b..8e78aa0 100644 --- a/src/Dto/Input/ClientInput.php +++ b/src/Dto/Input/ClientInput.php @@ -64,7 +64,7 @@ final class ClientInput description: 'El estado del cliente', example: 'active' )] - public ?string $status = 'power-off'; + public ?string $status = 'off'; #[Assert\NotNull(message: 'validators.organizational_unit.not_null')] #[Groups(['client:write', 'client:patch'])] -- 2.40.1 From 756bea48449ea1383587a07d8a0188d82f49d433 Mon Sep 17 00:00:00 2001 From: Manuel Aranda Date: Tue, 22 Oct 2024 22:12:40 +0200 Subject: [PATCH 115/157] refs #952. First revision status endpoint ogAgent --- src/Controller/OgAgent/StatusAction.php | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/Controller/OgAgent/StatusAction.php b/src/Controller/OgAgent/StatusAction.php index 3a25429..3b58617 100644 --- a/src/Controller/OgAgent/StatusAction.php +++ b/src/Controller/OgAgent/StatusAction.php @@ -47,9 +47,15 @@ class StatusAction extends AbstractController try { $response = $this->httpClient->request('POST', 'https://'.$client->getIp().':8000/ogAdmClient/status'); - $client->setStatus('active'); - $this->entityManager->persist($client); - $this->entityManager->flush(); + if ($response->getStatusCode() !== Response::HTTP_OK) { + $client->setStatus('off'); + $this->entityManager->persist($client); + $this->entityManager->flush(); + } else if ($response->getStatusCode() === Response::HTTP_OK) { + $client->setStatus('active'); + $this->entityManager->persist($client); + $this->entityManager->flush(); + } } catch (TransportExceptionInterface $e) { -- 2.40.1 From 0c94a1921e01173e20ed947e1acc745ba7b3d3ca Mon Sep 17 00:00:00 2001 From: Manuel Aranda Date: Tue, 22 Oct 2024 22:20:21 +0200 Subject: [PATCH 116/157] refs #952. First revision status endpoint ogAgent --- src/Controller/OgAgent/StatusAction.php | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/src/Controller/OgAgent/StatusAction.php b/src/Controller/OgAgent/StatusAction.php index 3b58617..76864f0 100644 --- a/src/Controller/OgAgent/StatusAction.php +++ b/src/Controller/OgAgent/StatusAction.php @@ -45,27 +45,23 @@ class StatusAction extends AbstractController } try { - $response = $this->httpClient->request('POST', 'https://'.$client->getIp().':8000/ogAdmClient/status'); + $response = $this->httpClient->request('POST', 'https://' . $client->getIp() . ':8000/ogAdmClient/status'); - if ($response->getStatusCode() !== Response::HTTP_OK) { - $client->setStatus('off'); - $this->entityManager->persist($client); - $this->entityManager->flush(); - } else if ($response->getStatusCode() === Response::HTTP_OK) { - $client->setStatus('active'); - $this->entityManager->persist($client); - $this->entityManager->flush(); - } + $statusCode = $response->getStatusCode(); + $client->setStatus($statusCode === Response::HTTP_OK ? 'active' : 'off'); } catch (TransportExceptionInterface $e) { - $client->setStatus('off'); - $this->entityManager->persist($client); - $this->entityManager->flush(); - return new JsonResponse( data: 'An error occurred', status: Response::HTTP_INTERNAL_SERVER_ERROR); + return new JsonResponse( + data: ['error' => 'An error occurred while communicating with the client'], + status: Response::HTTP_INTERNAL_SERVER_ERROR + ); } + $this->entityManager->persist($client); + $this->entityManager->flush(); + return new JsonResponse(status: Response::HTTP_OK); } } \ No newline at end of file -- 2.40.1 From dce78eef298c28b0b80b6363aaf603b3ec4ff395 Mon Sep 17 00:00:00 2001 From: Manuel Aranda Date: Wed, 23 Oct 2024 08:03:55 +0200 Subject: [PATCH 117/157] refs #952. ogAgent. Create partition --- src/Controller/OgAgent/StatusAction.php | 21 +++++++++++++++++++ .../OgBoot/OgLive/SetDefaultAction.php | 7 +++++++ .../Webhook/InstallOgLiveResponseAction.php | 6 +++--- src/Entity/PxeTemplate.php | 7 +++++++ 4 files changed, 38 insertions(+), 3 deletions(-) diff --git a/src/Controller/OgAgent/StatusAction.php b/src/Controller/OgAgent/StatusAction.php index 76864f0..c042048 100644 --- a/src/Controller/OgAgent/StatusAction.php +++ b/src/Controller/OgAgent/StatusAction.php @@ -3,6 +3,7 @@ namespace App\Controller\OgAgent; use App\Entity\Client; +use App\Entity\Partition; use App\Model\OgLiveStatus; use Doctrine\ORM\EntityManagerInterface; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; @@ -59,6 +60,26 @@ class StatusAction extends AbstractController ); } + $data = json_decode($response->getContent(), true); + + if (isset($data['cfg'])) { + foreach ($data['cfg'] as $cfg) { + $partitionEntity = $this->entityManager->getRepository(Partition::class) + ->findOneBy(['client' => $client, 'diskNumber' => $cfg['disk'], 'partitionNumber' => $cfg['par']]); + + if (!$partitionEntity) { + $partitionEntity = new Partition(); + } + + $partitionEntity->setClient($client); + $partitionEntity->setDiskNumber($cfg['disk']); + $partitionEntity->setPartitionNumber($cfg['par']); + $partitionEntity->setSize($cfg['tam']); + $partitionEntity->setMemoryUsage($cfg['uso']); + $this->entityManager->persist($partitionEntity); + } + } + $this->entityManager->persist($client); $this->entityManager->flush(); diff --git a/src/Controller/OgBoot/OgLive/SetDefaultAction.php b/src/Controller/OgBoot/OgLive/SetDefaultAction.php index b0d9698..3cf7184 100644 --- a/src/Controller/OgBoot/OgLive/SetDefaultAction.php +++ b/src/Controller/OgBoot/OgLive/SetDefaultAction.php @@ -38,6 +38,13 @@ class SetDefaultAction extends AbstractOgBootController $content = $this->createRequest($httpClient, 'PUT', $this->ogBootApiUrl.'/ogboot/v1/oglives/default', $params); + $oldDefaultOgLive = $this->entityManager->getRepository(OgLive::class)->findBy(['isDefault' => true]); + + foreach ($oldDefaultOgLive as $oldOgLive) { + $oldOgLive->setIsDefault(false); + $this->entityManager->persist($oldOgLive); + } + $data->setIsDefault(true); $entityManager->persist($data); $entityManager->flush(); diff --git a/src/Controller/OgBoot/OgLive/Webhook/InstallOgLiveResponseAction.php b/src/Controller/OgBoot/OgLive/Webhook/InstallOgLiveResponseAction.php index 2f4df6a..db90e58 100644 --- a/src/Controller/OgBoot/OgLive/Webhook/InstallOgLiveResponseAction.php +++ b/src/Controller/OgBoot/OgLive/Webhook/InstallOgLiveResponseAction.php @@ -82,9 +82,6 @@ class InstallOgLiveResponseAction extends AbstractController $ogLive->setStatus($status === self::OG_LIVE_INSTALL_SUCCESS ? OgLiveStatus::ACTIVE : OgLiveStatus::FAILED); $ogLive->setInstalled($status === self::OG_LIVE_INSTALL_SUCCESS); - $ogLive->setIsDefault(true); - - $this->entityManager->persist($ogLive); $oldDefaultOgLive = $this->entityManager->getRepository(OgLive::class)->findBy(['isDefault' => true]); @@ -93,6 +90,9 @@ class InstallOgLiveResponseAction extends AbstractController $this->entityManager->persist($oldOgLive); } + $ogLive->setIsDefault(true); + $this->entityManager->persist($ogLive); + $this->entityManager->flush(); } } \ No newline at end of file diff --git a/src/Entity/PxeTemplate.php b/src/Entity/PxeTemplate.php index 87b74b0..5a3efaa 100644 --- a/src/Entity/PxeTemplate.php +++ b/src/Entity/PxeTemplate.php @@ -3,6 +3,7 @@ namespace App\Entity; use App\Repository\PxeTemplateRepository; +use Doctrine\Common\Collections\ArrayCollection; use Doctrine\Common\Collections\Collection; use Doctrine\DBAL\Types\Types; use Doctrine\ORM\Mapping as ORM; @@ -25,6 +26,12 @@ class PxeTemplate extends AbstractEntity #[ORM\OneToMany(mappedBy: 'template', targetEntity: Client::class)] private Collection $clients; + public function __construct() + { + parent::__construct(); + $this->clients = new ArrayCollection(); + } + public function getTemplateContent(): ?string { return $this->templateContent; -- 2.40.1 From 835d461585653be39dfcf353e85f8c20ddcaea57 Mon Sep 17 00:00:00 2001 From: Manuel Aranda Date: Wed, 23 Oct 2024 08:43:21 +0200 Subject: [PATCH 118/157] Added DTO client position into OU --- src/Dto/Output/ClientOutput.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Dto/Output/ClientOutput.php b/src/Dto/Output/ClientOutput.php index 2724822..3d9399c 100644 --- a/src/Dto/Output/ClientOutput.php +++ b/src/Dto/Output/ClientOutput.php @@ -59,7 +59,7 @@ final class ClientOutput extends AbstractOutput #[Groups(['client:read'])] public ?string $subnet = null; - #[Groups(['client:read'])] + #[Groups(['client:read', 'organizational-unit:read'])] public ?array $position = ['x' => 0, 'y' => 0]; #[Groups(['client:read'])] -- 2.40.1 From 73fbd6b6f6cc006163c88e7702bdea828707f6b7 Mon Sep 17 00:00:00 2001 From: Nicolas Arenas Date: Wed, 23 Oct 2024 00:27:52 +0200 Subject: [PATCH 119/157] Modify Jenkinsfile to publish images --- Jenkinsfile | 64 +++++++++++++++++++++++++++------ docker-compose-ci-template.yaml | 51 ++++++++++++++++++++++++++ 2 files changed, 104 insertions(+), 11 deletions(-) create mode 100644 docker-compose-ci-template.yaml diff --git a/Jenkinsfile b/Jenkinsfile index faa778c..fb36ea2 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -1,33 +1,75 @@ pipeline { agent { - node { label 'jenkins-slave' - } + } + + environment { + DOCKER_REPO = "opengnsys" + DOCKER_CREDENTIALS = credentials('docker-hub-credentials') + DOCKER_TAG = "${env.BUILD_NUMBER}" + DOCKER_IMAGE_NAME = "ogcore" + BRANCH_NAME = "${GIT_BRANCH.split("/")[1]}" + DOCKER_IDENTITY = "${DOCKER_REPO}/${DOCKER_IMAGE_NAME}-php:${BRANCH_NAME}-${DOCKER_TAG}" + DOCKER_IDENTITY_NGINX = "${DOCKER_REPO}/${DOCKER_IMAGE_NAME}-nginx:${BRANCH_NAME}-${DOCKER_TAG}" + DOCKER_COMPOSE_TEMPLATE = 'docker-compose-ci-template.yaml' + DOCKER_COMPOSE_FILE = 'docker-compose-ci-tmp.yaml' } stages { stage('Build Environmen') { steps { - //Build environment - sh 'docker compose -f docker-compose-ci.yaml up --build -d' + script { + app = docker.build("${DOCKER_IDENTITY}", '-f docker/Dockerfile-php .') + nginx = docker.build("${DOCKER_IDENTITY_NGINX}", '-f docker/Dockerfile-nginx .') + } + } + } + stage(('Prepare Docker Composer')) { + steps { + sh """ + sed 's|ogcore-php:static|${DOCKER_IDENTITY}|g; s|ogcore-nginx:static|${DOCKER_IDENTITY_NGINX}|g' ${DOCKER_COMPOSE_TEMPLATE} > ${DOCKER_COMPOSE_FILE} + """ + } + } + + stage('Run containers') { + steps { + sh """ + docker compose -f ${DOCKER_COMPOSE_FILE} up -d + """ } } stage('Install dependencies') { steps { // Install dependencies - sh 'docker exec ogcore-php composer install' - sh 'docker exec ogcore-php php bin/console lexik:jwt:generate-keypair --overwrite' - sh 'docker exec ogcore-php php bin/console doctrine:migrations:migrate --no-interaction' - sh 'docker exec ogcore-php php bin/console doctrine:fixtures:load --no-interaction' + sh """ + docker compose exec php composer install + docker compose exec php php bin/console lexik:jwt:generate-keypair --overwrite + docker compose exec php php bin/console doctrine:migrations:migrate --no-interaction + docker compose exec php php bin/console doctrine:fixtures:load --no-interaction // Create report directory - sh 'docker exec ogcore-php mkdir -p /report' + docker compose exec php mkdir -p /report + """ } } + stage('Tests') { steps { // Run tests - sh 'docker compose exec php bin/phpunit --log-junit /report/phpunit.xml' - sh 'docker cp ogcore-php:/report/phpunit.xml .' + sh """ + docker compose exec php bin/phpunit --log-junit /report/phpunit.xml + docker compose cp php:/report/phpunit.xml ./phpunit.xml + """ + } + post { + sucess { + script { + docker.withRegistry('https://index.docker.io/v1/', "${DOCKER_CREDENTIALS}") { + app.push() + nginx.push() + } + } + } } } } diff --git a/docker-compose-ci-template.yaml b/docker-compose-ci-template.yaml new file mode 100644 index 0000000..b407aa8 --- /dev/null +++ b/docker-compose-ci-template.yaml @@ -0,0 +1,51 @@ +services: + database: + container_name: ogcore-database + image: mariadb:10.11 + environment: + MYSQL_ROOT_PASSWORD: root + MYSQL_DATABASE: ogcore + MYSQL_PASSWORD: root + MYSQL_USER: admin + ports: + - 3336:3306 + volumes: + - database_data:/var/lib/mysql + networks: + - ogcore-network + + nginx: + container_name: ogcore-nginx + build: + context: . + dockerfile: ./docker/Dockerfile-nginx + depends_on: + - php + ports: + - 8080:80 + volumes: + - ./public:/var/www/html/public:cached + networks: + - ogcore-network + image: ogcore-nginx:static + + php: + container_name: ogcore-php + build: + context: . + dockerfile: ./docker/Dockerfile-jenkins-php + depends_on: + - database + environment: + XDEBUG_CLIENT_HOST: 127.17.0.1 + XDEBUG_CLIENT_PORT: 9003 + PHP_IDE_CONFIG: serverName=ogcore + networks: + - ogcore-network + image: ogcore-php:static + +volumes: + database_data: + +networks: + ogcore-network: -- 2.40.1 From d78e719a9182d02b248b0edc4578a1791c5500a6 Mon Sep 17 00:00:00 2001 From: Nicolas Arenas Date: Wed, 23 Oct 2024 00:29:23 +0200 Subject: [PATCH 120/157] Fix typo --- Jenkinsfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Jenkinsfile b/Jenkinsfile index fb36ea2..35ba55a 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -62,7 +62,7 @@ pipeline { """ } post { - sucess { + success { script { docker.withRegistry('https://index.docker.io/v1/', "${DOCKER_CREDENTIALS}") { app.push() -- 2.40.1 From 98f15df11d87074b9cf9ecbbb09bf23bebc86c89 Mon Sep 17 00:00:00 2001 From: Nicolas Arenas Date: Wed, 23 Oct 2024 00:35:45 +0200 Subject: [PATCH 121/157] Miniaml changes --- Jenkinsfile | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index 35ba55a..5e7e046 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -16,7 +16,7 @@ pipeline { } stages { - stage('Build Environmen') { + stage('Build Environment') { steps { script { app = docker.build("${DOCKER_IDENTITY}", '-f docker/Dockerfile-php .') @@ -34,9 +34,7 @@ pipeline { stage('Run containers') { steps { - sh """ - docker compose -f ${DOCKER_COMPOSE_FILE} up -d - """ + sh "docker compose -f ${DOCKER_COMPOSE_FILE} up -d" } } stage('Install dependencies') { @@ -65,8 +63,8 @@ pipeline { success { script { docker.withRegistry('https://index.docker.io/v1/', "${DOCKER_CREDENTIALS}") { - app.push() - nginx.push() + app.push("${DOCKER_IDENTITY}") + nginx.push("${DOCKER_IDENTITY_NGINX}") } } } @@ -81,8 +79,8 @@ pipeline { tools: [ PHPUnit(pattern: 'phpunit.xml') ] ) // Remove containers - sh 'docker compose -f docker-compose-ci.yaml down' - sh 'docker compose -f docker-compose-ci.yaml rm -f' + sh 'docker compose -f ${DOCKER_COMPOSE_FILE} down' + sh 'docker compose -f ${DOCKER_COMPOSE_FILE} rm -f' } } } -- 2.40.1 From 33025e84ab9640aa6db293252cdecbd8e9a924fe Mon Sep 17 00:00:00 2001 From: Nicolas Arenas Date: Wed, 23 Oct 2024 00:40:08 +0200 Subject: [PATCH 122/157] Xunit back to node --- Jenkinsfile | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index 5e7e046..827eea8 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -73,11 +73,13 @@ pipeline { } post { always { - // Publish JUnit test results - xunit ( - thresholds: [ skipped(failureThreshold: '0') , failed(failureThreshold: '0') ], - tools: [ PHPUnit(pattern: 'phpunit.xml') ] - ) + node { + // Publicar los resultados de las pruebas de PHPUnit + xunit ( + thresholds: [ skipped(failureThreshold: '0'), failed(failureThreshold: '0') ], + tools: [ PHPUnit(pattern: 'phpunit.xml') ] + ) + } // Remove containers sh 'docker compose -f ${DOCKER_COMPOSE_FILE} down' sh 'docker compose -f ${DOCKER_COMPOSE_FILE} rm -f' -- 2.40.1 From d2e9732240c5964a040bfdec4cf079a7a603776d Mon Sep 17 00:00:00 2001 From: Nicolas Arenas Date: Wed, 23 Oct 2024 00:44:38 +0200 Subject: [PATCH 123/157] Fix --- Jenkinsfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Jenkinsfile b/Jenkinsfile index 827eea8..c6eb2f7 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -73,7 +73,7 @@ pipeline { } post { always { - node { + script { // Publicar los resultados de las pruebas de PHPUnit xunit ( thresholds: [ skipped(failureThreshold: '0'), failed(failureThreshold: '0') ], -- 2.40.1 From 80f2242b6c9778e7cde781ba3da70f186eeb848d Mon Sep 17 00:00:00 2001 From: Nicolas Arenas Date: Wed, 23 Oct 2024 00:49:49 +0200 Subject: [PATCH 124/157] Fix to push imagws --- Jenkinsfile | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index c6eb2f7..32a98e9 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -19,8 +19,8 @@ pipeline { stage('Build Environment') { steps { script { - app = docker.build("${DOCKER_IDENTITY}", '-f docker/Dockerfile-php .') - nginx = docker.build("${DOCKER_IDENTITY_NGINX}", '-f docker/Dockerfile-nginx .') + docker.build("${DOCKER_IDENTITY}", '-f docker/Dockerfile-php .') + docker.build("${DOCKER_IDENTITY_NGINX}", '-f docker/Dockerfile-nginx .') } } } @@ -63,8 +63,8 @@ pipeline { success { script { docker.withRegistry('https://index.docker.io/v1/', "${DOCKER_CREDENTIALS}") { - app.push("${DOCKER_IDENTITY}") - nginx.push("${DOCKER_IDENTITY_NGINX}") + docker.image("${DOCKER_IDENTITY}").push() + docker.image("${DOCKER_IDENTITY_NGINX}").push() } } } @@ -73,13 +73,11 @@ pipeline { } post { always { - script { - // Publicar los resultados de las pruebas de PHPUnit - xunit ( - thresholds: [ skipped(failureThreshold: '0'), failed(failureThreshold: '0') ], - tools: [ PHPUnit(pattern: 'phpunit.xml') ] - ) - } + // Publicar los resultados de las pruebas de PHPUnit + xunit ( + thresholds: [ skipped(failureThreshold: '0'), failed(failureThreshold: '0') ], + tools: [ PHPUnit(pattern: 'phpunit.xml') ] + ) // Remove containers sh 'docker compose -f ${DOCKER_COMPOSE_FILE} down' sh 'docker compose -f ${DOCKER_COMPOSE_FILE} rm -f' -- 2.40.1 From 7bbf84cc211b66a7a52b1afa3e9dd5fb1f97d5f0 Mon Sep 17 00:00:00 2001 From: Nicolas Arenas Date: Wed, 23 Oct 2024 00:51:27 +0200 Subject: [PATCH 125/157] Try comment out post condition --- Jenkinsfile | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index 32a98e9..553edd7 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -59,16 +59,16 @@ pipeline { docker compose cp php:/report/phpunit.xml ./phpunit.xml """ } - post { - success { - script { - docker.withRegistry('https://index.docker.io/v1/', "${DOCKER_CREDENTIALS}") { - docker.image("${DOCKER_IDENTITY}").push() - docker.image("${DOCKER_IDENTITY_NGINX}").push() - } - } - } - } + // post { + // success { + // script { + // docker.withRegistry('https://index.docker.io/v1/', "${DOCKER_CREDENTIALS}") { + // docker.image("${DOCKER_IDENTITY}").push() + // docker.image("${DOCKER_IDENTITY_NGINX}").push() + // } + // } + // } + // } } } post { -- 2.40.1 From c8e0fe69c695c7b0124ae00dab4e5870171c526d Mon Sep 17 00:00:00 2001 From: Nicolas Arenas Date: Wed, 23 Oct 2024 00:53:48 +0200 Subject: [PATCH 126/157] Step 1 --- Jenkinsfile | 120 ++++++++++++++++++++++++++++------------------------ 1 file changed, 65 insertions(+), 55 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index 553edd7..1c2dd88 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -24,63 +24,73 @@ pipeline { } } } - stage(('Prepare Docker Composer')) { + stage ("Delete Image") { steps { - sh """ - sed 's|ogcore-php:static|${DOCKER_IDENTITY}|g; s|ogcore-nginx:static|${DOCKER_IDENTITY_NGINX}|g' ${DOCKER_COMPOSE_TEMPLATE} > ${DOCKER_COMPOSE_FILE} - """ + script { + sh "docker rmi ${DOCKER_IDENTITY}" + sh "docker rmi ${DOCKER_IDENTITY_NGINX}" + } } } - - stage('Run containers') { - steps { - sh "docker compose -f ${DOCKER_COMPOSE_FILE} up -d" - } - } - stage('Install dependencies') { - steps { - // Install dependencies - sh """ - docker compose exec php composer install - docker compose exec php php bin/console lexik:jwt:generate-keypair --overwrite - docker compose exec php php bin/console doctrine:migrations:migrate --no-interaction - docker compose exec php php bin/console doctrine:fixtures:load --no-interaction - // Create report directory - docker compose exec php mkdir -p /report - """ - } - } - - stage('Tests') { - steps { - // Run tests - sh """ - docker compose exec php bin/phpunit --log-junit /report/phpunit.xml - docker compose cp php:/report/phpunit.xml ./phpunit.xml - """ - } - // post { - // success { - // script { - // docker.withRegistry('https://index.docker.io/v1/', "${DOCKER_CREDENTIALS}") { - // docker.image("${DOCKER_IDENTITY}").push() - // docker.image("${DOCKER_IDENTITY_NGINX}").push() - // } - // } - // } - // } - } - } - post { - always { - // Publicar los resultados de las pruebas de PHPUnit - xunit ( - thresholds: [ skipped(failureThreshold: '0'), failed(failureThreshold: '0') ], - tools: [ PHPUnit(pattern: 'phpunit.xml') ] - ) - // Remove containers - sh 'docker compose -f ${DOCKER_COMPOSE_FILE} down' - sh 'docker compose -f ${DOCKER_COMPOSE_FILE} rm -f' - } } + + // stage(('Prepare Docker Composer')) { + // steps { + // sh """ + // sed 's|ogcore-php:static|${DOCKER_IDENTITY}|g; s|ogcore-nginx:static|${DOCKER_IDENTITY_NGINX}|g' ${DOCKER_COMPOSE_TEMPLATE} > ${DOCKER_COMPOSE_FILE} + // """ + // } + // } + + // stage('Run containers') { + // steps { + // sh "docker compose -f ${DOCKER_COMPOSE_FILE} up -d" + // } + // } + // stage('Install dependencies') { + // steps { + // // Install dependencies + // sh """ + // docker compose exec php composer install + // docker compose exec php php bin/console lexik:jwt:generate-keypair --overwrite + // docker compose exec php php bin/console doctrine:migrations:migrate --no-interaction + // docker compose exec php php bin/console doctrine:fixtures:load --no-interaction + // // Create report directory + // docker compose exec php mkdir -p /report + // """ + // } + // } + + // stage('Tests') { + // steps { + // // Run tests + // sh """ + // docker compose exec php bin/phpunit --log-junit /report/phpunit.xml + // docker compose cp php:/report/phpunit.xml ./phpunit.xml + // """ + // } + // // post { + // // success { + // // script { + // // docker.withRegistry('https://index.docker.io/v1/', "${DOCKER_CREDENTIALS}") { + // // docker.image("${DOCKER_IDENTITY}").push() + // // docker.image("${DOCKER_IDENTITY_NGINX}").push() + // // } + // // } + // // } + // // } + // } + // } + // post { + // always { + // // Publicar los resultados de las pruebas de PHPUnit + // xunit ( + // thresholds: [ skipped(failureThreshold: '0'), failed(failureThreshold: '0') ], + // tools: [ PHPUnit(pattern: 'phpunit.xml') ] + // ) + // // Remove containers + // sh 'docker compose -f ${DOCKER_COMPOSE_FILE} down' + // sh 'docker compose -f ${DOCKER_COMPOSE_FILE} rm -f' + // } + // } } -- 2.40.1 From dc894ad22b8bc3f4644b8a6e14efdf02331b1817 Mon Sep 17 00:00:00 2001 From: Nicolas Arenas Date: Wed, 23 Oct 2024 00:54:53 +0200 Subject: [PATCH 127/157] Explicit checkout --- Jenkinsfile | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Jenkinsfile b/Jenkinsfile index 1c2dd88..2d2e5e7 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -16,6 +16,11 @@ pipeline { } stages { + stage ("Checkout") { + steps { + checkout scm + } + } stage('Build Environment') { steps { script { @@ -33,7 +38,7 @@ pipeline { } } } - + // stage(('Prepare Docker Composer')) { // steps { // sh """ -- 2.40.1 From dd031e033c5e3463c92bfe9b3b8cba9669d64358 Mon Sep 17 00:00:00 2001 From: Nicolas Arenas Date: Wed, 23 Oct 2024 00:56:16 +0200 Subject: [PATCH 128/157] Test --- Jenkinsfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Jenkinsfile b/Jenkinsfile index 2d2e5e7..03020bf 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -8,7 +8,7 @@ pipeline { DOCKER_CREDENTIALS = credentials('docker-hub-credentials') DOCKER_TAG = "${env.BUILD_NUMBER}" DOCKER_IMAGE_NAME = "ogcore" - BRANCH_NAME = "${GIT_BRANCH.split("/")[1]}" + BRANCH_NAME = "${env.BRANCH_NAME}" DOCKER_IDENTITY = "${DOCKER_REPO}/${DOCKER_IMAGE_NAME}-php:${BRANCH_NAME}-${DOCKER_TAG}" DOCKER_IDENTITY_NGINX = "${DOCKER_REPO}/${DOCKER_IMAGE_NAME}-nginx:${BRANCH_NAME}-${DOCKER_TAG}" DOCKER_COMPOSE_TEMPLATE = 'docker-compose-ci-template.yaml' -- 2.40.1 From b077a9d5a760c61a86428c4c24fdaccbb9479048 Mon Sep 17 00:00:00 2001 From: Nicolas Arenas Date: Wed, 23 Oct 2024 01:01:43 +0200 Subject: [PATCH 129/157] Adds running steps --- Jenkinsfile | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/Jenkinsfile b/Jenkinsfile index 03020bf..6af2d7f 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -29,6 +29,20 @@ pipeline { } } } + stage(('Prepare Docker Composer')) { + steps { + sh """ + sed 's|ogcore-php:static|${DOCKER_IDENTITY}|g; s|ogcore-nginx:static|${DOCKER_IDENTITY_NGINX}|g' ${DOCKER_COMPOSE_TEMPLATE} > ${DOCKER_COMPOSE_FILE} + """ + } + } + stage('Run containers') { + steps { + sh "docker compose -f ${DOCKER_COMPOSE_FILE} up -d" + sh "docker compose -f ${DOCKER_COMPOSE_FILE} ps" + sh "docker compose -f ${DOCKER_COMPOSE_FILE} down" + } + } stage ("Delete Image") { steps { script { -- 2.40.1 From 19f44305564a8663643da41fafebd7a5c2ce47ea Mon Sep 17 00:00:00 2001 From: Nicolas Arenas Date: Wed, 23 Oct 2024 01:03:07 +0200 Subject: [PATCH 130/157] Install deps --- Jenkinsfile | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index 6af2d7f..8450a43 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -39,8 +39,19 @@ pipeline { stage('Run containers') { steps { sh "docker compose -f ${DOCKER_COMPOSE_FILE} up -d" - sh "docker compose -f ${DOCKER_COMPOSE_FILE} ps" - sh "docker compose -f ${DOCKER_COMPOSE_FILE} down" + } + } + stage('Install dependencies') { + steps { + // Install dependencies + sh """ + docker compose exec php composer install + docker compose exec php php bin/console lexik:jwt:generate-keypair --overwrite + docker compose exec php php bin/console doctrine:migrations:migrate --no-interaction + docker compose exec php php bin/console doctrine:fixtures:load --no-interaction + // Create report directory + docker compose exec php mkdir -p /report + """ } } stage ("Delete Image") { -- 2.40.1 From 51500a4e35be95a3d758df515ae8e85266846100 Mon Sep 17 00:00:00 2001 From: Nicolas Arenas Date: Wed, 23 Oct 2024 01:08:14 +0200 Subject: [PATCH 131/157] Step 4 --- Jenkinsfile | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Jenkinsfile b/Jenkinsfile index 8450a43..56f701d 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -33,6 +33,7 @@ pipeline { steps { sh """ sed 's|ogcore-php:static|${DOCKER_IDENTITY}|g; s|ogcore-nginx:static|${DOCKER_IDENTITY_NGINX}|g' ${DOCKER_COMPOSE_TEMPLATE} > ${DOCKER_COMPOSE_FILE} + cat ${DOCKER_COMPOSE_FILE} """ } } @@ -54,6 +55,11 @@ pipeline { """ } } + stage ("Stop containers") { + steps { + sh "docker compose -f ${DOCKER_COMPOSE_FILE} down" + } + } stage ("Delete Image") { steps { script { -- 2.40.1 From c9320fb658e462e6d75da0fd93c35eb22c11c62e Mon Sep 17 00:00:00 2001 From: Nicolas Arenas Date: Wed, 23 Oct 2024 01:15:48 +0200 Subject: [PATCH 132/157] Updated path --- Jenkinsfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Jenkinsfile b/Jenkinsfile index 56f701d..0f03a6e 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -24,7 +24,7 @@ pipeline { stage('Build Environment') { steps { script { - docker.build("${DOCKER_IDENTITY}", '-f docker/Dockerfile-php .') + docker.build("${DOCKER_IDENTITY}", '-f docker/Dockerfile-jenkins-php .') docker.build("${DOCKER_IDENTITY_NGINX}", '-f docker/Dockerfile-nginx .') } } -- 2.40.1 From 3bb426c6c3a1eeff915788981c7e77fbdb2389ac Mon Sep 17 00:00:00 2001 From: Nicolas Arenas Date: Wed, 23 Oct 2024 01:20:42 +0200 Subject: [PATCH 133/157] Fix --- Jenkinsfile | 1 - 1 file changed, 1 deletion(-) diff --git a/Jenkinsfile b/Jenkinsfile index 0f03a6e..ba87abe 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -50,7 +50,6 @@ pipeline { docker compose exec php php bin/console lexik:jwt:generate-keypair --overwrite docker compose exec php php bin/console doctrine:migrations:migrate --no-interaction docker compose exec php php bin/console doctrine:fixtures:load --no-interaction - // Create report directory docker compose exec php mkdir -p /report """ } -- 2.40.1 From 231d72968289e575d6295754354a093a0fbcc30a Mon Sep 17 00:00:00 2001 From: Nicolas Arenas Date: Wed, 23 Oct 2024 01:25:14 +0200 Subject: [PATCH 134/157] Running Tests --- Jenkinsfile | 37 ++++++++++--------------------------- 1 file changed, 10 insertions(+), 27 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index ba87abe..b9c101f 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -59,6 +59,15 @@ pipeline { sh "docker compose -f ${DOCKER_COMPOSE_FILE} down" } } + stage('Tests') { + steps { + // Run tests + sh """ + docker compose exec php bin/phpunit --log-junit /report/phpunit.xml + docker compose cp php:/report/phpunit.xml ./phpunit.xml + """ + } + } stage ("Delete Image") { steps { script { @@ -69,33 +78,7 @@ pipeline { } } - // stage(('Prepare Docker Composer')) { - // steps { - // sh """ - // sed 's|ogcore-php:static|${DOCKER_IDENTITY}|g; s|ogcore-nginx:static|${DOCKER_IDENTITY_NGINX}|g' ${DOCKER_COMPOSE_TEMPLATE} > ${DOCKER_COMPOSE_FILE} - // """ - // } - // } - - // stage('Run containers') { - // steps { - // sh "docker compose -f ${DOCKER_COMPOSE_FILE} up -d" - // } - // } - // stage('Install dependencies') { - // steps { - // // Install dependencies - // sh """ - // docker compose exec php composer install - // docker compose exec php php bin/console lexik:jwt:generate-keypair --overwrite - // docker compose exec php php bin/console doctrine:migrations:migrate --no-interaction - // docker compose exec php php bin/console doctrine:fixtures:load --no-interaction - // // Create report directory - // docker compose exec php mkdir -p /report - // """ - // } - // } - + // stage('Tests') { // steps { // // Run tests -- 2.40.1 From 541653689eafb51f110c8c7a0fb96354b14df55d Mon Sep 17 00:00:00 2001 From: Nicolas Arenas Date: Wed, 23 Oct 2024 01:27:04 +0200 Subject: [PATCH 135/157] Running test --- Jenkinsfile | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index b9c101f..f38f0bb 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -54,11 +54,6 @@ pipeline { """ } } - stage ("Stop containers") { - steps { - sh "docker compose -f ${DOCKER_COMPOSE_FILE} down" - } - } stage('Tests') { steps { // Run tests @@ -71,6 +66,7 @@ pipeline { stage ("Delete Image") { steps { script { + sh "docker compose -f ${DOCKER_COMPOSE_FILE} down" sh "docker rmi ${DOCKER_IDENTITY}" sh "docker rmi ${DOCKER_IDENTITY_NGINX}" } -- 2.40.1 From e675af2b24114a95c0a6340f21e7c9efedfaddd9 Mon Sep 17 00:00:00 2001 From: Nicolas Arenas Date: Wed, 23 Oct 2024 01:35:28 +0200 Subject: [PATCH 136/157] Delete and post --- Jenkinsfile | 57 +++++++++++++++++++++-------------------------------- 1 file changed, 23 insertions(+), 34 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index f38f0bb..841efba 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -72,39 +72,28 @@ pipeline { } } } - } + stage ("Publish Image") { + steps { + script { + docker.withRegistry('https://index.docker.io/v1/', "${DOCKER_CREDENTIALS}") { + docker.image("${DOCKER_IDENTITY}").push() + docker.image("${DOCKER_IDENTITY_NGINX}").push() + } + } + } + } - - // stage('Tests') { - // steps { - // // Run tests - // sh """ - // docker compose exec php bin/phpunit --log-junit /report/phpunit.xml - // docker compose cp php:/report/phpunit.xml ./phpunit.xml - // """ - // } - // // post { - // // success { - // // script { - // // docker.withRegistry('https://index.docker.io/v1/', "${DOCKER_CREDENTIALS}") { - // // docker.image("${DOCKER_IDENTITY}").push() - // // docker.image("${DOCKER_IDENTITY_NGINX}").push() - // // } - // // } - // // } - // // } - // } - // } - // post { - // always { - // // Publicar los resultados de las pruebas de PHPUnit - // xunit ( - // thresholds: [ skipped(failureThreshold: '0'), failed(failureThreshold: '0') ], - // tools: [ PHPUnit(pattern: 'phpunit.xml') ] - // ) - // // Remove containers - // sh 'docker compose -f ${DOCKER_COMPOSE_FILE} down' - // sh 'docker compose -f ${DOCKER_COMPOSE_FILE} rm -f' - // } - // } + } + post { + always { + // Publicar los resultados de las pruebas de PHPUnit + xunit ( + thresholds: [ skipped(failureThreshold: '0'), failed(failureThreshold: '0') ], + tools: [ PHPUnit(pattern: 'phpunit.xml') ] + ) + // Remove containers + sh 'docker compose -f ${DOCKER_COMPOSE_FILE} down' + sh 'docker compose -f ${DOCKER_COMPOSE_FILE} rm -f' + } + } } -- 2.40.1 From e6024eb110ba7f674d4635d59e976dbb2922aaa8 Mon Sep 17 00:00:00 2001 From: Nicolas Arenas Date: Wed, 23 Oct 2024 08:17:28 +0200 Subject: [PATCH 137/157] Generate stuff report before tests --- Jenkinsfile | 22 +++++++--------------- docker-compose-ci-template.yaml | 2 ++ 2 files changed, 9 insertions(+), 15 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index 841efba..a8b07f1 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -46,11 +46,11 @@ pipeline { steps { // Install dependencies sh """ + rm -rf ./report docker compose exec php composer install docker compose exec php php bin/console lexik:jwt:generate-keypair --overwrite docker compose exec php php bin/console doctrine:migrations:migrate --no-interaction docker compose exec php php bin/console doctrine:fixtures:load --no-interaction - docker compose exec php mkdir -p /report """ } } @@ -59,19 +59,9 @@ pipeline { // Run tests sh """ docker compose exec php bin/phpunit --log-junit /report/phpunit.xml - docker compose cp php:/report/phpunit.xml ./phpunit.xml """ } } - stage ("Delete Image") { - steps { - script { - sh "docker compose -f ${DOCKER_COMPOSE_FILE} down" - sh "docker rmi ${DOCKER_IDENTITY}" - sh "docker rmi ${DOCKER_IDENTITY_NGINX}" - } - } - } stage ("Publish Image") { steps { script { @@ -86,14 +76,16 @@ pipeline { } post { always { - // Publicar los resultados de las pruebas de PHPUnit + // Publish JUnit test results xunit ( - thresholds: [ skipped(failureThreshold: '0'), failed(failureThreshold: '0') ], + thresholds: [ skipped(failureThreshold: '0') , failed(failureThreshold: '0') ], tools: [ PHPUnit(pattern: 'phpunit.xml') ] ) // Remove containers - sh 'docker compose -f ${DOCKER_COMPOSE_FILE} down' - sh 'docker compose -f ${DOCKER_COMPOSE_FILE} rm -f' + sh "docker compose -f ${DOCKER_COMPOSE_FILE} down" + sh "docker compose -f ${DOCKER_COMPOSE_FILE} rm -f" + sh "docker rmi ${DOCKER_IDENTITY}" + sh "docker rmi ${DOCKER_IDENTITY_NGINX}" } } } diff --git a/docker-compose-ci-template.yaml b/docker-compose-ci-template.yaml index b407aa8..0863431 100644 --- a/docker-compose-ci-template.yaml +++ b/docker-compose-ci-template.yaml @@ -43,6 +43,8 @@ services: networks: - ogcore-network image: ogcore-php:static + volumes: + - ./report:/report/ volumes: database_data: -- 2.40.1 From 0eeab3dc422d21781135db6dabc83fb5576befb4 Mon Sep 17 00:00:00 2001 From: Nicolas Arenas Date: Wed, 23 Oct 2024 08:21:07 +0200 Subject: [PATCH 138/157] Create report directory --- Jenkinsfile | 1 + 1 file changed, 1 insertion(+) diff --git a/Jenkinsfile b/Jenkinsfile index a8b07f1..514c673 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -47,6 +47,7 @@ pipeline { // Install dependencies sh """ rm -rf ./report + mkdir -p ./report docker compose exec php composer install docker compose exec php php bin/console lexik:jwt:generate-keypair --overwrite docker compose exec php php bin/console doctrine:migrations:migrate --no-interaction -- 2.40.1 From ad64bf3861c566da6b74ac3561af6ead40694920 Mon Sep 17 00:00:00 2001 From: Nicolas Arenas Date: Wed, 23 Oct 2024 08:21:31 +0200 Subject: [PATCH 139/157] Create report directory --- Jenkinsfile | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index 514c673..3ecf992 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -46,8 +46,7 @@ pipeline { steps { // Install dependencies sh """ - rm -rf ./report - mkdir -p ./report + rm -rf ./report && mkdir -p ./report docker compose exec php composer install docker compose exec php php bin/console lexik:jwt:generate-keypair --overwrite docker compose exec php php bin/console doctrine:migrations:migrate --no-interaction -- 2.40.1 From 77ad48f717aa36a22e161ef7e5912e5c08ee0c97 Mon Sep 17 00:00:00 2001 From: Nicolas Arenas Date: Wed, 23 Oct 2024 08:31:25 +0200 Subject: [PATCH 140/157] Adjust report directory --- docker-compose-ci-template.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker-compose-ci-template.yaml b/docker-compose-ci-template.yaml index 0863431..80c5cc9 100644 --- a/docker-compose-ci-template.yaml +++ b/docker-compose-ci-template.yaml @@ -44,7 +44,7 @@ services: - ogcore-network image: ogcore-php:static volumes: - - ./report:/report/ + - ./report:/root/ volumes: database_data: -- 2.40.1 From 8b8c57f8a4b5c4245c0e20e946f90686a3501328 Mon Sep 17 00:00:00 2001 From: Nicolas Arenas Date: Wed, 23 Oct 2024 08:47:20 +0200 Subject: [PATCH 141/157] Test to copy report back to jenkins --- Jenkinsfile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Jenkinsfile b/Jenkinsfile index 3ecf992..5b2c3a5 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -46,7 +46,7 @@ pipeline { steps { // Install dependencies sh """ - rm -rf ./report && mkdir -p ./report + docker compose exec php mkdir -p /report docker compose exec php composer install docker compose exec php php bin/console lexik:jwt:generate-keypair --overwrite docker compose exec php php bin/console doctrine:migrations:migrate --no-interaction @@ -76,6 +76,7 @@ pipeline { } post { always { + sh "docker compose -f ${DOCKER_COMPOSE_FILE} cp php:/report/phpunit.xml phpunit.xml" // Publish JUnit test results xunit ( thresholds: [ skipped(failureThreshold: '0') , failed(failureThreshold: '0') ], -- 2.40.1 From bb37c7aea06c9ab77384ee6c2fee485f51db6e23 Mon Sep 17 00:00:00 2001 From: Nicolas Arenas Date: Wed, 23 Oct 2024 09:17:25 +0200 Subject: [PATCH 142/157] Change credentials --- Jenkinsfile | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index 5b2c3a5..c8fdee4 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -5,7 +5,6 @@ pipeline { environment { DOCKER_REPO = "opengnsys" - DOCKER_CREDENTIALS = credentials('docker-hub-credentials') DOCKER_TAG = "${env.BUILD_NUMBER}" DOCKER_IMAGE_NAME = "ogcore" BRANCH_NAME = "${env.BRANCH_NAME}" @@ -65,7 +64,7 @@ pipeline { stage ("Publish Image") { steps { script { - docker.withRegistry('https://index.docker.io/v1/', "${DOCKER_CREDENTIALS}") { + docker.withRegistry('https://index.docker.io/v1/', 'docker-hub-credentials') { docker.image("${DOCKER_IDENTITY}").push() docker.image("${DOCKER_IDENTITY_NGINX}").push() } -- 2.40.1 From 5c43c43342fe0ed12e749b40f5867a62e89a3b91 Mon Sep 17 00:00:00 2001 From: Nicolas Arenas Date: Wed, 23 Oct 2024 09:23:07 +0200 Subject: [PATCH 143/157] Delete volume from docker compose template --- docker-compose-ci-template.yaml | 2 -- 1 file changed, 2 deletions(-) diff --git a/docker-compose-ci-template.yaml b/docker-compose-ci-template.yaml index 80c5cc9..b407aa8 100644 --- a/docker-compose-ci-template.yaml +++ b/docker-compose-ci-template.yaml @@ -43,8 +43,6 @@ services: networks: - ogcore-network image: ogcore-php:static - volumes: - - ./report:/root/ volumes: database_data: -- 2.40.1 From e9619575ae4104c01bac77fd0dd9bf3bbe4d3bb4 Mon Sep 17 00:00:00 2001 From: Manuel Aranda Date: Wed, 23 Oct 2024 09:56:27 +0200 Subject: [PATCH 144/157] Added DTO client position into OU --- src/Controller/OgAgent/StatusAction.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Controller/OgAgent/StatusAction.php b/src/Controller/OgAgent/StatusAction.php index c042048..b570e1a 100644 --- a/src/Controller/OgAgent/StatusAction.php +++ b/src/Controller/OgAgent/StatusAction.php @@ -55,7 +55,7 @@ class StatusAction extends AbstractController $client->setStatus('off'); return new JsonResponse( - data: ['error' => 'An error occurred while communicating with the client'], + data: ['error' => $e->getMessage()], status: Response::HTTP_INTERNAL_SERVER_ERROR ); } -- 2.40.1 From 436c297b7312ff18395fc42c22fa8443b02bd372 Mon Sep 17 00:00:00 2001 From: Manuel Aranda Date: Wed, 23 Oct 2024 10:08:41 +0200 Subject: [PATCH 145/157] Fixed bugs --- src/Controller/OgAgent/StatusAction.php | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) diff --git a/src/Controller/OgAgent/StatusAction.php b/src/Controller/OgAgent/StatusAction.php index b570e1a..7cc4061 100644 --- a/src/Controller/OgAgent/StatusAction.php +++ b/src/Controller/OgAgent/StatusAction.php @@ -25,20 +25,8 @@ class StatusAction extends AbstractController public function __construct( protected readonly EntityManagerInterface $entityManager, protected readonly HttpClientInterface $httpClient - ) - { - $httpClient = HttpClient::create([ - 'verify_peer' => false, - 'verify_host' => false, - ]); - } + ) {} - /** - * @throws TransportExceptionInterface - * @throws ServerExceptionInterface - * @throws RedirectionExceptionInterface - * @throws ClientExceptionInterface - */ public function __invoke(Client $client): JsonResponse { if (!$client->getIp()) { @@ -46,7 +34,10 @@ class StatusAction extends AbstractController } try { - $response = $this->httpClient->request('POST', 'https://' . $client->getIp() . ':8000/ogAdmClient/status'); + $response = $this->httpClient->request('POST', 'https://' . $client->getIp() . ':8000/ogAdmClient/status', [ + 'verify_peer' => false, + 'verify_host' => false, + ]); $statusCode = $response->getStatusCode(); $client->setStatus($statusCode === Response::HTTP_OK ? 'active' : 'off'); -- 2.40.1 From 104d1dc8acece1a4bb783cf84be9995ecfc5a1ba Mon Sep 17 00:00:00 2001 From: Manuel Aranda Date: Wed, 23 Oct 2024 10:16:50 +0200 Subject: [PATCH 146/157] Test ogdClient --- .../OgAgent/OgAdmClientController.php | 28 +++++++++++-------- 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/src/Controller/OgAgent/OgAdmClientController.php b/src/Controller/OgAgent/OgAdmClientController.php index f8fe811..bef47ef 100644 --- a/src/Controller/OgAgent/OgAdmClientController.php +++ b/src/Controller/OgAgent/OgAdmClientController.php @@ -44,22 +44,26 @@ class OgAdmClientController extends AbstractController } foreach ($data['cfg'] as $cfg) { - $partitionEntity = $this->entityManager->getRepository(Partition::class) - ->findOneBy(['client' => $clientEntity, 'diskNumber' => $cfg['disk'], 'partitionNumber' => $cfg['par']]); + if (isset($cfg['disk']) && isset($cfg['par'])) { + $partitionEntity = $this->entityManager->getRepository(Partition::class) + ->findOneBy(['client' => $clientEntity, 'diskNumber' => $cfg['disk'], 'partitionNumber' => $cfg['par']]); - if (!$partitionEntity) { - $partitionEntity = new Partition(); + if (!$partitionEntity) { + $partitionEntity = new Partition(); + } + + $partitionEntity->setClient($clientEntity); + $partitionEntity->setDiskNumber($cfg['disk']); + $partitionEntity->setPartitionNumber($cfg['par']); + $partitionEntity->setSize($cfg['tam'] ?? null); + $partitionEntity->setMemoryUsage($cfg['uso'] ?? null); + + $this->entityManager->persist($partitionEntity); } - - $partitionEntity->setClient($clientEntity); - $partitionEntity->setDiskNumber($cfg['disk']); - $partitionEntity->setPartitionNumber($cfg['par']); - $partitionEntity->setSize($cfg['tam']); - $partitionEntity->setMemoryUsage($cfg['uso']); - $this->entityManager->persist($partitionEntity); - $this->entityManager->flush(); } + $this->entityManager->flush(); + $center = $this->entityManager->getRepository(OrganizationalUnit::class)->find($clientEntity->getOrganizationalUnit()->getId()); $root = $this->entityManager->getRepository(OrganizationalUnit::class)->getRootNodes(); -- 2.40.1 From f13731a047dfa31d8da1e8235672153e98ef9725 Mon Sep 17 00:00:00 2001 From: Manuel Aranda Date: Wed, 23 Oct 2024 10:23:41 +0200 Subject: [PATCH 147/157] Test ogdClient --- src/Controller/OgAgent/OgAdmClientController.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Controller/OgAgent/OgAdmClientController.php b/src/Controller/OgAgent/OgAdmClientController.php index bef47ef..f533f9f 100644 --- a/src/Controller/OgAgent/OgAdmClientController.php +++ b/src/Controller/OgAgent/OgAdmClientController.php @@ -53,10 +53,10 @@ class OgAdmClientController extends AbstractController } $partitionEntity->setClient($clientEntity); - $partitionEntity->setDiskNumber($cfg['disk']); - $partitionEntity->setPartitionNumber($cfg['par']); - $partitionEntity->setSize($cfg['tam'] ?? null); - $partitionEntity->setMemoryUsage($cfg['uso'] ?? null); + $partitionEntity->setDiskNumber((int)$cfg['disk']); + $partitionEntity->setPartitionNumber((int) $cfg['par']); + $partitionEntity->setSize((int) $cfg['tam'] ?? null); + $partitionEntity->setMemoryUsage((int) $cfg['uso'] ?? null); $this->entityManager->persist($partitionEntity); } -- 2.40.1 From 4973be6af67931a350701b34fb961cc56b0cc061 Mon Sep 17 00:00:00 2001 From: Manuel Aranda Date: Wed, 23 Oct 2024 10:28:14 +0200 Subject: [PATCH 148/157] Test ogdClient --- src/Controller/OgAgent/StatusAction.php | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/src/Controller/OgAgent/StatusAction.php b/src/Controller/OgAgent/StatusAction.php index 7cc4061..2eae033 100644 --- a/src/Controller/OgAgent/StatusAction.php +++ b/src/Controller/OgAgent/StatusAction.php @@ -22,11 +22,25 @@ use Symfony\Contracts\HttpClient\HttpClientInterface; #[AsController] class StatusAction extends AbstractController { + protected readonly HttpClientInterface $httpClient; + + public function __construct( protected readonly EntityManagerInterface $entityManager, - protected readonly HttpClientInterface $httpClient - ) {} + ) + { + $this->httpClient = HttpClient::create([ + 'verify_peer' => false, + 'verify_host' => false, + ]); + } + /** + * @throws TransportExceptionInterface + * @throws ServerExceptionInterface + * @throws RedirectionExceptionInterface + * @throws ClientExceptionInterface + */ public function __invoke(Client $client): JsonResponse { if (!$client->getIp()) { @@ -34,10 +48,7 @@ class StatusAction extends AbstractController } try { - $response = $this->httpClient->request('POST', 'https://' . $client->getIp() . ':8000/ogAdmClient/status', [ - 'verify_peer' => false, - 'verify_host' => false, - ]); + $response = $this->httpClient->request('POST', 'https://' . $client->getIp() . ':8000/ogAdmClient/status'); $statusCode = $response->getStatusCode(); $client->setStatus($statusCode === Response::HTTP_OK ? 'active' : 'off'); -- 2.40.1 From 1db829d9ff3248c89b5030cd80fb91ebbac24ee4 Mon Sep 17 00:00:00 2001 From: Manuel Aranda Date: Wed, 23 Oct 2024 10:35:42 +0200 Subject: [PATCH 149/157] Test ogdClient --- src/Controller/OgAgent/StatusAction.php | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/src/Controller/OgAgent/StatusAction.php b/src/Controller/OgAgent/StatusAction.php index 2eae033..6497486 100644 --- a/src/Controller/OgAgent/StatusAction.php +++ b/src/Controller/OgAgent/StatusAction.php @@ -22,18 +22,10 @@ use Symfony\Contracts\HttpClient\HttpClientInterface; #[AsController] class StatusAction extends AbstractController { - protected readonly HttpClientInterface $httpClient; - - public function __construct( protected readonly EntityManagerInterface $entityManager, - ) - { - $this->httpClient = HttpClient::create([ - 'verify_peer' => false, - 'verify_host' => false, - ]); - } + protected readonly HttpClientInterface $httpClient + ) {} /** * @throws TransportExceptionInterface @@ -48,8 +40,11 @@ class StatusAction extends AbstractController } try { - $response = $this->httpClient->request('POST', 'https://' . $client->getIp() . ':8000/ogAdmClient/status'); - + $response = $this->httpClient->request('POST', 'https://' . $client->getIp() . ':8000/ogAdmClient/status', [ + 'verify_peer' => false, + 'verify_host' => false, + 'timeout' => 10, + ]); $statusCode = $response->getStatusCode(); $client->setStatus($statusCode === Response::HTTP_OK ? 'active' : 'off'); -- 2.40.1 From 38cb3e70983b2c222cd4f41d2ea2cbd418e64984 Mon Sep 17 00:00:00 2001 From: Manuel Aranda Date: Wed, 23 Oct 2024 10:42:13 +0200 Subject: [PATCH 150/157] Test ogdClient --- config/packages/framework.yaml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/config/packages/framework.yaml b/config/packages/framework.yaml index 980ee45..5b303be 100644 --- a/config/packages/framework.yaml +++ b/config/packages/framework.yaml @@ -18,6 +18,11 @@ framework: php_errors: log: true + http_client: + default_options: + verify_host: false + verify_peer: false + when@test: framework: test: true -- 2.40.1 From 4c76b9a71c8087ab38c3128ed99c13fa9b4c4c1e Mon Sep 17 00:00:00 2001 From: Manuel Aranda Date: Wed, 23 Oct 2024 10:44:14 +0200 Subject: [PATCH 151/157] Test ogdClient --- src/Controller/OgAgent/StatusAction.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Controller/OgAgent/StatusAction.php b/src/Controller/OgAgent/StatusAction.php index 6497486..4f813fa 100644 --- a/src/Controller/OgAgent/StatusAction.php +++ b/src/Controller/OgAgent/StatusAction.php @@ -44,6 +44,7 @@ class StatusAction extends AbstractController 'verify_peer' => false, 'verify_host' => false, 'timeout' => 10, + 'body' => [] ]); $statusCode = $response->getStatusCode(); $client->setStatus($statusCode === Response::HTTP_OK ? 'active' : 'off'); -- 2.40.1 From 354b18c068305de1ca448527016abe036fa3fedb Mon Sep 17 00:00:00 2001 From: Manuel Aranda Date: Wed, 23 Oct 2024 10:47:24 +0200 Subject: [PATCH 152/157] Test ogdClient --- src/Controller/OgAgent/StatusAction.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Controller/OgAgent/StatusAction.php b/src/Controller/OgAgent/StatusAction.php index 4f813fa..fd851cd 100644 --- a/src/Controller/OgAgent/StatusAction.php +++ b/src/Controller/OgAgent/StatusAction.php @@ -44,7 +44,7 @@ class StatusAction extends AbstractController 'verify_peer' => false, 'verify_host' => false, 'timeout' => 10, - 'body' => [] + 'body' => ['hola' => 'mundo'] ]); $statusCode = $response->getStatusCode(); $client->setStatus($statusCode === Response::HTTP_OK ? 'active' : 'off'); -- 2.40.1 From 8c30def21a23f51576d546a180b3d8f5ead21633 Mon Sep 17 00:00:00 2001 From: Manuel Aranda Date: Wed, 23 Oct 2024 10:49:55 +0200 Subject: [PATCH 153/157] Test ogdClient --- src/Controller/OgAgent/StatusAction.php | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/Controller/OgAgent/StatusAction.php b/src/Controller/OgAgent/StatusAction.php index fd851cd..d42ee0d 100644 --- a/src/Controller/OgAgent/StatusAction.php +++ b/src/Controller/OgAgent/StatusAction.php @@ -41,10 +41,13 @@ class StatusAction extends AbstractController try { $response = $this->httpClient->request('POST', 'https://' . $client->getIp() . ':8000/ogAdmClient/status', [ - 'verify_peer' => false, - 'verify_host' => false, - 'timeout' => 10, - 'body' => ['hola' => 'mundo'] + 'verify_peer' => false, // Desactivar verificación del certificado + 'verify_host' => false, // Desactivar verificación del nombre del host + 'timeout' => 10, // Tiempo máximo de espera + 'headers' => [ + 'Content-Type' => 'application/json', // Cabecera de tipo de contenido + ], + 'json' => ['hola' => 'mundo'], // Cuerpo de la solicitud como JSON ]); $statusCode = $response->getStatusCode(); $client->setStatus($statusCode === Response::HTTP_OK ? 'active' : 'off'); -- 2.40.1 From 6601a755a7e035fa0a9b18a5818f26ae43339da0 Mon Sep 17 00:00:00 2001 From: Manuel Aranda Date: Wed, 23 Oct 2024 10:54:21 +0200 Subject: [PATCH 154/157] Test ogdClient --- src/Controller/OgAgent/StatusAction.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Controller/OgAgent/StatusAction.php b/src/Controller/OgAgent/StatusAction.php index d42ee0d..d0ff631 100644 --- a/src/Controller/OgAgent/StatusAction.php +++ b/src/Controller/OgAgent/StatusAction.php @@ -47,7 +47,7 @@ class StatusAction extends AbstractController 'headers' => [ 'Content-Type' => 'application/json', // Cabecera de tipo de contenido ], - 'json' => ['hola' => 'mundo'], // Cuerpo de la solicitud como JSON + 'json' => [], // Cuerpo de la solicitud como JSON ]); $statusCode = $response->getStatusCode(); $client->setStatus($statusCode === Response::HTTP_OK ? 'active' : 'off'); -- 2.40.1 From d4f8acae81d8d56b8b68dc25939b5a5f302208d1 Mon Sep 17 00:00:00 2001 From: Manuel Aranda Date: Fri, 25 Oct 2024 09:58:31 +0200 Subject: [PATCH 155/157] Updated AgentController partition size --- src/Controller/OgAgent/OgAdmClientController.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Controller/OgAgent/OgAdmClientController.php b/src/Controller/OgAgent/OgAdmClientController.php index f533f9f..4ec47f4 100644 --- a/src/Controller/OgAgent/OgAdmClientController.php +++ b/src/Controller/OgAgent/OgAdmClientController.php @@ -56,7 +56,8 @@ class OgAdmClientController extends AbstractController $partitionEntity->setDiskNumber((int)$cfg['disk']); $partitionEntity->setPartitionNumber((int) $cfg['par']); $partitionEntity->setSize((int) $cfg['tam'] ?? null); - $partitionEntity->setMemoryUsage((int) $cfg['uso'] ?? null); + $partitionEntity->setMemoryUsage((int) $cfg['uso'] * 100 ?? null); + $partitionEntity->setFileSystem($cfg['fsi'] ?? null); $this->entityManager->persist($partitionEntity); } -- 2.40.1 From 6da4997f5144c4065ad9336c6022bcfd78d3883f Mon Sep 17 00:00:00 2001 From: Nicolas Arenas Date: Fri, 25 Oct 2024 10:09:26 +0200 Subject: [PATCH 156/157] Add email notification to pipeline --- Jenkinsfile | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/Jenkinsfile b/Jenkinsfile index c8fdee4..c7893df 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -82,10 +82,28 @@ pipeline { tools: [ PHPUnit(pattern: 'phpunit.xml') ] ) // Remove containers + sh "docker compose -f ${DOCKER_COMPOSE_FILE} down" sh "docker compose -f ${DOCKER_COMPOSE_FILE} rm -f" sh "docker rmi ${DOCKER_IDENTITY}" sh "docker rmi ${DOCKER_IDENTITY_NGINX}" + script { + def committerEmail = sh ( + script: "git show -s --pretty=%ae", + returnStdout: true + ).trim() + def buildResult = currentBuild.currentResult + mail to: committerEmail, + subject: "Opengnsys CI Build ${env.JOB_NAME} - ${env.BRANCH_NAME} - ${buildResult}", + body: """ +

Opengnsys CI Build ${JOB_NAME} - ${BRANCH_NAME} - ${buildResult}

+

Build Number: ${BUILD_NUMBER}

+

Build URL: ${BUILD_URL}

º + + Saludos cordiales, + Opengnsys CI + """ + } } } } -- 2.40.1 From 45fd79d2bc3c49a5b48bf63aeb80142fa6a8bab8 Mon Sep 17 00:00:00 2001 From: Manuel Aranda Date: Fri, 25 Oct 2024 10:57:00 +0200 Subject: [PATCH 157/157] Removed service. DTO input changes --- config/services/api_platform.yaml | 2 +- src/Dto/Input/PartitionInput.php | 5 +- .../OgBoot/PxeBootFile/PostService.php | 79 ------------------- 3 files changed, 5 insertions(+), 81 deletions(-) delete mode 100644 src/Service/OgBoot/PxeBootFile/PostService.php diff --git a/config/services/api_platform.yaml b/config/services/api_platform.yaml index 96d9926..b5f7b49 100644 --- a/config/services/api_platform.yaml +++ b/config/services/api_platform.yaml @@ -136,7 +136,7 @@ services: api_platform.filter.partition.search: parent: 'api_platform.doctrine.orm.search_filter' - arguments: [ { 'id': 'exact', 'usage': 'exact', 'diskNumber': 'exact' } ] + arguments: [ { 'id': 'exact', 'usage': 'exact', 'diskNumber': 'exact', 'client.id': 'exact' } ] tags: [ 'api_platform.filter' ] api_platform.filter.pxe_template.order: diff --git a/src/Dto/Input/PartitionInput.php b/src/Dto/Input/PartitionInput.php index 635149f..2e7d366 100644 --- a/src/Dto/Input/PartitionInput.php +++ b/src/Dto/Input/PartitionInput.php @@ -101,7 +101,10 @@ final class PartitionInput $partition->setOperativeSystem($this->operativeSystem->getEntity()); } $partition->setClient($this->client->getEntity()); - $partition->setMemoryUsage($this->memoryUsage * 100); + + if ($this->memoryUsage) { + $partition->setMemoryUsage($this->memoryUsage * 100); + } if ($this->image) { $partition->setImage($this->image->getEntity()); diff --git a/src/Service/OgBoot/PxeBootFile/PostService.php b/src/Service/OgBoot/PxeBootFile/PostService.php deleted file mode 100644 index 17ebe2e..0000000 --- a/src/Service/OgBoot/PxeBootFile/PostService.php +++ /dev/null @@ -1,79 +0,0 @@ - false, // Ignorar la verificación del certificado SSL - 'verify_host' => false, // Ignorar la verificación del nombre del host - ]); - - foreach ($bootFile->getClients() as $client) { - $data = [ - 'template_name' => 'pxe_default', - 'mac' => $client->getMac(), - 'lang' => 'es_ES.UTF_8', - 'ip' => $client->getIp(), - 'server_ip' => '92.168.2.1', - 'router' => $client->getOrganizationalUnit()->getNetworkSettings()->getRouter(), - 'netmask' => $client->getOrganizationalUnit()->getNetworkSettings() ? $client->getOrganizationalUnit()->getNetworkSettings()->getNetmask() : '255.255.255.0', - 'computer_name' => $client->getName(), - 'netiface' => $client->getNetiface(), - 'group' => $client->getOrganizationalUnit()->getName(), - 'ogrepo' => $client->getRepository() ? $client->getRepository()->getIpAddress() : '192.168.2.1', - 'oglive' => '127.0.0.1', - 'oglog' => '192.168.2.1', - 'ogshare' => '192.168.2.1', - 'oglivedir' => 'ogLive', - 'ogprof' => 'false', - 'hardprofile' => $client->getHardwareProfile() ? $client->getHardwareProfile()->getDescription() : 'default', - 'ogntp' => $client->getOrganizationalUnit()->getNetworkSettings()?->getNtp(), - 'ogdns' => $client->getOrganizationalUnit()->getNetworkSettings()?->getDns(), - 'ogProxy' => $client->getOrganizationalUnit()->getNetworkSettings()?->getProxy(), - 'ogunit' => '', - 'resolution' => '788' - ]; - - try { - $response = $httpClient->request('POST', $this->ogBootApiUrl.'/ogboot/v1/pxes', [ - 'headers' => [ - 'accept' => 'application/json', - 'Content-Type' => 'application/json', - ], - 'json' => $data - ]); - } catch (TransportExceptionInterface $e) { - return new JsonResponse( data: $e->getMessage(), status: Response::HTTP_INTERNAL_SERVER_ERROR); - } - - return json_decode($response->getContent(), true); - } - - return 1; - } -} \ No newline at end of file -- 2.40.1