Skip to content

Commit 7de36c3

Browse files
author
Martin Krulis
committed
Adding an endpoint to retrieve a list of group's shadow assignments.
1 parent 0650d28 commit 7de36c3

File tree

4 files changed

+89
-13
lines changed

4 files changed

+89
-13
lines changed

app/V1Module/presenters/GroupsPresenter.php

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
use App\Exceptions\NotFoundException;
77
use App\Helpers\Localizations;
88
use App\Model\Entity\Assignment;
9+
use App\Model\Entity\ShadowAssignment;
910
use App\Model\Entity\Exercise;
1011
use App\Model\Entity\Group;
1112
use App\Model\Entity\Instance;
@@ -18,9 +19,11 @@
1819
use App\Exceptions\ForbiddenRequestException;
1920
use App\Model\View\ExerciseViewFactory;
2021
use App\Model\View\AssignmentViewFactory;
22+
use App\Model\View\ShadowAssignmentViewFactory;
2123
use App\Model\View\GroupViewFactory;
2224
use App\Model\View\UserViewFactory;
2325
use App\Security\ACL\IAssignmentPermissions;
26+
use App\Security\ACL\IShadowAssignmentPermissions;
2427
use App\Security\ACL\IExercisePermissions;
2528
use App\Security\ACL\IGroupPermissions;
2629
use App\Security\Identity;
@@ -77,6 +80,12 @@ class GroupsPresenter extends BasePresenter {
7780
*/
7881
public $assignmentAcl;
7982

83+
/**
84+
* @var IShadowAssignmentPermissions
85+
* @inject
86+
*/
87+
public $shadowAssignmentAcl;
88+
8089
/**
8190
* @var Loader
8291
* @inject
@@ -113,6 +122,12 @@ class GroupsPresenter extends BasePresenter {
113122
*/
114123
public $assignmentViewFactory;
115124

125+
/**
126+
* @var ShadowAssignmentViewFactory
127+
* @inject
128+
*/
129+
public $shadowAssignmentViewFactory;
130+
116131
public function checkDefault() {
117132
if (!$this->groupAcl->canViewAll()) {
118133
throw new ForbiddenRequestException();
@@ -475,6 +490,32 @@ public function actionAssignments(string $id) {
475490
})->getValues());
476491
}
477492

