From bedc7d443dea3eb4d966bdf15a3818fab062668e Mon Sep 17 00:00:00 2001 From: Louis Charette Date: Thu, 10 Oct 2024 20:55:52 -0400 Subject: [PATCH] Replace group page with api --- .vscode/tasks.json | 4 +- .../{DashboardAction.php => DashboardApi.php} | 2 +- app/src/Controller/Group/GroupApi.php | 82 ++++++++++++ app/src/Controller/Group/GroupPageAction.php | 124 ------------------ app/src/Routes/DashboardRoutes.php | 4 +- app/src/Routes/GroupsRoute.php | 4 +- .../Dashboard/DashboardActionTest.php | 6 +- ...oupPageActionTest.php => GroupApiTest.php} | 33 ++++- 8 files changed, 121 insertions(+), 138 deletions(-) rename app/src/Controller/Dashboard/{DashboardAction.php => DashboardApi.php} (99%) create mode 100644 app/src/Controller/Group/GroupApi.php delete mode 100644 app/src/Controller/Group/GroupPageAction.php rename app/tests/Controller/Group/{GroupPageActionTest.php => GroupApiTest.php} (73%) diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 10a6b7b..3e8b032 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -12,8 +12,8 @@ } }, "command": "printf '\\33c\\e[3J' && vendor/bin/phpunit --stop-on-error --stop-on-failure", - // "command": "printf '\\33c\\e[3J' && vendor/bin/phpunit --filter UserRedirectedToDashboardTest --stop-on-error --stop-on-failure", - "problemMatcher": [], + // "command": "printf '\\33c\\e[3J' && vendor/bin/phpunit --filter GroupApiTest --stop-on-error --stop-on-failure", + "problemMatcher": [], "group": { "kind": "build", "isDefault": true diff --git a/app/src/Controller/Dashboard/DashboardAction.php b/app/src/Controller/Dashboard/DashboardApi.php similarity index 99% rename from app/src/Controller/Dashboard/DashboardAction.php rename to app/src/Controller/Dashboard/DashboardApi.php index 580ca80..cc9985c 100644 --- a/app/src/Controller/Dashboard/DashboardAction.php +++ b/app/src/Controller/Dashboard/DashboardApi.php @@ -29,7 +29,7 @@ /** * Api for /dashboard URL. Handles admin-related activities. */ -class DashboardAction +class DashboardApi { /** * Inject dependencies. diff --git a/app/src/Controller/Group/GroupApi.php b/app/src/Controller/Group/GroupApi.php new file mode 100644 index 0000000..61341b6 --- /dev/null +++ b/app/src/Controller/Group/GroupApi.php @@ -0,0 +1,82 @@ +validateAccess($group); + $payload = json_encode($group, JSON_THROW_ON_ERROR); + $response->getBody()->write($payload); + + return $response->withHeader('Content-Type', 'application/json'); + } + + /** + * Validate access to the page. + * + * @throws ForbiddenException + */ + protected function validateAccess(GroupInterface $group): void + { + // TODO : Change access to "api.group" or similar + if (!$this->authenticator->checkAccess('uri_group', [ + 'group' => $group, + ])) { + throw new ForbiddenException(); + } + + // Determine fields that currentUser is authorized to view + // TODO : Deprecated this properly, + // TODO : Handle view_group_field_own + /*$fieldNames = ['name', 'slug', 'icon', 'description']; + foreach ($fieldNames as $field) { + if (!$this->authenticator->checkAccess('view_group_field', [ + 'group' => $group, + 'property' => $field, + ])) { + throw new ForbiddenException(); + } + }*/ + } +} diff --git a/app/src/Controller/Group/GroupPageAction.php b/app/src/Controller/Group/GroupPageAction.php deleted file mode 100644 index 0458c33..0000000 --- a/app/src/Controller/Group/GroupPageAction.php +++ /dev/null @@ -1,124 +0,0 @@ -handle($group); - - // TODO : Turn into JSON API endpoint - return $this->view->render($response, $this->template, $payload); - } - - /** - * Handle the request and return the payload. - * - * @param GroupInterface $group - * - * @return mixed[] - */ - protected function handle(GroupInterface $group): array - { - // Access-controlled page - if (!$this->authenticator->checkAccess('uri_group', [ - 'group' => $group, - ])) { - throw new ForbiddenException(); - } - - // Determine fields that currentUser is authorized to view - $fieldNames = ['name', 'slug', 'icon', 'description']; - - // Fields to hide based on user's access. - $fields = [ - 'hidden' => [], - ]; - - // Determine which fields should be hidden - foreach ($fieldNames as $field) { - if (!$this->authenticator->checkAccess('view_group_field', [ - 'group' => $group, - 'property' => $field, - ])) { - $fields['hidden'][] = $field; - } - } - - // Determine buttons to display - $editButtons = [ - 'hidden' => [], - ]; - - if (!$this->authenticator->checkAccess('update_group_field', [ - 'group' => $group, - 'fields' => ['name', 'slug', 'icon', 'description'], - ])) { - $editButtons['hidden'][] = 'edit'; - } - - if (!$this->authenticator->checkAccess('delete_group', [ - 'group' => $group, - ])) { - $editButtons['hidden'][] = 'delete'; - } - - return [ - 'group' => $group, - 'fields' => $fields, - 'tools' => $editButtons, - 'delete_redirect' => $this->routeParser->urlFor('uri_groups'), - ]; - } -} diff --git a/app/src/Routes/DashboardRoutes.php b/app/src/Routes/DashboardRoutes.php index 5a60da7..45ec28c 100644 --- a/app/src/Routes/DashboardRoutes.php +++ b/app/src/Routes/DashboardRoutes.php @@ -16,7 +16,7 @@ use UserFrosting\Routes\RouteDefinitionInterface; use UserFrosting\Sprinkle\Account\Authenticate\AuthGuard; use UserFrosting\Sprinkle\Admin\Controller\Dashboard\CacheApiAction; -use UserFrosting\Sprinkle\Admin\Controller\Dashboard\DashboardAction; +use UserFrosting\Sprinkle\Admin\Controller\Dashboard\DashboardApi; use UserFrosting\Sprinkle\Core\Middlewares\NoCache; /* @@ -26,7 +26,7 @@ class DashboardRoutes implements RouteDefinitionInterface { public function register(App $app): void { - $app->get('/api/dashboard', DashboardAction::class) + $app->get('/api/dashboard', DashboardApi::class) ->setName('dashboard') ->add(NoCache::class); diff --git a/app/src/Routes/GroupsRoute.php b/app/src/Routes/GroupsRoute.php index 3436cf1..d8b4112 100644 --- a/app/src/Routes/GroupsRoute.php +++ b/app/src/Routes/GroupsRoute.php @@ -16,10 +16,10 @@ use Slim\Routing\RouteCollectorProxy; use UserFrosting\Routes\RouteDefinitionInterface; use UserFrosting\Sprinkle\Account\Authenticate\AuthGuard; +use UserFrosting\Sprinkle\Admin\Controller\Group\GroupApi; use UserFrosting\Sprinkle\Admin\Controller\Group\GroupCreateAction; use UserFrosting\Sprinkle\Admin\Controller\Group\GroupDeleteAction; use UserFrosting\Sprinkle\Admin\Controller\Group\GroupEditAction; -use UserFrosting\Sprinkle\Admin\Controller\Group\GroupPageAction; use UserFrosting\Sprinkle\Admin\Controller\Group\GroupsSprunjeAction as GroupsSprunje; use UserFrosting\Sprinkle\Admin\Controller\Group\GroupUsersSprunje; use UserFrosting\Sprinkle\Admin\Middlewares\GroupInjector; @@ -35,7 +35,7 @@ public function register(App $app): void $app->group('/api/groups', function (RouteCollectorProxy $group) { $group->get('', GroupsSprunje::class) ->setName('api_groups'); - $group->get('/g/{slug}', GroupPageAction::class) + $group->get('/g/{slug}', GroupApi::class) ->add(GroupInjector::class) ->setName('api_group'); $group->delete('/g/{slug}', GroupDeleteAction::class) diff --git a/app/tests/Controller/Dashboard/DashboardActionTest.php b/app/tests/Controller/Dashboard/DashboardActionTest.php index eb7f779..e071b22 100644 --- a/app/tests/Controller/Dashboard/DashboardActionTest.php +++ b/app/tests/Controller/Dashboard/DashboardActionTest.php @@ -17,7 +17,7 @@ use Mockery\Adapter\Phpunit\MockeryPHPUnitIntegration; use UserFrosting\Sprinkle\Account\Database\Models\User; use UserFrosting\Sprinkle\Account\Testing\WithTestUser; -use UserFrosting\Sprinkle\Admin\Controller\Dashboard\DashboardAction; +use UserFrosting\Sprinkle\Admin\Controller\Dashboard\DashboardApi; use UserFrosting\Sprinkle\Admin\Tests\AdminTestCase; use UserFrosting\Sprinkle\Core\Testing\RefreshDatabase; @@ -93,10 +93,10 @@ public function testPageDashboardWithPDOException(): void ->getMock(); // Create fake controller, inject mocked connection and set it in container - $controller = $this->ci->make(DashboardAction::class, [ + $controller = $this->ci->make(DashboardApi::class, [ 'dbConnection' => $connection, ]); - $this->ci->set(DashboardAction::class, $controller); + $this->ci->set(DashboardApi::class, $controller); /** @var User */ $user = User::factory()->create(); diff --git a/app/tests/Controller/Group/GroupPageActionTest.php b/app/tests/Controller/Group/GroupApiTest.php similarity index 73% rename from app/tests/Controller/Group/GroupPageActionTest.php rename to app/tests/Controller/Group/GroupApiTest.php index 9f8ed0f..20778aa 100644 --- a/app/tests/Controller/Group/GroupPageActionTest.php +++ b/app/tests/Controller/Group/GroupApiTest.php @@ -19,7 +19,7 @@ use UserFrosting\Sprinkle\Admin\Tests\AdminTestCase; use UserFrosting\Sprinkle\Core\Testing\RefreshDatabase; -class GroupPageActionTest extends AdminTestCase +class GroupApiTest extends AdminTestCase { use RefreshDatabase; use WithTestUser; @@ -34,7 +34,7 @@ public function setUp(): void $this->refreshDatabase(); } - public function testPageForGuestUser(): void + public function testForGuestUser(): void { // Create request with method and url and fetch response $request = $this->createJsonRequest('GET', '/api/groups/g/foo'); @@ -45,7 +45,7 @@ public function testPageForGuestUser(): void $this->assertResponseStatus(400, $response); } - public function testPageForForbiddenException(): void + public function testForForbiddenException(): void { /** @var User */ $user = User::factory()->create(); @@ -63,7 +63,7 @@ public function testPageForForbiddenException(): void $this->assertResponseStatus(403, $response); } - public function testPageForNotFound(): void + public function testForNotFound(): void { /** @var User */ $user = User::factory()->create(); @@ -77,4 +77,29 @@ public function testPageForNotFound(): void $this->assertJsonResponse('Group not found', $response, 'description'); $this->assertResponseStatus(404, $response); } + + public function testApi(): void + { + /** @var User */ + $user = User::factory()->create(); + $this->actAsUser($user, permissions: ['uri_group']); + + /** @var Group */ + $group = Group::factory()->create(); + + // Create request with method and url and fetch response + $request = $this->createJsonRequest('GET', '/api/groups/g/' . $group->slug); + $response = $this->handleRequest($request); + + // Assert response status & body + $this->assertJsonStructure([ + 'id', + 'slug', + 'name', + 'description', + 'icon', + 'created_at', + 'updated_at' + ], $response); + } }