From 3366f525ef0289614a0be9139e7252ac9e0a63c5 Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Wed, 29 Jan 2025 11:53:28 +0400 Subject: [PATCH 1/3] fixed moving device --- src/space-model/services/tag-model.service.ts | 6 +- src/space/dtos/tag/create-tag-dto.ts | 12 +- src/space/services/space.service.ts | 14 +- src/space/services/tag/tag.service.ts | 276 ++++++++++++++++-- 4 files changed, 274 insertions(+), 34 deletions(-) diff --git a/src/space-model/services/tag-model.service.ts b/src/space-model/services/tag-model.service.ts index 0f77b3d..67ce6a5 100644 --- a/src/space-model/services/tag-model.service.ts +++ b/src/space-model/services/tag-model.service.ts @@ -478,14 +478,14 @@ export class TagModelService { } getSubspaceTagsToBeAdded( - spaceTags: ModifyTagModelDto[], - subspaceModels: ModifySubspaceModelDto[], + spaceTags?: ModifyTagModelDto[], + subspaceModels?: ModifySubspaceModelDto[], ): ModifyTagModelDto[] { if (!subspaceModels || subspaceModels.length === 0) { return spaceTags; } - const spaceTagsToDelete = spaceTags.filter( + const spaceTagsToDelete = spaceTags?.filter( (tag) => tag.action === 'delete', ); diff --git a/src/space/dtos/tag/create-tag-dto.ts b/src/space/dtos/tag/create-tag-dto.ts index 8e89155..3e61f39 100644 --- a/src/space/dtos/tag/create-tag-dto.ts +++ b/src/space/dtos/tag/create-tag-dto.ts @@ -1,5 +1,5 @@ -import { ApiProperty } from '@nestjs/swagger'; -import { IsNotEmpty, IsString } from 'class-validator'; +import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; +import { IsNotEmpty, IsOptional, IsString } from 'class-validator'; export class CreateTagDto { @ApiProperty({ @@ -10,6 +10,14 @@ export class CreateTagDto { @IsString() tag: string; + @ApiPropertyOptional({ + description: 'UUID of the tag (required for update/delete)', + example: '123e4567-e89b-12d3-a456-426614174000', + }) + @IsOptional() + @IsString() + uuid?: string; + @ApiProperty({ description: 'ID of the product associated with the tag', example: '123e4567-e89b-12d3-a456-426614174000', diff --git a/src/space/services/space.service.ts b/src/space/services/space.service.ts index 267d1d1..394fd9f 100644 --- a/src/space/services/space.service.ts +++ b/src/space/services/space.service.ts @@ -356,16 +356,26 @@ export class SpaceService { } if (hasSubspace) { - await this.subSpaceService.modifySubSpace( + const modifiedSubspaces = this.tagService.getModifiedSubspaces( + updateSpaceDto.tags, updateSpaceDto.subspace, + ); + + await this.subSpaceService.modifySubSpace( + modifiedSubspaces, queryRunner, space, ); } if (hasTags) { - await this.tagService.modifyTags( + const spaceTagsAfterMove = this.tagService.getSubspaceTagsToBeAdded( updateSpaceDto.tags, + updateSpaceDto.subspace, + ); + + await this.tagService.modifyTags( + spaceTagsAfterMove, queryRunner, space, ); diff --git a/src/space/services/tag/tag.service.ts b/src/space/services/tag/tag.service.ts index be32a2e..94c157d 100644 --- a/src/space/services/tag/tag.service.ts +++ b/src/space/services/tag/tag.service.ts @@ -8,7 +8,8 @@ import { import { TagModel } from '@app/common/modules/space-model'; import { HttpException, HttpStatus, Injectable } from '@nestjs/common'; import { ProductService } from 'src/product/services'; -import { CreateTagDto } from 'src/space/dtos'; +import { ModifyTagModelDto } from 'src/space-model/dtos'; +import { CreateTagDto, ModifySubspaceDto } from 'src/space/dtos'; import { ModifyTagDto } from 'src/space/dtos/tag/modify-tag.dto'; import { QueryRunner } from 'typeorm'; @@ -25,25 +26,133 @@ export class TagService { space?: SpaceEntity, subspace?: SubspaceEntity, additionalTags?: CreateTagDto[], + tagsToDelete?: ModifyTagDto[], ): Promise { this.validateTagsInput(tags); const combinedTags = this.combineTags(tags, additionalTags); this.ensureNoDuplicateTags(combinedTags); + const tagEntitiesToCreate = tags.filter((tagDto) => tagDto.uuid === null); + const tagEntitiesToUpdate = tags.filter((tagDto) => tagDto.uuid !== null); + try { - const tagEntities = await Promise.all( - tags.map(async (tagDto) => - this.prepareTagEntity(tagDto, queryRunner, space, subspace), - ), + const createdTags = await this.bulkSaveTags( + tagEntitiesToCreate, + queryRunner, + space, + subspace, + tagsToDelete, + ); + const updatedTags = await this.moveTags( + tagEntitiesToUpdate, + queryRunner, + space, + subspace, ); - return await queryRunner.manager.save(tagEntities); + return [...createdTags, ...updatedTags]; } catch (error) { throw this.handleUnexpectedError('Failed to save tags', error); } } + async bulkSaveTags( + tags: CreateTagDto[], + queryRunner: QueryRunner, + space?: SpaceEntity, + subspace?: SubspaceEntity, + tagsToDelete?: ModifyTagDto[], + ): Promise { + if (!tags.length) { + return []; + } + + const tagEntities = await Promise.all( + tags.map((tagDto) => + this.prepareTagEntity( + tagDto, + queryRunner, + space, + subspace, + tagsToDelete, + ), + ), + ); + + try { + return await queryRunner.manager.save(tagEntities); + } catch (error) { + if (error instanceof HttpException) { + throw error; + } + + throw new HttpException( + `Failed to save tag models due to an unexpected error: ${error.message}`, + HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + } + + async moveTags( + tags: CreateTagDto[], + queryRunner: QueryRunner, + space?: SpaceEntity, + subspace?: SubspaceEntity, + ): Promise { + if (!tags.length) { + return []; + } + + 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 (subspace && subspace.space) { + await queryRunner.manager.update( + this.tagRepository.target, + { uuid: tag.uuid }, + { subspace, space: null }, + ); + tag.subspace = subspace; + } + + if (subspace === null && space) { + await queryRunner.manager.update( + this.tagRepository.target, + { uuid: tag.uuid }, + { subspace: null, space: space }, + ); + tag.subspace = null; + tag.space = space; + } + + 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 createTagsFromModel( queryRunner: QueryRunner, tagModels: TagModel[], @@ -170,6 +279,7 @@ export class TagService { ); } } + async modifyTags( tags: ModifyTagDto[], queryRunner: QueryRunner, @@ -179,15 +289,27 @@ export class TagService { if (!tags?.length) return; try { + const tagsToDelete = tags.filter( + (tag) => tag.action === ModifyAction.DELETE, + ); + await Promise.all( tags.map(async (tag) => { switch (tag.action) { case ModifyAction.ADD: await this.createTags( - [{ tag: tag.tag, productUuid: tag.productUuid }], + [ + { + tag: tag.tag, + productUuid: tag.productUuid, + uuid: tag.uuid, + }, + ], queryRunner, space, subspace, + null, + tagsToDelete, ); break; case ModifyAction.UPDATE: @@ -195,7 +317,6 @@ export class TagService { break; case ModifyAction.DELETE: await this.deleteTags([tag.uuid], queryRunner); - break; default: throw new HttpException( @@ -244,10 +365,11 @@ export class TagService { tag: string, productUuid: string, space: SpaceEntity, + tagsToDelete?: ModifyTagDto[], ): Promise { const { uuid: spaceUuid } = space; - const tagExists = await this.tagRepository.exists({ + const tagExists = await this.tagRepository.find({ where: [ { tag, @@ -264,7 +386,12 @@ export class TagService { ], }); - if (tagExists) { + const filteredTagExists = tagExists.filter( + (existingTag) => + !tagsToDelete?.some((deleteTag) => deleteTag.uuid === existingTag.uuid), + ); + + if (filteredTagExists.length > 0) { throw new HttpException(`Tag can't be reused`, HttpStatus.CONFLICT); } } @@ -274,28 +401,54 @@ export class TagService { queryRunner: QueryRunner, space?: SpaceEntity, subspace?: SubspaceEntity, + tagsToDelete?: ModifyTagDto[], ): Promise { - const product = await this.productService.findOne(tagDto.productUuid); + try { + const product = await this.productService.findOne(tagDto.productUuid); - if (!product) { + if (!product) { + throw new HttpException( + `Product with UUID ${tagDto.productUuid} not found.`, + HttpStatus.NOT_FOUND, + ); + } + + if (space) { + await this.checkTagReuse( + tagDto.tag, + tagDto.productUuid, + space, + tagsToDelete, + ); + } else if (subspace && subspace.space) { + await this.checkTagReuse( + tagDto.tag, + tagDto.productUuid, + subspace.space, + ); + } else { + throw new HttpException( + `Invalid subspace or space provided.`, + HttpStatus.BAD_REQUEST, + ); + } + const tagSpace = space !== null ? space : subspace.space; + + return queryRunner.manager.create(TagEntity, { + tag: tagDto.tag, + product: product.data, + space: tagSpace, + subspace: subspace, + }); + } catch (error) { + if (error instanceof HttpException) { + throw error; + } throw new HttpException( - `Product with UUID ${tagDto.productUuid} not found.`, - HttpStatus.NOT_FOUND, + `An error occurred while preparing the tag entity: ${error.message}`, + HttpStatus.INTERNAL_SERVER_ERROR, ); } - - await this.checkTagReuse( - tagDto.tag, - tagDto.productUuid, - space ?? subspace.space, - ); - - return queryRunner.manager.create(TagEntity, { - tag: tagDto.tag, - product: product.data, - space, - subspace, - }); } private async getTagByUuid(uuid: string): Promise { @@ -347,4 +500,73 @@ export class TagService { return; } } + + getSubspaceTagsToBeAdded( + spaceTags?: ModifyTagDto[], + subspaceModels?: ModifySubspaceDto[], + ): ModifyTagDto[] { + 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 + .filter((tagToAdd) => + spaceTagsToDelete.some( + (tagToDelete) => tagToAdd.uuid === tagToDelete.uuid, + ), + ) + .map((tag) => tag.uuid), + ); + + const remainingTags = spaceTags.filter( + (tag) => !commonTagUuids.has(tag.uuid), // Exclude tags in commonTagUuids + ); + + return remainingTags; + } + + getModifiedSubspaces( + spaceTags?: ModifyTagDto[], + subspaceModels?: ModifySubspaceDto[], + ): ModifySubspaceDto[] { + 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; + } } From 64a056cf95fe5a9c0e75178859b2e2be91cc61f1 Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Wed, 29 Jan 2025 14:02:26 +0400 Subject: [PATCH 2/3] tag fix --- src/space-model/services/space-model.service.ts | 2 +- src/space-model/services/tag-model.service.ts | 4 +--- src/space/services/tag/tag.service.ts | 3 +-- 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/src/space-model/services/space-model.service.ts b/src/space-model/services/space-model.service.ts index 05906ca..82054d5 100644 --- a/src/space-model/services/space-model.service.ts +++ b/src/space-model/services/space-model.service.ts @@ -395,7 +395,7 @@ export class SpaceModelService { if (!spaceModel) { throw new HttpException('space model not found', HttpStatus.NOT_FOUND); } - + console.log(JSON.stringify(spaceModel)); return spaceModel; } } diff --git a/src/space-model/services/tag-model.service.ts b/src/space-model/services/tag-model.service.ts index 67ce6a5..3f6a493 100644 --- a/src/space-model/services/tag-model.service.ts +++ b/src/space-model/services/tag-model.service.ts @@ -409,13 +409,11 @@ 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: tagSpace, + spaceModel: spaceModel, subspaceModel: subspaceModel, }); } catch (error) { diff --git a/src/space/services/tag/tag.service.ts b/src/space/services/tag/tag.service.ts index 94c157d..03bc42d 100644 --- a/src/space/services/tag/tag.service.ts +++ b/src/space/services/tag/tag.service.ts @@ -432,12 +432,11 @@ export class TagService { HttpStatus.BAD_REQUEST, ); } - const tagSpace = space !== null ? space : subspace.space; return queryRunner.manager.create(TagEntity, { tag: tagDto.tag, product: product.data, - space: tagSpace, + space: space, subspace: subspace, }); } catch (error) { From 1b02f1c86affa3403496140cf69439c0855e0c66 Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Wed, 29 Jan 2025 14:55:05 +0400 Subject: [PATCH 3/3] add tag_id --- libs/common/src/modules/device/entities/device.entity.ts | 1 + src/space-model/services/space-model.service.ts | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/common/src/modules/device/entities/device.entity.ts b/libs/common/src/modules/device/entities/device.entity.ts index bdd1ce5..f2a5a1e 100644 --- a/libs/common/src/modules/device/entities/device.entity.ts +++ b/libs/common/src/modules/device/entities/device.entity.ts @@ -78,6 +78,7 @@ export class DeviceEntity extends AbstractEntity { @OneToOne(() => TagEntity, (tag) => tag.device, { nullable: true, }) + @JoinColumn({ name: 'tag_id' }) tag: TagEntity; constructor(partial: Partial) { diff --git a/src/space-model/services/space-model.service.ts b/src/space-model/services/space-model.service.ts index 82054d5..2162c38 100644 --- a/src/space-model/services/space-model.service.ts +++ b/src/space-model/services/space-model.service.ts @@ -395,7 +395,6 @@ export class SpaceModelService { if (!spaceModel) { throw new HttpException('space model not found', HttpStatus.NOT_FOUND); } - console.log(JSON.stringify(spaceModel)); return spaceModel; } }