mirror of
https://github.com/SyncrowIOT/backend.git
synced 2025-07-10 07:07:21 +00:00

* task: add getCommunitiesV2 * task: update getOneSpace API to match revamp structure * refactor: implement modifications to pace management APIs * refactor: remove space link
277 lines
8.3 KiB
TypeScript
277 lines
8.3 KiB
TypeScript
import { SuccessResponseDto } from '@app/common/dto/success.response.dto';
|
|
import { CommunityRepository } from '@app/common/modules/community/repositories';
|
|
import { DeviceRepository } from '@app/common/modules/device/repositories';
|
|
import { ProjectRepository } from '@app/common/modules/project/repositiories';
|
|
import {
|
|
SpaceModelEntity,
|
|
SpaceModelRepository,
|
|
} from '@app/common/modules/space-model';
|
|
import { SpaceEntity } from '@app/common/modules/space/entities/space.entity';
|
|
import { SpaceRepository } from '@app/common/modules/space/repositories';
|
|
import {
|
|
BadRequestException,
|
|
forwardRef,
|
|
HttpException,
|
|
HttpStatus,
|
|
Inject,
|
|
Injectable,
|
|
} from '@nestjs/common';
|
|
import { In, QueryRunner } from 'typeorm';
|
|
import { CommunityService } from '../../community/services';
|
|
import { ProjectService } from '../../project/services';
|
|
import { ProjectParam } from '../dtos';
|
|
import { ValidateSpacesDto } from '../dtos/validation.space.dto';
|
|
|
|
@Injectable()
|
|
export class ValidationService {
|
|
constructor(
|
|
private readonly projectService: ProjectService,
|
|
@Inject(forwardRef(() => CommunityService))
|
|
private readonly communityService: CommunityService,
|
|
private readonly spaceRepository: SpaceRepository,
|
|
private readonly projectRepository: ProjectRepository,
|
|
private readonly communityRepository: CommunityRepository,
|
|
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', 'productAllocations', 'spaceModel'],
|
|
});
|
|
|
|
const hasInvalidSpaces = spaces.some(
|
|
(space) =>
|
|
space.devices.length > 0 ||
|
|
space.subspaces.length > 0 ||
|
|
space.productAllocations.length > 0 ||
|
|
space.spaceModel,
|
|
);
|
|
|
|
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,
|
|
queryRunner?: QueryRunner,
|
|
) {
|
|
const project = await this.projectService.findOne(projectUuid, queryRunner);
|
|
|
|
const community = await this.communityService.getCommunityById(
|
|
{
|
|
communityUuid,
|
|
projectUuid,
|
|
},
|
|
queryRunner,
|
|
);
|
|
|
|
return { community: community.data, project: project };
|
|
}
|
|
|
|
async checkCommunityAndProjectSpaceExistence(
|
|
communityUuid: string,
|
|
projectUuid: string,
|
|
spaceUuid?: string,
|
|
): Promise<void> {
|
|
const [projectExists, communityExists, spaceExists] = await Promise.all([
|
|
this.projectRepository.exists({ where: { uuid: projectUuid } }),
|
|
this.communityRepository.exists({
|
|
where: { uuid: communityUuid, project: { uuid: projectUuid } },
|
|
}),
|
|
spaceUuid
|
|
? this.spaceRepository.exists({
|
|
where: { uuid: spaceUuid },
|
|
})
|
|
: Promise.resolve(true),
|
|
]);
|
|
|
|
if (!projectExists)
|
|
throw new HttpException(`Project not found`, HttpStatus.NOT_FOUND);
|
|
if (!communityExists)
|
|
throw new HttpException(`Community not found`, HttpStatus.NOT_FOUND);
|
|
if (spaceUuid && !spaceExists)
|
|
throw new HttpException(`Space not found`, HttpStatus.NOT_FOUND);
|
|
}
|
|
|
|
async validateSpaceWithinCommunityAndProject(
|
|
communityUuid: string,
|
|
projectUuid: string,
|
|
spaceUuid?: string,
|
|
): Promise<SpaceEntity> {
|
|
await this.validateCommunityAndProject(communityUuid, projectUuid);
|
|
|
|
const space = await this.validateSpace(spaceUuid);
|
|
|
|
return space;
|
|
}
|
|
|
|
async validateSpace(spaceUuid: string): Promise<SpaceEntity> {
|
|
const space = await this.spaceRepository.findOne({
|
|
where: { uuid: spaceUuid, disabled: false },
|
|
relations: [
|
|
'parent',
|
|
'children',
|
|
'subspaces',
|
|
'productAllocations',
|
|
'productAllocations.product',
|
|
'subspaces.productAllocations',
|
|
'subspaces.productAllocations.product',
|
|
'subspaces.devices',
|
|
'spaceModel',
|
|
],
|
|
});
|
|
|
|
if (!space) {
|
|
throw new HttpException(
|
|
`Space with UUID ${spaceUuid} not found`,
|
|
HttpStatus.NOT_FOUND,
|
|
);
|
|
}
|
|
|
|
// const devices = await this.deviceRepository.find({
|
|
// where: { spaceDevice: { uuid: spaceUuid } },
|
|
// select: ['uuid', 'deviceTuyaUuid', 'isActive', 'createdAt', 'updatedAt'],
|
|
// relations: ['productDevice', 'subspace'],
|
|
// });
|
|
|
|
// space.devices = devices;
|
|
|
|
return space;
|
|
}
|
|
|
|
async fetchSpaceDevices(spaceUuid: string): Promise<SpaceEntity> {
|
|
const space = await this.spaceRepository.findOne({
|
|
where: { uuid: spaceUuid, disabled: false },
|
|
relations: [
|
|
'devices',
|
|
'devices.productDevice',
|
|
'devices.tag',
|
|
'devices.subspace',
|
|
],
|
|
});
|
|
|
|
if (!space) {
|
|
throw new HttpException(
|
|
`Space with UUID ${spaceUuid} not found`,
|
|
HttpStatus.NOT_FOUND,
|
|
);
|
|
}
|
|
return space;
|
|
}
|
|
|
|
async validateSpaceModel(
|
|
spaceModelUuid: string,
|
|
queryRunner?: QueryRunner,
|
|
): Promise<SpaceModelEntity> {
|
|
const queryBuilder = (
|
|
queryRunner.manager.getRepository(SpaceModelEntity) ||
|
|
this.spaceModelRepository
|
|
)
|
|
.createQueryBuilder('spaceModel')
|
|
.leftJoinAndSelect(
|
|
'spaceModel.subspaceModels',
|
|
'subspaceModels',
|
|
'subspaceModels.disabled = :disabled',
|
|
{ disabled: false },
|
|
)
|
|
.leftJoinAndSelect(
|
|
'subspaceModels.productAllocations',
|
|
'subspaceProductAllocations',
|
|
)
|
|
.leftJoinAndSelect(
|
|
'subspaceProductAllocations.tag',
|
|
'subspaceAllocationTag',
|
|
)
|
|
.leftJoinAndSelect(
|
|
'subspaceProductAllocations.product',
|
|
'subspaceAllocationProduct',
|
|
)
|
|
.leftJoinAndSelect('spaceModel.productAllocations', 'productAllocations')
|
|
.leftJoinAndSelect(
|
|
'productAllocations.product',
|
|
'productAllocationProduct',
|
|
)
|
|
.leftJoinAndSelect('productAllocations.tag', 'productAllocationTag')
|
|
.andWhere('spaceModel.disabled = :disabled', { disabled: false })
|
|
.where('spaceModel.uuid = :uuid', { uuid: spaceModelUuid });
|
|
|
|
const spaceModel = await queryBuilder.getOne();
|
|
|
|
if (!spaceModel) {
|
|
throw new HttpException(
|
|
`Space model with UUID ${spaceModelUuid} not found`,
|
|
HttpStatus.NOT_FOUND,
|
|
);
|
|
}
|
|
|
|
return spaceModel;
|
|
}
|
|
|
|
private async fetchAncestors(space: SpaceEntity): Promise<SpaceEntity[]> {
|
|
const ancestors: SpaceEntity[] = [];
|
|
|
|
let currentSpace = space;
|
|
while (currentSpace && currentSpace.parent) {
|
|
// Fetch the parent space
|
|
const parent = await this.spaceRepository.findOne({
|
|
where: { uuid: currentSpace.parent.uuid },
|
|
relations: ['parent'], // To continue fetching upwards
|
|
});
|
|
|
|
if (parent) {
|
|
ancestors.push(parent);
|
|
currentSpace = parent;
|
|
} else {
|
|
currentSpace = null;
|
|
}
|
|
}
|
|
|
|
// Return the ancestors in reverse order to have the root at the start
|
|
return ancestors.reverse();
|
|
}
|
|
|
|
async getParentHierarchy(
|
|
space: SpaceEntity,
|
|
): Promise<{ uuid: string; spaceName: string }[]> {
|
|
try {
|
|
const targetSpace = await this.spaceRepository.findOne({
|
|
where: { uuid: space.uuid },
|
|
relations: ['parent'],
|
|
});
|
|
|
|
if (!targetSpace) {
|
|
throw new HttpException('Space not found', HttpStatus.NOT_FOUND);
|
|
}
|
|
|
|
const ancestors = await this.fetchAncestors(targetSpace);
|
|
|
|
return ancestors.map((parentSpace) => ({
|
|
uuid: parentSpace.uuid,
|
|
spaceName: parentSpace.spaceName,
|
|
}));
|
|
} catch (error) {
|
|
console.error('Error fetching parent hierarchy:', error.message);
|
|
throw new HttpException(
|
|
'Error fetching parent hierarchy',
|
|
HttpStatus.INTERNAL_SERVER_ERROR,
|
|
);
|
|
}
|
|
}
|
|
}
|