diff --git a/src/Dto/Input/ClientInput.php b/src/Dto/Input/ClientInput.php index f929311..8c39d2d 100644 --- a/src/Dto/Input/ClientInput.php +++ b/src/Dto/Input/ClientInput.php @@ -3,6 +3,7 @@ namespace App\Dto\Input; use ApiPlatform\Metadata\ApiProperty; +use App\Dto\Output\HardwareProfileOutput; use App\Dto\Output\OrganizationalUnitOutput; use App\Entity\Client; use Symfony\Component\Serializer\Annotation\Groups; @@ -43,6 +44,11 @@ final class ClientInput #[ApiProperty(description: 'The organizational unit of the client')] public ?OrganizationalUnitOutput $organizationalUnit = null; + #[Assert\NotNull] + #[Groups(['client:write', 'client:patch'])] + #[ApiProperty(description: 'The hardware profile of the client')] + public ?HardwareProfileOutput $hardwareProfile = null; + public function __construct(?Client $client = null) { if (!$client) { @@ -53,6 +59,7 @@ final class ClientInput $this->serialNumber = $client->getSerialNumber(); $this->netiface = $client->getNetiface(); $this->organizationalUnit = new OrganizationalUnitOutput($client->getOrganizationalUnit()); + $this->hardwareProfile = new HardwareProfileOutput($client->getHardwareProfile()); $this->netDriver = $client->getNetDriver(); $this->mac = $client->getMac(); $this->ip = $client->getIp(); @@ -69,6 +76,7 @@ final class ClientInput $client->setSerialNumber($this->serialNumber); $client->setNetiface($this->netiface); $client->setOrganizationalUnit($this->organizationalUnit->getEntity()); + $client->setHardwareProfile($this->hardwareProfile->getEntity()); $client->setNetDriver($this->netDriver); $client->setMac($this->mac); $client->setIp($this->ip); diff --git a/src/Dto/Output/ClientOutput.php b/src/Dto/Output/ClientOutput.php index 5366342..6961c0b 100644 --- a/src/Dto/Output/ClientOutput.php +++ b/src/Dto/Output/ClientOutput.php @@ -21,6 +21,9 @@ final class ClientOutput extends AbstractOutput #[Groups(['client:read'])] public ?OrganizationalUnitOutput $organizationalUnit = null; + #[Groups(['client:read'])] + public ?HardwareProfileOutput $hardwareProfile = null; + #[Groups(['client:read'])] public \DateTime $createAt; @@ -34,7 +37,12 @@ final class ClientOutput extends AbstractOutput $this->name = $client->getName(); $this->serialNumber = $client->getSerialNumber(); $this->netiface = $client->getNetiface(); - $this->organizationalUnit = new OrganizationalUnitOutput($client->getOrganizationalUnit()); + if ($client->getOrganizationalUnit()) { + $this->organizationalUnit = new OrganizationalUnitOutput($client->getOrganizationalUnit()); + } + if($client->getHardwareProfile()) { + $this->hardwareProfile = new HardwareProfileOutput($client->getHardwareProfile()); + } $this->createAt = $client->getCreatedAt(); $this->createBy = $client->getCreatedBy(); } diff --git a/src/Dto/Output/OrganizationalUnitOutput.php b/src/Dto/Output/OrganizationalUnitOutput.php index 393c154..bb0ee2c 100644 --- a/src/Dto/Output/OrganizationalUnitOutput.php +++ b/src/Dto/Output/OrganizationalUnitOutput.php @@ -12,6 +12,9 @@ final class OrganizationalUnitOutput extends AbstractOutput #[Groups(['organizational-unit:read'])] public string $name; + #[Groups(['organizational-unit:read'])] + public ?string $comments = null; + #[Groups(['organizational-unit:read'])] public string $type; @@ -39,6 +42,7 @@ final class OrganizationalUnitOutput extends AbstractOutput parent::__construct($organizationalUnit); $this->name = $organizationalUnit->getName(); + $this->comments = $organizationalUnit->getComments(); $this->type = $organizationalUnit->getType(); $this->networkSettings = $organizationalUnit->getNetworkSettings() ? new NetworkSettingsOutput($organizationalUnit->getNetworkSettings()) : null; $this->clients = $organizationalUnit->getClients()->toArray(); diff --git a/src/Factory/ClientFactory.php b/src/Factory/ClientFactory.php new file mode 100644 index 0000000..a967161 --- /dev/null +++ b/src/Factory/ClientFactory.php @@ -0,0 +1,57 @@ + + */ +final class ClientFactory 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 class(): string + { + return Client::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(Client $client): void {}) + ; + } + + protected static function getClass(): string + { + return Client::class; + } +} diff --git a/src/Factory/HardwareProfileFactory.php b/src/Factory/HardwareProfileFactory.php new file mode 100644 index 0000000..eacb105 --- /dev/null +++ b/src/Factory/HardwareProfileFactory.php @@ -0,0 +1,57 @@ + + */ +final class HardwareProfileFactory 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 class(): string + { + return HardwareProfile::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(), + 'updatedAt' => self::faker()->dateTime(), + 'description' => self::faker()->text(255), + ]; + } + + /** + * @see https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#initialization + */ + protected function initialize(): self + { + return $this + // ->afterInstantiate(function(HardwareProfile $hardwareProfile): void {}) + ; + } + + protected static function getClass(): string + { + return HardwareProfile::class; + } +} diff --git a/tests/Functional/ClientTest.php b/tests/Functional/ClientTest.php new file mode 100644 index 0000000..eca7cbc --- /dev/null +++ b/tests/Functional/ClientTest.php @@ -0,0 +1,115 @@ + self::USER_ADMIN, 'roles'=> [UserGroupPermissions::ROLE_SUPER_ADMIN]]); + + ClientFactory::createMany(10); + + $this->createClientWithCredentials()->request('GET', '/clients'); + $this->assertResponseStatusCodeSame(Response::HTTP_OK); + $this->assertResponseHeaderSame('content-type', 'application/ld+json; charset=utf-8'); + $this->assertJsonContains([ + '@context' => '/contexts/Client', + '@id' => '/clients', + '@type' => 'hydra:Collection', + 'hydra:totalItems' => 10, + ]); + } + + /** + * @throws RedirectionExceptionInterface + * @throws DecodingExceptionInterface + * @throws ClientExceptionInterface + * @throws TransportExceptionInterface + * @throws ServerExceptionInterface + */ + public function testCreateClient(): void + { + UserFactory::createOne(['username' => self::USER_ADMIN, 'roles'=> [UserGroupPermissions::ROLE_SUPER_ADMIN]]); + + OrganizationalUnitFactory::createOne(['type' => OrganizationalUnitTypes::ORGANIZATIONAL_UNIT]); + $ouIri = $this->findIriBy(OrganizationalUnit::class, ['type' => OrganizationalUnitTypes::ORGANIZATIONAL_UNIT]); + + HardwareProfileFactory::createOne(['description' => self::HW_PROFILE]); + $hpIri = $this->findIriBy(HardwareProfile::class, ['description' => self::HW_PROFILE]); + + $this->createClientWithCredentials()->request('POST', '/clients',['json' => [ + 'name' => self::CLIENT_CREATE, + 'organizationalUnit' => $ouIri, + 'hardwareProfile' => $hpIri, + 'serialNumber' => '123abc', + ]]); + + $this->assertResponseStatusCodeSame(201); + $this->assertResponseHeaderSame('content-type', 'application/ld+json; charset=utf-8'); + $this->assertJsonContains([ + '@context' => '/contexts/ClientOutput', + '@type' => 'Client', + 'name' => self::CLIENT_CREATE, + 'serialNumber' => '123abc', + ]); + } + + /** + * @throws RedirectionExceptionInterface + * @throws DecodingExceptionInterface + * @throws ClientExceptionInterface + * @throws TransportExceptionInterface + * @throws ServerExceptionInterface + */ + public function testUpdateUserGroup(): void + { + UserFactory::createOne(['username' => self::USER_ADMIN, 'roles'=> [UserGroupPermissions::ROLE_SUPER_ADMIN]]); + + ClientFactory::createOne(['name' => self::CLIENT_UPDATE, 'serialNumber' => '123abc']); + $iri = $this->findIriBy(Client::class, ['name' => self::CLIENT_UPDATE]); + + $this->createClientWithCredentials()->request('PUT', $iri, ['json' => [ + 'name' => self::CLIENT_UPDATE, + 'serialNumber' => '987zyx' + ]]); + + $this->assertResponseIsSuccessful(); + $this->assertJsonContains([ + '@id' => $iri, + 'name' => self::CLIENT_UPDATE, + 'serialNumber' => '987zyx' + ]); + } +} \ No newline at end of file diff --git a/tests/Functional/OrganizationalUnitTest.php b/tests/Functional/OrganizationalUnitTest.php index 7bd6ca7..eaecd08 100644 --- a/tests/Functional/OrganizationalUnitTest.php +++ b/tests/Functional/OrganizationalUnitTest.php @@ -2,6 +2,8 @@ namespace Functional; +use App\Entity\OrganizationalUnit; +use App\Entity\UserGroup; use App\Factory\OrganizationalUnitFactory; use App\Factory\UserFactory; use App\Model\OrganizationalUnitTypes; @@ -16,8 +18,9 @@ use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface; class OrganizationalUnitTest extends AbstractTest { CONST string USER_ADMIN = 'ogadmin'; - CONST string ORGANIZATIONAL_UNIT_CREATE = 'test-organizational-unit-create'; + CONST string ORGANIZATIONAL_UNIT_UPDATE = 'test-organizational-unit-update'; + CONST string ORGANIZATIONAL_UNIT_DELETE = 'test-organizational-unit-delete'; /** * @throws RedirectionExceptionInterface @@ -145,4 +148,52 @@ class OrganizationalUnitTest extends AbstractTest 'type' => OrganizationalUnitTypes::CLIENTS_GROUP, ]); } + + /** + * @throws RedirectionExceptionInterface + * @throws DecodingExceptionInterface + * @throws ClientExceptionInterface + * @throws TransportExceptionInterface + * @throws ServerExceptionInterface + */ + public function testUpdateOrganizationalUnit(): void + { + UserFactory::createOne(['username' => self::USER_ADMIN, 'roles'=> [UserGroupPermissions::ROLE_SUPER_ADMIN]]); + + OrganizationalUnitFactory::createOne(['name' => self::ORGANIZATIONAL_UNIT_CREATE, 'type' => OrganizationalUnitTypes::ORGANIZATIONAL_UNIT]); + $iri = $this->findIriBy(OrganizationalUnit::class, ['name' => self::ORGANIZATIONAL_UNIT_CREATE]); + + $this->createClientWithCredentials()->request('PUT', $iri, ['json' => [ + 'name' => self::ORGANIZATIONAL_UNIT_UPDATE, + 'comments' => 'comments', + ]]); + + $this->assertResponseIsSuccessful(); + $this->assertJsonContains([ + '@id' => $iri, + 'name' => self::ORGANIZATIONAL_UNIT_UPDATE, + 'comments' => 'comments' + ]); + } + + /** + * @throws TransportExceptionInterface + * @throws ServerExceptionInterface + * @throws RedirectionExceptionInterface + * @throws DecodingExceptionInterface + * @throws ClientExceptionInterface + */ + public function testDeleteOrganizationalUnit(): void + { + UserFactory::createOne(['username' => self::USER_ADMIN, 'roles'=> [UserGroupPermissions::ROLE_SUPER_ADMIN]]); + + OrganizationalUnitFactory::createOne(['name' => self::ORGANIZATIONAL_UNIT_DELETE, 'type' => OrganizationalUnitTypes::ORGANIZATIONAL_UNIT]); + $iri = $this->findIriBy(OrganizationalUnit::class, ['name' => self::ORGANIZATIONAL_UNIT_DELETE]); + + $this->createClientWithCredentials()->request('DELETE', $iri); + $this->assertResponseStatusCodeSame(204); + $this->assertNull( + static::getContainer()->get('doctrine')->getRepository(UserGroup::class)->findOneBy(['name' => self::ORGANIZATIONAL_UNIT_DELETE]) + ); + } } \ No newline at end of file diff --git a/tests/Functional/UserGroupTest.php b/tests/Functional/UserGroupTest.php index 257360e..bc38bfe 100644 --- a/tests/Functional/UserGroupTest.php +++ b/tests/Functional/UserGroupTest.php @@ -2,7 +2,6 @@ namespace Functional; -use App\Entity\User; use App\Entity\UserGroup; use App\Factory\UserFactory; use App\Factory\UserGroupFactory; @@ -16,12 +15,11 @@ use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface; class UserGroupTest extends AbstractTest { - CONST USER_ADMIN = 'ogadmin'; - - CONST USER_GROUP_CREATE = 'test-user-group-create'; - CONST USER_GROUP_UPDATE = 'test-user-group-update'; - CONST USER_GROUP_DELETE = 'test-user-group-delete'; - CONST ROLE_ORGANIZATIONAL_UNIT_ADMIN = 'ROLE_ORGANIZATIONAL_UNIT_ADMIN'; + CONST string USER_ADMIN = 'ogadmin'; + CONST string USER_GROUP_CREATE = 'test-user-group-create'; + CONST string USER_GROUP_UPDATE = 'test-user-group-update'; + CONST string USER_GROUP_DELETE = 'test-user-group-delete'; + CONST string ROLE_ORGANIZATIONAL_UNIT_ADMIN = 'ROLE_ORGANIZATIONAL_UNIT_ADMIN'; /** * @throws RedirectionExceptionInterface