diff --git a/libs/common/src/constants/controller-route.ts b/libs/common/src/constants/controller-route.ts index ee2e727..515773e 100644 --- a/libs/common/src/constants/controller-route.ts +++ b/libs/common/src/constants/controller-route.ts @@ -197,7 +197,16 @@ export class ControllerRoute { 'retrieves all the spaces associated with a given community, organized into a hierarchical structure.'; }; }; + static SPACE_VALIDATION = class { + public static readonly ROUTE = '/projects/:projectUuid/spaces'; + static ACTIONS = class { + public static readonly VALIDATE_SPACE_WITH_DEVICES_OR_SUBSPACES_SUMMARY = + 'Check if a space has devices or sub-spaces'; + public static readonly VALIDATE_SPACE_WITH_DEVICES_OR_SUBSPACES_DESCRIPTION = + 'Checks if a space has any devices or sub-spaces associated with it.'; + }; + }; static SPACE_SCENE = class { public static readonly ROUTE = '/projects/:projectUuid/communities/:communityUuid/spaces/:spaceUuid/scenes'; diff --git a/src/community/services/community.service.ts b/src/community/services/community.service.ts index 7bfc9bc..12d6155 100644 --- a/src/community/services/community.service.ts +++ b/src/community/services/community.service.ts @@ -218,7 +218,7 @@ export class CommunityService { } } - private async validateProject(uuid: string) { + async validateProject(uuid: string) { const project = await this.projectRepository.findOne({ where: { uuid }, }); diff --git a/src/space/controllers/space-validation.controller.ts b/src/space/controllers/space-validation.controller.ts new file mode 100644 index 0000000..3cf23cf --- /dev/null +++ b/src/space/controllers/space-validation.controller.ts @@ -0,0 +1,38 @@ +import { ControllerRoute } from '@app/common/constants/controller-route'; +import { Body, Controller, Param, Post, UseGuards } from '@nestjs/common'; +import { ApiBearerAuth, ApiOperation, ApiTags } from '@nestjs/swagger'; +import { BaseResponseDto } from '@app/common/dto/base.response.dto'; +import { ValidationService } from '../services'; +import { ValidateSpacesDto } from '../dtos/validation.space.dto'; +import { JwtAuthGuard } from '@app/common/guards/jwt.auth.guard'; +import { ProjectParam } from '../dtos'; + +@ApiTags('Space Module') +@Controller({ + version: '1', + path: ControllerRoute.SPACE_VALIDATION.ROUTE, +}) +export class SpaceValidationController { + constructor(private readonly validationService: ValidationService) {} + + @ApiBearerAuth() + @UseGuards(JwtAuthGuard) + @ApiOperation({ + summary: + ControllerRoute.SPACE_VALIDATION.ACTIONS + .VALIDATE_SPACE_WITH_DEVICES_OR_SUBSPACES_SUMMARY, + description: + ControllerRoute.SPACE_VALIDATION.ACTIONS + .VALIDATE_SPACE_WITH_DEVICES_OR_SUBSPACES_DESCRIPTION, + }) + @Post('validate') + async validateSpaces( + @Body() validateSpacesDto: ValidateSpacesDto, + @Param() projectParam: ProjectParam, + ): Promise { + return this.validationService.validateSpacesWithDevicesOrSubspaces( + validateSpacesDto, + projectParam, + ); + } +} diff --git a/src/space/dtos/validation.space.dto.ts b/src/space/dtos/validation.space.dto.ts new file mode 100644 index 0000000..504a9d0 --- /dev/null +++ b/src/space/dtos/validation.space.dto.ts @@ -0,0 +1,14 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsArray, IsNotEmpty, IsString } from 'class-validator'; + +export class ValidateSpacesDto { + @ApiProperty({ + description: 'Array of space UUIDs to be validated', + type: [String], + example: ['space-1', 'space-2', 'space-3'], + }) + @IsArray() + @IsNotEmpty() + @IsString({ each: true }) + spacesUuids: string[]; +} diff --git a/src/space/services/space-validation.service.ts b/src/space/services/space-validation.service.ts index 4184a5f..be173b0 100644 --- a/src/space/services/space-validation.service.ts +++ b/src/space/services/space-validation.service.ts @@ -1,5 +1,10 @@ import { SpaceRepository } from '@app/common/modules/space/repositories'; -import { HttpException, HttpStatus, Injectable } from '@nestjs/common'; +import { + BadRequestException, + HttpException, + HttpStatus, + Injectable, +} from '@nestjs/common'; import { CommunityService } from '../../community/services'; import { ProjectService } from '../../project/services'; import { @@ -10,6 +15,10 @@ import { ProjectRepository } from '@app/common/modules/project/repositiories'; import { CommunityRepository } from '@app/common/modules/community/repositories'; import { DeviceRepository } from '@app/common/modules/device/repositories'; import { SpaceEntity } from '@app/common/modules/space/entities/space.entity'; +import { ValidateSpacesDto } from '../dtos/validation.space.dto'; +import { ProjectParam } from '../dtos'; +import { SuccessResponseDto } from '@app/common/dto/success.response.dto'; +import { In } from 'typeorm'; @Injectable() export class ValidationService { @@ -22,7 +31,34 @@ export class ValidationService { private readonly spaceModelRepository: SpaceModelRepository, private readonly deviceRepository: DeviceRepository, ) {} + async validateSpacesWithDevicesOrSubspaces( + validateSpacesDto: ValidateSpacesDto, + projectParam: ProjectParam, + ) { + const { spacesUuids } = validateSpacesDto; + const { projectUuid } = projectParam; + await this.communityService.validateProject(projectUuid); + const spaces = await this.spaceRepository.find({ + where: { uuid: In(spacesUuids), disabled: false }, + relations: ['devices', 'subspaces'], + }); + const hasInvalidSpaces = spaces.some( + (space) => space.devices.length > 0 || space.subspaces.length > 0, + ); + + if (hasInvalidSpaces) { + throw new BadRequestException( + 'Selected spaces already have linked space model / sub-spaces and devices', + ); + } + + return new SuccessResponseDto({ + statusCode: HttpStatus.OK, + message: + 'Validation completed successfully. No spaces have linked devices or sub-spaces.', + }); + } async validateCommunityAndProject( communityUuid: string, projectUuid: string, diff --git a/src/space/space.module.ts b/src/space/space.module.ts index ea0d757..17cf774 100644 --- a/src/space/space.module.ts +++ b/src/space/space.module.ts @@ -82,6 +82,7 @@ import { SpaceModelProductAllocationService } from 'src/space-model/services/spa import { SubspaceModelProductAllocationService } from 'src/space-model/services/subspace/subspace-model-product-allocation.service'; import { SpaceProductAllocationService } from './services/space-product-allocation.service'; import { SubspaceProductAllocationService } from './services/subspace/subspace-product-allocation.service'; +import { SpaceValidationController } from './controllers/space-validation.controller'; export const CommandHandlers = [DisableSpaceHandler]; @@ -94,6 +95,7 @@ export const CommandHandlers = [DisableSpaceHandler]; SubSpaceController, SubSpaceDeviceController, SpaceSceneController, + SpaceValidationController, ], providers: [ ValidationService,