From 783b37cd1f9dd1747d964141324a58b42825349d Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Tue, 10 Dec 2024 15:12:51 +0400 Subject: [PATCH 01/11] Added space model, subspace model, space product model entities --- libs/common/src/database/database.module.ts | 10 +++++ .../product/entities/product.entity.ts | 7 ++++ .../src/modules/space-model/dtos/index.ts | 4 ++ .../space-model/dtos/space-model.dto.ts | 15 +++++++ .../dtos/space-product-item.dto.ts | 15 +++++++ .../dtos/space-product-model.dto.ts | 23 +++++++++++ .../space-model/dtos/subspace-model.dto.ts | 15 +++++++ .../src/modules/space-model/entities/index.ts | 4 ++ .../entities/space-model.entity.ts | 38 ++++++++++++++++++ .../entities/space-product-item.entity.ts | 29 ++++++++++++++ .../entities/space-product-model.entity.ts | 40 +++++++++++++++++++ .../entities/subspace-model.entity.ts | 29 ++++++++++++++ libs/common/src/modules/space-model/index.ts | 3 ++ .../modules/space-model/repositories/index.ts | 1 + .../repositories/space-model.repository.ts | 34 ++++++++++++++++ .../space-model.repository.module.ts | 23 +++++++++++ 16 files changed, 290 insertions(+) create mode 100644 libs/common/src/modules/space-model/dtos/index.ts create mode 100644 libs/common/src/modules/space-model/dtos/space-model.dto.ts create mode 100644 libs/common/src/modules/space-model/dtos/space-product-item.dto.ts create mode 100644 libs/common/src/modules/space-model/dtos/space-product-model.dto.ts create mode 100644 libs/common/src/modules/space-model/dtos/subspace-model.dto.ts create mode 100644 libs/common/src/modules/space-model/entities/index.ts create mode 100644 libs/common/src/modules/space-model/entities/space-model.entity.ts create mode 100644 libs/common/src/modules/space-model/entities/space-product-item.entity.ts create mode 100644 libs/common/src/modules/space-model/entities/space-product-model.entity.ts create mode 100644 libs/common/src/modules/space-model/entities/subspace-model.entity.ts create mode 100644 libs/common/src/modules/space-model/index.ts create mode 100644 libs/common/src/modules/space-model/repositories/index.ts create mode 100644 libs/common/src/modules/space-model/repositories/space-model.repository.ts create mode 100644 libs/common/src/modules/space-model/space-model.repository.module.ts diff --git a/libs/common/src/database/database.module.ts b/libs/common/src/database/database.module.ts index 34e4bb6..44fd854 100644 --- a/libs/common/src/database/database.module.ts +++ b/libs/common/src/database/database.module.ts @@ -28,6 +28,12 @@ import { SceneEntity, SceneIconEntity } from '../modules/scene/entities'; import { SceneDeviceEntity } from '../modules/scene-device/entities'; import { SpaceProductEntity } from '../modules/space/entities/space-product.entity'; import { ProjectEntity } from '../modules/project/entities'; +import { + SpaceModelEntity, + SpaceProductItemModelEntity, + SpaceProductModelEntity, + SubspaceModelEntity, +} from '../modules/space-model/entities'; @Module({ imports: [ TypeOrmModule.forRootAsync({ @@ -68,6 +74,10 @@ import { ProjectEntity } from '../modules/project/entities'; SceneEntity, SceneIconEntity, SceneDeviceEntity, + SpaceModelEntity, + SpaceProductModelEntity, + SpaceProductItemModelEntity, + SubspaceModelEntity, ], namingStrategy: new SnakeNamingStrategy(), synchronize: Boolean(JSON.parse(configService.get('DB_SYNC'))), diff --git a/libs/common/src/modules/product/entities/product.entity.ts b/libs/common/src/modules/product/entities/product.entity.ts index 2c731a0..7b25470 100644 --- a/libs/common/src/modules/product/entities/product.entity.ts +++ b/libs/common/src/modules/product/entities/product.entity.ts @@ -3,6 +3,7 @@ import { ProductDto } from '../dtos'; import { AbstractEntity } from '../../abstract/entities/abstract.entity'; import { DeviceEntity } from '../../device/entities'; import { SpaceProductEntity } from '../../space/entities/space-product.entity'; +import { SpaceProductModelEntity } from '../../space-model/entities'; @Entity({ name: 'product' }) export class ProductEntity extends AbstractEntity { @@ -30,6 +31,12 @@ export class ProductEntity extends AbstractEntity { @OneToMany(() => SpaceProductEntity, (spaceProduct) => spaceProduct.product) spaceProducts: SpaceProductEntity[]; + @OneToMany( + () => SpaceProductModelEntity, + (spaceProductModel) => spaceProductModel.product, + ) + spaceProductModels: SpaceProductModelEntity[]; + @OneToMany( () => DeviceEntity, (devicesProductEntity) => devicesProductEntity.productDevice, diff --git a/libs/common/src/modules/space-model/dtos/index.ts b/libs/common/src/modules/space-model/dtos/index.ts new file mode 100644 index 0000000..f775b4a --- /dev/null +++ b/libs/common/src/modules/space-model/dtos/index.ts @@ -0,0 +1,4 @@ +export * from './subspace-model.dto'; +export * from './space-model.dto'; +export * from './space-product-item.dto'; +export * from './space-product-model.dto'; diff --git a/libs/common/src/modules/space-model/dtos/space-model.dto.ts b/libs/common/src/modules/space-model/dtos/space-model.dto.ts new file mode 100644 index 0000000..f4d7cbf --- /dev/null +++ b/libs/common/src/modules/space-model/dtos/space-model.dto.ts @@ -0,0 +1,15 @@ +import { IsString, IsNotEmpty } from 'class-validator'; + +export class SpaceModelDto { + @IsString() + @IsNotEmpty() + public uuid: string; + + @IsString() + @IsNotEmpty() + public spaceModelName: string; + + @IsString() + @IsNotEmpty() + projectUuid: string; +} diff --git a/libs/common/src/modules/space-model/dtos/space-product-item.dto.ts b/libs/common/src/modules/space-model/dtos/space-product-item.dto.ts new file mode 100644 index 0000000..fa68825 --- /dev/null +++ b/libs/common/src/modules/space-model/dtos/space-product-item.dto.ts @@ -0,0 +1,15 @@ +import { IsString, IsNotEmpty } from 'class-validator'; + +export class SpaceProductItemDto { + @IsString() + @IsNotEmpty() + uuid: string; + + @IsString() + @IsNotEmpty() + tag: string; + + @IsString() + @IsNotEmpty() + spaceProductModelUuid: string; +} diff --git a/libs/common/src/modules/space-model/dtos/space-product-model.dto.ts b/libs/common/src/modules/space-model/dtos/space-product-model.dto.ts new file mode 100644 index 0000000..eae8088 --- /dev/null +++ b/libs/common/src/modules/space-model/dtos/space-product-model.dto.ts @@ -0,0 +1,23 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsNotEmpty, IsNumber, IsString } from 'class-validator'; +import { SpaceProductItemDto } from './space-product-item.dto'; + +export class SpaceProductModelDto { + @IsString() + @IsNotEmpty() + uuid: string; + + @IsNumber() + @IsNotEmpty() + productCount: number; + + @IsString() + @IsNotEmpty() + productUuid: string; + + @ApiProperty({ + description: 'List of individual items with specific names for the product', + type: [SpaceProductItemDto], + }) + items: SpaceProductItemDto[]; +} diff --git a/libs/common/src/modules/space-model/dtos/subspace-model.dto.ts b/libs/common/src/modules/space-model/dtos/subspace-model.dto.ts new file mode 100644 index 0000000..8c9a64c --- /dev/null +++ b/libs/common/src/modules/space-model/dtos/subspace-model.dto.ts @@ -0,0 +1,15 @@ +import { IsString, IsNotEmpty } from 'class-validator'; + +export class SubSpaceModelDto { + @IsString() + @IsNotEmpty() + public uuid: string; + + @IsString() + @IsNotEmpty() + public subSpaceModelName: string; + + @IsString() + @IsNotEmpty() + spaceModelUuid: string; +} diff --git a/libs/common/src/modules/space-model/entities/index.ts b/libs/common/src/modules/space-model/entities/index.ts new file mode 100644 index 0000000..3bf3f69 --- /dev/null +++ b/libs/common/src/modules/space-model/entities/index.ts @@ -0,0 +1,4 @@ +export * from './space-model.entity'; +export * from './space-product-item.entity'; +export * from './space-product-model.entity'; +export * from './subspace-model.entity'; \ No newline at end of file diff --git a/libs/common/src/modules/space-model/entities/space-model.entity.ts b/libs/common/src/modules/space-model/entities/space-model.entity.ts new file mode 100644 index 0000000..8df7e45 --- /dev/null +++ b/libs/common/src/modules/space-model/entities/space-model.entity.ts @@ -0,0 +1,38 @@ +import { Entity, Column, OneToMany } from 'typeorm'; +import { AbstractEntity } from '../../abstract/entities/abstract.entity'; +import { SpaceModelDto } from '../dtos'; +import { SubspaceModelEntity } from './subspace-model.entity'; +import { SpaceProductModelEntity } from './space-product-model.entity'; + +@Entity({ name: 'space-model' }) +export class SpaceModelEntity extends AbstractEntity { + @Column({ + type: 'uuid', + default: () => 'gen_random_uuid()', + nullable: false, + }) + public uuid: string; + + @Column({ + nullable: false, + }) + public modelName: string; + + @OneToMany( + () => SubspaceModelEntity, + (subspaceModel) => subspaceModel.spaceModel, + { + cascade: true, + }, + ) + public subspaceModels: SubspaceModelEntity[]; + + @OneToMany( + () => SpaceProductModelEntity, + (productModel) => productModel.spaceModel, + { + cascade: true, + }, + ) + public spaceProductModels: SpaceProductModelEntity[]; +} diff --git a/libs/common/src/modules/space-model/entities/space-product-item.entity.ts b/libs/common/src/modules/space-model/entities/space-product-item.entity.ts new file mode 100644 index 0000000..f754769 --- /dev/null +++ b/libs/common/src/modules/space-model/entities/space-product-item.entity.ts @@ -0,0 +1,29 @@ +import { Entity, Column, ManyToOne } from 'typeorm'; +import { AbstractEntity } from '../../abstract/entities/abstract.entity'; +import { SpaceEntity } from '../../space/entities'; +import { SpaceProductItemDto } from '../dtos'; +import { SpaceProductModelEntity } from './space-product-model.entity'; + +@Entity({ name: 'space-product-item' }) +export class SpaceProductItemModelEntity extends AbstractEntity { + @Column({ + nullable: false, + }) + public itemName: string; + + @ManyToOne( + () => SpaceProductModelEntity, + (spaceProductModel) => spaceProductModel.items, + { + nullable: false, + onDelete: 'CASCADE', + }, + ) + public spaceProductModel: SpaceProductModelEntity; + + @ManyToOne(() => SpaceEntity, (space) => space.spaceProducts, { + nullable: true, + onDelete: 'CASCADE', + }) + public space: SpaceEntity; // Optional for associating the item to a space directly +} diff --git a/libs/common/src/modules/space-model/entities/space-product-model.entity.ts b/libs/common/src/modules/space-model/entities/space-product-model.entity.ts new file mode 100644 index 0000000..d843d07 --- /dev/null +++ b/libs/common/src/modules/space-model/entities/space-product-model.entity.ts @@ -0,0 +1,40 @@ +import { Entity, Column, ManyToOne, OneToMany } from 'typeorm'; +import { AbstractEntity } from '../../abstract/entities/abstract.entity'; +import { ProductEntity } from '../../product/entities'; +import { SpaceModelEntity } from './space-model.entity'; +import { SpaceProductItemModelEntity } from './space-product-item.entity'; +import { SpaceProductModelDto } from '../dtos'; + +@Entity({ name: 'space-product-model' }) +export class SpaceProductModelEntity extends AbstractEntity { + @Column({ + nullable: false, + type: 'int', + }) + productCount: number; + + @ManyToOne( + () => SpaceModelEntity, + (spaceModel) => spaceModel.spaceProductModels, + { + nullable: false, + onDelete: 'CASCADE', + }, + ) + public spaceModel: SpaceModelEntity; + + @ManyToOne(() => ProductEntity, (product) => product.spaceProductModels, { + nullable: false, + onDelete: 'CASCADE', + }) + public product: ProductEntity; + + @OneToMany( + () => SpaceProductItemModelEntity, + (item) => item.spaceProductModel, + { + cascade: true, + }, + ) + public items: SpaceProductItemModelEntity[]; +} diff --git a/libs/common/src/modules/space-model/entities/subspace-model.entity.ts b/libs/common/src/modules/space-model/entities/subspace-model.entity.ts new file mode 100644 index 0000000..abb1c64 --- /dev/null +++ b/libs/common/src/modules/space-model/entities/subspace-model.entity.ts @@ -0,0 +1,29 @@ +import { Column, Entity, ManyToOne } from 'typeorm'; +import { AbstractEntity } from '../../abstract/entities/abstract.entity'; +import { SpaceModelEntity } from './space-model.entity'; +import { SubSpaceModelDto } from '../dtos'; + +@Entity({ name: 'subspace-model' }) +export class SubspaceModelEntity extends AbstractEntity { + @Column({ + type: 'uuid', + default: () => 'gen_random_uuid()', + nullable: false, + }) + public uuid: string; + + @Column({ + nullable: false, + }) + public subspaceName: string; + + @ManyToOne( + () => SpaceModelEntity, + (spaceModel) => spaceModel.subspaceModels, + { + nullable: false, + onDelete: 'CASCADE', + }, + ) + public spaceModel: SpaceModelEntity; +} diff --git a/libs/common/src/modules/space-model/index.ts b/libs/common/src/modules/space-model/index.ts new file mode 100644 index 0000000..9d32775 --- /dev/null +++ b/libs/common/src/modules/space-model/index.ts @@ -0,0 +1,3 @@ +export * from './space-model.repository.module'; +export * from './entities'; +export * from './repositories'; diff --git a/libs/common/src/modules/space-model/repositories/index.ts b/libs/common/src/modules/space-model/repositories/index.ts new file mode 100644 index 0000000..d8fcff4 --- /dev/null +++ b/libs/common/src/modules/space-model/repositories/index.ts @@ -0,0 +1 @@ +export * from './space-model.repository'; diff --git a/libs/common/src/modules/space-model/repositories/space-model.repository.ts b/libs/common/src/modules/space-model/repositories/space-model.repository.ts new file mode 100644 index 0000000..0e37479 --- /dev/null +++ b/libs/common/src/modules/space-model/repositories/space-model.repository.ts @@ -0,0 +1,34 @@ +import { DataSource, Repository } from 'typeorm'; +import { Injectable } from '@nestjs/common'; +import { + SpaceModelEntity, + SpaceProductItemModelEntity, + SpaceProductModelEntity, + SubspaceModelEntity, +} from '../entities'; + +@Injectable() +export class SpaceModelRepository extends Repository { + constructor(private dataSource: DataSource) { + super(SpaceModelEntity, dataSource.createEntityManager()); + } +} +@Injectable() +export class SubspaceModelRepository extends Repository { + constructor(private dataSource: DataSource) { + super(SubspaceModelEntity, dataSource.createEntityManager()); + } +} + +@Injectable() +export class SpaceProductModelRepository extends Repository { + constructor(private dataSource: DataSource) { + super(SpaceProductModelEntity, dataSource.createEntityManager()); + } +} +@Injectable() +export class SpaceProductItemModelRepository extends Repository { + constructor(private dataSource: DataSource) { + super(SpaceProductItemModelEntity, dataSource.createEntityManager()); + } +} diff --git a/libs/common/src/modules/space-model/space-model.repository.module.ts b/libs/common/src/modules/space-model/space-model.repository.module.ts new file mode 100644 index 0000000..573bba0 --- /dev/null +++ b/libs/common/src/modules/space-model/space-model.repository.module.ts @@ -0,0 +1,23 @@ +import { TypeOrmModule } from '@nestjs/typeorm'; +import { + SpaceModelEntity, + SpaceProductItemModelEntity, + SpaceProductModelEntity, + SubspaceModelEntity, +} from './entities'; +import { Module } from '@nestjs/common'; + +@Module({ + providers: [], + exports: [], + controllers: [], + imports: [ + TypeOrmModule.forFeature([ + SpaceModelEntity, + SubspaceModelEntity, + SpaceProductModelEntity, + SpaceProductItemModelEntity, + ]), + ], +}) +export class SpaceModelRepositoryModule {} From 1ed7f4f2efab3fdffa2d9a9637dc4b80ab5b229c Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Tue, 10 Dec 2024 15:16:35 +0400 Subject: [PATCH 02/11] changed name to tag --- .../modules/space-model/entities/space-product-item.entity.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/common/src/modules/space-model/entities/space-product-item.entity.ts b/libs/common/src/modules/space-model/entities/space-product-item.entity.ts index f754769..8c0b9c7 100644 --- a/libs/common/src/modules/space-model/entities/space-product-item.entity.ts +++ b/libs/common/src/modules/space-model/entities/space-product-item.entity.ts @@ -9,7 +9,7 @@ export class SpaceProductItemModelEntity extends AbstractEntity SpaceProductModelEntity, From 3ee3ff1fc3894346f43969813027f62911f797a2 Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Tue, 10 Dec 2024 15:28:29 +0400 Subject: [PATCH 03/11] space model can have nullable space product model and subspace model --- .../src/modules/space-model/entities/space-model.entity.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libs/common/src/modules/space-model/entities/space-model.entity.ts b/libs/common/src/modules/space-model/entities/space-model.entity.ts index 8df7e45..517f273 100644 --- a/libs/common/src/modules/space-model/entities/space-model.entity.ts +++ b/libs/common/src/modules/space-model/entities/space-model.entity.ts @@ -23,6 +23,7 @@ export class SpaceModelEntity extends AbstractEntity { (subspaceModel) => subspaceModel.spaceModel, { cascade: true, + nullable: true, }, ) public subspaceModels: SubspaceModelEntity[]; @@ -32,6 +33,7 @@ export class SpaceModelEntity extends AbstractEntity { (productModel) => productModel.spaceModel, { cascade: true, + nullable: true, }, ) public spaceProductModels: SpaceProductModelEntity[]; From dc00fdc554f03253303a1ed1e2221dafc12eaced Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Tue, 10 Dec 2024 17:33:50 +0400 Subject: [PATCH 04/11] added project space model relation --- .../modules/project/entities/project.entity.ts | 6 +++++- .../space-model/entities/space-model.entity.ts | 18 +++++++++++++++++- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/libs/common/src/modules/project/entities/project.entity.ts b/libs/common/src/modules/project/entities/project.entity.ts index 04e3f11..f7213a2 100644 --- a/libs/common/src/modules/project/entities/project.entity.ts +++ b/libs/common/src/modules/project/entities/project.entity.ts @@ -2,6 +2,7 @@ import { Entity, Column, Unique, OneToMany } from 'typeorm'; import { AbstractEntity } from '../../abstract/entities/abstract.entity'; import { ProjectDto } from '../dtos'; import { CommunityEntity } from '../../community/entities'; +import { SpaceModelEntity } from '../../space-model'; @Entity({ name: 'project' }) @Unique(['name']) @@ -20,7 +21,10 @@ export class ProjectEntity extends AbstractEntity { @Column({ length: 255, nullable: true }) description: string; - + + @OneToMany(() => SpaceModelEntity, (spaceModel) => spaceModel.project) + public spaceModels: SpaceModelEntity[]; + @OneToMany(() => CommunityEntity, (community) => community.project) communities: CommunityEntity[]; diff --git a/libs/common/src/modules/space-model/entities/space-model.entity.ts b/libs/common/src/modules/space-model/entities/space-model.entity.ts index 517f273..9937a01 100644 --- a/libs/common/src/modules/space-model/entities/space-model.entity.ts +++ b/libs/common/src/modules/space-model/entities/space-model.entity.ts @@ -1,10 +1,19 @@ -import { Entity, Column, OneToMany } from 'typeorm'; +import { + Entity, + Column, + OneToMany, + ManyToOne, + JoinColumn, + Unique, +} from 'typeorm'; import { AbstractEntity } from '../../abstract/entities/abstract.entity'; import { SpaceModelDto } from '../dtos'; import { SubspaceModelEntity } from './subspace-model.entity'; import { SpaceProductModelEntity } from './space-product-model.entity'; +import { ProjectEntity } from '../../project/entities'; @Entity({ name: 'space-model' }) +@Unique(['modelName', 'project']) export class SpaceModelEntity extends AbstractEntity { @Column({ type: 'uuid', @@ -18,6 +27,13 @@ export class SpaceModelEntity extends AbstractEntity { }) public modelName: string; + @ManyToOne(() => ProjectEntity, (project) => project.spaceModels, { + nullable: false, + onDelete: 'CASCADE', + }) + @JoinColumn({ name: 'project_uuid' }) + public project: ProjectEntity; + @OneToMany( () => SubspaceModelEntity, (subspaceModel) => subspaceModel.spaceModel, From b945740fb8ae102a20bc22e594b5268b0efdbbf4 Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Wed, 11 Dec 2024 10:37:21 +0400 Subject: [PATCH 05/11] ensure in uniqueness in tag --- .../entities/space-product-item.entity.ts | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/libs/common/src/modules/space-model/entities/space-product-item.entity.ts b/libs/common/src/modules/space-model/entities/space-product-item.entity.ts index 8c0b9c7..d830fba 100644 --- a/libs/common/src/modules/space-model/entities/space-product-item.entity.ts +++ b/libs/common/src/modules/space-model/entities/space-product-item.entity.ts @@ -1,10 +1,11 @@ -import { Entity, Column, ManyToOne } from 'typeorm'; +import { Entity, Column, ManyToOne, Unique } from 'typeorm'; import { AbstractEntity } from '../../abstract/entities/abstract.entity'; -import { SpaceEntity } from '../../space/entities'; import { SpaceProductItemDto } from '../dtos'; import { SpaceProductModelEntity } from './space-product-model.entity'; +import { SpaceModelEntity } from './space-model.entity'; @Entity({ name: 'space-product-item' }) +@Unique(['tag', 'spaceProductModel', 'spaceModel']) export class SpaceProductItemModelEntity extends AbstractEntity { @Column({ nullable: false, @@ -16,14 +17,16 @@ export class SpaceProductItemModelEntity extends AbstractEntity spaceProductModel.items, { nullable: false, - onDelete: 'CASCADE', }, ) public spaceProductModel: SpaceProductModelEntity; - @ManyToOne(() => SpaceEntity, (space) => space.spaceProducts, { - nullable: true, - onDelete: 'CASCADE', - }) - public space: SpaceEntity; // Optional for associating the item to a space directly + @ManyToOne( + () => SpaceModelEntity, + (spaceModel) => spaceModel.spaceProductModels, + { + nullable: false, + }, + ) + public spaceModel: SpaceModelEntity; } From b2a422f71b7e27ad357713498ab0b5d2a1e79716 Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Wed, 11 Dec 2024 10:51:17 +0400 Subject: [PATCH 06/11] created space model create service --- .../dtos/create-space-model.dto.ts | 33 +++++++ .../create-space-product-item-model.dto.ts | 12 +++ .../dtos/create-space-product-model.dto.ts | 30 ++++++ .../dtos/create-subspace-model.dto.ts | 12 +++ src/space-model/dtos/index.ts | 5 + src/space-model/dtos/project-param.dto.ts | 11 +++ src/space-model/services/index.ts | 4 + .../services/space-model.service.ts | 94 +++++++++++++++++++ .../space-product-item-model.service.ts | 79 ++++++++++++++++ .../services/space-product-model.service.ts | 61 ++++++++++++ .../services/subspace-model.service.ts | 33 +++++++ 11 files changed, 374 insertions(+) create mode 100644 src/space-model/dtos/create-space-model.dto.ts create mode 100644 src/space-model/dtos/create-space-product-item-model.dto.ts create mode 100644 src/space-model/dtos/create-space-product-model.dto.ts create mode 100644 src/space-model/dtos/create-subspace-model.dto.ts create mode 100644 src/space-model/dtos/index.ts create mode 100644 src/space-model/dtos/project-param.dto.ts create mode 100644 src/space-model/services/index.ts create mode 100644 src/space-model/services/space-model.service.ts create mode 100644 src/space-model/services/space-product-item-model.service.ts create mode 100644 src/space-model/services/space-product-model.service.ts create mode 100644 src/space-model/services/subspace-model.service.ts diff --git a/src/space-model/dtos/create-space-model.dto.ts b/src/space-model/dtos/create-space-model.dto.ts new file mode 100644 index 0000000..9506728 --- /dev/null +++ b/src/space-model/dtos/create-space-model.dto.ts @@ -0,0 +1,33 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsNotEmpty, IsString, IsArray, ValidateNested } from 'class-validator'; +import { Type } from 'class-transformer'; +import { CreateSubspaceModelDto } from './create-subspace-model.dto'; +import { CreateSpaceProductModelDto } from './create-space-product-model.dto'; + +export class CreateSpaceModelDto { + @ApiProperty({ + description: 'Name of the space model', + example: 'Apartment Model', + }) + @IsNotEmpty() + @IsString() + modelName: string; + + @ApiProperty({ + description: 'List of subspaces included in the model', + type: [CreateSubspaceModelDto], + }) + @IsArray() + @ValidateNested({ each: true }) + @Type(() => CreateSubspaceModelDto) + subspaceModels?: CreateSubspaceModelDto[]; + + @ApiProperty({ + description: 'List of products included in the model', + type: [CreateSpaceProductModelDto], + }) + @IsArray() + @ValidateNested({ each: true }) + @Type(() => CreateSpaceProductModelDto) + spaceProductModels?: CreateSpaceProductModelDto[]; +} diff --git a/src/space-model/dtos/create-space-product-item-model.dto.ts b/src/space-model/dtos/create-space-product-item-model.dto.ts new file mode 100644 index 0000000..3825219 --- /dev/null +++ b/src/space-model/dtos/create-space-product-item-model.dto.ts @@ -0,0 +1,12 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsNotEmpty, IsString } from 'class-validator'; + +export class CreateSpaceProductItemModelDto { + @ApiProperty({ + description: 'Specific name for the product item', + example: 'Light 1', + }) + @IsNotEmpty() + @IsString() + tag: string; +} diff --git a/src/space-model/dtos/create-space-product-model.dto.ts b/src/space-model/dtos/create-space-product-model.dto.ts new file mode 100644 index 0000000..63fb9dd --- /dev/null +++ b/src/space-model/dtos/create-space-product-model.dto.ts @@ -0,0 +1,30 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsNotEmpty, IsString, IsArray, ValidateNested } from 'class-validator'; +import { Type } from 'class-transformer'; +import { CreateSpaceProductItemModelDto } from './create-space-product-item-model.dto'; + +export class CreateSpaceProductModelDto { + @ApiProperty({ + description: 'ID of the product associated with the model', + example: 'product-uuid', + }) + @IsNotEmpty() + @IsString() + productId: string; + + @ApiProperty({ + description: 'Number of products in the model', + example: 3, + }) + @IsNotEmpty() + productCount: number; + + @ApiProperty({ + description: 'Specific names for each product item', + type: [CreateSpaceProductItemModelDto], + }) + @IsArray() + @ValidateNested({ each: true }) + @Type(() => CreateSpaceProductItemModelDto) + items: CreateSpaceProductItemModelDto[]; +} diff --git a/src/space-model/dtos/create-subspace-model.dto.ts b/src/space-model/dtos/create-subspace-model.dto.ts new file mode 100644 index 0000000..a27ad3b --- /dev/null +++ b/src/space-model/dtos/create-subspace-model.dto.ts @@ -0,0 +1,12 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsNotEmpty, IsString } from 'class-validator'; + +export class CreateSubspaceModelDto { + @ApiProperty({ + description: 'Name of the subspace', + example: 'Living Room', + }) + @IsNotEmpty() + @IsString() + subspaceName: string; +} diff --git a/src/space-model/dtos/index.ts b/src/space-model/dtos/index.ts new file mode 100644 index 0000000..b4b1e07 --- /dev/null +++ b/src/space-model/dtos/index.ts @@ -0,0 +1,5 @@ +export * from './create-space-model.dto'; +export * from './create-space-product-item-model.dto'; +export * from './create-space-product-model.dto'; +export * from './create-subspace-model.dto'; +export * from './project-param.dto'; diff --git a/src/space-model/dtos/project-param.dto.ts b/src/space-model/dtos/project-param.dto.ts new file mode 100644 index 0000000..e7d9e97 --- /dev/null +++ b/src/space-model/dtos/project-param.dto.ts @@ -0,0 +1,11 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsUUID } from 'class-validator'; + +export class projectParam { + @ApiProperty({ + description: 'UUID of the Project', + example: 'd290f1ee-6c54-4b01-90e6-d701748f0851', + }) + @IsUUID() + projectUuid: string; +} diff --git a/src/space-model/services/index.ts b/src/space-model/services/index.ts new file mode 100644 index 0000000..88e2d41 --- /dev/null +++ b/src/space-model/services/index.ts @@ -0,0 +1,4 @@ +export * from './space-model.service'; +export * from './space-product-item-model.service'; +export * from './space-product-model.service'; +export * from './subspace-model.service'; diff --git a/src/space-model/services/space-model.service.ts b/src/space-model/services/space-model.service.ts new file mode 100644 index 0000000..f7b8f28 --- /dev/null +++ b/src/space-model/services/space-model.service.ts @@ -0,0 +1,94 @@ +import { SpaceModelRepository } from '@app/common/modules/space-model'; +import { HttpException, HttpStatus, Injectable } from '@nestjs/common'; +import { CreateSpaceModelDto } from '../dtos'; +import { ProjectRepository } from '@app/common/modules/project/repositiories'; +import { ProjectParam } from 'src/community/dtos'; +import { SuccessResponseDto } from '@app/common/dto/success.response.dto'; +import { SubSpaceModelService } from './subspace-model.service'; +import { SpaceProductModelService } from './space-product-model.service'; + +@Injectable() +export class SpaceModelService { + constructor( + private readonly spaceModelRepository: SpaceModelRepository, + private readonly projectRepository: ProjectRepository, + private readonly subSpaceModelService: SubSpaceModelService, + private readonly spaceProductModelService: SpaceProductModelService, + ) {} + + async createSpaceModel( + createSpaceModelDto: CreateSpaceModelDto, + params: ProjectParam, + ) { + const { modelName, subspaceModels, spaceProductModels } = + createSpaceModelDto; + const project = await this.validateProject(params.projectUuid); + + try { + const isModelExist = await this.validateName( + modelName, + params.projectUuid, + ); + if (isModelExist) { + throw new HttpException( + `Model name "${modelName}" already exists in this project ${project.name}.`, + HttpStatus.CONFLICT, + ); + } + + const spaceModel = await this.spaceModelRepository.create({ + modelName, + project, + }); + const savedSpaceModel = await this.spaceModelRepository.save(spaceModel); + + await this.subSpaceModelService.createSubSpaceModels( + subspaceModels, + savedSpaceModel, + ); + + await this.spaceProductModelService.createSpaceProductModels( + spaceProductModels, + savedSpaceModel, + ); + + return new SuccessResponseDto({ + message: `Successfully created new space model with uuid ${savedSpaceModel.uuid}`, + data: savedSpaceModel, + statusCode: HttpStatus.CREATED, + }); + } catch (error) { + if (error instanceof HttpException) { + throw error; + } + + throw new HttpException( + error.message || `An unexpected error occurred`, + HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + } + + async validateProject(projectUuid: string) { + const project = await this.projectRepository.findOne({ + where: { + uuid: projectUuid, + }, + }); + + if (!project) { + throw new HttpException( + `Project with uuid ${projectUuid} not found`, + HttpStatus.NOT_FOUND, + ); + } + return project; + } + + async validateName(modelName: string, projectUuid: string): Promise { + const isModelExist = await this.spaceModelRepository.exists({ + where: { modelName, project: { uuid: projectUuid } }, + }); + return isModelExist; + } +} diff --git a/src/space-model/services/space-product-item-model.service.ts b/src/space-model/services/space-product-item-model.service.ts new file mode 100644 index 0000000..ebf7d2d --- /dev/null +++ b/src/space-model/services/space-product-item-model.service.ts @@ -0,0 +1,79 @@ +import { + SpaceModelEntity, + SpaceProductItemModelRepository, + SpaceProductModelEntity, +} from '@app/common/modules/space-model'; +import { HttpException, HttpStatus, Injectable } from '@nestjs/common'; +import { CreateSpaceProductItemModelDto } from '../dtos'; + +@Injectable() +export class SpaceProductItemModelService { + constructor( + private readonly spaceProductItemRepository: SpaceProductItemModelRepository, + ) {} + + async createProdutItemModel( + itemModelDtos: CreateSpaceProductItemModelDto[], + spaceProductModel: SpaceProductModelEntity, + spaceModel: SpaceModelEntity, + ) { + try { + await this.validateTags(itemModelDtos, spaceModel); + + for (const itemModelDto of itemModelDtos) { + await this.create(itemModelDto, spaceProductModel, spaceModel); + } + } catch (error) { + throw new HttpException( + error.message || `An unexpected error occurred`, + HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + } + + async create( + itemModelDto: CreateSpaceProductItemModelDto, + spaceProductModel: SpaceProductModelEntity, + spaceModel: SpaceModelEntity, + ) { + const productItem = this.spaceProductItemRepository.create({ + tag: itemModelDto.tag, + spaceProductModel, + spaceModel, + }); + await this.spaceProductItemRepository.save(productItem); + } + + async validateTags( + itemModelDtos: CreateSpaceProductItemModelDto[], + spaceModel: SpaceModelEntity, + ) { + const incomingTags = itemModelDtos.map((item) => item.tag); + + const duplicateTags = incomingTags.filter( + (tag, index) => incomingTags.indexOf(tag) !== index, + ); + if (duplicateTags.length > 0) { + throw new HttpException( + `Duplicate tags found in request: ${duplicateTags.join(', ')}`, + HttpStatus.CONFLICT, + ); + } + + const existingTags = await this.spaceProductItemRepository.find({ + where: { spaceModel }, + select: ['tag'], + }); + const existingTagSet = new Set(existingTags.map((item) => item.tag)); + + const conflictingTags = incomingTags.filter((tag) => + existingTagSet.has(tag), + ); + if (conflictingTags.length > 0) { + throw new HttpException( + `Tags already exist in the model: ${conflictingTags.join(', ')}`, + HttpStatus.CONFLICT, + ); + } + } +} diff --git a/src/space-model/services/space-product-model.service.ts b/src/space-model/services/space-product-model.service.ts new file mode 100644 index 0000000..10ee9f5 --- /dev/null +++ b/src/space-model/services/space-product-model.service.ts @@ -0,0 +1,61 @@ +import { + SpaceModelEntity, + SpaceProductModelRepository, +} from '@app/common/modules/space-model'; +import { HttpException, HttpStatus, Injectable } from '@nestjs/common'; +import { CreateSpaceProductModelDto } from '../dtos'; +import { ProductRepository } from '@app/common/modules/product/repositories'; +import { SpaceProductItemModelService } from './space-product-item-model.service'; + +@Injectable() +export class SpaceProductModelService { + constructor( + private readonly spaceProductModelRepository: SpaceProductModelRepository, + private readonly productRepository: ProductRepository, + private readonly spaceProductItemModelService: SpaceProductItemModelService, + ) {} + + async createSpaceProductModels( + spaceProductModelDtos: CreateSpaceProductModelDto[], + spaceModel: SpaceModelEntity, + ) { + try { + for (const spaceProductModelDto of spaceProductModelDtos) { + await this.create(spaceProductModelDto, spaceModel); + } + } catch (error) { + throw new HttpException( + error.message || `An unexpected error occurred`, + HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + } + + async create( + spaceProductModelDto: CreateSpaceProductModelDto, + spaceModel: SpaceModelEntity, + ) { + const product = await this.productRepository.findOneBy({ + uuid: spaceProductModelDto.productId, + }); + if (!product) { + throw new HttpException( + `Product with ID ${spaceProductModelDto.productId} not found`, + HttpStatus.NOT_FOUND, + ); + } + const spaceProductModel = this.spaceProductModelRepository.create({ + product, + productCount: spaceProductModelDto.productCount, + spaceModel: spaceModel, + }); + const newProductModel = + await this.spaceProductModelRepository.save(spaceProductModel); + + await this.spaceProductItemModelService.createProdutItemModel( + spaceProductModelDto.items, + newProductModel, + spaceModel, + ); + } +} diff --git a/src/space-model/services/subspace-model.service.ts b/src/space-model/services/subspace-model.service.ts new file mode 100644 index 0000000..45d33ef --- /dev/null +++ b/src/space-model/services/subspace-model.service.ts @@ -0,0 +1,33 @@ +import { + SpaceModelEntity, + SubspaceModelRepository, +} from '@app/common/modules/space-model'; +import { HttpException, HttpStatus, Injectable } from '@nestjs/common'; +import { CreateSubspaceModelDto } from '../dtos'; + +@Injectable() +export class SubSpaceModelService { + constructor( + private readonly subspaceModelRepository: SubspaceModelRepository, + ) {} + + async createSubSpaceModels( + subSpaceModelDtos: CreateSubspaceModelDto[], + spaceModel: SpaceModelEntity, + ) { + try { + for (const subspaceDto of subSpaceModelDtos) { + const subspace = this.subspaceModelRepository.create({ + subspaceName: subspaceDto.subspaceName, + spaceModel: spaceModel, + }); + await this.subspaceModelRepository.save(subspace); + } + } catch (error) { + throw new HttpException( + error.message || `An unexpected error occurred`, + HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + } +} From 02faca33975d783ffa260215f53427d485b3c5c5 Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Wed, 11 Dec 2024 11:06:41 +0400 Subject: [PATCH 07/11] added space model controller for create --- libs/common/src/constants/controller-route.ts | 10 +++++ src/app.module.ts | 3 +- src/space-model/controllers/index.ts | 1 + .../controllers/space-model.controller.ts | 35 ++++++++++++++++++ src/space-model/index.ts | 1 + src/space-model/space-model.module.ts | 37 +++++++++++++++++++ src/space/services/space.service.ts | 7 +++- 7 files changed, 92 insertions(+), 2 deletions(-) create mode 100644 src/space-model/controllers/index.ts create mode 100644 src/space-model/controllers/space-model.controller.ts create mode 100644 src/space-model/index.ts create mode 100644 src/space-model/space-model.module.ts diff --git a/libs/common/src/constants/controller-route.ts b/libs/common/src/constants/controller-route.ts index bd37317..10e20d9 100644 --- a/libs/common/src/constants/controller-route.ts +++ b/libs/common/src/constants/controller-route.ts @@ -266,6 +266,16 @@ export class ControllerRoute { }; }; + static SPACE_MODEL = class { + public static readonly ROUTE = '/projects/:projectUuid/space-models'; + static ACTIONS = class { + public static readonly CREATE_SPACE_MODEL_SUMMARY = + 'Create a New Space Model'; + public static readonly CREATE_SPACE_MODEL_DESCRIPTION = + 'This endpoint allows you to create a new space model within a specified project. A space model defines the structure of spaces, including subspaces, products, and product items, and is uniquely identifiable within the project.'; + }; + }; + static PRODUCT = class { public static readonly ROUTE = 'products'; static ACTIONS = class { diff --git a/src/app.module.ts b/src/app.module.ts index bb5986b..ac06b84 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -23,6 +23,7 @@ import { ScheduleModule } from './schedule/schedule.module'; import { SpaceModule } from './space/space.module'; import { ProductModule } from './product'; import { ProjectModule } from './project'; +import { SpaceModelModule } from './space-model'; @Module({ imports: [ ConfigModule.forRoot({ @@ -34,7 +35,7 @@ import { ProjectModule } from './project'; CommunityModule, SpaceModule, - + SpaceModelModule, GroupModule, DeviceModule, DeviceMessagesSubscriptionModule, diff --git a/src/space-model/controllers/index.ts b/src/space-model/controllers/index.ts new file mode 100644 index 0000000..c12699e --- /dev/null +++ b/src/space-model/controllers/index.ts @@ -0,0 +1 @@ +export * from './space-model.controller'; diff --git a/src/space-model/controllers/space-model.controller.ts b/src/space-model/controllers/space-model.controller.ts new file mode 100644 index 0000000..8475b6c --- /dev/null +++ b/src/space-model/controllers/space-model.controller.ts @@ -0,0 +1,35 @@ +import { ControllerRoute } from '@app/common/constants/controller-route'; +import { Body, Controller, Param, Post, UseGuards } from '@nestjs/common'; +import { ApiBearerAuth, ApiOperation, ApiTags } from '@nestjs/swagger'; +import { SpaceModelService } from '../services'; +import { CreateSpaceModelDto } from '../dtos'; +import { ProjectParam } from 'src/community/dtos'; +import { JwtAuthGuard } from '@app/common/guards/jwt.auth.guard'; +import { BaseResponseDto } from '@app/common/dto/base.response.dto'; + +@ApiTags('Space Model Module') +@Controller({ + version: '1', + path: ControllerRoute.SPACE_MODEL.ROUTE, +}) +export class SpaceModelController { + constructor(private readonly spaceModelService: SpaceModelService) {} + + @ApiBearerAuth() + @UseGuards(JwtAuthGuard) + @ApiOperation({ + summary: ControllerRoute.SPACE_MODEL.ACTIONS.CREATE_SPACE_MODEL_SUMMARY, + description: + ControllerRoute.SPACE_MODEL.ACTIONS.CREATE_SPACE_MODEL_DESCRIPTION, + }) + @Post() + async createSpaceModel( + @Body() createSpaceModelDto: CreateSpaceModelDto, + @Param() projectParam: ProjectParam, + ): Promise { + return await this.spaceModelService.createSpaceModel( + createSpaceModelDto, + projectParam, + ); + } +} diff --git a/src/space-model/index.ts b/src/space-model/index.ts new file mode 100644 index 0000000..8885fc3 --- /dev/null +++ b/src/space-model/index.ts @@ -0,0 +1 @@ +export * from './space-model.module'; diff --git a/src/space-model/space-model.module.ts b/src/space-model/space-model.module.ts new file mode 100644 index 0000000..bbc6245 --- /dev/null +++ b/src/space-model/space-model.module.ts @@ -0,0 +1,37 @@ +import { SpaceRepositoryModule } from '@app/common/modules/space/space.repository.module'; +import { Module } from '@nestjs/common'; +import { ConfigModule } from '@nestjs/config'; +import { SpaceModelController } from './controllers'; +import { + SpaceModelService, + SpaceProductItemModelService, + SpaceProductModelService, + SubSpaceModelService, +} from './services'; +import { + SpaceModelRepository, + SpaceProductItemModelRepository, + SpaceProductModelRepository, + SubspaceModelRepository, +} from '@app/common/modules/space-model'; +import { ProjectRepository } from '@app/common/modules/project/repositiories'; +import { ProductRepository } from '@app/common/modules/product/repositories'; + +@Module({ + imports: [ConfigModule, SpaceRepositoryModule], + controllers: [SpaceModelController], + providers: [ + SpaceModelService, + SpaceModelRepository, + ProjectRepository, + SubSpaceModelService, + SpaceProductModelService, + SubspaceModelRepository, + SpaceProductModelRepository, + ProductRepository, + SpaceProductItemModelService, + SpaceProductItemModelRepository, + ], + exports: [], +}) +export class SpaceModelModule {} diff --git a/src/space/services/space.service.ts b/src/space/services/space.service.ts index 0055ddf..8189f8c 100644 --- a/src/space/services/space.service.ts +++ b/src/space/services/space.service.ts @@ -5,7 +5,12 @@ import { HttpStatus, Injectable, } from '@nestjs/common'; -import { AddSpaceDto, CommunitySpaceParam, GetSpaceParam, UpdateSpaceDto } from '../dtos'; +import { + AddSpaceDto, + CommunitySpaceParam, + GetSpaceParam, + UpdateSpaceDto, +} from '../dtos'; import { SuccessResponseDto } from '@app/common/dto/success.response.dto'; import { BaseResponseDto } from '@app/common/dto/base.response.dto'; import { CommunityRepository } from '@app/common/modules/community/repositories'; From 69d0065ee6c912711a24858a674cbb1f86b03a24 Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Wed, 11 Dec 2024 11:46:30 +0400 Subject: [PATCH 08/11] added unique subspace name --- .../entities/subspace-model.entity.ts | 3 +- .../dtos/create-space-product-model.dto.ts | 9 ++++- .../services/space-model.service.ts | 2 +- .../space-product-item-model.service.ts | 4 ++ .../services/space-product-model.service.ts | 4 ++ .../services/subspace-model.service.ts | 37 +++++++++++++++++++ 6 files changed, 56 insertions(+), 3 deletions(-) diff --git a/libs/common/src/modules/space-model/entities/subspace-model.entity.ts b/libs/common/src/modules/space-model/entities/subspace-model.entity.ts index abb1c64..776f831 100644 --- a/libs/common/src/modules/space-model/entities/subspace-model.entity.ts +++ b/libs/common/src/modules/space-model/entities/subspace-model.entity.ts @@ -1,9 +1,10 @@ -import { Column, Entity, ManyToOne } from 'typeorm'; +import { Column, Entity, ManyToOne, Unique } from 'typeorm'; import { AbstractEntity } from '../../abstract/entities/abstract.entity'; import { SpaceModelEntity } from './space-model.entity'; import { SubSpaceModelDto } from '../dtos'; @Entity({ name: 'subspace-model' }) +@Unique(['subspaceName', 'spaceModel']) export class SubspaceModelEntity extends AbstractEntity { @Column({ type: 'uuid', diff --git a/src/space-model/dtos/create-space-product-model.dto.ts b/src/space-model/dtos/create-space-product-model.dto.ts index 63fb9dd..8b98bc6 100644 --- a/src/space-model/dtos/create-space-product-model.dto.ts +++ b/src/space-model/dtos/create-space-product-model.dto.ts @@ -1,5 +1,11 @@ import { ApiProperty } from '@nestjs/swagger'; -import { IsNotEmpty, IsString, IsArray, ValidateNested } from 'class-validator'; +import { + IsNotEmpty, + IsString, + IsArray, + ValidateNested, + IsInt, +} from 'class-validator'; import { Type } from 'class-transformer'; import { CreateSpaceProductItemModelDto } from './create-space-product-item-model.dto'; @@ -17,6 +23,7 @@ export class CreateSpaceProductModelDto { example: 3, }) @IsNotEmpty() + @IsInt() productCount: number; @ApiProperty({ diff --git a/src/space-model/services/space-model.service.ts b/src/space-model/services/space-model.service.ts index f7b8f28..37b9ab1 100644 --- a/src/space-model/services/space-model.service.ts +++ b/src/space-model/services/space-model.service.ts @@ -36,7 +36,7 @@ export class SpaceModelService { ); } - const spaceModel = await this.spaceModelRepository.create({ + const spaceModel = this.spaceModelRepository.create({ modelName, project, }); diff --git a/src/space-model/services/space-product-item-model.service.ts b/src/space-model/services/space-product-item-model.service.ts index ebf7d2d..0a5b43d 100644 --- a/src/space-model/services/space-product-item-model.service.ts +++ b/src/space-model/services/space-product-item-model.service.ts @@ -24,6 +24,10 @@ export class SpaceProductItemModelService { await this.create(itemModelDto, spaceProductModel, spaceModel); } } catch (error) { + if (error instanceof HttpException) { + throw error; + } + throw new HttpException( error.message || `An unexpected error occurred`, HttpStatus.INTERNAL_SERVER_ERROR, diff --git a/src/space-model/services/space-product-model.service.ts b/src/space-model/services/space-product-model.service.ts index 10ee9f5..c478dca 100644 --- a/src/space-model/services/space-product-model.service.ts +++ b/src/space-model/services/space-product-model.service.ts @@ -24,6 +24,10 @@ export class SpaceProductModelService { await this.create(spaceProductModelDto, spaceModel); } } catch (error) { + if (error instanceof HttpException) { + throw error; + } + throw new HttpException( error.message || `An unexpected error occurred`, HttpStatus.INTERNAL_SERVER_ERROR, diff --git a/src/space-model/services/subspace-model.service.ts b/src/space-model/services/subspace-model.service.ts index 45d33ef..5d4d347 100644 --- a/src/space-model/services/subspace-model.service.ts +++ b/src/space-model/services/subspace-model.service.ts @@ -16,6 +16,7 @@ export class SubSpaceModelService { spaceModel: SpaceModelEntity, ) { try { + this.validateInputDtos(subSpaceModelDtos); for (const subspaceDto of subSpaceModelDtos) { const subspace = this.subspaceModelRepository.create({ subspaceName: subspaceDto.subspaceName, @@ -24,10 +25,46 @@ export class SubSpaceModelService { await this.subspaceModelRepository.save(subspace); } } catch (error) { + if (error instanceof HttpException) { + throw error; + } + throw new HttpException( error.message || `An unexpected error occurred`, HttpStatus.INTERNAL_SERVER_ERROR, ); } } + + 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(); + const duplicateNames = new Set(); + + 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, + ); + } + } } From 4f4fd7b73423ae766132419df26173f169091512 Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Wed, 11 Dec 2024 12:03:09 +0400 Subject: [PATCH 09/11] added count validation --- .../dtos/create-space-product-model.dto.ts | 2 ++ .../services/space-product-model.service.ts | 12 ++++++++++++ 2 files changed, 14 insertions(+) diff --git a/src/space-model/dtos/create-space-product-model.dto.ts b/src/space-model/dtos/create-space-product-model.dto.ts index 8b98bc6..11d8bb5 100644 --- a/src/space-model/dtos/create-space-product-model.dto.ts +++ b/src/space-model/dtos/create-space-product-model.dto.ts @@ -5,6 +5,7 @@ import { IsArray, ValidateNested, IsInt, + ArrayNotEmpty, } from 'class-validator'; import { Type } from 'class-transformer'; import { CreateSpaceProductItemModelDto } from './create-space-product-item-model.dto'; @@ -31,6 +32,7 @@ export class CreateSpaceProductModelDto { type: [CreateSpaceProductItemModelDto], }) @IsArray() + @ArrayNotEmpty() @ValidateNested({ each: true }) @Type(() => CreateSpaceProductItemModelDto) items: CreateSpaceProductItemModelDto[]; diff --git a/src/space-model/services/space-product-model.service.ts b/src/space-model/services/space-product-model.service.ts index c478dca..cf60203 100644 --- a/src/space-model/services/space-product-model.service.ts +++ b/src/space-model/services/space-product-model.service.ts @@ -39,6 +39,8 @@ export class SpaceProductModelService { spaceProductModelDto: CreateSpaceProductModelDto, spaceModel: SpaceModelEntity, ) { + this.validateCount(spaceProductModelDto); + const product = await this.productRepository.findOneBy({ uuid: spaceProductModelDto.productId, }); @@ -62,4 +64,14 @@ export class SpaceProductModelService { spaceModel, ); } + + private validateCount(spaceProductModelDto: CreateSpaceProductModelDto) { + const productItemCount = spaceProductModelDto.items.length; + if (spaceProductModelDto.productCount !== productItemCount) { + throw new HttpException( + `Product count (${spaceProductModelDto.productCount}) does not match the number of items (${productItemCount}) for product ID ${spaceProductModelDto.productId}.`, + HttpStatus.BAD_REQUEST, + ); + } + } } From 72c8445d24b6e82fc63bfa6a043e25a9c775c372 Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Wed, 11 Dec 2024 13:23:37 +0400 Subject: [PATCH 10/11] updated create space model with query runner --- .../services/space-model.service.ts | 36 ++++++-- .../space-product-item-model.service.ts | 52 ++++++------ .../services/space-product-model.service.ts | 85 ++++++++++--------- .../services/subspace-model.service.ts | 16 ++-- 4 files changed, 109 insertions(+), 80 deletions(-) diff --git a/src/space-model/services/space-model.service.ts b/src/space-model/services/space-model.service.ts index 37b9ab1..7359e25 100644 --- a/src/space-model/services/space-model.service.ts +++ b/src/space-model/services/space-model.service.ts @@ -6,10 +6,12 @@ import { ProjectParam } from 'src/community/dtos'; import { SuccessResponseDto } from '@app/common/dto/success.response.dto'; import { SubSpaceModelService } from './subspace-model.service'; import { SpaceProductModelService } from './space-product-model.service'; +import { DataSource } from 'typeorm'; @Injectable() export class SpaceModelService { constructor( + private readonly dataSource: DataSource, private readonly spaceModelRepository: SpaceModelRepository, private readonly projectRepository: ProjectRepository, private readonly subSpaceModelService: SubSpaceModelService, @@ -24,6 +26,11 @@ export class SpaceModelService { createSpaceModelDto; const project = await this.validateProject(params.projectUuid); + const queryRunner = this.dataSource.createQueryRunner(); + + await queryRunner.connect(); + await queryRunner.startTransaction(); + try { const isModelExist = await this.validateName( modelName, @@ -40,17 +47,24 @@ export class SpaceModelService { modelName, project, }); - const savedSpaceModel = await this.spaceModelRepository.save(spaceModel); + const savedSpaceModel = await queryRunner.manager.save(spaceModel); - await this.subSpaceModelService.createSubSpaceModels( - subspaceModels, - savedSpaceModel, - ); + if (subspaceModels) { + await this.subSpaceModelService.createSubSpaceModels( + subspaceModels, + savedSpaceModel, + queryRunner, + ); + } - await this.spaceProductModelService.createSpaceProductModels( - spaceProductModels, - savedSpaceModel, - ); + if (spaceProductModels) { + await this.spaceProductModelService.createSpaceProductModels( + spaceProductModels, + savedSpaceModel, + queryRunner, + ); + } + await queryRunner.commitTransaction(); return new SuccessResponseDto({ message: `Successfully created new space model with uuid ${savedSpaceModel.uuid}`, @@ -58,6 +72,8 @@ export class SpaceModelService { statusCode: HttpStatus.CREATED, }); } catch (error) { + await queryRunner.rollbackTransaction(); + if (error instanceof HttpException) { throw error; } @@ -66,6 +82,8 @@ export class SpaceModelService { error.message || `An unexpected error occurred`, HttpStatus.INTERNAL_SERVER_ERROR, ); + } finally { + await queryRunner.release(); } } diff --git a/src/space-model/services/space-product-item-model.service.ts b/src/space-model/services/space-product-item-model.service.ts index 0a5b43d..b2f856c 100644 --- a/src/space-model/services/space-product-item-model.service.ts +++ b/src/space-model/services/space-product-item-model.service.ts @@ -5,6 +5,7 @@ import { } from '@app/common/modules/space-model'; import { HttpException, HttpStatus, Injectable } from '@nestjs/common'; import { CreateSpaceProductItemModelDto } from '../dtos'; +import { QueryRunner } from 'typeorm'; @Injectable() export class SpaceProductItemModelService { @@ -16,41 +17,37 @@ export class SpaceProductItemModelService { itemModelDtos: CreateSpaceProductItemModelDto[], spaceProductModel: SpaceProductModelEntity, spaceModel: SpaceModelEntity, + queryRunner: QueryRunner, ) { - try { - await this.validateTags(itemModelDtos, spaceModel); + await this.validateTags(itemModelDtos, spaceModel, queryRunner); - for (const itemModelDto of itemModelDtos) { - await this.create(itemModelDto, spaceProductModel, spaceModel); - } + try { + const productItems = itemModelDtos.map((dto) => + queryRunner.manager.create(this.spaceProductItemRepository.target, { + tag: dto.tag, + spaceProductModel, + spaceModel, + }), + ); + + await queryRunner.manager.save(productItems); } catch (error) { if (error instanceof HttpException) { throw error; } throw new HttpException( - error.message || `An unexpected error occurred`, + error.message || + 'An unexpected error occurred while creating product items.', HttpStatus.INTERNAL_SERVER_ERROR, ); } } - async create( - itemModelDto: CreateSpaceProductItemModelDto, - spaceProductModel: SpaceProductModelEntity, - spaceModel: SpaceModelEntity, - ) { - const productItem = this.spaceProductItemRepository.create({ - tag: itemModelDto.tag, - spaceProductModel, - spaceModel, - }); - await this.spaceProductItemRepository.save(productItem); - } - - async validateTags( + private async validateTags( itemModelDtos: CreateSpaceProductItemModelDto[], spaceModel: SpaceModelEntity, + queryRunner: QueryRunner, ) { const incomingTags = itemModelDtos.map((item) => item.tag); @@ -59,15 +56,18 @@ export class SpaceProductItemModelService { ); if (duplicateTags.length > 0) { throw new HttpException( - `Duplicate tags found in request: ${duplicateTags.join(', ')}`, - HttpStatus.CONFLICT, + `Duplicate tags found in the request: ${[...new Set(duplicateTags)].join(', ')}`, + HttpStatus.BAD_REQUEST, ); } - const existingTags = await this.spaceProductItemRepository.find({ - where: { spaceModel }, - select: ['tag'], - }); + const existingTags = await queryRunner.manager.find( + this.spaceProductItemRepository.target, + { + where: { spaceModel }, + select: ['tag'], + }, + ); const existingTagSet = new Set(existingTags.map((item) => item.tag)); const conflictingTags = incomingTags.filter((tag) => diff --git a/src/space-model/services/space-product-model.service.ts b/src/space-model/services/space-product-model.service.ts index cf60203..aa08a16 100644 --- a/src/space-model/services/space-product-model.service.ts +++ b/src/space-model/services/space-product-model.service.ts @@ -6,6 +6,7 @@ import { HttpException, HttpStatus, Injectable } from '@nestjs/common'; import { CreateSpaceProductModelDto } from '../dtos'; import { ProductRepository } from '@app/common/modules/product/repositories'; import { SpaceProductItemModelService } from './space-product-item-model.service'; +import { QueryRunner } from 'typeorm'; @Injectable() export class SpaceProductModelService { @@ -18,60 +19,66 @@ export class SpaceProductModelService { async createSpaceProductModels( spaceProductModelDtos: CreateSpaceProductModelDto[], spaceModel: SpaceModelEntity, + queryRunner: QueryRunner, ) { try { - for (const spaceProductModelDto of spaceProductModelDtos) { - await this.create(spaceProductModelDto, spaceModel); - } + const productModels = await Promise.all( + spaceProductModelDtos.map(async (dto) => { + this.validateProductCount(dto); + const product = await this.getProduct(dto.productId); + return queryRunner.manager.create( + this.spaceProductModelRepository.target, + { + product, + productCount: dto.productCount, + spaceModel, + }, + ); + }), + ); + + const savedProductModels = await queryRunner.manager.save(productModels); + + await Promise.all( + spaceProductModelDtos.map((dto, index) => + this.spaceProductItemModelService.createProdutItemModel( + dto.items, + savedProductModels[index], + spaceModel, + queryRunner, + ), + ), + ); } catch (error) { if (error instanceof HttpException) { throw error; } - throw new HttpException( - error.message || `An unexpected error occurred`, + error.message || + 'An unexpected error occurred while creating product models.', HttpStatus.INTERNAL_SERVER_ERROR, ); } } - async create( - spaceProductModelDto: CreateSpaceProductModelDto, - spaceModel: SpaceModelEntity, - ) { - this.validateCount(spaceProductModelDto); - - const product = await this.productRepository.findOneBy({ - uuid: spaceProductModelDto.productId, - }); - if (!product) { + private validateProductCount(dto: CreateSpaceProductModelDto) { + const productItemCount = dto.items.length; + if (dto.productCount !== productItemCount) { throw new HttpException( - `Product with ID ${spaceProductModelDto.productId} not found`, - HttpStatus.NOT_FOUND, - ); - } - const spaceProductModel = this.spaceProductModelRepository.create({ - product, - productCount: spaceProductModelDto.productCount, - spaceModel: spaceModel, - }); - const newProductModel = - await this.spaceProductModelRepository.save(spaceProductModel); - - await this.spaceProductItemModelService.createProdutItemModel( - spaceProductModelDto.items, - newProductModel, - spaceModel, - ); - } - - private validateCount(spaceProductModelDto: CreateSpaceProductModelDto) { - const productItemCount = spaceProductModelDto.items.length; - if (spaceProductModelDto.productCount !== productItemCount) { - throw new HttpException( - `Product count (${spaceProductModelDto.productCount}) does not match the number of items (${productItemCount}) for product ID ${spaceProductModelDto.productId}.`, + `Product count (${dto.productCount}) does not match the number of items (${productItemCount}) for product ID ${dto.productId}.`, HttpStatus.BAD_REQUEST, ); } } + + private async getProduct(productId: string) { + const product = await this.productRepository.findOneBy({ uuid: productId }); + if (!product) { + throw new HttpException( + `Product with ID ${productId} not found.`, + HttpStatus.NOT_FOUND, + ); + } + return product; + } } diff --git a/src/space-model/services/subspace-model.service.ts b/src/space-model/services/subspace-model.service.ts index 5d4d347..a6a75aa 100644 --- a/src/space-model/services/subspace-model.service.ts +++ b/src/space-model/services/subspace-model.service.ts @@ -4,6 +4,7 @@ import { } from '@app/common/modules/space-model'; import { HttpException, HttpStatus, Injectable } from '@nestjs/common'; import { CreateSubspaceModelDto } from '../dtos'; +import { QueryRunner } from 'typeorm'; @Injectable() export class SubSpaceModelService { @@ -14,16 +15,19 @@ export class SubSpaceModelService { async createSubSpaceModels( subSpaceModelDtos: CreateSubspaceModelDto[], spaceModel: SpaceModelEntity, + queryRunner: QueryRunner, ) { + this.validateInputDtos(subSpaceModelDtos); + try { - this.validateInputDtos(subSpaceModelDtos); - for (const subspaceDto of subSpaceModelDtos) { - const subspace = this.subspaceModelRepository.create({ + const subspaces = subSpaceModelDtos.map((subspaceDto) => + queryRunner.manager.create(this.subspaceModelRepository.target, { subspaceName: subspaceDto.subspaceName, spaceModel: spaceModel, - }); - await this.subspaceModelRepository.save(subspace); - } + }), + ); + + await queryRunner.manager.save(subspaces); } catch (error) { if (error instanceof HttpException) { throw error; From 708a1d9a92b1d3b496841702ab53af27b8fdfb7d Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Wed, 11 Dec 2024 13:56:19 +0400 Subject: [PATCH 11/11] updated table name --- .../modules/space-model/entities/space-product-item.entity.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/common/src/modules/space-model/entities/space-product-item.entity.ts b/libs/common/src/modules/space-model/entities/space-product-item.entity.ts index d830fba..3695831 100644 --- a/libs/common/src/modules/space-model/entities/space-product-item.entity.ts +++ b/libs/common/src/modules/space-model/entities/space-product-item.entity.ts @@ -4,7 +4,7 @@ import { SpaceProductItemDto } from '../dtos'; import { SpaceProductModelEntity } from './space-product-model.entity'; import { SpaceModelEntity } from './space-model.entity'; -@Entity({ name: 'space-product-item' }) +@Entity({ name: 'space-product-item-model' }) @Unique(['tag', 'spaceProductModel', 'spaceModel']) export class SpaceProductItemModelEntity extends AbstractEntity { @Column({