From 88a4b26919fe0a9d09da18544f5fab9290e51eca Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Wed, 11 Dec 2024 15:27:06 +0400 Subject: [PATCH 01/26] added entities for space product item --- libs/common/src/database/database.module.ts | 3 +++ .../src/modules/space-model/dtos/index.ts | 2 +- .../dtos/space-product-item-model.dto.ts | 15 ++++++++++++ .../dtos/space-product-model.dto.ts | 6 ++--- .../entities/space-product-item.entity.ts | 17 +++----------- libs/common/src/modules/space/dtos/index.ts | 2 ++ .../dtos/space-product-item.dto.ts | 2 +- .../modules/space/dtos/space-product.dto.ts | 23 +++++++++++++++++++ .../src/modules/space/entities/index.ts | 2 ++ .../entities/space-product-item.entity.ts | 17 ++++++++++++++ .../space/entities/space-product.entity.ts | 8 ++++++- .../space-product-item-model.service.ts | 7 +++--- 12 files changed, 80 insertions(+), 24 deletions(-) create mode 100644 libs/common/src/modules/space-model/dtos/space-product-item-model.dto.ts rename libs/common/src/modules/{space-model => space}/dtos/space-product-item.dto.ts (86%) create mode 100644 libs/common/src/modules/space/dtos/space-product.dto.ts create mode 100644 libs/common/src/modules/space/entities/space-product-item.entity.ts diff --git a/libs/common/src/database/database.module.ts b/libs/common/src/database/database.module.ts index 44fd854..90b287a 100644 --- a/libs/common/src/database/database.module.ts +++ b/libs/common/src/database/database.module.ts @@ -11,6 +11,7 @@ import { PermissionTypeEntity } from '../modules/permission/entities'; import { SpaceEntity, SpaceLinkEntity, + SpaceProductItemEntity, SubspaceEntity, } from '../modules/space/entities'; import { UserSpaceEntity } from '../modules/user/entities'; @@ -78,6 +79,8 @@ import { SpaceProductModelEntity, SpaceProductItemModelEntity, SubspaceModelEntity, + SpaceProductEntity, + SpaceProductItemEntity, ], namingStrategy: new SnakeNamingStrategy(), synchronize: Boolean(JSON.parse(configService.get('DB_SYNC'))), diff --git a/libs/common/src/modules/space-model/dtos/index.ts b/libs/common/src/modules/space-model/dtos/index.ts index f775b4a..3923af4 100644 --- a/libs/common/src/modules/space-model/dtos/index.ts +++ b/libs/common/src/modules/space-model/dtos/index.ts @@ -1,4 +1,4 @@ export * from './subspace-model.dto'; export * from './space-model.dto'; -export * from './space-product-item.dto'; +export * from './space-product-item-model.dto'; export * from './space-product-model.dto'; diff --git a/libs/common/src/modules/space-model/dtos/space-product-item-model.dto.ts b/libs/common/src/modules/space-model/dtos/space-product-item-model.dto.ts new file mode 100644 index 0000000..5eb9bca --- /dev/null +++ b/libs/common/src/modules/space-model/dtos/space-product-item-model.dto.ts @@ -0,0 +1,15 @@ +import { IsString, IsNotEmpty } from 'class-validator'; + +export class SpaceProductItemModelDto { + @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 index eae8088..7952a23 100644 --- 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 @@ -1,6 +1,6 @@ import { ApiProperty } from '@nestjs/swagger'; import { IsNotEmpty, IsNumber, IsString } from 'class-validator'; -import { SpaceProductItemDto } from './space-product-item.dto'; +import { SpaceProductItemModelDto } from './space-product-item-model.dto'; export class SpaceProductModelDto { @IsString() @@ -17,7 +17,7 @@ export class SpaceProductModelDto { @ApiProperty({ description: 'List of individual items with specific names for the product', - type: [SpaceProductItemDto], + type: [SpaceProductItemModelDto], }) - items: SpaceProductItemDto[]; + items: SpaceProductItemModelDto[]; } 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 3695831..7d39974 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,12 +1,10 @@ -import { Entity, Column, ManyToOne, Unique } from 'typeorm'; +import { Entity, Column, ManyToOne } from 'typeorm'; import { AbstractEntity } from '../../abstract/entities/abstract.entity'; -import { SpaceProductItemDto } from '../dtos'; +import { SpaceProductItemModelDto } from '../dtos'; import { SpaceProductModelEntity } from './space-product-model.entity'; -import { SpaceModelEntity } from './space-model.entity'; @Entity({ name: 'space-product-item-model' }) -@Unique(['tag', 'spaceProductModel', 'spaceModel']) -export class SpaceProductItemModelEntity extends AbstractEntity { +export class SpaceProductItemModelEntity extends AbstractEntity { @Column({ nullable: false, }) @@ -20,13 +18,4 @@ export class SpaceProductItemModelEntity extends AbstractEntity SpaceModelEntity, - (spaceModel) => spaceModel.spaceProductModels, - { - nullable: false, - }, - ) - public spaceModel: SpaceModelEntity; } diff --git a/libs/common/src/modules/space/dtos/index.ts b/libs/common/src/modules/space/dtos/index.ts index fcc0fdd..b470336 100644 --- a/libs/common/src/modules/space/dtos/index.ts +++ b/libs/common/src/modules/space/dtos/index.ts @@ -1,2 +1,4 @@ export * from './space.dto'; export * from './subspace.dto'; +export * from './space-product-item.dto'; +export * from './space-product.dto'; diff --git a/libs/common/src/modules/space-model/dtos/space-product-item.dto.ts b/libs/common/src/modules/space/dtos/space-product-item.dto.ts similarity index 86% rename from libs/common/src/modules/space-model/dtos/space-product-item.dto.ts rename to libs/common/src/modules/space/dtos/space-product-item.dto.ts index fa68825..8973c1a 100644 --- a/libs/common/src/modules/space-model/dtos/space-product-item.dto.ts +++ b/libs/common/src/modules/space/dtos/space-product-item.dto.ts @@ -11,5 +11,5 @@ export class SpaceProductItemDto { @IsString() @IsNotEmpty() - spaceProductModelUuid: string; + spaceProductUuid: string; } diff --git a/libs/common/src/modules/space/dtos/space-product.dto.ts b/libs/common/src/modules/space/dtos/space-product.dto.ts new file mode 100644 index 0000000..a57d29e --- /dev/null +++ b/libs/common/src/modules/space/dtos/space-product.dto.ts @@ -0,0 +1,23 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsString, IsNotEmpty, IsNumber } 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/entities/index.ts b/libs/common/src/modules/space/entities/index.ts index f720cf4..d09bdac 100644 --- a/libs/common/src/modules/space/entities/index.ts +++ b/libs/common/src/modules/space/entities/index.ts @@ -1,3 +1,5 @@ export * from './space.entity'; export * from './subspace.entity'; +export * from './space-product-item.entity'; +export * from './space-product.entity'; export * from './space-link.entity'; diff --git a/libs/common/src/modules/space/entities/space-product-item.entity.ts b/libs/common/src/modules/space/entities/space-product-item.entity.ts new file mode 100644 index 0000000..d45cfa0 --- /dev/null +++ b/libs/common/src/modules/space/entities/space-product-item.entity.ts @@ -0,0 +1,17 @@ +import { Column, Entity, ManyToOne } from 'typeorm'; +import { SpaceProductEntity } from './space-product.entity'; +import { AbstractEntity } from '../../abstract/entities/abstract.entity'; +import { SpaceProductItemDto } from '../dtos'; + +@Entity({ name: 'space-product-item-model' }) +export class SpaceProductItemEntity extends AbstractEntity { + @Column({ + nullable: false, + }) + public tag: string; + + @ManyToOne(() => SpaceProductEntity, (spaceProduct) => spaceProduct.items, { + nullable: false, + }) + public spaceProducts: SpaceProductEntity; +} diff --git a/libs/common/src/modules/space/entities/space-product.entity.ts b/libs/common/src/modules/space/entities/space-product.entity.ts index 92ad411..244a860 100644 --- a/libs/common/src/modules/space/entities/space-product.entity.ts +++ b/libs/common/src/modules/space/entities/space-product.entity.ts @@ -1,7 +1,8 @@ -import { Column, Entity, ManyToOne, JoinColumn } from 'typeorm'; +import { Column, Entity, ManyToOne, JoinColumn, OneToMany } from 'typeorm'; import { SpaceEntity } from './space.entity'; import { AbstractEntity } from '../../abstract/entities/abstract.entity'; import { ProductEntity } from '../../product/entities'; +import { SpaceProductItemEntity } from './space-product-item.entity'; @Entity({ name: 'space-product' }) export class SpaceProductEntity extends AbstractEntity { @@ -25,6 +26,11 @@ export class SpaceProductEntity extends AbstractEntity { }) productCount: number; + @OneToMany(() => SpaceProductItemEntity, (item) => item.spaceProducts, { + cascade: true, + }) + public items: SpaceProductItemEntity[]; + constructor(partial: Partial) { super(); Object.assign(this, partial); 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 b2f856c..3ec0eb2 100644 --- a/src/space-model/services/space-product-item-model.service.ts +++ b/src/space-model/services/space-product-item-model.service.ts @@ -19,14 +19,13 @@ export class SpaceProductItemModelService { spaceModel: SpaceModelEntity, queryRunner: QueryRunner, ) { - await this.validateTags(itemModelDtos, spaceModel, queryRunner); + await this.validateTags(itemModelDtos, queryRunner, spaceModel); try { const productItems = itemModelDtos.map((dto) => queryRunner.manager.create(this.spaceProductItemRepository.target, { tag: dto.tag, spaceProductModel, - spaceModel, }), ); @@ -46,8 +45,8 @@ export class SpaceProductItemModelService { private async validateTags( itemModelDtos: CreateSpaceProductItemModelDto[], - spaceModel: SpaceModelEntity, queryRunner: QueryRunner, + spaceModel: SpaceModelEntity, ) { const incomingTags = itemModelDtos.map((item) => item.tag); @@ -64,7 +63,7 @@ export class SpaceProductItemModelService { const existingTags = await queryRunner.manager.find( this.spaceProductItemRepository.target, { - where: { spaceModel }, + where: { spaceProductModel: { spaceModel } }, select: ['tag'], }, ); From 6d8c9a0ec021e49fa1989ae3ea39717e1c1a3fa2 Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Wed, 11 Dec 2024 16:06:51 +0400 Subject: [PATCH 02/26] added the relation between models and space --- .../space-model/entities/space-model.entity.ts | 6 ++++++ .../space-model/entities/space-product-item.entity.ts | 10 +++++++++- .../entities/space-product-model.entity.ts | 10 ++++++++++ .../space-model/entities/subspace-model.entity.ts | 8 +++++++- .../space/entities/space-product-item.entity.ts | 11 +++++++++++ .../modules/space/entities/space-product.entity.ts | 11 +++++++++++ .../common/src/modules/space/entities/space.entity.ts | 5 +++++ .../src/modules/space/entities/subspace.entity.ts | 5 +++++ 8 files changed, 64 insertions(+), 2 deletions(-) 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 9937a01..b67c642 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 @@ -11,6 +11,7 @@ import { SpaceModelDto } from '../dtos'; import { SubspaceModelEntity } from './subspace-model.entity'; import { SpaceProductModelEntity } from './space-product-model.entity'; import { ProjectEntity } from '../../project/entities'; +import { SpaceEntity } from '../../space/entities'; @Entity({ name: 'space-model' }) @Unique(['modelName', 'project']) @@ -53,4 +54,9 @@ export class SpaceModelEntity extends AbstractEntity { }, ) public spaceProductModels: SpaceProductModelEntity[]; + + @OneToMany(() => SpaceEntity, (space) => space.spaceModel, { + cascade: true, + }) + public spaces: SpaceEntity[]; } 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 7d39974..062418f 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,7 +1,8 @@ -import { Entity, Column, ManyToOne } from 'typeorm'; +import { Entity, Column, ManyToOne, OneToMany } from 'typeorm'; import { AbstractEntity } from '../../abstract/entities/abstract.entity'; import { SpaceProductItemModelDto } from '../dtos'; import { SpaceProductModelEntity } from './space-product-model.entity'; +import { SpaceProductItemEntity } from '../../space/entities'; @Entity({ name: 'space-product-item-model' }) export class SpaceProductItemModelEntity extends AbstractEntity { @@ -18,4 +19,11 @@ export class SpaceProductItemModelEntity extends AbstractEntity SpaceProductItemEntity, + (spaceProductItem) => spaceProductItem.spaceProductItemModel, + { cascade: true }, + ) + public items: SpaceProductItemEntity[]; } 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 index d843d07..c48cf91 100644 --- 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 @@ -4,6 +4,7 @@ import { ProductEntity } from '../../product/entities'; import { SpaceModelEntity } from './space-model.entity'; import { SpaceProductItemModelEntity } from './space-product-item.entity'; import { SpaceProductModelDto } from '../dtos'; +import { SpaceProductEntity } from '../../space/entities'; @Entity({ name: 'space-product-model' }) export class SpaceProductModelEntity extends AbstractEntity { @@ -37,4 +38,13 @@ export class SpaceProductModelEntity extends AbstractEntity SpaceProductEntity, + (spaceProduct) => spaceProduct.spaceProductModel, + { + cascade: true, + }, + ) + public spaceProducts: SpaceProductEntity[]; } 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 776f831..c1e335a 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,7 +1,8 @@ -import { Column, Entity, ManyToOne, Unique } from 'typeorm'; +import { Column, Entity, ManyToOne, OneToMany, Unique } from 'typeorm'; import { AbstractEntity } from '../../abstract/entities/abstract.entity'; import { SpaceModelEntity } from './space-model.entity'; import { SubSpaceModelDto } from '../dtos'; +import { SubspaceEntity } from '../../space/entities'; @Entity({ name: 'subspace-model' }) @Unique(['subspaceName', 'spaceModel']) @@ -27,4 +28,9 @@ export class SubspaceModelEntity extends AbstractEntity { }, ) public spaceModel: SpaceModelEntity; + + @OneToMany(() => SubspaceEntity, (space) => space.subSpaceModel, { + cascade: true, + }) + public spaces: SubspaceEntity[]; } diff --git a/libs/common/src/modules/space/entities/space-product-item.entity.ts b/libs/common/src/modules/space/entities/space-product-item.entity.ts index d45cfa0..7c6260f 100644 --- a/libs/common/src/modules/space/entities/space-product-item.entity.ts +++ b/libs/common/src/modules/space/entities/space-product-item.entity.ts @@ -2,6 +2,7 @@ import { Column, Entity, ManyToOne } from 'typeorm'; import { SpaceProductEntity } from './space-product.entity'; import { AbstractEntity } from '../../abstract/entities/abstract.entity'; import { SpaceProductItemDto } from '../dtos'; +import { SpaceProductItemModelEntity } from '../../space-model'; @Entity({ name: 'space-product-item-model' }) export class SpaceProductItemEntity extends AbstractEntity { @@ -14,4 +15,14 @@ export class SpaceProductItemEntity extends AbstractEntity nullable: false, }) public spaceProducts: SpaceProductEntity; + + @ManyToOne( + () => SpaceProductItemModelEntity, + (spaceProductItemModel) => spaceProductItemModel.items, + { + nullable: true, + onDelete: 'SET NULL', + }, + ) + public spaceProductItemModel?: SpaceProductItemModelEntity; } diff --git a/libs/common/src/modules/space/entities/space-product.entity.ts b/libs/common/src/modules/space/entities/space-product.entity.ts index 244a860..8fa0c4f 100644 --- a/libs/common/src/modules/space/entities/space-product.entity.ts +++ b/libs/common/src/modules/space/entities/space-product.entity.ts @@ -3,6 +3,7 @@ import { SpaceEntity } from './space.entity'; import { AbstractEntity } from '../../abstract/entities/abstract.entity'; import { ProductEntity } from '../../product/entities'; import { SpaceProductItemEntity } from './space-product-item.entity'; +import { SpaceProductModelEntity } from '../../space-model'; @Entity({ name: 'space-product' }) export class SpaceProductEntity extends AbstractEntity { @@ -31,6 +32,16 @@ export class SpaceProductEntity extends AbstractEntity { }) public items: SpaceProductItemEntity[]; + @ManyToOne( + () => SpaceProductModelEntity, + (spaceProductModel) => spaceProductModel.spaceProducts, + { + nullable: true, + }, + ) + @JoinColumn({ name: 'space_product_model_uuid' }) + public spaceProductModel?: SpaceProductModelEntity; + constructor(partial: Partial) { super(); Object.assign(this, partial); diff --git a/libs/common/src/modules/space/entities/space.entity.ts b/libs/common/src/modules/space/entities/space.entity.ts index 103f0a2..9d6e6fd 100644 --- a/libs/common/src/modules/space/entities/space.entity.ts +++ b/libs/common/src/modules/space/entities/space.entity.ts @@ -15,6 +15,7 @@ import { SubspaceEntity } from './subspace.entity'; import { SpaceLinkEntity } from './space-link.entity'; import { SpaceProductEntity } from './space-product.entity'; import { SceneEntity } from '../../scene/entities'; +import { SpaceModelEntity } from '../../space-model'; @Entity({ name: 'space' }) @Unique(['invitationCode']) @@ -98,6 +99,10 @@ export class SpaceEntity extends AbstractEntity { @OneToMany(() => SceneEntity, (scene) => scene.space) scenes: SceneEntity[]; + @ManyToOne(() => SpaceModelEntity, { nullable: true }) + @JoinColumn({ name: 'space_model_uuid' }) + spaceModel?: SpaceModelEntity; + constructor(partial: Partial) { super(); Object.assign(this, partial); diff --git a/libs/common/src/modules/space/entities/subspace.entity.ts b/libs/common/src/modules/space/entities/subspace.entity.ts index ab38b00..ccb5118 100644 --- a/libs/common/src/modules/space/entities/subspace.entity.ts +++ b/libs/common/src/modules/space/entities/subspace.entity.ts @@ -3,6 +3,7 @@ import { AbstractEntity } from '../../abstract/entities/abstract.entity'; import { DeviceEntity } from '../../device/entities'; import { SpaceEntity } from './space.entity'; import { SubspaceDto } from '../dtos'; +import { SubspaceModelEntity } from '../../space-model'; @Entity({ name: 'subspace' }) export class SubspaceEntity extends AbstractEntity { @@ -30,6 +31,10 @@ export class SubspaceEntity extends AbstractEntity { }) devices: DeviceEntity[]; + @ManyToOne(() => SubspaceModelEntity, { nullable: true }) + @JoinColumn({ name: 'subspace_model_uuid' }) + subSpaceModel?: SubspaceModelEntity; + constructor(partial: Partial) { super(); Object.assign(this, partial); From bbd40253b84a784463c9192ee9c3978e2b1637c3 Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Wed, 11 Dec 2024 16:42:39 +0400 Subject: [PATCH 03/26] enities typo fix --- libs/common/src/modules/space-model/entities/index.ts | 2 +- ...roduct-item.entity.ts => space-product-item-model.entity.ts} | 0 .../modules/space-model/entities/space-product-model.entity.ts | 2 +- .../src/modules/space/entities/space-product-item.entity.ts | 2 +- 4 files changed, 3 insertions(+), 3 deletions(-) rename libs/common/src/modules/space-model/entities/{space-product-item.entity.ts => space-product-item-model.entity.ts} (100%) diff --git a/libs/common/src/modules/space-model/entities/index.ts b/libs/common/src/modules/space-model/entities/index.ts index 3bf3f69..f1e4016 100644 --- a/libs/common/src/modules/space-model/entities/index.ts +++ b/libs/common/src/modules/space-model/entities/index.ts @@ -1,4 +1,4 @@ export * from './space-model.entity'; -export * from './space-product-item.entity'; +export * from './space-product-item-model.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-product-item.entity.ts b/libs/common/src/modules/space-model/entities/space-product-item-model.entity.ts similarity index 100% rename from libs/common/src/modules/space-model/entities/space-product-item.entity.ts rename to libs/common/src/modules/space-model/entities/space-product-item-model.entity.ts 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 index c48cf91..13b2b33 100644 --- 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 @@ -2,7 +2,7 @@ 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 { SpaceProductItemModelEntity } from './space-product-item-model.entity'; import { SpaceProductModelDto } from '../dtos'; import { SpaceProductEntity } from '../../space/entities'; diff --git a/libs/common/src/modules/space/entities/space-product-item.entity.ts b/libs/common/src/modules/space/entities/space-product-item.entity.ts index 7c6260f..70e7c1f 100644 --- a/libs/common/src/modules/space/entities/space-product-item.entity.ts +++ b/libs/common/src/modules/space/entities/space-product-item.entity.ts @@ -4,7 +4,7 @@ import { AbstractEntity } from '../../abstract/entities/abstract.entity'; import { SpaceProductItemDto } from '../dtos'; import { SpaceProductItemModelEntity } from '../../space-model'; -@Entity({ name: 'space-product-item-model' }) +@Entity({ name: 'space-product-item' }) export class SpaceProductItemEntity extends AbstractEntity { @Column({ nullable: false, From a2ee7a000106b1d52fa578604bca4b4ceaa594c5 Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Wed, 11 Dec 2024 16:45:09 +0400 Subject: [PATCH 04/26] added space model details to space --- .../dtos/create-space-product-model.dto.ts | 2 +- .../space-product-item-model.service.ts | 6 ++ .../services/space-product-model.service.ts | 15 ++-- src/space/dtos/add.space.dto.ts | 69 ++++++++++++++++--- 4 files changed, 73 insertions(+), 19 deletions(-) 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 11d8bb5..9f251be 100644 --- a/src/space-model/dtos/create-space-product-model.dto.ts +++ b/src/space-model/dtos/create-space-product-model.dto.ts @@ -17,7 +17,7 @@ export class CreateSpaceProductModelDto { }) @IsNotEmpty() @IsString() - productId: string; + productUuid: string; @ApiProperty({ description: 'Number of products in the model', 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 3ec0eb2..ac9c0d8 100644 --- a/src/space-model/services/space-product-item-model.service.ts +++ b/src/space-model/services/space-product-item-model.service.ts @@ -21,6 +21,12 @@ export class SpaceProductItemModelService { ) { await this.validateTags(itemModelDtos, queryRunner, spaceModel); + if (!spaceProductModel) { + throw new HttpException( + 'Space product model is required to create product items.', + HttpStatus.BAD_REQUEST, + ); + } try { const productItems = itemModelDtos.map((dto) => queryRunner.manager.create(this.spaceProductItemRepository.target, { diff --git a/src/space-model/services/space-product-model.service.ts b/src/space-model/services/space-product-model.service.ts index aa08a16..27bad29 100644 --- a/src/space-model/services/space-product-model.service.ts +++ b/src/space-model/services/space-product-model.service.ts @@ -25,7 +25,7 @@ export class SpaceProductModelService { const productModels = await Promise.all( spaceProductModelDtos.map(async (dto) => { this.validateProductCount(dto); - const product = await this.getProduct(dto.productId); + const product = await this.getProduct(dto.productUuid); return queryRunner.manager.create( this.spaceProductModelRepository.target, { @@ -40,14 +40,15 @@ export class SpaceProductModelService { const savedProductModels = await queryRunner.manager.save(productModels); await Promise.all( - spaceProductModelDtos.map((dto, index) => - this.spaceProductItemModelService.createProdutItemModel( + spaceProductModelDtos.map((dto, index) => { + const savedModel = savedProductModels[index]; + return this.spaceProductItemModelService.createProdutItemModel( dto.items, - savedProductModels[index], + savedModel, // Pass the saved model spaceModel, queryRunner, - ), - ), + ); + }), ); } catch (error) { if (error instanceof HttpException) { @@ -65,7 +66,7 @@ export class SpaceProductModelService { const productItemCount = dto.items.length; if (dto.productCount !== productItemCount) { throw new HttpException( - `Product count (${dto.productCount}) does not match the number of items (${productItemCount}) for product ID ${dto.productId}.`, + `Product count (${dto.productCount}) does not match the number of items (${productItemCount}) for product ID ${dto.productUuid}.`, HttpStatus.BAD_REQUEST, ); } diff --git a/src/space/dtos/add.space.dto.ts b/src/space/dtos/add.space.dto.ts index 554f17b..fe88c33 100644 --- a/src/space/dtos/add.space.dto.ts +++ b/src/space/dtos/add.space.dto.ts @@ -1,6 +1,7 @@ import { ApiProperty } from '@nestjs/swagger'; import { Type } from 'class-transformer'; import { + ArrayNotEmpty, IsArray, IsBoolean, IsNotEmpty, @@ -11,6 +12,42 @@ import { ValidateNested, } from 'class-validator'; +export class CreateSpaceProductItemDto { + @ApiProperty({ + description: 'Specific name for the product item', + example: 'Light 1', + }) + @IsNotEmpty() + @IsString() + tag: string; +} + +class ProductAssignmentDto { + @ApiProperty({ + description: 'UUID of the product to be assigned', + example: 'prod-uuid-1234', + }) + @IsNotEmpty() + productId: string; + + @ApiProperty({ + description: 'Number of items to assign for the product', + example: 3, + }) + count: number; + + @ApiProperty({ + description: 'Specific names for each product item', + type: [CreateSpaceProductItemDto], + example: [{ tag: 'Light 1' }, { tag: 'Light 2' }, { tag: 'Light 3' }], + }) + @IsArray() + @ArrayNotEmpty() + @ValidateNested({ each: true }) + @Type(() => CreateSpaceProductItemDto) + items: CreateSpaceProductItemDto[]; +} + export class AddSpaceDto { @ApiProperty({ description: 'Name of the space (e.g., Floor 1, Unit 101)', @@ -29,9 +66,14 @@ export class AddSpaceDto { @IsOptional() parentUuid?: string; + @ApiProperty({ + description: 'Icon identifier for the space', + example: 'assets/location', + required: false, + }) @IsString() @IsOptional() - public icon: string; + public icon?: string; @ApiProperty({ description: 'Indicates whether the space is private or public', @@ -49,16 +91,29 @@ export class AddSpaceDto { @IsNumber() y: number; + @ApiProperty({ + description: 'UUID of the Space', + example: 'd290f1ee-6c54-4b01-90e6-d701748f0851', + }) + @IsString() + @IsOptional() + spaceModelUuid?: string; + @ApiProperty({ description: 'Y position on canvas', example: 200 }) @IsString() @IsOptional() - direction: string; + direction?: string; + @ApiProperty({ + description: 'List of products assigned to this space', + type: [ProductAssignmentDto], + required: false, + }) @IsArray() @ValidateNested({ each: true }) @IsOptional() @Type(() => ProductAssignmentDto) - products: ProductAssignmentDto[]; + products?: ProductAssignmentDto[]; } export class AddUserSpaceDto { @@ -101,11 +156,3 @@ export class AddUserSpaceUsingCodeDto { Object.assign(this, dto); } } - -class ProductAssignmentDto { - @IsNotEmpty() - productId: string; - - @IsNotEmpty() - count: number; -} From 8af29cddfd4d8651d2fc54d937e8fbcc6c4f2417 Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Wed, 11 Dec 2024 17:01:23 +0400 Subject: [PATCH 05/26] add validation to check both space model and products, subspaces are not created --- src/space/dtos/add.space.dto.ts | 22 +++++++++++++++++++++- src/space/services/space.service.ts | 20 +++++++++++++++++++- 2 files changed, 40 insertions(+), 2 deletions(-) diff --git a/src/space/dtos/add.space.dto.ts b/src/space/dtos/add.space.dto.ts index fe88c33..d66fa87 100644 --- a/src/space/dtos/add.space.dto.ts +++ b/src/space/dtos/add.space.dto.ts @@ -22,7 +22,17 @@ export class CreateSpaceProductItemDto { tag: string; } -class ProductAssignmentDto { +export class CreateSubspaceDto { + @ApiProperty({ + description: 'Name of the subspace', + example: 'Living Room', + }) + @IsNotEmpty() + @IsString() + subspaceName: string; +} + +export class ProductAssignmentDto { @ApiProperty({ description: 'UUID of the product to be assigned', example: 'prod-uuid-1234', @@ -114,6 +124,16 @@ export class AddSpaceDto { @IsOptional() @Type(() => ProductAssignmentDto) products?: ProductAssignmentDto[]; + + @ApiProperty({ + description: 'List of subspaces included in the model', + type: [CreateSubspaceDto], + }) + @IsOptional() + @IsArray() + @ValidateNested({ each: true }) + @Type(() => CreateSubspaceDto) + subspaces?: CreateSubspaceDto[]; } export class AddUserSpaceDto { diff --git a/src/space/services/space.service.ts b/src/space/services/space.service.ts index 8189f8c..6fc35eb 100644 --- a/src/space/services/space.service.ts +++ b/src/space/services/space.service.ts @@ -9,6 +9,7 @@ import { AddSpaceDto, CommunitySpaceParam, GetSpaceParam, + ProductAssignmentDto, UpdateSpaceDto, } from '../dtos'; import { SuccessResponseDto } from '@app/common/dto/success.response.dto'; @@ -19,6 +20,7 @@ import { generateRandomString } from '@app/common/helper/randomString'; import { SpaceLinkService } from './space-link'; import { SpaceProductService } from './space-products'; import { ProjectRepository } from '@app/common/modules/project/repositiories'; +import { CreateSubspaceModelDto } from 'src/space-model/dtos'; @Injectable() export class SpaceService { @@ -34,11 +36,14 @@ export class SpaceService { addSpaceDto: AddSpaceDto, params: CommunitySpaceParam, ): Promise { - const { parentUuid, direction, products } = addSpaceDto; + const { parentUuid, direction, products, spaceModelUuid, subspaces } = + addSpaceDto; const { communityUuid, projectUuid } = params; await this.validateProject(projectUuid); + this.validateSpaceCreation(spaceModelUuid, products, subspaces); + const community = await this.validateCommunity(communityUuid); const parent = parentUuid ? await this.validateSpace(parentUuid) : null; @@ -344,4 +349,17 @@ export class SpaceService { HttpStatus.NOT_FOUND, ); } + + private validateSpaceCreation( + spaceModelUuid?: string, + products?: ProductAssignmentDto[], + subSpaces?: CreateSubspaceModelDto[], + ) { + if (spaceModelUuid && (products?.length || subSpaces?.length)) { + throw new HttpException( + 'Space model cannot be assigned with products or subspaces.', + HttpStatus.CONFLICT, + ); + } + } } From ba002ae474c8ce7e9ba9ad4ff03d05d4141a1dc0 Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Thu, 12 Dec 2024 09:21:56 +0400 Subject: [PATCH 06/26] reduced code duplication --- src/project/project.module.ts | 3 +- src/space/dtos/add.space.dto.ts | 17 +- .../services/space-validation.service.ts | 74 ++++++++ src/space/services/space.service.ts | 163 +++++++++--------- .../services/subspace/subspace.service.ts | 89 ++++++++-- src/space/space.module.ts | 8 +- 6 files changed, 244 insertions(+), 110 deletions(-) create mode 100644 src/space/services/space-validation.service.ts diff --git a/src/project/project.module.ts b/src/project/project.module.ts index 9d82ba9..3cd906e 100644 --- a/src/project/project.module.ts +++ b/src/project/project.module.ts @@ -1,8 +1,9 @@ -import { Module } from '@nestjs/common'; +import { Global, Module } from '@nestjs/common'; import { ProjectController } from './controllers'; import { ProjectService } from './services'; import { ProjectRepository } from '@app/common/modules/project/repositiories'; +@Global() @Module({ imports: [], controllers: [ProjectController], diff --git a/src/space/dtos/add.space.dto.ts b/src/space/dtos/add.space.dto.ts index d66fa87..402d37a 100644 --- a/src/space/dtos/add.space.dto.ts +++ b/src/space/dtos/add.space.dto.ts @@ -11,6 +11,7 @@ import { IsUUID, ValidateNested, } from 'class-validator'; +import { AddSubspaceDto } from './subspace'; export class CreateSpaceProductItemDto { @ApiProperty({ @@ -22,16 +23,6 @@ export class CreateSpaceProductItemDto { tag: string; } -export class CreateSubspaceDto { - @ApiProperty({ - description: 'Name of the subspace', - example: 'Living Room', - }) - @IsNotEmpty() - @IsString() - subspaceName: string; -} - export class ProductAssignmentDto { @ApiProperty({ description: 'UUID of the product to be assigned', @@ -127,13 +118,13 @@ export class AddSpaceDto { @ApiProperty({ description: 'List of subspaces included in the model', - type: [CreateSubspaceDto], + type: [AddSubspaceDto], }) @IsOptional() @IsArray() @ValidateNested({ each: true }) - @Type(() => CreateSubspaceDto) - subspaces?: CreateSubspaceDto[]; + @Type(() => AddSubspaceDto) + subspaces?: AddSubspaceDto[]; } export class AddUserSpaceDto { diff --git a/src/space/services/space-validation.service.ts b/src/space/services/space-validation.service.ts new file mode 100644 index 0000000..56e6532 --- /dev/null +++ b/src/space/services/space-validation.service.ts @@ -0,0 +1,74 @@ +import { CommunityEntity } from '@app/common/modules/community/entities'; +import { SpaceEntity } from '@app/common/modules/space/entities'; +import { SpaceRepository } from '@app/common/modules/space/repositories'; +import { HttpException, HttpStatus, Injectable } from '@nestjs/common'; +import { CommunityService } from '../../community/services'; +import { ProjectService } from '../../project/services'; +import { + SpaceModelEntity, + SpaceModelRepository, +} from '@app/common/modules/space-model'; + +@Injectable() +export class ValidationService { + constructor( + private readonly projectService: ProjectService, + private readonly communityService: CommunityService, + private readonly spaceRepository: SpaceRepository, + private readonly spaceModelRepository: SpaceModelRepository, + ) {} + + async validateCommunityAndProject( + communityUuid: string, + projectUuid: string, + ): Promise { + await this.projectService.findOne(projectUuid); + const community = await this.communityService.getCommunityById({ + communityUuid, + projectUuid, + }); + + return community.data; + } + + async validateSpaceWithinCommunityAndProject( + communityUuid: string, + projectUuid: string, + spaceUuid?: string, + ): Promise { + await this.validateCommunityAndProject(communityUuid, projectUuid); + const space = await this.validateSpace(spaceUuid); + return space; + } + + async validateSpace(spaceUuid: string): Promise { + const space = await this.spaceRepository.findOne({ + where: { uuid: spaceUuid }, + }); + + if (!space) { + throw new HttpException( + `Space with UUID ${spaceUuid} not found`, + HttpStatus.NOT_FOUND, + ); + } + + return space; + } + + async validateSpaceModel(spaceModelUuid: string): Promise { + const spaceModel = await this.spaceModelRepository.findOne({ + where: { uuid: spaceModelUuid }, + relations: ['subspaceModels'], + }); + + if (!spaceModel) { + throw new HttpException( + `Space model with UUID ${spaceModelUuid} not found`, + HttpStatus.NOT_FOUND, + ); + } + + return spaceModel; + } +} diff --git a/src/space/services/space.service.ts b/src/space/services/space.service.ts index 6fc35eb..135f2e5 100644 --- a/src/space/services/space.service.ts +++ b/src/space/services/space.service.ts @@ -14,22 +14,24 @@ import { } 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'; import { SpaceEntity } from '@app/common/modules/space/entities'; import { generateRandomString } from '@app/common/helper/randomString'; import { SpaceLinkService } from './space-link'; import { SpaceProductService } from './space-products'; -import { ProjectRepository } from '@app/common/modules/project/repositiories'; import { CreateSubspaceModelDto } from 'src/space-model/dtos'; +import { SubSpaceService } from './subspace'; +import { DataSource } from 'typeorm'; +import { ValidationService } from './space-validation.service'; @Injectable() export class SpaceService { constructor( + private readonly dataSource: DataSource, private readonly spaceRepository: SpaceRepository, - private readonly communityRepository: CommunityRepository, private readonly spaceLinkService: SpaceLinkService, private readonly spaceProductService: SpaceProductService, - private readonly projectRepository: ProjectRepository, + private readonly subSpaceService: SubSpaceService, + private readonly validationService: ValidationService, ) {} async createSpace( @@ -40,21 +42,35 @@ export class SpaceService { addSpaceDto; const { communityUuid, projectUuid } = params; - await this.validateProject(projectUuid); + const queryRunner = this.dataSource.createQueryRunner(); + + await queryRunner.connect(); + await queryRunner.startTransaction(); + + const community = await this.validationService.validateCommunityAndProject( + communityUuid, + projectUuid, + ); this.validateSpaceCreation(spaceModelUuid, products, subspaces); - const community = await this.validateCommunity(communityUuid); + const parent = parentUuid + ? await this.validationService.validateSpace(parentUuid) + : null; + + const spaceModel = spaceModelUuid + ? await this.validationService.validateSpaceModel(spaceModelUuid) + : null; - const parent = parentUuid ? await this.validateSpace(parentUuid) : null; try { const newSpace = this.spaceRepository.create({ ...addSpaceDto, - community, + spaceModel, parent: parentUuid ? parent : null, + community, }); - await this.spaceRepository.save(newSpace); + await queryRunner.manager.save(newSpace); if (direction && parent) { await this.spaceLinkService.saveSpaceLink( @@ -64,12 +80,27 @@ export class SpaceService { ); } + if (subspaces) { + await this.subSpaceService.createSubspacesFromNames( + subspaces, + newSpace, + queryRunner, + ); + } else { + await this.subSpaceService.createSubSpaceFromModel( + spaceModel, + newSpace, + queryRunner, + ); + } + if (products && products.length > 0) { await this.spaceProductService.assignProductsToSpace( newSpace, products, ); } + await queryRunner.commitTransaction(); return new SuccessResponseDto({ statusCode: HttpStatus.CREATED, @@ -77,7 +108,14 @@ export class SpaceService { message: 'Space created successfully', }); } catch (error) { + await queryRunner.rollbackTransaction(); + + if (error instanceof HttpException) { + throw error; + } throw new HttpException(error.message, HttpStatus.INTERNAL_SERVER_ERROR); + } finally { + await queryRunner.release(); } } @@ -85,8 +123,10 @@ export class SpaceService { params: CommunitySpaceParam, ): Promise { const { communityUuid, projectUuid } = params; - await this.validateCommunity(communityUuid); - await this.validateProject(projectUuid); + await this.validationService.validateCommunityAndProject( + communityUuid, + projectUuid, + ); try { // Get all spaces related to the community, including the parent-child relations const spaces = await this.spaceRepository.find({ @@ -119,11 +159,12 @@ export class SpaceService { async findOne(params: GetSpaceParam): Promise { const { communityUuid, spaceUuid, projectUuid } = params; try { - const space = await this.validateCommunityAndSpace( - communityUuid, - spaceUuid, - projectUuid, - ); + const space = + await this.validationService.validateSpaceWithinCommunityAndProject( + communityUuid, + projectUuid, + spaceUuid, + ); return new SuccessResponseDto({ message: `Space with ID ${spaceUuid} successfully fetched`, @@ -144,12 +185,13 @@ export class SpaceService { async delete(params: GetSpaceParam): Promise { try { const { communityUuid, spaceUuid, projectUuid } = params; - // First, check if the community exists - const space = await this.validateCommunityAndSpace( - communityUuid, - spaceUuid, - projectUuid, - ); + + const space = + await this.validationService.validateSpaceWithinCommunityAndProject( + communityUuid, + projectUuid, + spaceUuid, + ); // Delete the space await this.spaceRepository.remove(space); @@ -175,15 +217,18 @@ export class SpaceService { ): Promise { const { communityUuid, spaceUuid, projectUuid } = params; try { - const space = await this.validateCommunityAndSpace( - communityUuid, - spaceUuid, - projectUuid, - ); + const space = + await this.validationService.validateSpaceWithinCommunityAndProject( + communityUuid, + projectUuid, + spaceUuid, + ); // If a parentId is provided, check if the parent exists const { parentUuid, products } = updateSpaceDto; - const parent = parentUuid ? await this.validateSpace(parentUuid) : null; + const parent = parentUuid + ? await this.validationService.validateSpace(parentUuid) + : null; // Update other space properties from updateSpaceDto Object.assign(space, updateSpaceDto, { parent }); @@ -218,7 +263,11 @@ export class SpaceService { params: GetSpaceParam, ): Promise { const { spaceUuid, communityUuid, projectUuid } = params; - await this.validateCommunityAndSpace(communityUuid, spaceUuid, projectUuid); + await this.validationService.validateSpaceWithinCommunityAndProject( + communityUuid, + projectUuid, + spaceUuid, + ); try { // Get all spaces that are children of the provided space, including the parent-child relations @@ -248,11 +297,12 @@ export class SpaceService { try { const invitationCode = generateRandomString(6); - const space = await this.validateCommunityAndSpace( - communityUuid, - spaceUuid, - projectUuid, - ); + const space = + await this.validationService.validateSpaceWithinCommunityAndProject( + communityUuid, + projectUuid, + spaceUuid, + ); space.invitationCode = invitationCode; await this.spaceRepository.save(space); @@ -298,51 +348,6 @@ export class SpaceService { return rootSpaces; } - private async validateCommunity(communityId: string) { - const community = await this.communityRepository.findOne({ - where: { uuid: communityId }, - }); - if (!community) { - throw new HttpException( - `Community with ID ${communityId} not found`, - HttpStatus.NOT_FOUND, - ); - } - return community; - } - - async validateCommunityAndSpace( - communityUuid: string, - spaceUuid: string, - projectUuid: string, - ) { - await this.validateProject(projectUuid); - - const community = await this.validateCommunity(communityUuid); - if (!community) { - this.throwNotFound('Community', communityUuid); - } - - const space = await this.validateSpace(spaceUuid); - return space; - } - - private async validateSpace(spaceUuid: string) { - const space = await this.spaceRepository.findOne({ - where: { uuid: spaceUuid }, - }); - if (!space) this.throwNotFound('Space', spaceUuid); - return space; - } - - private async validateProject(uuid: string) { - const project = await this.projectRepository.findOne({ - where: { uuid }, - }); - - if (!project) this.throwNotFound('Project', uuid); - } - private throwNotFound(entity: string, uuid: string) { throw new HttpException( `${entity} with ID ${uuid} not found`, diff --git a/src/space/services/subspace/subspace.service.ts b/src/space/services/subspace/subspace.service.ts index c343512..dae280b 100644 --- a/src/space/services/subspace/subspace.service.ts +++ b/src/space/services/subspace/subspace.service.ts @@ -9,25 +9,83 @@ import { } from '@app/common/models/typeOrmCustom.model'; import { PageResponse } from '@app/common/dto/pagination.response.dto'; import { SubspaceDto } from '@app/common/modules/space/dtos'; -import { SpaceService } from '../space.service'; +import { QueryRunner } from 'typeorm'; +import { + SpaceEntity, + SubspaceEntity, +} from '@app/common/modules/space/entities'; +import { SpaceModelEntity } from '@app/common/modules/space-model'; +import { ValidationService } from '../space-validation.service'; @Injectable() export class SubSpaceService { constructor( private readonly subspaceRepository: SubspaceRepository, - private readonly spaceService: SpaceService, + private readonly validationService: ValidationService, ) {} + async createSubspaces( + subspaceData: Array<{ subspaceName: string; space: SpaceEntity }>, + queryRunner: QueryRunner, + ): Promise { + try { + const subspaces = subspaceData.map((data) => + queryRunner.manager.create(this.subspaceRepository.target, data), + ); + + return await queryRunner.manager.save(subspaces); + } catch (error) { + throw new HttpException( + 'An unexpected error occurred while creating subspaces.', + HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + } + + async createSubSpaceFromModel( + spaceModel: SpaceModelEntity, + space: SpaceEntity, + queryRunner: QueryRunner, + ): Promise { + const subSpaces = spaceModel.subspaceModels; + + if (!subSpaces || subSpaces.length === 0) { + return; + } + + const subspaceData = subSpaces.map((subSpaceModel) => ({ + subspaceName: subSpaceModel.subspaceName, + space, + subSpaceModel, + })); + + await this.createSubspaces(subspaceData, queryRunner); + } + + async createSubspacesFromNames( + addSubspaceDtos: AddSubspaceDto[], + space: SpaceEntity, + queryRunner: QueryRunner, + ): Promise { + const subspaceData = addSubspaceDtos.map((dto) => ({ + subspaceName: dto.subspaceName, + space, + })); + + return await this.createSubspaces(subspaceData, queryRunner); + } + async createSubspace( addSubspaceDto: AddSubspaceDto, params: GetSpaceParam, ): Promise { const { communityUuid, spaceUuid, projectUuid } = params; - const space = await this.spaceService.validateCommunityAndSpace( - communityUuid, - spaceUuid, - projectUuid, - ); + const space = + await this.validationService.validateSpaceWithinCommunityAndProject( + communityUuid, + projectUuid, + spaceUuid, + ); try { const newSubspace = this.subspaceRepository.create({ @@ -52,10 +110,10 @@ export class SubSpaceService { pageable: Partial, ): Promise { const { communityUuid, spaceUuid, projectUuid } = params; - await this.spaceService.validateCommunityAndSpace( + await this.validationService.validateSpaceWithinCommunityAndProject( communityUuid, - spaceUuid, projectUuid, + spaceUuid, ); try { @@ -76,10 +134,10 @@ export class SubSpaceService { async findOne(params: GetSubSpaceParam): Promise { const { communityUuid, subSpaceUuid, spaceUuid, projectUuid } = params; - await this.spaceService.validateCommunityAndSpace( + await this.validationService.validateSpaceWithinCommunityAndProject( communityUuid, - spaceUuid, projectUuid, + spaceUuid, ); try { const subSpace = await this.subspaceRepository.findOne({ @@ -116,12 +174,11 @@ export class SubSpaceService { updateSubSpaceDto: AddSubspaceDto, ): Promise { const { spaceUuid, communityUuid, subSpaceUuid, projectUuid } = params; - await this.spaceService.validateCommunityAndSpace( + await this.validationService.validateSpaceWithinCommunityAndProject( communityUuid, - spaceUuid, projectUuid, + spaceUuid, ); - const subSpace = await this.subspaceRepository.findOne({ where: { uuid: subSpaceUuid }, }); @@ -156,10 +213,10 @@ export class SubSpaceService { async delete(params: GetSubSpaceParam): Promise { const { spaceUuid, communityUuid, subSpaceUuid, projectUuid } = params; - await this.spaceService.validateCommunityAndSpace( + await this.validationService.validateSpaceWithinCommunityAndProject( communityUuid, - spaceUuid, projectUuid, + spaceUuid, ); const subspace = await this.subspaceRepository.findOne({ diff --git a/src/space/space.module.ts b/src/space/space.module.ts index 002c2df..3a52f7b 100644 --- a/src/space/space.module.ts +++ b/src/space/space.module.ts @@ -43,9 +43,12 @@ import { DeviceStatusFirebaseService } from '@app/common/firebase/devices-status import { DeviceStatusLogRepository } from '@app/common/modules/device-status-log/repositories'; import { SceneDeviceRepository } from '@app/common/modules/scene-device/repositories'; import { ProjectRepository } from '@app/common/modules/project/repositiories'; +import { SpaceModelRepository } from '@app/common/modules/space-model'; +import { CommunityModule } from 'src/community/community.module'; +import { ValidationService } from './services/space-validation.service'; @Module({ - imports: [ConfigModule, SpaceRepositoryModule], + imports: [ConfigModule, SpaceRepositoryModule, CommunityModule], controllers: [ SpaceController, SpaceUserController, @@ -55,6 +58,7 @@ import { ProjectRepository } from '@app/common/modules/project/repositiories'; SpaceSceneController, ], providers: [ + ValidationService, SpaceService, TuyaService, ProductRepository, @@ -81,6 +85,8 @@ import { ProjectRepository } from '@app/common/modules/project/repositiories'; SpaceProductService, SpaceProductRepository, ProjectRepository, + SpaceModelRepository, + SubspaceRepository, ], exports: [SpaceService], }) From 72bebe3b06727581a41c6c46a6f308f9adf4234e Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Thu, 12 Dec 2024 09:29:25 +0400 Subject: [PATCH 07/26] reduced duplication --- src/space/services/space-device.service.ts | 22 +++++++---------- src/space/services/space-scene.service.ts | 8 +++---- src/space/services/space-user.service.ts | 21 ++++++++-------- .../subspace/subspace-device.service.ts | 24 +++++++------------ 4 files changed, 32 insertions(+), 43 deletions(-) diff --git a/src/space/services/space-device.service.ts b/src/space/services/space-device.service.ts index 0a262a1..f905814 100644 --- a/src/space/services/space-device.service.ts +++ b/src/space/services/space-device.service.ts @@ -1,33 +1,29 @@ import { TuyaService } from '@app/common/integrations/tuya/services/tuya.service'; -import { CommunityRepository } from '@app/common/modules/community/repositories'; -import { SpaceRepository } from '@app/common/modules/space/repositories'; import { HttpException, HttpStatus, Injectable } from '@nestjs/common'; import { GetDeviceDetailsInterface } from 'src/device/interfaces/get.device.interface'; import { GetSpaceParam } from '../dtos'; import { BaseResponseDto } from '@app/common/dto/base.response.dto'; import { SuccessResponseDto } from '@app/common/dto/success.response.dto'; import { convertKeysToCamelCase } from '@app/common/helper/camelCaseConverter'; -import { ProductRepository } from '@app/common/modules/product/repositories'; -import { SpaceService } from './space.service'; + +import { ValidationService } from './space-validation.service'; @Injectable() export class SpaceDeviceService { constructor( - private readonly spaceRepository: SpaceRepository, private readonly tuyaService: TuyaService, - private readonly productRepository: ProductRepository, - private readonly communityRepository: CommunityRepository, - private readonly spaceService: SpaceService, + private readonly validationService: ValidationService, ) {} async listDevicesInSpace(params: GetSpaceParam): Promise { const { spaceUuid, communityUuid, projectUuid } = params; try { - const space = await this.spaceService.validateCommunityAndSpace( - communityUuid, - spaceUuid, - projectUuid, - ); + const space = + await this.validationService.validateSpaceWithinCommunityAndProject( + communityUuid, + projectUuid, + spaceUuid, + ); const safeFetch = async (device: any) => { try { diff --git a/src/space/services/space-scene.service.ts b/src/space/services/space-scene.service.ts index 57d63a5..4e77158 100644 --- a/src/space/services/space-scene.service.ts +++ b/src/space/services/space-scene.service.ts @@ -1,16 +1,16 @@ import { HttpException, HttpStatus, Injectable } from '@nestjs/common'; import { GetSpaceParam } from '../dtos'; import { BaseResponseDto } from '@app/common/dto/base.response.dto'; -import { SpaceService } from './space.service'; import { SceneService } from '../../scene/services'; import { SuccessResponseDto } from '@app/common/dto/success.response.dto'; import { GetSceneDto } from '../../scene/dtos'; +import { ValidationService } from './space-validation.service'; @Injectable() export class SpaceSceneService { constructor( - private readonly spaceSevice: SpaceService, private readonly sceneSevice: SceneService, + private readonly validationService: ValidationService, ) {} async getScenes( @@ -20,10 +20,10 @@ export class SpaceSceneService { try { const { spaceUuid, communityUuid, projectUuid } = params; - await this.spaceSevice.validateCommunityAndSpace( + await this.validationService.validateSpaceWithinCommunityAndProject( communityUuid, - spaceUuid, projectUuid, + spaceUuid, ); const scenes = await this.sceneSevice.findScenesBySpace( diff --git a/src/space/services/space-user.service.ts b/src/space/services/space-user.service.ts index 0e10e1a..0912132 100644 --- a/src/space/services/space-user.service.ts +++ b/src/space/services/space-user.service.ts @@ -1,21 +1,19 @@ import { BaseResponseDto } from '@app/common/dto/base.response.dto'; import { SuccessResponseDto } from '@app/common/dto/success.response.dto'; -import { SpaceRepository } from '@app/common/modules/space/repositories'; import { UserRepository, UserSpaceRepository, } from '@app/common/modules/user/repositories'; import { HttpException, HttpStatus, Injectable } from '@nestjs/common'; -import { SpaceService } from './space.service'; import { UserSpaceParam } from '../dtos'; +import { ValidationService } from './space-validation.service'; @Injectable() export class SpaceUserService { constructor( - private readonly spaceRepository: SpaceRepository, + private readonly validationService: ValidationService, private readonly userRepository: UserRepository, private readonly userSpaceRepository: UserSpaceRepository, - private readonly spaceService: SpaceService, ) {} async associateUserToSpace(params: UserSpaceParam): Promise { const { communityUuid, spaceUuid, userUuid, projectUuid } = params; @@ -31,11 +29,12 @@ export class SpaceUserService { } // Find the space by ID - const space = await this.spaceService.validateCommunityAndSpace( - communityUuid, - spaceUuid, - projectUuid, - ); + const space = + await this.validationService.validateSpaceWithinCommunityAndProject( + communityUuid, + projectUuid, + spaceUuid, + ); // Check if the association already exists const existingAssociation = await this.userSpaceRepository.findOne({ @@ -73,10 +72,10 @@ export class SpaceUserService { } // Find the space by ID - await this.spaceService.validateCommunityAndSpace( + await this.validationService.validateSpaceWithinCommunityAndProject( communityUuid, - spaceUuid, projectUuid, + spaceUuid, ); // Find the existing association diff --git a/src/space/services/subspace/subspace-device.service.ts b/src/space/services/subspace/subspace-device.service.ts index e773ea4..2bcfb93 100644 --- a/src/space/services/subspace/subspace-device.service.ts +++ b/src/space/services/subspace/subspace-device.service.ts @@ -1,29 +1,23 @@ import { BaseResponseDto } from '@app/common/dto/base.response.dto'; import { SuccessResponseDto } from '@app/common/dto/success.response.dto'; import { DeviceRepository } from '@app/common/modules/device/repositories'; -import { - SpaceRepository, - SubspaceRepository, -} from '@app/common/modules/space/repositories'; +import { SubspaceRepository } from '@app/common/modules/space/repositories'; import { HttpException, HttpStatus, Injectable } from '@nestjs/common'; import { DeviceSubSpaceParam, GetSubSpaceParam } from '../../dtos'; -import { CommunityRepository } from '@app/common/modules/community/repositories'; import { convertKeysToCamelCase } from '@app/common/helper/camelCaseConverter'; import { TuyaService } from '@app/common/integrations/tuya/services/tuya.service'; import { ProductRepository } from '@app/common/modules/product/repositories'; import { GetDeviceDetailsInterface } from '../../../device/interfaces/get.device.interface'; -import { SpaceService } from '../space.service'; +import { ValidationService } from '../space-validation.service'; @Injectable() export class SubspaceDeviceService { constructor( - private readonly spaceRepository: SpaceRepository, - private readonly communityRepository: CommunityRepository, private readonly subspaceRepository: SubspaceRepository, private readonly deviceRepository: DeviceRepository, private readonly tuyaService: TuyaService, private readonly productRepository: ProductRepository, - private readonly spaceService: SpaceService, + private readonly validationService: ValidationService, ) {} async listDevicesInSubspace( @@ -31,10 +25,10 @@ export class SubspaceDeviceService { ): Promise { const { subSpaceUuid, spaceUuid, communityUuid, projectUuid } = params; - await this.spaceService.validateCommunityAndSpace( + await this.validationService.validateSpaceWithinCommunityAndProject( communityUuid, - spaceUuid, projectUuid, + spaceUuid, ); const subspace = await this.findSubspaceWithDevices(subSpaceUuid); @@ -76,10 +70,10 @@ export class SubspaceDeviceService { const { subSpaceUuid, deviceUuid, spaceUuid, communityUuid, projectUuid } = params; try { - await this.spaceService.validateCommunityAndSpace( + await this.validationService.validateSpaceWithinCommunityAndProject( communityUuid, - spaceUuid, projectUuid, + spaceUuid, ); const subspace = await this.findSubspace(subSpaceUuid); @@ -111,10 +105,10 @@ export class SubspaceDeviceService { const { subSpaceUuid, deviceUuid, spaceUuid, communityUuid, projectUuid } = params; try { - await this.spaceService.validateCommunityAndSpace( + await this.validationService.validateSpaceWithinCommunityAndProject( communityUuid, - spaceUuid, projectUuid, + spaceUuid, ); const subspace = await this.findSubspace(subSpaceUuid); From b8590841a8efa148930417280a47b397f6a56de3 Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Thu, 12 Dec 2024 14:37:44 +0400 Subject: [PATCH 08/26] aadded product item to space --- .../src/modules/space/entities/index.ts | 4 +- .../entities/space-product-item.entity.ts | 3 +- .../space/entities/space-product.entity.ts | 2 +- .../modules/space/entities/space.entity.ts | 2 +- .../modules/space/entities/subspace/index.ts | 1 + .../{ => subspace}/subspace.entity.ts | 10 +- libs/common/src/modules/space/index.ts | 4 + .../space/repositories/space.repository.ts | 14 +- .../space-product-item-model.service.ts | 7 - src/space/services/index.ts | 1 + .../services/space-product-items/index.ts | 1 + .../space-product-items.service.ts | 112 +++++++++ .../space-products/space-products.service.ts | 212 ++++++++++++------ .../services/space-validation.service.ts | 7 +- src/space/services/space.service.ts | 35 ++- .../services/subspace/subspace.service.ts | 11 +- src/space/space.module.ts | 4 + 17 files changed, 328 insertions(+), 102 deletions(-) create mode 100644 libs/common/src/modules/space/entities/subspace/index.ts rename libs/common/src/modules/space/entities/{ => subspace}/subspace.entity.ts (74%) create mode 100644 libs/common/src/modules/space/index.ts create mode 100644 src/space/services/space-product-items/index.ts create mode 100644 src/space/services/space-product-items/space-product-items.service.ts diff --git a/libs/common/src/modules/space/entities/index.ts b/libs/common/src/modules/space/entities/index.ts index d09bdac..f07ec93 100644 --- a/libs/common/src/modules/space/entities/index.ts +++ b/libs/common/src/modules/space/entities/index.ts @@ -1,5 +1,5 @@ export * from './space.entity'; -export * from './subspace.entity'; -export * from './space-product-item.entity'; +export * from './subspace'; export * from './space-product.entity'; +export * from './space-product-item.entity'; export * from './space-link.entity'; diff --git a/libs/common/src/modules/space/entities/space-product-item.entity.ts b/libs/common/src/modules/space/entities/space-product-item.entity.ts index 70e7c1f..c53dfd4 100644 --- a/libs/common/src/modules/space/entities/space-product-item.entity.ts +++ b/libs/common/src/modules/space/entities/space-product-item.entity.ts @@ -14,14 +14,13 @@ export class SpaceProductItemEntity extends AbstractEntity @ManyToOne(() => SpaceProductEntity, (spaceProduct) => spaceProduct.items, { nullable: false, }) - public spaceProducts: SpaceProductEntity; + public spaceProduct: SpaceProductEntity; @ManyToOne( () => SpaceProductItemModelEntity, (spaceProductItemModel) => spaceProductItemModel.items, { nullable: true, - onDelete: 'SET NULL', }, ) public spaceProductItemModel?: SpaceProductItemModelEntity; diff --git a/libs/common/src/modules/space/entities/space-product.entity.ts b/libs/common/src/modules/space/entities/space-product.entity.ts index 8fa0c4f..5f8a062 100644 --- a/libs/common/src/modules/space/entities/space-product.entity.ts +++ b/libs/common/src/modules/space/entities/space-product.entity.ts @@ -27,7 +27,7 @@ export class SpaceProductEntity extends AbstractEntity { }) productCount: number; - @OneToMany(() => SpaceProductItemEntity, (item) => item.spaceProducts, { + @OneToMany(() => SpaceProductItemEntity, (item) => item.spaceProduct, { cascade: true, }) public items: SpaceProductItemEntity[]; diff --git a/libs/common/src/modules/space/entities/space.entity.ts b/libs/common/src/modules/space/entities/space.entity.ts index 9d6e6fd..1b972f6 100644 --- a/libs/common/src/modules/space/entities/space.entity.ts +++ b/libs/common/src/modules/space/entities/space.entity.ts @@ -11,7 +11,7 @@ import { AbstractEntity } from '../../abstract/entities/abstract.entity'; import { UserSpaceEntity } from '../../user/entities'; import { DeviceEntity } from '../../device/entities'; import { CommunityEntity } from '../../community/entities'; -import { SubspaceEntity } from './subspace.entity'; +import { SubspaceEntity } from './subspace'; import { SpaceLinkEntity } from './space-link.entity'; import { SpaceProductEntity } from './space-product.entity'; import { SceneEntity } from '../../scene/entities'; diff --git a/libs/common/src/modules/space/entities/subspace/index.ts b/libs/common/src/modules/space/entities/subspace/index.ts new file mode 100644 index 0000000..be13961 --- /dev/null +++ b/libs/common/src/modules/space/entities/subspace/index.ts @@ -0,0 +1 @@ +export * from './subspace.entity'; diff --git a/libs/common/src/modules/space/entities/subspace.entity.ts b/libs/common/src/modules/space/entities/subspace/subspace.entity.ts similarity index 74% rename from libs/common/src/modules/space/entities/subspace.entity.ts rename to libs/common/src/modules/space/entities/subspace/subspace.entity.ts index ccb5118..8f0d15d 100644 --- a/libs/common/src/modules/space/entities/subspace.entity.ts +++ b/libs/common/src/modules/space/entities/subspace/subspace.entity.ts @@ -1,9 +1,9 @@ +import { AbstractEntity } from '@app/common/modules/abstract/entities/abstract.entity'; +import { DeviceEntity } from '@app/common/modules/device/entities'; +import { SubspaceModelEntity } from '@app/common/modules/space-model'; import { Column, Entity, JoinColumn, ManyToOne, OneToMany } from 'typeorm'; -import { AbstractEntity } from '../../abstract/entities/abstract.entity'; -import { DeviceEntity } from '../../device/entities'; -import { SpaceEntity } from './space.entity'; -import { SubspaceDto } from '../dtos'; -import { SubspaceModelEntity } from '../../space-model'; +import { SubspaceDto } from '../../dtos'; +import { SpaceEntity } from '../space.entity'; @Entity({ name: 'subspace' }) export class SubspaceEntity extends AbstractEntity { diff --git a/libs/common/src/modules/space/index.ts b/libs/common/src/modules/space/index.ts new file mode 100644 index 0000000..b797801 --- /dev/null +++ b/libs/common/src/modules/space/index.ts @@ -0,0 +1,4 @@ +export * from './dtos'; +export * from './entities'; +export * from './repositories'; +export * from './space.repository.module'; diff --git a/libs/common/src/modules/space/repositories/space.repository.ts b/libs/common/src/modules/space/repositories/space.repository.ts index 43ce45e..677db0e 100644 --- a/libs/common/src/modules/space/repositories/space.repository.ts +++ b/libs/common/src/modules/space/repositories/space.repository.ts @@ -1,7 +1,12 @@ import { DataSource, Repository } from 'typeorm'; import { Injectable } from '@nestjs/common'; import { SpaceProductEntity } from '../entities/space-product.entity'; -import { SpaceEntity, SpaceLinkEntity, SubspaceEntity } from '../entities'; +import { + SpaceEntity, + SpaceLinkEntity, + SpaceProductItemEntity, + SubspaceEntity, +} from '../entities'; @Injectable() export class SpaceRepository extends Repository { @@ -28,3 +33,10 @@ export class SpaceProductRepository extends Repository { super(SpaceProductEntity, dataSource.createEntityManager()); } } + +@Injectable() +export class SpaceProductItemRepository extends Repository { + constructor(private dataSource: DataSource) { + super(SpaceProductItemEntity, dataSource.createEntityManager()); + } +} 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 ac9c0d8..9f40efa 100644 --- a/src/space-model/services/space-product-item-model.service.ts +++ b/src/space-model/services/space-product-item-model.service.ts @@ -20,13 +20,6 @@ export class SpaceProductItemModelService { queryRunner: QueryRunner, ) { await this.validateTags(itemModelDtos, queryRunner, spaceModel); - - if (!spaceProductModel) { - throw new HttpException( - 'Space product model is required to create product items.', - HttpStatus.BAD_REQUEST, - ); - } try { const productItems = itemModelDtos.map((dto) => queryRunner.manager.create(this.spaceProductItemRepository.target, { diff --git a/src/space/services/index.ts b/src/space/services/index.ts index 79eb32f..6a3beeb 100644 --- a/src/space/services/index.ts +++ b/src/space/services/index.ts @@ -5,3 +5,4 @@ export * from './subspace'; export * from './space-link'; export * from './space-scene.service'; export * from './space-products'; +export * from './space-product-items'; diff --git a/src/space/services/space-product-items/index.ts b/src/space/services/space-product-items/index.ts new file mode 100644 index 0000000..fff8634 --- /dev/null +++ b/src/space/services/space-product-items/index.ts @@ -0,0 +1 @@ +export * from './space-product-items.service'; diff --git a/src/space/services/space-product-items/space-product-items.service.ts b/src/space/services/space-product-items/space-product-items.service.ts new file mode 100644 index 0000000..4dbe0ff --- /dev/null +++ b/src/space/services/space-product-items/space-product-items.service.ts @@ -0,0 +1,112 @@ +import { + SpaceEntity, + SpaceProductEntity, + SpaceProductItemRepository, +} from '@app/common/modules/space'; +import { HttpException, HttpStatus, Injectable } from '@nestjs/common'; +import { CreateSpaceProductItemDto } from '../../dtos'; +import { QueryRunner } from 'typeorm'; +import { SpaceProductModelEntity } from '@app/common/modules/space-model'; + +@Injectable() +export class SpaceProductItemService { + constructor( + private readonly spaceProductItemRepository: SpaceProductItemRepository, + ) {} + + async createProductItem( + itemModelDtos: CreateSpaceProductItemDto[], + spaceProduct: SpaceProductEntity, + space: SpaceEntity, + queryRunner: QueryRunner, + ) { + await this.validateTags(itemModelDtos, queryRunner, space); + + try { + const productItems = itemModelDtos.map((dto) => + queryRunner.manager.create(this.spaceProductItemRepository.target, { + tag: dto.tag, + spaceProduct, + }), + ); + + await queryRunner.manager.save(productItems); + } catch (error) { + if (error instanceof HttpException) { + throw error; + } + + throw new HttpException( + error.message || + 'An unexpected error occurred while creating product items.', + HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + } + + async createSpaceProductItemFromModel( + spaceProduct: SpaceProductEntity, + spaceProductModel: SpaceProductModelEntity, + queryRunner: QueryRunner, + ) { + const spaceProductItemModels = spaceProductModel.items; + + try { + const productItems = spaceProductItemModels.map((model) => + queryRunner.manager.create(this.spaceProductItemRepository.target, { + tag: model.tag, + spaceProduct, + }), + ); + + await queryRunner.manager.save(productItems); + } catch (error) { + if (error instanceof HttpException) { + throw error; + } + + throw new HttpException( + error.message || + 'An unexpected error occurred while creating product items.', + HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + } + + private async validateTags( + itemModelDtos: CreateSpaceProductItemDto[], + queryRunner: QueryRunner, + space: SpaceEntity, + ) { + 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 the request: ${[...new Set(duplicateTags)].join(', ')}`, + HttpStatus.BAD_REQUEST, + ); + } + + const existingTags = await queryRunner.manager.find( + this.spaceProductItemRepository.target, + { + where: { spaceProduct: { space } }, + 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/services/space-products/space-products.service.ts b/src/space/services/space-products/space-products.service.ts index 5b218e4..895053f 100644 --- a/src/space/services/space-products/space-products.service.ts +++ b/src/space/services/space-products/space-products.service.ts @@ -2,96 +2,98 @@ import { Injectable, HttpException, HttpStatus } from '@nestjs/common'; import { ProductRepository } from '@app/common/modules/product/repositories'; import { SpaceEntity } from '@app/common/modules/space/entities'; import { SpaceProductEntity } from '@app/common/modules/space/entities/space-product.entity'; -import { SpaceProductRepository } from '@app/common/modules/space/repositories'; -import { In } from 'typeorm'; +import { In, QueryRunner } from 'typeorm'; +import { ProductAssignmentDto } from '../../dtos'; +import { SpaceProductItemService } from '../space-product-items'; +import { SpaceModelEntity } from '@app/common/modules/space-model'; @Injectable() export class SpaceProductService { constructor( private readonly productRepository: ProductRepository, - private readonly spaceProductRepository: SpaceProductRepository, + private readonly spaceProductItemService: SpaceProductItemService, ) {} + async createProductItemFromModel( + spaceModel: SpaceModelEntity, + space: SpaceEntity, + queryRunner: QueryRunner, + ) { + const spaceProductModels = spaceModel.spaceProductModels; + if (!spaceProductModels?.length) return; + const newSpaceProducts = []; + + spaceProductModels.map((spaceProductModel) => { + newSpaceProducts.push( + queryRunner.manager.create(SpaceProductEntity, { + space: space, + product: spaceProductModel.product, + productCount: spaceProductModel.productCount, + spaceProductModel: spaceProductModel, + }), + ); + }); + if (newSpaceProducts.length > 0) { + await queryRunner.manager.save(SpaceProductEntity, newSpaceProducts); + await Promise.all( + newSpaceProducts.map((spaceProduct, index) => { + const spaceProductModel = spaceProductModels[index]; + return this.spaceProductItemService.createSpaceProductItemFromModel( + spaceProduct, + spaceProductModel, + queryRunner, + ); + }), + ); + } + } + async assignProductsToSpace( space: SpaceEntity, - products: { productId: string; count: number }[], + products: ProductAssignmentDto[], + queryRunner: QueryRunner, ): Promise { + let updatedProducts: SpaceProductEntity[] = []; + try { const uniqueProducts = this.validateUniqueProducts(products); const productEntities = await this.getProductEntities(uniqueProducts); + const existingSpaceProducts = await this.getExistingSpaceProducts( + space, + queryRunner, + ); - // Fetch existing space products - const existingSpaceProducts = await this.spaceProductRepository.find({ - where: { - space: { - uuid: space.uuid, - }, - }, - relations: ['product'], - }); - - const updatedProducts = []; - const newProducts = []; - - for (const { productId, count } of uniqueProducts) { - const product = productEntities.get(productId); - if (!product) { - throw new HttpException( - `Product with ID ${productId} not found`, - HttpStatus.NOT_FOUND, - ); - } - - // Check if product already exists in the space - const existingProduct = existingSpaceProducts.find( - (spaceProduct) => spaceProduct.product.uuid === productId, + if (existingSpaceProducts) { + updatedProducts = await this.updateExistingProducts( + existingSpaceProducts, + uniqueProducts, + productEntities, + queryRunner, ); - - if (existingProduct) { - // If count is different, update the existing record - if (existingProduct.productCount !== count) { - existingProduct.productCount = count; - updatedProducts.push(existingProduct); - } - } else { - // Add new product if it doesn't exist - newProducts.push( - this.spaceProductRepository.create({ - space, - product, - productCount: count, - }), - ); - } } - // Save updates and new records - if (updatedProducts.length > 0) { - await this.spaceProductRepository.save(updatedProducts); - } - - if (newProducts.length > 0) { - await this.spaceProductRepository.save(newProducts); - } + const newProducts = await this.createNewProducts( + uniqueProducts, + productEntities, + space, + queryRunner, + ); return [...updatedProducts, ...newProducts]; } catch (error) { - console.error('Error assigning products to space:', error); - if (!(error instanceof HttpException)) { throw new HttpException( - 'An error occurred while assigning products to the space', + `An error occurred while assigning products to the space ${error}`, HttpStatus.INTERNAL_SERVER_ERROR, ); } - throw error; } } private validateUniqueProducts( - products: { productId: string; count: number }[], - ): { productId: string; count: number }[] { + products: ProductAssignmentDto[], + ): ProductAssignmentDto[] { const productIds = new Set(); const uniqueProducts = []; @@ -110,15 +112,13 @@ export class SpaceProductService { } private async getProductEntities( - products: { productId: string; count: number }[], + products: ProductAssignmentDto[], ): Promise> { try { const productIds = products.map((p) => p.productId); - const productEntities = await this.productRepository.find({ where: { uuid: In(productIds) }, }); - return new Map(productEntities.map((p) => [p.uuid, p])); } catch (error) { console.error('Error fetching product entities:', error); @@ -128,4 +128,90 @@ export class SpaceProductService { ); } } + + private async getExistingSpaceProducts( + space: SpaceEntity, + queryRunner: QueryRunner, + ): Promise { + return queryRunner.manager.find(SpaceProductEntity, { + where: { space: { uuid: space.uuid } }, + relations: ['product'], + }); + } + + private async updateExistingProducts( + existingSpaceProducts: SpaceProductEntity[], + uniqueProducts: ProductAssignmentDto[], + productEntities: Map, + queryRunner: QueryRunner, + ): Promise { + const updatedProducts = []; + + for (const { productId, count } of uniqueProducts) { + productEntities.get(productId); + const existingProduct = existingSpaceProducts.find( + (spaceProduct) => spaceProduct.product.uuid === productId, + ); + + if (existingProduct && existingProduct.productCount !== count) { + existingProduct.productCount = count; + updatedProducts.push(existingProduct); + } + } + + if (updatedProducts.length > 0) { + await queryRunner.manager.save(SpaceProductEntity, updatedProducts); + } + + return updatedProducts; + } + + private async createNewProducts( + uniqueSpaceProducts: ProductAssignmentDto[], + productEntities: Map, + space: SpaceEntity, + queryRunner: QueryRunner, + ): Promise { + const newProducts = []; + + for (const uniqueSpaceProduct of uniqueSpaceProducts) { + const product = productEntities.get(uniqueSpaceProduct.productId); + this.validateProductCount(uniqueSpaceProduct); + + newProducts.push( + queryRunner.manager.create(SpaceProductEntity, { + space, + product, + productCount: uniqueSpaceProduct.count, + }), + ); + } + if (newProducts.length > 0) { + await queryRunner.manager.save(SpaceProductEntity, newProducts); + + await Promise.all( + uniqueSpaceProducts.map((dto, index) => { + const spaceProduct = newProducts[index]; + return this.spaceProductItemService.createProductItem( + dto.items, + spaceProduct, + space, + queryRunner, + ); + }), + ); + } + + return newProducts; + } + + private validateProductCount(dto: ProductAssignmentDto) { + const productItemCount = dto.items.length; + if (dto.count !== productItemCount) { + throw new HttpException( + `Product count (${dto.count}) does not match the number of items (${productItemCount}) for product ID ${dto.productId}.`, + HttpStatus.BAD_REQUEST, + ); + } + } } diff --git a/src/space/services/space-validation.service.ts b/src/space/services/space-validation.service.ts index 56e6532..cd80387 100644 --- a/src/space/services/space-validation.service.ts +++ b/src/space/services/space-validation.service.ts @@ -59,7 +59,12 @@ export class ValidationService { async validateSpaceModel(spaceModelUuid: string): Promise { const spaceModel = await this.spaceModelRepository.findOne({ where: { uuid: spaceModelUuid }, - relations: ['subspaceModels'], + relations: [ + 'subspaceModels', + 'spaceProductModels', + 'spaceProductModels.product', + 'spaceProductModels.items', + ], }); if (!spaceModel) { diff --git a/src/space/services/space.service.ts b/src/space/services/space.service.ts index 135f2e5..5481d19 100644 --- a/src/space/services/space.service.ts +++ b/src/space/services/space.service.ts @@ -63,7 +63,7 @@ export class SpaceService { : null; try { - const newSpace = this.spaceRepository.create({ + const newSpace = queryRunner.manager.create(SpaceEntity, { ...addSpaceDto, spaceModel, parent: parentUuid ? parent : null, @@ -80,13 +80,13 @@ export class SpaceService { ); } - if (subspaces) { + if (subspaces?.length) { await this.subSpaceService.createSubspacesFromNames( subspaces, newSpace, queryRunner, ); - } else { + } else if (spaceModel && spaceModel.subspaceModels.length) { await this.subSpaceService.createSubSpaceFromModel( spaceModel, newSpace, @@ -98,6 +98,13 @@ export class SpaceService { await this.spaceProductService.assignProductsToSpace( newSpace, products, + queryRunner, + ); + } else if (spaceModel && spaceModel.spaceProductModels.length) { + await this.spaceProductService.createProductItemFromModel( + spaceModel, + newSpace, + queryRunner, ); } await queryRunner.commitTransaction(); @@ -216,7 +223,12 @@ export class SpaceService { updateSpaceDto: UpdateSpaceDto, ): Promise { const { communityUuid, spaceUuid, projectUuid } = params; + const queryRunner = this.dataSource.createQueryRunner(); + try { + await queryRunner.connect(); + await queryRunner.startTransaction(); + const space = await this.validationService.validateSpaceWithinCommunityAndProject( communityUuid, @@ -234,14 +246,16 @@ export class SpaceService { Object.assign(space, updateSpaceDto, { parent }); // Save the updated space - const updatedSpace = await this.spaceRepository.save(space); + const updatedSpace = await queryRunner.manager.save(space); if (products && products.length > 0) { await this.spaceProductService.assignProductsToSpace( updatedSpace, products, + queryRunner, ); } + await queryRunner.commitTransaction(); return new SuccessResponseDto({ message: `Space with ID ${spaceUuid} successfully updated`, @@ -249,6 +263,8 @@ export class SpaceService { statusCode: HttpStatus.OK, }); } catch (error) { + await queryRunner.rollbackTransaction(); + if (error instanceof HttpException) { throw error; } @@ -256,6 +272,8 @@ export class SpaceService { 'An error occurred while updating the space', HttpStatus.INTERNAL_SERVER_ERROR, ); + } finally { + await queryRunner.release(); } } @@ -348,13 +366,6 @@ export class SpaceService { return rootSpaces; } - private throwNotFound(entity: string, uuid: string) { - throw new HttpException( - `${entity} with ID ${uuid} not found`, - HttpStatus.NOT_FOUND, - ); - } - private validateSpaceCreation( spaceModelUuid?: string, products?: ProductAssignmentDto[], @@ -362,7 +373,7 @@ export class SpaceService { ) { if (spaceModelUuid && (products?.length || subSpaces?.length)) { throw new HttpException( - 'Space model cannot be assigned with products or subspaces.', + 'For space creation choose either space model or products and subspace', HttpStatus.CONFLICT, ); } diff --git a/src/space/services/subspace/subspace.service.ts b/src/space/services/subspace/subspace.service.ts index dae280b..4e9b2e3 100644 --- a/src/space/services/subspace/subspace.service.ts +++ b/src/space/services/subspace/subspace.service.ts @@ -49,9 +49,7 @@ export class SubSpaceService { ): Promise { const subSpaces = spaceModel.subspaceModels; - if (!subSpaces || subSpaces.length === 0) { - return; - } + if (!subSpaces?.length) return; const subspaceData = subSpaces.map((subSpaceModel) => ({ subspaceName: subSpaceModel.subspaceName, @@ -79,12 +77,11 @@ export class SubSpaceService { addSubspaceDto: AddSubspaceDto, params: GetSpaceParam, ): Promise { - const { communityUuid, spaceUuid, projectUuid } = params; const space = await this.validationService.validateSpaceWithinCommunityAndProject( - communityUuid, - projectUuid, - spaceUuid, + params.projectUuid, + params.projectUuid, + params.spaceUuid, ); try { diff --git a/src/space/space.module.ts b/src/space/space.module.ts index 3a52f7b..40a72d3 100644 --- a/src/space/space.module.ts +++ b/src/space/space.module.ts @@ -12,6 +12,7 @@ import { import { SpaceDeviceService, SpaceLinkService, + SpaceProductItemService, SpaceProductService, SpaceSceneService, SpaceService, @@ -24,6 +25,7 @@ import { SpaceRepository, SubspaceRepository, SpaceLinkRepository, + SpaceProductItemRepository, } from '@app/common/modules/space/repositories'; import { CommunityRepository } from '@app/common/modules/community/repositories'; import { @@ -87,6 +89,8 @@ import { ValidationService } from './services/space-validation.service'; ProjectRepository, SpaceModelRepository, SubspaceRepository, + SpaceProductItemService, + SpaceProductItemRepository, ], exports: [SpaceService], }) From 2e95dbfd9ff1c4a4904380f978f04a19b05502f6 Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Thu, 12 Dec 2024 14:42:24 +0400 Subject: [PATCH 09/26] folder restructure --- libs/common/src/modules/space-model/entities/index.ts | 2 +- .../modules/space-model/entities/space-model.entity.ts | 2 +- .../modules/space-model/entities/subspace-model/index.ts | 1 + .../{ => subspace-model}/subspace-model.entity.ts | 8 ++++---- .../entities/subspace/subspace-product-item.entity.ts | 0 .../space/entities/subspace/subspace-product.entity.ts | 0 6 files changed, 7 insertions(+), 6 deletions(-) create mode 100644 libs/common/src/modules/space-model/entities/subspace-model/index.ts rename libs/common/src/modules/space-model/entities/{ => subspace-model}/subspace-model.entity.ts (73%) create mode 100644 libs/common/src/modules/space/entities/subspace/subspace-product-item.entity.ts create mode 100644 libs/common/src/modules/space/entities/subspace/subspace-product.entity.ts diff --git a/libs/common/src/modules/space-model/entities/index.ts b/libs/common/src/modules/space-model/entities/index.ts index f1e4016..ec19308 100644 --- a/libs/common/src/modules/space-model/entities/index.ts +++ b/libs/common/src/modules/space-model/entities/index.ts @@ -1,4 +1,4 @@ export * from './space-model.entity'; export * from './space-product-item-model.entity'; export * from './space-product-model.entity'; -export * from './subspace-model.entity'; \ No newline at end of file +export * from './subspace-model'; 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 b67c642..c340b8b 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 @@ -8,7 +8,7 @@ import { } from 'typeorm'; import { AbstractEntity } from '../../abstract/entities/abstract.entity'; import { SpaceModelDto } from '../dtos'; -import { SubspaceModelEntity } from './subspace-model.entity'; +import { SubspaceModelEntity } from './subspace-model'; import { SpaceProductModelEntity } from './space-product-model.entity'; import { ProjectEntity } from '../../project/entities'; import { SpaceEntity } from '../../space/entities'; diff --git a/libs/common/src/modules/space-model/entities/subspace-model/index.ts b/libs/common/src/modules/space-model/entities/subspace-model/index.ts new file mode 100644 index 0000000..cd29d67 --- /dev/null +++ b/libs/common/src/modules/space-model/entities/subspace-model/index.ts @@ -0,0 +1 @@ +export * from './subspace-model.entity'; diff --git a/libs/common/src/modules/space-model/entities/subspace-model.entity.ts b/libs/common/src/modules/space-model/entities/subspace-model/subspace-model.entity.ts similarity index 73% rename from libs/common/src/modules/space-model/entities/subspace-model.entity.ts rename to libs/common/src/modules/space-model/entities/subspace-model/subspace-model.entity.ts index c1e335a..a8a3595 100644 --- a/libs/common/src/modules/space-model/entities/subspace-model.entity.ts +++ b/libs/common/src/modules/space-model/entities/subspace-model/subspace-model.entity.ts @@ -1,8 +1,8 @@ +import { AbstractEntity } from '@app/common/modules/abstract/entities/abstract.entity'; import { Column, Entity, ManyToOne, OneToMany, Unique } from 'typeorm'; -import { AbstractEntity } from '../../abstract/entities/abstract.entity'; -import { SpaceModelEntity } from './space-model.entity'; -import { SubSpaceModelDto } from '../dtos'; -import { SubspaceEntity } from '../../space/entities'; +import { SubSpaceModelDto } from '../../dtos'; +import { SpaceModelEntity } from '../space-model.entity'; +import { SubspaceEntity } from '@app/common/modules/space/entities'; @Entity({ name: 'subspace-model' }) @Unique(['subspaceName', 'spaceModel']) diff --git a/libs/common/src/modules/space/entities/subspace/subspace-product-item.entity.ts b/libs/common/src/modules/space/entities/subspace/subspace-product-item.entity.ts new file mode 100644 index 0000000..e69de29 diff --git a/libs/common/src/modules/space/entities/subspace/subspace-product.entity.ts b/libs/common/src/modules/space/entities/subspace/subspace-product.entity.ts new file mode 100644 index 0000000..e69de29 From ff7c02d5848c8160388fddccc9199429fe165074 Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Thu, 12 Dec 2024 16:14:35 +0400 Subject: [PATCH 10/26] folder restructure --- libs/common/src/modules/space-model/dtos/index.ts | 2 +- .../common/src/modules/space-model/dtos/subspace-model/index.ts | 1 + .../space-model/dtos/{ => subspace-model}/subspace-model.dto.ts | 0 3 files changed, 2 insertions(+), 1 deletion(-) create mode 100644 libs/common/src/modules/space-model/dtos/subspace-model/index.ts rename libs/common/src/modules/space-model/dtos/{ => subspace-model}/subspace-model.dto.ts (100%) diff --git a/libs/common/src/modules/space-model/dtos/index.ts b/libs/common/src/modules/space-model/dtos/index.ts index 3923af4..9ba8130 100644 --- a/libs/common/src/modules/space-model/dtos/index.ts +++ b/libs/common/src/modules/space-model/dtos/index.ts @@ -1,4 +1,4 @@ -export * from './subspace-model.dto'; +export * from './subspace-model'; export * from './space-model.dto'; export * from './space-product-item-model.dto'; export * from './space-product-model.dto'; diff --git a/libs/common/src/modules/space-model/dtos/subspace-model/index.ts b/libs/common/src/modules/space-model/dtos/subspace-model/index.ts new file mode 100644 index 0000000..05a7548 --- /dev/null +++ b/libs/common/src/modules/space-model/dtos/subspace-model/index.ts @@ -0,0 +1 @@ +export * from './subspace-model.dto' \ No newline at end of file diff --git a/libs/common/src/modules/space-model/dtos/subspace-model.dto.ts b/libs/common/src/modules/space-model/dtos/subspace-model/subspace-model.dto.ts similarity index 100% rename from libs/common/src/modules/space-model/dtos/subspace-model.dto.ts rename to libs/common/src/modules/space-model/dtos/subspace-model/subspace-model.dto.ts From 8d0001cb95f9b5a0d2dfe8f4787d0e05fb862c7f Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Thu, 12 Dec 2024 16:19:37 +0400 Subject: [PATCH 11/26] added subspace model remaining dtos --- .../subspace-product-item-model.dto.ts | 15 ++++++++++++ .../subspace-product-model.dto.ts | 23 +++++++++++++++++++ 2 files changed, 38 insertions(+) create mode 100644 libs/common/src/modules/space-model/dtos/subspace-model/subspace-product-item-model.dto.ts create mode 100644 libs/common/src/modules/space-model/dtos/subspace-model/subspace-product-model.dto.ts diff --git a/libs/common/src/modules/space-model/dtos/subspace-model/subspace-product-item-model.dto.ts b/libs/common/src/modules/space-model/dtos/subspace-model/subspace-product-item-model.dto.ts new file mode 100644 index 0000000..479642b --- /dev/null +++ b/libs/common/src/modules/space-model/dtos/subspace-model/subspace-product-item-model.dto.ts @@ -0,0 +1,15 @@ +import { IsString, IsNotEmpty } from 'class-validator'; + +export class SubspaceProductItemModelDto { + @IsString() + @IsNotEmpty() + uuid: string; + + @IsString() + @IsNotEmpty() + tag: string; + + @IsString() + @IsNotEmpty() + subspaceProductModelUuid: string; +} diff --git a/libs/common/src/modules/space-model/dtos/subspace-model/subspace-product-model.dto.ts b/libs/common/src/modules/space-model/dtos/subspace-model/subspace-product-model.dto.ts new file mode 100644 index 0000000..4eebaae --- /dev/null +++ b/libs/common/src/modules/space-model/dtos/subspace-model/subspace-product-model.dto.ts @@ -0,0 +1,23 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsNotEmpty, IsNumber, IsString } from 'class-validator'; +import { SubspaceProductItemModelDto } from './subspace-product-item-model.dto'; + +export class SubpaceProductModelDto { + @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: [SubspaceProductItemModelDto], + }) + items: SubspaceProductItemModelDto[]; +} From e198c081cb3e159522762ab40c0f5c60366244c9 Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Thu, 12 Dec 2024 16:19:57 +0400 Subject: [PATCH 12/26] indexing --- .../src/modules/space-model/dtos/subspace-model/index.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/libs/common/src/modules/space-model/dtos/subspace-model/index.ts b/libs/common/src/modules/space-model/dtos/subspace-model/index.ts index 05a7548..70337b6 100644 --- a/libs/common/src/modules/space-model/dtos/subspace-model/index.ts +++ b/libs/common/src/modules/space-model/dtos/subspace-model/index.ts @@ -1 +1,3 @@ -export * from './subspace-model.dto' \ No newline at end of file +export * from './subspace-model.dto'; +export * from './subspace-product-item-model.dto'; +export * from './subspace-product-model.dto'; From 9fdd559b9fbea538863fd2f00ccd0ce99bd5a13c Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Thu, 12 Dec 2024 16:37:49 +0400 Subject: [PATCH 13/26] added subspace model product and item entities --- libs/common/src/database/database.module.ts | 4 ++ .../product/entities/product.entity.ts | 7 ++++ .../entities/space-model.entity.ts | 1 - .../entities/subspace-model/index.ts | 2 + .../subspace-model/subspace-model.entity.ts | 10 +++++ .../subspace-product-item-model.entity.ts | 21 ++++++++++ .../subspace-product-model.entity.ts | 38 +++++++++++++++++++ 7 files changed, 82 insertions(+), 1 deletion(-) create mode 100644 libs/common/src/modules/space-model/entities/subspace-model/subspace-product-item-model.entity.ts create mode 100644 libs/common/src/modules/space-model/entities/subspace-model/subspace-product-model.entity.ts diff --git a/libs/common/src/database/database.module.ts b/libs/common/src/database/database.module.ts index 90b287a..3681bed 100644 --- a/libs/common/src/database/database.module.ts +++ b/libs/common/src/database/database.module.ts @@ -34,6 +34,8 @@ import { SpaceProductItemModelEntity, SpaceProductModelEntity, SubspaceModelEntity, + SubspaceProductItemModelEntity, + SubspaceProductModelEntity, } from '../modules/space-model/entities'; @Module({ imports: [ @@ -81,6 +83,8 @@ import { SubspaceModelEntity, SpaceProductEntity, SpaceProductItemEntity, + SubspaceProductModelEntity, + SubspaceProductItemModelEntity, ], 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 7b25470..926d179 100644 --- a/libs/common/src/modules/product/entities/product.entity.ts +++ b/libs/common/src/modules/product/entities/product.entity.ts @@ -4,6 +4,7 @@ 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'; +import { SubspaceProductModelEntity } from '../../space-model/entities/subspace-model/subspace-product-model.entity'; @Entity({ name: 'product' }) export class ProductEntity extends AbstractEntity { @@ -37,6 +38,12 @@ export class ProductEntity extends AbstractEntity { ) spaceProductModels: SpaceProductModelEntity[]; + @OneToMany( + () => SubspaceProductModelEntity, + (subspaceProductModel) => subspaceProductModel.product, + ) + subpaceProductModels: SubspaceProductModelEntity[]; + @OneToMany( () => DeviceEntity, (devicesProductEntity) => devicesProductEntity.productDevice, 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 c340b8b..591d04f 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 @@ -49,7 +49,6 @@ export class SpaceModelEntity extends AbstractEntity { () => SpaceProductModelEntity, (productModel) => productModel.spaceModel, { - cascade: true, nullable: true, }, ) diff --git a/libs/common/src/modules/space-model/entities/subspace-model/index.ts b/libs/common/src/modules/space-model/entities/subspace-model/index.ts index cd29d67..e39403f 100644 --- a/libs/common/src/modules/space-model/entities/subspace-model/index.ts +++ b/libs/common/src/modules/space-model/entities/subspace-model/index.ts @@ -1 +1,3 @@ export * from './subspace-model.entity'; +export * from './subspace-product-item-model.entity'; +export * from './subspace-product-model.entity'; diff --git a/libs/common/src/modules/space-model/entities/subspace-model/subspace-model.entity.ts b/libs/common/src/modules/space-model/entities/subspace-model/subspace-model.entity.ts index a8a3595..2d1a170 100644 --- a/libs/common/src/modules/space-model/entities/subspace-model/subspace-model.entity.ts +++ b/libs/common/src/modules/space-model/entities/subspace-model/subspace-model.entity.ts @@ -3,6 +3,7 @@ import { Column, Entity, ManyToOne, OneToMany, Unique } from 'typeorm'; import { SubSpaceModelDto } from '../../dtos'; import { SpaceModelEntity } from '../space-model.entity'; import { SubspaceEntity } from '@app/common/modules/space/entities'; +import { SubspaceProductModelEntity } from './subspace-product-model.entity'; @Entity({ name: 'subspace-model' }) @Unique(['subspaceName', 'spaceModel']) @@ -33,4 +34,13 @@ export class SubspaceModelEntity extends AbstractEntity { cascade: true, }) public spaces: SubspaceEntity[]; + + @OneToMany( + () => SubspaceProductModelEntity, + (productModel) => productModel.subspaceModel, + { + nullable: true, + }, + ) + public subspaceProductModels: SubspaceProductModelEntity[]; } diff --git a/libs/common/src/modules/space-model/entities/subspace-model/subspace-product-item-model.entity.ts b/libs/common/src/modules/space-model/entities/subspace-model/subspace-product-item-model.entity.ts new file mode 100644 index 0000000..decd0a7 --- /dev/null +++ b/libs/common/src/modules/space-model/entities/subspace-model/subspace-product-item-model.entity.ts @@ -0,0 +1,21 @@ +import { AbstractEntity } from '@app/common/modules/abstract/entities/abstract.entity'; +import { Entity, Column, ManyToOne } from 'typeorm'; +import { SubspaceProductItemModelDto } from '../../dtos'; +import { SubspaceProductModelEntity } from './subspace-product-model.entity'; + +@Entity({ name: 'subspace-product-item-model' }) +export class SubspaceProductItemModelEntity extends AbstractEntity { + @Column({ + nullable: false, + }) + public tag: string; + + @ManyToOne( + () => SubspaceProductModelEntity, + (subspaceProductModel) => subspaceProductModel.items, + { + nullable: false, + }, + ) + public subspaceProductModel: SubspaceProductModelEntity; +} diff --git a/libs/common/src/modules/space-model/entities/subspace-model/subspace-product-model.entity.ts b/libs/common/src/modules/space-model/entities/subspace-model/subspace-product-model.entity.ts new file mode 100644 index 0000000..3a5ab1f --- /dev/null +++ b/libs/common/src/modules/space-model/entities/subspace-model/subspace-product-model.entity.ts @@ -0,0 +1,38 @@ +import { Entity, Column, ManyToOne, OneToMany } from 'typeorm'; +import { SubpaceProductModelDto } from '../../dtos'; +import { AbstractEntity } from '@app/common/modules/abstract/entities/abstract.entity'; +import { SubspaceModelEntity } from './subspace-model.entity'; +import { ProductEntity } from '@app/common/modules/product/entities'; +import { SubspaceProductItemModelEntity } from './subspace-product-item-model.entity'; + +@Entity({ name: 'subspace-product-model' }) +export class SubspaceProductModelEntity extends AbstractEntity { + @Column({ + nullable: false, + type: 'int', + }) + productCount: number; + + @ManyToOne( + () => SubspaceModelEntity, + (spaceModel) => spaceModel.subspaceProductModels, + { + nullable: false, + }, + ) + public subspaceModel: SubspaceModelEntity; + + @ManyToOne(() => ProductEntity, (product) => product.subpaceProductModels, { + nullable: false, + }) + public product: ProductEntity; + + @OneToMany( + () => SubspaceProductItemModelEntity, + (item) => item.subspaceProductModel, + { + cascade: true, + }, + ) + public items: SubspaceProductItemModelEntity[]; +} From 1b9b79ba6b567a8c9ae4fce1ab937b5917b4a208 Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Fri, 13 Dec 2024 09:48:33 +0400 Subject: [PATCH 14/26] renamed dtos --- .../dtos/create-space-product-item-model.dto.ts | 2 +- src/space-model/dtos/create-space-product-model.dto.ts | 8 ++++---- .../services/space-product-item-model.service.ts | 6 +++--- 3 files changed, 8 insertions(+), 8 deletions(-) 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 index 3825219..e6a7e76 100644 --- a/src/space-model/dtos/create-space-product-item-model.dto.ts +++ b/src/space-model/dtos/create-space-product-item-model.dto.ts @@ -1,7 +1,7 @@ import { ApiProperty } from '@nestjs/swagger'; import { IsNotEmpty, IsString } from 'class-validator'; -export class CreateSpaceProductItemModelDto { +export class CreateProductItemModelDto { @ApiProperty({ description: 'Specific name for the product item', example: 'Light 1', 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 9f251be..a4379a1 100644 --- a/src/space-model/dtos/create-space-product-model.dto.ts +++ b/src/space-model/dtos/create-space-product-model.dto.ts @@ -8,7 +8,7 @@ import { ArrayNotEmpty, } from 'class-validator'; import { Type } from 'class-transformer'; -import { CreateSpaceProductItemModelDto } from './create-space-product-item-model.dto'; +import { CreateProductItemModelDto } from './create-space-product-item-model.dto'; export class CreateSpaceProductModelDto { @ApiProperty({ @@ -29,11 +29,11 @@ export class CreateSpaceProductModelDto { @ApiProperty({ description: 'Specific names for each product item', - type: [CreateSpaceProductItemModelDto], + type: [CreateProductItemModelDto], }) @IsArray() @ArrayNotEmpty() @ValidateNested({ each: true }) - @Type(() => CreateSpaceProductItemModelDto) - items: CreateSpaceProductItemModelDto[]; + @Type(() => CreateProductItemModelDto) + items: CreateProductItemModelDto[]; } 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 9f40efa..f7f8b04 100644 --- a/src/space-model/services/space-product-item-model.service.ts +++ b/src/space-model/services/space-product-item-model.service.ts @@ -4,7 +4,7 @@ import { SpaceProductModelEntity, } from '@app/common/modules/space-model'; import { HttpException, HttpStatus, Injectable } from '@nestjs/common'; -import { CreateSpaceProductItemModelDto } from '../dtos'; +import { CreateProductItemModelDto } from '../dtos'; import { QueryRunner } from 'typeorm'; @Injectable() @@ -14,7 +14,7 @@ export class SpaceProductItemModelService { ) {} async createProdutItemModel( - itemModelDtos: CreateSpaceProductItemModelDto[], + itemModelDtos: CreateProductItemModelDto[], spaceProductModel: SpaceProductModelEntity, spaceModel: SpaceModelEntity, queryRunner: QueryRunner, @@ -43,7 +43,7 @@ export class SpaceProductItemModelService { } private async validateTags( - itemModelDtos: CreateSpaceProductItemModelDto[], + itemModelDtos: CreateProductItemModelDto[], queryRunner: QueryRunner, spaceModel: SpaceModelEntity, ) { From 5faeba0869e98a71953a34ad21b87f673a364fa1 Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Fri, 13 Dec 2024 09:56:43 +0400 Subject: [PATCH 15/26] common module import --- libs/common/src/common.module.ts | 27 +++++++++++++++++++ .../repositories/space-model.repository.ts | 16 +++++++++++ 2 files changed, 43 insertions(+) diff --git a/libs/common/src/common.module.ts b/libs/common/src/common.module.ts index de0780a..9a3de9c 100644 --- a/libs/common/src/common.module.ts +++ b/libs/common/src/common.module.ts @@ -9,6 +9,18 @@ import { EmailService } from './util/email.service'; import { ErrorMessageService } from 'src/error-message/error-message.service'; import { TuyaService } from './integrations/tuya/services/tuya.service'; import { SceneDeviceRepository } from './modules/scene-device/repositories'; +import { + SpaceProductItemRepository, + SpaceRepository, + SubspaceRepository, +} from './modules/space'; +import { + SpaceModelRepository, + SpaceProductModelRepository, + SubspaceModelRepository, + SubspaceProductItemModelRepository, + SubspaceProductModelRepository, +} from './modules/space-model'; @Module({ providers: [ CommonService, @@ -16,6 +28,14 @@ import { SceneDeviceRepository } from './modules/scene-device/repositories'; ErrorMessageService, TuyaService, SceneDeviceRepository, + SpaceRepository, + SubspaceRepository, + SubspaceModelRepository, + SubspaceProductModelRepository, + SubspaceProductItemModelRepository, + SpaceModelRepository, + SpaceProductModelRepository, + SpaceProductItemRepository, ], exports: [ CommonService, @@ -25,6 +45,13 @@ import { SceneDeviceRepository } from './modules/scene-device/repositories'; EmailService, ErrorMessageService, SceneDeviceRepository, + SpaceRepository, + SubspaceRepository, + SubspaceModelRepository, + SubspaceProductModelRepository, + SubspaceProductItemModelRepository, + SpaceModelRepository, + SpaceProductModelRepository, ], imports: [ ConfigModule.forRoot({ 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 index 0e37479..fc92f14 100644 --- a/libs/common/src/modules/space-model/repositories/space-model.repository.ts +++ b/libs/common/src/modules/space-model/repositories/space-model.repository.ts @@ -5,6 +5,8 @@ import { SpaceProductItemModelEntity, SpaceProductModelEntity, SubspaceModelEntity, + SubspaceProductItemModelEntity, + SubspaceProductModelEntity, } from '../entities'; @Injectable() @@ -20,6 +22,20 @@ export class SubspaceModelRepository extends Repository { } } +@Injectable() +export class SubspaceProductModelRepository extends Repository { + constructor(private dataSource: DataSource) { + super(SubspaceProductModelEntity, dataSource.createEntityManager()); + } +} + +@Injectable() +export class SubspaceProductItemModelRepository extends Repository { + constructor(private dataSource: DataSource) { + super(SubspaceProductItemModelEntity, dataSource.createEntityManager()); + } +} + @Injectable() export class SpaceProductModelRepository extends Repository { constructor(private dataSource: DataSource) { From 1373dfb0ce3a106302480f517ce488f8f0d05d1d Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Fri, 13 Dec 2024 15:00:33 +0400 Subject: [PATCH 16/26] subspace model also has product model and item --- src/product/product.module.ts | 4 +- src/product/services/product.service.ts | 19 +++++ src/space-model/common/index.ts | 1 + .../base-product-item-model.service.ts | 60 ++++++++++++++++ .../services/base-product-model.service.ts | 24 +++++++ src/space-model/common/services/index.ts | 2 + .../dtos/create-subspace-model.dto.ts | 20 +++++- src/space-model/services/index.ts | 2 + .../space-product-item-model.service.ts | 44 ++---------- .../services/space-product-model.service.ts | 32 ++------- .../services/subspace-model.service.ts | 14 ++++ .../subspace-product-item-model.service.ts | 53 ++++++++++++++ .../subspace-product-model.service.ts | 69 +++++++++++++++++++ src/space-model/space-model.module.ts | 8 +++ 14 files changed, 286 insertions(+), 66 deletions(-) create mode 100644 src/space-model/common/index.ts create mode 100644 src/space-model/common/services/base-product-item-model.service.ts create mode 100644 src/space-model/common/services/base-product-model.service.ts create mode 100644 src/space-model/common/services/index.ts create mode 100644 src/space-model/services/subspace-product-item-model.service.ts create mode 100644 src/space-model/services/subspace-product-model.service.ts diff --git a/src/product/product.module.ts b/src/product/product.module.ts index e5ffd25..aa13d20 100644 --- a/src/product/product.module.ts +++ b/src/product/product.module.ts @@ -1,10 +1,12 @@ -import { Module } from '@nestjs/common'; +import { Global, Module } from '@nestjs/common'; import { ProductService } from './services'; import { ProductRepository } from '@app/common/modules/product/repositories'; import { ProductController } from './controllers'; +@Global() @Module({ controllers: [ProductController], providers: [ProductService, ProductRepository], + exports: [ProductService], }) export class ProductModule {} diff --git a/src/product/services/product.service.ts b/src/product/services/product.service.ts index cebf9ba..4b9377c 100644 --- a/src/product/services/product.service.ts +++ b/src/product/services/product.service.ts @@ -21,4 +21,23 @@ export class ProductService { message: 'List of products retrieved successfully', }); } + + async findOne(productUuid: string): Promise { + const product = await this.productRepository.findOne({ + where: { + uuid: productUuid, + }, + }); + if (!product) { + throw new HttpException( + `No product with ${productUuid} found in the system`, + HttpStatus.NOT_FOUND, + ); + } + + return new SuccessResponseDto({ + data: product, + message: 'Succefully retrieved product', + }); + } } diff --git a/src/space-model/common/index.ts b/src/space-model/common/index.ts new file mode 100644 index 0000000..e371345 --- /dev/null +++ b/src/space-model/common/index.ts @@ -0,0 +1 @@ +export * from './services'; diff --git a/src/space-model/common/services/base-product-item-model.service.ts b/src/space-model/common/services/base-product-item-model.service.ts new file mode 100644 index 0000000..80c8119 --- /dev/null +++ b/src/space-model/common/services/base-product-item-model.service.ts @@ -0,0 +1,60 @@ +import { HttpException, HttpStatus } from '@nestjs/common'; +import { QueryRunner } from 'typeorm'; +import { SpaceModelEntity } from '@app/common/modules/space-model'; +import { CreateProductItemModelDto } from 'src/space-model/dtos'; + +export abstract class BaseProductItemService { + async validateTags( + itemModelDtos: CreateProductItemModelDto[], + queryRunner: QueryRunner, + spaceModel: SpaceModelEntity, + ): Promise { + const incomingTags = new Set( + itemModelDtos.map((item) => item.tag).filter(Boolean), + ); + + const duplicateTags = itemModelDtos + .map((item) => item.tag) + .filter((tag, index, array) => array.indexOf(tag) !== index); + + if (duplicateTags.length > 0) { + throw new HttpException( + `Duplicate tags found in the request: ${[...new Set(duplicateTags)].join(', ')}`, + HttpStatus.BAD_REQUEST, + ); + } + + const existingTagsQuery = ` + SELECT DISTINCT tag + FROM ( + SELECT spi.tag + FROM "subspace-product-item-model" spi + INNER JOIN "subspace-product-model" spm ON spi.subspace_product_model_uuid = spm.uuid + INNER JOIN "subspace-model" sm ON spm.subspace_model_uuid = sm.uuid + WHERE sm.space_model_uuid = $1 + UNION + SELECT spi.tag + FROM "space-product-item-model" spi + INNER JOIN "space-product-model" spm ON spi.space_product_model_uuid = spm.uuid + WHERE spm.space_model_uuid = $1 + ) AS combined_tags; + `; + + const existingTags = await queryRunner.manager.query(existingTagsQuery, [ + spaceModel.uuid, + ]); + const existingTagsSet = new Set( + existingTags.map((row: { tag: string }) => row.tag), + ); + + const conflictingTags = [...incomingTags].filter((tag) => + existingTagsSet.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/common/services/base-product-model.service.ts b/src/space-model/common/services/base-product-model.service.ts new file mode 100644 index 0000000..b83511a --- /dev/null +++ b/src/space-model/common/services/base-product-model.service.ts @@ -0,0 +1,24 @@ +import { HttpException, HttpStatus } from '@nestjs/common'; +import { CreateSpaceProductModelDto } from 'src/space-model/dtos'; +import { ProductService } from '../../../product/services'; + +export abstract class BaseProductModelService { + constructor(private readonly productService: ProductService) {} + + protected async validateProductCount( + dto: CreateSpaceProductModelDto, + ): Promise { + const productItemCount = dto.items.length; + if (dto.productCount !== productItemCount) { + throw new HttpException( + `Product count (${dto.productCount}) does not match the number of items (${productItemCount}) for product ID ${dto.productUuid}.`, + HttpStatus.BAD_REQUEST, + ); + } + } + + protected async getProduct(productId: string) { + const product = await this.productService.findOne(productId); + return product.data; + } +} diff --git a/src/space-model/common/services/index.ts b/src/space-model/common/services/index.ts new file mode 100644 index 0000000..d1cc61e --- /dev/null +++ b/src/space-model/common/services/index.ts @@ -0,0 +1,2 @@ +export * from './base-product-item-model.service'; +export * from './base-product-model.service'; diff --git a/src/space-model/dtos/create-subspace-model.dto.ts b/src/space-model/dtos/create-subspace-model.dto.ts index a27ad3b..f8dbcdd 100644 --- a/src/space-model/dtos/create-subspace-model.dto.ts +++ b/src/space-model/dtos/create-subspace-model.dto.ts @@ -1,5 +1,13 @@ import { ApiProperty } from '@nestjs/swagger'; -import { IsNotEmpty, IsString } from 'class-validator'; +import { + IsArray, + IsNotEmpty, + IsOptional, + IsString, + ValidateNested, +} from 'class-validator'; +import { CreateSpaceProductModelDto } from './create-space-product-model.dto'; +import { Type } from 'class-transformer'; export class CreateSubspaceModelDto { @ApiProperty({ @@ -9,4 +17,14 @@ export class CreateSubspaceModelDto { @IsNotEmpty() @IsString() subspaceName: string; + + @ApiProperty({ + description: 'List of products included in the model', + type: [CreateSpaceProductModelDto], + }) + @IsArray() + @IsOptional() + @ValidateNested({ each: true }) + @Type(() => CreateSpaceProductModelDto) + spaceProductModels?: CreateSpaceProductModelDto[]; } diff --git a/src/space-model/services/index.ts b/src/space-model/services/index.ts index 88e2d41..9972aab 100644 --- a/src/space-model/services/index.ts +++ b/src/space-model/services/index.ts @@ -2,3 +2,5 @@ export * from './space-model.service'; export * from './space-product-item-model.service'; export * from './space-product-model.service'; export * from './subspace-model.service'; +export * from './subspace-product-item-model.service'; +export * from './space-product-model.service'; 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 f7f8b04..c69cae8 100644 --- a/src/space-model/services/space-product-item-model.service.ts +++ b/src/space-model/services/space-product-item-model.service.ts @@ -6,12 +6,15 @@ import { import { HttpException, HttpStatus, Injectable } from '@nestjs/common'; import { CreateProductItemModelDto } from '../dtos'; import { QueryRunner } from 'typeorm'; +import { BaseProductItemService } from '../common'; @Injectable() -export class SpaceProductItemModelService { +export class SpaceProductItemModelService extends BaseProductItemService { constructor( private readonly spaceProductItemRepository: SpaceProductItemModelRepository, - ) {} + ) { + super(); + } async createProdutItemModel( itemModelDtos: CreateProductItemModelDto[], @@ -41,41 +44,4 @@ export class SpaceProductItemModelService { ); } } - - private async validateTags( - itemModelDtos: CreateProductItemModelDto[], - queryRunner: QueryRunner, - 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 the request: ${[...new Set(duplicateTags)].join(', ')}`, - HttpStatus.BAD_REQUEST, - ); - } - - const existingTags = await queryRunner.manager.find( - this.spaceProductItemRepository.target, - { - where: { spaceProductModel: { 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 index 27bad29..1e16c6f 100644 --- a/src/space-model/services/space-product-model.service.ts +++ b/src/space-model/services/space-product-model.service.ts @@ -4,17 +4,20 @@ import { } 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'; import { QueryRunner } from 'typeorm'; +import { BaseProductModelService } from '../common'; +import { ProductService } from 'src/product/services'; @Injectable() -export class SpaceProductModelService { +export class SpaceProductModelService extends BaseProductModelService { constructor( private readonly spaceProductModelRepository: SpaceProductModelRepository, - private readonly productRepository: ProductRepository, private readonly spaceProductItemModelService: SpaceProductItemModelService, - ) {} + productService: ProductService, + ) { + super(productService); + } async createSpaceProductModels( spaceProductModelDtos: CreateSpaceProductModelDto[], @@ -61,25 +64,4 @@ export class SpaceProductModelService { ); } } - - private validateProductCount(dto: CreateSpaceProductModelDto) { - const productItemCount = dto.items.length; - if (dto.productCount !== productItemCount) { - throw new HttpException( - `Product count (${dto.productCount}) does not match the number of items (${productItemCount}) for product ID ${dto.productUuid}.`, - 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 a6a75aa..11a501c 100644 --- a/src/space-model/services/subspace-model.service.ts +++ b/src/space-model/services/subspace-model.service.ts @@ -5,11 +5,13 @@ import { import { HttpException, HttpStatus, Injectable } from '@nestjs/common'; import { CreateSubspaceModelDto } from '../dtos'; import { QueryRunner } from 'typeorm'; +import { SubspaceProductModelService } from './subspace-product-model.service'; @Injectable() export class SubSpaceModelService { constructor( private readonly subspaceModelRepository: SubspaceModelRepository, + private readonly subSpaceProducetModelService: SubspaceProductModelService, ) {} async createSubSpaceModels( @@ -28,6 +30,18 @@ export class SubSpaceModelService { ); await queryRunner.manager.save(subspaces); + + await Promise.all( + subSpaceModelDtos.map((dto, index) => { + const subspaceModel = subspaces[index]; + return this.subSpaceProducetModelService.createSubspaceProductModels( + dto.spaceProductModels, + spaceModel, + subspaceModel, + queryRunner, + ); + }), + ); } catch (error) { if (error instanceof HttpException) { throw error; diff --git a/src/space-model/services/subspace-product-item-model.service.ts b/src/space-model/services/subspace-product-item-model.service.ts new file mode 100644 index 0000000..54c76ff --- /dev/null +++ b/src/space-model/services/subspace-product-item-model.service.ts @@ -0,0 +1,53 @@ +import { HttpException, HttpStatus, Injectable } from '@nestjs/common'; +import { CreateProductItemModelDto } from '../dtos'; +import { QueryRunner } from 'typeorm'; +import { + SpaceModelEntity, + SubspaceProductItemModelRepository, + SubspaceProductModelEntity, +} from '@app/common/modules/space-model'; +import { BaseProductItemService } from '../common'; + +@Injectable() +export class SubspaceProductItemModelService extends BaseProductItemService { + constructor( + private readonly subspaceProductItemRepository: SubspaceProductItemModelRepository, + ) { + super(); + } + + async createProdutItemModel( + itemModelDtos: CreateProductItemModelDto[], + subspaceProductModel: SubspaceProductModelEntity, + spaceModel: SpaceModelEntity, + queryRunner: QueryRunner, + ) { + if (!subspaceProductModel) { + throw new HttpException( + 'The spaceProductModel parameter is required but was not provided.', + HttpStatus.BAD_REQUEST, + ); + } + await this.validateTags(itemModelDtos, queryRunner, spaceModel); + try { + const productItems = itemModelDtos.map((dto) => + queryRunner.manager.create(this.subspaceProductItemRepository.target, { + tag: dto.tag, + subspaceProductModel, + }), + ); + + await queryRunner.manager.save(productItems); + } catch (error) { + if (error instanceof HttpException) { + throw error; + } + + throw new HttpException( + error.message || + 'An unexpected error occurred while creating product items.', + HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + } +} diff --git a/src/space-model/services/subspace-product-model.service.ts b/src/space-model/services/subspace-product-model.service.ts new file mode 100644 index 0000000..ccf3d11 --- /dev/null +++ b/src/space-model/services/subspace-product-model.service.ts @@ -0,0 +1,69 @@ +import { + SpaceModelEntity, + SubspaceModelEntity, + SubspaceProductModelRepository, +} from '@app/common/modules/space-model'; +import { HttpException, HttpStatus, Injectable } from '@nestjs/common'; +import { SubspaceProductItemModelService } from './subspace-product-item-model.service'; +import { CreateSpaceProductModelDto } from '../dtos'; +import { QueryRunner } from 'typeorm'; +import { BaseProductModelService } from '../common'; +import { ProductService } from 'src/product/services'; + +@Injectable() +export class SubspaceProductModelService extends BaseProductModelService { + constructor( + private readonly subpaceProductModelRepository: SubspaceProductModelRepository, + productService: ProductService, + private readonly subspaceProductItemModelService: SubspaceProductItemModelService, + ) { + super(productService); + } + + async createSubspaceProductModels( + spaceProductModelDtos: CreateSpaceProductModelDto[], + spaceModel: SpaceModelEntity, + subspaceModel: SubspaceModelEntity, + queryRunner: QueryRunner, + ) { + try { + const productModels = await Promise.all( + spaceProductModelDtos.map(async (dto) => { + this.validateProductCount(dto); + const product = await this.getProduct(dto.productUuid); + return queryRunner.manager.create( + this.subpaceProductModelRepository.target, + { + product, + productCount: dto.productCount, + subspaceModel, + }, + ); + }), + ); + + const savedProductModels = await queryRunner.manager.save(productModels); + + await Promise.all( + spaceProductModelDtos.map((dto, index) => { + const savedModel = savedProductModels[index]; + return this.subspaceProductItemModelService.createProdutItemModel( + dto.items, + savedModel, // Pass the saved model + spaceModel, + queryRunner, + ); + }), + ); + } catch (error) { + if (error instanceof HttpException) { + throw error; + } + throw new HttpException( + error.message || + 'An unexpected error occurred while creating product models.', + HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + } +} diff --git a/src/space-model/space-model.module.ts b/src/space-model/space-model.module.ts index bbc6245..5c189a1 100644 --- a/src/space-model/space-model.module.ts +++ b/src/space-model/space-model.module.ts @@ -7,15 +7,19 @@ import { SpaceProductItemModelService, SpaceProductModelService, SubSpaceModelService, + SubspaceProductItemModelService, } from './services'; import { SpaceModelRepository, SpaceProductItemModelRepository, SpaceProductModelRepository, SubspaceModelRepository, + SubspaceProductItemModelRepository, + SubspaceProductModelRepository, } from '@app/common/modules/space-model'; import { ProjectRepository } from '@app/common/modules/project/repositiories'; import { ProductRepository } from '@app/common/modules/product/repositories'; +import { SubspaceProductModelService } from './services/subspace-product-model.service'; @Module({ imports: [ConfigModule, SpaceRepositoryModule], @@ -31,6 +35,10 @@ import { ProductRepository } from '@app/common/modules/product/repositories'; ProductRepository, SpaceProductItemModelService, SpaceProductItemModelRepository, + SubspaceProductItemModelService, + SubspaceProductItemModelRepository, + SubspaceProductModelService, + SubspaceProductModelRepository, ], exports: [], }) From 4fb4e32c3d0a45472c685760fa9fe4ed2478cbdd Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Fri, 13 Dec 2024 15:08:50 +0400 Subject: [PATCH 17/26] folder restructuring --- src/space-model/services/index.ts | 4 +--- src/space-model/services/space-model.service.ts | 2 +- .../services/space-product-model.service.ts | 12 ++++++------ src/space-model/services/subspace/index.ts | 3 +++ .../{ => subspace}/subspace-model.service.ts | 2 +- .../subspace-product-item-model.service.ts | 4 ++-- .../{ => subspace}/subspace-product-model.service.ts | 4 ++-- src/space-model/space-model.module.ts | 2 +- 8 files changed, 17 insertions(+), 16 deletions(-) create mode 100644 src/space-model/services/subspace/index.ts rename src/space-model/services/{ => subspace}/subspace-model.service.ts (97%) rename src/space-model/services/{ => subspace}/subspace-product-item-model.service.ts (93%) rename src/space-model/services/{ => subspace}/subspace-product-model.service.ts (95%) diff --git a/src/space-model/services/index.ts b/src/space-model/services/index.ts index 9972aab..5c39727 100644 --- a/src/space-model/services/index.ts +++ b/src/space-model/services/index.ts @@ -1,6 +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'; -export * from './subspace-product-item-model.service'; -export * from './space-product-model.service'; +export * from './subspace'; diff --git a/src/space-model/services/space-model.service.ts b/src/space-model/services/space-model.service.ts index 7359e25..0423c42 100644 --- a/src/space-model/services/space-model.service.ts +++ b/src/space-model/services/space-model.service.ts @@ -4,7 +4,7 @@ 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 { SubSpaceModelService } from './subspace/subspace-model.service'; import { SpaceProductModelService } from './space-product-model.service'; import { DataSource } from 'typeorm'; diff --git a/src/space-model/services/space-product-model.service.ts b/src/space-model/services/space-product-model.service.ts index 1e16c6f..0f1e93e 100644 --- a/src/space-model/services/space-product-model.service.ts +++ b/src/space-model/services/space-product-model.service.ts @@ -1,13 +1,13 @@ +import { QueryRunner } from 'typeorm'; +import { HttpException, HttpStatus, Injectable } from '@nestjs/common'; +import { CreateSpaceProductModelDto } from '../dtos'; +import { SpaceProductItemModelService } from './space-product-item-model.service'; +import { BaseProductModelService } from '../common'; +import { ProductService } from 'src/product/services'; import { SpaceModelEntity, SpaceProductModelRepository, } from '@app/common/modules/space-model'; -import { HttpException, HttpStatus, Injectable } from '@nestjs/common'; -import { CreateSpaceProductModelDto } from '../dtos'; -import { SpaceProductItemModelService } from './space-product-item-model.service'; -import { QueryRunner } from 'typeorm'; -import { BaseProductModelService } from '../common'; -import { ProductService } from 'src/product/services'; @Injectable() export class SpaceProductModelService extends BaseProductModelService { diff --git a/src/space-model/services/subspace/index.ts b/src/space-model/services/subspace/index.ts new file mode 100644 index 0000000..78d7cd3 --- /dev/null +++ b/src/space-model/services/subspace/index.ts @@ -0,0 +1,3 @@ +export * from './subspace-model.service'; +export * from './subspace-product-item-model.service'; +export * from './subspace-product-model.service'; diff --git a/src/space-model/services/subspace-model.service.ts b/src/space-model/services/subspace/subspace-model.service.ts similarity index 97% rename from src/space-model/services/subspace-model.service.ts rename to src/space-model/services/subspace/subspace-model.service.ts index 11a501c..222dee4 100644 --- a/src/space-model/services/subspace-model.service.ts +++ b/src/space-model/services/subspace/subspace-model.service.ts @@ -3,7 +3,7 @@ import { SubspaceModelRepository, } from '@app/common/modules/space-model'; import { HttpException, HttpStatus, Injectable } from '@nestjs/common'; -import { CreateSubspaceModelDto } from '../dtos'; +import { CreateSubspaceModelDto } from '../../dtos'; import { QueryRunner } from 'typeorm'; import { SubspaceProductModelService } from './subspace-product-model.service'; diff --git a/src/space-model/services/subspace-product-item-model.service.ts b/src/space-model/services/subspace/subspace-product-item-model.service.ts similarity index 93% rename from src/space-model/services/subspace-product-item-model.service.ts rename to src/space-model/services/subspace/subspace-product-item-model.service.ts index 54c76ff..393a5f3 100644 --- a/src/space-model/services/subspace-product-item-model.service.ts +++ b/src/space-model/services/subspace/subspace-product-item-model.service.ts @@ -1,12 +1,12 @@ import { HttpException, HttpStatus, Injectable } from '@nestjs/common'; -import { CreateProductItemModelDto } from '../dtos'; import { QueryRunner } from 'typeorm'; import { SpaceModelEntity, SubspaceProductItemModelRepository, SubspaceProductModelEntity, } from '@app/common/modules/space-model'; -import { BaseProductItemService } from '../common'; +import { BaseProductItemService } from '../../common'; +import { CreateProductItemModelDto } from '../../dtos'; @Injectable() export class SubspaceProductItemModelService extends BaseProductItemService { diff --git a/src/space-model/services/subspace-product-model.service.ts b/src/space-model/services/subspace/subspace-product-model.service.ts similarity index 95% rename from src/space-model/services/subspace-product-model.service.ts rename to src/space-model/services/subspace/subspace-product-model.service.ts index ccf3d11..30f9062 100644 --- a/src/space-model/services/subspace-product-model.service.ts +++ b/src/space-model/services/subspace/subspace-product-model.service.ts @@ -5,9 +5,9 @@ import { } from '@app/common/modules/space-model'; import { HttpException, HttpStatus, Injectable } from '@nestjs/common'; import { SubspaceProductItemModelService } from './subspace-product-item-model.service'; -import { CreateSpaceProductModelDto } from '../dtos'; +import { CreateSpaceProductModelDto } from '../../dtos'; import { QueryRunner } from 'typeorm'; -import { BaseProductModelService } from '../common'; +import { BaseProductModelService } from '../../common'; import { ProductService } from 'src/product/services'; @Injectable() diff --git a/src/space-model/space-model.module.ts b/src/space-model/space-model.module.ts index 5c189a1..ae275e2 100644 --- a/src/space-model/space-model.module.ts +++ b/src/space-model/space-model.module.ts @@ -19,7 +19,7 @@ import { } from '@app/common/modules/space-model'; import { ProjectRepository } from '@app/common/modules/project/repositiories'; import { ProductRepository } from '@app/common/modules/product/repositories'; -import { SubspaceProductModelService } from './services/subspace-product-model.service'; +import { SubspaceProductModelService } from './services/subspace/subspace-product-model.service'; @Module({ imports: [ConfigModule, SpaceRepositoryModule], From d268a81d851000d2ea15cc85e2801e6728b8a190 Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Fri, 13 Dec 2024 15:30:38 +0400 Subject: [PATCH 18/26] added subspace product --- libs/common/src/database/database.module.ts | 2 ++ .../dtos/subspace/subspace-product.dto.ts | 15 +++++++++ .../modules/space/entities/subspace/index.ts | 1 + .../subspace/subspace-product.entity.ts | 31 +++++++++++++++++++ .../entities/subspace/subspace.entity.ts | 10 ++++++ .../modules/space/space.repository.module.ts | 10 ++++-- 6 files changed, 67 insertions(+), 2 deletions(-) create mode 100644 libs/common/src/modules/space/dtos/subspace/subspace-product.dto.ts diff --git a/libs/common/src/database/database.module.ts b/libs/common/src/database/database.module.ts index 3681bed..2f87d05 100644 --- a/libs/common/src/database/database.module.ts +++ b/libs/common/src/database/database.module.ts @@ -13,6 +13,7 @@ import { SpaceLinkEntity, SpaceProductItemEntity, SubspaceEntity, + SubspaceProductEntity, } from '../modules/space/entities'; import { UserSpaceEntity } from '../modules/user/entities'; import { DeviceUserPermissionEntity } from '../modules/device/entities'; @@ -85,6 +86,7 @@ import { SpaceProductItemEntity, SubspaceProductModelEntity, SubspaceProductItemModelEntity, + SubspaceProductEntity, ], namingStrategy: new SnakeNamingStrategy(), synchronize: Boolean(JSON.parse(configService.get('DB_SYNC'))), diff --git a/libs/common/src/modules/space/dtos/subspace/subspace-product.dto.ts b/libs/common/src/modules/space/dtos/subspace/subspace-product.dto.ts new file mode 100644 index 0000000..4fd2db1 --- /dev/null +++ b/libs/common/src/modules/space/dtos/subspace/subspace-product.dto.ts @@ -0,0 +1,15 @@ +import { IsString, IsNotEmpty, IsNumber } from 'class-validator'; + +export class SubspaceProductDto { + @IsString() + @IsNotEmpty() + uuid: string; + + @IsNumber() + @IsNotEmpty() + productCount: number; + + @IsString() + @IsNotEmpty() + productUuid: string; +} diff --git a/libs/common/src/modules/space/entities/subspace/index.ts b/libs/common/src/modules/space/entities/subspace/index.ts index be13961..bd9d689 100644 --- a/libs/common/src/modules/space/entities/subspace/index.ts +++ b/libs/common/src/modules/space/entities/subspace/index.ts @@ -1 +1,2 @@ export * from './subspace.entity'; +export * from './subspace-product.entity'; diff --git a/libs/common/src/modules/space/entities/subspace/subspace-product.entity.ts b/libs/common/src/modules/space/entities/subspace/subspace-product.entity.ts index e69de29..f9308ae 100644 --- a/libs/common/src/modules/space/entities/subspace/subspace-product.entity.ts +++ b/libs/common/src/modules/space/entities/subspace/subspace-product.entity.ts @@ -0,0 +1,31 @@ +import { ProductEntity } from '@app/common/modules/product/entities'; +import { Column, Entity, ManyToOne } from 'typeorm'; +import { SubspaceEntity } from './subspace.entity'; +import { AbstractEntity } from '@app/common/modules/abstract/entities/abstract.entity'; +import { SubspaceProductDto } from '../../dtos/subspace/subspace-product.dto'; + +@Entity({ name: 'subspace-product' }) +export class SubspaceProductEntity extends AbstractEntity { + @Column({ + type: 'uuid', + default: () => 'gen_random_uuid()', + nullable: false, + }) + public uuid: string; + + @Column({ + nullable: false, + type: 'int', + }) + productCount: number; + + @ManyToOne(() => SubspaceEntity, (subspace) => subspace.subspaceProducts, { + nullable: false, + }) + public subspace: SubspaceEntity; + + @ManyToOne(() => ProductEntity, (product) => product.subpaceProductModels, { + nullable: false, + }) + public product: ProductEntity; +} diff --git a/libs/common/src/modules/space/entities/subspace/subspace.entity.ts b/libs/common/src/modules/space/entities/subspace/subspace.entity.ts index 8f0d15d..932757d 100644 --- a/libs/common/src/modules/space/entities/subspace/subspace.entity.ts +++ b/libs/common/src/modules/space/entities/subspace/subspace.entity.ts @@ -4,6 +4,7 @@ import { SubspaceModelEntity } from '@app/common/modules/space-model'; import { Column, Entity, JoinColumn, ManyToOne, OneToMany } from 'typeorm'; import { SubspaceDto } from '../../dtos'; import { SpaceEntity } from '../space.entity'; +import { SubspaceProductEntity } from './subspace-product.entity'; @Entity({ name: 'subspace' }) export class SubspaceEntity extends AbstractEntity { @@ -35,6 +36,15 @@ export class SubspaceEntity extends AbstractEntity { @JoinColumn({ name: 'subspace_model_uuid' }) subSpaceModel?: SubspaceModelEntity; + @OneToMany( + () => SubspaceProductEntity, + (subspaceProduct) => subspaceProduct.subspace, + { + nullable: true, + }, + ) + public subspaceProducts: SubspaceProductEntity[]; + constructor(partial: Partial) { super(); Object.assign(this, partial); diff --git a/libs/common/src/modules/space/space.repository.module.ts b/libs/common/src/modules/space/space.repository.module.ts index 90916c2..b39f98d 100644 --- a/libs/common/src/modules/space/space.repository.module.ts +++ b/libs/common/src/modules/space/space.repository.module.ts @@ -1,11 +1,17 @@ import { Module } from '@nestjs/common'; import { TypeOrmModule } from '@nestjs/typeorm'; -import { SpaceEntity, SubspaceEntity } from './entities'; +import { SpaceEntity, SubspaceEntity, SubspaceProductEntity } from './entities'; @Module({ providers: [], exports: [], controllers: [], - imports: [TypeOrmModule.forFeature([SpaceEntity, SubspaceEntity])], + imports: [ + TypeOrmModule.forFeature([ + SpaceEntity, + SubspaceEntity, + SubspaceProductEntity, + ]), + ], }) export class SpaceRepositoryModule {} From 05b2bddc26035da49a3c99573ebcfa93a6e98ad0 Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Fri, 13 Dec 2024 15:39:13 +0400 Subject: [PATCH 19/26] added subspace product item --- libs/common/src/database/database.module.ts | 2 ++ .../modules/space/entities/subspace/index.ts | 1 + .../subspace/subspace-product-item.entity.ts | 21 +++++++++++++++++++ .../subspace/subspace-product.entity.ts | 8 ++++++- 4 files changed, 31 insertions(+), 1 deletion(-) diff --git a/libs/common/src/database/database.module.ts b/libs/common/src/database/database.module.ts index 2f87d05..3da6ed6 100644 --- a/libs/common/src/database/database.module.ts +++ b/libs/common/src/database/database.module.ts @@ -14,6 +14,7 @@ import { SpaceProductItemEntity, SubspaceEntity, SubspaceProductEntity, + SubspaceProductItemEntity, } from '../modules/space/entities'; import { UserSpaceEntity } from '../modules/user/entities'; import { DeviceUserPermissionEntity } from '../modules/device/entities'; @@ -87,6 +88,7 @@ import { SubspaceProductModelEntity, SubspaceProductItemModelEntity, SubspaceProductEntity, + SubspaceProductItemEntity, ], namingStrategy: new SnakeNamingStrategy(), synchronize: Boolean(JSON.parse(configService.get('DB_SYNC'))), diff --git a/libs/common/src/modules/space/entities/subspace/index.ts b/libs/common/src/modules/space/entities/subspace/index.ts index bd9d689..471b7b1 100644 --- a/libs/common/src/modules/space/entities/subspace/index.ts +++ b/libs/common/src/modules/space/entities/subspace/index.ts @@ -1,2 +1,3 @@ export * from './subspace.entity'; export * from './subspace-product.entity'; +export * from './subspace-product-item.entity'; diff --git a/libs/common/src/modules/space/entities/subspace/subspace-product-item.entity.ts b/libs/common/src/modules/space/entities/subspace/subspace-product-item.entity.ts index e69de29..671b046 100644 --- a/libs/common/src/modules/space/entities/subspace/subspace-product-item.entity.ts +++ b/libs/common/src/modules/space/entities/subspace/subspace-product-item.entity.ts @@ -0,0 +1,21 @@ +import { AbstractEntity } from '@app/common/modules/abstract/entities/abstract.entity'; +import { SpaceProductItemDto } from '../../dtos'; +import { Column, Entity, ManyToOne } from 'typeorm'; +import { SubspaceProductEntity } from './subspace-product.entity'; + +@Entity({ name: 'subspace-product-item' }) +export class SubspaceProductItemEntity extends AbstractEntity { + @Column({ + nullable: false, + }) + public tag: string; + + @ManyToOne( + () => SubspaceProductEntity, + (subspaceProduct) => subspaceProduct.items, + { + nullable: false, + }, + ) + public subspaceProduct: SubspaceProductEntity; +} diff --git a/libs/common/src/modules/space/entities/subspace/subspace-product.entity.ts b/libs/common/src/modules/space/entities/subspace/subspace-product.entity.ts index f9308ae..f5d0eda 100644 --- a/libs/common/src/modules/space/entities/subspace/subspace-product.entity.ts +++ b/libs/common/src/modules/space/entities/subspace/subspace-product.entity.ts @@ -1,8 +1,9 @@ import { ProductEntity } from '@app/common/modules/product/entities'; -import { Column, Entity, ManyToOne } from 'typeorm'; +import { Column, Entity, ManyToOne, OneToMany } from 'typeorm'; import { SubspaceEntity } from './subspace.entity'; import { AbstractEntity } from '@app/common/modules/abstract/entities/abstract.entity'; import { SubspaceProductDto } from '../../dtos/subspace/subspace-product.dto'; +import { SubspaceProductItemEntity } from './subspace-product-item.entity'; @Entity({ name: 'subspace-product' }) export class SubspaceProductEntity extends AbstractEntity { @@ -28,4 +29,9 @@ export class SubspaceProductEntity extends AbstractEntity { nullable: false, }) public product: ProductEntity; + + @OneToMany(() => SubspaceProductItemEntity, (item) => item.subspaceProduct, { + nullable: true, + }) + public items: SubspaceProductItemEntity[]; } From 654642db6cc261a4e0c3a2d3745276e3ff8f2f6a Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Fri, 13 Dec 2024 15:43:34 +0400 Subject: [PATCH 20/26] added relation between subspace product item and model --- .../subspace-model/subspace-product-item-model.entity.ts | 8 +++++++- .../entities/subspace/subspace-product-item.entity.ts | 6 ++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/libs/common/src/modules/space-model/entities/subspace-model/subspace-product-item-model.entity.ts b/libs/common/src/modules/space-model/entities/subspace-model/subspace-product-item-model.entity.ts index decd0a7..3821e5c 100644 --- a/libs/common/src/modules/space-model/entities/subspace-model/subspace-product-item-model.entity.ts +++ b/libs/common/src/modules/space-model/entities/subspace-model/subspace-product-item-model.entity.ts @@ -1,7 +1,8 @@ import { AbstractEntity } from '@app/common/modules/abstract/entities/abstract.entity'; -import { Entity, Column, ManyToOne } from 'typeorm'; +import { Entity, Column, ManyToOne, OneToMany } from 'typeorm'; import { SubspaceProductItemModelDto } from '../../dtos'; import { SubspaceProductModelEntity } from './subspace-product-model.entity'; +import { SubspaceProductItemEntity } from '@app/common/modules/space/entities'; @Entity({ name: 'subspace-product-item-model' }) export class SubspaceProductItemModelEntity extends AbstractEntity { @@ -18,4 +19,9 @@ export class SubspaceProductItemModelEntity extends AbstractEntity SubspaceProductItemEntity, (item) => item.model, { + nullable: true, + }) + items: SubspaceProductItemEntity[]; } diff --git a/libs/common/src/modules/space/entities/subspace/subspace-product-item.entity.ts b/libs/common/src/modules/space/entities/subspace/subspace-product-item.entity.ts index 671b046..5041053 100644 --- a/libs/common/src/modules/space/entities/subspace/subspace-product-item.entity.ts +++ b/libs/common/src/modules/space/entities/subspace/subspace-product-item.entity.ts @@ -2,6 +2,7 @@ import { AbstractEntity } from '@app/common/modules/abstract/entities/abstract.e import { SpaceProductItemDto } from '../../dtos'; import { Column, Entity, ManyToOne } from 'typeorm'; import { SubspaceProductEntity } from './subspace-product.entity'; +import { SubspaceProductItemModelEntity } from '@app/common/modules/space-model'; @Entity({ name: 'subspace-product-item' }) export class SubspaceProductItemEntity extends AbstractEntity { @@ -18,4 +19,9 @@ export class SubspaceProductItemEntity extends AbstractEntity SubspaceProductItemModelEntity, (model) => model.items, { + nullable: true, + }) + model: SubspaceProductItemModelEntity; } From 7cab62b02ae703bbfdae024877c8174095c35389 Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Fri, 13 Dec 2024 15:57:25 +0400 Subject: [PATCH 21/26] added subspace product item --- .../subspace-product-item-model.entity.ts | 4 ++-- .../subspace-product-model.entity.ts | 12 +++++++++--- .../space/dtos/subspace/subspace-product.dto.ts | 15 --------------- .../subspace/subspace-product-item.entity.ts | 5 +++++ .../subspace/subspace-product.entity.ts | 17 +++++++++++++++-- 5 files changed, 31 insertions(+), 22 deletions(-) delete mode 100644 libs/common/src/modules/space/dtos/subspace/subspace-product.dto.ts diff --git a/libs/common/src/modules/space-model/entities/subspace-model/subspace-product-item-model.entity.ts b/libs/common/src/modules/space-model/entities/subspace-model/subspace-product-item-model.entity.ts index 3821e5c..2b6d305 100644 --- a/libs/common/src/modules/space-model/entities/subspace-model/subspace-product-item-model.entity.ts +++ b/libs/common/src/modules/space-model/entities/subspace-model/subspace-product-item-model.entity.ts @@ -13,14 +13,14 @@ export class SubspaceProductItemModelEntity extends AbstractEntity SubspaceProductModelEntity, - (subspaceProductModel) => subspaceProductModel.items, + (productModel) => productModel.itemModels, { nullable: false, }, ) public subspaceProductModel: SubspaceProductModelEntity; - @OneToMany(() => SubspaceProductItemEntity, (item) => item.model, { + @OneToMany(() => SubspaceProductItemEntity, (item) => item.subspaceProduct, { nullable: true, }) items: SubspaceProductItemEntity[]; diff --git a/libs/common/src/modules/space-model/entities/subspace-model/subspace-product-model.entity.ts b/libs/common/src/modules/space-model/entities/subspace-model/subspace-product-model.entity.ts index 3a5ab1f..6e22a35 100644 --- a/libs/common/src/modules/space-model/entities/subspace-model/subspace-product-model.entity.ts +++ b/libs/common/src/modules/space-model/entities/subspace-model/subspace-product-model.entity.ts @@ -3,6 +3,7 @@ import { SubpaceProductModelDto } from '../../dtos'; import { AbstractEntity } from '@app/common/modules/abstract/entities/abstract.entity'; import { SubspaceModelEntity } from './subspace-model.entity'; import { ProductEntity } from '@app/common/modules/product/entities'; +import { SubspaceProductEntity } from '@app/common/modules/space/entities'; import { SubspaceProductItemModelEntity } from './subspace-product-item-model.entity'; @Entity({ name: 'subspace-product-model' }) @@ -27,12 +28,17 @@ export class SubspaceProductModelEntity extends AbstractEntity SubspaceProductEntity, (product) => product.model, { + nullable: true, + }) + public subspaceProducts: SubspaceProductEntity[]; + @OneToMany( () => SubspaceProductItemModelEntity, - (item) => item.subspaceProductModel, + (product) => product.subspaceProductModel, { - cascade: true, + nullable: true, }, ) - public items: SubspaceProductItemModelEntity[]; + public itemModels: SubspaceProductItemModelEntity[]; } diff --git a/libs/common/src/modules/space/dtos/subspace/subspace-product.dto.ts b/libs/common/src/modules/space/dtos/subspace/subspace-product.dto.ts deleted file mode 100644 index 4fd2db1..0000000 --- a/libs/common/src/modules/space/dtos/subspace/subspace-product.dto.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { IsString, IsNotEmpty, IsNumber } from 'class-validator'; - -export class SubspaceProductDto { - @IsString() - @IsNotEmpty() - uuid: string; - - @IsNumber() - @IsNotEmpty() - productCount: number; - - @IsString() - @IsNotEmpty() - productUuid: string; -} diff --git a/libs/common/src/modules/space/entities/subspace/subspace-product-item.entity.ts b/libs/common/src/modules/space/entities/subspace/subspace-product-item.entity.ts index 5041053..1ec7958 100644 --- a/libs/common/src/modules/space/entities/subspace/subspace-product-item.entity.ts +++ b/libs/common/src/modules/space/entities/subspace/subspace-product-item.entity.ts @@ -24,4 +24,9 @@ export class SubspaceProductItemEntity extends AbstractEntity) { + super(); + Object.assign(this, partial); + } } diff --git a/libs/common/src/modules/space/entities/subspace/subspace-product.entity.ts b/libs/common/src/modules/space/entities/subspace/subspace-product.entity.ts index f5d0eda..03531cc 100644 --- a/libs/common/src/modules/space/entities/subspace/subspace-product.entity.ts +++ b/libs/common/src/modules/space/entities/subspace/subspace-product.entity.ts @@ -2,11 +2,15 @@ import { ProductEntity } from '@app/common/modules/product/entities'; import { Column, Entity, ManyToOne, OneToMany } from 'typeorm'; import { SubspaceEntity } from './subspace.entity'; import { AbstractEntity } from '@app/common/modules/abstract/entities/abstract.entity'; -import { SubspaceProductDto } from '../../dtos/subspace/subspace-product.dto'; import { SubspaceProductItemEntity } from './subspace-product-item.entity'; +import { + SubspaceProductItemModelEntity, + SubspaceProductModelEntity, +} from '@app/common/modules/space-model'; +import { SpaceProductModelDto } from '../../dtos'; @Entity({ name: 'subspace-product' }) -export class SubspaceProductEntity extends AbstractEntity { +export class SubspaceProductEntity extends AbstractEntity { @Column({ type: 'uuid', default: () => 'gen_random_uuid()', @@ -34,4 +38,13 @@ export class SubspaceProductEntity extends AbstractEntity { nullable: true, }) public items: SubspaceProductItemEntity[]; + + @ManyToOne( + () => SubspaceProductModelEntity, + (model) => model.subspaceProducts, + { + nullable: true, + }, + ) + model: SubspaceProductItemModelEntity; } From 3099e905e28fbe56e3120d790ca1945ea224ef52 Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Sun, 15 Dec 2024 19:59:36 +0400 Subject: [PATCH 22/26] subspace product repository created --- libs/common/src/common.module.ts | 7 ++--- .../space/repositories/space.repository.ts | 7 ----- .../space/repositories/subspace.repository.ts | 28 +++++++++++++++++++ .../subspace/subspace-device.service.ts | 2 +- .../services/subspace/subspace.service.ts | 2 +- src/space/space.module.ts | 2 +- 6 files changed, 33 insertions(+), 15 deletions(-) create mode 100644 libs/common/src/modules/space/repositories/subspace.repository.ts diff --git a/libs/common/src/common.module.ts b/libs/common/src/common.module.ts index 9a3de9c..582510d 100644 --- a/libs/common/src/common.module.ts +++ b/libs/common/src/common.module.ts @@ -9,11 +9,7 @@ import { EmailService } from './util/email.service'; import { ErrorMessageService } from 'src/error-message/error-message.service'; import { TuyaService } from './integrations/tuya/services/tuya.service'; import { SceneDeviceRepository } from './modules/scene-device/repositories'; -import { - SpaceProductItemRepository, - SpaceRepository, - SubspaceRepository, -} from './modules/space'; +import { SpaceProductItemRepository, SpaceRepository } from './modules/space'; import { SpaceModelRepository, SpaceProductModelRepository, @@ -21,6 +17,7 @@ import { SubspaceProductItemModelRepository, SubspaceProductModelRepository, } from './modules/space-model'; +import { SubspaceRepository } from './modules/space/repositories/subspace.repository'; @Module({ providers: [ CommonService, diff --git a/libs/common/src/modules/space/repositories/space.repository.ts b/libs/common/src/modules/space/repositories/space.repository.ts index 677db0e..66f96a4 100644 --- a/libs/common/src/modules/space/repositories/space.repository.ts +++ b/libs/common/src/modules/space/repositories/space.repository.ts @@ -5,7 +5,6 @@ import { SpaceEntity, SpaceLinkEntity, SpaceProductItemEntity, - SubspaceEntity, } from '../entities'; @Injectable() @@ -14,12 +13,6 @@ export class SpaceRepository extends Repository { super(SpaceEntity, dataSource.createEntityManager()); } } -@Injectable() -export class SubspaceRepository extends Repository { - constructor(private dataSource: DataSource) { - super(SubspaceEntity, dataSource.createEntityManager()); - } -} @Injectable() export class SpaceLinkRepository extends Repository { diff --git a/libs/common/src/modules/space/repositories/subspace.repository.ts b/libs/common/src/modules/space/repositories/subspace.repository.ts new file mode 100644 index 0000000..3682c05 --- /dev/null +++ b/libs/common/src/modules/space/repositories/subspace.repository.ts @@ -0,0 +1,28 @@ +import { DataSource, Repository } from 'typeorm'; +import { + SubspaceEntity, + SubspaceProductEntity, + SubspaceProductItemEntity, +} from '../entities'; +import { Injectable } from '@nestjs/common'; + +@Injectable() +export class SubspaceRepository extends Repository { + constructor(private dataSource: DataSource) { + super(SubspaceEntity, dataSource.createEntityManager()); + } +} + +@Injectable() +export class SubspaceProductRepository extends Repository { + constructor(private dataSource: DataSource) { + super(SubspaceProductEntity, dataSource.createEntityManager()); + } +} + +@Injectable() +export class SubspaceProductItemRepository extends Repository { + constructor(private dataSource: DataSource) { + super(SubspaceProductItemEntity, dataSource.createEntityManager()); + } +} diff --git a/src/space/services/subspace/subspace-device.service.ts b/src/space/services/subspace/subspace-device.service.ts index 2bcfb93..d6f6320 100644 --- a/src/space/services/subspace/subspace-device.service.ts +++ b/src/space/services/subspace/subspace-device.service.ts @@ -1,7 +1,6 @@ import { BaseResponseDto } from '@app/common/dto/base.response.dto'; import { SuccessResponseDto } from '@app/common/dto/success.response.dto'; import { DeviceRepository } from '@app/common/modules/device/repositories'; -import { SubspaceRepository } from '@app/common/modules/space/repositories'; import { HttpException, HttpStatus, Injectable } from '@nestjs/common'; import { DeviceSubSpaceParam, GetSubSpaceParam } from '../../dtos'; import { convertKeysToCamelCase } from '@app/common/helper/camelCaseConverter'; @@ -9,6 +8,7 @@ import { TuyaService } from '@app/common/integrations/tuya/services/tuya.service import { ProductRepository } from '@app/common/modules/product/repositories'; import { GetDeviceDetailsInterface } from '../../../device/interfaces/get.device.interface'; import { ValidationService } from '../space-validation.service'; +import { SubspaceRepository } from '@app/common/modules/space/repositories/subspace.repository'; @Injectable() export class SubspaceDeviceService { diff --git a/src/space/services/subspace/subspace.service.ts b/src/space/services/subspace/subspace.service.ts index 4e9b2e3..96afa41 100644 --- a/src/space/services/subspace/subspace.service.ts +++ b/src/space/services/subspace/subspace.service.ts @@ -1,5 +1,4 @@ import { BaseResponseDto } from '@app/common/dto/base.response.dto'; -import { SubspaceRepository } from '@app/common/modules/space/repositories'; import { HttpException, HttpStatus, Injectable } from '@nestjs/common'; import { AddSubspaceDto, GetSpaceParam, GetSubSpaceParam } from '../../dtos'; import { SuccessResponseDto } from '@app/common/dto/success.response.dto'; @@ -16,6 +15,7 @@ import { } from '@app/common/modules/space/entities'; import { SpaceModelEntity } from '@app/common/modules/space-model'; import { ValidationService } from '../space-validation.service'; +import { SubspaceRepository } from '@app/common/modules/space/repositories/subspace.repository'; @Injectable() export class SubSpaceService { diff --git a/src/space/space.module.ts b/src/space/space.module.ts index 40a72d3..cd443e5 100644 --- a/src/space/space.module.ts +++ b/src/space/space.module.ts @@ -23,7 +23,6 @@ import { import { SpaceProductRepository, SpaceRepository, - SubspaceRepository, SpaceLinkRepository, SpaceProductItemRepository, } from '@app/common/modules/space/repositories'; @@ -48,6 +47,7 @@ import { ProjectRepository } from '@app/common/modules/project/repositiories'; import { SpaceModelRepository } from '@app/common/modules/space-model'; import { CommunityModule } from 'src/community/community.module'; import { ValidationService } from './services/space-validation.service'; +import { SubspaceRepository } from '@app/common/modules/space/repositories/subspace.repository'; @Module({ imports: [ConfigModule, SpaceRepositoryModule, CommunityModule], From b3e8af7540b5f5791983adfec0e3418fac909bf0 Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Tue, 17 Dec 2024 12:18:03 +0400 Subject: [PATCH 23/26] added subspace product item --- .../subspace-model/subspace-model.entity.ts | 2 +- .../subspace-product-model.entity.ts | 2 +- .../subspace/subspace-product.entity.ts | 2 +- src/space/dtos/subspace/add.subspace.dto.ts | 21 +++++- src/space/services/index.ts | 1 + .../services/space-validation.service.ts | 2 + src/space/services/subspace/index.ts | 2 + .../subspace/subspace-product-item.service.ts | 50 ++++++++++++++ .../subspace/subspace-product.service.ts | 66 +++++++++++++++++++ .../services/subspace/subspace.service.ts | 31 +++++++-- src/space/space.module.ts | 12 +++- 11 files changed, 179 insertions(+), 12 deletions(-) create mode 100644 src/space/services/subspace/subspace-product-item.service.ts create mode 100644 src/space/services/subspace/subspace-product.service.ts diff --git a/libs/common/src/modules/space-model/entities/subspace-model/subspace-model.entity.ts b/libs/common/src/modules/space-model/entities/subspace-model/subspace-model.entity.ts index 2d1a170..89fcf63 100644 --- a/libs/common/src/modules/space-model/entities/subspace-model/subspace-model.entity.ts +++ b/libs/common/src/modules/space-model/entities/subspace-model/subspace-model.entity.ts @@ -42,5 +42,5 @@ export class SubspaceModelEntity extends AbstractEntity { nullable: true, }, ) - public subspaceProductModels: SubspaceProductModelEntity[]; + public productModels: SubspaceProductModelEntity[]; } diff --git a/libs/common/src/modules/space-model/entities/subspace-model/subspace-product-model.entity.ts b/libs/common/src/modules/space-model/entities/subspace-model/subspace-product-model.entity.ts index 6e22a35..843c70d 100644 --- a/libs/common/src/modules/space-model/entities/subspace-model/subspace-product-model.entity.ts +++ b/libs/common/src/modules/space-model/entities/subspace-model/subspace-product-model.entity.ts @@ -16,7 +16,7 @@ export class SubspaceProductModelEntity extends AbstractEntity SubspaceModelEntity, - (spaceModel) => spaceModel.subspaceProductModels, + (spaceModel) => spaceModel.productModels, { nullable: false, }, diff --git a/libs/common/src/modules/space/entities/subspace/subspace-product.entity.ts b/libs/common/src/modules/space/entities/subspace/subspace-product.entity.ts index 03531cc..87ed807 100644 --- a/libs/common/src/modules/space/entities/subspace/subspace-product.entity.ts +++ b/libs/common/src/modules/space/entities/subspace/subspace-product.entity.ts @@ -46,5 +46,5 @@ export class SubspaceProductEntity extends AbstractEntity nullable: true, }, ) - model: SubspaceProductItemModelEntity; + model: SubspaceProductModelEntity; } diff --git a/src/space/dtos/subspace/add.subspace.dto.ts b/src/space/dtos/subspace/add.subspace.dto.ts index a2b12e2..6b5078b 100644 --- a/src/space/dtos/subspace/add.subspace.dto.ts +++ b/src/space/dtos/subspace/add.subspace.dto.ts @@ -1,5 +1,13 @@ import { ApiProperty } from '@nestjs/swagger'; -import { IsNotEmpty, IsString } from 'class-validator'; +import { Type } from 'class-transformer'; +import { + IsArray, + IsNotEmpty, + IsOptional, + IsString, + ValidateNested, +} from 'class-validator'; +import { ProductAssignmentDto } from '../add.space.dto'; export class AddSubspaceDto { @ApiProperty({ @@ -9,4 +17,15 @@ export class AddSubspaceDto { @IsNotEmpty() @IsString() subspaceName: string; + + @ApiProperty({ + description: 'List of products assigned to this space', + type: [ProductAssignmentDto], + required: false, + }) + @IsArray() + @ValidateNested({ each: true }) + @IsOptional() + @Type(() => ProductAssignmentDto) + products?: ProductAssignmentDto[]; } diff --git a/src/space/services/index.ts b/src/space/services/index.ts index 6a3beeb..c67ccae 100644 --- a/src/space/services/index.ts +++ b/src/space/services/index.ts @@ -6,3 +6,4 @@ export * from './space-link'; export * from './space-scene.service'; export * from './space-products'; export * from './space-product-items'; +export * from './space-validation.service'; diff --git a/src/space/services/space-validation.service.ts b/src/space/services/space-validation.service.ts index cd80387..fa4a5f7 100644 --- a/src/space/services/space-validation.service.ts +++ b/src/space/services/space-validation.service.ts @@ -61,6 +61,8 @@ export class ValidationService { where: { uuid: spaceModelUuid }, relations: [ 'subspaceModels', + 'subspaceModels.productModels.product', + 'subspaceModels.productModels', 'spaceProductModels', 'spaceProductModels.product', 'spaceProductModels.items', diff --git a/src/space/services/subspace/index.ts b/src/space/services/subspace/index.ts index 973d199..b51a84a 100644 --- a/src/space/services/subspace/index.ts +++ b/src/space/services/subspace/index.ts @@ -1,2 +1,4 @@ export * from './subspace.service'; export * from './subspace-device.service'; +export * from './subspace-product-item.service'; +export * from './subspace-product.service'; diff --git a/src/space/services/subspace/subspace-product-item.service.ts b/src/space/services/subspace/subspace-product-item.service.ts new file mode 100644 index 0000000..fa3f041 --- /dev/null +++ b/src/space/services/subspace/subspace-product-item.service.ts @@ -0,0 +1,50 @@ +import { Injectable, HttpException, HttpStatus } from '@nestjs/common'; +import { QueryRunner } from 'typeorm'; + +import { SubspaceProductEntity } from '@app/common/modules/space'; +import { SubspaceProductModelEntity } from '@app/common/modules/space-model'; +import { SubspaceProductItemRepository } from '@app/common/modules/space/repositories/subspace.repository'; + +@Injectable() +export class SubspaceProductItemService { + constructor( + private readonly productItemRepository: SubspaceProductItemRepository, + ) {} + + async createItemFromModel( + product: SubspaceProductEntity, + productModel: SubspaceProductModelEntity, + queryRunner: QueryRunner, + ): Promise { + const itemModels = productModel.itemModels; + + if (!itemModels?.length) return; + + try { + const productItems = itemModels.map((model) => + this.createProductItem(product, model, queryRunner), + ); + + await queryRunner.manager.save( + this.productItemRepository.target, + productItems, + ); + } catch (error) { + throw new HttpException( + error.message || 'An error occurred while creating product items.', + HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + } + + private createProductItem( + product: SubspaceProductEntity, + model: any, + queryRunner: QueryRunner, + ): Partial { + return queryRunner.manager.create(this.productItemRepository.target, { + tag: model.tag, + product, + }); + } +} diff --git a/src/space/services/subspace/subspace-product.service.ts b/src/space/services/subspace/subspace-product.service.ts new file mode 100644 index 0000000..0b43880 --- /dev/null +++ b/src/space/services/subspace/subspace-product.service.ts @@ -0,0 +1,66 @@ +import { HttpException, HttpStatus, Injectable } from '@nestjs/common'; +import { QueryRunner } from 'typeorm'; + +import { + SubspaceEntity, + SubspaceProductEntity, +} from '@app/common/modules/space'; +import { + SubspaceModelEntity, + SubspaceProductModelEntity, +} from '@app/common/modules/space-model'; +import { SubspaceProductItemService } from './subspace-product-item.service'; + +@Injectable() +export class SubspaceProductService { + constructor( + private readonly subspaceProductItemService: SubspaceProductItemService, + ) {} + + async createFromModel( + subspaceModel: SubspaceModelEntity, + subspace: SubspaceEntity, + queryRunner: QueryRunner, + ): Promise { + const productModels = subspaceModel.productModels; + if (!productModels?.length) return; + + try { + const newSpaceProducts = productModels.map((productModel) => + this.createSubspaceProductEntity(subspace, productModel), + ); + + const subspaceProducts = await queryRunner.manager.save( + SubspaceProductEntity, + newSpaceProducts, + ); + + await Promise.all( + subspaceProducts.map((subspaceProduct, index) => + this.subspaceProductItemService.createItemFromModel( + subspaceProduct, + productModels[index], + queryRunner, + ), + ), + ); + } catch (error) { + throw new HttpException( + `Transaction failed: Unable to create subspace products ${error}`, + HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + } + + private createSubspaceProductEntity( + subspace: SubspaceEntity, + productModel: SubspaceProductModelEntity, + ): Partial { + return { + subspace, + product: productModel.product, + productCount: productModel.productCount, + model: productModel, + }; + } +} diff --git a/src/space/services/subspace/subspace.service.ts b/src/space/services/subspace/subspace.service.ts index 96afa41..5848e43 100644 --- a/src/space/services/subspace/subspace.service.ts +++ b/src/space/services/subspace/subspace.service.ts @@ -13,19 +13,28 @@ import { SpaceEntity, SubspaceEntity, } from '@app/common/modules/space/entities'; -import { SpaceModelEntity } from '@app/common/modules/space-model'; +import { + SpaceModelEntity, + SubspaceModelEntity, +} from '@app/common/modules/space-model'; import { ValidationService } from '../space-validation.service'; import { SubspaceRepository } from '@app/common/modules/space/repositories/subspace.repository'; +import { SubspaceProductService } from './subspace-product.service'; @Injectable() export class SubSpaceService { constructor( private readonly subspaceRepository: SubspaceRepository, private readonly validationService: ValidationService, + private readonly productService: SubspaceProductService, ) {} async createSubspaces( - subspaceData: Array<{ subspaceName: string; space: SpaceEntity }>, + subspaceData: Array<{ + subspaceName: string; + space: SpaceEntity; + subSpaceModel?: SubspaceModelEntity; + }>, queryRunner: QueryRunner, ): Promise { try { @@ -47,17 +56,27 @@ export class SubSpaceService { space: SpaceEntity, queryRunner: QueryRunner, ): Promise { - const subSpaces = spaceModel.subspaceModels; + const subSpaceModels = spaceModel.subspaceModels; - if (!subSpaces?.length) return; + if (!subSpaceModels?.length) return; - const subspaceData = subSpaces.map((subSpaceModel) => ({ + const subspaceData = subSpaceModels.map((subSpaceModel) => ({ subspaceName: subSpaceModel.subspaceName, space, subSpaceModel, })); - await this.createSubspaces(subspaceData, queryRunner); + const subspaces = await this.createSubspaces(subspaceData, queryRunner); + + await Promise.all( + subSpaceModels.map((model, index) => { + this.productService.createFromModel( + model, + subspaces[index], + queryRunner, + ); + }), + ); } async createSubspacesFromNames( diff --git a/src/space/space.module.ts b/src/space/space.module.ts index cd443e5..da589e8 100644 --- a/src/space/space.module.ts +++ b/src/space/space.module.ts @@ -18,6 +18,7 @@ import { SpaceService, SpaceUserService, SubspaceDeviceService, + SubspaceProductItemService, SubSpaceService, } from './services'; import { @@ -46,8 +47,12 @@ import { SceneDeviceRepository } from '@app/common/modules/scene-device/reposito import { ProjectRepository } from '@app/common/modules/project/repositiories'; import { SpaceModelRepository } from '@app/common/modules/space-model'; import { CommunityModule } from 'src/community/community.module'; -import { ValidationService } from './services/space-validation.service'; -import { SubspaceRepository } from '@app/common/modules/space/repositories/subspace.repository'; +import { ValidationService } from './services'; +import { + SubspaceProductItemRepository, + SubspaceRepository, +} from '@app/common/modules/space/repositories/subspace.repository'; +import { SubspaceProductService } from './services'; @Module({ imports: [ConfigModule, SpaceRepositoryModule, CommunityModule], @@ -82,6 +87,7 @@ import { SubspaceRepository } from '@app/common/modules/space/repositories/subsp SceneRepository, DeviceService, DeviceStatusFirebaseService, + SubspaceProductItemRepository, DeviceStatusLogRepository, SceneDeviceRepository, SpaceProductService, @@ -91,6 +97,8 @@ import { SubspaceRepository } from '@app/common/modules/space/repositories/subsp SubspaceRepository, SpaceProductItemService, SpaceProductItemRepository, + SubspaceProductService, + SubspaceProductItemService, ], exports: [SpaceService], }) From d1050babd1cfb403a872a6d5a50114a18e9a7ca8 Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Tue, 17 Dec 2024 19:08:48 +0400 Subject: [PATCH 24/26] space product service --- .../entities/subspace/subspace.entity.ts | 2 +- src/space/dtos/add.space.dto.ts | 2 - .../space-product-items.service.ts | 29 +++-- .../space-products/space-products.service.ts | 2 +- src/space/services/space.service.ts | 4 +- .../subspace/subspace-product-item.service.ts | 100 +++++++++++++++++- .../subspace/subspace-product.service.ts | 62 +++++++++++ .../services/subspace/subspace.service.ts | 31 ++++-- src/space/space.module.ts | 2 + 9 files changed, 212 insertions(+), 22 deletions(-) diff --git a/libs/common/src/modules/space/entities/subspace/subspace.entity.ts b/libs/common/src/modules/space/entities/subspace/subspace.entity.ts index 932757d..bc6ff06 100644 --- a/libs/common/src/modules/space/entities/subspace/subspace.entity.ts +++ b/libs/common/src/modules/space/entities/subspace/subspace.entity.ts @@ -24,7 +24,7 @@ export class SubspaceEntity extends AbstractEntity { nullable: false, onDelete: 'CASCADE', }) - @JoinColumn({ name: 'space_id' }) + @JoinColumn({ name: 'space_uuid' }) space: SpaceEntity; @OneToMany(() => DeviceEntity, (device) => device.subspace, { diff --git a/src/space/dtos/add.space.dto.ts b/src/space/dtos/add.space.dto.ts index 402d37a..dba8e92 100644 --- a/src/space/dtos/add.space.dto.ts +++ b/src/space/dtos/add.space.dto.ts @@ -1,7 +1,6 @@ import { ApiProperty } from '@nestjs/swagger'; import { Type } from 'class-transformer'; import { - ArrayNotEmpty, IsArray, IsBoolean, IsNotEmpty, @@ -43,7 +42,6 @@ export class ProductAssignmentDto { example: [{ tag: 'Light 1' }, { tag: 'Light 2' }, { tag: 'Light 3' }], }) @IsArray() - @ArrayNotEmpty() @ValidateNested({ each: true }) @Type(() => CreateSpaceProductItemDto) items: CreateSpaceProductItemDto[]; diff --git a/src/space/services/space-product-items/space-product-items.service.ts b/src/space/services/space-product-items/space-product-items.service.ts index 4dbe0ff..b253fa3 100644 --- a/src/space/services/space-product-items/space-product-items.service.ts +++ b/src/space/services/space-product-items/space-product-items.service.ts @@ -78,6 +78,27 @@ export class SpaceProductItemService { queryRunner: QueryRunner, space: SpaceEntity, ) { + const query = ` + SELECT spi.tag +FROM "space-product-item" spi +INNER JOIN "space-product" spm + ON spi.space_product_uuid = spm.uuid +WHERE spm.space_uuid = $1 + +UNION + +SELECT spi.tag +FROM "subspace-product-item" spi +INNER JOIN "subspace-product" spm + ON spi.subspace_product_uuid = spm.uuid +INNER JOIN "subspace" sm + ON spm.subspace_uuid = sm.uuid +WHERE sm.space_uuid = $1; + `; + + const result = await queryRunner.manager.query(query, [space.uuid]); + console.log(result); + const incomingTags = itemModelDtos.map((item) => item.tag); const duplicateTags = incomingTags.filter( @@ -90,13 +111,7 @@ export class SpaceProductItemService { ); } - const existingTags = await queryRunner.manager.find( - this.spaceProductItemRepository.target, - { - where: { spaceProduct: { space } }, - select: ['tag'], - }, - ); + const existingTags = await queryRunner.manager.query(query, [space.uuid]); const existingTagSet = new Set(existingTags.map((item) => item.tag)); const conflictingTags = incomingTags.filter((tag) => diff --git a/src/space/services/space-products/space-products.service.ts b/src/space/services/space-products/space-products.service.ts index 895053f..a523d72 100644 --- a/src/space/services/space-products/space-products.service.ts +++ b/src/space/services/space-products/space-products.service.ts @@ -14,7 +14,7 @@ export class SpaceProductService { private readonly spaceProductItemService: SpaceProductItemService, ) {} - async createProductItemFromModel( + async createFromModel( spaceModel: SpaceModelEntity, space: SpaceEntity, queryRunner: QueryRunner, diff --git a/src/space/services/space.service.ts b/src/space/services/space.service.ts index 5481d19..59aceb5 100644 --- a/src/space/services/space.service.ts +++ b/src/space/services/space.service.ts @@ -81,7 +81,7 @@ export class SpaceService { } if (subspaces?.length) { - await this.subSpaceService.createSubspacesFromNames( + await this.subSpaceService.createSubspacesFromDto( subspaces, newSpace, queryRunner, @@ -101,7 +101,7 @@ export class SpaceService { queryRunner, ); } else if (spaceModel && spaceModel.spaceProductModels.length) { - await this.spaceProductService.createProductItemFromModel( + await this.spaceProductService.createFromModel( spaceModel, newSpace, queryRunner, diff --git a/src/space/services/subspace/subspace-product-item.service.ts b/src/space/services/subspace/subspace-product-item.service.ts index fa3f041..f9a0713 100644 --- a/src/space/services/subspace/subspace-product-item.service.ts +++ b/src/space/services/subspace/subspace-product-item.service.ts @@ -1,9 +1,17 @@ import { Injectable, HttpException, HttpStatus } from '@nestjs/common'; import { QueryRunner } from 'typeorm'; -import { SubspaceProductEntity } from '@app/common/modules/space'; -import { SubspaceProductModelEntity } from '@app/common/modules/space-model'; +import { + SpaceEntity, + SubspaceProductEntity, + SubspaceProductItemEntity, +} from '@app/common/modules/space'; +import { + SubspaceProductItemModelEntity, + SubspaceProductModelEntity, +} from '@app/common/modules/space-model'; import { SubspaceProductItemRepository } from '@app/common/modules/space/repositories/subspace.repository'; +import { CreateSpaceProductItemDto } from 'src/space/dtos'; @Injectable() export class SubspaceProductItemService { @@ -39,12 +47,98 @@ export class SubspaceProductItemService { private createProductItem( product: SubspaceProductEntity, - model: any, + model: SubspaceProductItemModelEntity, queryRunner: QueryRunner, ): Partial { return queryRunner.manager.create(this.productItemRepository.target, { tag: model.tag, product, + model, }); } + + async createItemFromDtos( + product: SubspaceProductEntity, + itemDto: CreateSpaceProductItemDto[], + queryRunner: QueryRunner, + space: SpaceEntity, + ) { + if (!itemDto?.length) return; + + try { + await this.validateTags(itemDto, queryRunner, space); + + const productItems = itemDto.map((dto) => + queryRunner.manager.create(SubspaceProductItemEntity, { + tag: dto.tag, + subspaceProduct: product, + }), + ); + + await queryRunner.manager.save( + this.productItemRepository.target, + productItems, + ); + } catch (error) { + throw new HttpException( + error.message || 'An error occurred while creating product items.', + HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + } + + private async validateTags( + subspaceItemModelDtos: CreateSpaceProductItemDto[], + queryRunner: QueryRunner, + space: SpaceEntity, + ) { + const incomingTags = subspaceItemModelDtos.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 the request: ${[...new Set(duplicateTags)].join(', ')}`, + HttpStatus.BAD_REQUEST, + ); + } + + const existingTagsQuery = ` + SELECT spi.tag +FROM "space-product-item" spi +INNER JOIN "space-product" spm + ON spi.space_product_uuid = spm.uuid +WHERE spm.space_uuid = $1 + +UNION + +SELECT spi.tag +FROM "subspace-product-item" spi +INNER JOIN "subspace-product" spm + ON spi.subspace_product_uuid = spm.uuid +INNER JOIN "subspace" sm + ON spm.subspace_uuid = sm.uuid +WHERE sm.space_uuid = $1; + `; + + const existingTags = await queryRunner.manager.query(existingTagsQuery, [ + space.uuid, + ]); + + console.log(existingTags); + + const existingTagsSet = new Set( + existingTags.map((row: { tag: string }) => row.tag), + ); + const conflictingTags = [...incomingTags].filter((tag) => + existingTagsSet.has(tag), + ); + if (conflictingTags.length > 0) { + throw new HttpException( + `Tags already exist in the model: ${conflictingTags.join(', ')}`, + HttpStatus.CONFLICT, + ); + } + } } diff --git a/src/space/services/subspace/subspace-product.service.ts b/src/space/services/subspace/subspace-product.service.ts index 0b43880..e789311 100644 --- a/src/space/services/subspace/subspace-product.service.ts +++ b/src/space/services/subspace/subspace-product.service.ts @@ -2,6 +2,7 @@ import { HttpException, HttpStatus, Injectable } from '@nestjs/common'; import { QueryRunner } from 'typeorm'; import { + SpaceEntity, SubspaceEntity, SubspaceProductEntity, } from '@app/common/modules/space'; @@ -10,11 +11,15 @@ import { SubspaceProductModelEntity, } from '@app/common/modules/space-model'; import { SubspaceProductItemService } from './subspace-product-item.service'; +import { ProductAssignmentDto } from 'src/space/dtos'; +import { ProductService } from 'src/product/services'; +import { ProductEntity } from '@app/common/modules/product/entities'; @Injectable() export class SubspaceProductService { constructor( private readonly subspaceProductItemService: SubspaceProductItemService, + private readonly productService: ProductService, ) {} async createFromModel( @@ -63,4 +68,61 @@ export class SubspaceProductService { model: productModel, }; } + + async createFromDto( + productDtos: ProductAssignmentDto[], + subspace: SubspaceEntity, + queryRunner: QueryRunner, + space: SpaceEntity, + ): Promise { + try { + const newSpaceProducts = await Promise.all( + productDtos.map(async (dto) => { + this.validateProductCount(dto); + + const product = await this.getProduct(dto.productId); + return queryRunner.manager.create(SubspaceProductEntity, { + subspace, + product, + productCount: dto.count, + }); + }), + ); + + const subspaceProducts = await queryRunner.manager.save( + SubspaceProductEntity, + newSpaceProducts, + ); + + await Promise.all( + productDtos.map((dto, index) => + this.subspaceProductItemService.createItemFromDtos( + subspaceProducts[index], + dto.items, + queryRunner, + space, + ), + ), + ); + } catch (error) { + throw new HttpException( + `Failed to create subspace products from DTOs. Error: ${error.message}`, + HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + } + + async getProduct(productId: string): Promise { + const product = await this.productService.findOne(productId); + return product.data; + } + + async validateProductCount(dto: ProductAssignmentDto) { + if (dto.count !== dto.items.length) { + throw new HttpException( + 'Producy item and count doesnot match', + HttpStatus.BAD_REQUEST, + ); + } + } } diff --git a/src/space/services/subspace/subspace.service.ts b/src/space/services/subspace/subspace.service.ts index 5848e43..6b9a1a3 100644 --- a/src/space/services/subspace/subspace.service.ts +++ b/src/space/services/subspace/subspace.service.ts @@ -79,17 +79,36 @@ export class SubSpaceService { ); } - async createSubspacesFromNames( + async createSubspacesFromDto( addSubspaceDtos: AddSubspaceDto[], space: SpaceEntity, queryRunner: QueryRunner, ): Promise { - const subspaceData = addSubspaceDtos.map((dto) => ({ - subspaceName: dto.subspaceName, - space, - })); + try { + const subspaceData = addSubspaceDtos.map((dto) => ({ + subspaceName: dto.subspaceName, + space, + })); - return await this.createSubspaces(subspaceData, queryRunner); + const subspaces = await this.createSubspaces(subspaceData, queryRunner); + + await Promise.all( + addSubspaceDtos.map((dto, index) => + this.productService.createFromDto( + dto.products, + subspaces[index], + queryRunner, + space, + ), + ), + ); + + return subspaces; + } catch (error) { + throw new Error( + `Transaction failed: Unable to create subspaces and products. ${error.message}`, + ); + } } async createSubspace( diff --git a/src/space/space.module.ts b/src/space/space.module.ts index da589e8..81d99e2 100644 --- a/src/space/space.module.ts +++ b/src/space/space.module.ts @@ -50,6 +50,7 @@ import { CommunityModule } from 'src/community/community.module'; import { ValidationService } from './services'; import { SubspaceProductItemRepository, + SubspaceProductRepository, SubspaceRepository, } from '@app/common/modules/space/repositories/subspace.repository'; import { SubspaceProductService } from './services'; @@ -99,6 +100,7 @@ import { SubspaceProductService } from './services'; SpaceProductItemRepository, SubspaceProductService, SubspaceProductItemService, + SubspaceProductRepository, ], exports: [SpaceService], }) From e35cefb03b5e2eb6192010f42acc1be0967c04b7 Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Tue, 17 Dec 2024 19:21:00 +0400 Subject: [PATCH 25/26] added base service --- src/space/common/base-product-item.service.ts | 69 ++++++++++++++++++ src/space/common/index.ts | 1 + .../space-product-items.service.ts | 72 +++++-------------- .../subspace/subspace-product-item.service.ts | 68 +++--------------- 4 files changed, 94 insertions(+), 116 deletions(-) create mode 100644 src/space/common/base-product-item.service.ts create mode 100644 src/space/common/index.ts diff --git a/src/space/common/base-product-item.service.ts b/src/space/common/base-product-item.service.ts new file mode 100644 index 0000000..dd62050 --- /dev/null +++ b/src/space/common/base-product-item.service.ts @@ -0,0 +1,69 @@ +import { HttpException, HttpStatus } from '@nestjs/common'; +import { QueryRunner } from 'typeorm'; + +export abstract class BaseProductItemService { + protected async validateTags( + incomingTags: string[], + queryRunner: QueryRunner, + spaceUuid: string, + ): Promise { + const duplicateTags = incomingTags.filter( + (tag, index) => incomingTags.indexOf(tag) !== index, + ); + + if (duplicateTags.length > 0) { + throw new HttpException( + `Duplicate tags found in the request: ${[...new Set(duplicateTags)].join(', ')}`, + HttpStatus.BAD_REQUEST, + ); + } + + const existingTagsQuery = ` + SELECT tag FROM ( + SELECT spi.tag + FROM "space-product-item" spi + INNER JOIN "space-product" spm ON spi.space_product_uuid = spm.uuid + WHERE spm.space_uuid = $1 + UNION + SELECT spi.tag + FROM "subspace-product-item" spi + INNER JOIN "subspace-product" spm ON spi.subspace_product_uuid = spm.uuid + INNER JOIN "subspace" sm ON spm.subspace_uuid = sm.uuid + WHERE sm.space_uuid = $1 + ) AS combined_tags; + `; + + const existingTags = await queryRunner.manager.query(existingTagsQuery, [ + spaceUuid, + ]); + + const existingTagSet = new Set( + existingTags.map((row: { tag: string }) => row.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, + ); + } + } + + protected async saveProductItems( + productItems: T[], + targetRepository: any, + queryRunner: QueryRunner, + ): Promise { + try { + await queryRunner.manager.save(targetRepository, productItems); + } catch (error) { + throw new HttpException( + error.message || 'An error occurred while creating product items.', + HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + } +} diff --git a/src/space/common/index.ts b/src/space/common/index.ts new file mode 100644 index 0000000..6e76f39 --- /dev/null +++ b/src/space/common/index.ts @@ -0,0 +1 @@ +export * from './base-product-item.service'; diff --git a/src/space/services/space-product-items/space-product-items.service.ts b/src/space/services/space-product-items/space-product-items.service.ts index b253fa3..d4883b9 100644 --- a/src/space/services/space-product-items/space-product-items.service.ts +++ b/src/space/services/space-product-items/space-product-items.service.ts @@ -7,12 +7,15 @@ import { HttpException, HttpStatus, Injectable } from '@nestjs/common'; import { CreateSpaceProductItemDto } from '../../dtos'; import { QueryRunner } from 'typeorm'; import { SpaceProductModelEntity } from '@app/common/modules/space-model'; +import { BaseProductItemService } from '../../common'; @Injectable() -export class SpaceProductItemService { +export class SpaceProductItemService extends BaseProductItemService { constructor( private readonly spaceProductItemRepository: SpaceProductItemRepository, - ) {} + ) { + super(); + } async createProductItem( itemModelDtos: CreateSpaceProductItemDto[], @@ -20,7 +23,11 @@ export class SpaceProductItemService { space: SpaceEntity, queryRunner: QueryRunner, ) { - await this.validateTags(itemModelDtos, queryRunner, space); + if (!itemModelDtos?.length) return; + + const incomingTags = itemModelDtos.map((item) => item.tag); + + await this.validateTags(incomingTags, queryRunner, space.uuid); try { const productItems = itemModelDtos.map((dto) => @@ -30,7 +37,11 @@ export class SpaceProductItemService { }), ); - await queryRunner.manager.save(productItems); + await this.saveProductItems( + productItems, + this.spaceProductItemRepository.target, + queryRunner, + ); } catch (error) { if (error instanceof HttpException) { throw error; @@ -50,6 +61,7 @@ export class SpaceProductItemService { queryRunner: QueryRunner, ) { const spaceProductItemModels = spaceProductModel.items; + if (!spaceProductItemModels?.length) return; try { const productItems = spaceProductItemModels.map((model) => @@ -72,56 +84,4 @@ export class SpaceProductItemService { ); } } - - private async validateTags( - itemModelDtos: CreateSpaceProductItemDto[], - queryRunner: QueryRunner, - space: SpaceEntity, - ) { - const query = ` - SELECT spi.tag -FROM "space-product-item" spi -INNER JOIN "space-product" spm - ON spi.space_product_uuid = spm.uuid -WHERE spm.space_uuid = $1 - -UNION - -SELECT spi.tag -FROM "subspace-product-item" spi -INNER JOIN "subspace-product" spm - ON spi.subspace_product_uuid = spm.uuid -INNER JOIN "subspace" sm - ON spm.subspace_uuid = sm.uuid -WHERE sm.space_uuid = $1; - `; - - const result = await queryRunner.manager.query(query, [space.uuid]); - console.log(result); - - 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 the request: ${[...new Set(duplicateTags)].join(', ')}`, - HttpStatus.BAD_REQUEST, - ); - } - - const existingTags = await queryRunner.manager.query(query, [space.uuid]); - 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/services/subspace/subspace-product-item.service.ts b/src/space/services/subspace/subspace-product-item.service.ts index f9a0713..647a597 100644 --- a/src/space/services/subspace/subspace-product-item.service.ts +++ b/src/space/services/subspace/subspace-product-item.service.ts @@ -11,13 +11,16 @@ import { SubspaceProductModelEntity, } from '@app/common/modules/space-model'; import { SubspaceProductItemRepository } from '@app/common/modules/space/repositories/subspace.repository'; -import { CreateSpaceProductItemDto } from 'src/space/dtos'; +import { CreateSpaceProductItemDto } from '../../dtos'; +import { BaseProductItemService } from '../../common'; @Injectable() -export class SubspaceProductItemService { +export class SubspaceProductItemService extends BaseProductItemService { constructor( private readonly productItemRepository: SubspaceProductItemRepository, - ) {} + ) { + super(); + } async createItemFromModel( product: SubspaceProductEntity, @@ -64,10 +67,10 @@ export class SubspaceProductItemService { space: SpaceEntity, ) { if (!itemDto?.length) return; + const incomingTags = itemDto.map((item) => item.tag); + await this.validateTags(incomingTags, queryRunner, space.uuid); try { - await this.validateTags(itemDto, queryRunner, space); - const productItems = itemDto.map((dto) => queryRunner.manager.create(SubspaceProductItemEntity, { tag: dto.tag, @@ -86,59 +89,4 @@ export class SubspaceProductItemService { ); } } - - private async validateTags( - subspaceItemModelDtos: CreateSpaceProductItemDto[], - queryRunner: QueryRunner, - space: SpaceEntity, - ) { - const incomingTags = subspaceItemModelDtos.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 the request: ${[...new Set(duplicateTags)].join(', ')}`, - HttpStatus.BAD_REQUEST, - ); - } - - const existingTagsQuery = ` - SELECT spi.tag -FROM "space-product-item" spi -INNER JOIN "space-product" spm - ON spi.space_product_uuid = spm.uuid -WHERE spm.space_uuid = $1 - -UNION - -SELECT spi.tag -FROM "subspace-product-item" spi -INNER JOIN "subspace-product" spm - ON spi.subspace_product_uuid = spm.uuid -INNER JOIN "subspace" sm - ON spm.subspace_uuid = sm.uuid -WHERE sm.space_uuid = $1; - `; - - const existingTags = await queryRunner.manager.query(existingTagsQuery, [ - space.uuid, - ]); - - console.log(existingTags); - - const existingTagsSet = new Set( - existingTags.map((row: { tag: string }) => row.tag), - ); - const conflictingTags = [...incomingTags].filter((tag) => - existingTagsSet.has(tag), - ); - if (conflictingTags.length > 0) { - throw new HttpException( - `Tags already exist in the model: ${conflictingTags.join(', ')}`, - HttpStatus.CONFLICT, - ); - } - } } From ef33b3e5f64d141606f5ea9aafb567376d89c4d6 Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Tue, 17 Dec 2024 20:37:29 +0400 Subject: [PATCH 26/26] created abstract class --- .../services/space-products/space-products.service.ts | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/space/services/space-products/space-products.service.ts b/src/space/services/space-products/space-products.service.ts index a523d72..2fe75cb 100644 --- a/src/space/services/space-products/space-products.service.ts +++ b/src/space/services/space-products/space-products.service.ts @@ -6,12 +6,15 @@ import { In, QueryRunner } from 'typeorm'; import { ProductAssignmentDto } from '../../dtos'; import { SpaceProductItemService } from '../space-product-items'; import { SpaceModelEntity } from '@app/common/modules/space-model'; +import { ProductEntity } from '@app/common/modules/product/entities'; +import { ProductService } from 'src/product/services'; @Injectable() export class SpaceProductService { constructor( private readonly productRepository: ProductRepository, private readonly spaceProductItemService: SpaceProductItemService, + private readonly productService: ProductService, ) {} async createFromModel( @@ -176,6 +179,7 @@ export class SpaceProductService { for (const uniqueSpaceProduct of uniqueSpaceProducts) { const product = productEntities.get(uniqueSpaceProduct.productId); + await this.getProduct(uniqueSpaceProduct.productId); this.validateProductCount(uniqueSpaceProduct); newProducts.push( @@ -214,4 +218,9 @@ export class SpaceProductService { ); } } + + async getProduct(productId: string): Promise { + const product = await this.productService.findOne(productId); + return product.data; + } }