delete propogation

This commit is contained in:
hannathkadher
2024-12-27 09:02:47 +04:00
parent f7b75a630a
commit fcf1964599
8 changed files with 154 additions and 111 deletions

View File

@ -0,0 +1,10 @@
import { SpaceEntity } from '@app/common/modules/space';
export class DisableSpaceCommand {
constructor(
public readonly param: {
spaceUuid: string;
orphanSpace: SpaceEntity;
},
) {}
}

View File

@ -0,0 +1 @@
export * from './disable-space.command';

View File

@ -68,7 +68,7 @@ export class SpaceController {
})
@Delete('/:spaceUuid')
async deleteSpace(@Param() params: GetSpaceParam) {
return await this.spaceService.deleteSpace(params);
return await this.spaceService.delete(params);
}
@ApiBearerAuth()

View File

@ -0,0 +1,100 @@
import { SpaceEntity } from '@app/common/modules/space';
import { HttpException, HttpStatus } from '@nestjs/common';
import { CommandHandler, ICommandHandler } from '@nestjs/cqrs';
import { DeviceService } from 'src/device/services';
import { UserSpaceService } from 'src/users/services';
import { DataSource } from 'typeorm';
import { DisableSpaceCommand } from '../commands';
import {
SubSpaceService,
SpaceLinkService,
SpaceSceneService,
} from '../services';
import { TagService } from '../services/tag';
@CommandHandler(DisableSpaceCommand)
export class DisableSpaceHandler
implements ICommandHandler<DisableSpaceCommand>
{
constructor(
private readonly subSpaceService: SubSpaceService,
private readonly userService: UserSpaceService,
private readonly tagService: TagService,
private readonly deviceService: DeviceService,
private readonly spaceLinkService: SpaceLinkService,
private readonly sceneService: SpaceSceneService,
private readonly dataSource: DataSource,
) {}
async execute(command: DisableSpaceCommand): Promise<void> {
const queryRunner = this.dataSource.createQueryRunner();
await queryRunner.connect();
await queryRunner.startTransaction();
try {
const { spaceUuid, orphanSpace } = command.param;
const space = await queryRunner.manager.findOne(SpaceEntity, {
where: { uuid: spaceUuid, disabled: false },
relations: [
'subspaces',
'parent',
'tags',
'devices',
'outgoingConnections',
'incomingConnections',
'scenes',
'children',
'userSpaces',
],
});
if (!space) {
throw new HttpException(
`Space with UUID ${spaceUuid} not found`,
HttpStatus.NOT_FOUND,
);
}
if (space.children && space.children.length > 0) {
for (const child of space.children) {
await this.execute(
new DisableSpaceCommand({ spaceUuid: child.uuid, orphanSpace }),
);
}
}
const tagUuids = space.tags?.map((tag) => tag.uuid) || [];
const subspaceDtos =
space.subspaces?.map((subspace) => ({
subspaceUuid: subspace.uuid,
})) || [];
const deletionTasks = [
this.subSpaceService.deleteSubspaces(subspaceDtos, queryRunner),
this.userService.deleteUserSpace(space.uuid),
this.tagService.deleteTags(tagUuids, queryRunner),
this.deviceService.deleteDevice(
space.devices,
orphanSpace,
queryRunner,
),
this.spaceLinkService.deleteSpaceLink(space, queryRunner),
this.sceneService.deleteScenes(space, queryRunner),
];
await Promise.all(deletionTasks);
// Mark space as disabled
space.disabled = true;
await queryRunner.manager.save(space);
await queryRunner.commitTransaction();
} catch (error) {
await queryRunner.rollbackTransaction();
console.error(`Failed to disable space: ${error.message}`);
} finally {
await queryRunner.release();
}
}
}

View File

@ -0,0 +1 @@
export * from './disable-space.handler';

View File

