added space delete

This commit is contained in:
hannathkadher
2024-12-24 14:51:06 +04:00
parent cb2778dce5
commit 2e46176fd5
9 changed files with 200 additions and 44 deletions

View File

@ -199,6 +199,12 @@ export class SpaceModelService {
);
}
if (spaceModel.tags?.length) {
const deleteSpaceTagsDtos = spaceModel.tags.map((tag) => tag.uuid);
await this.tagModelService.deleteTags(deleteSpaceTagsDtos, queryRunner);
}
await queryRunner.manager.update(
this.spaceModelRepository.target,
{ uuid: param.spaceModelUuid },
@ -248,6 +254,7 @@ export class SpaceModelService {
uuid,
disabled: true,
},
relations: ['subspaceModels', 'tags'],
});
if (!spaceModel) {
throw new HttpException('space model not found', HttpStatus.NOT_FOUND);

View File

@ -5,7 +5,7 @@ import {
} from '@app/common/modules/space-model';
import { HttpException, HttpStatus, Injectable } from '@nestjs/common';
import { CreateSubspaceModelDto, CreateTagModelDto } from '../../dtos';
import { QueryRunner } from 'typeorm';
import { In, QueryRunner } from 'typeorm';
import { IDeletedSubsaceModelInterface } from 'src/space-model/interfaces';
import {
DeleteSubspaceModelDto,
@ -27,7 +27,7 @@ export class SubSpaceModelService {
queryRunner: QueryRunner,
otherTags?: CreateTagModelDto[],
): Promise<SubspaceModelEntity[]> {
this.validateInputDtos(subSpaceModelDtos);
this.validateInputDtos(subSpaceModelDtos, spaceModel);
const subspaces = subSpaceModelDtos.map((subspaceDto) =>
queryRunner.manager.create(this.subspaceModelRepository.target, {
@ -41,13 +41,18 @@ export class SubSpaceModelService {
await Promise.all(
subSpaceModelDtos.map(async (dto, index) => {
const subspace = savedSubspaces[index];
const otherDtoTags = subSpaceModelDtos
.filter((_, i) => i !== index)
.flatMap((otherDto) => otherDto.tags || []);
if (dto.tags?.length) {
subspace.tags = await this.tagModelService.createTags(
dto.tags,
queryRunner,
null,
subspace,
otherTags,
[...(otherTags || []), ...otherDtoTags],
);
}
}),
@ -194,34 +199,61 @@ export class SubSpaceModelService {
return subspace;
}
private validateInputDtos(subSpaceModelDtos: CreateSubspaceModelDto[]): void {
private validateInputDtos(
subSpaceModelDtos: CreateSubspaceModelDto[],
spaceModel: SpaceModelEntity,
): void {
if (subSpaceModelDtos.length === 0) {
throw new HttpException(
'Subspace models cannot be empty.',
HttpStatus.BAD_REQUEST,
);
}
this.validateName(subSpaceModelDtos.map((dto) => dto.subspaceName));
this.validateName(
subSpaceModelDtos.map((dto) => dto.subspaceName),
spaceModel,
);
}
private validateName(names: string[]): void {
private async validateName(
names: string[],
spaceModel: SpaceModelEntity,
): Promise<void> {
const seenNames = new Set<string>();
const duplicateNames = new Set<string>();
for (const name of names) {
if (seenNames.has(name)) {
if (!seenNames.add(name)) {
duplicateNames.add(name);
} else {
seenNames.add(name);
}
}
if (duplicateNames.size > 0) {
throw new HttpException(
`Duplicate subspace names found: ${[...duplicateNames].join(', ')}`,
`Duplicate subspace model names found: ${[...duplicateNames].join(', ')}`,
HttpStatus.CONFLICT,
);
}
const existingNames = await this.subspaceModelRepository.find({
select: ['subspaceName'],
where: {
subspaceName: In([...seenNames]),
spaceModel: {
uuid: spaceModel.uuid,
},
},
});
if (existingNames.length > 0) {
const existingNamesList = existingNames
.map((e) => e.subspaceName)
.join(', ');
throw new HttpException(
`Subspace model names already exist in the space: ${existingNamesList}`,
HttpStatus.BAD_REQUEST,
);
}
}
private async updateSubspaceName(

View File

@ -180,18 +180,24 @@ export class TagModelService {
productUuid: string,
spaceModel: SpaceModelEntity,
): Promise<void> {
const isTagInSpaceModel = await this.tagModelRepository.exists({
where: { tag, spaceModel, product: { uuid: productUuid } },
});
const isTagInSubspaceModel = await this.tagModelRepository.exists({
where: {
const tagExists = await this.tagModelRepository.exists({
where: [
{
tag,
subspaceModel: { spaceModel },
spaceModel: { uuid: spaceModel.uuid },
product: { uuid: productUuid },
disabled: false,
},
{
tag,
subspaceModel: { spaceModel: { uuid: spaceModel.uuid } },
product: { uuid: productUuid },
disabled: false,
},
],
});
if (isTagInSpaceModel || isTagInSubspaceModel) {
if (tagExists) {
throw new HttpException(`Tag can't be reused`, HttpStatus.CONFLICT);
}
}

View File

@ -65,7 +65,7 @@ export class SubSpaceController {
})
@Get(':subSpaceUuid')
async findOne(@Param() params: GetSubSpaceParam): Promise<BaseResponseDto> {
return this.subSpaceService.findOne(params);
return this.subSpaceService.getOne(params);
}
@ApiBearerAuth()

View File

@ -30,11 +30,13 @@ export class UpdateSpaceDto {
@ApiProperty({ description: 'X position on canvas', example: 120 })
@IsNumber()
x: number;
@IsOptional()
x?: number;
@ApiProperty({ description: 'Y position on canvas', example: 200 })
@IsNumber()
y: number;
@IsOptional()
y?: number;
@ApiPropertyOptional({
description: 'List of subspace modifications (add/update/delete)',

View File

@ -44,6 +44,7 @@ export class ValidationService {
async validateSpace(spaceUuid: string): Promise<SpaceEntity> {
const space = await this.spaceRepository.findOne({
where: { uuid: spaceUuid },
relations: ['subspaces', 'tags'],
});
if (!space) {

View File

@ -58,7 +58,7 @@ export class SpaceService {
projectUuid,
);
this.validateSpaceCreation(spaceModelUuid);
this.validateSpaceCreation(addSpaceDto, spaceModelUuid);
const parent = parentUuid
? await this.validationService.validateSpace(parentUuid)
@ -91,6 +91,7 @@ export class SpaceService {
subspaces,
newSpace,
queryRunner,
tags,
);
}
@ -189,6 +190,10 @@ 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;
@ -205,14 +210,38 @@ export class SpaceService {
HttpStatus.BAD_REQUEST,
);
}
// Delete the space
await this.spaceRepository.remove(space);
if (space.tags?.length) {
const deleteSpaceTagsDtos = space.tags.map((tag) => tag.uuid);
await this.tagService.deleteTags(deleteSpaceTagsDtos, queryRunner);
}
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();
return new SuccessResponseDto({
message: `Space with ID ${spaceUuid} successfully deleted`,
statusCode: HttpStatus.OK,
});
} catch (error) {
await queryRunner.rollbackTransaction();
if (error instanceof HttpException) {
throw error;
}
@ -220,6 +249,8 @@ export class SpaceService {
'An error occurred while deleting the space',
HttpStatus.INTERNAL_SERVER_ERROR,
);
} finally {
await queryRunner.release();
}
}
@ -384,8 +415,11 @@ export class SpaceService {
return rootSpaces;
}
private validateSpaceCreation(spaceModelUuid?: string) {
if (spaceModelUuid) {
private validateSpaceCreation(
addSpaceDto: AddSpaceDto,
spaceModelUuid?: string,
) {
if (spaceModelUuid && (addSpaceDto.tags || addSpaceDto.subspaces)) {
throw new HttpException(
'For space creation choose either space model or products and subspace',
HttpStatus.CONFLICT,

View File

@ -15,7 +15,7 @@ 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 { QueryRunner } from 'typeorm';
import { In, QueryRunner } from 'typeorm';
import {
SpaceEntity,
SubspaceEntity,
@ -84,6 +84,11 @@ export class SubSpaceService {
otherTags?: CreateTagDto[],
): Promise<SubspaceEntity[]> {
try {
await this.validateName(
addSubspaceDtos.map((dto) => dto.subspaceName),
space,
);
const subspaceData = addSubspaceDtos.map((dto) => ({
subspaceName: dto.subspaceName,
space,
@ -93,6 +98,9 @@ export class SubSpaceService {
await Promise.all(
addSubspaceDtos.map(async (dto, index) => {
const otherDtoTags = addSubspaceDtos
.filter((_, i) => i !== index)
.flatMap((otherDto) => otherDto.tags || []);
const subspace = subspaces[index];
if (dto.tags?.length) {
subspace.tags = await this.tagService.createTags(
@ -100,15 +108,20 @@ export class SubSpaceService {
queryRunner,
null,
subspace,
otherTags,
[...(otherTags || []), ...otherDtoTags],
);
}
}),
);
return subspaces;
} catch (error) {
throw new Error(
`Transaction failed: Unable to create subspaces and products. ${error.message}`,
if (error instanceof HttpException) {
throw error;
}
throw new HttpException(
'Failed to save subspaces due to an unexpected error.',
HttpStatus.INTERNAL_SERVER_ERROR,
);
}
}
@ -304,6 +317,19 @@ export class SubSpaceService {
}
}
async getOne(params: GetSubSpaceParam): Promise<BaseResponseDto> {
await this.validationService.validateSpaceWithinCommunityAndProject(
params.communityUuid,
params.projectUuid,
params.spaceUuid,
);
const subspace = await this.findOne(params.subSpaceUuid);
return new SuccessResponseDto({
message: `Successfully retrieved subspace`,
data: subspace,
});
}
private async handleAddAction(
subspace: ModifySubspaceDto,
space: SpaceEntity,
@ -372,7 +398,7 @@ export class SubSpaceService {
private async findOne(subspaceUuid: string): Promise<SubspaceEntity> {
const subspace = await this.subspaceRepository.findOne({
where: { uuid: subspaceUuid },
relations: ['tags'],
relations: ['tags', 'space'],
});
if (!subspace) {
throw new HttpException(
@ -393,4 +419,45 @@ export class SubSpaceService {
await queryRunner.manager.save(subSpace);
}
}
private async validateName(
names: string[],
space: SpaceEntity,
): Promise<void> {
const seenNames = new Set<string>();
const duplicateNames = new Set<string>();
for (const name of names) {
if (!seenNames.add(name)) {
duplicateNames.add(name);
}
}
if (duplicateNames.size > 0) {
throw new HttpException(
`Duplicate subspace names found: ${[...duplicateNames].join(', ')}`,
HttpStatus.CONFLICT,
);
}
const existingNames = await this.subspaceRepository.find({
select: ['subspaceName'],
where: {
subspaceName: In([...seenNames]),
space: {
uuid: space.uuid,
},
},
});
if (existingNames.length > 0) {
const existingNamesList = existingNames
.map((e) => e.subspaceName)
.join(', ');
throw new HttpException(
`Subspace names already exist in the space: ${existingNamesList}`,
HttpStatus.BAD_REQUEST,
);
}
}
}

View File

@ -135,7 +135,6 @@ export class TagService {
subspace?: SubspaceEntity,
): Promise<void> {
try {
console.log(tags);
for (const tag of tags) {
if (tag.action === ModifyAction.ADD) {
const createTagDto: CreateTagDto = {
@ -176,18 +175,26 @@ export class TagService {
productUuid: string,
space: SpaceEntity,
): Promise<void> {
const isTagInSpace = await this.tagRepository.exists({
where: { tag, space, product: { uuid: productUuid } },
});
const isTagInSubspace = await this.tagRepository.exists({
where: {
const { uuid: spaceUuid } = space;
const tagExists = await this.tagRepository.exists({
where: [
{
tag,
subspace: { space },
space: { uuid: spaceUuid },
product: { uuid: productUuid },
disabled: false,
},
{
tag,
subspace: { space: { uuid: spaceUuid } },
product: { uuid: productUuid },
disabled: false,
},
],
});
if (isTagInSpace || isTagInSubspace) {
if (tagExists) {
throw new HttpException(`Tag can't be reused`, HttpStatus.CONFLICT);
}
}