Skip to content

Commit

Permalink
Merge pull request #676 from bounswe/feature/BE/636/group
Browse files Browse the repository at this point in the history
Feature/be/636/group
  • Loading branch information
melihgezerr authored Dec 10, 2023
2 parents 93e15dc + 33c389d commit 4c81585
Show file tree
Hide file tree
Showing 21 changed files with 873 additions and 29 deletions.
8 changes: 8 additions & 0 deletions ludos/backend/src/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,10 @@ import { EntityRepository } from './repositories/entity.repository';
import { EntityController } from './controllers/entity.controller';
import { CompletionDuration } from './entities/completion-duration.entity';
import { CompletionDurationRepository } from './repositories/completion-duration.repository';
import { Group } from './entities/group.entity';
import { GroupController } from './controllers/group.controller';
import { GroupRepository } from './repositories/group.repository';
import { GroupService } from './services/group.service';

@Module({
imports: [
Expand Down Expand Up @@ -70,6 +74,7 @@ import { CompletionDurationRepository } from './repositories/completion-duration
Rating,
Comment,
Entity,
Group,
]),
],
controllers: [
Expand All @@ -82,6 +87,7 @@ import { CompletionDurationRepository } from './repositories/completion-duration
RatingController,
CommentController,
EntityController,
GroupController,
],
providers: [
SearchService,
Expand All @@ -102,6 +108,8 @@ import { CompletionDurationRepository } from './repositories/completion-duration
EntityService,
EntityRepository,
CompletionDurationRepository,
GroupRepository,
GroupService,
],
})
export class AppModule implements NestModule {
Expand Down
239 changes: 239 additions & 0 deletions ludos/backend/src/controllers/group.controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,239 @@
import {
Body,
Controller,
DefaultValuePipe,
Delete,
Get,
HttpCode,
HttpStatus,
Param,
ParseBoolPipe,
ParseIntPipe,
Post,
Put,
Query,
Req,
UseGuards,
UseInterceptors,
} from '@nestjs/common';
import {
ApiBadRequestResponse,
ApiBearerAuth,
ApiConflictResponse,
ApiCreatedResponse,
ApiForbiddenResponse,
ApiNotFoundResponse,
ApiOkResponse,
ApiOperation,
ApiQuery,
ApiTags,
} from '@nestjs/swagger';
import { GroupCreateDto } from '../dtos/group/request/create.dto';
import { GroupUpdateDto } from '../dtos/group/request/update.dto';
import { GroupCreateResponseDto } from '../dtos/group/response/create.response.dto';
import { GroupGetResponseDto } from '../dtos/group/response/get.response.dto';
import { GroupPageResponseDto } from '../dtos/group/response/page.response.dto';
import { Group } from '../entities/group.entity';
import { SerializerInterceptor } from '../interceptors/customSerializer.interceptor';
import { AuthorizedRequest } from '../interfaces/common/authorized-request.interface';
import { GroupService } from '../services/group.service';
import { AuthGuard } from '../services/guards/auth.guard';

@ApiTags('group')
@Controller('group')
export class GroupController {
constructor(private readonly groupService: GroupService) {}

@ApiCreatedResponse({
description: 'Group created successfully',
type: GroupCreateResponseDto,
})
@ApiBadRequestResponse({
description: 'Bad Request',
})
@ApiForbiddenResponse({
description: 'User should login',
})
@HttpCode(201)
@UseGuards(AuthGuard)
@UseInterceptors(new SerializerInterceptor(GroupCreateResponseDto))
@ApiOperation({ summary: 'Create Group Endpoint' })
@ApiBearerAuth()
@Post()
public async createGroup(
@Body() input: GroupCreateDto,
@Req() req: AuthorizedRequest,
) {
const createdGroup = await this.groupService.createGroup(
req.user.id,
input,
);
return createdGroup;
}

@ApiOkResponse({ type: GroupGetResponseDto })
@ApiOperation({ summary: 'Get Group by ID Endpoint' })
@ApiBearerAuth()
@UseInterceptors(new SerializerInterceptor(GroupGetResponseDto))
@Get(':id')
public async getGroup(
@Req() req: AuthorizedRequest,
@Param('id') id: string,
) {
return await this.groupService.getGroup(id, req.user && req.user.id);
}
@ApiBearerAuth()
@ApiOperation({ summary: 'Join a Group' })
@ApiNotFoundResponse({ description: 'Group is not found!' })
@ApiConflictResponse({ description: 'Group is already joined!' })
@ApiForbiddenResponse({ description: 'User should login' })
@UseGuards(AuthGuard)
@Put('/join/:groupId')
public async joinGroup(
@Req() req: AuthorizedRequest,
@Param('groupId') groupId: string,
) {
await this.groupService.joinGroup(req.user.id, groupId);
return HttpStatus.OK;
}

@ApiBearerAuth()
@ApiOperation({
summary: 'Leave a group, group admin can not leave the group',
})
@ApiNotFoundResponse({ description: 'Group is not found!' })
@ApiConflictResponse({ description: 'Group is not joined!' })
@ApiForbiddenResponse({ description: 'User should login' })
@UseGuards(AuthGuard)
@Put('/leave/:groupId')
public async leaveGroup(
@Req() req: AuthorizedRequest,
@Param('groupId') groupId: string,
) {
await this.groupService.leaveGroup(req.user.id, groupId);
return HttpStatus.OK;
}
@ApiBearerAuth()
@ApiOperation({ summary: 'Remove user from a group' })
@ApiNotFoundResponse({
description: 'Group is not found or User is not found',
})
@ApiConflictResponse({ description: 'Group is not joined!' })
@ApiForbiddenResponse({
description: 'User should login or User is the admin of the group',
})
@UseGuards(AuthGuard)
@Put('/remove/:groupId/:userId')
public async removeUserFromeGroup(
@Req() req: AuthorizedRequest,
@Param('groupId') groupId: string,
@Param('userId') userId: string,
) {
await this.groupService.removeUserFromGroup(req.user.id, userId, groupId);
return HttpStatus.OK;
}
@ApiOperation({ summary: 'List groups' })
@ApiQuery({ name: 'page', required: false, description: 'Default is 1' })
@ApiQuery({
name: 'limit',
required: false,
description: 'Limit the number of the items in the page. Default is 10',
})
@ApiQuery({
name: 'searchKey',
required: false,
description: 'Search by name',
})
@ApiQuery({
name: 'tags',
required: false,
description: 'Comma separated list of tags. This filter works like AND',
example: 'tag1,tag2,tag3',
})
@ApiQuery({ name: 'gameId', required: false })
@ApiQuery({ name: 'adminId', required: false })
@ApiQuery({
name: 'orderByKey',
required: false,
type: 'string',
description:
'A group field that will be used for ordering the items. Default is id',
example: 'name',
})
@ApiQuery({
name: 'order',
required: false,
description: 'ASC or DESC. Default is ASC',
example: 'ASC',
})
@ApiQuery({
name: 'isJoined',
required: false,
description: 'Filter by joined groups. If false no filter is applied',
example: 'true',
})
@ApiOkResponse({
type: GroupPageResponseDto,
})
@ApiBearerAuth()
@UseInterceptors(new SerializerInterceptor(GroupPageResponseDto))
@Get()
public async listGroups(
@Req() req: AuthorizedRequest,
@Query('page', new DefaultValuePipe(1), ParseIntPipe) page: number = 1,
@Query('limit', new DefaultValuePipe(10), ParseIntPipe) limit: number = 10,
@Query('searchKey') searchKey?: string,
@Query('tags') tags?: string,
@Query('gameId') gameId?: string,
@Query('adminId') adminId?: string,
@Query('orderByKey') orderByKey?: keyof Group,
@Query('order') order?: 'ASC' | 'DESC',
@Query('isJoined', new DefaultValuePipe(false), ParseBoolPipe)
isJoined?: boolean,
) {
return await this.groupService.listGroups(
page,
limit,
searchKey,
tags,
gameId,
adminId,
orderByKey,
order,
req.user && req.user.id,
isJoined,
);
}

@ApiOkResponse()
@ApiOperation({ summary: 'Edit Group Endpoint' })
@ApiBadRequestResponse({
description: 'Bad Request',
})
@ApiNotFoundResponse({ description: 'Group is not found' })
@ApiBearerAuth()
@UseGuards(AuthGuard)
@Put(':groupId')
public async editGroup(
@Body() input: GroupUpdateDto,
@Param('groupId') groupId: string,
@Req() req: AuthorizedRequest,
) {
await this.groupService.updateGroup(groupId, req.user.id, input);
return HttpStatus.OK;
}

@ApiOkResponse()
@ApiOperation({ summary: 'Delete Group Endpoint' })
@ApiNotFoundResponse({ description: 'Group is not found' })
@ApiBearerAuth()
@UseGuards(AuthGuard)
@Delete(':groupId')
public async deleteGroup(
@Param('groupId') groupId: string,
@Req() req: AuthorizedRequest,
) {
await this.groupService.deleteGroup(groupId, req.user.id);
return HttpStatus.OK;
}
}
7 changes: 7 additions & 0 deletions ludos/backend/src/controllers/post.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,11 @@ export class PostController {
example: 'tag1,tag2,tag3',
})
@ApiQuery({ name: 'gameId', required: false })
@ApiQuery({
name: 'groupId',
required: false,
description: 'If not specified, posts with group id is null will be listed',
})
@ApiQuery({ name: 'ownerUserId', required: false })
@ApiQuery({
name: 'isLiked',
Expand Down Expand Up @@ -204,6 +209,7 @@ export class PostController {
@Query('searchKey') searchKey?: string,
@Query('tags') tags?: string,
@Query('gameId') gameId?: string,
@Query('groupId') groupId?: string,
@Query('ownerUserId') ownerUserId?: string,
@Query('isLiked', new DefaultValuePipe(false), ParseBoolPipe)
isLiked?: boolean,
Expand All @@ -218,6 +224,7 @@ export class PostController {
searchKey,
tags,
gameId,
groupId,
ownerUserId,
req.user && req.user.id,
isLiked,
Expand Down
44 changes: 44 additions & 0 deletions ludos/backend/src/dtos/group/request/create.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { ApiProperty } from '@nestjs/swagger';
import {
IsArray,
IsNotEmpty,
IsNumber,
IsOptional,
IsString,
IsUUID,
} from 'class-validator';

export class GroupCreateDto {
@ApiProperty()
@IsNotEmpty()
@IsUUID()
gameId: string;

@ApiProperty()
@IsNotEmpty()
@IsNumber()
maxNumberOfMembers: number;

@ApiProperty()
@IsNotEmpty()
@IsString()
description: string;

@ApiProperty()
@IsNotEmpty()
@IsString()
name: string;

@ApiProperty()
@IsNotEmpty()
@IsString()
logo: string;

@ApiProperty({
description: 'Optional list of tags',
required: false,
})
@IsArray()
@IsOptional()
tags?: string[];
}
32 changes: 32 additions & 0 deletions ludos/backend/src/dtos/group/request/update.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { ApiProperty } from '@nestjs/swagger';
import { IsArray, IsNumber, IsOptional, IsString } from 'class-validator';

export class GroupUpdateDto {
@ApiProperty({ required: false })
@IsOptional()
@IsNumber()
maxNumberOfMembers: number;

@ApiProperty({ required: false })
@IsOptional()
@IsString()
description: string;

@ApiProperty({ required: false })
@IsOptional()
@IsString()
name: string;

@ApiProperty({ required: false })
@IsOptional()
@IsString()
logo: string;

@ApiProperty({
description: 'Optional list of tags',
required: false,
})
@IsArray()
@IsOptional()
tags?: string[];
}
Loading

0 comments on commit 4c81585

Please sign in to comment.