space model update

This commit is contained in:
hannathkadher
2024-12-23 20:49:48 +04:00
parent b0eec7c38e
commit 41c86b47eb
4 changed files with 217 additions and 219 deletions

View File

@ -1,15 +1,22 @@
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
import { Type } from 'class-transformer'; import { Type } from 'class-transformer';
import { IsString, IsOptional, IsArray, ValidateNested } from 'class-validator'; import {
IsString,
IsOptional,
IsArray,
ValidateNested,
IsEnum,
} from 'class-validator';
import { ModifyTagModelDto } from '../tag-model-dtos'; import { ModifyTagModelDto } from '../tag-model-dtos';
import { ModifyAction } from '@app/common/constants/modify-action.enum';
export class ModifySubspaceModelDto { export class ModifySubspaceModelDto {
@ApiProperty({ @ApiProperty({
description: 'Action to perform: add, update, or delete', description: 'Action to perform: add, update, or delete',
example: 'add', example: ModifyAction.ADD,
}) })
@IsString() @IsEnum(ModifyAction)
action: 'add' | 'update' | 'delete'; action: ModifyAction;
@ApiPropertyOptional({ @ApiPropertyOptional({
description: 'UUID of the subspace (required for update/delete)', description: 'UUID of the subspace (required for update/delete)',

View File

@ -171,7 +171,7 @@ export class SpaceModelService {
async validateName(modelName: string, projectUuid: string): Promise<void> { async validateName(modelName: string, projectUuid: string): Promise<void> {
const isModelExist = await this.spaceModelRepository.findOne({ const isModelExist = await this.spaceModelRepository.findOne({
where: { modelName, project: { uuid: projectUuid } }, where: { modelName, project: { uuid: projectUuid }, disabled: false },
}); });
if (isModelExist) { if (isModelExist) {
@ -186,6 +186,7 @@ export class SpaceModelService {
const spaceModel = await this.spaceModelRepository.findOne({ const spaceModel = await this.spaceModelRepository.findOne({
where: { where: {
uuid, uuid,
disabled: true,
}, },
}); });
if (!spaceModel) { if (!spaceModel) {

View File

@ -19,6 +19,7 @@ import {
ModifySubspaceModelDto, ModifySubspaceModelDto,
} from 'src/space-model/dtos/subspaces-model-dtos'; } from 'src/space-model/dtos/subspaces-model-dtos';
import { TagModelService } from '../tag-model.service'; import { TagModelService } from '../tag-model.service';
import { ModifyAction } from '@app/common/constants/modify-action.enum';
@Injectable() @Injectable()
export class SubSpaceModelService { export class SubSpaceModelService {
@ -35,94 +36,223 @@ export class SubSpaceModelService {
): Promise<SubspaceModelEntity[]> { ): Promise<SubspaceModelEntity[]> {
this.validateInputDtos(subSpaceModelDtos); this.validateInputDtos(subSpaceModelDtos);
try { const subspaces = subSpaceModelDtos.map((subspaceDto) =>
const subspaces = subSpaceModelDtos.map((subspaceDto) => queryRunner.manager.create(this.subspaceModelRepository.target, {
queryRunner.manager.create(this.subspaceModelRepository.target, { subspaceName: subspaceDto.subspaceName,
subspaceName: subspaceDto.subspaceName, spaceModel,
spaceModel, }),
}), );
);
const savedSubspaces = await queryRunner.manager.save(subspaces); const savedSubspaces = await queryRunner.manager.save(subspaces);
await Promise.all( await Promise.all(
subSpaceModelDtos.map(async (dto, index) => { subSpaceModelDtos.map(async (dto, index) => {
const subspace = savedSubspaces[index]; const subspace = savedSubspaces[index];
if (dto.tags?.length) {
subspace.tags = await this.tagModelService.createTags(
dto.tags,
queryRunner,
null,
subspace,
otherTags,
);
}
}),
);
if (dto.tags && dto.tags.length > 0) { return savedSubspaces;
const tagModels = await this.tagModelService.createTags(
dto.tags,
queryRunner,
null,
subspace,
otherTags,
);
subspace.tags = tagModels;
}
}),
);
return savedSubspaces;
} catch (error) {
if (error instanceof HttpException) {
throw error;
}
throw new HttpException(
error.message || 'Failed to create subspaces.',
HttpStatus.INTERNAL_SERVER_ERROR,
);
}
} }
async updateSubspaceModels( async updateSubspaceModels(
updateDtos: UpdateSubspaceModelDto[], updateDtos: UpdateSubspaceModelDto[],
spaceModel: SpaceModelEntity,
queryRunner: QueryRunner, queryRunner: QueryRunner,
): Promise<IUpdateSubspaceModelInterface[]> { ): Promise<IUpdateSubspaceModelInterface[]> {
try { const updateResults: IUpdateSubspaceModelInterface[] = [];
const updateResults: IUpdateSubspaceModelInterface[] = [];
const updatePromises = updateDtos.map(async (dto) => { for (const dto of updateDtos) {
try { await this.findOne(dto.subspaceUuid);
const subspaceModel = await this.findOne(dto.subspaceUuid); const updateResult: IUpdateSubspaceModelInterface = {
uuid: dto.subspaceUuid,
};
if (!subspaceModel) { if (dto.subspaceName) {
throw new HttpException( await this.updateSubspaceName(
`Subspace model with UUID ${dto.subspaceUuid} not found.`, dto.subspaceUuid,
HttpStatus.NOT_FOUND, dto.subspaceName,
); queryRunner,
} );
updateResult.subspaceName = dto.subspaceName;
}
const updateResult: IUpdateSubspaceModelInterface = { updateResults.push(updateResult);
uuid: dto.subspaceUuid, }
};
if (dto.subspaceName) { return updateResults;
await this.updateSubspaceName( }
dto.subspaceUuid,
dto.subspaceName,
queryRunner,
);
subspaceModel.subspaceName = dto.subspaceName;
updateResult.subspaceName = dto.subspaceName;
}
updateResults.push(updateResult); async deleteSubspaceModels(
} catch (error) { deleteDtos: DeleteSubspaceModelDto[],
queryRunner: QueryRunner,
): Promise<IDeletedSubsaceModelInterface[]> {
const deleteResults: IDeletedSubsaceModelInterface[] = [];
for (const dto of deleteDtos) {
const subspaceModel = await this.findOne(dto.subspaceUuid);
await queryRunner.manager.update(
this.subspaceModelRepository.target,
{ uuid: dto.subspaceUuid },
{ disabled: true },
);
if (subspaceModel.tags?.length) {
const modifyTagDtos = subspaceModel.tags.map((tag) => ({
uuid: tag.uuid,
action: ModifyAction.DELETE,
}));
await this.tagModelService.modifyTags(
modifyTagDtos,
queryRunner,
null,
subspaceModel,
);
}
deleteResults.push({ uuid: dto.subspaceUuid });
}
return deleteResults;
}
async modifySubSpaceModels(
subspaceDtos: ModifySubspaceModelDto[],
spaceModel: SpaceModelEntity,
queryRunner: QueryRunner,
): Promise<void> {
for (const subspace of subspaceDtos) {
switch (subspace.action) {
case ModifyAction.ADD:
await this.handleAddAction(subspace, spaceModel, queryRunner);
break;
case ModifyAction.UPDATE:
await this.handleUpdateAction(subspace, queryRunner);
break;
case ModifyAction.DELETE:
await this.handleDeleteAction(subspace, queryRunner);
break;
default:
throw new HttpException( throw new HttpException(
`Failed to update subspace model with UUID ${dto.subspaceUuid}: ${error.message}`, `Invalid action "${subspace.action}".`,
HttpStatus.INTERNAL_SERVER_ERROR, HttpStatus.BAD_REQUEST,
); );
} }
}); }
}
await Promise.all(updatePromises); private async handleAddAction(
subspace: ModifySubspaceModelDto,
spaceModel: SpaceModelEntity,
queryRunner: QueryRunner,
): Promise<void> {
const createTagDtos: CreateTagModelDto[] =
subspace.tags?.map((tag) => ({
tag: tag.tag as string,
productUuid: tag.productUuid as string,
})) || [];
await this.createSubSpaceModels(
[{ subspaceName: subspace.subspaceName, tags: createTagDtos }],
spaceModel,
queryRunner,
);
}
return updateResults; private async handleUpdateAction(
} catch (error) { subspace: ModifySubspaceModelDto,
queryRunner: QueryRunner,
): Promise<void> {
const existingSubspace = await this.findOne(subspace.uuid);
if (subspace.subspaceName) {
existingSubspace.subspaceName = subspace.subspaceName;
await queryRunner.manager.save(existingSubspace);
}
if (subspace.tags?.length) {
await this.tagModelService.modifyTags(
subspace.tags,
queryRunner,
null,
existingSubspace,
);
}
}
private async handleDeleteAction(
subspace: ModifySubspaceModelDto,
queryRunner: QueryRunner,
): Promise<void> {
const subspaceModel = await this.findOne(subspace.uuid);
await queryRunner.manager.update(
this.subspaceModelRepository.target,
{ uuid: subspace.uuid },
{ disabled: true },
);
if (subspaceModel.tags?.length) {
const modifyTagDtos = subspaceModel.tags.map((tag) => ({
uuid: tag.uuid,
action: ModifyAction.DELETE,
}));
await this.tagModelService.modifyTags(
modifyTagDtos,
queryRunner,
null,
subspaceModel,
);
}
}
private async findOne(subspaceUuid: string): Promise<SubspaceModelEntity> {
const subspace = await this.subspaceModelRepository.findOne({
where: { uuid: subspaceUuid },
relations: ['tags'],
});
if (!subspace) {
throw new HttpException( throw new HttpException(
error.message || 'Failed to update subspace models.', `SubspaceModel with UUID ${subspaceUuid} not found.`,
HttpStatus.INTERNAL_SERVER_ERROR, HttpStatus.NOT_FOUND,
);
}
return subspace;
}
private validateInputDtos(subSpaceModelDtos: CreateSubspaceModelDto[]): void {
if (subSpaceModelDtos.length === 0) {
throw new HttpException(
'Subspace models cannot be empty.',
HttpStatus.BAD_REQUEST,
);
}
this.validateName(subSpaceModelDtos.map((dto) => dto.subspaceName));
}
private validateName(names: string[]): void {
const seenNames = new Set<string>();
const duplicateNames = new Set<string>();
for (const name of names) {
if (seenNames.has(name)) {
duplicateNames.add(name);
} else {
seenNames.add(name);
}
}
if (duplicateNames.size > 0) {
throw new HttpException(
`Duplicate subspace names found: ${[...duplicateNames].join(', ')}`,
HttpStatus.CONFLICT,
); );
} }
} }
@ -138,146 +268,4 @@ export class SubSpaceModelService {
{ subspaceName }, { subspaceName },
); );
} }
async deleteSubspaceModels(
deleteDtos: DeleteSubspaceModelDto[],
queryRunner: QueryRunner,
): Promise<IDeletedSubsaceModelInterface[]> {
try {
const deleteResults: IDeletedSubsaceModelInterface[] = [];
const deletePromises = deleteDtos.map(async (dto) => {
try {
const subspaceModel = await this.findOne(dto.subspaceUuid);
if (!subspaceModel) {
throw new HttpException(
`Subspace model with UUID ${dto.subspaceUuid} not found.`,
HttpStatus.NOT_FOUND,
);
}
await queryRunner.manager.update(
this.subspaceModelRepository.target,
{ uuid: dto.subspaceUuid },
{ disabled: true },
);
deleteResults.push({ uuid: dto.subspaceUuid });
} catch (error) {
throw new HttpException(
`Failed to delete subspace model with UUID ${dto.subspaceUuid}: ${error.message}`,
HttpStatus.INTERNAL_SERVER_ERROR,
);
}
});
await Promise.all(deletePromises);
return deleteResults;
} catch (error) {
throw new HttpException(
'Bulk delete operation failed. Please try again.',
HttpStatus.INTERNAL_SERVER_ERROR,
);
}
}
async findOne(subspaceUuid: string) {
const subspace = await this.subspaceModelRepository.findOne({
where: {
uuid: subspaceUuid,
},
});
if (!subspace) {
throw new HttpException(
`SubspaceModel with ${subspaceUuid} not found`,
HttpStatus.NOT_FOUND,
);
}
return subspace;
}
private validateInputDtos(subSpaceModelDtos: CreateSubspaceModelDto[]) {
if (subSpaceModelDtos.length === 0) {
throw new HttpException(
'Subspace models cannot be empty.',
HttpStatus.BAD_REQUEST,
);
}
const incomingNames = subSpaceModelDtos.map((dto) => dto.subspaceName);
this.validateName(incomingNames);
}
private validateName(names: string[]) {
const seenNames = new Set<string>();
const duplicateNames = new Set<string>();
for (const name of names) {
if (seenNames.has(name)) {
duplicateNames.add(name);
} else {
seenNames.add(name);
}
}
if (duplicateNames.size > 0) {
throw new HttpException(
`Duplicate subspace names found in request: ${[...duplicateNames].join(', ')}`,
HttpStatus.CONFLICT,
);
}
}
async modifySubSpaceModels(
subspaceDtos: ModifySubspaceModelDto[],
spaceModel: SpaceModelEntity,
queryRunner: QueryRunner,
) {
try {
for (const subspace of subspaceDtos) {
if (subspace.action === 'add') {
const createTagDtos: CreateTagModelDto[] =
subspace.tags?.map((tag) => ({
tag: tag.tag as string,
productUuid: tag.productUuid as string,
})) || [];
await this.createSubSpaceModels(
[{ subspaceName: subspace.subspaceName, tags: createTagDtos }],
spaceModel,
queryRunner,
);
} else if (subspace.action === 'update') {
const existingSubspace = await this.findOne(subspace.uuid);
if (!existingSubspace) {
throw new HttpException(
`Subspace with ID ${subspace.uuid} not found.`,
HttpStatus.NOT_FOUND,
);
}
existingSubspace.subspaceName =
subspace.subspaceName || existingSubspace.subspaceName;
const updatedSubspace =
await queryRunner.manager.save(existingSubspace);
if (subspace.tags) {
await this.tagModelService.modifyTags(
subspace.tags,
queryRunner,
null,
updatedSubspace,
);
}
}
}
} catch (error) {
throw new HttpException(
error.message || 'Failed to modify SpaceModels',
HttpStatus.INTERNAL_SERVER_ERROR,
);
}
}
} }

View File

@ -8,6 +8,7 @@ import { SubspaceModelEntity } from '@app/common/modules/space-model/entities';
import { TagModelRepository } from '@app/common/modules/space-model'; import { TagModelRepository } from '@app/common/modules/space-model';
import { CreateTagModelDto, ModifyTagModelDto } from '../dtos'; import { CreateTagModelDto, ModifyTagModelDto } from '../dtos';
import { ProductService } from 'src/product/services'; import { ProductService } from 'src/product/services';
import { ModifyAction } from '@app/common/constants/modify-action.enum';
@Injectable() @Injectable()
export class TagModelService { export class TagModelService {
@ -133,8 +134,9 @@ export class TagModelService {
subspaceModel?: SubspaceModelEntity, subspaceModel?: SubspaceModelEntity,
): Promise<void> { ): Promise<void> {
try { try {
console.log(tags);
for (const tag of tags) { for (const tag of tags) {
if (tag.action === 'add') { if (tag.action === ModifyAction.ADD) {
const createTagDto: CreateTagModelDto = { const createTagDto: CreateTagModelDto = {
tag: tag.tag as string, tag: tag.tag as string,
productUuid: tag.productUuid as string, productUuid: tag.productUuid as string,
@ -146,9 +148,9 @@ export class TagModelService {
spaceModel, spaceModel,
subspaceModel, subspaceModel,
); );
} else if (tag.action === 'update') { } else if (tag.action === ModifyAction.UPDATE) {
await this.updateTag(tag, queryRunner, spaceModel, subspaceModel); await this.updateTag(tag, queryRunner, spaceModel, subspaceModel);
} else if (tag.action === 'delete') { } else if (tag.action === ModifyAction.DELETE) {
await queryRunner.manager.update( await queryRunner.manager.update(
this.tagModelRepository.target, this.tagModelRepository.target,
{ uuid: tag.uuid }, { uuid: tag.uuid },