diff --git a/README.md b/README.md index 0328408..4aeaad1 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,14 @@ ogCore es el servicio central de OpenGnsys, diseñado para proporcionar funcionalidades a través de una API RESTful. Esta herramienta utiliza tecnología PHP, aprovechando el framework Symfony y el ORM Doctrine para gestionar la base de datos. A continuación, se detallan los pasos necesarios para desplegar el proyecto en un entorno de desarrollo. +## Versiones y tecnologías utilizadas + +- PHP 8.3 +- Symfony 6.4 +- Doctrine 2.19 +- API Platform 3.2 +- MariaDB 10.11 + ## Requisitos Antes de comenzar, asegúrate de tener los siguientes requisitos: @@ -46,7 +54,7 @@ Comprobamos, que el contenedor de Nginx, tiene el puerto 8080 levantado correcta acceder a la siguiente URL: ```sh -http://127.0.0.1:8080/api/docs +http://127.0.0.1:8080/docs ``` Si todo ha ido bien, deberiamos ver la documentación de la API de ogCore. @@ -68,7 +76,7 @@ docker exec ogcore-php php bin/console app:load-default-user-groups Api Platform proporciona una interfaz de usuario para interactuar con la API de ogCore. Para acceder a la interfaz de usuario, accede a la siguiente URL: ```sh -http://127.0.0.1:8080/api/docs +http://127.0.0.1:8080/docs ``` Para poder autenticarte, necesitas un token JWT. Para obtenerlo, accedemos al endpoint de autenticación "auth/login": diff --git a/config/packages/security.yaml b/config/packages/security.yaml index 978777f..cf97c8d 100644 --- a/config/packages/security.yaml +++ b/config/packages/security.yaml @@ -27,7 +27,7 @@ security: access_control: - { path: ^/$, roles: PUBLIC_ACCESS } # Allows accessing the Swagger UI - - { path: ^/api/docs, roles: PUBLIC_ACCESS } # Allows accessing the Swagger UI docs + - { path: ^/docs, roles: PUBLIC_ACCESS } # Allows accessing the Swagger UI docs - { path: ^/auth/login, roles: PUBLIC_ACCESS } - { path: ^/auth/refresh, roles: PUBLIC_ACCESS } - { path: ^/, roles: IS_AUTHENTICATED_FULLY } diff --git a/config/routes/api_platform.yaml b/config/routes/api_platform.yaml index 38f11cb..350d2a8 100644 --- a/config/routes/api_platform.yaml +++ b/config/routes/api_platform.yaml @@ -1,4 +1,3 @@ api_platform: resource: . type: api_platform - prefix: /api diff --git a/migrations/Version20240529131520.php b/migrations/Version20240529131520.php new file mode 100644 index 0000000..b596cab --- /dev/null +++ b/migrations/Version20240529131520.php @@ -0,0 +1,34 @@ +addSql('ALTER TABLE organizational_unit DROP FOREIGN KEY FK_749AEB2D727ACA70'); + $this->addSql('ALTER TABLE organizational_unit ADD CONSTRAINT FK_749AEB2D727ACA70 FOREIGN KEY (parent_id) REFERENCES organizational_unit (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 organizational_unit DROP FOREIGN KEY FK_749AEB2D727ACA70'); + $this->addSql('ALTER TABLE organizational_unit ADD CONSTRAINT FK_749AEB2D727ACA70 FOREIGN KEY (parent_id) REFERENCES organizational_unit (id)'); + } +} diff --git a/src/DataFixtures/AppFixtures.php b/src/DataFixtures/AppFixtures.php index 8feb58e..3f0d310 100644 --- a/src/DataFixtures/AppFixtures.php +++ b/src/DataFixtures/AppFixtures.php @@ -2,6 +2,8 @@ namespace App\DataFixtures; +use App\Entity\OrganizationalUnit; +use App\Factory\OrganizationalUnitFactory; use App\Factory\UserFactory; use Doctrine\Bundle\FixturesBundle\Fixture; use Doctrine\Persistence\ObjectManager; @@ -10,12 +12,22 @@ class AppFixtures extends Fixture { CONST ADMIN_USER = 'ogadmin'; - /** * @throws \Exception */ public function load(ObjectManager $manager): void { UserFactory::createOne(['username' => self::ADMIN_USER]); + $rootUnit = OrganizationalUnitFactory::createOne(['name' => 'Centro de Computación', 'parent' => null]); + + $roomUnit = OrganizationalUnitFactory::createOne([ + 'name' => 'Aula 1', + 'parent' => $rootUnit + ]); + + OrganizationalUnitFactory::createOne([ + 'name' => 'Aula 2', + 'parent' => $roomUnit + ]); } } diff --git a/src/Dto/Input/UserInput.php b/src/Dto/Input/UserInput.php index 79394c9..97b1230 100644 --- a/src/Dto/Input/UserInput.php +++ b/src/Dto/Input/UserInput.php @@ -2,6 +2,8 @@ namespace App\Dto\Input; +use ApiPlatform\Metadata\ApiProperty; +use App\Dto\Output\UserGroupOutput; use App\Entity\OrganizationalUnit; use App\Entity\User; use App\Entity\UserGroup; @@ -14,13 +16,11 @@ final class UserInput #[Groups(['user:write'])] public ?string $username = null; - #[Groups(['user:write'])] - public array $roles = []; - /** * @var OrganizationalUnit[] */ #[Groups(['user:write'])] + #[ApiProperty(readableLink: false, writableLink: false)] public array $allowedOrganizationalUnits = []; #[Assert\NotBlank(groups: ['user:post'])] @@ -36,6 +36,7 @@ final class UserInput * @var UserGroup[] */ #[Groups(['user:write'])] + #[ApiProperty(readableLink: false, writableLink: false)] public array $userGroups = []; public function __construct(?User $user = null) @@ -45,7 +46,6 @@ final class UserInput } $this->username = $user->getUsername(); - $this->roles = $user->getRoles(); $this->enabled= $user->isEnabled(); $this->userGroups = $user->getUserGroups()->toArray(); $this->allowedOrganizationalUnits = $user->getAllowedOrganizationalUnits()->toArray(); @@ -58,7 +58,6 @@ final class UserInput } $user->setUsername($this->username); - $user->setRoles($this->roles); $user->setEnabled($this->enabled); foreach ($this->userGroups as $userGroup) { diff --git a/src/Entity/OrganizationalUnit.php b/src/Entity/OrganizationalUnit.php index 014c847..6e58c7f 100644 --- a/src/Entity/OrganizationalUnit.php +++ b/src/Entity/OrganizationalUnit.php @@ -35,6 +35,7 @@ class OrganizationalUnit extends AbstractEntity #[Gedmo\TreeParent] #[ORM\ManyToOne(targetEntity: self::class, inversedBy: 'organizationalUnits')] + #[ORM\JoinColumn( onDelete: 'SET NULL')] private ?self $parent = null; /** diff --git a/src/Factory/OrganizationalUnitFactory.php b/src/Factory/OrganizationalUnitFactory.php new file mode 100644 index 0000000..c4be743 --- /dev/null +++ b/src/Factory/OrganizationalUnitFactory.php @@ -0,0 +1,54 @@ + + */ +final class OrganizationalUnitFactory 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), + 'updatedAt' => self::faker()->dateTime(), + ]; + } + + /** + * @see https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#initialization + */ + protected function initialize(): self + { + return $this + // ->afterInstantiate(function(OrganizationalUnit $organizationalUnit): void {}) + ; + } + + protected static function getClass(): string + { + return OrganizationalUnit::class; + } +} diff --git a/tests/Functional/UserGroupTest.php b/tests/Functional/UserGroupTest.php index 5806634..0e5572c 100644 --- a/tests/Functional/UserGroupTest.php +++ b/tests/Functional/UserGroupTest.php @@ -38,12 +38,12 @@ class UserGroupTest extends AbstractTest UserGroupFactory::createOne(['name' => 'Operador de aulas', 'permissions' => ['ROLE_ORGANIZATIONAL_UNIT_OPERATOR'], 'enabled' => true]); UserGroupFactory::createOne(['name' => 'Usuario', 'permissions' => ['ROLE_USER'], 'enabled' => true]); - $this->createClientWithCredentials()->request('GET', '/api/user-groups'); + $this->createClientWithCredentials()->request('GET', '/user-groups'); $this->assertResponseStatusCodeSame(Response::HTTP_OK); $this->assertResponseHeaderSame('content-type', 'application/ld+json; charset=utf-8'); $this->assertJsonContains([ - '@context' => '/api/contexts/UserGroup', - '@id' => '/api/user-groups', + '@context' => '/contexts/UserGroup', + '@id' => '/user-groups', '@type' => 'hydra:Collection', 'hydra:totalItems' => 4, ]); @@ -59,7 +59,7 @@ class UserGroupTest extends AbstractTest public function testCreateUserGroup(): void { UserFactory::createOne(['username' => self::USER_ADMIN]); - $this->createClientWithCredentials()->request('POST', '/api/user-groups',['json' => [ + $this->createClientWithCredentials()->request('POST', '/user-groups',['json' => [ 'name' => self::USER_GROUP_CREATE, 'enabled' => true, ]]); @@ -67,7 +67,7 @@ class UserGroupTest extends AbstractTest $this->assertResponseStatusCodeSame(201); $this->assertResponseHeaderSame('content-type', 'application/ld+json; charset=utf-8'); $this->assertJsonContains([ - '@context' => '/api/contexts/UserGroupOutput', + '@context' => '/contexts/UserGroupOutput', '@type' => 'UserGroup', 'name' => self::USER_GROUP_CREATE, 'enabled' => true, diff --git a/tests/Functional/UserTest.php b/tests/Functional/UserTest.php index b0e9cc4..a63d626 100644 --- a/tests/Functional/UserTest.php +++ b/tests/Functional/UserTest.php @@ -30,12 +30,12 @@ class UserTest extends AbstractTest UserFactory::createOne(['username' => self::USER_ADMIN]); UserFactory::createMany(10); - $this->createClientWithCredentials()->request('GET', '/api/users'); + $this->createClientWithCredentials()->request('GET', '/users'); $this->assertResponseStatusCodeSame(Response::HTTP_OK); $this->assertResponseHeaderSame('content-type', 'application/ld+json; charset=utf-8'); $this->assertJsonContains([ - '@context' => '/api/contexts/User', - '@id' => '/api/users', + '@context' => '/contexts/User', + '@id' => '/users', '@type' => 'hydra:Collection', 'hydra:totalItems' => 11, ]); @@ -51,7 +51,7 @@ class UserTest extends AbstractTest public function testCreateUser(): void { UserFactory::createOne(['username' => self::USER_ADMIN]); - $this->createClientWithCredentials()->request('POST', '/api/users',['json' => [ + $this->createClientWithCredentials()->request('POST', '/users',['json' => [ 'username' => self::USER_CREATE, 'password' => '12345678', 'enabled' => true, @@ -60,7 +60,7 @@ class UserTest extends AbstractTest $this->assertResponseStatusCodeSame(201); $this->assertResponseHeaderSame('content-type', 'application/ld+json; charset=utf-8'); $this->assertJsonContains([ - '@context' => '/api/contexts/UserOutput', + '@context' => '/contexts/UserOutput', '@type' => 'User', 'username' => self::USER_CREATE, 'enabled' => true,