493+
public function checkShadowAssignments(string $id) {
494+
/** @var Group $group */
495+
$group = $this->groups->findOrThrow($id);
496+
497+
if (!$this->groupAcl->canViewAssignments($group)) {
498+
throw new ForbiddenRequestException();
499+
}
500+
}
501+
502+
/**
503+
* Get all shadow assignments for a group
504+
* @GET
505+
* @param string $id Identifier of the group
506+
*/
507+
public function actionShadowAssignments(string $id) {
508+
/** @var Group $group */
509+
$group = $this->groups->findOrThrow($id);
510+
511+
$assignments = $group->getShadowAssignments();
512+
$this->sendSuccessResponse($assignments->filter(function (ShadowAssignment $assignment) {
513+
return $this->shadowAssignmentAcl->canViewDetail($assignment);
514+
})->map(function (ShadowAssignment $assignment) {
515+
return $this->shadowAssignmentViewFactory->getAssignment($assignment);
516+
})->getValues());
517+
}
518+
478519
public function checkExercises(string $id) {
479520
$group = $this->groups->findOrThrow($id);
480521

app/V1Module/router/RouterFactory.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,7 @@ private static function createGroupsRoutes(string $prefix): RouteList {
212212
$router[] = new DeleteRoute("$prefix/<id>/admin/<userId>", "Groups:removeAdmin");
213213

214214
$router[] = new GetRoute("$prefix/<id>/assignments", "Groups:assignments");
215+
$router[] = new GetRoute("$prefix/<id>/shadow-assignments", "Groups:shadowAssignments");
215216
$router[] = new GetRoute("$prefix/<id>/exercises", "Groups:exercises");
216217

217218
return $router;

tests/Presenters/GroupsPresenter.phpt

Lines changed: 24 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -462,20 +462,33 @@ class TestGroupsPresenter extends Tester\TestCase
462462
$token = PresenterTestHelper::login($this->container, $this->adminLogin);
463463

464464
$groups = $this->presenter->groups->findAll();
465-
$group = array_pop($groups);
465+
foreach ($groups as $group) {
466+
$payload = PresenterTestHelper::performPresenterRequest($this->presenter, 'V1:Groups', 'GET',
467+
['action' => 'assignments', 'id' => $group->getId()]
468+
);
466469

467-
$request = new Nette\Application\Request('V1:Groups',
468-
'GET',
469-
['action' => 'assignments', 'id' => $group->getId()]
470-
);
471-
$response = $this->presenter->run($request);
472-
Assert::type(Nette\Application\Responses\JsonResponse::class, $response);
470+
$correctIds = PresenterTestHelper::extractIdsMap($group->getAssignments()->toArray());
471+
$payloadIds = PresenterTestHelper::extractIdsMap($payload);
473472

474-
$result = $response->getPayload();
475-
$payload = $result['payload'];
476-
Assert::equal(200, $result['code']);
473+
Assert::equal($correctIds, $payloadIds);
474+
}
475+
}
476+
477+
public function testShadowAssignments()
478+
{
479+
$token = PresenterTestHelper::login($this->container, $this->adminLogin);
477480

478-
Assert::equal($group->getAssignments()->toArray(), $payload); // admin can access everything
481+
$groups = $this->presenter->groups->findAll();
482+
foreach ($groups as $group) {
483+
$payload = PresenterTestHelper::performPresenterRequest($this->presenter, 'V1:Groups', 'GET',
484+
['action' => 'shadowAssignments', 'id' => $group->getId()]
485+
);
486+
487+
$correctIds = PresenterTestHelper::extractIdsMap($group->getShadowAssignments()->toArray());
488+
$payloadIds = PresenterTestHelper::extractIdsMap($payload);
489+
490+
Assert::equal($correctIds, $payloadIds);
491+
}
479492
}
480493

481494
public function testExercises()

tests/base/PresenterTestHelper.php

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -158,10 +158,10 @@ public static function jsonResponse($payload) {
158158
/**
159159
* Perform regular presenter request and make common asserts.
160160
* @param $presenter The presenter which should handle the request.
161-
* @param $module String representing the module path (e.g., 'V1:Exercises').
161+
* @param string $module String representing the module path (e.g., 'V1:Exercises').
162162
* @param string $method HTTP method of the request (GET, POST, ...).
163163
* @param array $params Parameters of the request.
164-
* @param array $params Body of the request (must be POST).
164+
* @param array $post Body of the request (must be POST).
165165
* @param int $expectedCode Expected HTTP response code (200 by default).
166166
* @return array|null Payload subtree of JSON request.
167167
* @throws Exception
@@ -177,4 +177,25 @@ public static function performPresenterRequest($presenter, string $module, strin
177177
return array_key_exists('payload', $result) ? $result['payload'] : null;
178178
}
179179

180+
/**
181+
* Traverse a list of objects, assoc. arrays, or string IDs and convert it into an array indexed by the IDs.
182+
* @param array $list List of entities to be processed.
183+
* @return array Indexed by IDs, values are trues.
184+
* @throws Exception
185+
*/
186+
public static function extractIdsMap(array $list)
187+
{
188+
$res = [];
189+
foreach ($list as $item) {
190+
if (is_array($item) && !empty($item['id'])) {
191+
$res[$item['id']] = true;
192+
} else if (is_object($item) && !empty($item->id)) {
193+
$res[$item->id] = true;
194+
} else {
195+
Tester\Assert::match('#^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$#', $item);
196+
$res[(string)$item->id] = true;
197+
}
198+
}
199+
return $res;
200+
}
180201
}

0 commit comments

Comments
 (0)