From 26c0c0efc7ac749fd8808821671c02e814303e51 Mon Sep 17 00:00:00 2001 From: Anand Chowdhary Date: Sat, 24 Oct 2020 11:05:43 +0530 Subject: [PATCH] :sparkles: Add group membership controller --- .../memberships-group.controller.ts | 75 +++++++++++++++++++ .../memberships-user.controller.ts | 8 +- src/modules/memberships/memberships.dto.ts | 8 ++ src/modules/memberships/memberships.module.ts | 3 +- .../memberships/memberships.service.ts | 73 ++++++++++++++---- 5 files changed, 149 insertions(+), 18 deletions(-) create mode 100644 src/modules/memberships/memberships-group.controller.ts create mode 100644 src/modules/memberships/memberships.dto.ts diff --git a/src/modules/memberships/memberships-group.controller.ts b/src/modules/memberships/memberships-group.controller.ts new file mode 100644 index 000000000..26402d9b9 --- /dev/null +++ b/src/modules/memberships/memberships-group.controller.ts @@ -0,0 +1,75 @@ +import { + Body, + Controller, + Delete, + Get, + Param, + ParseIntPipe, + Patch, + Query, +} from '@nestjs/common'; +import { memberships } from '@prisma/client'; +import { Expose } from 'src/modules/prisma/prisma.interface'; +import { CursorPipe } from 'src/pipes/cursor.pipe'; +import { OptionalIntPipe } from 'src/pipes/optional-int.pipe'; +import { OrderByPipe } from 'src/pipes/order-by.pipe'; +import { WherePipe } from 'src/pipes/where.pipe'; +import { Scopes } from '../auth/scope.decorator'; +import { UpdateMembershipDto } from './memberships.dto'; +import { MembershipsService } from './memberships.service'; + +@Controller('groups/:groupId/memberships') +export class GroupMembershipController { + constructor(private membershipsService: MembershipsService) {} + + @Get() + @Scopes('group-{groupId}:read-membership') + async getAll( + @Param('groupId', ParseIntPipe) groupId: number, + @Query('skip', OptionalIntPipe) skip?: number, + @Query('take', OptionalIntPipe) take?: number, + @Query('cursor', CursorPipe) cursor?: Record, + @Query('where', WherePipe) where?: Record, + @Query('orderBy', OrderByPipe) orderBy?: Record, + ): Promise[]> { + return this.membershipsService.getMemberships({ + skip, + take, + orderBy, + cursor, + where: { ...where, group: { id: groupId } }, + }); + } + + @Get(':id') + @Scopes('group-{groupId}:read-membership-{id}') + async get( + @Param('groupId', ParseIntPipe) groupId: number, + @Param('id', ParseIntPipe) id: number, + ): Promise> { + return this.membershipsService.getGroupMembership(groupId, Number(id)); + } + + @Patch(':id') + @Scopes('group-{groupId}:read-membership-{id}') + async update( + @Body() data: UpdateMembershipDto, + @Param('groupId', ParseIntPipe) groupId: number, + @Param('id', ParseIntPipe) id: number, + ): Promise> { + return this.membershipsService.updateGroupMembership( + groupId, + Number(id), + data, + ); + } + + @Delete(':id') + @Scopes('group-{groupId}:delete-membership-{id}') + async remove( + @Param('groupId', ParseIntPipe) groupId: number, + @Param('id', ParseIntPipe) id: number, + ): Promise> { + return this.membershipsService.deleteGroupMembership(groupId, Number(id)); + } +} diff --git a/src/modules/memberships/memberships-user.controller.ts b/src/modules/memberships/memberships-user.controller.ts index dbc04e69f..0f7870856 100644 --- a/src/modules/memberships/memberships-user.controller.ts +++ b/src/modules/memberships/memberships-user.controller.ts @@ -29,12 +29,12 @@ export class UserMembershipController { @Query('where', WherePipe) where?: Record, @Query('orderBy', OrderByPipe) orderBy?: Record, ): Promise[]> { - return this.membershipsService.getMemberships(userId, { + return this.membershipsService.getMemberships({ skip, take, orderBy, cursor, - where, + where: { ...where, user: { id: userId } }, }); } @@ -44,7 +44,7 @@ export class UserMembershipController { @Param('userId', ParseIntPipe) userId: number, @Param('id', ParseIntPipe) id: number, ): Promise> { - return this.membershipsService.getMembership(userId, Number(id)); + return this.membershipsService.getUserMembership(userId, Number(id)); } @Delete(':id') @@ -53,6 +53,6 @@ export class UserMembershipController { @Param('userId', ParseIntPipe) userId: number, @Param('id', ParseIntPipe) id: number, ): Promise> { - return this.membershipsService.deleteMembership(userId, Number(id)); + return this.membershipsService.deleteUserMembership(userId, Number(id)); } } diff --git a/src/modules/memberships/memberships.dto.ts b/src/modules/memberships/memberships.dto.ts new file mode 100644 index 000000000..1f8b8bb96 --- /dev/null +++ b/src/modules/memberships/memberships.dto.ts @@ -0,0 +1,8 @@ +import { IsIn, IsOptional, IsString } from 'class-validator'; + +export class UpdateMembershipDto { + @IsString() + @IsIn(['OWNER', 'ADMIN', 'MEMBER']) + @IsOptional() + role?: 'OWNER' | 'ADMIN' | 'MEMBER'; +} diff --git a/src/modules/memberships/memberships.module.ts b/src/modules/memberships/memberships.module.ts index e78fb7fa5..3c7c21542 100644 --- a/src/modules/memberships/memberships.module.ts +++ b/src/modules/memberships/memberships.module.ts @@ -1,11 +1,12 @@ import { Module } from '@nestjs/common'; import { PrismaModule } from '../prisma/prisma.module'; +import { GroupMembershipController } from './memberships-group.controller'; import { UserMembershipController } from './memberships-user.controller'; import { MembershipsService } from './memberships.service'; @Module({ imports: [PrismaModule], - controllers: [UserMembershipController], + controllers: [UserMembershipController, GroupMembershipController], providers: [MembershipsService], }) export class MembershipsModule {} diff --git a/src/modules/memberships/memberships.service.ts b/src/modules/memberships/memberships.service.ts index 96ef04a11..91a59bdd9 100644 --- a/src/modules/memberships/memberships.service.ts +++ b/src/modules/memberships/memberships.service.ts @@ -7,6 +7,7 @@ import { import { memberships, membershipsOrderByInput, + membershipsUpdateInput, membershipsWhereInput, membershipsWhereUniqueInput, } from '@prisma/client'; @@ -16,29 +17,26 @@ import { PrismaService } from '../prisma/prisma.service'; @Injectable() export class MembershipsService { constructor(private prisma: PrismaService) {} - async getMemberships( - userId: number, - params: { - skip?: number; - take?: number; - cursor?: membershipsWhereUniqueInput; - where?: membershipsWhereInput; - orderBy?: membershipsOrderByInput; - }, - ): Promise[]> { + async getMemberships(params: { + skip?: number; + take?: number; + cursor?: membershipsWhereUniqueInput; + where?: membershipsWhereInput; + orderBy?: membershipsOrderByInput; + }): Promise[]> { const { skip, take, cursor, where, orderBy } = params; const memberships = await this.prisma.memberships.findMany({ skip, take, cursor, - where: { ...where, user: { id: userId } }, + where, orderBy, include: { group: true }, }); return memberships.map(user => this.prisma.expose(user)); } - async getMembership( + async getUserMembership( userId: number, id: number, ): Promise | null> { @@ -52,7 +50,21 @@ export class MembershipsService { return this.prisma.expose(membership); } - async deleteMembership( + async getGroupMembership( + groupId: number, + id: number, + ): Promise | null> { + const membership = await this.prisma.memberships.findOne({ + where: { id }, + include: { group: true }, + }); + if (!membership) + throw new HttpException('Membership not found', HttpStatus.NOT_FOUND); + if (membership.groupId !== groupId) throw new UnauthorizedException(); + return this.prisma.expose(membership); + } + + async deleteUserMembership( userId: number, id: number, ): Promise> { @@ -69,6 +81,41 @@ export class MembershipsService { return this.prisma.expose(membership); } + async updateGroupMembership( + groupId: number, + id: number, + data: membershipsUpdateInput, + ): Promise> { + const testMembership = await this.prisma.memberships.findOne({ + where: { id }, + }); + if (!testMembership) + throw new HttpException('Membership not found', HttpStatus.NOT_FOUND); + if (testMembership.groupId !== groupId) throw new UnauthorizedException(); + const membership = await this.prisma.memberships.update({ + where: { id }, + data, + }); + return this.prisma.expose(membership); + } + + async deleteGroupMembership( + groupId: number, + id: number, + ): Promise> { + const testMembership = await this.prisma.memberships.findOne({ + where: { id }, + }); + if (!testMembership) + throw new HttpException('Membership not found', HttpStatus.NOT_FOUND); + if (testMembership.groupId !== groupId) throw new UnauthorizedException(); + await this.verifyDeleteMembership(testMembership.groupId, id); + const membership = await this.prisma.memberships.delete({ + where: { id }, + }); + return this.prisma.expose(membership); + } + /** Verify whether a group membership can be deleted */ async verifyDeleteMembership( groupId: number,