diff --git a/config/api_platform/OperativeSystem.yaml b/config/api_platform/OperativeSystem.yaml new file mode 100644 index 0000000..737c8d0 --- /dev/null +++ b/config/api_platform/OperativeSystem.yaml @@ -0,0 +1,31 @@ +resources: + App\Entity\OperativeSystem: + processor: App\State\Processor\OperativeSystemProcessor + input: App\Dto\Input\OperativeSystemInput + output: App\Dto\Output\OperativeSystemOutput + normalization_context: + groups: ['default', 'operative-system:read'] + denormalization_context: + groups: ['operative-system:write'] + operations: + ApiPlatform\Metadata\GetCollection: + provider: App\State\Provider\OperativeSystemProvider + filters: + - 'api_platform.filter.operative-system.order' + - 'api_platform.filter.operative-system.search' + + ApiPlatform\Metadata\Get: + provider: App\State\Provider\OperativeSystemProvider + ApiPlatform\Metadata\Put: + provider: App\State\Provider\OperativeSystemProvider + ApiPlatform\Metadata\Patch: + provider: App\State\Provider\OperativeSystemProvider + ApiPlatform\Metadata\Post: ~ + ApiPlatform\Metadata\Delete: ~ + +properties: + App\Entity\OperativeSystem: + id: + identifier: false + uuid: + identifier: true \ No newline at end of file diff --git a/config/services.yaml b/config/services.yaml index d495931..dd0d241 100644 --- a/config/services.yaml +++ b/config/services.yaml @@ -66,6 +66,11 @@ services: $itemProvider: '@api_platform.doctrine.orm.state.item_provider' App\State\Provider\NetworkSettingsProvider: + bind: + $collectionProvider: '@api_platform.doctrine.orm.state.collection_provider' + $itemProvider: '@api_platform.doctrine.orm.state.item_provider' + + App\State\Provider\OperativeSystemProvider: 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/Version20240618083013.php b/migrations/Version20240618083013.php new file mode 100644 index 0000000..df541b8 --- /dev/null +++ b/migrations/Version20240618083013.php @@ -0,0 +1,41 @@ +addSql('CREATE TABLE hardware_type (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_2AA5A113D17F50A6 (uuid), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB'); + $this->addSql('CREATE TABLE operative_system (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_E9C44095D17F50A6 (uuid), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB'); + $this->addSql('CREATE TABLE operative_system_type (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_4A13A156D17F50A6 (uuid), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB'); + $this->addSql('ALTER TABLE hardware ADD type_id INT DEFAULT NULL, DROP type'); + $this->addSql('ALTER TABLE hardware ADD CONSTRAINT FK_FE99E9E0C54C8C93 FOREIGN KEY (type_id) REFERENCES hardware_type (id)'); + $this->addSql('CREATE INDEX IDX_FE99E9E0C54C8C93 ON hardware (type_id)'); + } + + public function down(Schema $schema): void + { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql('ALTER TABLE hardware DROP FOREIGN KEY FK_FE99E9E0C54C8C93'); + $this->addSql('DROP TABLE hardware_type'); + $this->addSql('DROP TABLE operative_system'); + $this->addSql('DROP TABLE operative_system_type'); + $this->addSql('DROP INDEX IDX_FE99E9E0C54C8C93 ON hardware'); + $this->addSql('ALTER TABLE hardware ADD type VARCHAR(255) DEFAULT NULL, DROP type_id'); + } +} diff --git a/src/Dto/Input/OperativeSystemInput.php b/src/Dto/Input/OperativeSystemInput.php new file mode 100644 index 0000000..e08416d --- /dev/null +++ b/src/Dto/Input/OperativeSystemInput.php @@ -0,0 +1,35 @@ +name = $operativeSystem->getName(); + } + + public function createOrUpdateEntity(?OperativeSystem $operativeSystem = null): OperativeSystem + { + if (!$operativeSystem) { + $operativeSystem = new OperativeSystem(); + } + + $operativeSystem->setName($this->name); + + return $operativeSystem; + } + +} \ No newline at end of file diff --git a/src/Dto/Output/OperativeSystemOutput.php b/src/Dto/Output/OperativeSystemOutput.php new file mode 100644 index 0000000..8e9002a --- /dev/null +++ b/src/Dto/Output/OperativeSystemOutput.php @@ -0,0 +1,21 @@ +name = $operativeSystem->getName(); + } +} \ No newline at end of file diff --git a/src/Dto/Output/PartitionOutput.php b/src/Dto/Output/PartitionOutput.php index cd5a0d6..483aab7 100644 --- a/src/Dto/Output/PartitionOutput.php +++ b/src/Dto/Output/PartitionOutput.php @@ -46,5 +46,4 @@ class PartitionOutput extends AbstractOutput $this->osName = $partition->getOsName(); $this->memoryUsage = $partition->getMemoryUsage() / 100; } - } \ No newline at end of file diff --git a/src/Entity/Hardware.php b/src/Entity/Hardware.php index 5a1ba19..50545a2 100644 --- a/src/Entity/Hardware.php +++ b/src/Entity/Hardware.php @@ -15,15 +15,15 @@ class Hardware extends AbstractEntity #[ORM\Column(length: 255, nullable: true)] private ?string $description = null; - #[ORM\Column(length: 255, nullable: true)] - private ?string $type = null; - /** * @var Collection */ #[ORM\ManyToMany(targetEntity: HardwareProfile::class, mappedBy: 'hardwareCollection')] private Collection $hardwareProfiles; + #[ORM\ManyToOne] + private ?HardwareType $type = null; + public function __construct() { parent::__Construct(); @@ -48,18 +48,6 @@ class Hardware extends AbstractEntity return $this; } - public function getType(): ?string - { - return $this->type; - } - - public function setType(?string $type): static - { - $this->type = $type; - - return $this; - } - /** * @return Collection */ @@ -86,4 +74,16 @@ class Hardware extends AbstractEntity return $this; } + + public function getType(): ?HardwareType + { + return $this->type; + } + + public function setType(?HardwareType $type): static + { + $this->type = $type; + + return $this; + } } diff --git a/src/Entity/HardwareType.php b/src/Entity/HardwareType.php new file mode 100644 index 0000000..e0c2260 --- /dev/null +++ b/src/Entity/HardwareType.php @@ -0,0 +1,12 @@ + + */ +final class OperativeSystemFactory extends ModelFactory +{ + /** + * @see https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#factories-as-services + */ + public function __construct() + { + parent::__construct(); + } + + public static function class(): string + { + return OperativeSystem::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(), + '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(OperativeSystem $operativeSystem): void {}) + ; + } + + protected static function getClass(): string + { + return OperativeSystem::class; + } +} diff --git a/src/Repository/HardwareTypeRepository.php b/src/Repository/HardwareTypeRepository.php new file mode 100644 index 0000000..004e63d --- /dev/null +++ b/src/Repository/HardwareTypeRepository.php @@ -0,0 +1,18 @@ + + */ +class HardwareTypeRepository extends AbstractRepository +{ + public function __construct(ManagerRegistry $registry) + { + parent::__construct($registry, HardwareType::class); + } +} diff --git a/src/Repository/OperativeSystemRepository.php b/src/Repository/OperativeSystemRepository.php new file mode 100644 index 0000000..793093c --- /dev/null +++ b/src/Repository/OperativeSystemRepository.php @@ -0,0 +1,18 @@ + + */ +class OperativeSystemRepository extends AbstractRepository +{ + public function __construct(ManagerRegistry $registry) + { + parent::__construct($registry, OperativeSystem::class); + } +} diff --git a/src/Repository/OperativeSystemTypeRepository.php b/src/Repository/OperativeSystemTypeRepository.php new file mode 100644 index 0000000..e776e55 --- /dev/null +++ b/src/Repository/OperativeSystemTypeRepository.php @@ -0,0 +1,18 @@ + + */ +class OperativeSystemTypeRepository extends AbstractRepository +{ + public function __construct(ManagerRegistry $registry) + { + parent::__construct($registry, OperativeSystemType::class); + } +} diff --git a/src/State/Processor/OperativeSystemProcessor.php b/src/State/Processor/OperativeSystemProcessor.php new file mode 100644 index 0000000..dadd449 --- /dev/null +++ b/src/State/Processor/OperativeSystemProcessor.php @@ -0,0 +1,74 @@ +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 = []): OperativeSystemOutput + { + if (!($data instanceof OperativeSystemInput)) { + throw new \Exception(sprintf('data is not instance of %s', MenuInput::class)); + } + + $entity = null; + if (isset($uriVariables['uuid'])) { + $entity = $this->operativeSystemRepository->findOneByUuid($uriVariables['uuid']); + } + + $operativeSystem = $data->createOrUpdateEntity($entity); + $this->validator->validate($operativeSystem); + $this->operativeSystemRepository->save($operativeSystem); + + return new OperativeSystemOutput($operativeSystem); + } + + private function processDelete($data, Operation $operation, array $uriVariables = [], array $context = []): null + { + $user = $this->operativeSystemRepository->findOneByUuid($uriVariables['uuid']); + $this->operativeSystemRepository->delete($user); + + return null; + } +} diff --git a/src/State/Provider/OperativeSystemProvider.php b/src/State/Provider/OperativeSystemProvider.php new file mode 100644 index 0000000..6b0535c --- /dev/null +++ b/src/State/Provider/OperativeSystemProvider.php @@ -0,0 +1,76 @@ +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 OperativeSystemOutput($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('Operative system not found'); + } + + return new OperativeSystemOutput($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 OperativeSystemInput($item) : null; + } + + return new OperativeSystemInput(); + } +} diff --git a/tests/Functional/ClientTest.php b/tests/Functional/ClientTest.php index e85ffb6..f28429a 100644 --- a/tests/Functional/ClientTest.php +++ b/tests/Functional/ClientTest.php @@ -140,6 +140,4 @@ class ClientTest extends AbstractTest static::getContainer()->get('doctrine')->getRepository(Client::class)->findOneBy(['name' => self::CLIENT_DELETE]) ); } - - } \ No newline at end of file diff --git a/tests/Functional/OperativeSystemTest.php b/tests/Functional/OperativeSystemTest.php new file mode 100644 index 0000000..0a0a3f8 --- /dev/null +++ b/tests/Functional/OperativeSystemTest.php @@ -0,0 +1,117 @@ + self::USER_ADMIN, 'roles'=> [UserGroupPermissions::ROLE_SUPER_ADMIN]]); + + OperativeSystemFactory::createMany(10); + + $this->createClientWithCredentials()->request('GET', '/operative-systems'); + $this->assertResponseStatusCodeSame(Response::HTTP_OK); + $this->assertResponseHeaderSame('content-type', 'application/ld+json; charset=utf-8'); + $this->assertJsonContains([ + '@context' => '/contexts/OperativeSystem', + '@id' => '/operative-systems', + '@type' => 'hydra:Collection', + 'hydra:totalItems' => 10, + ]); + } + + /** + * @throws RedirectionExceptionInterface + * @throws DecodingExceptionInterface + * @throws ClientExceptionInterface + * @throws TransportExceptionInterface + * @throws ServerExceptionInterface + */ + public function testCreateOperativeSystem(): void + { + UserFactory::createOne(['username' => self::USER_ADMIN, 'roles'=> [UserGroupPermissions::ROLE_SUPER_ADMIN]]); + + $this->createClientWithCredentials()->request('POST', '/operative-systems',['json' => [ + 'name' => self::OS_CREATE, + ]]); + + $this->assertResponseStatusCodeSame(201); + $this->assertResponseHeaderSame('content-type', 'application/ld+json; charset=utf-8'); + $this->assertJsonContains([ + '@context' => '/contexts/OperativeSystemOutput', + '@type' => 'OperativeSystem', + 'name' => self::OS_CREATE + ]); + } + + /** + * @throws RedirectionExceptionInterface + * @throws DecodingExceptionInterface + * @throws ClientExceptionInterface + * @throws TransportExceptionInterface + * @throws ServerExceptionInterface + */ + public function testUpdateOperativeSystem(): void + { + UserFactory::createOne(['username' => self::USER_ADMIN, 'roles'=> [UserGroupPermissions::ROLE_SUPER_ADMIN]]); + + OperativeSystemFactory::createOne(['name' => self::OS_CREATE]); + $iri = $this->findIriBy(OperativeSystem::class, ['name' => self::OS_CREATE]); + + $this->createClientWithCredentials()->request('PUT', $iri, ['json' => [ + 'name' => self::OS_UPDATE, + ]]); + + $this->assertResponseIsSuccessful(); + $this->assertJsonContains([ + '@id' => $iri, + 'name' => self::OS_UPDATE, + ]); + } + + /** + * @throws TransportExceptionInterface + * @throws ServerExceptionInterface + * @throws RedirectionExceptionInterface + * @throws DecodingExceptionInterface + * @throws ClientExceptionInterface + */ + public function testDeleteOperativeSystem(): void + { + UserFactory::createOne(['username' => self::USER_ADMIN, 'roles'=> [UserGroupPermissions::ROLE_SUPER_ADMIN]]); + + OperativeSystemFactory::createOne(['name' => self::OS_DELETE]); + $iri = $this->findIriBy(OperativeSystem::class, ['name' => self::OS_DELETE]); + + $this->createClientWithCredentials()->request('DELETE', $iri); + $this->assertResponseStatusCodeSame(204); + $this->assertNull( + static::getContainer()->get('doctrine')->getRepository(OperativeSystem::class)->findOneBy(['name' => self::OS_DELETE]) + ); + } +} \ No newline at end of file diff --git a/tests/Functional/PartitionTest.php b/tests/Functional/PartitionTest.php index b4a8a53..0265dc6 100644 --- a/tests/Functional/PartitionTest.php +++ b/tests/Functional/PartitionTest.php @@ -2,9 +2,16 @@ namespace Functional; +use App\Entity\Client; +use App\Entity\Menu; +use App\Entity\Partition; +use App\Factory\ClientFactory; +use App\Factory\HardwareProfileFactory; use App\Factory\MenuFactory; +use App\Factory\OrganizationalUnitFactory; use App\Factory\PartitionFactory; use App\Factory\UserFactory; +use App\Model\OrganizationalUnitTypes; use App\Model\UserGroupPermissions; use Symfony\Component\HttpFoundation\Response; use Symfony\Contracts\HttpClient\Exception\ClientExceptionInterface; @@ -16,9 +23,9 @@ use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface; class PartitionTest extends AbstractTest { CONST string USER_ADMIN = 'ogadmin'; - CONST string PARTITION_CREATE = 'test-partition-create'; - CONST string PARTITION_UPDATE = 'test-partition-update'; - CONST string PARTITION_DELETE = 'test-partition-delete'; + CONST string CLIENT_CREATE = 'test-client-create'; + + const string HW_PROFILE = 'HW Test'; /** * @throws RedirectionExceptionInterface @@ -31,7 +38,12 @@ class PartitionTest extends AbstractTest { UserFactory::createOne(['username' => self::USER_ADMIN, 'roles'=> [UserGroupPermissions::ROLE_SUPER_ADMIN]]); - PartitionFactory::createMany(10); + $ou = OrganizationalUnitFactory::createOne(['type' => OrganizationalUnitTypes::ORGANIZATIONAL_UNIT]); + $hp = HardwareProfileFactory::createOne(['description' => self::HW_PROFILE]); + + $client = ClientFactory::createOne(['name' => self::CLIENT_CREATE, 'serialNumber' => '123abc', 'organizationalUnit' => $ou, 'hardwareProfile' => $hp]); + + PartitionFactory::createMany(10, ['client' => $client]); $this->createClientWithCredentials()->request('GET', '/partitions'); $this->assertResponseStatusCodeSame(Response::HTTP_OK); @@ -43,4 +55,40 @@ class PartitionTest extends AbstractTest 'hydra:totalItems' => 10, ]); } + + /** + * @throws RedirectionExceptionInterface + * @throws DecodingExceptionInterface + * @throws ClientExceptionInterface + * @throws TransportExceptionInterface + * @throws ServerExceptionInterface + */ + public function testCreatePartition(): void + { + UserFactory::createOne(['username' => self::USER_ADMIN, 'roles'=> [UserGroupPermissions::ROLE_SUPER_ADMIN]]); + + $ou = OrganizationalUnitFactory::createOne(['type' => OrganizationalUnitTypes::ORGANIZATIONAL_UNIT]); + $hp = HardwareProfileFactory::createOne(['description' => self::HW_PROFILE]); + + ClientFactory::createOne(['name' => self::CLIENT_CREATE, 'serialNumber' => '123abc', 'organizationalUnit' => $ou, 'hardwareProfile' => $hp]); + $iri = $this->findIriBy(Client::class, ['name' => self::CLIENT_CREATE]); + + $this->createClientWithCredentials()->request('POST', '/partitions',['json' => [ + 'size' => 100, + 'osName' => 'Ubuntu', + 'client' => $iri, + 'memoryUsage' => 100 + ]]); + + $this->assertResponseStatusCodeSame(201); + $this->assertResponseHeaderSame('content-type', 'application/ld+json; charset=utf-8'); + $this->assertJsonContains([ + '@context' => '/contexts/PartitionOutput', + '@type' => 'Partition', + 'size' => 100, + 'osName' => 'Ubuntu', + 'memoryUsage' => 100 + ]); + } + } \ No newline at end of file