From ac5d99b5e7fe824840f7770b3153ebc9b63050d8 Mon Sep 17 00:00:00 2001 From: Adrian Kunz Date: Fri, 27 Oct 2023 18:00:36 +0200 Subject: [PATCH 01/13] refactor(assignments-service): Rename member module to assignment-member --- .../assignment-member.controller.ts} | 2 +- .../assignment-member.handler.ts} | 2 +- .../assignment-member.module.ts} | 10 +++++----- .../assignments/src/assignment/assignment.module.ts | 4 ++-- services/apps/assignments/src/assignments.module.ts | 4 ++-- 5 files changed, 11 insertions(+), 11 deletions(-) rename services/apps/assignments/src/{member/member.controller.ts => assignment-member/assignment-member.controller.ts} (98%) rename services/apps/assignments/src/{member/member.handler.ts => assignment-member/assignment-member.handler.ts} (94%) rename services/apps/assignments/src/{member/member.module.ts => assignment-member/assignment-member.module.ts} (66%) diff --git a/services/apps/assignments/src/member/member.controller.ts b/services/apps/assignments/src/assignment-member/assignment-member.controller.ts similarity index 98% rename from services/apps/assignments/src/member/member.controller.ts rename to services/apps/assignments/src/assignment-member/assignment-member.controller.ts index 0ad53d627..8bb8a67ce 100644 --- a/services/apps/assignments/src/member/member.controller.ts +++ b/services/apps/assignments/src/assignment-member/assignment-member.controller.ts @@ -12,7 +12,7 @@ const removeOwnerResponse = 'Owner cannot remove themselves as member'; @Controller('assignments/:assignment/members') @ApiTags('Assignment Members') -export class MemberController { +export class AssignmentMemberController { constructor( private readonly memberService: MemberService, ) { diff --git a/services/apps/assignments/src/member/member.handler.ts b/services/apps/assignments/src/assignment-member/assignment-member.handler.ts similarity index 94% rename from services/apps/assignments/src/member/member.handler.ts rename to services/apps/assignments/src/assignment-member/assignment-member.handler.ts index 6f61f0bee..6b79e0054 100644 --- a/services/apps/assignments/src/member/member.handler.ts +++ b/services/apps/assignments/src/assignment-member/assignment-member.handler.ts @@ -4,7 +4,7 @@ import {Injectable} from "@nestjs/common"; import {MemberService} from "@app/member"; @Injectable() -export class MemberHandler { +export class AssignmentMemberHandler { constructor( readonly memberService: MemberService, ) { diff --git a/services/apps/assignments/src/member/member.module.ts b/services/apps/assignments/src/assignment-member/assignment-member.module.ts similarity index 66% rename from services/apps/assignments/src/member/member.module.ts rename to services/apps/assignments/src/assignment-member/assignment-member.module.ts index 653dadb8b..63b86ea33 100644 --- a/services/apps/assignments/src/member/member.module.ts +++ b/services/apps/assignments/src/assignment-member/assignment-member.module.ts @@ -1,8 +1,8 @@ import {forwardRef, Module} from '@nestjs/common'; import {MongooseModule} from '@nestjs/mongoose'; import {Member, MemberAuthGuard, MemberSchema, MemberService} from '@app/member'; -import {MemberController} from './member.controller'; -import {MemberHandler} from "./member.handler"; +import {AssignmentMemberController} from './assignment-member.controller'; +import {AssignmentMemberHandler} from "./assignment-member.handler"; import {AssignmentModule} from "../assignment/assignment.module"; @Module({ @@ -10,16 +10,16 @@ import {AssignmentModule} from "../assignment/assignment.module"; MongooseModule.forFeature([{name: Member.name, schema: MemberSchema}]), forwardRef(() => AssignmentModule), ], - controllers: [MemberController], + controllers: [AssignmentMemberController], providers: [ MemberService, MemberAuthGuard, - MemberHandler, + AssignmentMemberHandler, ], exports: [ MemberService, MemberAuthGuard, ], }) -export class MemberModule { +export class AssignmentMemberModule { } diff --git a/services/apps/assignments/src/assignment/assignment.module.ts b/services/apps/assignments/src/assignment/assignment.module.ts index de326784c..712cd84f4 100644 --- a/services/apps/assignments/src/assignment/assignment.module.ts +++ b/services/apps/assignments/src/assignment/assignment.module.ts @@ -5,7 +5,7 @@ import {AssignmentAuthGuard} from './assignment-auth.guard'; import {AssignmentController} from './assignment.controller'; import {Assignment, AssignmentSchema} from './assignment.schema'; import {AssignmentService} from './assignment.service'; -import {MemberModule} from "../member/member.module"; +import {AssignmentMemberModule} from "../assignment-member/assignment-member.module"; @Module({ imports: [ @@ -16,7 +16,7 @@ import {MemberModule} from "../member/member.module"; }, ]), HttpModule, - MemberModule, + AssignmentMemberModule, ], controllers: [AssignmentController], providers: [ diff --git a/services/apps/assignments/src/assignments.module.ts b/services/apps/assignments/src/assignments.module.ts index 7d804e255..a69290ce6 100644 --- a/services/apps/assignments/src/assignments.module.ts +++ b/services/apps/assignments/src/assignments.module.ts @@ -19,7 +19,7 @@ import {APP_INTERCEPTOR} from "@nestjs/core"; import {EmbeddingModule} from './embedding/embedding.module'; import {MossModule} from './moss/moss.module'; import { FileModule } from './file/file.module'; -import {MemberModule} from "./member/member.module"; +import {AssignmentMemberModule} from "./assignment-member/assignment-member.module"; @Module({ imports: [ @@ -38,7 +38,7 @@ import {MemberModule} from "./member/member.module"; }, }), AssignmentModule, - MemberModule, + AssignmentMemberModule, ClassroomModule, SolutionModule, AssigneeModule, From a0b7a295f4d7d0377b549834f446092f86964a8d Mon Sep 17 00:00:00 2001 From: Adrian Kunz Date: Fri, 27 Oct 2023 18:04:33 +0200 Subject: [PATCH 02/13] refactor(assignments-service): Add CourseAuth decorator and guard --- .../src/course/course-auth.decorator.ts | 12 ++++++++ .../src/course/course-auth.guard.ts | 26 ++++++++++++++++ .../src/course/course.controller.ts | 30 +++++-------------- 3 files changed, 45 insertions(+), 23 deletions(-) create mode 100644 services/apps/assignments/src/course/course-auth.decorator.ts create mode 100644 services/apps/assignments/src/course/course-auth.guard.ts diff --git a/services/apps/assignments/src/course/course-auth.decorator.ts b/services/apps/assignments/src/course/course-auth.decorator.ts new file mode 100644 index 000000000..2eb582203 --- /dev/null +++ b/services/apps/assignments/src/course/course-auth.decorator.ts @@ -0,0 +1,12 @@ +import {Auth} from '@app/keycloak-auth'; +import {applyDecorators, UseGuards} from '@nestjs/common'; +import {ApiForbiddenResponse} from '@nestjs/swagger'; +import {CourseAuthGuard} from './course-auth.guard'; + +export function CourseAuth(options: { forbiddenResponse: string }) { + return applyDecorators( + Auth(), + ApiForbiddenResponse({description: options.forbiddenResponse}), + UseGuards(CourseAuthGuard), + ); +} diff --git a/services/apps/assignments/src/course/course-auth.guard.ts b/services/apps/assignments/src/course/course-auth.guard.ts new file mode 100644 index 000000000..b9cb8fed9 --- /dev/null +++ b/services/apps/assignments/src/course/course-auth.guard.ts @@ -0,0 +1,26 @@ +import {UserToken} from '@app/keycloak-auth'; +import {CanActivate, ExecutionContext, Injectable} from '@nestjs/common'; +import {Request} from 'express'; +import {Observable} from 'rxjs'; +import {notFound} from '@mean-stream/nestx'; +import {CourseService} from "./course.service"; + +@Injectable() +export class CourseAuthGuard implements CanActivate { + constructor( + private courseService: CourseService, + ) { + } + + canActivate(context: ExecutionContext): boolean | Promise | Observable { + const req = context.switchToHttp().getRequest() as Request; + const id = req.params.course ?? req.params.id; + const user = (req as any).user; + return user && this.checkAuth(id, user); + } + + async checkAuth(id: string, user: UserToken): Promise { + const course = await this.courseService.findOne(id) ?? notFound(id); + return course.createdBy === user.sub; + } +} diff --git a/services/apps/assignments/src/course/course.controller.ts b/services/apps/assignments/src/course/course.controller.ts index 1f95abbf5..75538f3fa 100644 --- a/services/apps/assignments/src/course/course.controller.ts +++ b/services/apps/assignments/src/course/course.controller.ts @@ -1,10 +1,11 @@ import {Auth, AuthUser, UserToken} from '@app/keycloak-auth'; -import {NotFound, notFound} from '@mean-stream/nestx'; -import {Body, Controller, Delete, ForbiddenException, Get, Param, Patch, Post, Query} from '@nestjs/common'; -import {ApiCreatedResponse, ApiForbiddenResponse, ApiOkResponse, ApiOperation, ApiTags} from '@nestjs/swagger'; +import {NotFound} from '@mean-stream/nestx'; +import {Body, Controller, Delete, Get, Param, Patch, Post, Query} from '@nestjs/common'; +import {ApiCreatedResponse, ApiOkResponse, ApiOperation, ApiTags} from '@nestjs/swagger'; import {CourseStudent, CreateCourseDto, UpdateCourseDto} from './course.dto'; import {Course} from './course.schema'; import {CourseService} from './course.service'; +import {CourseAuth} from "./course-auth.decorator"; const forbiddenResponse = 'Not owner.'; @@ -43,50 +44,33 @@ export class CourseController { @Get(':id/students') @ApiOperation({summary: 'Get summary of all students in a course'}) - @Auth() + @CourseAuth({forbiddenResponse}) @NotFound() @ApiOkResponse({type: [CourseStudent]}) - @ApiForbiddenResponse({description: forbiddenResponse}) async getStudents( @Param('id') id: string, - @AuthUser() user: UserToken, ): Promise { - await this.checkAuth(id, user); return this.courseService.getStudents(id); } @Patch(':id') - @Auth() + @CourseAuth({forbiddenResponse}) @NotFound() @ApiOkResponse({type: Course}) - @ApiForbiddenResponse({description: forbiddenResponse}) async update( @Param('id') id: string, @Body() dto: UpdateCourseDto, - @AuthUser() user: UserToken, ): Promise { - await this.checkAuth(id, user); return this.courseService.update(id, dto); } @Delete(':id') - @Auth() + @CourseAuth({forbiddenResponse}) @NotFound() @ApiOkResponse({type: Course}) - @ApiForbiddenResponse({description: forbiddenResponse}) async remove( @Param('id') id: string, - @Body() dto: UpdateCourseDto, - @AuthUser() user: UserToken, ): Promise { - await this.checkAuth(id, user); return this.courseService.remove(id); } - - private async checkAuth(id: string, user: UserToken) { - const course = await this.courseService.findOne(id) ?? notFound(id); - if (course.createdBy !== user.sub) { - throw new ForbiddenException(forbiddenResponse); - } - } } From 352f5d1ba2e8a9152fe9d938f7ba862d1a58d27b Mon Sep 17 00:00:00 2001 From: Adrian Kunz Date: Fri, 27 Oct 2023 18:05:06 +0200 Subject: [PATCH 03/13] fix(assignments-service): Require authentication for course creation --- services/apps/assignments/src/course/course.controller.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/services/apps/assignments/src/course/course.controller.ts b/services/apps/assignments/src/course/course.controller.ts index 75538f3fa..4990853ab 100644 --- a/services/apps/assignments/src/course/course.controller.ts +++ b/services/apps/assignments/src/course/course.controller.ts @@ -18,13 +18,13 @@ export class CourseController { } @Post() - @Auth({optional: true}) + @Auth() @ApiCreatedResponse({type: Course}) async create( @Body() dto: CreateCourseDto, - @AuthUser() user?: UserToken, + @AuthUser() user: UserToken, ): Promise { - return this.courseService.create(dto, user?.sub); + return this.courseService.create(dto, user.sub); } @Get() From 6cf69f24148e3cf445edf52b11d27fe06ac90a0a Mon Sep 17 00:00:00 2001 From: Adrian Kunz Date: Fri, 27 Oct 2023 18:14:18 +0200 Subject: [PATCH 04/13] refactor(assignments-service): Add Course._id --- services/apps/assignments/src/course/course.dto.ts | 5 ++--- services/apps/assignments/src/course/course.schema.ts | 5 ++++- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/services/apps/assignments/src/course/course.dto.ts b/services/apps/assignments/src/course/course.dto.ts index e1f5132c7..ed4c62c7a 100644 --- a/services/apps/assignments/src/course/course.dto.ts +++ b/services/apps/assignments/src/course/course.dto.ts @@ -3,13 +3,12 @@ import {AuthorInfo, Solution} from '../solution/solution.schema'; import {Course} from './course.schema'; export class CreateCourseDto extends OmitType(Course, [ + '_id', 'createdBy', ] as const) { } -export class UpdateCourseDto extends PartialType(OmitType(Course, [ - 'createdBy', -] as const)) { +export class UpdateCourseDto extends PartialType(CreateCourseDto) { } export class SolutionSummary extends PickType(Solution, [ diff --git a/services/apps/assignments/src/course/course.schema.ts b/services/apps/assignments/src/course/course.schema.ts index 089505c77..24a99c066 100644 --- a/services/apps/assignments/src/course/course.schema.ts +++ b/services/apps/assignments/src/course/course.schema.ts @@ -1,10 +1,13 @@ import {Prop, Schema, SchemaFactory} from '@nestjs/mongoose'; import {ApiProperty, ApiPropertyOptional} from '@nestjs/swagger'; import {IsArray, IsMongoId, IsNotEmpty, IsOptional, IsString, IsUUID} from 'class-validator'; -import {Document} from 'mongoose'; +import {Document, Types} from 'mongoose'; @Schema() export class Course { + @ApiProperty() + _id: Types.ObjectId; + @Prop() @ApiPropertyOptional() @IsOptional() From 6f4927ff4f9f0cdf579b3e6ea7087e2dd50fa57b Mon Sep 17 00:00:00 2001 From: Adrian Kunz Date: Fri, 27 Oct 2023 18:15:39 +0200 Subject: [PATCH 05/13] feat(assignments-service): Add CourseMember module --- .../assignments/src/assignments.module.ts | 2 + .../course-member/course-member.controller.ts | 68 +++++++++++++++++++ .../course-member/course-member.handler.ts | 23 +++++++ .../src/course-member/course-member.module.ts | 25 +++++++ .../assignments/src/course/course.module.ts | 10 ++- 5 files changed, 127 insertions(+), 1 deletion(-) create mode 100644 services/apps/assignments/src/course-member/course-member.controller.ts create mode 100644 services/apps/assignments/src/course-member/course-member.handler.ts create mode 100644 services/apps/assignments/src/course-member/course-member.module.ts diff --git a/services/apps/assignments/src/assignments.module.ts b/services/apps/assignments/src/assignments.module.ts index a69290ce6..bbcc93626 100644 --- a/services/apps/assignments/src/assignments.module.ts +++ b/services/apps/assignments/src/assignments.module.ts @@ -20,6 +20,7 @@ import {EmbeddingModule} from './embedding/embedding.module'; import {MossModule} from './moss/moss.module'; import { FileModule } from './file/file.module'; import {AssignmentMemberModule} from "./assignment-member/assignment-member.module"; +import {CourseMemberModule} from "./course-member/course-member.module"; @Module({ imports: [ @@ -44,6 +45,7 @@ import {AssignmentMemberModule} from "./assignment-member/assignment-member.modu AssigneeModule, CommentModule, CourseModule, + CourseMemberModule, EvaluationModule, SearchModule, StatisticsModule, diff --git a/services/apps/assignments/src/course-member/course-member.controller.ts b/services/apps/assignments/src/course-member/course-member.controller.ts new file mode 100644 index 000000000..9bd1c5b76 --- /dev/null +++ b/services/apps/assignments/src/course-member/course-member.controller.ts @@ -0,0 +1,68 @@ +import {AuthUser, UserToken} from '@app/keycloak-auth'; +import {NotFound, ObjectIdPipe} from '@mean-stream/nestx'; +import {Body, ConflictException, Controller, Delete, Get, Param, Put} from '@nestjs/common'; +import {ApiConflictResponse, ApiOkResponse, ApiTags} from '@nestjs/swagger'; +import {Types} from 'mongoose'; +import {CourseAuth} from '../course/course-auth.decorator'; +import {Member, MemberService, UpdateMemberDto} from '@app/member'; + +const forbiddenResponse = 'Not member of course'; +const notOwnerResponse = 'Not owner of course'; +const removeOwnerResponse = 'Owner cannot remove themselves as member'; + +@Controller('courses/:course/members') +@ApiTags('Course Members') +export class CourseMemberController { + constructor( + private readonly memberService: MemberService, + ) { + } + + @Get() + @CourseAuth({forbiddenResponse}) + @ApiOkResponse({type: [Member]}) + async findAll( + @Param('course', ObjectIdPipe) course: Types.ObjectId, + ): Promise { + return this.memberService.findAll({parent: course}); + } + + @Get(':user') + @CourseAuth({forbiddenResponse}) + @NotFound() + @ApiOkResponse({type: Member}) + async findOne( + @Param('course', ObjectIdPipe) course: Types.ObjectId, + @Param('user') user: string, + ): Promise { + return this.memberService.findOne({parent: course, user}); + } + + @Put(':user') + @CourseAuth({forbiddenResponse: notOwnerResponse}) + @ApiOkResponse({type: Member}) + @NotFound() + async update( + @Param('course', ObjectIdPipe) course: Types.ObjectId, + @Param('user') user: string, + @Body() dto: UpdateMemberDto, + ): Promise { + return this.memberService.upsert({parent: course, user}, dto); + } + + @Delete(':user') + @CourseAuth({forbiddenResponse: notOwnerResponse}) + @NotFound() + @ApiOkResponse({type: Member}) + @ApiConflictResponse({description: removeOwnerResponse}) + async remove( + @Param('course', ObjectIdPipe) course: Types.ObjectId, + @Param('user') user: string, + @AuthUser() token: UserToken, + ): Promise { + if (token.sub === user) { + throw new ConflictException(removeOwnerResponse); + } + return this.memberService.deleteOne({parent: course, user}); + } +} diff --git a/services/apps/assignments/src/course-member/course-member.handler.ts b/services/apps/assignments/src/course-member/course-member.handler.ts new file mode 100644 index 000000000..7e4e5b521 --- /dev/null +++ b/services/apps/assignments/src/course-member/course-member.handler.ts @@ -0,0 +1,23 @@ +import {OnEvent} from "@nestjs/event-emitter"; +import {Course} from "../course/course.schema"; +import {Injectable} from "@nestjs/common"; +import {MemberService} from "@app/member"; + +@Injectable() +export class CourseMemberHandler { + constructor( + readonly memberService: MemberService, + ) { + } + + @OnEvent('courses.*.created') + @OnEvent('courses.*.updated') + async onCourseChanged(course: Course) { + await this.memberService.upsert({parent: course._id, user: course.createdBy}, {}); + } + + @OnEvent('courses.*.deleted') + async onCourseDeleted(course: Course) { + await this.memberService.deleteMany({parent: course._id}); + } +} diff --git a/services/apps/assignments/src/course-member/course-member.module.ts b/services/apps/assignments/src/course-member/course-member.module.ts new file mode 100644 index 000000000..9969f83d1 --- /dev/null +++ b/services/apps/assignments/src/course-member/course-member.module.ts @@ -0,0 +1,25 @@ +import {forwardRef, Module} from '@nestjs/common'; +import {MongooseModule} from '@nestjs/mongoose'; +import {Member, MemberAuthGuard, MemberSchema, MemberService} from '@app/member'; +import {CourseMemberController} from './course-member.controller'; +import {CourseMemberHandler} from "./course-member.handler"; +import {CourseModule} from "../course/course.module"; + +@Module({ + imports: [ + MongooseModule.forFeature([{name: Member.name, schema: MemberSchema}]), + forwardRef(() => CourseModule), + ], + controllers: [CourseMemberController], + providers: [ + MemberService, + MemberAuthGuard, + CourseMemberHandler, + ], + exports: [ + MemberService, + MemberAuthGuard, + ], +}) +export class CourseMemberModule { +} diff --git a/services/apps/assignments/src/course/course.module.ts b/services/apps/assignments/src/course/course.module.ts index ec59739e8..cc0ec8f94 100644 --- a/services/apps/assignments/src/course/course.module.ts +++ b/services/apps/assignments/src/course/course.module.ts @@ -5,6 +5,7 @@ import {SolutionModule} from '../solution/solution.module'; import {CourseController} from './course.controller'; import {Course, CourseSchema} from './course.schema'; import {CourseService} from './course.service'; +import {CourseAuthGuard} from "./course-auth.guard"; @Module({ imports: [ @@ -18,7 +19,14 @@ import {CourseService} from './course.service'; AssigneeModule, ], controllers: [CourseController], - providers: [CourseService], + providers: [ + CourseService, + CourseAuthGuard, + ], + exports: [ + CourseService, + CourseAuthGuard, + ], }) export class CourseModule { } From a2bafda90d6fac375a74091a7daef5753bb0a7cd Mon Sep 17 00:00:00 2001 From: Adrian Kunz Date: Fri, 27 Oct 2023 18:21:20 +0200 Subject: [PATCH 06/13] feat(assignments-service): Move CourseAuth to CourseMember module and check for members --- .../{course => course-member}/course-auth.decorator.ts | 0 .../src/{course => course-member}/course-auth.guard.ts | 9 +++++++-- .../src/course-member/course-member.controller.ts | 2 +- .../src/course-member/course-member.module.ts | 7 ++++--- .../apps/assignments/src/course/course.controller.ts | 2 +- services/apps/assignments/src/course/course.module.ts | 5 ++--- 6 files changed, 15 insertions(+), 10 deletions(-) rename services/apps/assignments/src/{course => course-member}/course-auth.decorator.ts (100%) rename services/apps/assignments/src/{course => course-member}/course-auth.guard.ts (74%) diff --git a/services/apps/assignments/src/course/course-auth.decorator.ts b/services/apps/assignments/src/course-member/course-auth.decorator.ts similarity index 100% rename from services/apps/assignments/src/course/course-auth.decorator.ts rename to services/apps/assignments/src/course-member/course-auth.decorator.ts diff --git a/services/apps/assignments/src/course/course-auth.guard.ts b/services/apps/assignments/src/course-member/course-auth.guard.ts similarity index 74% rename from services/apps/assignments/src/course/course-auth.guard.ts rename to services/apps/assignments/src/course-member/course-auth.guard.ts index b9cb8fed9..e118f8101 100644 --- a/services/apps/assignments/src/course/course-auth.guard.ts +++ b/services/apps/assignments/src/course-member/course-auth.guard.ts @@ -3,12 +3,14 @@ import {CanActivate, ExecutionContext, Injectable} from '@nestjs/common'; import {Request} from 'express'; import {Observable} from 'rxjs'; import {notFound} from '@mean-stream/nestx'; -import {CourseService} from "./course.service"; +import {CourseService} from "../course/course.service"; +import {MemberService} from "@app/member"; @Injectable() export class CourseAuthGuard implements CanActivate { constructor( private courseService: CourseService, + private memberService: MemberService, ) { } @@ -21,6 +23,9 @@ export class CourseAuthGuard implements CanActivate { async checkAuth(id: string, user: UserToken): Promise { const course = await this.courseService.findOne(id) ?? notFound(id); - return course.createdBy === user.sub; + return course.createdBy === user.sub || !!await this.memberService.findOne({ + parent: course._id, + user: user.sub, + }); } } diff --git a/services/apps/assignments/src/course-member/course-member.controller.ts b/services/apps/assignments/src/course-member/course-member.controller.ts index 9bd1c5b76..b980a8df1 100644 --- a/services/apps/assignments/src/course-member/course-member.controller.ts +++ b/services/apps/assignments/src/course-member/course-member.controller.ts @@ -3,7 +3,7 @@ import {NotFound, ObjectIdPipe} from '@mean-stream/nestx'; import {Body, ConflictException, Controller, Delete, Get, Param, Put} from '@nestjs/common'; import {ApiConflictResponse, ApiOkResponse, ApiTags} from '@nestjs/swagger'; import {Types} from 'mongoose'; -import {CourseAuth} from '../course/course-auth.decorator'; +import {CourseAuth} from './course-auth.decorator'; import {Member, MemberService, UpdateMemberDto} from '@app/member'; const forbiddenResponse = 'Not member of course'; diff --git a/services/apps/assignments/src/course-member/course-member.module.ts b/services/apps/assignments/src/course-member/course-member.module.ts index 9969f83d1..1e85c6a6d 100644 --- a/services/apps/assignments/src/course-member/course-member.module.ts +++ b/services/apps/assignments/src/course-member/course-member.module.ts @@ -1,9 +1,10 @@ import {forwardRef, Module} from '@nestjs/common'; import {MongooseModule} from '@nestjs/mongoose'; -import {Member, MemberAuthGuard, MemberSchema, MemberService} from '@app/member'; +import {Member, MemberSchema, MemberService} from '@app/member'; import {CourseMemberController} from './course-member.controller'; import {CourseMemberHandler} from "./course-member.handler"; import {CourseModule} from "../course/course.module"; +import {CourseAuthGuard} from "./course-auth.guard"; @Module({ imports: [ @@ -13,12 +14,12 @@ import {CourseModule} from "../course/course.module"; controllers: [CourseMemberController], providers: [ MemberService, - MemberAuthGuard, + CourseAuthGuard, CourseMemberHandler, ], exports: [ MemberService, - MemberAuthGuard, + CourseAuthGuard, ], }) export class CourseMemberModule { diff --git a/services/apps/assignments/src/course/course.controller.ts b/services/apps/assignments/src/course/course.controller.ts index 4990853ab..77f31d310 100644 --- a/services/apps/assignments/src/course/course.controller.ts +++ b/services/apps/assignments/src/course/course.controller.ts @@ -5,7 +5,7 @@ import {ApiCreatedResponse, ApiOkResponse, ApiOperation, ApiTags} from '@nestjs/ import {CourseStudent, CreateCourseDto, UpdateCourseDto} from './course.dto'; import {Course} from './course.schema'; import {CourseService} from './course.service'; -import {CourseAuth} from "./course-auth.decorator"; +import {CourseAuth} from "../course-member/course-auth.decorator"; const forbiddenResponse = 'Not owner.'; diff --git a/services/apps/assignments/src/course/course.module.ts b/services/apps/assignments/src/course/course.module.ts index cc0ec8f94..75d9034a5 100644 --- a/services/apps/assignments/src/course/course.module.ts +++ b/services/apps/assignments/src/course/course.module.ts @@ -5,7 +5,7 @@ import {SolutionModule} from '../solution/solution.module'; import {CourseController} from './course.controller'; import {Course, CourseSchema} from './course.schema'; import {CourseService} from './course.service'; -import {CourseAuthGuard} from "./course-auth.guard"; +import {CourseMemberModule} from "../course-member/course-member.module"; @Module({ imports: [ @@ -17,15 +17,14 @@ import {CourseAuthGuard} from "./course-auth.guard"; ]), SolutionModule, AssigneeModule, + CourseMemberModule, ], controllers: [CourseController], providers: [ CourseService, - CourseAuthGuard, ], exports: [ CourseService, - CourseAuthGuard, ], }) export class CourseModule { From c1dba2e898ab8ef9c8a562931150b41021a25554 Mon Sep 17 00:00:00 2001 From: Adrian Kunz Date: Fri, 27 Oct 2023 20:25:24 +0200 Subject: [PATCH 07/13] refactor(frontend): Move MemberService to top AssignmentModule --- frontend/src/app/assignment/assignment.module.ts | 2 ++ .../app/assignment/modules/assignment/assignment.module.ts | 2 -- .../assignment/modules/assignment/share/share.component.ts | 2 +- .../{modules/assignment => services}/member.service.ts | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) rename frontend/src/app/assignment/{modules/assignment => services}/member.service.ts (88%) diff --git a/frontend/src/app/assignment/assignment.module.ts b/frontend/src/app/assignment/assignment.module.ts index 7d8ca94a6..f6d5a692a 100644 --- a/frontend/src/app/assignment/assignment.module.ts +++ b/frontend/src/app/assignment/assignment.module.ts @@ -31,6 +31,7 @@ import {AssigneeService} from "./services/assignee.service"; import {EvaluationService} from "./services/evaluation.service"; import {EmbeddingService} from "./services/embedding.service"; import {KeycloakBearerInterceptor} from "keycloak-angular"; +import {MemberService} from "./services/member.service"; @NgModule({ declarations: [ @@ -77,6 +78,7 @@ import {KeycloakBearerInterceptor} from "keycloak-angular"; AssigneeService, EvaluationService, EmbeddingService, + MemberService, ], }) export class AssignmentModule { diff --git a/frontend/src/app/assignment/modules/assignment/assignment.module.ts b/frontend/src/app/assignment/modules/assignment/assignment.module.ts index 2b1961280..250cd289e 100644 --- a/frontend/src/app/assignment/modules/assignment/assignment.module.ts +++ b/frontend/src/app/assignment/modules/assignment/assignment.module.ts @@ -26,7 +26,6 @@ import {SubmitModalComponent} from './submit-modal/submit-modal.component'; import {AssignmentTasksComponent} from './tasks/tasks.component'; import {StatisticsService} from "./statistics.service"; import {SubmitService} from "./submit.service"; -import {MemberService} from "./member.service"; import {UserModule} from "../../../user/user.module"; @NgModule({ @@ -59,7 +58,6 @@ import {UserModule} from "../../../user/user.module"; UserModule, ], providers: [ - MemberService, StatisticsService, SubmitService, ], diff --git a/frontend/src/app/assignment/modules/assignment/share/share.component.ts b/frontend/src/app/assignment/modules/assignment/share/share.component.ts index e7c17d7f4..f1350a92e 100644 --- a/frontend/src/app/assignment/modules/assignment/share/share.component.ts +++ b/frontend/src/app/assignment/modules/assignment/share/share.component.ts @@ -4,7 +4,7 @@ import {ActivatedRoute} from '@angular/router'; import {switchMap, tap} from 'rxjs/operators'; import {AssignmentService} from '../../../services/assignment.service'; import Assignment, {ReadAssignmentDto} from "../../../model/assignment"; -import {MemberService} from "../member.service"; +import {MemberService} from "../../../services/member.service"; import {Member} from "../../../../user/member"; import {User} from "../../../../user/user"; import {forkJoin} from "rxjs"; diff --git a/frontend/src/app/assignment/modules/assignment/member.service.ts b/frontend/src/app/assignment/services/member.service.ts similarity index 88% rename from frontend/src/app/assignment/modules/assignment/member.service.ts rename to frontend/src/app/assignment/services/member.service.ts index 2bfcb818d..d506cb3b6 100644 --- a/frontend/src/app/assignment/modules/assignment/member.service.ts +++ b/frontend/src/app/assignment/services/member.service.ts @@ -2,8 +2,8 @@ import {HttpClient} from '@angular/common/http'; import {Injectable} from '@angular/core'; import {Observable} from 'rxjs'; import {tap} from 'rxjs/operators'; -import {Member} from "../../../user/member"; -import {environment} from "../../../../environments/environment"; +import {Member} from "../../user/member"; +import {environment} from "../../../environments/environment"; @Injectable() export class MemberService { From c04d9b779ef284633cf4391c39c54e113b6870d9 Mon Sep 17 00:00:00 2001 From: Adrian Kunz Date: Fri, 27 Oct 2023 20:27:48 +0200 Subject: [PATCH 08/13] refactor(frontend): Make MemberService more generic --- .../modules/assignment/share/share.component.ts | 6 +++--- .../src/app/assignment/services/member.service.ts | 14 ++++++++------ 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/frontend/src/app/assignment/modules/assignment/share/share.component.ts b/frontend/src/app/assignment/modules/assignment/share/share.component.ts index f1350a92e..835322be5 100644 --- a/frontend/src/app/assignment/modules/assignment/share/share.component.ts +++ b/frontend/src/app/assignment/modules/assignment/share/share.component.ts @@ -41,7 +41,7 @@ export class ShareComponent implements OnInit { }); this.route.params.pipe( - switchMap(({aid}) => this.memberService.findAll(aid)), + switchMap(({aid}) => this.memberService.findAll('assignments', aid)), tap(members => this.members = members), switchMap(members => forkJoin(members.map(member => this.userService.findOne(member.user).pipe( tap(user => member._user = user), @@ -60,7 +60,7 @@ export class ShareComponent implements OnInit { } addMember() { - this.memberService.update({ + this.memberService.update('assignments', { parent: this.assignment!._id, user: this.newMember!.id!, _user: this.newMember!, @@ -71,7 +71,7 @@ export class ShareComponent implements OnInit { } deleteMember(member: Member) { - this.memberService.delete(member).subscribe(() => { + this.memberService.delete('assignments', member.parent, member.user).subscribe(() => { this.members.splice(this.members.indexOf(member), 1); }); } diff --git a/frontend/src/app/assignment/services/member.service.ts b/frontend/src/app/assignment/services/member.service.ts index d506cb3b6..8e4591421 100644 --- a/frontend/src/app/assignment/services/member.service.ts +++ b/frontend/src/app/assignment/services/member.service.ts @@ -5,6 +5,8 @@ import {tap} from 'rxjs/operators'; import {Member} from "../../user/member"; import {environment} from "../../../environments/environment"; +export type Namespace = 'assignments' | 'courses'; + @Injectable() export class MemberService { constructor( @@ -12,18 +14,18 @@ export class MemberService { ) { } - findAll(id: string): Observable { - return this.http.get(`${environment.assignmentsApiUrl}/assignments/${id}/members`); + findAll(namespace: Namespace, parent: string): Observable { + return this.http.get(`${environment.assignmentsApiUrl}/${namespace}/${parent}/members`); } - update(member: Member): Observable { + update(namespace: Namespace, member: Member): Observable { const {_user, parent, user, ...rest} = member; - return this.http.put(`${environment.assignmentsApiUrl}/assignments/${parent}/members/${user}`, rest).pipe( + return this.http.put(`${environment.assignmentsApiUrl}/${namespace}/${parent}/members/${user}`, rest).pipe( tap(newMember => newMember._user = _user), ); } - delete({parent, user}: Member): Observable { - return this.http.delete(`${environment.assignmentsApiUrl}/assignments/${parent}/members/${user}`); + delete(namespace: Namespace, parent: string, user: string): Observable { + return this.http.delete(`${environment.assignmentsApiUrl}/${namespace}/${parent}/members/${user}`); } } From c6122bad64402a2462b0e3e2d6d67352b3da6156 Mon Sep 17 00:00:00 2001 From: Adrian Kunz Date: Fri, 27 Oct 2023 20:35:55 +0200 Subject: [PATCH 09/13] refactor(frontend): Add EditMemberList component --- .../modules/assignment/assignment.module.ts | 2 - .../assignment/share/share.component.html | 14 +---- .../assignment/share/share.component.ts | 37 +------------ .../edit-member-list.component.html | 13 +++++ .../edit-member-list.component.scss | 0 .../edit-member-list.component.ts | 54 +++++++++++++++++++ .../modules/shared/shared.module.ts | 5 ++ 7 files changed, 75 insertions(+), 50 deletions(-) create mode 100644 frontend/src/app/assignment/modules/shared/edit-member-list/edit-member-list.component.html create mode 100644 frontend/src/app/assignment/modules/shared/edit-member-list/edit-member-list.component.scss create mode 100644 frontend/src/app/assignment/modules/shared/edit-member-list/edit-member-list.component.ts diff --git a/frontend/src/app/assignment/modules/assignment/assignment.module.ts b/frontend/src/app/assignment/modules/assignment/assignment.module.ts index 250cd289e..48be25ee6 100644 --- a/frontend/src/app/assignment/modules/assignment/assignment.module.ts +++ b/frontend/src/app/assignment/modules/assignment/assignment.module.ts @@ -26,7 +26,6 @@ import {SubmitModalComponent} from './submit-modal/submit-modal.component'; import {AssignmentTasksComponent} from './tasks/tasks.component'; import {StatisticsService} from "./statistics.service"; import {SubmitService} from "./submit.service"; -import {UserModule} from "../../../user/user.module"; @NgModule({ declarations: [ @@ -55,7 +54,6 @@ import {UserModule} from "../../../user/user.module"; NgbAccordionModule, RouteTabsModule, ModalModule, - UserModule, ], providers: [ StatisticsService, diff --git a/frontend/src/app/assignment/modules/assignment/share/share.component.html b/frontend/src/app/assignment/modules/assignment/share/share.component.html index 164622981..78d2188d5 100644 --- a/frontend/src/app/assignment/modules/assignment/share/share.component.html +++ b/frontend/src/app/assignment/modules/assignment/share/share.component.html @@ -11,19 +11,7 @@
Share this link with your students. They will be able to submit solutions to this assignment.
-
- Members - - -
-
- -
- Add teaching assistants and other people who should have full access to this assignment. -
-
+
The following fields contain the assignment token, a secret key that can be used to access and grade all submissions. diff --git a/frontend/src/app/assignment/modules/assignment/share/share.component.ts b/frontend/src/app/assignment/modules/assignment/share/share.component.ts index 835322be5..756e1b5c3 100644 --- a/frontend/src/app/assignment/modules/assignment/share/share.component.ts +++ b/frontend/src/app/assignment/modules/assignment/share/share.component.ts @@ -1,14 +1,9 @@ import {DOCUMENT} from '@angular/common'; import {Component, Inject, OnInit} from '@angular/core'; import {ActivatedRoute} from '@angular/router'; -import {switchMap, tap} from 'rxjs/operators'; +import {switchMap} from 'rxjs/operators'; import {AssignmentService} from '../../../services/assignment.service'; import Assignment, {ReadAssignmentDto} from "../../../model/assignment"; -import {MemberService} from "../../../services/member.service"; -import {Member} from "../../../../user/member"; -import {User} from "../../../../user/user"; -import {forkJoin} from "rxjs"; -import {UserService} from "../../../../user/user.service"; @Component({ selector: 'app-assignment-share', @@ -17,16 +12,11 @@ import {UserService} from "../../../../user/user.service"; }) export class ShareComponent implements OnInit { assignment?: Assignment | ReadAssignmentDto; - members: Member[]; - - newMember?: User; readonly origin: string; constructor( private assignmentService: AssignmentService, - private memberService: MemberService, - private userService: UserService, private route: ActivatedRoute, @Inject(DOCUMENT) document: Document, ) { @@ -39,14 +29,6 @@ export class ShareComponent implements OnInit { ).subscribe(assignment => { this.assignment = assignment; }); - - this.route.params.pipe( - switchMap(({aid}) => this.memberService.findAll('assignments', aid)), - tap(members => this.members = members), - switchMap(members => forkJoin(members.map(member => this.userService.findOne(member.user).pipe( - tap(user => member._user = user), - )))), - ).subscribe(); } regenerateToken() { @@ -59,20 +41,5 @@ export class ShareComponent implements OnInit { }); } - addMember() { - this.memberService.update('assignments', { - parent: this.assignment!._id, - user: this.newMember!.id!, - _user: this.newMember!, - }).subscribe(member => { - this.members.push(member); - this.newMember = undefined; - }); - } - - deleteMember(member: Member) { - this.memberService.delete('assignments', member.parent, member.user).subscribe(() => { - this.members.splice(this.members.indexOf(member), 1); - }); - } + protected readonly switchMap = switchMap; } diff --git a/frontend/src/app/assignment/modules/shared/edit-member-list/edit-member-list.component.html b/frontend/src/app/assignment/modules/shared/edit-member-list/edit-member-list.component.html new file mode 100644 index 000000000..6e12de77c --- /dev/null +++ b/frontend/src/app/assignment/modules/shared/edit-member-list/edit-member-list.component.html @@ -0,0 +1,13 @@ +
+ Members + + +
+
+ +
+ Add teaching assistants and other people who should have full access to this {{ namespace|slice:0:-1 }}. +
+
diff --git a/frontend/src/app/assignment/modules/shared/edit-member-list/edit-member-list.component.scss b/frontend/src/app/assignment/modules/shared/edit-member-list/edit-member-list.component.scss new file mode 100644 index 000000000..e69de29bb diff --git a/frontend/src/app/assignment/modules/shared/edit-member-list/edit-member-list.component.ts b/frontend/src/app/assignment/modules/shared/edit-member-list/edit-member-list.component.ts new file mode 100644 index 000000000..ab736a625 --- /dev/null +++ b/frontend/src/app/assignment/modules/shared/edit-member-list/edit-member-list.component.ts @@ -0,0 +1,54 @@ +import {Component, Input, OnInit} from '@angular/core'; +import {Member} from "../../../../user/member"; +import {User} from "../../../../user/user"; +import {switchMap, tap} from "rxjs/operators"; +import {forkJoin} from "rxjs"; +import {MemberService, Namespace} from "../../../services/member.service"; +import {UserService} from "../../../../user/user.service"; + +@Component({ + selector: 'app-edit-member-list', + templateUrl: './edit-member-list.component.html', + styleUrls: ['./edit-member-list.component.scss'] +}) +export class EditMemberListComponent implements OnInit { + @Input({required: true}) namespace: Namespace; + @Input({required: true}) parent: string; + @Input() owner?: string; + + members: Member[]; + + newMember?: User; + + constructor( + private memberService: MemberService, + private userService: UserService, + ) { + } + + ngOnInit() { + this.memberService.findAll(this.namespace, this.parent).pipe( + tap(members => this.members = members), + switchMap(members => forkJoin(members.map(member => this.userService.findOne(member.user).pipe( + tap(user => member._user = user), + )))), + ).subscribe(); + } + + addMember() { + this.memberService.update(this.namespace, { + parent: this.parent, + user: this.newMember!.id!, + _user: this.newMember!, + }).subscribe(member => { + this.members.push(member); + this.newMember = undefined; + }); + } + + deleteMember(member: Member) { + this.memberService.delete(this.namespace, member.parent, member.user).subscribe(() => { + this.members.splice(this.members.indexOf(member), 1); + }); + } +} diff --git a/frontend/src/app/assignment/modules/shared/shared.module.ts b/frontend/src/app/assignment/modules/shared/shared.module.ts index 8d4f68f37..5ba30b89d 100644 --- a/frontend/src/app/assignment/modules/shared/shared.module.ts +++ b/frontend/src/app/assignment/modules/shared/shared.module.ts @@ -16,6 +16,8 @@ import {GithubLinkPipe} from './pipes/github-link.pipe'; import {CloneLinkPipe} from './pipes/clone-link.pipe'; import {AssignmentActionsComponent} from './assignment-actions/assignment-actions.component'; import {AssigneeInputComponent} from './assignee-input/assignee-input.component'; +import {EditMemberListComponent} from './edit-member-list/edit-member-list.component'; +import {UserModule} from "../../../user/user.module"; @NgModule({ imports: [ @@ -26,6 +28,7 @@ import {AssigneeInputComponent} from './assignee-input/assignee-input.component' NgbDropdownModule, NgbTypeaheadModule, FormsModule, + UserModule, ], declarations: [ AssignmentInfoComponent, @@ -40,6 +43,7 @@ import {AssigneeInputComponent} from './assignee-input/assignee-input.component' CloneLinkPipe, AssignmentActionsComponent, AssigneeInputComponent, + EditMemberListComponent, ], exports: [ AssignmentInfoComponent, @@ -54,6 +58,7 @@ import {AssigneeInputComponent} from './assignee-input/assignee-input.component' CloneLinkPipe, AssignmentActionsComponent, AssigneeInputComponent, + EditMemberListComponent, ], }) export class AssignmentSharedModule { From ffef5c6629115d3ed12a67cea2eb84a802d831f8 Mon Sep 17 00:00:00 2001 From: Adrian Kunz Date: Fri, 27 Oct 2023 20:42:14 +0200 Subject: [PATCH 10/13] feat(frontend): Edit course members --- .../modules/course/share/share.component.html | 4 +++- .../modules/course/share/share.component.ts | 18 +++++++++++++++--- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/frontend/src/app/assignment/modules/course/share/share.component.html b/frontend/src/app/assignment/modules/course/share/share.component.html index 7eb4aec03..1d85323eb 100644 --- a/frontend/src/app/assignment/modules/course/share/share.component.html +++ b/frontend/src/app/assignment/modules/course/share/share.component.html @@ -1,4 +1,6 @@
+ +
Student Link
@@ -6,7 +8,7 @@
You can now give your students the following link:

- +
diff --git a/frontend/src/app/assignment/modules/course/share/share.component.ts b/frontend/src/app/assignment/modules/course/share/share.component.ts index 4f98aa85b..a65200498 100644 --- a/frontend/src/app/assignment/modules/course/share/share.component.ts +++ b/frontend/src/app/assignment/modules/course/share/share.component.ts @@ -1,19 +1,31 @@ import {DOCUMENT} from '@angular/common'; -import {Component, Inject} from '@angular/core'; +import {Component, Inject, OnInit} from '@angular/core'; import {ActivatedRoute} from '@angular/router'; +import {CourseService} from "../../../services/course.service"; +import {switchMap} from "rxjs/operators"; +import Course from "../../../model/course"; @Component({ selector: 'app-assignment-share', templateUrl: './share.component.html', styleUrls: ['./share.component.scss'], }) -export class ShareComponent { +export class ShareComponent implements OnInit { + course?: Course; + readonly origin: string; constructor( - public readonly route: ActivatedRoute, + private courseService: CourseService, + private route: ActivatedRoute, @Inject(DOCUMENT) document: Document, ) { this.origin = document.location.origin; } + + ngOnInit() { + this.route.params.pipe( + switchMap(({cid}) => this.courseService.get(cid)), + ).subscribe(course => this.course = course); + } } From f9309c82bb03e779e45b925ad074d04b1e28f1b8 Mon Sep 17 00:00:00 2001 From: Adrian Kunz Date: Fri, 27 Oct 2023 20:49:08 +0200 Subject: [PATCH 11/13] feat(frontend): Adapt course share page to assignments --- .../modules/course/share/share.component.html | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/frontend/src/app/assignment/modules/course/share/share.component.html b/frontend/src/app/assignment/modules/course/share/share.component.html index 1d85323eb..e5ad5ad8e 100644 --- a/frontend/src/app/assignment/modules/course/share/share.component.html +++ b/frontend/src/app/assignment/modules/course/share/share.component.html @@ -1,15 +1,15 @@
- -
Student Link
-

- You can now give your students the following link: -

+
+ Share this link with your students. They will be able to submit solutions for this course. +
+
+
From 5d19a14d8aa531c5dcd32ea230d7e6fb5dd687b8 Mon Sep 17 00:00:00 2001 From: Adrian Kunz Date: Fri, 27 Oct 2023 20:49:25 +0200 Subject: [PATCH 12/13] feat(assignments-service): Find courses by members --- .../assignments/src/course/course.controller.ts | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/services/apps/assignments/src/course/course.controller.ts b/services/apps/assignments/src/course/course.controller.ts index 77f31d310..779ce5448 100644 --- a/services/apps/assignments/src/course/course.controller.ts +++ b/services/apps/assignments/src/course/course.controller.ts @@ -1,11 +1,13 @@ import {Auth, AuthUser, UserToken} from '@app/keycloak-auth'; import {NotFound} from '@mean-stream/nestx'; -import {Body, Controller, Delete, Get, Param, Patch, Post, Query} from '@nestjs/common'; +import {Body, Controller, Delete, Get, Param, ParseArrayPipe, Patch, Post, Query} from '@nestjs/common'; import {ApiCreatedResponse, ApiOkResponse, ApiOperation, ApiTags} from '@nestjs/swagger'; import {CourseStudent, CreateCourseDto, UpdateCourseDto} from './course.dto'; import {Course} from './course.schema'; import {CourseService} from './course.service'; import {CourseAuth} from "../course-member/course-auth.decorator"; +import {FilterQuery} from "mongoose"; +import {MemberService} from "@app/member"; const forbiddenResponse = 'Not owner.'; @@ -14,6 +16,7 @@ const forbiddenResponse = 'Not owner.'; export class CourseController { constructor( private readonly courseService: CourseService, + private readonly memberService: MemberService, ) { } @@ -31,8 +34,17 @@ export class CourseController { @ApiOkResponse({type: [Course]}) async findAll( @Query('createdBy') createdBy?: string, + @Query('members', new ParseArrayPipe({optional: true})) memberIds?: string[], ): Promise { - return this.courseService.findAll({createdBy}); + const filter: FilterQuery = {}; + if (createdBy) { + (filter.$or ||= []).push({createdBy}); + } + if (memberIds) { + const members = await this.memberService.findAll({user: {$in: memberIds}}); + (filter.$or ||= []).push({_id: {$in: members.map(m => m.parent)}}); + } + return this.courseService.findAll(filter); } @Get(':id') From 2ae96453d1f35e47b686d7a175b50aeeda91de55 Mon Sep 17 00:00:00 2001 From: Adrian Kunz Date: Fri, 27 Oct 2023 20:58:14 +0200 Subject: [PATCH 13/13] feat(frontend): Find my courses as member --- frontend/src/app/assignment/services/course.service.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/frontend/src/app/assignment/services/course.service.ts b/frontend/src/app/assignment/services/course.service.ts index 3f8631494..5e0e458b0 100644 --- a/frontend/src/app/assignment/services/course.service.ts +++ b/frontend/src/app/assignment/services/course.service.ts @@ -73,7 +73,12 @@ export class CourseService { getOwn(): Observable { return this.userService.getCurrent().pipe( - switchMap(user => user ? this.http.get(`${environment.assignmentsApiUrl}/courses`, {params: {createdBy: user.id!}}) : of([])), + switchMap(user => user ? this.http.get(`${environment.assignmentsApiUrl}/courses`, { + params: { + createdBy: user.id!, + members: [user.id!], + }, + }) : of([])), ); } }