diff --git a/libs/common/src/constants/controller-route.ts b/libs/common/src/constants/controller-route.ts index 2aff604..bd37317 100644 --- a/libs/common/src/constants/controller-route.ts +++ b/libs/common/src/constants/controller-route.ts @@ -35,7 +35,7 @@ export class ControllerRoute { }; static COMMUNITY = class { - public static readonly ROUTE = 'communities'; + public static readonly ROUTE = '/projects/:projectUuid/communities'; static ACTIONS = class { public static readonly GET_COMMUNITY_BY_ID_SUMMARY = 'Get community by community community uuid'; @@ -140,7 +140,8 @@ export class ControllerRoute { }; static SPACE = class { - public static readonly ROUTE = '/communities/:communityUuid/spaces'; + public static readonly ROUTE = + '/projects/:projectUuid/communities/:communityUuid/spaces'; static ACTIONS = class { public static readonly CREATE_SPACE_SUMMARY = 'Create a new space'; public static readonly CREATE_SPACE_DESCRIPTION = @@ -181,7 +182,7 @@ export class ControllerRoute { static SPACE_SCENE = class { public static readonly ROUTE = - '/communities/:communityUuid/spaces/:spaceUuid/scenes'; + '/projects/:projectUuid/communities/:communityUuid/spaces/:spaceUuid/scenes'; static ACTIONS = class { public static readonly GET_TAP_TO_RUN_SCENE_BY_SPACE_SUMMARY = 'Retrieve Tap-to-Run Scenes by Space'; @@ -192,7 +193,7 @@ export class ControllerRoute { static SPACE_USER = class { public static readonly ROUTE = - '/communities/:communityUuid/spaces/:spaceUuid/user'; + '/projects/:projectUuid/communities/:communityUuid/spaces/:spaceUuid/user'; static ACTIONS = class { public static readonly ASSOCIATE_SPACE_USER_SUMMARY = 'Associate a user to a space'; @@ -208,7 +209,7 @@ export class ControllerRoute { static SPACE_DEVICES = class { public static readonly ROUTE = - '/communities/:communityUuid/spaces/:spaceUuid/devices'; + '/projects/:projectUuid/communities/:communityUuid/spaces/:spaceUuid/devices'; static ACTIONS = class { public static readonly LIST_SPACE_DEVICE_SUMMARY = 'List devices in a space'; @@ -219,7 +220,7 @@ export class ControllerRoute { static SUBSPACE = class { public static readonly ROUTE = - '/communities/:communityUuid/spaces/:spaceUuid/subspaces'; + '/projects/:projectUuid/communities/:communityUuid/spaces/:spaceUuid/subspaces'; static ACTIONS = class { public static readonly CREATE_SUBSPACE_SUMMARY = 'Create Subspace'; public static readonly CREATE_SUBSPACE_DESCRIPTION = @@ -245,7 +246,7 @@ export class ControllerRoute { static SUBSPACE_DEVICE = class { public static readonly ROUTE = - '/communities/:communityUuid/spaces/:spaceUuid/subspaces/:subSpaceUuid/devices'; + '/projects/:projectUuid/communities/:communityUuid/spaces/:spaceUuid/subspaces/:subSpaceUuid/devices'; static ACTIONS = class { public static readonly LIST_SUBSPACE_DEVICE_SUMMARY = diff --git a/libs/common/src/modules/community/entities/community.entity.ts b/libs/common/src/modules/community/entities/community.entity.ts index 3b6f122..8809f94 100644 --- a/libs/common/src/modules/community/entities/community.entity.ts +++ b/libs/common/src/modules/community/entities/community.entity.ts @@ -1,7 +1,8 @@ -import { Column, Entity, OneToMany, Unique } from 'typeorm'; +import { Column, Entity, ManyToOne, OneToMany, Unique } from 'typeorm'; import { AbstractEntity } from '../../abstract/entities/abstract.entity'; import { CommunityDto } from '../dtos'; import { SpaceEntity } from '../../space/entities'; +import { ProjectEntity } from '../../project/entities'; @Entity({ name: 'community' }) @Unique(['name']) @@ -31,4 +32,9 @@ export class CommunityEntity extends AbstractEntity { nullable: true, }) externalId: string; + + @ManyToOne(() => ProjectEntity, (project) => project.communities, { + nullable: true, + }) + project: ProjectEntity; } diff --git a/libs/common/src/modules/project/entities/project.entity.ts b/libs/common/src/modules/project/entities/project.entity.ts index 6c18b6c..04e3f11 100644 --- a/libs/common/src/modules/project/entities/project.entity.ts +++ b/libs/common/src/modules/project/entities/project.entity.ts @@ -1,6 +1,7 @@ -import { Entity, Column, Unique } from 'typeorm'; +import { Entity, Column, Unique, OneToMany } from 'typeorm'; import { AbstractEntity } from '../../abstract/entities/abstract.entity'; import { ProjectDto } from '../dtos'; +import { CommunityEntity } from '../../community/entities'; @Entity({ name: 'project' }) @Unique(['name']) @@ -19,6 +20,9 @@ export class ProjectEntity extends AbstractEntity { @Column({ length: 255, nullable: true }) description: string; + + @OneToMany(() => CommunityEntity, (community) => community.project) + communities: CommunityEntity[]; constructor(partial: Partial) { super(); diff --git a/libs/common/src/modules/project/project.repository.module.ts b/libs/common/src/modules/project/project.repository.module.ts index 0b744f8..d067af1 100644 --- a/libs/common/src/modules/project/project.repository.module.ts +++ b/libs/common/src/modules/project/project.repository.module.ts @@ -8,4 +8,4 @@ import { ProjectEntity } from './entities'; controllers: [], imports: [TypeOrmModule.forFeature([ProjectEntity])], }) -export class ProjectEntityModule {} +export class ProjectRepositoryModule {} diff --git a/src/community/community.module.ts b/src/community/community.module.ts index 106d9d1..a87ef85 100644 --- a/src/community/community.module.ts +++ b/src/community/community.module.ts @@ -9,6 +9,7 @@ import { UserRepositoryModule } from '@app/common/modules/user/user.repository.m import { SpacePermissionService } from '@app/common/helper/services'; import { CommunityRepository } from '@app/common/modules/community/repositories'; import { TuyaService } from '@app/common/integrations/tuya/services/tuya.service'; +import { ProjectRepository } from '@app/common/modules/project/repositiories'; @Module({ imports: [ConfigModule, SpaceRepositoryModule, UserRepositoryModule], @@ -20,6 +21,7 @@ import { TuyaService } from '@app/common/integrations/tuya/services/tuya.service TuyaService, CommunityRepository, SpacePermissionService, + ProjectRepository, ], exports: [CommunityService, SpacePermissionService], }) diff --git a/src/community/controllers/community.controller.ts b/src/community/controllers/community.controller.ts index 86bad74..5548e84 100644 --- a/src/community/controllers/community.controller.ts +++ b/src/community/controllers/community.controller.ts @@ -19,6 +19,7 @@ import { JwtAuthGuard } from '@app/common/guards/jwt.auth.guard'; import { ControllerRoute } from '@app/common/constants/controller-route'; import { BaseResponseDto } from '@app/common/dto/base.response.dto'; import { PaginationRequestGetListDto } from '@app/common/dto/pagination.request.dto'; +import { ProjectParam } from '../dtos'; @ApiTags('Community Module') @Controller({ @@ -36,9 +37,10 @@ export class CommunityController { description: ControllerRoute.COMMUNITY.ACTIONS.CREATE_COMMUNITY_DESCRIPTION, }) async createCommunity( + @Param() param: ProjectParam, @Body() addCommunityDto: AddCommunityDto, ): Promise { - return await this.communityService.createCommunity(addCommunityDto); + return await this.communityService.createCommunity(param, addCommunityDto); } @ApiBearerAuth() @@ -52,7 +54,7 @@ export class CommunityController { async getCommunityByUuid( @Param() params: GetCommunityParams, ): Promise { - return await this.communityService.getCommunityById(params.communityUuid); + return await this.communityService.getCommunityById(params); } @ApiBearerAuth() @@ -63,9 +65,10 @@ export class CommunityController { }) @Get() async getCommunities( + @Param() param: ProjectParam, @Query() query: PaginationRequestGetListDto, ): Promise { - return this.communityService.getCommunities(query); + return this.communityService.getCommunities(param, query); } @ApiBearerAuth() @@ -76,13 +79,10 @@ export class CommunityController { }) @Put('/:communityUuid') async updateCommunity( - @Param() param: GetCommunityParams, + @Param() params: GetCommunityParams, @Body() updateCommunityDto: UpdateCommunityNameDto, ) { - return this.communityService.updateCommunity( - param.communityUuid, - updateCommunityDto, - ); + return this.communityService.updateCommunity(params, updateCommunityDto); } @ApiBearerAuth() @@ -93,8 +93,8 @@ export class CommunityController { description: ControllerRoute.COMMUNITY.ACTIONS.DELETE_COMMUNITY_DESCRIPTION, }) async deleteCommunity( - @Param() param: GetCommunityParams, + @Param() params: GetCommunityParams, ): Promise { - return this.communityService.deleteCommunity(param.communityUuid); + return this.communityService.deleteCommunity(params); } } diff --git a/src/community/dtos/get.community.dto.ts b/src/community/dtos/get.community.dto.ts index fe2cf46..d6da53a 100644 --- a/src/community/dtos/get.community.dto.ts +++ b/src/community/dtos/get.community.dto.ts @@ -10,6 +10,7 @@ import { IsUUID, Min, } from 'class-validator'; +import { ProjectParam } from './project.param.dto'; export class GetCommunityDto { @ApiProperty({ @@ -21,7 +22,7 @@ export class GetCommunityDto { public communityUuid: string; } -export class GetCommunityParams { +export class GetCommunityParams extends ProjectParam { @ApiProperty({ description: 'Community id of the specific community', required: true, diff --git a/src/community/dtos/index.ts b/src/community/dtos/index.ts index 7119b23..34d8bbb 100644 --- a/src/community/dtos/index.ts +++ b/src/community/dtos/index.ts @@ -1 +1,3 @@ export * from './add.community.dto'; +export * from './project.param.dto'; +export * from './get.community.dto'; diff --git a/src/community/dtos/project.param.dto.ts b/src/community/dtos/project.param.dto.ts new file mode 100644 index 0000000..8bb2929 --- /dev/null +++ b/src/community/dtos/project.param.dto.ts @@ -0,0 +1,11 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsUUID } from 'class-validator'; + +export class ProjectParam { + @ApiProperty({ + description: 'UUID of the project this community belongs to', + example: 'd290f1ee-6c54-4b01-90e6-d701748f0851', + }) + @IsUUID() + projectUuid: string; +} diff --git a/src/community/services/community.service.ts b/src/community/services/community.service.ts index e833416..c21d20a 100644 --- a/src/community/services/community.service.ts +++ b/src/community/services/community.service.ts @@ -1,5 +1,5 @@ import { Injectable, HttpException, HttpStatus } from '@nestjs/common'; -import { AddCommunityDto } from '../dtos'; +import { AddCommunityDto, GetCommunityParams, ProjectParam } from '../dtos'; import { UpdateCommunityNameDto } from '../dtos/update.community.dto'; import { BaseResponseDto } from '@app/common/dto/base.response.dto'; import { @@ -11,17 +11,24 @@ import { CommunityRepository } from '@app/common/modules/community/repositories' import { CommunityDto } from '@app/common/modules/community/dtos'; import { SuccessResponseDto } from '@app/common/dto/success.response.dto'; import { TuyaService } from '@app/common/integrations/tuya/services/tuya.service'; +import { ProjectRepository } from '@app/common/modules/project/repositiories'; @Injectable() export class CommunityService { constructor( private readonly communityRepository: CommunityRepository, + private readonly projectRepository: ProjectRepository, private readonly tuyaService: TuyaService, ) {} - async createCommunity(dto: AddCommunityDto): Promise { + async createCommunity( + param: ProjectParam, + dto: AddCommunityDto, + ): Promise { const { name, description } = dto; + const project = await this.validateProject(param.projectUuid); + const existingCommunity = await this.communityRepository.findOneBy({ name, }); @@ -36,6 +43,7 @@ export class CommunityService { const community = this.communityRepository.create({ name: name, description: description, + project: project, }); // Save the community to the database @@ -54,7 +62,11 @@ export class CommunityService { } } - async getCommunityById(communityUuid: string): Promise { + async getCommunityById(params: GetCommunityParams): Promise { + const { communityUuid, projectUuid } = params; + + await this.validateProject(projectUuid); + const community = await this.communityRepository.findOneBy({ uuid: communityUuid, }); @@ -75,9 +87,13 @@ export class CommunityService { } async getCommunities( + param: ProjectParam, pageable: Partial, ): Promise { + await this.validateProject(param.projectUuid); + pageable.modelName = 'community'; + pageable.where = { project: { uuid: param.projectUuid } }; const customModel = TypeORMCustomModel(this.communityRepository); @@ -91,9 +107,13 @@ export class CommunityService { } async updateCommunity( - communityUuid: string, + params: GetCommunityParams, updateCommunityDto: UpdateCommunityNameDto, ): Promise { + const { communityUuid, projectUuid } = params; + + await this.validateProject(projectUuid); + const community = await this.communityRepository.findOne({ where: { uuid: communityUuid }, }); @@ -128,7 +148,11 @@ export class CommunityService { } } - async deleteCommunity(communityUuid: string): Promise { + async deleteCommunity(params: GetCommunityParams): Promise { + const { communityUuid, projectUuid } = params; + + await this.validateProject(projectUuid); + const community = await this.communityRepository.findOne({ where: { uuid: communityUuid }, }); @@ -169,4 +193,17 @@ export class CommunityService { ); } } + + private async validateProject(uuid: string) { + const project = await this.projectRepository.findOne({ + where: { uuid }, + }); + if (!project) { + throw new HttpException( + `A project with the uuid '${uuid}' doesn't exists.`, + HttpStatus.BAD_REQUEST, + ); + } + return project; + } } diff --git a/src/space/controllers/space-user.controller.ts b/src/space/controllers/space-user.controller.ts index 89fa3a0..9efdac8 100644 --- a/src/space/controllers/space-user.controller.ts +++ b/src/space/controllers/space-user.controller.ts @@ -26,10 +26,7 @@ export class SpaceUserController { async associateUserToSpace( @Param() params: UserSpaceParam, ): Promise { - return this.spaceUserService.associateUserToSpace( - params.userUuid, - params.spaceUuid, - ); + return this.spaceUserService.associateUserToSpace(params); } @ApiBearerAuth() @@ -43,9 +40,6 @@ export class SpaceUserController { async disassociateUserFromSpace( @Param() params: UserSpaceParam, ): Promise { - return this.spaceUserService.disassociateUserFromSpace( - params.userUuid, - params.spaceUuid, - ); + return this.spaceUserService.disassociateUserFromSpace(params); } } diff --git a/src/space/controllers/space.controller.ts b/src/space/controllers/space.controller.ts index e0615eb..d31bb23 100644 --- a/src/space/controllers/space.controller.ts +++ b/src/space/controllers/space.controller.ts @@ -12,7 +12,7 @@ import { UseGuards, } from '@nestjs/common'; import { JwtAuthGuard } from '@app/common/guards/jwt.auth.guard'; -import { AddSpaceDto, CommunitySpaceParam } from '../dtos'; +import { AddSpaceDto, CommunitySpaceParam, UpdateSpaceDto } from '../dtos'; import { BaseResponseDto } from '@app/common/dto/base.response.dto'; import { GetSpaceParam } from '../dtos/get.space.param'; @@ -37,7 +37,7 @@ export class SpaceController { ): Promise { return await this.spaceService.createSpace( addSpaceDto, - communitySpaceParam.communityUuid, + communitySpaceParam, ); } @@ -51,11 +51,9 @@ export class SpaceController { }) @Get() async getHierarchy( - @Param() param: CommunitySpaceParam, + @Param() params: CommunitySpaceParam, ): Promise { - return this.spaceService.getSpacesHierarchyForCommunity( - param.communityUuid, - ); + return this.spaceService.getSpacesHierarchyForCommunity(params); } @ApiBearerAuth() @@ -66,7 +64,7 @@ export class SpaceController { }) @Delete('/:spaceUuid') async deleteSpace(@Param() params: GetSpaceParam): Promise { - return this.spaceService.delete(params.spaceUuid, params.communityUuid); + return this.spaceService.delete(params); } @ApiBearerAuth() @@ -78,13 +76,9 @@ export class SpaceController { }) async updateSpace( @Param() params: GetSpaceParam, - @Body() updateSpaceDto: AddSpaceDto, + @Body() updateSpaceDto: UpdateSpaceDto, ): Promise { - return this.spaceService.updateSpace( - params.spaceUuid, - params.communityUuid, - updateSpaceDto, - ); + return this.spaceService.updateSpace(params, updateSpaceDto); } @ApiBearerAuth() @@ -95,7 +89,7 @@ export class SpaceController { }) @Get('/:spaceUuid') async get(@Param() params: GetSpaceParam): Promise { - return this.spaceService.findOne(params.spaceUuid); + return this.spaceService.findOne(params); } @ApiBearerAuth() @@ -108,7 +102,7 @@ export class SpaceController { async getHierarchyUnderSpace( @Param() params: GetSpaceParam, ): Promise { - return this.spaceService.getSpacesHierarchyForSpace(params.spaceUuid); + return this.spaceService.getSpacesHierarchyForSpace(params); } //should it be post? @@ -123,6 +117,6 @@ export class SpaceController { async generateSpaceInvitationCode( @Param() params: GetSpaceParam, ): Promise { - return this.spaceService.getSpaceInvitationCode(params.spaceUuid); + return this.spaceService.getSpaceInvitationCode(params); } } diff --git a/src/space/dtos/add.space.dto.ts b/src/space/dtos/add.space.dto.ts index 1f369b1..554f17b 100644 --- a/src/space/dtos/add.space.dto.ts +++ b/src/space/dtos/add.space.dto.ts @@ -30,7 +30,7 @@ export class AddSpaceDto { parentUuid?: string; @IsString() - @IsNotEmpty() + @IsOptional() public icon: string; @ApiProperty({ @@ -56,6 +56,7 @@ export class AddSpaceDto { @IsArray() @ValidateNested({ each: true }) + @IsOptional() @Type(() => ProductAssignmentDto) products: ProductAssignmentDto[]; } diff --git a/src/space/dtos/community-space.param.ts b/src/space/dtos/community-space.param.ts index ab35e4e..caf05b0 100644 --- a/src/space/dtos/community-space.param.ts +++ b/src/space/dtos/community-space.param.ts @@ -1,7 +1,8 @@ import { ApiProperty } from '@nestjs/swagger'; import { IsUUID } from 'class-validator'; +import { ProjectParam } from './project.param.dto'; -export class CommunitySpaceParam { +export class CommunitySpaceParam extends ProjectParam { @ApiProperty({ description: 'UUID of the community this space belongs to', example: 'd290f1ee-6c54-4b01-90e6-d701748f0851', diff --git a/src/space/dtos/index.ts b/src/space/dtos/index.ts index 2cebe7b..3c85266 100644 --- a/src/space/dtos/index.ts +++ b/src/space/dtos/index.ts @@ -3,3 +3,5 @@ export * from './community-space.param'; export * from './get.space.param'; export * from './user-space.param'; export * from './subspace'; +export * from './project.param.dto'; +export * from './update.space.dto'; diff --git a/src/space/dtos/project.param.dto.ts b/src/space/dtos/project.param.dto.ts new file mode 100644 index 0000000..8bb2929 --- /dev/null +++ b/src/space/dtos/project.param.dto.ts @@ -0,0 +1,11 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsUUID } from 'class-validator'; + +export class ProjectParam { + @ApiProperty({ + description: 'UUID of the project this community belongs to', + example: 'd290f1ee-6c54-4b01-90e6-d701748f0851', + }) + @IsUUID() + projectUuid: string; +} diff --git a/src/space/dtos/update.space.dto.ts b/src/space/dtos/update.space.dto.ts new file mode 100644 index 0000000..d40476b --- /dev/null +++ b/src/space/dtos/update.space.dto.ts @@ -0,0 +1,4 @@ +import { PartialType } from '@nestjs/swagger'; +import { AddSpaceDto } from './add.space.dto'; + +export class UpdateSpaceDto extends PartialType(AddSpaceDto) {} diff --git a/src/space/services/space-device.service.ts b/src/space/services/space-device.service.ts index 4dc24ba..0a262a1 100644 --- a/src/space/services/space-device.service.ts +++ b/src/space/services/space-device.service.ts @@ -8,6 +8,7 @@ import { BaseResponseDto } from '@app/common/dto/base.response.dto'; import { SuccessResponseDto } from '@app/common/dto/success.response.dto'; import { convertKeysToCamelCase } from '@app/common/helper/camelCaseConverter'; import { ProductRepository } from '@app/common/modules/product/repositories'; +import { SpaceService } from './space.service'; @Injectable() export class SpaceDeviceService { @@ -16,14 +17,16 @@ export class SpaceDeviceService { private readonly tuyaService: TuyaService, private readonly productRepository: ProductRepository, private readonly communityRepository: CommunityRepository, + private readonly spaceService: SpaceService, ) {} async listDevicesInSpace(params: GetSpaceParam): Promise { - const { spaceUuid, communityUuid } = params; + const { spaceUuid, communityUuid, projectUuid } = params; try { - const space = await this.validateCommunityAndSpace( + const space = await this.spaceService.validateCommunityAndSpace( communityUuid, spaceUuid, + projectUuid, ); const safeFetch = async (device: any) => { @@ -52,7 +55,7 @@ export class SpaceDeviceService { const detailedDevices = await Promise.all(space.devices.map(safeFetch)); return new SuccessResponseDto({ - data: detailedDevices.filter(Boolean), // Remove null or undefined values + data: detailedDevices.filter(Boolean), message: 'Successfully retrieved list of devices', }); } catch (error) { @@ -64,31 +67,6 @@ export class SpaceDeviceService { } } - async validateCommunityAndSpace(communityUuid: string, spaceUuid: string) { - const community = await this.communityRepository.findOne({ - where: { uuid: communityUuid }, - }); - if (!community) { - this.throwNotFound('Community', communityUuid); - } - - const space = await this.spaceRepository.findOne({ - where: { uuid: spaceUuid, community: { uuid: communityUuid } }, - relations: ['devices', 'devices.productDevice'], - }); - if (!space) { - this.throwNotFound('Space', spaceUuid); - } - return space; - } - - private throwNotFound(entity: string, uuid: string) { - throw new HttpException( - `${entity} with ID ${uuid} not found`, - HttpStatus.NOT_FOUND, - ); - } - private async getDeviceDetailsByDeviceIdTuya( deviceId: string, ): Promise { diff --git a/src/space/services/space-scene.service.ts b/src/space/services/space-scene.service.ts index ac26889..57d63a5 100644 --- a/src/space/services/space-scene.service.ts +++ b/src/space/services/space-scene.service.ts @@ -18,11 +18,12 @@ export class SpaceSceneService { getSceneDto: GetSceneDto, ): Promise { try { - const { spaceUuid, communityUuid } = params; + const { spaceUuid, communityUuid, projectUuid } = params; await this.spaceSevice.validateCommunityAndSpace( communityUuid, spaceUuid, + projectUuid, ); const scenes = await this.sceneSevice.findScenesBySpace( diff --git a/src/space/services/space-user.service.ts b/src/space/services/space-user.service.ts index d4f2e9d..0e10e1a 100644 --- a/src/space/services/space-user.service.ts +++ b/src/space/services/space-user.service.ts @@ -6,6 +6,8 @@ import { UserSpaceRepository, } from '@app/common/modules/user/repositories'; import { HttpException, HttpStatus, Injectable } from '@nestjs/common'; +import { SpaceService } from './space.service'; +import { UserSpaceParam } from '../dtos'; @Injectable() export class SpaceUserService { @@ -13,11 +15,10 @@ export class SpaceUserService { private readonly spaceRepository: SpaceRepository, private readonly userRepository: UserRepository, private readonly userSpaceRepository: UserSpaceRepository, + private readonly spaceService: SpaceService, ) {} - async associateUserToSpace( - userUuid: string, - spaceUuid: string, - ): Promise { + async associateUserToSpace(params: UserSpaceParam): Promise { + const { communityUuid, spaceUuid, userUuid, projectUuid } = params; // Find the user by ID const user = await this.userRepository.findOne({ where: { uuid: userUuid }, @@ -30,15 +31,11 @@ export class SpaceUserService { } // Find the space by ID - const space = await this.spaceRepository.findOne({ - where: { uuid: spaceUuid }, - }); - if (!space) { - throw new HttpException( - `Space with ID ${spaceUuid} not found`, - HttpStatus.NOT_FOUND, - ); - } + const space = await this.spaceService.validateCommunityAndSpace( + communityUuid, + spaceUuid, + projectUuid, + ); // Check if the association already exists const existingAssociation = await this.userSpaceRepository.findOne({ @@ -61,9 +58,9 @@ export class SpaceUserService { } async disassociateUserFromSpace( - userUuid: string, - spaceUuid: string, + params: UserSpaceParam, ): Promise { + const { userUuid, spaceUuid, communityUuid, projectUuid } = params; // Find the user by ID const user = await this.userRepository.findOne({ where: { uuid: userUuid }, @@ -76,15 +73,11 @@ export class SpaceUserService { } // Find the space by ID - const space = await this.spaceRepository.findOne({ - where: { uuid: spaceUuid }, - }); - if (!space) { - throw new HttpException( - `Space with ID ${spaceUuid} not found`, - HttpStatus.NOT_FOUND, - ); - } + await this.spaceService.validateCommunityAndSpace( + communityUuid, + spaceUuid, + projectUuid, + ); // Find the existing association const existingAssociation = await this.userSpaceRepository.findOne({ diff --git a/src/space/services/space.service.ts b/src/space/services/space.service.ts index 6e3e600..0055ddf 100644 --- a/src/space/services/space.service.ts +++ b/src/space/services/space.service.ts @@ -5,7 +5,7 @@ import { HttpStatus, Injectable, } from '@nestjs/common'; -import { AddSpaceDto } from '../dtos'; +import { AddSpaceDto, CommunitySpaceParam, GetSpaceParam, UpdateSpaceDto } from '../dtos'; import { SuccessResponseDto } from '@app/common/dto/success.response.dto'; import { BaseResponseDto } from '@app/common/dto/base.response.dto'; import { CommunityRepository } from '@app/common/modules/community/repositories'; @@ -13,6 +13,7 @@ import { SpaceEntity } from '@app/common/modules/space/entities'; import { generateRandomString } from '@app/common/helper/randomString'; import { SpaceLinkService } from './space-link'; import { SpaceProductService } from './space-products'; +import { ProjectRepository } from '@app/common/modules/project/repositiories'; @Injectable() export class SpaceService { @@ -21,15 +22,19 @@ export class SpaceService { private readonly communityRepository: CommunityRepository, private readonly spaceLinkService: SpaceLinkService, private readonly spaceProductService: SpaceProductService, + private readonly projectRepository: ProjectRepository, ) {} async createSpace( addSpaceDto: AddSpaceDto, - communityId: string, + params: CommunitySpaceParam, ): Promise { const { parentUuid, direction, products } = addSpaceDto; + const { communityUuid, projectUuid } = params; - const community = await this.validateCommunity(communityId); + await this.validateProject(projectUuid); + + const community = await this.validateCommunity(communityUuid); const parent = parentUuid ? await this.validateSpace(parentUuid) : null; try { @@ -67,9 +72,11 @@ export class SpaceService { } async getSpacesHierarchyForCommunity( - communityUuid: string, + params: CommunitySpaceParam, ): Promise { + const { communityUuid, projectUuid } = params; await this.validateCommunity(communityUuid); + await this.validateProject(projectUuid); try { // Get all spaces related to the community, including the parent-child relations const spaces = await this.spaceRepository.find({ @@ -80,7 +87,7 @@ export class SpaceService { 'incomingConnections', 'spaceProducts', 'spaceProducts.product', - ], // Include parent and children relations + ], }); // Organize spaces into a hierarchical structure @@ -99,9 +106,14 @@ export class SpaceService { } } - async findOne(spaceUuid: string): Promise { + async findOne(params: GetSpaceParam): Promise { + const { communityUuid, spaceUuid, projectUuid } = params; try { - const space = await this.validateSpace(spaceUuid); + const space = await this.validateCommunityAndSpace( + communityUuid, + spaceUuid, + projectUuid, + ); return new SuccessResponseDto({ message: `Space with ID ${spaceUuid} successfully fetched`, @@ -119,15 +131,14 @@ export class SpaceService { } } - async delete( - spaceUuid: string, - communityUuid: string, - ): Promise { + async delete(params: GetSpaceParam): Promise { try { + const { communityUuid, spaceUuid, projectUuid } = params; // First, check if the community exists const space = await this.validateCommunityAndSpace( communityUuid, spaceUuid, + projectUuid, ); // Delete the space @@ -149,14 +160,15 @@ export class SpaceService { } async updateSpace( - spaceUuid: string, - communityId: string, - updateSpaceDto: AddSpaceDto, + params: GetSpaceParam, + updateSpaceDto: UpdateSpaceDto, ): Promise { + const { communityUuid, spaceUuid, projectUuid } = params; try { const space = await this.validateCommunityAndSpace( - communityId, + communityUuid, spaceUuid, + projectUuid, ); // If a parentId is provided, check if the parent exists @@ -193,9 +205,10 @@ export class SpaceService { } async getSpacesHierarchyForSpace( - spaceUuid: string, + params: GetSpaceParam, ): Promise { - await this.validateSpace(spaceUuid); + const { spaceUuid, communityUuid, projectUuid } = params; + await this.validateCommunityAndSpace(communityUuid, spaceUuid, projectUuid); try { // Get all spaces that are children of the provided space, including the parent-child relations @@ -220,11 +233,16 @@ export class SpaceService { } } - async getSpaceInvitationCode(spaceUuid: string): Promise { + async getSpaceInvitationCode(params: GetSpaceParam): Promise { + const { communityUuid, spaceUuid, projectUuid } = params; try { const invitationCode = generateRandomString(6); - const space = await this.validateSpace(spaceUuid); + const space = await this.validateCommunityAndSpace( + communityUuid, + spaceUuid, + projectUuid, + ); space.invitationCode = invitationCode; await this.spaceRepository.save(space); @@ -283,7 +301,13 @@ export class SpaceService { return community; } - async validateCommunityAndSpace(communityUuid: string, spaceUuid: string) { + async validateCommunityAndSpace( + communityUuid: string, + spaceUuid: string, + projectUuid: string, + ) { + await this.validateProject(projectUuid); + const community = await this.validateCommunity(communityUuid); if (!community) { this.throwNotFound('Community', communityUuid); @@ -301,6 +325,14 @@ export class SpaceService { return space; } + private async validateProject(uuid: string) { + const project = await this.projectRepository.findOne({ + where: { uuid }, + }); + + if (!project) this.throwNotFound('Project', uuid); + } + private throwNotFound(entity: string, uuid: string) { throw new HttpException( `${entity} with ID ${uuid} not found`, diff --git a/src/space/services/subspace/subspace-device.service.ts b/src/space/services/subspace/subspace-device.service.ts index 74f6610..e773ea4 100644 --- a/src/space/services/subspace/subspace-device.service.ts +++ b/src/space/services/subspace/subspace-device.service.ts @@ -12,6 +12,7 @@ import { convertKeysToCamelCase } from '@app/common/helper/camelCaseConverter'; import { TuyaService } from '@app/common/integrations/tuya/services/tuya.service'; import { ProductRepository } from '@app/common/modules/product/repositories'; import { GetDeviceDetailsInterface } from '../../../device/interfaces/get.device.interface'; +import { SpaceService } from '../space.service'; @Injectable() export class SubspaceDeviceService { @@ -22,14 +23,19 @@ export class SubspaceDeviceService { private readonly deviceRepository: DeviceRepository, private readonly tuyaService: TuyaService, private readonly productRepository: ProductRepository, + private readonly spaceService: SpaceService, ) {} async listDevicesInSubspace( params: GetSubSpaceParam, ): Promise { - const { subSpaceUuid, spaceUuid, communityUuid } = params; + const { subSpaceUuid, spaceUuid, communityUuid, projectUuid } = params; - await this.validateCommunityAndSpace(communityUuid, spaceUuid); + await this.spaceService.validateCommunityAndSpace( + communityUuid, + spaceUuid, + projectUuid, + ); const subspace = await this.findSubspaceWithDevices(subSpaceUuid); @@ -67,9 +73,14 @@ export class SubspaceDeviceService { async associateDeviceToSubspace( params: DeviceSubSpaceParam, ): Promise { - const { subSpaceUuid, deviceUuid, spaceUuid, communityUuid } = params; + const { subSpaceUuid, deviceUuid, spaceUuid, communityUuid, projectUuid } = + params; try { - await this.validateCommunityAndSpace(communityUuid, spaceUuid); + await this.spaceService.validateCommunityAndSpace( + communityUuid, + spaceUuid, + projectUuid, + ); const subspace = await this.findSubspace(subSpaceUuid); const device = await this.findDevice(deviceUuid); @@ -97,9 +108,14 @@ export class SubspaceDeviceService { async disassociateDeviceFromSubspace( params: DeviceSubSpaceParam, ): Promise { - const { subSpaceUuid, deviceUuid, spaceUuid, communityUuid } = params; + const { subSpaceUuid, deviceUuid, spaceUuid, communityUuid, projectUuid } = + params; try { - await this.validateCommunityAndSpace(communityUuid, spaceUuid); + await this.spaceService.validateCommunityAndSpace( + communityUuid, + spaceUuid, + projectUuid, + ); const subspace = await this.findSubspace(subSpaceUuid); const device = await this.findDevice(deviceUuid); @@ -129,26 +145,6 @@ export class SubspaceDeviceService { } } } - // Helper method to validate community and space - private async validateCommunityAndSpace( - communityUuid: string, - spaceUuid: string, - ) { - const community = await this.communityRepository.findOne({ - where: { uuid: communityUuid }, - }); - if (!community) { - this.throwNotFound('Community', communityUuid); - } - - const space = await this.spaceRepository.findOne({ - where: { uuid: spaceUuid, community: { uuid: communityUuid } }, - }); - if (!space) { - this.throwNotFound('Space', spaceUuid); - } - return space; - } // Helper method to find subspace with devices relation private async findSubspaceWithDevices(subSpaceUuid: string) { diff --git a/src/space/services/subspace/subspace.service.ts b/src/space/services/subspace/subspace.service.ts index 22c60ee..c343512 100644 --- a/src/space/services/subspace/subspace.service.ts +++ b/src/space/services/subspace/subspace.service.ts @@ -1,9 +1,5 @@ import { BaseResponseDto } from '@app/common/dto/base.response.dto'; -import { CommunityRepository } from '@app/common/modules/community/repositories'; -import { - SpaceRepository, - SubspaceRepository, -} from '@app/common/modules/space/repositories'; +import { SubspaceRepository } from '@app/common/modules/space/repositories'; import { HttpException, HttpStatus, Injectable } from '@nestjs/common'; import { AddSubspaceDto, GetSpaceParam, GetSubSpaceParam } from '../../dtos'; import { SuccessResponseDto } from '@app/common/dto/success.response.dto'; @@ -13,23 +9,24 @@ import { } from '@app/common/models/typeOrmCustom.model'; import { PageResponse } from '@app/common/dto/pagination.response.dto'; import { SubspaceDto } from '@app/common/modules/space/dtos'; +import { SpaceService } from '../space.service'; @Injectable() export class SubSpaceService { constructor( - private readonly spaceRepository: SpaceRepository, - private readonly communityRepository: CommunityRepository, private readonly subspaceRepository: SubspaceRepository, + private readonly spaceService: SpaceService, ) {} async createSubspace( addSubspaceDto: AddSubspaceDto, params: GetSpaceParam, ): Promise { - const { communityUuid, spaceUuid } = params; - const space = await this.validateCommunityAndSpace( + const { communityUuid, spaceUuid, projectUuid } = params; + const space = await this.spaceService.validateCommunityAndSpace( communityUuid, spaceUuid, + projectUuid, ); try { @@ -54,8 +51,12 @@ export class SubSpaceService { params: GetSpaceParam, pageable: Partial, ): Promise { - const { communityUuid, spaceUuid } = params; - await this.validateCommunityAndSpace(communityUuid, spaceUuid); + const { communityUuid, spaceUuid, projectUuid } = params; + await this.spaceService.validateCommunityAndSpace( + communityUuid, + spaceUuid, + projectUuid, + ); try { pageable.modelName = 'subspace'; @@ -74,8 +75,12 @@ export class SubSpaceService { } async findOne(params: GetSubSpaceParam): Promise { - const { communityUuid, subSpaceUuid, spaceUuid } = params; - await this.validateCommunityAndSpace(communityUuid, spaceUuid); + const { communityUuid, subSpaceUuid, spaceUuid, projectUuid } = params; + await this.spaceService.validateCommunityAndSpace( + communityUuid, + spaceUuid, + projectUuid, + ); try { const subSpace = await this.subspaceRepository.findOne({ where: { @@ -110,8 +115,12 @@ export class SubSpaceService { params: GetSubSpaceParam, updateSubSpaceDto: AddSubspaceDto, ): Promise { - const { spaceUuid, communityUuid, subSpaceUuid } = params; - await this.validateCommunityAndSpace(communityUuid, spaceUuid); + const { spaceUuid, communityUuid, subSpaceUuid, projectUuid } = params; + await this.spaceService.validateCommunityAndSpace( + communityUuid, + spaceUuid, + projectUuid, + ); const subSpace = await this.subspaceRepository.findOne({ where: { uuid: subSpaceUuid }, @@ -146,8 +155,12 @@ export class SubSpaceService { } async delete(params: GetSubSpaceParam): Promise { - const { spaceUuid, communityUuid, subSpaceUuid } = params; - await this.validateCommunityAndSpace(communityUuid, spaceUuid); + const { spaceUuid, communityUuid, subSpaceUuid, projectUuid } = params; + await this.spaceService.validateCommunityAndSpace( + communityUuid, + spaceUuid, + projectUuid, + ); const subspace = await this.subspaceRepository.findOne({ where: { uuid: subSpaceUuid }, @@ -174,30 +187,4 @@ export class SubSpaceService { ); } } - - private async validateCommunityAndSpace( - communityUuid: string, - spaceUuid: string, - ) { - const community = await this.communityRepository.findOne({ - where: { uuid: communityUuid }, - }); - if (!community) { - throw new HttpException( - `Community with ID ${communityUuid} not found`, - HttpStatus.NOT_FOUND, - ); - } - - const space = await this.spaceRepository.findOne({ - where: { uuid: spaceUuid, community: { uuid: communityUuid } }, - }); - if (!space) { - throw new HttpException( - `Space with ID ${spaceUuid} not found`, - HttpStatus.NOT_FOUND, - ); - } - return space; - } } diff --git a/src/space/space.module.ts b/src/space/space.module.ts index 9c80fa1..002c2df 100644 --- a/src/space/space.module.ts +++ b/src/space/space.module.ts @@ -42,6 +42,7 @@ import { DeviceService } from 'src/device/services'; import { DeviceStatusFirebaseService } from '@app/common/firebase/devices-status/services/devices-status.service'; import { DeviceStatusLogRepository } from '@app/common/modules/device-status-log/repositories'; import { SceneDeviceRepository } from '@app/common/modules/scene-device/repositories'; +import { ProjectRepository } from '@app/common/modules/project/repositiories'; @Module({ imports: [ConfigModule, SpaceRepositoryModule], @@ -79,6 +80,7 @@ import { SceneDeviceRepository } from '@app/common/modules/scene-device/reposito SceneDeviceRepository, SpaceProductService, SpaceProductRepository, + ProjectRepository, ], exports: [SpaceService], })