diff --git a/src/space-model/commands/index.ts b/src/space-model/commands/index.ts index 4a7aafa..59f7dfc 100644 --- a/src/space-model/commands/index.ts +++ b/src/space-model/commands/index.ts @@ -1,2 +1,3 @@ export * from './propagate-space-model-deletion.command'; export * from './propagate-subspace-model-update-command'; +export * from './propagate-space-model-product-allocation-command'; \ No newline at end of file diff --git a/src/space-model/commands/propagate-space-model-product-allocation-command.ts b/src/space-model/commands/propagate-space-model-product-allocation-command.ts new file mode 100644 index 0000000..1240bd3 --- /dev/null +++ b/src/space-model/commands/propagate-space-model-product-allocation-command.ts @@ -0,0 +1,16 @@ +import { ICommand } from '@nestjs/cqrs'; +import { SpaceModelEntity } from '@app/common/modules/space-model'; +import { IUpdatedSpaceAllocations } from '../interfaces'; +import { SpaceEntity } from '@app/common/modules/space/entities/space.entity'; + +export class PropogateUpdateSpaceModelProductAllocationCommand + implements ICommand +{ + constructor( + public readonly param: { + spaceModel: SpaceModelEntity; + updatedAllocations: IUpdatedSpaceAllocations[]; + spaces: SpaceEntity[]; + }, + ) {} +} diff --git a/src/space-model/handlers/index.ts b/src/space-model/handlers/index.ts index d7bb550..ff9ffbb 100644 --- a/src/space-model/handlers/index.ts +++ b/src/space-model/handlers/index.ts @@ -1,2 +1,3 @@ -export * from './propate-subspace-handler'; +export * from './propogate-subspace-handler'; export * from './propogate-space-model-deletion.handler'; +export * from './propogate-space-model-product-allocation.handler'; diff --git a/src/space-model/handlers/propogate-space-model-product-allocation.handler.ts b/src/space-model/handlers/propogate-space-model-product-allocation.handler.ts new file mode 100644 index 0000000..51f2623 --- /dev/null +++ b/src/space-model/handlers/propogate-space-model-product-allocation.handler.ts @@ -0,0 +1,123 @@ +import { CommandHandler, ICommandHandler } from '@nestjs/cqrs'; +import { PropogateUpdateSpaceModelProductAllocationCommand } from '../commands'; +import { SpaceProductAllocationRepository } from '@app/common/modules/space'; +import { SubspaceModelProductAllocationRepoitory } from '@app/common/modules/space-model'; +import { + SubspaceRepository, + SubspaceProductAllocationRepository, +} from '@app/common/modules/space/repositories/subspace.repository'; + +@CommandHandler(PropogateUpdateSpaceModelProductAllocationCommand) +export class PropogateUpdateSpaceModelProductAllocationHandler + implements ICommandHandler +{ + constructor( + private readonly subspaceRepository: SubspaceRepository, + private readonly subspaceModelProductRepository: SubspaceModelProductAllocationRepoitory, + private readonly subspaceProductRepository: SubspaceProductAllocationRepository, + private readonly spaceProductRepository: SpaceProductAllocationRepository, + ) {} + + async execute( + command: PropogateUpdateSpaceModelProductAllocationCommand, + ): Promise { + const { spaces, updatedAllocations } = command.param; + + try { + if (!updatedAllocations || updatedAllocations.length === 0) { + console.log('No updated allocations found. Exiting execution.'); + return; + } + + console.log(`Processing ${updatedAllocations.length} allocations...`); + + for (const allocation of updatedAllocations) { + try { + if (allocation.allocation) { + const spaceAllocations = await this.spaceProductRepository.find({ + where: { uuid: allocation.allocation.uuid }, + relations: ['tags'], + }); + + if (!spaceAllocations || spaceAllocations.length === 0) { + console.warn( + `No space allocations found for UUID: ${allocation.allocation.uuid}`, + ); + continue; + } + + if (allocation.tagsAdded?.length) { + for (const spaceAllocation of spaceAllocations) { + spaceAllocation.tags.push(...allocation.tagsAdded); + } + await this.spaceProductRepository.save(spaceAllocations); + console.log( + `Added tags to ${spaceAllocations.length} space allocations.`, + ); + } + + if (allocation.tagsRemoved?.length) { + const tagsToRemoveUUIDs = new Set( + allocation.tagsRemoved.map((tag) => tag.uuid), + ); + + for (const spaceAllocation of spaceAllocations) { + spaceAllocation.tags = spaceAllocation.tags.filter( + (tag) => !tagsToRemoveUUIDs.has(tag.uuid), + ); + } + await this.spaceProductRepository.save(spaceAllocations); + console.log( + `Removed tags from ${spaceAllocations.length} space allocations.`, + ); + } + } + + if (allocation.deletedAllocation) { + const spaceAllocations = await this.spaceProductRepository.find({ + where: { uuid: allocation.deletedAllocation.uuid }, + relations: ['tags'], + }); + + if (!spaceAllocations || spaceAllocations.length === 0) { + console.warn( + `No space allocations found to delete for UUID: ${allocation.deletedAllocation.uuid}`, + ); + continue; + } + + await this.spaceProductRepository.remove(spaceAllocations); + console.log( + `Deleted ${spaceAllocations.length} space allocations.`, + ); + } + + if (allocation.newAllocation) { + const newAllocations = spaces.map((space) => + this.spaceProductRepository.create({ + space, + product: allocation.newAllocation.product, + tags: allocation.newAllocation.tags, + inheritedFromModel: allocation.newAllocation, + }), + ); + + await this.spaceProductRepository.save(newAllocations); + console.log( + `Created ${newAllocations.length} new space allocations.`, + ); + } + } catch (error) { + console.error( + `Error processing allocation update: ${JSON.stringify(allocation)}`, + error, + ); + } + } + + console.log('Finished processing all allocations.'); + } catch (error) { + console.error('Unexpected error during execute method:', error); + } + } +} diff --git a/src/space-model/handlers/propogate-subspace-handler.ts b/src/space-model/handlers/propogate-subspace-handler.ts index 68b36b4..d651d85 100644 --- a/src/space-model/handlers/propogate-subspace-handler.ts +++ b/src/space-model/handlers/propogate-subspace-handler.ts @@ -1,16 +1,12 @@ import { CommandHandler, ICommandHandler } from '@nestjs/cqrs'; import { PropogateUpdateSpaceModelCommand } from '../commands'; -import { - SpaceProductAllocationRepository, - SpaceRepository, -} from '@app/common/modules/space'; +import { SpaceProductAllocationRepository } from '@app/common/modules/space'; import { SubspaceProductAllocationRepository, SubspaceRepository, } from '@app/common/modules/space/repositories/subspace.repository'; import { SubspaceModelProductAllocationRepoitory } from '@app/common/modules/space-model'; import { In } from 'typeorm'; -import { TagService } from 'src/space/services/tag'; import { ISingleSubspaceModel } from '../interfaces'; import { SpaceEntity } from '@app/common/modules/space/entities/space.entity'; import { ModifyAction } from '@app/common/constants/modify-action.enum'; @@ -21,9 +17,7 @@ export class PropogateUpdateSpaceModelHandler implements ICommandHandler { constructor( - private readonly spaceRepository: SpaceRepository, private readonly subspaceRepository: SubspaceRepository, - private readonly tagService: TagService, private readonly subspaceModelProductRepository: SubspaceModelProductAllocationRepoitory, private readonly subspaceProductRepository: SubspaceProductAllocationRepository, private readonly spaceProductRepository: SpaceProductAllocationRepository, diff --git a/src/space-model/interfaces/index.ts b/src/space-model/interfaces/index.ts index 6d55822..61561f8 100644 --- a/src/space-model/interfaces/index.ts +++ b/src/space-model/interfaces/index.ts @@ -1,3 +1,4 @@ export * from './update-subspace.interface'; export * from './modify-subspace.interface'; export * from './single-subspace.interface'; +export * from './space-product-allocation.interface'; diff --git a/src/space-model/interfaces/space-product-allocation.interface.ts b/src/space-model/interfaces/space-product-allocation.interface.ts new file mode 100644 index 0000000..fe8e3fc --- /dev/null +++ b/src/space-model/interfaces/space-product-allocation.interface.ts @@ -0,0 +1,10 @@ +import { SpaceModelProductAllocationEntity } from '@app/common/modules/space-model'; +import { NewTagEntity } from '@app/common/modules/tag'; + +export type IUpdatedSpaceAllocations = { + allocation?: SpaceModelProductAllocationEntity; + tagsRemoved?: NewTagEntity[]; + tagsAdded?: NewTagEntity[]; + newAllocation?: SpaceModelProductAllocationEntity; + deletedAllocation?: SpaceModelProductAllocationEntity; +}; diff --git a/src/space-model/services/space-model-product-allocation.service.ts b/src/space-model/services/space-model-product-allocation.service.ts index aa2c3da..d8f78a6 100644 --- a/src/space-model/services/space-model-product-allocation.service.ts +++ b/src/space-model/services/space-model-product-allocation.service.ts @@ -15,6 +15,7 @@ import { NewTagEntity } from '@app/common/modules/tag'; import { ProductEntity } from '@app/common/modules/product/entities'; import { SpaceRepository } from '@app/common/modules/space'; import { ProjectEntity } from '@app/common/modules/project/entities'; +import { IUpdatedSpaceAllocations } from '../interfaces'; @Injectable() export class SpaceModelProductAllocationService { @@ -30,10 +31,12 @@ export class SpaceModelProductAllocationService { tags: ProcessTagDto[], queryRunner?: QueryRunner, modifySubspaceModels?: ModifySubspaceModelDto[], - ): Promise { + ): Promise { try { if (!tags.length) return []; + const allocationUpdates: IUpdatedSpaceAllocations[] = []; + const processedTags = await this.tagService.processTags( tags, projectUuid, @@ -108,8 +111,15 @@ export class SpaceModelProductAllocationService { if (!allocation) { allocation = this.createNewAllocation(spaceModel, tag, queryRunner); productAllocations.push(allocation); + allocationUpdates.push({ + newAllocation: allocation, + }); } else if (!allocation.tags.some((t) => t.uuid === tag.uuid)) { allocation.tags.push(tag); + allocationUpdates.push({ + allocation: allocation, + tagsAdded: [tag], + }); await this.saveAllocation(allocation, queryRunner); } } @@ -119,7 +129,7 @@ export class SpaceModelProductAllocationService { await this.saveAllocations(productAllocations, queryRunner); } - return productAllocations; + return allocationUpdates; } catch (error) { throw this.handleError(error, 'Failed to create product allocations'); } @@ -131,7 +141,7 @@ export class SpaceModelProductAllocationService { spaceModel: SpaceModelEntity, queryRunner: QueryRunner, modifySubspaceModels?: ModifySubspaceModelDto[], - ): Promise { + ): Promise { try { const addDtos = dtos.filter((dto) => dto.action === ModifyAction.ADD); const deleteDtos = dtos.filter( @@ -144,11 +154,13 @@ export class SpaceModelProductAllocationService { uuid: dto.newTagUuid, })); + // Process added tags const processedTags = await this.tagService.processTags( addTagDtos, project.uuid, queryRunner, ); + const addTagUuidMap = new Map(); processedTags.forEach((tag, index) => { addTagUuidMap.set(tag.uuid, addDtos[index]); @@ -161,6 +173,7 @@ export class SpaceModelProductAllocationService { [...addTagUuids].filter((uuid) => deleteTagUuids.has(uuid)), ); + // Filter out tags that are added and deleted in the same request const filteredDtos = dtos.filter( (dto) => !( @@ -174,7 +187,8 @@ export class SpaceModelProductAllocationService { ), ); - await Promise.all([ + // Process add and delete actions concurrently + const [updatedAllocations, deletedAllocations] = await Promise.all([ this.processAddActions( filteredDtos, project.uuid, @@ -184,6 +198,9 @@ export class SpaceModelProductAllocationService { ), this.processDeleteActions(filteredDtos, queryRunner, spaceModel), ]); + + // Combine results and return + return [...updatedAllocations, ...deletedAllocations]; } catch (error) { throw this.handleError(error, 'Error while updating product allocations'); } @@ -195,7 +212,9 @@ export class SpaceModelProductAllocationService { spaceModel: SpaceModelEntity, queryRunner: QueryRunner, modifySubspaceModels?: ModifySubspaceModelDto[], - ): Promise { + ): Promise { + let allocationUpdates: IUpdatedSpaceAllocations[] = []; + const addDtos: ProcessTagDto[] = dtos .filter((dto) => dto.action === ModifyAction.ADD) .map((dto) => ({ @@ -205,7 +224,7 @@ export class SpaceModelProductAllocationService { })); if (addDtos.length > 0) { - await this.createProductAllocations( + allocationUpdates = await this.createProductAllocations( projectUuid, spaceModel, addDtos, @@ -213,6 +232,7 @@ export class SpaceModelProductAllocationService { modifySubspaceModels, ); } + return allocationUpdates; } private createNewAllocation( @@ -292,11 +312,12 @@ export class SpaceModelProductAllocationService { dtos: ModifyTagModelDto[], queryRunner: QueryRunner, spaceModel: SpaceModelEntity, - ): Promise { + ): Promise { try { if (!dtos || dtos.length === 0) { return; } + let allocationUpdateToPropagate: IUpdatedSpaceAllocations[] = []; const tagUuidsToDelete = dtos .filter((dto) => dto.action === ModifyAction.DELETE && dto.tagUuid) @@ -331,15 +352,26 @@ export class SpaceModelProductAllocationService { (tag) => !tagUuidsToDelete.includes(tag.uuid), ); + const deletedTags = allocation.tags.filter((tag) => + tagUuidsToDelete.includes(tag.uuid), + ); + if (updatedTags.length === allocation.tags.length) { continue; } if (updatedTags.length === 0) { deletedAllocations.push(allocation); + allocationUpdateToPropagate.push({ + deletedAllocation: allocation, + }); } else { allocation.tags = updatedTags; allocationUpdates.push(allocation); + allocationUpdateToPropagate.push({ + allocation: allocation, + tagsRemoved: deletedTags, + }); } } @@ -372,7 +404,7 @@ export class SpaceModelProductAllocationService { ) .execute(); - return deletedAllocations; + return allocationUpdateToPropagate; } catch (error) { throw this.handleError(error, `Failed to delete tags in space model`); } diff --git a/src/space-model/services/space-model.service.ts b/src/space-model/services/space-model.service.ts index eed3e34..cdc0ec6 100644 --- a/src/space-model/services/space-model.service.ts +++ b/src/space-model/services/space-model.service.ts @@ -28,6 +28,7 @@ import { SpaceModelProductAllocationService } from './space-model-product-alloca import { PropogateDeleteSpaceModelCommand, PropogateUpdateSpaceModelCommand, + PropogateUpdateSpaceModelProductAllocationCommand, } from '../commands'; import { SpaceProductAllocationRepository, @@ -47,7 +48,11 @@ import { SpaceProductAllocationEntity } from '@app/common/modules/space/entities import { SubspaceEntity } from '@app/common/modules/space/entities/subspace/subspace.entity'; import { SubspaceProductAllocationEntity } from '@app/common/modules/space/entities/subspace/subspace-product-allocation.entity'; import { DeviceEntity } from '@app/common/modules/device/entities'; -import { ISingleSubspaceModel, ISubspaceModelUpdates } from '../interfaces'; +import { + ISingleSubspaceModel, + ISubspaceModelUpdates, + IUpdatedSpaceAllocations, +} from '../interfaces'; @Injectable() export class SpaceModelService { @@ -200,6 +205,7 @@ export class SpaceModelService { ); await queryRunner.connect(); let modifiedSubspaces: ISubspaceModelUpdates; + let modifiedProductAllocations: IUpdatedSpaceAllocations[]; try { await queryRunner.startTransaction(); const spaces = await this.fetchModelSpaces(spaceModel); @@ -230,24 +236,35 @@ export class SpaceModelService { } if (dto.tags) { - await this.spaceModelProductAllocationService.updateProductAllocations( - dto.tags, - project, - spaceModel, - queryRunner, - dto.subspaceModels, - ); + modifiedProductAllocations = + await this.spaceModelProductAllocationService.updateProductAllocations( + dto.tags, + project, + spaceModel, + queryRunner, + dto.subspaceModels, + ); } await queryRunner.commitTransaction(); await this.commandBus.execute( new PropogateUpdateSpaceModelCommand({ spaceModel: spaceModel, - subspaceModels: modifiedSubspaces, + subspaceUpdates: modifiedSubspaces, spaces: spaces, }), ); + if (modifiedProductAllocations?.length > 0) { + await this.commandBus.execute( + new PropogateUpdateSpaceModelProductAllocationCommand({ + spaceModel: spaceModel, + updatedAllocations: modifiedProductAllocations, + spaces: spaces, + }), + ); + } + return new SuccessResponseDto({ message: 'SpaceModel updated successfully', }); diff --git a/src/space-model/space-model.module.ts b/src/space-model/space-model.module.ts index d49f590..41aebed 100644 --- a/src/space-model/space-model.module.ts +++ b/src/space-model/space-model.module.ts @@ -15,6 +15,7 @@ import { ProductRepository } from '@app/common/modules/product/repositories'; import { PropogateDeleteSpaceModelHandler, PropogateUpdateSpaceModelHandler, + PropogateUpdateSpaceModelProductAllocationHandler, } from './handlers'; import { CqrsModule } from '@nestjs/cqrs'; import { @@ -60,6 +61,7 @@ import { AutomationRepository } from '@app/common/modules/automation/repositorie const CommandHandlers = [ PropogateUpdateSpaceModelHandler, PropogateDeleteSpaceModelHandler, + PropogateUpdateSpaceModelProductAllocationHandler, ]; @Module({