Merge pull request #200 from SyncrowIOT/bugfix/update-space-model

fixed bugs
This commit is contained in:
hannathkadher
2025-01-06 13:21:46 +04:00
committed by GitHub
6 changed files with 308 additions and 188 deletions

View File

@ -1,11 +1,4 @@
import {
Entity,
Column,
OneToMany,
ManyToOne,
JoinColumn,
Unique,
} from 'typeorm';
import { Entity, Column, OneToMany, ManyToOne, JoinColumn } from 'typeorm';
import { AbstractEntity } from '../../abstract/entities/abstract.entity';
import { SpaceModelDto } from '../dtos';
import { SubspaceModelEntity } from './subspace-model';
@ -14,7 +7,6 @@ import { SpaceEntity } from '../../space/entities';
import { TagModel } from './tag-model.entity';
@Entity({ name: 'space-model' })
@Unique(['modelName', 'project'])
export class SpaceModelEntity extends AbstractEntity<SpaceModelDto> {
@Column({
type: 'uuid',

View File

@ -1,12 +1,14 @@
import { ICommand } from '@nestjs/cqrs';
import { SpaceModelEntity } from '@app/common/modules/space-model';
import { ModifyspaceModelPayload } from '../interfaces';
import { QueryRunner } from 'typeorm';
export class PropogateUpdateSpaceModelCommand implements ICommand {
constructor(
public readonly param: {
spaceModel: SpaceModelEntity;
modifiedSpaceModels: ModifyspaceModelPayload;
queryRunner: QueryRunner;
},
) {}
}

View File

@ -29,36 +29,37 @@ export class PropogateUpdateSpaceModelHandler
) {}
async execute(command: PropogateUpdateSpaceModelCommand): Promise<void> {
const { spaceModel, modifiedSpaceModels } = command.param;
const queryRunner = this.dataSource.createQueryRunner();
const { spaceModel, modifiedSpaceModels, queryRunner } = command.param;
try {
await queryRunner.connect();
await queryRunner.startTransaction();
const spaces = await this.spaceRepository.find({
const spaces = await queryRunner.manager.find(SpaceEntity, {
where: { spaceModel },
});
if (
modifiedSpaceModels.modifiedSubspaceModels.addedSubspaceModels.length >
0
) {
const { modifiedSubspaceModels = {}, modifiedTags = {} } =
modifiedSpaceModels;
const {
addedSubspaceModels = [],
updatedSubspaceModels = [],
deletedSubspaceModels = [],
} = modifiedSubspaceModels;
const { added = [], updated = [], deleted = [] } = modifiedTags;
if (addedSubspaceModels.length > 0) {
await this.addSubspaceModels(
modifiedSpaceModels.modifiedSubspaceModels.addedSubspaceModels,
spaces,
queryRunner,
);
} else if (
modifiedSpaceModels.modifiedSubspaceModels.updatedSubspaceModels
.length > 0
) {
} else if (updatedSubspaceModels.length > 0) {
await this.updateSubspaceModels(
modifiedSpaceModels.modifiedSubspaceModels.updatedSubspaceModels,
queryRunner,
);
}
if (
modifiedSpaceModels.modifiedSubspaceModels.deletedSubspaceModels?.length
) {
if (deletedSubspaceModels.length > 0) {
const dtos: ModifySubspaceDto[] =
modifiedSpaceModels.modifiedSubspaceModels.deletedSubspaceModels.map(
(model) => ({
@ -69,7 +70,7 @@ export class PropogateUpdateSpaceModelHandler
await this.subSpaceService.modifySubSpace(dtos, queryRunner);
}
if (modifiedSpaceModels.modifiedTags.added.length > 0) {
if (added.length > 0) {
await this.createTags(
modifiedSpaceModels.modifiedTags.added,
queryRunner,
@ -78,26 +79,21 @@ export class PropogateUpdateSpaceModelHandler
);
}
if (modifiedSpaceModels.modifiedTags.updated.length > 0) {
if (updated.length > 0) {
await this.updateTags(
modifiedSpaceModels.modifiedTags.updated,
queryRunner,
);
}
if (modifiedSpaceModels.modifiedTags.deleted.length > 0) {
if (deleted.length > 0) {
await this.deleteTags(
modifiedSpaceModels.modifiedTags.deleted,
queryRunner,
);
}
await queryRunner.commitTransaction();
} catch (error) {
await queryRunner.rollbackTransaction();
throw error;
} finally {
await queryRunner.release();
console.error(error);
}
}

View File

@ -7,7 +7,7 @@ import { CreateSpaceModelDto, UpdateSpaceModelDto } from '../dtos';
import { ProjectParam } from 'src/community/dtos';
import { SuccessResponseDto } from '@app/common/dto/success.response.dto';
import { SubSpaceModelService } from './subspace/subspace-model.service';
import { DataSource } from 'typeorm';
import { DataSource, QueryRunner } from 'typeorm';
import {
TypeORMCustomModel,
TypeORMCustomModelFindAllQuery,
@ -49,7 +49,11 @@ export class SpaceModelService {
try {
const project = await this.validateProject(params.projectUuid);
await this.validateName(modelName, params.projectUuid);
await this.validateNameUsingQueryRunner(
modelName,
params.projectUuid,
queryRunner,
);
const spaceModel = this.spaceModelRepository.create({
modelName,
@ -140,20 +144,29 @@ export class SpaceModelService {
}
async update(dto: UpdateSpaceModelDto, param: SpaceModelParam) {
const queryRunner = this.dataSource.createQueryRunner();
await this.validateProject(param.projectUuid);
const spaceModel = await this.validateSpaceModel(param.spaceModelUuid);
const queryRunner = this.dataSource.createQueryRunner();
await queryRunner.connect();
await queryRunner.startTransaction();
let modifiedSubspaceModels: ModifySubspaceModelPayload = {};
let modifiedTagsModelPayload: ModifiedTagsModelPayload = {};
try {
await queryRunner.startTransaction();
const { modelName } = dto;
if (modelName) {
await this.validateName(modelName, param.projectUuid);
await this.validateNameUsingQueryRunner(
modelName,
param.projectUuid,
queryRunner,
);
spaceModel.modelName = modelName;
await queryRunner.manager.save(spaceModel);
await queryRunner.manager.save(
this.spaceModelRepository.target,
spaceModel,
);
}
if (dto.subspaceModels) {
@ -182,6 +195,7 @@ export class SpaceModelService {
modifiedSubspaceModels,
modifiedTags: modifiedTagsModelPayload,
},
queryRunner,
}),
);
@ -191,6 +205,11 @@ export class SpaceModelService {
});
} catch (error) {
await queryRunner.rollbackTransaction();
if (error instanceof HttpException) {
throw error;
}
throw new HttpException(
error.message || 'Failed to update SpaceModel',
HttpStatus.INTERNAL_SERVER_ERROR,
@ -271,6 +290,23 @@ export class SpaceModelService {
}
}
async validateNameUsingQueryRunner(
modelName: string,
projectUuid: string,
queryRunner: QueryRunner,
): Promise<void> {
const isModelExist = await queryRunner.manager.findOne(SpaceModelEntity, {
where: { modelName, project: { uuid: projectUuid }, disabled: false },
});
if (isModelExist) {
throw new HttpException(
`Model name ${modelName} already exists in the project with UUID ${projectUuid}.`,
HttpStatus.CONFLICT,
);
}
}
async validateSpaceModel(uuid: string): Promise<SpaceModelEntity> {
const spaceModel = await this.spaceModelRepository.findOne({
where: {

View File

@ -31,7 +31,8 @@ export class SubSpaceModelService {
queryRunner: QueryRunner,
otherTags?: CreateTagModelDto[],
): Promise<SubspaceModelEntity[]> {
this.validateInputDtos(subSpaceModelDtos, spaceModel);
try {
await this.validateInputDtos(subSpaceModelDtos, spaceModel);
const subspaces = subSpaceModelDtos.map((subspaceDto) =>
queryRunner.manager.create(this.subspaceModelRepository.target, {
@ -63,6 +64,17 @@ export class SubSpaceModelService {
);
return savedSubspaces;
} catch (error) {
if (error instanceof HttpException) {
throw error; // Rethrow known HttpExceptions
}
// Handle unexpected errors
throw new HttpException(
`An error occurred while creating subspace models: ${error.message}`,
HttpStatus.INTERNAL_SERVER_ERROR,
);
}
}
async deleteSubspaceModels(
@ -104,7 +116,12 @@ export class SubSpaceModelService {
spaceModel: SpaceModelEntity,
queryRunner: QueryRunner,
): Promise<ModifySubspaceModelPayload> {
const modifiedSubspaceModels: ModifySubspaceModelPayload = {};
const modifiedSubspaceModels: ModifySubspaceModelPayload = {
addedSubspaceModels: [],
updatedSubspaceModels: [],
deletedSubspaceModels: [],
};
try {
for (const subspace of subspaceDtos) {
switch (subspace.action) {
case ModifyAction.ADD:
@ -136,6 +153,16 @@ export class SubSpaceModelService {
}
}
return modifiedSubspaceModels;
} catch (error) {
if (error instanceof HttpException) {
throw error;
}
throw new HttpException(
`An error occurred while modifying subspace models: ${error.message}`,
HttpStatus.INTERNAL_SERVER_ERROR,
);
}
}
private async handleAddAction(
@ -143,6 +170,7 @@ export class SubSpaceModelService {
spaceModel: SpaceModelEntity,
queryRunner: QueryRunner,
): Promise<SubspaceModelEntity> {
try {
const createTagDtos: CreateTagModelDto[] =
subspace.tags?.map((tag) => ({
tag: tag.tag,
@ -161,6 +189,16 @@ export class SubSpaceModelService {
);
return createdSubspaceModel;
} catch (error) {
if (error instanceof HttpException) {
throw error; // Rethrow known HttpExceptions
}
throw new HttpException(
`An error occurred while adding subspace: ${error.message}`,
HttpStatus.INTERNAL_SERVER_ERROR,
);
}
}
private async handleUpdateAction(
@ -221,7 +259,7 @@ export class SubSpaceModelService {
private async findOne(subspaceUuid: string): Promise<SubspaceModelEntity> {
const subspace = await this.subspaceModelRepository.findOne({
where: { uuid: subspaceUuid },
relations: ['tags'],
relations: ['tags', 'spaceModel'],
});
if (!subspace) {
throw new HttpException(
@ -232,29 +270,44 @@ export class SubSpaceModelService {
return subspace;
}
private validateInputDtos(
private async validateInputDtos(
subSpaceModelDtos: CreateSubspaceModelDto[],
spaceModel: SpaceModelEntity,
): void {
): Promise<void> {
try {
if (subSpaceModelDtos.length === 0) {
throw new HttpException(
'Subspace models cannot be empty.',
HttpStatus.BAD_REQUEST,
);
}
this.validateName(
await this.validateName(
subSpaceModelDtos.map((dto) => dto.subspaceName),
spaceModel,
);
} catch (error) {
if (error instanceof HttpException) {
throw error; // Rethrow known HttpExceptions to preserve their message and status
}
// Wrap unexpected errors
throw new HttpException(
`An error occurred while validating subspace models: ${error.message}`,
HttpStatus.INTERNAL_SERVER_ERROR,
);
}
}
private async validateName(
names: string[],
spaceModel: SpaceModelEntity,
): Promise<void> {
try {
const seenNames = new Set<string>();
const duplicateNames = new Set<string>();
// Check for duplicate names within the input array
for (const name of names) {
if (!seenNames.add(name)) {
duplicateNames.add(name);
@ -268,6 +321,7 @@ export class SubSpaceModelService {
);
}
// Check for existing names in the database
const existingNames = await this.subspaceModelRepository.find({
select: ['subspaceName'],
where: {
@ -287,6 +341,17 @@ export class SubSpaceModelService {
HttpStatus.BAD_REQUEST,
);
}
} catch (error) {
if (error instanceof HttpException) {
throw error; // Rethrow known HttpExceptions
}
// Handle unexpected errors
throw new HttpException(
`An error occurred while validating subspace model names: ${error.message}`,
HttpStatus.INTERNAL_SERVER_ERROR,
);
}
}
private async updateSubspaceName(

View File

@ -134,7 +134,11 @@ export class TagModelService {
spaceModel?: SpaceModelEntity,
subspaceModel?: SubspaceModelEntity,
): Promise<ModifiedTagsModelPayload> {
const modifiedTagModels: ModifiedTagsModelPayload = {};
const modifiedTagModels: ModifiedTagsModelPayload = {
added: [],
updated: [],
deleted: [],
};
try {
for (const tag of tags) {
if (tag.action === ModifyAction.ADD) {
@ -190,6 +194,7 @@ export class TagModelService {
productUuid: string,
spaceModel: SpaceModelEntity,
): Promise<void> {
try {
const tagExists = await this.tagModelRepository.exists({
where: [
{
@ -210,6 +215,15 @@ export class TagModelService {
if (tagExists) {
throw new HttpException(`Tag can't be reused`, HttpStatus.CONFLICT);
}
} catch (error) {
if (error instanceof HttpException) {
throw error;
}
throw new HttpException(
`An error occurred while checking tag reuse: ${error.message}`,
HttpStatus.INTERNAL_SERVER_ERROR,
);
}
}
private async prepareTagEntity(
@ -218,6 +232,7 @@ export class TagModelService {
spaceModel?: SpaceModelEntity,
subspaceModel?: SubspaceModelEntity,
): Promise<TagModel> {
try {
const product = await this.productService.findOne(tagDto.productUuid);
if (!product) {
@ -229,12 +244,17 @@ export class TagModelService {
if (spaceModel) {
await this.checkTagReuse(tagDto.tag, tagDto.productUuid, spaceModel);
} else {
} else if (subspaceModel && subspaceModel.spaceModel) {
await this.checkTagReuse(
tagDto.tag,
tagDto.productUuid,
subspaceModel.spaceModel,
);
} else {
throw new HttpException(
`Invalid subspaceModel or spaceModel provided.`,
HttpStatus.BAD_REQUEST,
);
}
return queryRunner.manager.create(TagModel, {
@ -243,6 +263,15 @@ export class TagModelService {
spaceModel,
subspaceModel,
});
} catch (error) {
if (error instanceof HttpException) {
throw error;
}
throw new HttpException(
`An error occurred while preparing the tag entity: ${error.message}`,
HttpStatus.INTERNAL_SERVER_ERROR,
);
}
}
async getTagByUuid(uuid: string): Promise<TagModel> {