From 4b9127676e1cd9948c8b45fafbc5474987c7bf3f Mon Sep 17 00:00:00 2001 From: Mhd Zayd Skaff Date: Mon, 21 Jul 2025 14:10:07 +0300 Subject: [PATCH] add pagination to device API --- libs/common/src/dto/pagination.request.dto.ts | 17 +------- .../dtos/bookable-space-request.dto.ts | 7 +-- .../controllers/community.controller.ts | 6 +-- src/community/dtos/community-filter.dto.ts | 20 +++++++++ src/device/dtos/get.device.dto.ts | 3 +- src/device/services/device.service.ts | 43 +++++++++++++------ src/project/dto/list-project.dto.ts | 6 +-- .../controllers/space-model.controller.ts | 14 +++--- .../dtos/space-model-filter.dto.ts | 20 +++++++++ .../subspace/subspace.controller.ts | 12 +++--- src/space/dtos/subspace-filter.dto.ts | 20 +++++++++ 11 files changed, 111 insertions(+), 57 deletions(-) create mode 100644 src/community/dtos/community-filter.dto.ts create mode 100644 src/space-model/dtos/space-model-filter.dto.ts create mode 100644 src/space/dtos/subspace-filter.dto.ts diff --git a/libs/common/src/dto/pagination.request.dto.ts b/libs/common/src/dto/pagination.request.dto.ts index 467fb61..4e2aa6f 100644 --- a/libs/common/src/dto/pagination.request.dto.ts +++ b/libs/common/src/dto/pagination.request.dto.ts @@ -1,24 +1,9 @@ import { ApiProperty } from '@nestjs/swagger'; -import { Transform } from 'class-transformer'; -import { IsBoolean, IsOptional } from 'class-validator'; -import { BooleanValues } from '../constants/boolean-values.enum'; +import { IsOptional } from 'class-validator'; import { IsPageRequestParam } from '../validators/is-page-request-param.validator'; import { IsSizeRequestParam } from '../validators/is-size-request-param.validator'; export class PaginationRequestGetListDto { - @ApiProperty({ - example: true, - description: 'include spaces', - required: false, - default: false, - }) - @IsOptional() - @IsBoolean() - @Transform((value) => { - return value.obj.includeSpaces === BooleanValues.TRUE; - }) - public includeSpaces?: boolean = false; - @IsOptional() @IsPageRequestParam({ message: 'Page must be bigger than 0', diff --git a/src/booking/dtos/bookable-space-request.dto.ts b/src/booking/dtos/bookable-space-request.dto.ts index e958aa0..d3480c2 100644 --- a/src/booking/dtos/bookable-space-request.dto.ts +++ b/src/booking/dtos/bookable-space-request.dto.ts @@ -1,13 +1,10 @@ import { BooleanValues } from '@app/common/constants/boolean-values.enum'; import { PaginationRequestWithSearchGetListDto } from '@app/common/dto/pagination-with-search.request.dto'; -import { ApiProperty, OmitType } from '@nestjs/swagger'; +import { ApiProperty } from '@nestjs/swagger'; import { Transform } from 'class-transformer'; import { IsBoolean, IsNotEmpty, IsOptional } from 'class-validator'; -export class BookableSpaceRequestDto extends OmitType( - PaginationRequestWithSearchGetListDto, - ['includeSpaces'], -) { +export class BookableSpaceRequestDto extends PaginationRequestWithSearchGetListDto { @ApiProperty({ type: Boolean, required: false, diff --git a/src/community/controllers/community.controller.ts b/src/community/controllers/community.controller.ts index a2b0ef2..f2cbbd3 100644 --- a/src/community/controllers/community.controller.ts +++ b/src/community/controllers/community.controller.ts @@ -17,10 +17,10 @@ import { CommunityService } from '../services/community.service'; // import { CheckUserCommunityGuard } from 'src/guards/user.community.guard'; import { ControllerRoute } from '@app/common/constants/controller-route'; import { BaseResponseDto } from '@app/common/dto/base.response.dto'; -import { PaginationRequestWithSearchGetListDto } from '@app/common/dto/pagination-with-search.request.dto'; import { Permissions } from 'src/decorators/permissions.decorator'; import { PermissionsGuard } from 'src/guards/permissions.guard'; import { ProjectParam } from '../dtos'; +import { CommunityFilterDto } from '../dtos/community-filter.dto'; @ApiTags('Community Module') @Controller({ @@ -55,7 +55,7 @@ export class CommunityController { @Get('v2') async getCommunitiesV2( @Param() param: ProjectParam, - @Query() query: PaginationRequestWithSearchGetListDto, + @Query() query: CommunityFilterDto, ): Promise { return this.communityService.getCommunitiesV2(param, query); } @@ -85,7 +85,7 @@ export class CommunityController { @Get() async getCommunities( @Param() param: ProjectParam, - @Query() query: PaginationRequestWithSearchGetListDto, + @Query() query: CommunityFilterDto, ): Promise { return this.communityService.getCommunities(param, query); } diff --git a/src/community/dtos/community-filter.dto.ts b/src/community/dtos/community-filter.dto.ts new file mode 100644 index 0000000..183fae6 --- /dev/null +++ b/src/community/dtos/community-filter.dto.ts @@ -0,0 +1,20 @@ +import { BooleanValues } from '@app/common/constants/boolean-values.enum'; +import { PaginationRequestWithSearchGetListDto } from '@app/common/dto/pagination-with-search.request.dto'; +import { ApiProperty } from '@nestjs/swagger'; +import { Transform } from 'class-transformer'; +import { IsBoolean, IsOptional } from 'class-validator'; + +export class CommunityFilterDto extends PaginationRequestWithSearchGetListDto { + @ApiProperty({ + example: true, + description: 'include spaces', + required: false, + default: false, + }) + @IsOptional() + @IsBoolean() + @Transform((value) => { + return value.obj.includeSpaces === BooleanValues.TRUE; + }) + public includeSpaces?: boolean = false; +} diff --git a/src/device/dtos/get.device.dto.ts b/src/device/dtos/get.device.dto.ts index 429273a..8707ff1 100644 --- a/src/device/dtos/get.device.dto.ts +++ b/src/device/dtos/get.device.dto.ts @@ -1,4 +1,5 @@ import { DeviceTypeEnum } from '@app/common/constants/device-type.enum'; +import { PaginationRequestGetListDto } from '@app/common/dto/pagination.request.dto'; import { ApiProperty } from '@nestjs/swagger'; import { Transform } from 'class-transformer'; import { @@ -65,7 +66,7 @@ export class GetDevicesBySpaceOrCommunityDto { requireEither?: never; // This ensures at least one of them is provided } -export class GetDevicesFilterDto { +export class GetDevicesFilterDto extends PaginationRequestGetListDto { @ApiProperty({ description: 'Device Type', enum: DeviceTypeEnum, diff --git a/src/device/services/device.service.ts b/src/device/services/device.service.ts index 9b3897c..eb6fbd5 100644 --- a/src/device/services/device.service.ts +++ b/src/device/services/device.service.ts @@ -7,6 +7,7 @@ import { CommonErrorCodes } from '@app/common/constants/error-codes.enum'; import { ProductType } from '@app/common/constants/product-type.enum'; import { SceneSwitchesTypeEnum } from '@app/common/constants/scene-switch-type.enum'; import { BaseResponseDto } from '@app/common/dto/base.response.dto'; +import { PageResponse } from '@app/common/dto/pagination.response.dto'; import { SuccessResponseDto } from '@app/common/dto/success.response.dto'; import { DeviceStatusFirebaseService } from '@app/common/firebase/devices-status/services/devices-status.service'; import { convertKeysToCamelCase } from '@app/common/helper/camelCaseConverter'; @@ -20,6 +21,7 @@ import { SceneDeviceRepository } from '@app/common/modules/scene-device/reposito import { SpaceEntity } from '@app/common/modules/space/entities/space.entity'; import { SpaceRepository } from '@app/common/modules/space/repositories'; import { addSpaceUuidToDevices } from '@app/common/util/device-utils'; +import { getPaginationResponseDto } from '@app/common/util/getPaginationResponseDto'; import { BadRequestException, forwardRef, @@ -100,7 +102,7 @@ export class DeviceService { async getAllDevices( param: ProjectParam, - { deviceType, spaces, communities }: GetDevicesFilterDto, + { deviceType, spaces, communities, page, size }: GetDevicesFilterDto, ): Promise { try { await this.validateProject(param.projectUuid); @@ -108,9 +110,12 @@ export class DeviceService { return await this.getDoorLockDevices(param.projectUuid, { spaces, communities, + deviceType, + page, + size, }); } else if (!deviceType) { - const devices = await this.deviceRepository.find({ + const [devices, count] = await this.deviceRepository.findAndCount({ where: { isActive: true, spaceDevice: { @@ -133,6 +138,8 @@ export class DeviceService { 'permission.permissionType', 'subspace', ], + take: size ?? 10, + skip: (page ? page - 1 : 0) * (size ?? 10), }); const devicesData = await Promise.allSettled( @@ -234,11 +241,14 @@ export class DeviceService { .value, ); - return new SuccessResponseDto({ - message: `Devices fetched successfully`, - data: fulfilledDevices, - statusCode: HttpStatus.OK, - }); + return new PageResponse( + { + message: `Devices fetched successfully`, + data: fulfilledDevices, + statusCode: HttpStatus.OK, + }, + getPaginationResponseDto(count, page ?? 1, size ?? 10), + ); } } catch (error) { if (error instanceof HttpException) { @@ -1301,11 +1311,11 @@ export class DeviceService { private async getDoorLockDevices( projectUuid: string, - { communities, spaces }: { spaces?: string[]; communities?: string[] }, + { communities, spaces, page, size }: GetDevicesFilterDto, ) { await this.validateProject(projectUuid); - const devices = await this.deviceRepository.find({ + const [devices, count] = await this.deviceRepository.findAndCount({ where: { productDevice: { prodType: ProductType.DL, @@ -1324,6 +1334,8 @@ export class DeviceService { isActive: true, }, relations: ['productDevice', 'spaceDevice'], + take: size ?? 10, + skip: (page ? page - 1 : 0) * (size ?? 10), }); const devicesData = await Promise.all( @@ -1355,11 +1367,14 @@ export class DeviceService { (deviceData) => deviceData !== null, ); - return new SuccessResponseDto({ - message: 'Successfully retrieved all pass devices', - data: filteredDevicesData, - statusCode: HttpStatus.OK, - }); + return new PageResponse( + { + message: 'Successfully retrieved all pass devices', + data: filteredDevicesData, + statusCode: HttpStatus.OK, + }, + getPaginationResponseDto(count, page ?? 1, size ?? 10), + ); } private async controlDeviceTuya( diff --git a/src/project/dto/list-project.dto.ts b/src/project/dto/list-project.dto.ts index 344c8c4..f0872b9 100644 --- a/src/project/dto/list-project.dto.ts +++ b/src/project/dto/list-project.dto.ts @@ -1,7 +1,3 @@ import { PaginationRequestGetListDto } from '@app/common/dto/pagination.request.dto'; -import { PickType } from '@nestjs/swagger'; -export class ListProjectsDto extends PickType(PaginationRequestGetListDto, [ - 'page', - 'size', -]) {} +export class ListProjectsDto extends PaginationRequestGetListDto {} diff --git a/src/space-model/controllers/space-model.controller.ts b/src/space-model/controllers/space-model.controller.ts index 6f23c3e..6a92462 100644 --- a/src/space-model/controllers/space-model.controller.ts +++ b/src/space-model/controllers/space-model.controller.ts @@ -1,4 +1,5 @@ import { ControllerRoute } from '@app/common/constants/controller-route'; +import { BaseResponseDto } from '@app/common/dto/base.response.dto'; import { Body, Controller, @@ -11,18 +12,17 @@ import { UseGuards, } from '@nestjs/common'; import { ApiBearerAuth, ApiOperation, ApiTags } from '@nestjs/swagger'; -import { SpaceModelService } from '../services'; +import { ProjectParam } from 'src/community/dtos'; +import { Permissions } from 'src/decorators/permissions.decorator'; +import { PermissionsGuard } from 'src/guards/permissions.guard'; import { CreateSpaceModelDto, LinkSpacesToModelDto, SpaceModelParam, UpdateSpaceModelDto, } from '../dtos'; -import { ProjectParam } from 'src/community/dtos'; -import { BaseResponseDto } from '@app/common/dto/base.response.dto'; -import { PermissionsGuard } from 'src/guards/permissions.guard'; -import { Permissions } from 'src/decorators/permissions.decorator'; -import { PaginationRequestGetListDto } from '@app/common/dto/pagination.request.dto'; +import { SpaceModelFilterDto } from '../dtos/space-model-filter.dto'; +import { SpaceModelService } from '../services'; @ApiTags('Space Model Module') @Controller({ @@ -62,7 +62,7 @@ export class SpaceModelController { @Get() async listSpaceModel( @Param() projectParam: ProjectParam, - @Query() query: PaginationRequestGetListDto, + @Query() query: SpaceModelFilterDto, ): Promise { return await this.spaceModelService.list(projectParam, query); } diff --git a/src/space-model/dtos/space-model-filter.dto.ts b/src/space-model/dtos/space-model-filter.dto.ts new file mode 100644 index 0000000..215b4f1 --- /dev/null +++ b/src/space-model/dtos/space-model-filter.dto.ts @@ -0,0 +1,20 @@ +import { BooleanValues } from '@app/common/constants/boolean-values.enum'; +import { PaginationRequestGetListDto } from '@app/common/dto/pagination.request.dto'; +import { ApiProperty } from '@nestjs/swagger'; +import { Transform } from 'class-transformer'; +import { IsBoolean, IsOptional } from 'class-validator'; + +export class SpaceModelFilterDto extends PaginationRequestGetListDto { + @ApiProperty({ + example: true, + description: 'include spaces', + required: false, + default: false, + }) + @IsOptional() + @IsBoolean() + @Transform((value) => { + return value.obj.includeSpaces === BooleanValues.TRUE; + }) + public includeSpaces?: boolean = false; +} diff --git a/src/space/controllers/subspace/subspace.controller.ts b/src/space/controllers/subspace/subspace.controller.ts index 3ed36af..625efe3 100644 --- a/src/space/controllers/subspace/subspace.controller.ts +++ b/src/space/controllers/subspace/subspace.controller.ts @@ -1,4 +1,5 @@ import { ControllerRoute } from '@app/common/constants/controller-route'; +import { BaseResponseDto } from '@app/common/dto/base.response.dto'; import { Body, Controller, @@ -10,13 +11,12 @@ import { Query, UseGuards, } from '@nestjs/common'; -import { SubSpaceService } from '../../services'; import { ApiBearerAuth, ApiOperation, ApiTags } from '@nestjs/swagger'; -import { AddSubspaceDto, GetSpaceParam, GetSubSpaceParam } from '../../dtos'; -import { BaseResponseDto } from '@app/common/dto/base.response.dto'; -import { PaginationRequestGetListDto } from '@app/common/dto/pagination.request.dto'; -import { PermissionsGuard } from 'src/guards/permissions.guard'; import { Permissions } from 'src/decorators/permissions.decorator'; +import { PermissionsGuard } from 'src/guards/permissions.guard'; +import { SubspaceFilterDto } from 'src/space/dtos/subspace-filter.dto'; +import { AddSubspaceDto, GetSpaceParam, GetSubSpaceParam } from '../../dtos'; +import { SubSpaceService } from '../../services'; @ApiTags('Space Module') @Controller({ @@ -51,7 +51,7 @@ export class SubSpaceController { @Get() async list( @Param() params: GetSpaceParam, - @Query() query: PaginationRequestGetListDto, + @Query() query: SubspaceFilterDto, ): Promise { return this.subSpaceService.list(params, query); } diff --git a/src/space/dtos/subspace-filter.dto.ts b/src/space/dtos/subspace-filter.dto.ts new file mode 100644 index 0000000..00b2ba4 --- /dev/null +++ b/src/space/dtos/subspace-filter.dto.ts @@ -0,0 +1,20 @@ +import { BooleanValues } from '@app/common/constants/boolean-values.enum'; +import { PaginationRequestGetListDto } from '@app/common/dto/pagination.request.dto'; +import { ApiProperty } from '@nestjs/swagger'; +import { Transform } from 'class-transformer'; +import { IsBoolean, IsOptional } from 'class-validator'; + +export class SubspaceFilterDto extends PaginationRequestGetListDto { + @ApiProperty({ + example: true, + description: 'include spaces', + required: false, + default: false, + }) + @IsOptional() + @IsBoolean() + @Transform((value) => { + return value.obj.includeSpaces === BooleanValues.TRUE; + }) + public includeSpaces?: boolean = false; +}