mirror of
https://github.com/SyncrowIOT/backend.git
synced 2025-07-16 18:56:22 +00:00
Merge pull request #272 from SyncrowIOT/SP-1079-BE-Implement-API-for-linking-space-model-to-space
This commit is contained in:
@ -292,7 +292,7 @@ export class ControllerRoute {
|
||||
public static readonly CREATE_SPACE_MODEL_DESCRIPTION =
|
||||
'This endpoint allows you to create a new space model within a specified project. A space model defines the structure of spaces, including subspaces, products, and product items, and is uniquely identifiable within the project.';
|
||||
|
||||
public static readonly GET_SPACE_MODEL_SUMMARY = 'Get a New Space Model';
|
||||
public static readonly GET_SPACE_MODEL_SUMMARY = 'Get a Space Model';
|
||||
public static readonly GET_SPACE_MODEL_DESCRIPTION =
|
||||
'Fetch a space model details';
|
||||
|
||||
|
@ -14,6 +14,7 @@ import { ApiBearerAuth, ApiOperation, ApiTags } from '@nestjs/swagger';
|
||||
import { SpaceModelService } from '../services';
|
||||
import {
|
||||
CreateSpaceModelDto,
|
||||
LinkSpacesToModelDto,
|
||||
SpaceModelParam,
|
||||
UpdateSpaceModelDto,
|
||||
} from '../dtos';
|
||||
@ -107,4 +108,20 @@ export class SpaceModelController {
|
||||
async delete(@Param() param: SpaceModelParam): Promise<BaseResponseDto> {
|
||||
return await this.spaceModelService.deleteSpaceModel(param);
|
||||
}
|
||||
|
||||
@ApiBearerAuth()
|
||||
@UseGuards(PermissionsGuard)
|
||||
@Permissions('SPACE_MODEL_LINK')
|
||||
@ApiOperation({
|
||||
summary: ControllerRoute.SPACE_MODEL.ACTIONS.DELETE_SPACE_MODEL_SUMMARY,
|
||||
description:
|
||||
ControllerRoute.SPACE_MODEL.ACTIONS.DELETE_SPACE_MODEL_DESCRIPTION,
|
||||
})
|
||||
@Post(':spaceModelUuid/spaces/link')
|
||||
async link(
|
||||
@Param() params: SpaceModelParam,
|
||||
@Body() dto: LinkSpacesToModelDto,
|
||||
): Promise<BaseResponseDto> {
|
||||
return await this.spaceModelService.linkSpaceModel(params, dto);
|
||||
}
|
||||
}
|
||||
|
@ -5,11 +5,15 @@ import {
|
||||
SubspaceModelProductAllocationEntity,
|
||||
} from '@app/common/modules/space-model';
|
||||
import { HttpException, HttpStatus, Injectable } from '@nestjs/common';
|
||||
import { CreateSpaceModelDto, UpdateSpaceModelDto } from '../dtos';
|
||||
import {
|
||||
CreateSpaceModelDto,
|
||||
LinkSpacesToModelDto,
|
||||
UpdateSpaceModelDto,
|
||||
} from '../dtos';
|
||||
import { ProjectParam } from 'src/community/dtos';
|
||||
import { SuccessResponseDto } from '@app/common/dto/success.response.dto';
|
||||
import { SubSpaceModelService } from './subspace/subspace-model.service';
|
||||
import { DataSource, QueryRunner, SelectQueryBuilder } from 'typeorm';
|
||||
import { DataSource, In, QueryRunner, SelectQueryBuilder } from 'typeorm';
|
||||
import {
|
||||
TypeORMCustomModel,
|
||||
TypeORMCustomModelFindAllQuery,
|
||||
@ -21,7 +25,20 @@ import { BaseResponseDto } from '@app/common/dto/base.response.dto';
|
||||
import { CommandBus } from '@nestjs/cqrs';
|
||||
import { ProcessTagDto } from 'src/tags/dtos';
|
||||
import { SpaceModelProductAllocationService } from './space-model-product-allocation.service';
|
||||
import { SpaceRepository } from '@app/common/modules/space';
|
||||
import {
|
||||
SpaceProductAllocationRepository,
|
||||
SpaceRepository,
|
||||
} from '@app/common/modules/space';
|
||||
import { SpaceEntity } from '@app/common/modules/space/entities/space.entity';
|
||||
import {
|
||||
SubspaceProductAllocationRepository,
|
||||
SubspaceRepository,
|
||||
} from '@app/common/modules/space/repositories/subspace.repository';
|
||||
import {
|
||||
ORPHAN_COMMUNITY_NAME,
|
||||
ORPHAN_SPACE_NAME,
|
||||
} from '@app/common/constants/orphan-constant';
|
||||
import { DeviceRepository } from '@app/common/modules/device/repositories';
|
||||
|
||||
@Injectable()
|
||||
export class SpaceModelService {
|
||||
@ -33,6 +50,10 @@ export class SpaceModelService {
|
||||
private commandBus: CommandBus,
|
||||
private readonly spaceModelProductAllocationService: SpaceModelProductAllocationService,
|
||||
private readonly spaceRepository: SpaceRepository,
|
||||
private readonly spaceProductAllocationRepository: SpaceProductAllocationRepository,
|
||||
private readonly subspaceRepository: SubspaceRepository,
|
||||
private readonly subspaceProductAllocationRepository: SubspaceProductAllocationRepository,
|
||||
private readonly deviceRepository: DeviceRepository,
|
||||
) {}
|
||||
|
||||
async createSpaceModel(
|
||||
@ -286,6 +307,205 @@ export class SpaceModelService {
|
||||
}
|
||||
}
|
||||
|
||||
async linkSpaceModel(
|
||||
params: SpaceModelParam,
|
||||
dto: LinkSpacesToModelDto,
|
||||
): Promise<BaseResponseDto> {
|
||||
const project = await this.validateProject(params.projectUuid);
|
||||
|
||||
try {
|
||||
const spaceModel = await this.spaceModelRepository.findOne({
|
||||
where: { uuid: params.spaceModelUuid },
|
||||
relations: [
|
||||
'productAllocations',
|
||||
'subspaceModels',
|
||||
'subspaceModels.productAllocations',
|
||||
],
|
||||
});
|
||||
|
||||
if (!spaceModel) {
|
||||
throw new HttpException(
|
||||
`Space Model with UUID ${params.spaceModelUuid} not found`,
|
||||
HttpStatus.NOT_FOUND,
|
||||
);
|
||||
}
|
||||
|
||||
if (!spaceModel.productAllocations.length) {
|
||||
throw new HttpException(
|
||||
`Space Model ${params.spaceModelUuid} has no product allocations`,
|
||||
HttpStatus.BAD_REQUEST,
|
||||
);
|
||||
}
|
||||
|
||||
const spaces = await this.spaceRepository.find({
|
||||
where: { uuid: In(dto.spaceUuids), disabled: false },
|
||||
relations: [
|
||||
'spaceModel',
|
||||
'devices',
|
||||
'subspaces',
|
||||
'productAllocations',
|
||||
'subspaces.productAllocations',
|
||||
'community',
|
||||
],
|
||||
});
|
||||
|
||||
if (!spaces.length) {
|
||||
throw new HttpException(
|
||||
`No spaces found for the given UUIDs`,
|
||||
HttpStatus.NOT_FOUND,
|
||||
);
|
||||
}
|
||||
|
||||
await Promise.all(
|
||||
spaces.map(async (space) => {
|
||||
const hasDependencies =
|
||||
space.devices.length > 0 ||
|
||||
space.subspaces.length > 0 ||
|
||||
space.productAllocations.length > 0;
|
||||
|
||||
if (!hasDependencies && !space.spaceModel) {
|
||||
await this.linkToSpace(space, spaceModel);
|
||||
} else if (dto.overwrite) {
|
||||
await this.overwriteSpace(space, project);
|
||||
await this.linkToSpace(space, spaceModel);
|
||||
}
|
||||
}),
|
||||
);
|
||||
|
||||
return new SuccessResponseDto({
|
||||
message: 'Spaces linked successfully',
|
||||
data: dto.spaceUuids,
|
||||
});
|
||||
} catch (error) {
|
||||
throw new HttpException(
|
||||
`Failed to link space model: ${error.message}`,
|
||||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
async linkToSpace(
|
||||
space: SpaceEntity,
|
||||
spaceModel: SpaceModelEntity,
|
||||
): Promise<void> {
|
||||
try {
|
||||
space.spaceModel = spaceModel;
|
||||
await this.spaceRepository.save(space);
|
||||
|
||||
const spaceProductAllocations = spaceModel.productAllocations.map(
|
||||
(modelAllocation) =>
|
||||
this.spaceProductAllocationRepository.create({
|
||||
space,
|
||||
inheritedFromModel: modelAllocation,
|
||||
product: modelAllocation.product,
|
||||
tags: modelAllocation.tags,
|
||||
}),
|
||||
);
|
||||
await this.spaceProductAllocationRepository.save(spaceProductAllocations);
|
||||
|
||||
if (!spaceModel.subspaceModels.length) {
|
||||
throw new HttpException(
|
||||
`Space Model ${spaceModel.uuid} has no subspaces`,
|
||||
HttpStatus.BAD_REQUEST,
|
||||
);
|
||||
}
|
||||
|
||||
await Promise.all(
|
||||
spaceModel.subspaceModels.map(async (subspaceModel) => {
|
||||
const subspace = this.subspaceRepository.create({
|
||||
subspaceName: subspaceModel.subspaceName,
|
||||
subSpaceModel: subspaceModel,
|
||||
space: space,
|
||||
});
|
||||
|
||||
await this.subspaceRepository.save(subspace);
|
||||
|
||||
const subspaceAllocations = subspaceModel.productAllocations.map(
|
||||
(modelAllocation) =>
|
||||
this.subspaceProductAllocationRepository.create({
|
||||
subspace,
|
||||
inheritedFromModel: modelAllocation,
|
||||
product: modelAllocation.product,
|
||||
tags: modelAllocation.tags,
|
||||
}),
|
||||
);
|
||||
|
||||
if (subspaceAllocations.length) {
|
||||
await this.subspaceProductAllocationRepository.save(
|
||||
subspaceAllocations,
|
||||
);
|
||||
}
|
||||
}),
|
||||
);
|
||||
} catch (error) {
|
||||
throw new HttpException(
|
||||
`Failed to link space ${space.uuid} to space model ${spaceModel.uuid}: ${error.message}`,
|
||||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
async overwriteSpace(
|
||||
space: SpaceEntity,
|
||||
project: ProjectEntity,
|
||||
): Promise<void> {
|
||||
try {
|
||||
if (space.productAllocations.length) {
|
||||
await this.spaceProductAllocationRepository.delete({
|
||||
uuid: In(
|
||||
space.productAllocations.map((allocation) => allocation.uuid),
|
||||
),
|
||||
});
|
||||
}
|
||||
|
||||
await Promise.all(
|
||||
space.subspaces.map(async (subspace) => {
|
||||
await this.subspaceRepository.update(
|
||||
{ uuid: subspace.uuid },
|
||||
{ disabled: true },
|
||||
);
|
||||
|
||||
if (subspace.productAllocations.length) {
|
||||
await this.subspaceProductAllocationRepository.delete({
|
||||
uuid: In(
|
||||
subspace.productAllocations.map(
|
||||
(allocation) => allocation.uuid,
|
||||
),
|
||||
),
|
||||
});
|
||||
}
|
||||
}),
|
||||
);
|
||||
|
||||
if (space.devices.length > 0) {
|
||||
const orphanSpace = await this.spaceRepository.findOne({
|
||||
where: {
|
||||
community: {
|
||||
name: `${ORPHAN_COMMUNITY_NAME}-${project.name}`,
|
||||
},
|
||||
spaceName: ORPHAN_SPACE_NAME,
|
||||
},
|
||||
});
|
||||
|
||||
if (!orphanSpace) {
|
||||
throw new HttpException(
|
||||
`Orphan space not found in community ${project.name}`,
|
||||
HttpStatus.NOT_FOUND,
|
||||
);
|
||||
}
|
||||
|
||||
await this.deviceRepository.update(
|
||||
{ uuid: In(space.devices.map((device) => device.uuid)) },
|
||||
{ spaceDevice: orphanSpace },
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
throw new Error(
|
||||
`Failed to overwrite space ${space.uuid}: ${error.message}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
async validateName(modelName: string, projectUuid: string): Promise<void> {
|
||||
const isModelExist = await this.spaceModelRepository.findOne({
|
||||
where: { modelName, project: { uuid: projectUuid }, disabled: false },
|
||||
|
@ -66,12 +66,12 @@ export class DisableSpaceHandler
|
||||
}
|
||||
|
||||
const tagUuids = space.tags?.map((tag) => tag.uuid) || [];
|
||||
const subspaceDtos =
|
||||
/* const subspaceDtos =
|
||||
space.subspaces?.map((subspace) => ({
|
||||
subspaceUuid: subspace.uuid,
|
||||
})) || [];
|
||||
})) || []; */
|
||||
const deletionTasks = [
|
||||
this.subSpaceService.deleteSubspaces(subspaceDtos, queryRunner),
|
||||
// this.subSpaceService.deleteSubspaces(subspaceDtos, queryRunner),
|
||||
this.userService.deleteUserSpace(space.uuid),
|
||||
this.tagService.deleteTags(tagUuids, queryRunner),
|
||||
this.deviceService.deleteDevice(
|
||||
|
@ -282,11 +282,11 @@ export class SubSpaceService {
|
||||
}
|
||||
}
|
||||
|
||||
async deleteSubspaces(
|
||||
/* async deleteSubspaces(
|
||||
deleteDtos: DeleteSubspaceDto[],
|
||||
queryRunner: QueryRunner,
|
||||
) {
|
||||
const deleteResults: { uuid: string }[] = [];
|
||||
/* const deleteResults: { uuid: string }[] = [];
|
||||
|
||||
for (const dto of deleteDtos) {
|
||||
const subspace = await this.findOne(dto.subspaceUuid);
|
||||
@ -319,8 +319,8 @@ export class SubSpaceService {
|
||||
deleteResults.push({ uuid: dto.subspaceUuid });
|
||||
}
|
||||
|
||||
return deleteResults;
|
||||
}
|
||||
return deleteResults;
|
||||
} */
|
||||
|
||||
async modifySubSpace(
|
||||
subspaceDtos: ModifySubspaceDto[],
|
||||
|
Reference in New Issue
Block a user