From 41ecf2d082657b0cd3edd68bff16f55347e08b3e Mon Sep 17 00:00:00 2001 From: Philipinho <16838612+Philipinho@users.noreply.github.com> Date: Wed, 28 Feb 2024 16:36:41 +0000 Subject: [PATCH] pagination - wip --- .../controllers/workspace.controller.ts | 16 ++++++--- .../workspace/services/workspace.service.ts | 35 ++++++++++++------- .../helpers/pagination/paginated-result.ts | 14 ++++++++ .../helpers/pagination/pagination-meta-dto.ts | 29 +++++++++++++++ .../helpers/pagination/pagination-options.ts | 23 ++++++++++++ apps/server/src/main.ts | 1 + 6 files changed, 102 insertions(+), 16 deletions(-) create mode 100644 apps/server/src/helpers/pagination/paginated-result.ts create mode 100644 apps/server/src/helpers/pagination/pagination-meta-dto.ts create mode 100644 apps/server/src/helpers/pagination/pagination-options.ts diff --git a/apps/server/src/core/workspace/controllers/workspace.controller.ts b/apps/server/src/core/workspace/controllers/workspace.controller.ts index c0229722..a9e14620 100644 --- a/apps/server/src/core/workspace/controllers/workspace.controller.ts +++ b/apps/server/src/core/workspace/controllers/workspace.controller.ts @@ -1,11 +1,14 @@ import { Body, Controller, + DefaultValuePipe, Delete, Get, HttpCode, HttpStatus, + ParseIntPipe, Post, + Query, UseGuards, } from '@nestjs/common'; import { WorkspaceService } from '../services/workspace.service'; @@ -20,9 +23,10 @@ import { AuthUser } from '../../../decorators/auth-user.decorator'; import { User } from '../../user/entities/user.entity'; import { CurrentWorkspace } from '../../../decorators/current-workspace.decorator'; import { Workspace } from '../entities/workspace.entity'; +import { PaginationOptions } from '../../../helpers/pagination/pagination-options'; @UseGuards(JwtGuard) -@Controller('workspace') +@Controller('workspaces') export class WorkspaceController { constructor(private readonly workspaceService: WorkspaceService) {} @@ -51,9 +55,13 @@ export class WorkspaceController { } @HttpCode(HttpStatus.OK) - @Get('members') - async getWorkspaceMembers(@CurrentWorkspace() workspace: Workspace) { - return this.workspaceService.getWorkspaceUsers(workspace.id); + @Post('members') + async getWorkspaceMembers( + @Body() + pagination: PaginationOptions, + @CurrentWorkspace() workspace: Workspace, + ) { + return this.workspaceService.getWorkspaceUsers(workspace.id, pagination); } @HttpCode(HttpStatus.OK) diff --git a/apps/server/src/core/workspace/services/workspace.service.ts b/apps/server/src/core/workspace/services/workspace.service.ts index eb36683a..dc11c28a 100644 --- a/apps/server/src/core/workspace/services/workspace.service.ts +++ b/apps/server/src/core/workspace/services/workspace.service.ts @@ -15,6 +15,10 @@ import { UpdateWorkspaceDto } from '../dto/update-workspace.dto'; import { DeleteWorkspaceDto } from '../dto/delete-workspace.dto'; import { UpdateWorkspaceUserRoleDto } from '../dto/update-workspace-user-role.dto'; import { SpaceService } from '../../space/space.service'; +import { UserWithRole } from '../../user/entities/user.entity'; +import { PaginationOptions } from '../../../helpers/pagination/pagination-options'; +import { PaginationMetaDto } from '../../../helpers/pagination/pagination-meta-dto'; +import { PaginatedResult } from '../../../helpers/pagination/paginated-result'; @Injectable() export class WorkspaceService { @@ -209,25 +213,32 @@ export class WorkspaceService { ); } - async getWorkspaceUsers(workspaceId: string) { - const workspace = await this.workspaceRepository.findOne({ - where: { id: workspaceId }, - relations: ['workspaceUsers', 'workspaceUsers.user'], - }); + async getWorkspaceUsers( + workspaceId: string, + paginationOptions: PaginationOptions, + ): Promise> { + const [workspaceUsers, count] = + await this.workspaceUserRepository.findAndCount({ + relations: ['user'], + where: { + workspace: { + id: workspaceId, + }, + }, + take: paginationOptions.limit, + skip: paginationOptions.skip, + }); - if (!workspace) { - throw new BadRequestException('Invalid workspace'); - } - - const users = workspace.workspaceUsers.map((workspaceUser) => { + const users = workspaceUsers.map((workspaceUser) => { workspaceUser.user.password = ''; return { ...workspaceUser.user, - workspaceRole: workspaceUser.role, + role: workspaceUser.role, }; }); - return { users }; + const paginationMeta = new PaginationMetaDto({ count, paginationOptions }); + return new PaginatedResult(users, paginationMeta); } async validateWorkspaceMember( diff --git a/apps/server/src/helpers/pagination/paginated-result.ts b/apps/server/src/helpers/pagination/paginated-result.ts new file mode 100644 index 00000000..e41aae5e --- /dev/null +++ b/apps/server/src/helpers/pagination/paginated-result.ts @@ -0,0 +1,14 @@ +import { IsArray } from 'class-validator'; +import { PaginationMetaDto } from './pagination-meta-dto'; + +export class PaginatedResult { + @IsArray() + readonly items: T[]; + + readonly pagination: PaginationMetaDto; + + constructor(items: T[], pagination: PaginationMetaDto) { + this.items = items; + this.pagination = pagination; + } +} diff --git a/apps/server/src/helpers/pagination/pagination-meta-dto.ts b/apps/server/src/helpers/pagination/pagination-meta-dto.ts new file mode 100644 index 00000000..9991bedb --- /dev/null +++ b/apps/server/src/helpers/pagination/pagination-meta-dto.ts @@ -0,0 +1,29 @@ +import { PaginationOptions } from './pagination-options'; + +export class PaginationMetaDto { + readonly page: number; + + readonly limit: number; + + readonly total: number; + + readonly pageCount: number; + + readonly hasPreviousPage: boolean; + + readonly hasNextPage: boolean; + + constructor({ count, paginationOptions }: PageMetaDtoParameters) { + this.page = paginationOptions.page; + this.limit = paginationOptions.limit; + this.total = count; + this.pageCount = Math.ceil(this.total / this.limit); + this.hasPreviousPage = this.page > 1; + this.hasNextPage = this.page < this.pageCount; + } +} + +export interface PageMetaDtoParameters { + count: number; + paginationOptions: PaginationOptions; +} diff --git a/apps/server/src/helpers/pagination/pagination-options.ts b/apps/server/src/helpers/pagination/pagination-options.ts new file mode 100644 index 00000000..15d5c5ed --- /dev/null +++ b/apps/server/src/helpers/pagination/pagination-options.ts @@ -0,0 +1,23 @@ +import { IsNumber, IsOptional, IsPositive, Max, Min } from 'class-validator'; + +export class PaginationOptions { + @IsOptional() + @IsNumber() + @Min(1) + page = 1; + + @IsOptional() + @IsNumber() + @IsPositive() + @Min(1) + @Max(100) + limit = 25; + + @IsOptional() + @IsNumber() + offset = 0; + + get skip(): number { + return (this.page - 1) * this.limit; + } +} diff --git a/apps/server/src/main.ts b/apps/server/src/main.ts index 664622db..717838b9 100644 --- a/apps/server/src/main.ts +++ b/apps/server/src/main.ts @@ -25,6 +25,7 @@ async function bootstrap() { new ValidationPipe({ whitelist: true, stopAtFirstError: true, + transform: true, }), ); app.enableCors();