From d1050babd1cfb403a872a6d5a50114a18e9a7ca8 Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Tue, 17 Dec 2024 19:08:48 +0400 Subject: [PATCH] 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], })