diff --git a/libs/common/src/modules/space-model/entities/tag-model.entity.ts b/libs/common/src/modules/space-model/entities/tag-model.entity.ts index 3f7805c..e2a70ee 100644 --- a/libs/common/src/modules/space-model/entities/tag-model.entity.ts +++ b/libs/common/src/modules/space-model/entities/tag-model.entity.ts @@ -1,11 +1,4 @@ -import { - Column, - Entity, - JoinColumn, - ManyToOne, - OneToMany, - Unique, -} from 'typeorm'; +import { Column, Entity, JoinColumn, ManyToOne, OneToMany } from 'typeorm'; import { AbstractEntity } from '../../abstract/entities/abstract.entity'; import { TagModelDto } from '../dtos/tag-model.dto'; import { SpaceModelEntity } from './space-model.entity'; @@ -14,7 +7,6 @@ import { ProductEntity } from '../../product/entities'; import { TagEntity } from '../../space/entities/tag.entity'; @Entity({ name: 'tag_model' }) -@Unique(['tag', 'product', 'spaceModel', 'subspaceModel']) export class TagModel extends AbstractEntity { @Column({ type: 'varchar', length: 255 }) tag: string; diff --git a/src/space-model/services/space-model.service.ts b/src/space-model/services/space-model.service.ts index 98d6c78..05906ca 100644 --- a/src/space-model/services/space-model.service.ts +++ b/src/space-model/services/space-model.service.ts @@ -89,8 +89,6 @@ export class SpaceModelService { statusCode: HttpStatus.CREATED, }); } catch (error) { - console.log(JSON.stringify(createSpaceModelDto)); - await queryRunner.rollbackTransaction(); const errorMessage = @@ -206,12 +204,16 @@ export class SpaceModelService { dto.tags, dto.subspaceModels, ); - console.log(spaceTagsAfterMove); + + const modifiedSubspaces = this.tagModelService.getModifiedSubspaces( + dto.tags, + dto.subspaceModels, + ); if (dto.subspaceModels) { modifiedSubspaceModels = await this.subSpaceModelService.modifySubSpaceModels( - dto.subspaceModels, + modifiedSubspaces, spaceModel, queryRunner, ); @@ -243,7 +245,6 @@ export class SpaceModelService { message: 'SpaceModel updated successfully', }); } catch (error) { - console.log(JSON.stringify(dto)); await queryRunner.rollbackTransaction(); if (error instanceof HttpException) { throw error; diff --git a/src/space-model/services/tag-model.service.ts b/src/space-model/services/tag-model.service.ts index abb0d3a..0fb1942 100644 --- a/src/space-model/services/tag-model.service.ts +++ b/src/space-model/services/tag-model.service.ts @@ -28,6 +28,7 @@ export class TagModelService { spaceModel?: SpaceModelEntity, subspaceModel?: SubspaceModelEntity, additionalTags?: CreateTagModelDto[], + tagsToDelete?: ModifyTagModelDto[], ): Promise { if (!tags.length) { throw new HttpException('Tags cannot be empty.', HttpStatus.BAD_REQUEST); @@ -46,12 +47,14 @@ export class TagModelService { const tagEntitiesToCreate = tags.filter((tagDto) => tagDto.uuid === null); const tagEntitiesToUpdate = tags.filter((tagDto) => tagDto.uuid !== null); + try { const createdTags = await this.bulkSaveTags( tagEntitiesToCreate, queryRunner, spaceModel, subspaceModel, + tagsToDelete, ); // Update existing tags @@ -81,6 +84,7 @@ export class TagModelService { queryRunner: QueryRunner, spaceModel?: SpaceModelEntity, subspaceModel?: SubspaceModelEntity, + tagsToDelete?: ModifyTagModelDto[], ): Promise { if (!tags.length) { return []; @@ -88,7 +92,13 @@ export class TagModelService { const tagEntities = await Promise.all( tags.map((tagDto) => - this.prepareTagEntity(tagDto, queryRunner, spaceModel, subspaceModel), + this.prepareTagEntity( + tagDto, + queryRunner, + spaceModel, + subspaceModel, + tagsToDelete, + ), ), ); @@ -116,28 +126,53 @@ export class TagModelService { return []; } - return await Promise.all( - tags.map(async (tagDto) => { - const tag = await this.getTagByUuid(tagDto.uuid); - if (!tag) { - throw new HttpException( - `Tag with UUID ${tagDto.uuid} not found.`, - HttpStatus.NOT_FOUND, - ); - } + try { + return await Promise.all( + tags.map(async (tagDto) => { + try { + const tag = await this.getTagByUuid(tagDto.uuid); + if (!tag) { + throw new HttpException( + `Tag with UUID ${tagDto.uuid} not found.`, + HttpStatus.NOT_FOUND, + ); + } + + if (subspaceModel && subspaceModel.spaceModel) { + await queryRunner.manager.update( + this.tagModelRepository.target, + { uuid: tag.uuid }, + { subspaceModel, spaceModel: null }, + ); + tag.subspaceModel = subspaceModel; + } - if (subspaceModel && subspaceModel.spaceModel) { - await queryRunner.manager.update( - this.tagModelRepository.target, - { uuid: tag.uuid }, - { subspaceModel }, - ); - tag.subspaceModel = subspaceModel; - } + if (subspaceModel === null && spaceModel) { + await queryRunner.manager.update( + this.tagModelRepository.target, + { uuid: tag.uuid }, + { subspaceModel: null, spaceModel: spaceModel }, + ); + tag.subspaceModel = null; + tag.spaceModel = spaceModel; + } - return tag; - }), - ); + return tag; + } catch (error) { + console.error( + `Error moving tag with UUID ${tagDto.uuid}: ${error.message}`, + ); + throw error; // Re-throw the error to propagate it to the parent Promise.all + } + }), + ); + } catch (error) { + console.error(`Error in moveTags: ${error.message}`); + throw new HttpException( + `Failed to move tags due to an unexpected error: ${error.message}`, + HttpStatus.INTERNAL_SERVER_ERROR, + ); + } } async updateTag( @@ -231,6 +266,10 @@ export class TagModelService { deleted: [], }; try { + const tagsToDelete = tags.filter( + (tag) => tag.action === ModifyAction.DELETE, + ); + for (const tag of tags) { if (tag.action === ModifyAction.ADD) { const createTagDto: CreateTagModelDto = { @@ -239,11 +278,14 @@ export class TagModelService { productUuid: tag.productUuid as string, }; + const newModel = await this.createTags( [createTagDto], queryRunner, spaceModel, subspaceModel, + null, + tagsToDelete, ); modifiedTagModels.added.push(...newModel); } else if (tag.action === ModifyAction.UPDATE) { @@ -285,9 +327,11 @@ export class TagModelService { tag: string, productUuid: string, spaceModel: SpaceModelEntity, + tagsToDelete?: ModifyTagModelDto[], ): Promise { try { - const tagExists = await this.tagModelRepository.exists({ + // Query to find existing tags + const tagExists = await this.tagModelRepository.find({ where: [ { tag, @@ -304,16 +348,29 @@ export class TagModelService { ], }); - if (tagExists) { + + // Remove tags that are marked for deletion + const filteredTagExists = tagExists.filter( + (existingTag) => + !tagsToDelete?.some( + (deleteTag) => deleteTag.uuid === existingTag.uuid, + ), + ); + + + // If any tags remain, throw an exception + if (filteredTagExists.length > 0) { throw new HttpException( `Tag ${tag} can't be reused`, HttpStatus.CONFLICT, ); } + } catch (error) { if (error instanceof HttpException) { throw error; } + console.error(`Error while checking tag reuse: ${error.message}`); throw new HttpException( `An error occurred while checking tag reuse: ${error.message}`, HttpStatus.INTERNAL_SERVER_ERROR, @@ -326,6 +383,7 @@ export class TagModelService { queryRunner: QueryRunner, spaceModel?: SpaceModelEntity, subspaceModel?: SubspaceModelEntity, + tagsToDelete?: ModifyTagModelDto[], ): Promise { try { const product = await this.productService.findOne(tagDto.productUuid); @@ -338,7 +396,12 @@ export class TagModelService { } if (spaceModel) { - await this.checkTagReuse(tagDto.tag, tagDto.productUuid, spaceModel); + await this.checkTagReuse( + tagDto.tag, + tagDto.productUuid, + spaceModel, + tagsToDelete, + ); } else if (subspaceModel && subspaceModel.spaceModel) { await this.checkTagReuse( tagDto.tag, @@ -351,11 +414,14 @@ export class TagModelService { HttpStatus.BAD_REQUEST, ); } + const tagSpace = + spaceModel !== null ? spaceModel : subspaceModel.spaceModel; + return queryRunner.manager.create(TagModel, { tag: tagDto.tag, product: product.data, - spaceModel, - subspaceModel, + spaceModel: tagSpace, + subspaceModel: subspaceModel, }); } catch (error) { if (error instanceof HttpException) { @@ -421,16 +487,20 @@ export class TagModelService { subspaceModels: ModifySubspaceModelDto[], ): ModifyTagModelDto[] { if (!subspaceModels || subspaceModels.length === 0) { + return spaceTags; } + const spaceTagsToDelete = spaceTags.filter( (tag) => tag.action === 'delete', ); + const tagsToAdd = subspaceModels.flatMap( (subspace) => subspace.tags?.filter((tag) => tag.action === 'add') || [], ); + const commonTagUuids = new Set( tagsToAdd @@ -443,10 +513,45 @@ export class TagModelService { ); const remainingTags = spaceTags.filter( - (tag) => - !tag.uuid || commonTagUuids.size === 0 || commonTagUuids.has(tag.uuid), + (tag) => !commonTagUuids.has(tag.uuid), // Exclude tags in commonTagUuids ); return remainingTags; } + + getModifiedSubspaces( + spaceTags: ModifyTagModelDto[], + subspaceModels: ModifySubspaceModelDto[], + ): ModifySubspaceModelDto[] { + if (!subspaceModels || subspaceModels.length === 0) { + return []; + } + + // Extract tags marked for addition in spaceTags + const spaceTagsToAdd = spaceTags.filter((tag) => tag.action === 'add'); + + // Find UUIDs of tags that are common between spaceTagsToAdd and subspace tags marked for deletion + const commonTagUuids = new Set( + spaceTagsToAdd + .flatMap((tagToAdd) => + subspaceModels.flatMap( + (subspace) => + subspace.tags?.filter( + (tagToDelete) => + tagToDelete.action === 'delete' && + tagToAdd.uuid === tagToDelete.uuid, + ) || [], + ), + ) + .map((tag) => tag.uuid), + ); + + // Modify subspaceModels by removing tags with UUIDs present in commonTagUuids + const modifiedSubspaces = subspaceModels.map((subspace) => ({ + ...subspace, + tags: subspace.tags?.filter((tag) => !commonTagUuids.has(tag.uuid)) || [], + })); + + return modifiedSubspaces; + } }