@ -30,7 +30,6 @@ export class ValidationService {
return { community: community.data, project: project };
}
async validateSpaceWithinCommunityAndProject(
communityUuid: string,
projectUuid: string,
@ -44,7 +43,14 @@ export class ValidationService {
async validateSpace(spaceUuid: string): Promise<SpaceEntity> {
const space = await this.spaceRepository.findOne({
where: { uuid: spaceUuid, disabled: false },
relations: ['subspaces', 'tags'],
relations: [
'parent',
'children',
'subspaces',
'tags',
'subspaces.tags',
'devices',
],
});
if (!space) {

View File

@ -21,13 +21,14 @@ import { SpaceLinkService } from './space-link';
import { SubSpaceService } from './subspace';
import { DataSource, Not, QueryRunner } from 'typeorm';
import { ValidationService } from './space-validation.service';
import { ORPHAN_SPACE_NAME } from '@app/common/constants/orphan-constant';
import {
ORPHAN_COMMUNITY_NAME,
ORPHAN_SPACE_NAME,
} from '@app/common/constants/orphan-constant';
import { CommandBus } from '@nestjs/cqrs';
import { TagService } from './tag';
import { SpaceModelService } from 'src/space-model/services';
import { UserSpaceService } from 'src/users/services';
import { DeviceService } from 'src/device/services';
import { SpaceSceneService } from './space-scene.service';
import { DisableSpaceCommand } from '../commands';
@Injectable()
export class SpaceService {
constructor(
@ -38,9 +39,7 @@ export class SpaceService {
private readonly validationService: ValidationService,
private readonly tagService: TagService,
private readonly spaceModelService: SpaceModelService,
private readonly userService: UserSpaceService,
private readonly deviceService: DeviceService,
private readonly sceneService: SpaceSceneService,
private commandBus: CommandBus,
) {}
async createSpace(
@ -228,20 +227,17 @@ export class SpaceService {
}
async delete(params: GetSpaceParam): Promise<BaseResponseDto> {
const queryRunner = this.dataSource.createQueryRunner();
await queryRunner.connect();
await queryRunner.startTransaction();
try {
const { communityUuid, spaceUuid, projectUuid } = params;
const space =
await this.validationService.validateSpaceWithinCommunityAndProject(
const { project } =
await this.validationService.validateCommunityAndProject(
communityUuid,
projectUuid,
spaceUuid,
);
const space = await this.validationService.validateSpace(spaceUuid);
if (space.spaceName === ORPHAN_SPACE_NAME) {
throw new HttpException(
`space ${ORPHAN_SPACE_NAME} cannot be deleted`,
@ -249,37 +245,22 @@ export class SpaceService {
);
}
if (space.tags?.length) {
const deleteSpaceTagsDtos = space.tags.map((tag) => tag.uuid);
await this.tagService.deleteTags(deleteSpaceTagsDtos, queryRunner);
}
const orphanSpace = await this.spaceRepository.findOne({
where: {
community: {
uuid: `${ORPHAN_COMMUNITY_NAME}-${project.name}`,
},
spaceName: ORPHAN_SPACE_NAME,
},
});
if (space.subspaces?.length) {
const deleteSubspaceDtos = space.subspaces.map((subspace) => ({
subspaceUuid: subspace.uuid,
}));
await this.subSpaceService.deleteSubspaces(
deleteSubspaceDtos,
queryRunner,
);
}
await queryRunner.manager.update(
this.spaceRepository.target,
{ uuid: params.spaceUuid },
{ disabled: true },
);
await queryRunner.commitTransaction();
await this.disableSpace(space, orphanSpace);
return new SuccessResponseDto({
message: `Space with ID ${spaceUuid} successfully deleted`,
statusCode: HttpStatus.OK,
});
} catch (error) {
await queryRunner.rollbackTransaction();
if (error instanceof HttpException) {
throw error;
}
@ -287,11 +268,15 @@ export class SpaceService {
'An error occurred while deleting the space',
HttpStatus.INTERNAL_SERVER_ERROR,
);
} finally {
await queryRunner.release();
}
}
async disableSpace(space: SpaceEntity, orphanSpace: SpaceEntity) {
await this.commandBus.execute(
new DisableSpaceCommand({ spaceUuid: space.uuid, orphanSpace }),
);
}
async updateSpace(
params: GetSpaceParam,
updateSpaceDto: UpdateSpaceDto,
@ -368,71 +353,6 @@ export class SpaceService {
}
}
async deleteSpace(params: GetSpaceParam) {
const queryRunner = this.dataSource.createQueryRunner();
const { spaceUuid } = params;
try {
await queryRunner.connect();
await queryRunner.startTransaction();
const spaces = await this.spaceRepository.find({
where: { parent: { uuid: spaceUuid }, disabled: false },
relations: [
'parent',
'children',
'subspaces',
'tags',
'subspaces.tags',
'devices',
], // Include parent and children relations
});
//this.disableSpace(space, orphanSpace);
return spaces;
} catch (error) {
if (error instanceof HttpException) {
throw error;
}
throw new HttpException(
`An error occurred while deleting the space ${error}`,
HttpStatus.INTERNAL_SERVER_ERROR,
);
}
}
async disableSpace(space: SpaceEntity, orphanSpace: SpaceEntity) {
const queryRunner = this.dataSource.createQueryRunner();
await queryRunner.connect();
await queryRunner.startTransaction();
try {
const deleteSubspaceDtos = space.subspaces?.map((subspace) => ({
subspaceUuid: subspace.uuid,
}));
const deleteSpaceTagsDtos = space.tags?.map((tag) => tag.uuid);
await this.userService.deleteUserSpace(space.uuid);
await this.subSpaceService.deleteSubspaces(
deleteSubspaceDtos,
queryRunner,
);
await this.tagService.deleteTags(deleteSpaceTagsDtos, queryRunner);
await this.deviceService.deleteDevice(
space.devices,
orphanSpace,
queryRunner,
);
await this.spaceLinkService.deleteSpaceLink(space, queryRunner);
await this.sceneService.deleteScenes(space, queryRunner);
} catch (error) {
await queryRunner.rollbackTransaction();
} finally {
await queryRunner.release();
}
}
private updateSpaceProperties(
space: SpaceEntity,
updateSpaceDto: UpdateSpaceDto,

View File

@ -61,9 +61,13 @@ import {
import { UserSpaceService } from 'src/users/services';
import { UserDevicePermissionService } from 'src/user-device-permission/services';
import { PermissionTypeRepository } from '@app/common/modules/permission/repositories';
import { CqrsModule } from '@nestjs/cqrs';
import { DisableSpaceHandler } from './handlers';
export const CommandHandlers = [DisableSpaceHandler];
@Module({
imports: [ConfigModule, SpaceRepositoryModule, CommunityModule],
imports: [ConfigModule, SpaceRepositoryModule, CommunityModule, CqrsModule],
controllers: [
SpaceController,
SpaceUserController,
@ -110,6 +114,7 @@ import { PermissionTypeRepository } from '@app/common/modules/permission/reposit
UserDevicePermissionService,
DeviceUserPermissionRepository,
PermissionTypeRepository,
...CommandHandlers,
],
exports: [SpaceService],
})