updated space product model allocation

This commit is contained in:
hannathkadher
2025-03-12 23:08:18 +04:00
parent 537f20f8de
commit 8cbbb3a8ba
10 changed files with 222 additions and 25 deletions

View File

@ -1,2 +1,3 @@
export * from './propagate-space-model-deletion.command'; export * from './propagate-space-model-deletion.command';
export * from './propagate-subspace-model-update-command'; export * from './propagate-subspace-model-update-command';
export * from './propagate-space-model-product-allocation-command';

View File

@ -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[];
},
) {}
}

View File

@ -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-deletion.handler';
export * from './propogate-space-model-product-allocation.handler';

View File

@ -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<PropogateUpdateSpaceModelProductAllocationCommand>
{
constructor(
private readonly subspaceRepository: SubspaceRepository,
private readonly subspaceModelProductRepository: SubspaceModelProductAllocationRepoitory,
private readonly subspaceProductRepository: SubspaceProductAllocationRepository,
private readonly spaceProductRepository: SpaceProductAllocationRepository,
) {}
async execute(
command: PropogateUpdateSpaceModelProductAllocationCommand,
): Promise<void> {
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);
}
}
}

View File

@ -1,16 +1,12 @@
import { CommandHandler, ICommandHandler } from '@nestjs/cqrs'; import { CommandHandler, ICommandHandler } from '@nestjs/cqrs';
import { PropogateUpdateSpaceModelCommand } from '../commands'; import { PropogateUpdateSpaceModelCommand } from '../commands';
import { import { SpaceProductAllocationRepository } from '@app/common/modules/space';
SpaceProductAllocationRepository,
SpaceRepository,
} from '@app/common/modules/space';
import { import {
SubspaceProductAllocationRepository, SubspaceProductAllocationRepository,
SubspaceRepository, SubspaceRepository,
} from '@app/common/modules/space/repositories/subspace.repository'; } from '@app/common/modules/space/repositories/subspace.repository';
import { SubspaceModelProductAllocationRepoitory } from '@app/common/modules/space-model'; import { SubspaceModelProductAllocationRepoitory } from '@app/common/modules/space-model';
import { In } from 'typeorm'; import { In } from 'typeorm';
import { TagService } from 'src/space/services/tag';
import { ISingleSubspaceModel } from '../interfaces'; import { ISingleSubspaceModel } from '../interfaces';
import { SpaceEntity } from '@app/common/modules/space/entities/space.entity'; import { SpaceEntity } from '@app/common/modules/space/entities/space.entity';
import { ModifyAction } from '@app/common/constants/modify-action.enum'; import { ModifyAction } from '@app/common/constants/modify-action.enum';
@ -21,9 +17,7 @@ export class PropogateUpdateSpaceModelHandler
implements ICommandHandler<PropogateUpdateSpaceModelCommand> implements ICommandHandler<PropogateUpdateSpaceModelCommand>
{ {
constructor( constructor(
private readonly spaceRepository: SpaceRepository,
private readonly subspaceRepository: SubspaceRepository, private readonly subspaceRepository: SubspaceRepository,
private readonly tagService: TagService,
private readonly subspaceModelProductRepository: SubspaceModelProductAllocationRepoitory, private readonly subspaceModelProductRepository: SubspaceModelProductAllocationRepoitory,
private readonly subspaceProductRepository: SubspaceProductAllocationRepository, private readonly subspaceProductRepository: SubspaceProductAllocationRepository,
private readonly spaceProductRepository: SpaceProductAllocationRepository, private readonly spaceProductRepository: SpaceProductAllocationRepository,

View File

@ -1,3 +1,4 @@
export * from './update-subspace.interface'; export * from './update-subspace.interface';
export * from './modify-subspace.interface'; export * from './modify-subspace.interface';
export * from './single-subspace.interface'; export * from './single-subspace.interface';
export * from './space-product-allocation.interface';

View File

@ -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;
};

View File

@ -15,6 +15,7 @@ import { NewTagEntity } from '@app/common/modules/tag';
import { ProductEntity } from '@app/common/modules/product/entities'; import { ProductEntity } from '@app/common/modules/product/entities';
import { SpaceRepository } from '@app/common/modules/space'; import { SpaceRepository } from '@app/common/modules/space';
import { ProjectEntity } from '@app/common/modules/project/entities'; import { ProjectEntity } from '@app/common/modules/project/entities';
import { IUpdatedSpaceAllocations } from '../interfaces';
@Injectable() @Injectable()
export class SpaceModelProductAllocationService { export class SpaceModelProductAllocationService {
@ -30,10 +31,12 @@ export class SpaceModelProductAllocationService {
tags: ProcessTagDto[], tags: ProcessTagDto[],
queryRunner?: QueryRunner, queryRunner?: QueryRunner,
modifySubspaceModels?: ModifySubspaceModelDto[], modifySubspaceModels?: ModifySubspaceModelDto[],
): Promise<SpaceModelProductAllocationEntity[]> { ): Promise<IUpdatedSpaceAllocations[]> {
try { try {
if (!tags.length) return []; if (!tags.length) return [];
const allocationUpdates: IUpdatedSpaceAllocations[] = [];
const processedTags = await this.tagService.processTags( const processedTags = await this.tagService.processTags(
tags, tags,
projectUuid, projectUuid,
@ -108,8 +111,15 @@ export class SpaceModelProductAllocationService {
if (!allocation) { if (!allocation) {
allocation = this.createNewAllocation(spaceModel, tag, queryRunner); allocation = this.createNewAllocation(spaceModel, tag, queryRunner);
productAllocations.push(allocation); productAllocations.push(allocation);
allocationUpdates.push({
newAllocation: allocation,
});
} else if (!allocation.tags.some((t) => t.uuid === tag.uuid)) { } else if (!allocation.tags.some((t) => t.uuid === tag.uuid)) {
allocation.tags.push(tag); allocation.tags.push(tag);
allocationUpdates.push({
allocation: allocation,
tagsAdded: [tag],
});
await this.saveAllocation(allocation, queryRunner); await this.saveAllocation(allocation, queryRunner);
} }
} }
@ -119,7 +129,7 @@ export class SpaceModelProductAllocationService {
await this.saveAllocations(productAllocations, queryRunner); await this.saveAllocations(productAllocations, queryRunner);
} }
return productAllocations; return allocationUpdates;
} catch (error) { } catch (error) {
throw this.handleError(error, 'Failed to create product allocations'); throw this.handleError(error, 'Failed to create product allocations');
} }
@ -131,7 +141,7 @@ export class SpaceModelProductAllocationService {
spaceModel: SpaceModelEntity, spaceModel: SpaceModelEntity,
queryRunner: QueryRunner, queryRunner: QueryRunner,
modifySubspaceModels?: ModifySubspaceModelDto[], modifySubspaceModels?: ModifySubspaceModelDto[],
): Promise<void> { ): Promise<IUpdatedSpaceAllocations[]> {
try { try {
const addDtos = dtos.filter((dto) => dto.action === ModifyAction.ADD); const addDtos = dtos.filter((dto) => dto.action === ModifyAction.ADD);
const deleteDtos = dtos.filter( const deleteDtos = dtos.filter(
@ -144,11 +154,13 @@ export class SpaceModelProductAllocationService {
uuid: dto.newTagUuid, uuid: dto.newTagUuid,
})); }));
// Process added tags
const processedTags = await this.tagService.processTags( const processedTags = await this.tagService.processTags(
addTagDtos, addTagDtos,
project.uuid, project.uuid,
queryRunner, queryRunner,
); );
const addTagUuidMap = new Map<string, ModifyTagModelDto>(); const addTagUuidMap = new Map<string, ModifyTagModelDto>();
processedTags.forEach((tag, index) => { processedTags.forEach((tag, index) => {
addTagUuidMap.set(tag.uuid, addDtos[index]); addTagUuidMap.set(tag.uuid, addDtos[index]);
@ -161,6 +173,7 @@ export class SpaceModelProductAllocationService {
[...addTagUuids].filter((uuid) => deleteTagUuids.has(uuid)), [...addTagUuids].filter((uuid) => deleteTagUuids.has(uuid)),
); );
// Filter out tags that are added and deleted in the same request
const filteredDtos = dtos.filter( const filteredDtos = dtos.filter(
(dto) => (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( this.processAddActions(
filteredDtos, filteredDtos,
project.uuid, project.uuid,
@ -184,6 +198,9 @@ export class SpaceModelProductAllocationService {
), ),
this.processDeleteActions(filteredDtos, queryRunner, spaceModel), this.processDeleteActions(filteredDtos, queryRunner, spaceModel),
]); ]);
// Combine results and return
return [...updatedAllocations, ...deletedAllocations];
} catch (error) { } catch (error) {
throw this.handleError(error, 'Error while updating product allocations'); throw this.handleError(error, 'Error while updating product allocations');
} }
@ -195,7 +212,9 @@ export class SpaceModelProductAllocationService {
spaceModel: SpaceModelEntity, spaceModel: SpaceModelEntity,
queryRunner: QueryRunner, queryRunner: QueryRunner,
modifySubspaceModels?: ModifySubspaceModelDto[], modifySubspaceModels?: ModifySubspaceModelDto[],
): Promise<void> { ): Promise<IUpdatedSpaceAllocations[]> {
let allocationUpdates: IUpdatedSpaceAllocations[] = [];
const addDtos: ProcessTagDto[] = dtos const addDtos: ProcessTagDto[] = dtos
.filter((dto) => dto.action === ModifyAction.ADD) .filter((dto) => dto.action === ModifyAction.ADD)
.map((dto) => ({ .map((dto) => ({
@ -205,7 +224,7 @@ export class SpaceModelProductAllocationService {
})); }));
if (addDtos.length > 0) { if (addDtos.length > 0) {
await this.createProductAllocations( allocationUpdates = await this.createProductAllocations(
projectUuid, projectUuid,
spaceModel, spaceModel,
addDtos, addDtos,
@ -213,6 +232,7 @@ export class SpaceModelProductAllocationService {
modifySubspaceModels, modifySubspaceModels,
); );
} }
return allocationUpdates;
} }
private createNewAllocation( private createNewAllocation(
@ -292,11 +312,12 @@ export class SpaceModelProductAllocationService {
dtos: ModifyTagModelDto[], dtos: ModifyTagModelDto[],
queryRunner: QueryRunner, queryRunner: QueryRunner,
spaceModel: SpaceModelEntity, spaceModel: SpaceModelEntity,
): Promise<SpaceModelProductAllocationEntity[]> { ): Promise<IUpdatedSpaceAllocations[]> {
try { try {
if (!dtos || dtos.length === 0) { if (!dtos || dtos.length === 0) {
return; return;
} }
let allocationUpdateToPropagate: IUpdatedSpaceAllocations[] = [];
const tagUuidsToDelete = dtos const tagUuidsToDelete = dtos
.filter((dto) => dto.action === ModifyAction.DELETE && dto.tagUuid) .filter((dto) => dto.action === ModifyAction.DELETE && dto.tagUuid)
@ -331,15 +352,26 @@ export class SpaceModelProductAllocationService {
(tag) => !tagUuidsToDelete.includes(tag.uuid), (tag) => !tagUuidsToDelete.includes(tag.uuid),
); );
const deletedTags = allocation.tags.filter((tag) =>
tagUuidsToDelete.includes(tag.uuid),
);
if (updatedTags.length === allocation.tags.length) { if (updatedTags.length === allocation.tags.length) {
continue; continue;
} }
if (updatedTags.length === 0) { if (updatedTags.length === 0) {
deletedAllocations.push(allocation); deletedAllocations.push(allocation);
allocationUpdateToPropagate.push({
deletedAllocation: allocation,
});
} else { } else {
allocation.tags = updatedTags; allocation.tags = updatedTags;
allocationUpdates.push(allocation); allocationUpdates.push(allocation);
allocationUpdateToPropagate.push({
allocation: allocation,
tagsRemoved: deletedTags,
});
} }
} }
@ -372,7 +404,7 @@ export class SpaceModelProductAllocationService {
) )
.execute(); .execute();
return deletedAllocations; return allocationUpdateToPropagate;
} catch (error) { } catch (error) {
throw this.handleError(error, `Failed to delete tags in space model`); throw this.handleError(error, `Failed to delete tags in space model`);
} }

View File

@ -28,6 +28,7 @@ import { SpaceModelProductAllocationService } from './space-model-product-alloca
import { import {
PropogateDeleteSpaceModelCommand, PropogateDeleteSpaceModelCommand,
PropogateUpdateSpaceModelCommand, PropogateUpdateSpaceModelCommand,
PropogateUpdateSpaceModelProductAllocationCommand,
} from '../commands'; } from '../commands';
import { import {
SpaceProductAllocationRepository, 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 { SubspaceEntity } from '@app/common/modules/space/entities/subspace/subspace.entity';
import { SubspaceProductAllocationEntity } from '@app/common/modules/space/entities/subspace/subspace-product-allocation.entity'; import { SubspaceProductAllocationEntity } from '@app/common/modules/space/entities/subspace/subspace-product-allocation.entity';
import { DeviceEntity } from '@app/common/modules/device/entities'; import { DeviceEntity } from '@app/common/modules/device/entities';
import { ISingleSubspaceModel, ISubspaceModelUpdates } from '../interfaces'; import {
ISingleSubspaceModel,
ISubspaceModelUpdates,
IUpdatedSpaceAllocations,
} from '../interfaces';
@Injectable() @Injectable()
export class SpaceModelService { export class SpaceModelService {
@ -200,6 +205,7 @@ export class SpaceModelService {
); );
await queryRunner.connect(); await queryRunner.connect();
let modifiedSubspaces: ISubspaceModelUpdates; let modifiedSubspaces: ISubspaceModelUpdates;
let modifiedProductAllocations: IUpdatedSpaceAllocations[];
try { try {
await queryRunner.startTransaction(); await queryRunner.startTransaction();
const spaces = await this.fetchModelSpaces(spaceModel); const spaces = await this.fetchModelSpaces(spaceModel);
@ -230,24 +236,35 @@ export class SpaceModelService {
} }
if (dto.tags) { if (dto.tags) {
await this.spaceModelProductAllocationService.updateProductAllocations( modifiedProductAllocations =
dto.tags, await this.spaceModelProductAllocationService.updateProductAllocations(
project, dto.tags,
spaceModel, project,
queryRunner, spaceModel,
dto.subspaceModels, queryRunner,
); dto.subspaceModels,
);
} }
await queryRunner.commitTransaction(); await queryRunner.commitTransaction();
await this.commandBus.execute( await this.commandBus.execute(
new PropogateUpdateSpaceModelCommand({ new PropogateUpdateSpaceModelCommand({
spaceModel: spaceModel, spaceModel: spaceModel,
subspaceModels: modifiedSubspaces, subspaceUpdates: modifiedSubspaces,
spaces: spaces, spaces: spaces,
}), }),
); );
if (modifiedProductAllocations?.length > 0) {
await this.commandBus.execute(
new PropogateUpdateSpaceModelProductAllocationCommand({
spaceModel: spaceModel,
updatedAllocations: modifiedProductAllocations,
spaces: spaces,
}),
);
}
return new SuccessResponseDto({ return new SuccessResponseDto({
message: 'SpaceModel updated successfully', message: 'SpaceModel updated successfully',
}); });

View File

@ -15,6 +15,7 @@ import { ProductRepository } from '@app/common/modules/product/repositories';
import { import {
PropogateDeleteSpaceModelHandler, PropogateDeleteSpaceModelHandler,
PropogateUpdateSpaceModelHandler, PropogateUpdateSpaceModelHandler,
PropogateUpdateSpaceModelProductAllocationHandler,
} from './handlers'; } from './handlers';
import { CqrsModule } from '@nestjs/cqrs'; import { CqrsModule } from '@nestjs/cqrs';
import { import {
@ -60,6 +61,7 @@ import { AutomationRepository } from '@app/common/modules/automation/repositorie
const CommandHandlers = [ const CommandHandlers = [
PropogateUpdateSpaceModelHandler, PropogateUpdateSpaceModelHandler,
PropogateDeleteSpaceModelHandler, PropogateDeleteSpaceModelHandler,
PropogateUpdateSpaceModelProductAllocationHandler,
]; ];
@Module({ @Module({