From a3feebebac6c93ea5e71d45c4db8d602efbfde0d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Witold=20Wis=CC=81niewski?= Date: Thu, 30 Apr 2026 10:49:48 +0200 Subject: [PATCH 1/4] Added webinar users endpoint --- src/Dto/WebinarUserDto.php | 27 +++++++++++++ src/Http/Controllers/WebinarController.php | 10 +++++ src/Http/Requests/WebinarUserRequest.php | 23 +++++++++++ .../Criteria/WebinarUserCriterion.php | 24 ++++++++++++ src/routes.php | 1 + tests/APIs/WebinarApiTest.php | 38 +++++++++++++++++++ 6 files changed, 123 insertions(+) create mode 100644 src/Dto/WebinarUserDto.php create mode 100644 src/Http/Requests/WebinarUserRequest.php create mode 100644 src/Repositories/Criteria/WebinarUserCriterion.php diff --git a/src/Dto/WebinarUserDto.php b/src/Dto/WebinarUserDto.php new file mode 100644 index 0000000..f6234c8 --- /dev/null +++ b/src/Dto/WebinarUserDto.php @@ -0,0 +1,27 @@ +push(new WebinarUserCriterion($array['webinar_id'])); + } + + if (key_exists('search', $array) && !is_null($array['search'])) { + $criteria->push(new UserSearchCriterion($array['search'])); + } + + return new self($criteria); + } +} diff --git a/src/Http/Controllers/WebinarController.php b/src/Http/Controllers/WebinarController.php index e6b2462..f530176 100644 --- a/src/Http/Controllers/WebinarController.php +++ b/src/Http/Controllers/WebinarController.php @@ -5,6 +5,7 @@ use EscolaLms\Auth\Dtos\Admin\UserAssignableDto; use EscolaLms\Auth\Http\Resources\UserFullResource; use EscolaLms\Auth\Services\Contracts\UserServiceContract; +use EscolaLms\Webinar\Dto\WebinarUserDto; use EscolaLms\Webinar\Http\Requests\DeleteWebinarRequest; use EscolaLms\Webinar\Http\Requests\ShowWebinarRequest; use EscolaLms\Core\Dtos\OrderDto; @@ -17,6 +18,7 @@ use EscolaLms\Core\Http\Controllers\EscolaLmsBaseController; use EscolaLms\Webinar\Http\Requests\ListWebinarsRequest; use EscolaLms\Webinar\Http\Requests\WebinarAssignableUserListRequest; +use EscolaLms\Webinar\Http\Requests\WebinarUserRequest; use EscolaLms\Webinar\Http\Resources\WebinarSimpleResource; use EscolaLms\Webinar\Services\Contracts\WebinarServiceContract; use Illuminate\Http\JsonResponse; @@ -93,4 +95,12 @@ public function assignableUsers(WebinarAssignableUserListRequest $request): Json ->assignableUsersWithCriteria($dto, $request->get('per_page'), $request->get('page')); return $this->sendResponseForResource(UserFullResource::collection($result), __('Users assignable to courses')); } + + public function webinarUsers(int $id, WebinarUserRequest $request): JsonResponse + { + $dto = WebinarUserDto::instantiateFromArray(array_merge($request->validated(), ['webinar_id' => $id])); + $result = $this->userService + ->assignableUsersWithCriteria($dto, $request->get('per_page'), $request->get('page')); + return $this->sendResponseForResource(UserFullResource::collection($result), __('Webinar Users retrieved successfully')); + } } diff --git a/src/Http/Requests/WebinarUserRequest.php b/src/Http/Requests/WebinarUserRequest.php new file mode 100644 index 0000000..07e11f8 --- /dev/null +++ b/src/Http/Requests/WebinarUserRequest.php @@ -0,0 +1,23 @@ + ['string'], + ]; + } +} diff --git a/src/Repositories/Criteria/WebinarUserCriterion.php b/src/Repositories/Criteria/WebinarUserCriterion.php new file mode 100644 index 0000000..2c7c243 --- /dev/null +++ b/src/Repositories/Criteria/WebinarUserCriterion.php @@ -0,0 +1,24 @@ +whereHas( + 'webinars', + fn (Builder $query) => $query + ->where('id', $this->value) + ); + } +} diff --git a/src/routes.php b/src/routes.php index 996dcf1..ca62fb4 100644 --- a/src/routes.php +++ b/src/routes.php @@ -7,6 +7,7 @@ // admin endpoints Route::group(['middleware' => ['auth:api'], 'prefix' => 'api/admin'], function () { Route::post('webinars/{id}', [WebinarController::class, 'update']); + Route::get('webinars/{id}/users', [WebinarController::class, 'webinarUsers']); Route::resource('webinars', WebinarController::class); Route::get('webinars/users/assignable', [WebinarController::class, 'assignableUsers']); }); diff --git a/tests/APIs/WebinarApiTest.php b/tests/APIs/WebinarApiTest.php index 76d3a56..e8b2381 100644 --- a/tests/APIs/WebinarApiTest.php +++ b/tests/APIs/WebinarApiTest.php @@ -8,6 +8,7 @@ use EscolaLms\Core\Tests\CreatesUsers; use EscolaLms\Tags\Models\Tag; use EscolaLms\Webinar\Database\Seeders\WebinarsPermissionSeeder; +use EscolaLms\Webinar\Dto\WebinarUserDto; use EscolaLms\Webinar\Enum\WebinarPermissionsEnum; use EscolaLms\Webinar\Enum\WebinarStatusEnum; use EscolaLms\Webinar\Models\Webinar; @@ -451,4 +452,41 @@ public function testGenerateSignedUrlsNotSupported(): void ]) ->assertStatus(400); } + + public function testWebinarUsersUnauthorized(): void + { + $this->response = $this + ->json('GET', "/api/admin/webinars/{$this->webinar->getKey()}/users") + ->assertUnauthorized(); + } + + public function testWebinarUsers(): void + { + $admin = $this->makeAdmin(); + $student = $this->makeStudent(); + + $this->webinar->users()->sync([$student]); + + $dto = WebinarUserDto::instantiateFromArray(['webinar_id' => $this->webinar->getKey()]); + $users = app(UserServiceContract::class)->assignableUsersWithCriteria($dto); + assert($users instanceof LengthAwarePaginator); + + $this->response = $this + ->actingAs($this->user, 'api') + ->json('GET', "/api/admin/webinars/{$this->webinar->getKey()}/users") + ->assertOk() + ->assertJsonCount(min($users->total(), $users->perPage()), 'data') + ->assertJson([ + 'id' => $student->getKey(), + 'email' => $student->email, + ]) + ->assertJsonMissing([ + 'id' => $admin->getKey(), + 'email' => $admin->email, + ]) + ->assertJsonMissing([ + 'id' => $this->user->getKey(), + 'email' => $this->user->email, + ]); + } } From ac2effc2d32fa19c0019fd396b04b9f273b2a674 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Witold=20Wis=CC=81niewski?= Date: Thu, 30 Apr 2026 10:59:14 +0200 Subject: [PATCH 2/4] Fix tests --- tests/APIs/WebinarApiTest.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/APIs/WebinarApiTest.php b/tests/APIs/WebinarApiTest.php index e8b2381..35473ce 100644 --- a/tests/APIs/WebinarApiTest.php +++ b/tests/APIs/WebinarApiTest.php @@ -463,9 +463,11 @@ public function testWebinarUsersUnauthorized(): void public function testWebinarUsers(): void { $admin = $this->makeAdmin(); - $student = $this->makeStudent(); + $student = config('auth.providers.users.model')::factory()->create(); + $student->guard_name = 'api'; + $student->assignRole('student'); - $this->webinar->users()->sync([$student]); + $this->webinar->users()->sync($student); $dto = WebinarUserDto::instantiateFromArray(['webinar_id' => $this->webinar->getKey()]); $users = app(UserServiceContract::class)->assignableUsersWithCriteria($dto); From 6f581a95d99a7e806394d56c2749a01de4267f7d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Witold=20Wis=CC=81niewski?= Date: Thu, 30 Apr 2026 11:26:00 +0200 Subject: [PATCH 3/4] Fix criterion --- .../Controllers/Swagger/WebinarSwagger.php | 44 +++++++++++++++++++ .../Criteria/WebinarUserCriterion.php | 15 ++++--- 2 files changed, 53 insertions(+), 6 deletions(-) diff --git a/src/Http/Controllers/Swagger/WebinarSwagger.php b/src/Http/Controllers/Swagger/WebinarSwagger.php index 83a1ee5..b042b11 100644 --- a/src/Http/Controllers/Swagger/WebinarSwagger.php +++ b/src/Http/Controllers/Swagger/WebinarSwagger.php @@ -7,6 +7,7 @@ use EscolaLms\Webinar\Http\Requests\UpdateWebinarRequest; use EscolaLms\Webinar\Http\Requests\ListWebinarsRequest; use EscolaLms\Webinar\Http\Requests\WebinarAssignableUserListRequest; +use EscolaLms\Webinar\Http\Requests\WebinarUserRequest; use Illuminate\Http\JsonResponse; interface WebinarSwagger @@ -313,4 +314,47 @@ public function destroy(int $id, DeleteWebinarRequest $request): JsonResponse; * ) */ public function assignableUsers(WebinarAssignableUserListRequest $request): JsonResponse; + + /** + * @OA\Get( + * tags={"Admin Webinars"}, + * path="/api/admin/webinars/{id}/users", + * description="Get webinars users", + * security={ + * {"passport": {}}, + * }, + * @OA\Parameter( + * name="id", + * description="id of Webinar", + * @OA\Schema( + * type="integer", + * ), + * required=true, + * in="path" + * ), + * @OA\Parameter( + * name="search", + * required=false, + * in="query", + * @OA\Schema( + * type="string", + * ), + * ), + * @OA\Response( + * response=200, + * description="successful operation", + * @OA\MediaType( + * mediaType="application/json", + * ), + * ), + * @OA\Response( + * response=422, + * description="Bad request", + * @OA\MediaType( + * mediaType="application/json" + * ) + * ) + * ) + */ + public function webinarUsers(int $id, WebinarUserRequest $request): JsonResponse; } diff --git a/src/Repositories/Criteria/WebinarUserCriterion.php b/src/Repositories/Criteria/WebinarUserCriterion.php index 2c7c243..49db3bc 100644 --- a/src/Repositories/Criteria/WebinarUserCriterion.php +++ b/src/Repositories/Criteria/WebinarUserCriterion.php @@ -4,6 +4,7 @@ use EscolaLms\Core\Repositories\Criteria\Criterion; use Illuminate\Database\Eloquent\Builder; +use Illuminate\Support\Facades\DB; class WebinarUserCriterion extends Criterion { @@ -14,11 +15,13 @@ public function __construct(string $value) public function apply(Builder $query): Builder { - return $query - ->whereHas( - 'webinars', - fn (Builder $query) => $query - ->where('id', $this->value) - ); + $userTable = $query->getModel()->getTable(); + + return $query->whereExists(function ($subQuery) use ($userTable) { + $subQuery->select(DB::raw(1)) + ->from('webinar_user') + ->whereRaw("webinar_user.user_id = {$userTable}.id") + ->where('webinar_user.webinar_id', $this->value); + }); } } From e4cb76aa45007faeec744f0766b09eb2708f6a32 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Witold=20Wis=CC=81niewski?= Date: Thu, 30 Apr 2026 11:37:55 +0200 Subject: [PATCH 4/4] Fix tests --- tests/APIs/WebinarApiTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/APIs/WebinarApiTest.php b/tests/APIs/WebinarApiTest.php index 35473ce..4bb26d1 100644 --- a/tests/APIs/WebinarApiTest.php +++ b/tests/APIs/WebinarApiTest.php @@ -478,7 +478,7 @@ public function testWebinarUsers(): void ->json('GET', "/api/admin/webinars/{$this->webinar->getKey()}/users") ->assertOk() ->assertJsonCount(min($users->total(), $users->perPage()), 'data') - ->assertJson([ + ->assertJsonFragment([ 'id' => $student->getKey(), 'email' => $student->email, ])