From 85aa64ac369e8700d0f3629a6428e154a4ac8fc4 Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Wed, 18 Dec 2024 15:00:34 +0400 Subject: [PATCH 01/45] added edit space module endpoint --- libs/common/src/constants/controller-route.ts | 4 ++ libs/common/src/constants/role-permissions.ts | 2 + .../controllers/space-model.controller.ts | 23 ++++++- src/space-model/dtos/index.ts | 2 + src/space-model/dtos/project-param.dto.ts | 2 +- src/space-model/dtos/space-model-param.ts | 11 ++++ .../dtos/update-space-model.dto.ts | 12 ++++ .../services/space-model.service.ts | 64 ++++++++++++++----- 8 files changed, 103 insertions(+), 17 deletions(-) create mode 100644 src/space-model/dtos/space-model-param.ts create mode 100644 src/space-model/dtos/update-space-model.dto.ts diff --git a/libs/common/src/constants/controller-route.ts b/libs/common/src/constants/controller-route.ts index f6cbfe1..aace4d6 100644 --- a/libs/common/src/constants/controller-route.ts +++ b/libs/common/src/constants/controller-route.ts @@ -277,6 +277,10 @@ export class ControllerRoute { public static readonly LIST_SPACE_MODEL_SUMMARY = 'List Space Models'; public static readonly LIST_SPACE_MODEL_DESCRIPTION = 'This endpoint allows you to retrieve a list of space models within a specified project. Each space model includes its structure, associated subspaces, products, and product items.'; + + public static readonly UPDATE_SPACE_MODEL_SUMMARY = 'Update Space Model'; + public static readonly UPDATE_SPACE_MODEL_DESCRIPTION = + 'This endpoint allows you to update a Space Model attributesas well as manage its associated Subspaces and Device'; }; }; diff --git a/libs/common/src/constants/role-permissions.ts b/libs/common/src/constants/role-permissions.ts index aae658a..a7163c3 100644 --- a/libs/common/src/constants/role-permissions.ts +++ b/libs/common/src/constants/role-permissions.ts @@ -18,6 +18,7 @@ export const RolePermissions = { 'SPACE_DELETE', 'SPACE_MODULE_ADD', 'SPACE_MODEL_VIEW', + 'SPACE_MODEL_UPDATE', 'ASSIGN_USER_TO_SPACE', 'DELETE_USER_FROM_SPACE', 'SUBSPACE_VIEW', @@ -62,6 +63,7 @@ export const RolePermissions = { 'SPACE_DELETE', 'SPACE_MODULE_ADD', 'SPACE_MODEL_VIEW', + 'SPACE_MODEL_UPDATE', 'ASSIGN_USER_TO_SPACE', 'DELETE_USER_FROM_SPACE', 'SUBSPACE_VIEW', diff --git a/src/space-model/controllers/space-model.controller.ts b/src/space-model/controllers/space-model.controller.ts index d994ba6..ba47ae9 100644 --- a/src/space-model/controllers/space-model.controller.ts +++ b/src/space-model/controllers/space-model.controller.ts @@ -5,12 +5,17 @@ import { Get, Param, Post, + Put, Query, UseGuards, } from '@nestjs/common'; import { ApiBearerAuth, ApiOperation, ApiTags } from '@nestjs/swagger'; import { SpaceModelService } from '../services'; -import { CreateSpaceModelDto } from '../dtos'; +import { + CreateSpaceModelDto, + SpaceModelParam, + UpdateSpaceModelDto, +} from '../dtos'; import { ProjectParam } from 'src/community/dtos'; import { BaseResponseDto } from '@app/common/dto/base.response.dto'; import { PermissionsGuard } from 'src/guards/permissions.guard'; @@ -59,4 +64,20 @@ export class SpaceModelController { ): Promise { return await this.spaceModelService.list(projectParam, query); } + + @ApiBearerAuth() + @UseGuards(PermissionsGuard) + @Permissions('SPACE_MODEL_UPDATE') + @ApiOperation({ + summary: ControllerRoute.SPACE_MODEL.ACTIONS.UPDATE_SPACE_MODEL_SUMMARY, + description: + ControllerRoute.SPACE_MODEL.ACTIONS.UPDATE_SPACE_MODEL_DESCRIPTION, + }) + @Put(':spaceModelUuid') + async update( + @Body() dto: UpdateSpaceModelDto, + @Param() param: SpaceModelParam, + ): Promise { + return await this.spaceModelService.update(dto, param); + } } diff --git a/src/space-model/dtos/index.ts b/src/space-model/dtos/index.ts index b4b1e07..cc4e0df 100644 --- a/src/space-model/dtos/index.ts +++ b/src/space-model/dtos/index.ts @@ -3,3 +3,5 @@ export * from './create-space-product-item-model.dto'; export * from './create-space-product-model.dto'; export * from './create-subspace-model.dto'; export * from './project-param.dto'; +export * from './update-space-model.dto'; +export * from './space-model-param'; diff --git a/src/space-model/dtos/project-param.dto.ts b/src/space-model/dtos/project-param.dto.ts index e7d9e97..69e09b5 100644 --- a/src/space-model/dtos/project-param.dto.ts +++ b/src/space-model/dtos/project-param.dto.ts @@ -1,7 +1,7 @@ import { ApiProperty } from '@nestjs/swagger'; import { IsUUID } from 'class-validator'; -export class projectParam { +export class ProjectParam { @ApiProperty({ description: 'UUID of the Project', example: 'd290f1ee-6c54-4b01-90e6-d701748f0851', diff --git a/src/space-model/dtos/space-model-param.ts b/src/space-model/dtos/space-model-param.ts new file mode 100644 index 0000000..2111546 --- /dev/null +++ b/src/space-model/dtos/space-model-param.ts @@ -0,0 +1,11 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsUUID } from 'class-validator'; +import { ProjectParam } from './project-param.dto'; +export class SpaceModelParam extends ProjectParam { + @ApiProperty({ + description: 'UUID of the Space', + example: 'd290f1ee-6c54-4b01-90e6-d701748f0851', + }) + @IsUUID() + spaceModelUuid: string; +} diff --git a/src/space-model/dtos/update-space-model.dto.ts b/src/space-model/dtos/update-space-model.dto.ts new file mode 100644 index 0000000..fc2f6aa --- /dev/null +++ b/src/space-model/dtos/update-space-model.dto.ts @@ -0,0 +1,12 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsOptional, IsString } from 'class-validator'; + +export class UpdateSpaceModelDto { + @ApiProperty({ + description: 'Updated name of the space model', + example: 'New Space Model Name', + }) + @IsOptional() + @IsString() + modelName?: string; +} diff --git a/src/space-model/services/space-model.service.ts b/src/space-model/services/space-model.service.ts index 0ce4083..dc1a11e 100644 --- a/src/space-model/services/space-model.service.ts +++ b/src/space-model/services/space-model.service.ts @@ -1,7 +1,9 @@ -import { SpaceModelRepository } from '@app/common/modules/space-model'; +import { + SpaceModelEntity, + SpaceModelRepository, +} from '@app/common/modules/space-model'; import { HttpException, HttpStatus, Injectable } from '@nestjs/common'; -import { CreateSpaceModelDto } from '../dtos'; -import { ProjectRepository } from '@app/common/modules/project/repositiories'; +import { CreateSpaceModelDto, UpdateSpaceModelDto } from '../dtos'; import { ProjectParam } from 'src/community/dtos'; import { SuccessResponseDto } from '@app/common/dto/success.response.dto'; import { SubSpaceModelService } from './subspace/subspace-model.service'; @@ -13,13 +15,16 @@ import { } from '@app/common/models/typeOrmCustom.model'; import { PageResponse } from '@app/common/dto/pagination.response.dto'; import { SpaceModelDto } from '@app/common/modules/space-model/dtos'; +import { SpaceModelParam } from '../dtos/space-model-param'; +import { ProjectService } from 'src/project/services'; +import { ProjectEntity } from '@app/common/modules/project/entities'; @Injectable() export class SpaceModelService { constructor( private readonly dataSource: DataSource, private readonly spaceModelRepository: SpaceModelRepository, - private readonly projectRepository: ProjectRepository, + private readonly projectService: ProjectService, private readonly subSpaceModelService: SubSpaceModelService, private readonly spaceProductModelService: SpaceProductModelService, ) {} @@ -44,7 +49,7 @@ export class SpaceModelService { ); if (isModelExist) { throw new HttpException( - `Model name "${modelName}" already exists in this project ${project.name}.`, + `Model name "${modelName}" already exists in this project ${params.projectUuid}.`, HttpStatus.CONFLICT, ); } @@ -125,20 +130,37 @@ export class SpaceModelService { } } - async validateProject(projectUuid: string) { - const project = await this.projectRepository.findOne({ - where: { - uuid: projectUuid, - }, - }); + async validateProject(projectUuid: string): Promise { + return await this.projectService.findOne(projectUuid); + } - if (!project) { + async update(dto: UpdateSpaceModelDto, param: SpaceModelParam) { + await this.validateProject(param.projectUuid); + const spaceModel = await this.validateSpaceModel(param.spaceModelUuid); + const queryRunner = this.dataSource.createQueryRunner(); + await queryRunner.connect(); + await queryRunner.startTransaction(); + try { + const { modelName } = dto; + if (modelName) spaceModel.modelName = modelName; + + await queryRunner.manager.save(spaceModel); + + await queryRunner.commitTransaction(); + + return new SuccessResponseDto({ + message: 'SpaceModel updated successfully', + data: spaceModel, + }); + } catch (error) { + await queryRunner.rollbackTransaction(); throw new HttpException( - `Project with uuid ${projectUuid} not found`, - HttpStatus.NOT_FOUND, + error.message || 'Failed to update SpaceModel', + HttpStatus.INTERNAL_SERVER_ERROR, ); + } finally { + await queryRunner.release(); } - return project; } async validateName(modelName: string, projectUuid: string): Promise { @@ -147,4 +169,16 @@ export class SpaceModelService { }); return isModelExist; } + + async validateSpaceModel(uuid: string): Promise { + const spaceModel = await this.spaceModelRepository.findOne({ + where: { + uuid, + }, + }); + if (!spaceModel) { + throw new HttpException('space model not found', HttpStatus.NOT_FOUND); + } + return spaceModel; + } } From 23d3cd620c20a1c29bf853f909ff373d2bc9e295 Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Wed, 18 Dec 2024 16:21:17 +0400 Subject: [PATCH 02/45] return new SubspaceModels onn space update --- .../dtos/update-space-model.dto.ts | 19 ++++++- .../services/space-model.service.ts | 9 ++++ .../subspace/subspace-model.service.ts | 51 +++++++++++++++---- .../subspace-product-item-model.service.ts | 4 +- .../subspace-product-model.service.ts | 24 ++++++--- 5 files changed, 86 insertions(+), 21 deletions(-) diff --git a/src/space-model/dtos/update-space-model.dto.ts b/src/space-model/dtos/update-space-model.dto.ts index fc2f6aa..09b196d 100644 --- a/src/space-model/dtos/update-space-model.dto.ts +++ b/src/space-model/dtos/update-space-model.dto.ts @@ -1,6 +1,20 @@ import { ApiProperty } from '@nestjs/swagger'; -import { IsOptional, IsString } from 'class-validator'; +import { IsArray, IsOptional, IsString, ValidateNested } from 'class-validator'; +import { CreateSubspaceModelDto } from './create-subspace-model.dto'; +import { Type } from 'class-transformer'; +export class UpdateSubspaceModelDto { + @ApiProperty({ + description: 'List of subspaces to add', + type: [CreateSubspaceModelDto], + required: false, + }) + @IsOptional() + @IsArray() + @ValidateNested({ each: true }) + @Type(() => CreateSubspaceModelDto) + add?: CreateSubspaceModelDto[]; +} export class UpdateSpaceModelDto { @ApiProperty({ description: 'Updated name of the space model', @@ -9,4 +23,7 @@ export class UpdateSpaceModelDto { @IsOptional() @IsString() modelName?: string; + + @IsOptional() + subspaceModels?: UpdateSubspaceModelDto; } diff --git a/src/space-model/services/space-model.service.ts b/src/space-model/services/space-model.service.ts index dc1a11e..805ffa0 100644 --- a/src/space-model/services/space-model.service.ts +++ b/src/space-model/services/space-model.service.ts @@ -146,6 +146,15 @@ export class SpaceModelService { await queryRunner.manager.save(spaceModel); + if (dto.subspaceModels) { + const updatedSubspaces = + await this.subSpaceModelService.updateSubSpaceModels( + dto.subspaceModels, + spaceModel, + queryRunner, + ); + } + await queryRunner.commitTransaction(); return new SuccessResponseDto({ diff --git a/src/space-model/services/subspace/subspace-model.service.ts b/src/space-model/services/subspace/subspace-model.service.ts index 222dee4..6814362 100644 --- a/src/space-model/services/subspace/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, UpdateSubspaceModelDto } from '../../dtos'; import { QueryRunner } from 'typeorm'; import { SubspaceProductModelService } from './subspace-product-model.service'; @@ -29,19 +29,25 @@ export class SubSpaceModelService { }), ); - await queryRunner.manager.save(subspaces); + const newSubspaces = await queryRunner.manager.save(subspaces); - await Promise.all( - subSpaceModelDtos.map((dto, index) => { - const subspaceModel = subspaces[index]; - return this.subSpaceProducetModelService.createSubspaceProductModels( - dto.spaceProductModels, - spaceModel, + const addedSubspaces = await Promise.all( + subSpaceModelDtos.map(async (dto, index) => { + const subspaceModel = newSubspaces[index]; + const productModels = + await this.subSpaceProducetModelService.createSubspaceProductModels( + dto.spaceProductModels, + spaceModel, + subspaceModel, + queryRunner, + ); + return { subspaceModel, - queryRunner, - ); + productModels, + }; }), ); + return addedSubspaces; } catch (error) { if (error instanceof HttpException) { throw error; @@ -61,7 +67,6 @@ export class SubSpaceModelService { HttpStatus.BAD_REQUEST, ); } - const incomingNames = subSpaceModelDtos.map((dto) => dto.subspaceName); this.validateName(incomingNames); } @@ -85,4 +90,28 @@ export class SubSpaceModelService { ); } } + + async updateSubSpaceModels( + dto: UpdateSubspaceModelDto, + spaceModel: SpaceModelEntity, + queryRunner: QueryRunner, + ) { + const subspaces: { new?: any } = {}; + try { + if (dto.add) { + const addedSubspaces = await this.createSubSpaceModels( + dto.add, + spaceModel, + queryRunner, + ); + subspaces.new = addedSubspaces; + } + return subspaces; + } catch (error) { + throw new HttpException( + error.message || 'Failed to update SpaceModel', + HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + } } diff --git a/src/space-model/services/subspace/subspace-product-item-model.service.ts b/src/space-model/services/subspace/subspace-product-item-model.service.ts index 393a5f3..85989f8 100644 --- a/src/space-model/services/subspace/subspace-product-item-model.service.ts +++ b/src/space-model/services/subspace/subspace-product-item-model.service.ts @@ -2,6 +2,7 @@ import { HttpException, HttpStatus, Injectable } from '@nestjs/common'; import { QueryRunner } from 'typeorm'; import { SpaceModelEntity, + SubspaceProductItemModelEntity, SubspaceProductItemModelRepository, SubspaceProductModelEntity, } from '@app/common/modules/space-model'; @@ -21,7 +22,7 @@ export class SubspaceProductItemModelService extends BaseProductItemService { subspaceProductModel: SubspaceProductModelEntity, spaceModel: SpaceModelEntity, queryRunner: QueryRunner, - ) { + ): Promise { if (!subspaceProductModel) { throw new HttpException( 'The spaceProductModel parameter is required but was not provided.', @@ -38,6 +39,7 @@ export class SubspaceProductItemModelService extends BaseProductItemService { ); await queryRunner.manager.save(productItems); + return productItems; } catch (error) { if (error instanceof HttpException) { throw error; diff --git a/src/space-model/services/subspace/subspace-product-model.service.ts b/src/space-model/services/subspace/subspace-product-model.service.ts index 30f9062..8b3f9ac 100644 --- a/src/space-model/services/subspace/subspace-product-model.service.ts +++ b/src/space-model/services/subspace/subspace-product-model.service.ts @@ -27,6 +27,8 @@ export class SubspaceProductModelService extends BaseProductModelService { queryRunner: QueryRunner, ) { try { + if (!spaceProductModelDtos?.length) return; + const productModels = await Promise.all( spaceProductModelDtos.map(async (dto) => { this.validateProductCount(dto); @@ -44,17 +46,23 @@ export class SubspaceProductModelService extends BaseProductModelService { const savedProductModels = await queryRunner.manager.save(productModels); - await Promise.all( - spaceProductModelDtos.map((dto, index) => { + const newProductModels = await Promise.all( + spaceProductModelDtos.map(async (dto, index) => { const savedModel = savedProductModels[index]; - return this.subspaceProductItemModelService.createProdutItemModel( - dto.items, - savedModel, // Pass the saved model - spaceModel, - queryRunner, - ); + const productItemModels = + await this.subspaceProductItemModelService.createProdutItemModel( + dto.items, + savedModel, + spaceModel, + queryRunner, + ); + return { + productModel: savedModel, + productItemModels, + }; }), ); + return newProductModels; } catch (error) { if (error instanceof HttpException) { throw error; From a771fa8ee50064176296d89a295874653802474b Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Thu, 19 Dec 2024 09:12:26 +0400 Subject: [PATCH 03/45] propagate add --- src/space-model/commands/index.ts | 1 + .../propogate-subspace-update-command.ts | 6 + src/space-model/dtos/index.ts | 1 + .../dtos/update-space-model.dto.ts | 17 +- .../dtos/update-subspace-model.dto.ts | 30 +++ src/space-model/handlers/index.ts | 1 + .../handlers/propate-subspace-handler.ts | 177 ++++++++++++++++++ src/space-model/interfaces/index.ts | 1 + .../interfaces/update-subspace.interface.ts | 19 ++ .../services/space-model.service.ts | 22 ++- .../subspace/subspace-model.service.ts | 11 +- src/space-model/space-model.module.ts | 19 +- 12 files changed, 291 insertions(+), 14 deletions(-) create mode 100644 src/space-model/commands/index.ts create mode 100644 src/space-model/commands/propogate-subspace-update-command.ts create mode 100644 src/space-model/dtos/update-subspace-model.dto.ts create mode 100644 src/space-model/handlers/index.ts create mode 100644 src/space-model/handlers/propate-subspace-handler.ts create mode 100644 src/space-model/interfaces/index.ts create mode 100644 src/space-model/interfaces/update-subspace.interface.ts diff --git a/src/space-model/commands/index.ts b/src/space-model/commands/index.ts new file mode 100644 index 0000000..716d458 --- /dev/null +++ b/src/space-model/commands/index.ts @@ -0,0 +1 @@ +export * from './propogate-subspace-update-command'; diff --git a/src/space-model/commands/propogate-subspace-update-command.ts b/src/space-model/commands/propogate-subspace-update-command.ts new file mode 100644 index 0000000..286b9bd --- /dev/null +++ b/src/space-model/commands/propogate-subspace-update-command.ts @@ -0,0 +1,6 @@ +import { ICommand } from '@nestjs/cqrs'; +import { UpdatedSubspaceModelInterface } from '../interfaces'; + +export class PropogateSubspaceCommand implements ICommand { + constructor(public readonly param: UpdatedSubspaceModelInterface) {} +} diff --git a/src/space-model/dtos/index.ts b/src/space-model/dtos/index.ts index cc4e0df..e7b2373 100644 --- a/src/space-model/dtos/index.ts +++ b/src/space-model/dtos/index.ts @@ -5,3 +5,4 @@ export * from './create-subspace-model.dto'; export * from './project-param.dto'; export * from './update-space-model.dto'; export * from './space-model-param'; +export * from './update-subspace-model.dto'; diff --git a/src/space-model/dtos/update-space-model.dto.ts b/src/space-model/dtos/update-space-model.dto.ts index 09b196d..daaa62b 100644 --- a/src/space-model/dtos/update-space-model.dto.ts +++ b/src/space-model/dtos/update-space-model.dto.ts @@ -2,8 +2,9 @@ import { ApiProperty } from '@nestjs/swagger'; import { IsArray, IsOptional, IsString, ValidateNested } from 'class-validator'; import { CreateSubspaceModelDto } from './create-subspace-model.dto'; import { Type } from 'class-transformer'; +import { UpdateSubspaceModelDto } from './update-subspace-model.dto'; -export class UpdateSubspaceModelDto { +export class UpdateSubspacesModelDto { @ApiProperty({ description: 'List of subspaces to add', type: [CreateSubspaceModelDto], @@ -14,7 +15,19 @@ export class UpdateSubspaceModelDto { @ValidateNested({ each: true }) @Type(() => CreateSubspaceModelDto) add?: CreateSubspaceModelDto[]; + + @ApiProperty({ + description: 'List of subspaces to add', + type: [CreateSubspaceModelDto], + required: false, + }) + @IsOptional() + @IsArray() + @ValidateNested({ each: true }) + @Type(() => UpdateSubspaceModelDto) + update?: UpdateSubspaceModelDto[]; } + export class UpdateSpaceModelDto { @ApiProperty({ description: 'Updated name of the space model', @@ -25,5 +38,5 @@ export class UpdateSpaceModelDto { modelName?: string; @IsOptional() - subspaceModels?: UpdateSubspaceModelDto; + subspaceModels?: UpdateSubspacesModelDto; } diff --git a/src/space-model/dtos/update-subspace-model.dto.ts b/src/space-model/dtos/update-subspace-model.dto.ts new file mode 100644 index 0000000..da71afc --- /dev/null +++ b/src/space-model/dtos/update-subspace-model.dto.ts @@ -0,0 +1,30 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { Type } from 'class-transformer'; +import { + IsNotEmpty, + IsString, + IsArray, + IsOptional, + ValidateNested, +} from 'class-validator'; +import { CreateSpaceProductModelDto } from './create-space-product-model.dto'; + +export class UpdateSubspaceModelDto { + @ApiProperty({ + description: 'Name of the subspace', + example: 'Living Room', + }) + @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/handlers/index.ts b/src/space-model/handlers/index.ts new file mode 100644 index 0000000..7dfa7f5 --- /dev/null +++ b/src/space-model/handlers/index.ts @@ -0,0 +1 @@ +export * from './propate-subspace-handler'; diff --git a/src/space-model/handlers/propate-subspace-handler.ts b/src/space-model/handlers/propate-subspace-handler.ts new file mode 100644 index 0000000..e7a4db7 --- /dev/null +++ b/src/space-model/handlers/propate-subspace-handler.ts @@ -0,0 +1,177 @@ +import { CommandHandler, ICommandHandler } from '@nestjs/cqrs'; +import { PropogateSubspaceCommand } from '../commands'; +import { Logger } from '@nestjs/common'; +import { SpaceEntity, SpaceRepository } from '@app/common/modules/space'; +import { + SubspaceProductItemRepository, + SubspaceProductRepository, + SubspaceRepository, +} from '@app/common/modules/space/repositories/subspace.repository'; + +@CommandHandler(PropogateSubspaceCommand) +export class PropogateSubspaceHandler + implements ICommandHandler +{ + private readonly logger = new Logger(PropogateSubspaceHandler.name); + + constructor( + private readonly spaceRepository: SpaceRepository, + private readonly subspaceRepository: SubspaceRepository, + private readonly productRepository: SubspaceProductRepository, + private readonly productItemRepository: SubspaceProductItemRepository, + ) {} + + async execute(command: PropogateSubspaceCommand): Promise { + try { + const newSubspaceModels = command.param?.new; + + if (!newSubspaceModels || newSubspaceModels.length === 0) { + this.logger.warn('No new subspace models provided.'); + return; + } + + const spaceModelUuid = + newSubspaceModels[0]?.subspaceModel?.spaceModel?.uuid; + + if (!spaceModelUuid) { + this.logger.error( + 'Space model UUID is missing in the command parameters.', + ); + return; + } + + const spaces = await this.getSpacesByModel(spaceModelUuid); + + if (spaces.length === 0) { + this.logger.warn(`No spaces found for model UUID: ${spaceModelUuid}`); + return; + } + + await this.processSubspaces(newSubspaceModels, spaces); + } catch (error) { + this.logger.error( + 'Error in PropogateSubspaceHandler execution', + error.stack, + ); + } + } + + private async processSubspaces( + newSubspaceModels: any[], + spaces: SpaceEntity[], + ) { + for (const newSubspaceModel of newSubspaceModels) { + for (const space of spaces) { + try { + const subspace = await this.createSubspace(newSubspaceModel, space); + + if (newSubspaceModel.productModels?.length > 0) { + await this.processProducts( + newSubspaceModel.productModels, + subspace, + ); + } + } catch (error) { + this.logger.error( + `Failed to create subspace for space ID: ${space.uuid}`, + error.stack, + ); + } + } + } + } + + private async createSubspace(newSubspaceModel: any, space: SpaceEntity) { + const subspace = this.subspaceRepository.create({ + subspaceName: newSubspaceModel.subspaceModel.subspaceName, + space, + subSpaceModel: newSubspaceModel.subspaceModel, + }); + + const createdSubspace = await this.subspaceRepository.save(subspace); + this.logger.log( + `Subspace created for space ${space.uuid} with name ${createdSubspace.subspaceName}`, + ); + return createdSubspace; + } + + private async processProducts(productModels: any[], subspace: any) { + for (const productModel of productModels) { + try { + const subspaceProduct = await this.createSubspaceProduct( + productModel, + subspace, + ); + + if (productModel.productItemModels?.length > 0) { + await this.processProductItems( + productModel.productItemModels, + subspaceProduct, + ); + } + } catch (error) { + this.logger.error( + `Failed to create product for subspace ID: ${subspace.id}`, + error.stack, + ); + } + } + } + + private async createSubspaceProduct(productModel: any, subspace: any) { + const subspaceProduct = this.productRepository.create({ + product: productModel.productModel.product, + subspace, + productCount: productModel.productModel.productCount, + model: productModel.productModel, + }); + + const createdSubspaceProduct = + await this.productRepository.save(subspaceProduct); + this.logger.log( + `Product added to subspace ${subspace.id} with count ${createdSubspaceProduct.productCount}`, + ); + return createdSubspaceProduct; + } + + private async processProductItems( + productItemModels: any[], + subspaceProduct: any, + ) { + for (const productItemModel of productItemModels) { + try { + const subspaceProductItem = this.productItemRepository.create({ + tag: productItemModel.tag, + subspaceProduct, + model: productItemModel, + }); + + await this.productItemRepository.save(subspaceProductItem); + this.logger.log( + `Product item added to subspace product ${subspaceProduct.id} with tag ${subspaceProductItem.tag}`, + ); + } catch (error) { + this.logger.error( + `Failed to create product item for subspace product ID: ${subspaceProduct.id}`, + error.stack, + ); + } + } + } + + private async getSpacesByModel(uuid: string): Promise { + try { + return await this.spaceRepository.find({ + where: { + spaceModel: { uuid }, + }, + }); + } catch (error) { + this.logger.error( + `Failed to fetch spaces for model UUID: ${uuid}`, + error.stack, + ); + throw error; + } + } +} diff --git a/src/space-model/interfaces/index.ts b/src/space-model/interfaces/index.ts new file mode 100644 index 0000000..606eb46 --- /dev/null +++ b/src/space-model/interfaces/index.ts @@ -0,0 +1 @@ +export * from './update-subspace.interface' \ No newline at end of file diff --git a/src/space-model/interfaces/update-subspace.interface.ts b/src/space-model/interfaces/update-subspace.interface.ts new file mode 100644 index 0000000..4ccc588 --- /dev/null +++ b/src/space-model/interfaces/update-subspace.interface.ts @@ -0,0 +1,19 @@ +import { + SubspaceModelEntity, + SubspaceProductItemModelEntity, + SubspaceProductModelEntity, +} from '@app/common/modules/space-model'; + +export interface AddSubspaceModelInterface { + subspaceModel: SubspaceModelEntity; + productModels: ProductModelInterface[]; +} + +export interface ProductModelInterface { + productModel: SubspaceProductModelEntity; + productItemModels: SubspaceProductItemModelEntity[]; +} + +export interface UpdatedSubspaceModelInterface { + new?: AddSubspaceModelInterface[]; +} diff --git a/src/space-model/services/space-model.service.ts b/src/space-model/services/space-model.service.ts index 805ffa0..6b0c5db 100644 --- a/src/space-model/services/space-model.service.ts +++ b/src/space-model/services/space-model.service.ts @@ -18,6 +18,9 @@ import { SpaceModelDto } from '@app/common/modules/space-model/dtos'; import { SpaceModelParam } from '../dtos/space-model-param'; import { ProjectService } from 'src/project/services'; import { ProjectEntity } from '@app/common/modules/project/entities'; +import { UpdatedSubspaceModelInterface } from '../interfaces'; +import { CommandBus } from '@nestjs/cqrs'; +import { PropogateSubspaceCommand } from '../commands'; @Injectable() export class SpaceModelService { @@ -27,6 +30,7 @@ export class SpaceModelService { private readonly projectService: ProjectService, private readonly subSpaceModelService: SubSpaceModelService, private readonly spaceProductModelService: SpaceProductModelService, + private commandBus: CommandBus, ) {} async createSpaceModel( @@ -142,21 +146,27 @@ export class SpaceModelService { await queryRunner.startTransaction(); try { const { modelName } = dto; + let updatedSubspaces: UpdatedSubspaceModelInterface; if (modelName) spaceModel.modelName = modelName; await queryRunner.manager.save(spaceModel); if (dto.subspaceModels) { - const updatedSubspaces = - await this.subSpaceModelService.updateSubSpaceModels( - dto.subspaceModels, - spaceModel, - queryRunner, - ); + updatedSubspaces = await this.subSpaceModelService.modifySubSpaceModels( + dto.subspaceModels, + spaceModel, + queryRunner, + ); } await queryRunner.commitTransaction(); + if (updatedSubspaces) { + await this.commandBus.execute( + new PropogateSubspaceCommand(updatedSubspaces), + ); + } + return new SuccessResponseDto({ message: 'SpaceModel updated successfully', data: spaceModel, diff --git a/src/space-model/services/subspace/subspace-model.service.ts b/src/space-model/services/subspace/subspace-model.service.ts index 6814362..d49ff83 100644 --- a/src/space-model/services/subspace/subspace-model.service.ts +++ b/src/space-model/services/subspace/subspace-model.service.ts @@ -3,9 +3,10 @@ import { SubspaceModelRepository, } from '@app/common/modules/space-model'; import { HttpException, HttpStatus, Injectable } from '@nestjs/common'; -import { CreateSubspaceModelDto, UpdateSubspaceModelDto } from '../../dtos'; +import { CreateSubspaceModelDto, UpdateSubspacesModelDto } from '../../dtos'; import { QueryRunner } from 'typeorm'; import { SubspaceProductModelService } from './subspace-product-model.service'; +import { UpdatedSubspaceModelInterface } from 'src/space-model/interfaces'; @Injectable() export class SubSpaceModelService { @@ -91,12 +92,12 @@ export class SubSpaceModelService { } } - async updateSubSpaceModels( - dto: UpdateSubspaceModelDto, + async modifySubSpaceModels( + dto: UpdateSubspacesModelDto, spaceModel: SpaceModelEntity, queryRunner: QueryRunner, ) { - const subspaces: { new?: any } = {}; + const subspaces: UpdatedSubspaceModelInterface = {}; try { if (dto.add) { const addedSubspaces = await this.createSubSpaceModels( @@ -105,6 +106,8 @@ export class SubSpaceModelService { queryRunner, ); subspaces.new = addedSubspaces; + } else if (dto.update) { + } return subspaces; } catch (error) { diff --git a/src/space-model/space-model.module.ts b/src/space-model/space-model.module.ts index ae275e2..e1e4b56 100644 --- a/src/space-model/space-model.module.ts +++ b/src/space-model/space-model.module.ts @@ -20,13 +20,25 @@ import { import { ProjectRepository } from '@app/common/modules/project/repositiories'; import { ProductRepository } from '@app/common/modules/product/repositories'; import { SubspaceProductModelService } from './services/subspace/subspace-product-model.service'; +import { PropogateSubspaceHandler } from './handlers'; +import { CqrsModule } from '@nestjs/cqrs'; +import { SpaceRepository } from '@app/common/modules/space'; +import { + SubspaceProductItemRepository, + SubspaceProductRepository, + SubspaceRepository, +} from '@app/common/modules/space/repositories/subspace.repository'; + +const CommandHandlers = [PropogateSubspaceHandler]; @Module({ - imports: [ConfigModule, SpaceRepositoryModule], + imports: [ConfigModule, SpaceRepositoryModule, CqrsModule], controllers: [SpaceModelController], providers: [ + ...CommandHandlers, SpaceModelService, SpaceModelRepository, + SpaceRepository, ProjectRepository, SubSpaceModelService, SpaceProductModelService, @@ -39,7 +51,10 @@ import { SubspaceProductModelService } from './services/subspace/subspace-produc SubspaceProductItemModelRepository, SubspaceProductModelService, SubspaceProductModelRepository, + SubspaceRepository, + SubspaceProductRepository, + SubspaceProductItemRepository, ], - exports: [], + exports: [CqrsModule], }) export class SpaceModelModule {} From 35888a1fcea63eadbea80e2ed144eeb9fd1e7295 Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Thu, 19 Dec 2024 10:22:33 +0400 Subject: [PATCH 04/45] added disabled to subspaces --- .../entities/subspace-model/subspace-model.entity.ts | 6 ++++++ .../subspace-model/subspace-product-item-model.entity.ts | 6 ++++++ .../subspace-model/subspace-product-model.entity.ts | 6 ++++++ 3 files changed, 18 insertions(+) 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 89fcf63..f166c73 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 @@ -35,6 +35,12 @@ export class SubspaceModelEntity extends AbstractEntity { }) public spaces: SubspaceEntity[]; + @Column({ + nullable: false, + default: false, + }) + public disabled: boolean; + @OneToMany( () => SubspaceProductModelEntity, (productModel) => productModel.subspaceModel, 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 2b6d305..59d6db6 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 @@ -11,6 +11,12 @@ export class SubspaceProductItemModelEntity extends AbstractEntity SubspaceProductModelEntity, (productModel) => productModel.itemModels, 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 843c70d..960c6fe 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 @@ -14,6 +14,12 @@ export class SubspaceProductModelEntity extends AbstractEntity SubspaceModelEntity, (spaceModel) => spaceModel.productModels, From 9063c65e688e5b266ac372e0c209ef4b5fdb778f Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Thu, 19 Dec 2024 10:23:08 +0400 Subject: [PATCH 05/45] added subspace delete --- .../propogate-subspace-update-command.ts | 4 +- .../delete-subspace-model.dto.ts | 12 +++ .../dtos/subspaces-model-dtos/index.ts | 1 + .../dtos/update-space-model.dto.ts | 12 +++ .../dtos/update-subspace-model.dto.ts | 4 + .../interfaces/update-subspace.interface.ts | 13 ++- .../services/space-model.service.ts | 4 +- .../subspace/subspace-model.service.ts | 94 ++++++++++++++++++- 8 files changed, 135 insertions(+), 9 deletions(-) create mode 100644 src/space-model/dtos/subspaces-model-dtos/delete-subspace-model.dto.ts create mode 100644 src/space-model/dtos/subspaces-model-dtos/index.ts diff --git a/src/space-model/commands/propogate-subspace-update-command.ts b/src/space-model/commands/propogate-subspace-update-command.ts index 286b9bd..96276ee 100644 --- a/src/space-model/commands/propogate-subspace-update-command.ts +++ b/src/space-model/commands/propogate-subspace-update-command.ts @@ -1,6 +1,6 @@ import { ICommand } from '@nestjs/cqrs'; -import { UpdatedSubspaceModelInterface } from '../interfaces'; +import { IModifySubspaceModelInterface } from '../interfaces'; export class PropogateSubspaceCommand implements ICommand { - constructor(public readonly param: UpdatedSubspaceModelInterface) {} + constructor(public readonly param: IModifySubspaceModelInterface) {} } diff --git a/src/space-model/dtos/subspaces-model-dtos/delete-subspace-model.dto.ts b/src/space-model/dtos/subspaces-model-dtos/delete-subspace-model.dto.ts new file mode 100644 index 0000000..62fe84e --- /dev/null +++ b/src/space-model/dtos/subspaces-model-dtos/delete-subspace-model.dto.ts @@ -0,0 +1,12 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsNotEmpty, IsString } from 'class-validator'; + +export class DeleteSubspaceModelDto { + @ApiProperty({ + description: 'Uuid of the subspace model need to be deleted', + example: '982fc3a3-64dc-4afb-a5b5-65ee8fef0424', + }) + @IsNotEmpty() + @IsString() + subspaceUuid: string; +} diff --git a/src/space-model/dtos/subspaces-model-dtos/index.ts b/src/space-model/dtos/subspaces-model-dtos/index.ts new file mode 100644 index 0000000..e70ef7b --- /dev/null +++ b/src/space-model/dtos/subspaces-model-dtos/index.ts @@ -0,0 +1 @@ +export * from './delete-subspace-model.dto'; diff --git a/src/space-model/dtos/update-space-model.dto.ts b/src/space-model/dtos/update-space-model.dto.ts index daaa62b..62a6bc1 100644 --- a/src/space-model/dtos/update-space-model.dto.ts +++ b/src/space-model/dtos/update-space-model.dto.ts @@ -3,6 +3,7 @@ import { IsArray, IsOptional, IsString, ValidateNested } from 'class-validator'; import { CreateSubspaceModelDto } from './create-subspace-model.dto'; import { Type } from 'class-transformer'; import { UpdateSubspaceModelDto } from './update-subspace-model.dto'; +import { DeleteSubspaceModelDto } from './subspaces-model-dtos'; export class UpdateSubspacesModelDto { @ApiProperty({ @@ -26,6 +27,17 @@ export class UpdateSubspacesModelDto { @ValidateNested({ each: true }) @Type(() => UpdateSubspaceModelDto) update?: UpdateSubspaceModelDto[]; + + @ApiProperty({ + description: 'List of subspaces to delete', + type: [DeleteSubspaceModelDto], + required: false, + }) + @IsOptional() + @IsArray() + @ValidateNested({ each: true }) + @Type(() => DeleteSubspaceModelDto) + delete?: DeleteSubspaceModelDto[]; } export class UpdateSpaceModelDto { diff --git a/src/space-model/dtos/update-subspace-model.dto.ts b/src/space-model/dtos/update-subspace-model.dto.ts index da71afc..ee25bd2 100644 --- a/src/space-model/dtos/update-subspace-model.dto.ts +++ b/src/space-model/dtos/update-subspace-model.dto.ts @@ -18,6 +18,10 @@ export class UpdateSubspaceModelDto { @IsString() subspaceName?: string; + @IsNotEmpty() + @IsString() + subspaceUuid: string; + @ApiProperty({ description: 'List of products included in the model', type: [CreateSpaceProductModelDto], diff --git a/src/space-model/interfaces/update-subspace.interface.ts b/src/space-model/interfaces/update-subspace.interface.ts index 4ccc588..4d58469 100644 --- a/src/space-model/interfaces/update-subspace.interface.ts +++ b/src/space-model/interfaces/update-subspace.interface.ts @@ -14,6 +14,17 @@ export interface ProductModelInterface { productItemModels: SubspaceProductItemModelEntity[]; } -export interface UpdatedSubspaceModelInterface { +export interface IModifySubspaceModelInterface { new?: AddSubspaceModelInterface[]; + update?: IUpdateSubspaceModelInterface[]; + delete?: IDeletedSubsaceModelInterface[]; +} + +export interface IUpdateSubspaceModelInterface { + subspaceName?: string; + uuid: string; +} + +export interface IDeletedSubsaceModelInterface { + uuid: string; } diff --git a/src/space-model/services/space-model.service.ts b/src/space-model/services/space-model.service.ts index 6b0c5db..fff944b 100644 --- a/src/space-model/services/space-model.service.ts +++ b/src/space-model/services/space-model.service.ts @@ -18,7 +18,7 @@ import { SpaceModelDto } from '@app/common/modules/space-model/dtos'; import { SpaceModelParam } from '../dtos/space-model-param'; import { ProjectService } from 'src/project/services'; import { ProjectEntity } from '@app/common/modules/project/entities'; -import { UpdatedSubspaceModelInterface } from '../interfaces'; +import { IModifySubspaceModelInterface } from '../interfaces'; import { CommandBus } from '@nestjs/cqrs'; import { PropogateSubspaceCommand } from '../commands'; @@ -146,7 +146,7 @@ export class SpaceModelService { await queryRunner.startTransaction(); try { const { modelName } = dto; - let updatedSubspaces: UpdatedSubspaceModelInterface; + let updatedSubspaces: IModifySubspaceModelInterface; if (modelName) spaceModel.modelName = modelName; await queryRunner.manager.save(spaceModel); diff --git a/src/space-model/services/subspace/subspace-model.service.ts b/src/space-model/services/subspace/subspace-model.service.ts index d49ff83..f69d859 100644 --- a/src/space-model/services/subspace/subspace-model.service.ts +++ b/src/space-model/services/subspace/subspace-model.service.ts @@ -3,10 +3,19 @@ import { SubspaceModelRepository, } from '@app/common/modules/space-model'; import { HttpException, HttpStatus, Injectable } from '@nestjs/common'; -import { CreateSubspaceModelDto, UpdateSubspacesModelDto } from '../../dtos'; +import { + CreateSubspaceModelDto, + UpdateSubspaceModelDto, + UpdateSubspacesModelDto, +} from '../../dtos'; import { QueryRunner } from 'typeorm'; import { SubspaceProductModelService } from './subspace-product-model.service'; -import { UpdatedSubspaceModelInterface } from 'src/space-model/interfaces'; +import { + IDeletedSubsaceModelInterface, + IModifySubspaceModelInterface, + IUpdateSubspaceModelInterface, +} from 'src/space-model/interfaces'; +import { DeleteSubspaceModelDto } from 'src/space-model/dtos/subspaces-model-dtos'; @Injectable() export class SubSpaceModelService { @@ -61,6 +70,79 @@ export class SubSpaceModelService { } } + async updateSubspaceModels( + updateDtos: UpdateSubspaceModelDto[], + queryRunner: QueryRunner, + ) { + const updateResults: IUpdateSubspaceModelInterface[] = []; + + for (const dto of updateDtos) { + try { + const subspaceModel = await this.findOne(dto.subspaceUuid); + const updateResult: IUpdateSubspaceModelInterface = { + uuid: dto.subspaceUuid, + }; + + if (dto.subspaceName) { + subspaceModel.subspaceName = dto.subspaceName; + await queryRunner.manager.update( + this.subspaceModelRepository.target, + { uuid: dto.subspaceUuid }, + { subspaceName: dto.subspaceName }, + ); + updateResult.subspaceName = dto.subspaceName; + } + + updateResults.push(updateResult); + } catch (error) { + console.error( + `SubspaceModel with ${dto.subspaceUuid} not able to update ${error}`, + ); + } + } + + return updateResults; + } + + async deleteSubspaceModels( + dtos: DeleteSubspaceModelDto[], + queryRunner: QueryRunner, + ) { + const deleteResults: IDeletedSubsaceModelInterface[] = []; + try { + for (const dto of dtos) { + await this.findOne(dto.subspaceUuid); + + await queryRunner.manager.update( + this.subspaceModelRepository.target, + { uuid: dto.subspaceUuid }, + { disabled: true }, + ); + + deleteResults.push({ uuid: dto.subspaceUuid }); + } + return deleteResults; + } catch (error) { + console.error(`Bulk delete operation failed: ${error.message}`); + throw new Error('Bulk delete operation failed.'); + } + } + + async findOne(subspaceUuid: string) { + const subspace = await this.subspaceModelRepository.findOne({ + where: { + uuid: subspaceUuid, + }, + }); + if (!subspace) { + throw new HttpException( + `SubspaceModel with ${subspaceUuid} not found`, + HttpStatus.NOT_FOUND, + ); + } + return subspace; + } + private validateInputDtos(subSpaceModelDtos: CreateSubspaceModelDto[]) { if (subSpaceModelDtos.length === 0) { throw new HttpException( @@ -97,7 +179,7 @@ export class SubSpaceModelService { spaceModel: SpaceModelEntity, queryRunner: QueryRunner, ) { - const subspaces: UpdatedSubspaceModelInterface = {}; + const subspaces: IModifySubspaceModelInterface = {}; try { if (dto.add) { const addedSubspaces = await this.createSubSpaceModels( @@ -107,7 +189,11 @@ export class SubSpaceModelService { ); subspaces.new = addedSubspaces; } else if (dto.update) { - + const updatedSubspaces = await this.updateSubspaceModels( + dto.update, + queryRunner, + ); + subspaces.update = updatedSubspaces; } return subspaces; } catch (error) { From 23be781d2eff8c33d2b7b0a68ef5a713015aea96 Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Thu, 19 Dec 2024 10:25:25 +0400 Subject: [PATCH 06/45] added disabled to all space entities --- .../space-model/entities/space-model.entity.ts | 6 ++++++ .../entities/space-product-item-model.entity.ts | 6 ++++++ .../entities/space-product-model.entity.ts | 6 ++++++ .../src/modules/space/entities/space-link.entity.ts | 6 ++++++ .../space/entities/space-product-item.entity.ts | 6 ++++++ .../modules/space/entities/space-product.entity.ts | 6 ++++++ .../common/src/modules/space/entities/space.entity.ts | 6 ++++++ .../entities/subspace/subspace-product-item.entity.ts | 6 ++++++ .../entities/subspace/subspace-product.entity.ts | 11 +++++++---- .../space/entities/subspace/subspace.entity.ts | 7 ++++++- 10 files changed, 61 insertions(+), 5 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 591d04f..4c90bcc 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 @@ -28,6 +28,12 @@ export class SpaceModelEntity extends AbstractEntity { }) public modelName: string; + @Column({ + nullable: false, + default: false, + }) + public disabled: boolean; + @ManyToOne(() => ProjectEntity, (project) => project.spaceModels, { nullable: false, onDelete: 'CASCADE', diff --git a/libs/common/src/modules/space-model/entities/space-product-item-model.entity.ts b/libs/common/src/modules/space-model/entities/space-product-item-model.entity.ts index 062418f..1d7bd09 100644 --- a/libs/common/src/modules/space-model/entities/space-product-item-model.entity.ts +++ b/libs/common/src/modules/space-model/entities/space-product-item-model.entity.ts @@ -20,6 +20,12 @@ export class SpaceProductItemModelEntity extends AbstractEntity SpaceProductItemEntity, (spaceProductItem) => spaceProductItem.spaceProductItemModel, 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 13b2b33..9fdcd83 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 @@ -30,6 +30,12 @@ export class SpaceProductModelEntity extends AbstractEntity SpaceProductItemModelEntity, (item) => item.spaceProductModel, diff --git a/libs/common/src/modules/space/entities/space-link.entity.ts b/libs/common/src/modules/space/entities/space-link.entity.ts index a62ce4f..da11eb7 100644 --- a/libs/common/src/modules/space/entities/space-link.entity.ts +++ b/libs/common/src/modules/space/entities/space-link.entity.ts @@ -13,6 +13,12 @@ export class SpaceLinkEntity extends AbstractEntity { @JoinColumn({ name: 'end_space_id' }) public endSpace: SpaceEntity; + @Column({ + nullable: false, + default: false, + }) + public disabled: boolean; + @Column({ nullable: false, enum: Object.values(Direction), 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 c53dfd4..ebbe572 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 @@ -16,6 +16,12 @@ export class SpaceProductItemEntity extends AbstractEntity }) public spaceProduct: SpaceProductEntity; + @Column({ + nullable: false, + default: false, + }) + public disabled: boolean; + @ManyToOne( () => SpaceProductItemModelEntity, (spaceProductItemModel) => spaceProductItemModel.items, 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 5f8a062..c268302 100644 --- a/libs/common/src/modules/space/entities/space-product.entity.ts +++ b/libs/common/src/modules/space/entities/space-product.entity.ts @@ -27,6 +27,12 @@ export class SpaceProductEntity extends AbstractEntity { }) productCount: number; + @Column({ + nullable: false, + default: false, + }) + public disabled: boolean; + @OneToMany(() => SpaceProductItemEntity, (item) => item.spaceProduct, { cascade: true, }) diff --git a/libs/common/src/modules/space/entities/space.entity.ts b/libs/common/src/modules/space/entities/space.entity.ts index 63beed5..d6133da 100644 --- a/libs/common/src/modules/space/entities/space.entity.ts +++ b/libs/common/src/modules/space/entities/space.entity.ts @@ -60,6 +60,12 @@ export class SpaceEntity extends AbstractEntity { @OneToMany(() => UserSpaceEntity, (userSpace) => userSpace.space) userSpaces: UserSpaceEntity[]; + @Column({ + nullable: false, + default: false, + }) + public disabled: boolean; + @OneToMany(() => SubspaceEntity, (subspace) => subspace.space, { nullable: true, }) 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 1ec7958..51e206f 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 @@ -20,6 +20,12 @@ export class SubspaceProductItemEntity extends AbstractEntity SubspaceProductItemModelEntity, (model) => model.items, { nullable: true, }) 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 87ed807..501f539 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 @@ -3,10 +3,7 @@ import { Column, Entity, ManyToOne, OneToMany } from 'typeorm'; import { SubspaceEntity } from './subspace.entity'; import { AbstractEntity } from '@app/common/modules/abstract/entities/abstract.entity'; import { SubspaceProductItemEntity } from './subspace-product-item.entity'; -import { - SubspaceProductItemModelEntity, - SubspaceProductModelEntity, -} from '@app/common/modules/space-model'; +import { SubspaceProductModelEntity } from '@app/common/modules/space-model'; import { SpaceProductModelDto } from '../../dtos'; @Entity({ name: 'subspace-product' }) @@ -24,6 +21,12 @@ export class SubspaceProductEntity extends AbstractEntity }) productCount: number; + @Column({ + nullable: false, + default: false, + }) + public disabled: boolean; + @ManyToOne(() => SubspaceEntity, (subspace) => subspace.subspaceProducts, { nullable: false, }) 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 bc6ff06..6ad7751 100644 --- a/libs/common/src/modules/space/entities/subspace/subspace.entity.ts +++ b/libs/common/src/modules/space/entities/subspace/subspace.entity.ts @@ -22,11 +22,16 @@ export class SubspaceEntity extends AbstractEntity { @ManyToOne(() => SpaceEntity, (space) => space.subspaces, { nullable: false, - onDelete: 'CASCADE', }) @JoinColumn({ name: 'space_uuid' }) space: SpaceEntity; + @Column({ + nullable: false, + default: false, + }) + public disabled: boolean; + @OneToMany(() => DeviceEntity, (device) => device.subspace, { nullable: true, }) From aefe788dd02beaf375102436b5b66df7aa896821 Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Thu, 19 Dec 2024 10:29:21 +0400 Subject: [PATCH 07/45] added device-to-tag --- .../src/modules/device/entities/device.entity.ts | 12 +++++++++++- .../space/entities/space-product-item.entity.ts | 8 +++++++- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/libs/common/src/modules/device/entities/device.entity.ts b/libs/common/src/modules/device/entities/device.entity.ts index 9a75950..ade7d99 100644 --- a/libs/common/src/modules/device/entities/device.entity.ts +++ b/libs/common/src/modules/device/entities/device.entity.ts @@ -6,10 +6,15 @@ import { Unique, Index, JoinColumn, + OneToOne, } from 'typeorm'; import { AbstractEntity } from '../../abstract/entities/abstract.entity'; import { DeviceDto, DeviceUserPermissionDto } from '../dtos/device.dto'; -import { SpaceEntity, SubspaceEntity } from '../../space/entities'; +import { + SpaceEntity, + SpaceProductItemEntity, + SubspaceEntity, +} from '../../space/entities'; import { ProductEntity } from '../../product/entities'; import { UserEntity } from '../../user/entities'; import { DeviceNotificationDto } from '../dtos'; @@ -74,6 +79,11 @@ export class DeviceEntity extends AbstractEntity { @OneToMany(() => SceneDeviceEntity, (sceneDevice) => sceneDevice.device, {}) sceneDevices: SceneDeviceEntity[]; + @OneToOne(() => SpaceProductItemEntity, (tag) => tag.device, { + nullable: true, + }) + public tag?: SpaceProductItemEntity; + constructor(partial: Partial) { super(); Object.assign(this, partial); 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 ebbe572..d9add42 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 @@ -1,8 +1,9 @@ -import { Column, Entity, ManyToOne } from 'typeorm'; +import { Column, Entity, ManyToOne, OneToOne } from 'typeorm'; import { SpaceProductEntity } from './space-product.entity'; import { AbstractEntity } from '../../abstract/entities/abstract.entity'; import { SpaceProductItemDto } from '../dtos'; import { SpaceProductItemModelEntity } from '../../space-model'; +import { DeviceEntity } from '../../device/entities'; @Entity({ name: 'space-product-item' }) export class SpaceProductItemEntity extends AbstractEntity { @@ -30,4 +31,9 @@ export class SpaceProductItemEntity extends AbstractEntity }, ) public spaceProductItemModel?: SpaceProductItemModelEntity; + + @OneToOne(() => DeviceEntity, (device) => device.tag, { + nullable: true, + }) + public device?: DeviceEntity; } From 5e089f76ac0ecfe9b2735da9f5a7b50dcf657a2a Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Thu, 19 Dec 2024 11:21:33 +0400 Subject: [PATCH 08/45] for now --- .../handlers/propate-subspace-handler.ts | 52 ++++++++++++++++--- .../interfaces/update-subspace.interface.ts | 1 + .../subspace/subspace-model.service.ts | 36 +++++++++---- 3 files changed, 72 insertions(+), 17 deletions(-) diff --git a/src/space-model/handlers/propate-subspace-handler.ts b/src/space-model/handlers/propate-subspace-handler.ts index e7a4db7..158bad3 100644 --- a/src/space-model/handlers/propate-subspace-handler.ts +++ b/src/space-model/handlers/propate-subspace-handler.ts @@ -7,6 +7,7 @@ import { SubspaceProductRepository, SubspaceRepository, } from '@app/common/modules/space/repositories/subspace.repository'; +import { IUpdateSubspaceModelInterface } from '../interfaces'; @CommandHandler(PropogateSubspaceCommand) export class PropogateSubspaceHandler @@ -24,14 +25,14 @@ export class PropogateSubspaceHandler async execute(command: PropogateSubspaceCommand): Promise { try { const newSubspaceModels = command.param?.new; + const updateSubspaceModels = command.param?.update; - if (!newSubspaceModels || newSubspaceModels.length === 0) { - this.logger.warn('No new subspace models provided.'); + if (!newSubspaceModels && !updateSubspaceModels) { + this.logger.warn('No new or updated subspace models provided.'); return; } - const spaceModelUuid = - newSubspaceModels[0]?.subspaceModel?.spaceModel?.uuid; + const spaceModelUuid = command.param.spaceModelUuid; if (!spaceModelUuid) { this.logger.error( @@ -47,7 +48,13 @@ export class PropogateSubspaceHandler return; } - await this.processSubspaces(newSubspaceModels, spaces); + if (newSubspaceModels && newSubspaceModels.length > 0) { + await this.processNewSubspaces(newSubspaceModels, spaces); + } + + if (updateSubspaceModels && updateSubspaceModels.length > 0) { + await this.updateSubspaces(updateSubspaceModels); + } } catch (error) { this.logger.error( 'Error in PropogateSubspaceHandler execution', @@ -56,7 +63,40 @@ export class PropogateSubspaceHandler } } - private async processSubspaces( + private async updateSubspaces(models: IUpdateSubspaceModelInterface[]) { + try { + for (const model of models) { + try { + const subspaceModelUuid = model.uuid; + if (model.subspaceName) { + const subspaces = await this.subspaceRepository.find({ + where: { + subSpaceModel: { + uuid: subspaceModelUuid, + }, + }, + }); + + if (subspaces.length > 0) { + await this.subspaceRepository.update(subspaces, { + subspaceName: model.subspaceName, + }); + } + } + } catch (innerError) { + this.logger.error( + `Error updating subspace model with UUID: ${model.uuid}`, + innerError.stack, + ); + } + } + } catch (error) { + this.logger.error('Error in updateSubspaces method', error.stack); + throw new Error('Failed to update subspaces.'); + } + } + + private async processNewSubspaces( newSubspaceModels: any[], spaces: SpaceEntity[], ) { diff --git a/src/space-model/interfaces/update-subspace.interface.ts b/src/space-model/interfaces/update-subspace.interface.ts index 4d58469..a49491f 100644 --- a/src/space-model/interfaces/update-subspace.interface.ts +++ b/src/space-model/interfaces/update-subspace.interface.ts @@ -15,6 +15,7 @@ export interface ProductModelInterface { } export interface IModifySubspaceModelInterface { + spaceModelUuid: string; new?: AddSubspaceModelInterface[]; update?: IUpdateSubspaceModelInterface[]; delete?: IDeletedSubsaceModelInterface[]; diff --git a/src/space-model/services/subspace/subspace-model.service.ts b/src/space-model/services/subspace/subspace-model.service.ts index f69d859..bcf5433 100644 --- a/src/space-model/services/subspace/subspace-model.service.ts +++ b/src/space-model/services/subspace/subspace-model.service.ts @@ -75,9 +75,8 @@ export class SubSpaceModelService { queryRunner: QueryRunner, ) { const updateResults: IUpdateSubspaceModelInterface[] = []; - - for (const dto of updateDtos) { - try { + try { + for (const dto of updateDtos) { const subspaceModel = await this.findOne(dto.subspaceUuid); const updateResult: IUpdateSubspaceModelInterface = { uuid: dto.subspaceUuid, @@ -94,14 +93,18 @@ export class SubSpaceModelService { } updateResults.push(updateResult); - } catch (error) { - console.error( - `SubspaceModel with ${dto.subspaceUuid} not able to update ${error}`, - ); } - } - return updateResults; + return updateResults; + } catch (error) { + if (error instanceof HttpException) { + throw error; + } + throw new HttpException( + error.message || 'Failed to update SpaceModels', + HttpStatus.INTERNAL_SERVER_ERROR, + ); + } } async deleteSubspaceModels( @@ -179,7 +182,9 @@ export class SubSpaceModelService { spaceModel: SpaceModelEntity, queryRunner: QueryRunner, ) { - const subspaces: IModifySubspaceModelInterface = {}; + const subspaces: IModifySubspaceModelInterface = { + spaceModelUuid: spaceModel.uuid, + }; try { if (dto.add) { const addedSubspaces = await this.createSubSpaceModels( @@ -194,11 +199,20 @@ export class SubSpaceModelService { queryRunner, ); subspaces.update = updatedSubspaces; + } else if (dto.delete) { + const deletedSubspaces = await this.deleteSubspaceModels( + dto.delete, + queryRunner, + ); + subspaces.delete = deletedSubspaces; } return subspaces; } catch (error) { + if (error instanceof HttpException) { + throw error; + } throw new HttpException( - error.message || 'Failed to update SpaceModel', + error.message || 'Failed to modify SpaceModels', HttpStatus.INTERNAL_SERVER_ERROR, ); } From 7f19073ed6493f95592a9f7d7c7aa69bb7594e49 Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Thu, 19 Dec 2024 15:48:10 +0400 Subject: [PATCH 09/45] propagate delete subspace model --- .../handlers/propate-subspace-handler.ts | 75 +++++++++++++------ .../services/space-product-model.service.ts | 2 +- 2 files changed, 52 insertions(+), 25 deletions(-) diff --git a/src/space-model/handlers/propate-subspace-handler.ts b/src/space-model/handlers/propate-subspace-handler.ts index 158bad3..82431bd 100644 --- a/src/space-model/handlers/propate-subspace-handler.ts +++ b/src/space-model/handlers/propate-subspace-handler.ts @@ -7,7 +7,10 @@ import { SubspaceProductRepository, SubspaceRepository, } from '@app/common/modules/space/repositories/subspace.repository'; -import { IUpdateSubspaceModelInterface } from '../interfaces'; +import { + IDeletedSubsaceModelInterface, + IUpdateSubspaceModelInterface, +} from '../interfaces'; @CommandHandler(PropogateSubspaceCommand) export class PropogateSubspaceHandler @@ -26,6 +29,7 @@ export class PropogateSubspaceHandler try { const newSubspaceModels = command.param?.new; const updateSubspaceModels = command.param?.update; + const deleteSubspaceModels = command.param?.delete; if (!newSubspaceModels && !updateSubspaceModels) { this.logger.warn('No new or updated subspace models provided.'); @@ -55,6 +59,10 @@ export class PropogateSubspaceHandler if (updateSubspaceModels && updateSubspaceModels.length > 0) { await this.updateSubspaces(updateSubspaceModels); } + + if (deleteSubspaceModels && deleteSubspaceModels.length > 0) { + await this.deleteSubspaces(deleteSubspaceModels); + } } catch (error) { this.logger.error( 'Error in PropogateSubspaceHandler execution', @@ -63,36 +71,55 @@ export class PropogateSubspaceHandler } } - private async updateSubspaces(models: IUpdateSubspaceModelInterface[]) { + private async updateSubspaces( + models: IUpdateSubspaceModelInterface[], + ): Promise { try { - for (const model of models) { - try { - const subspaceModelUuid = model.uuid; - if (model.subspaceName) { - const subspaces = await this.subspaceRepository.find({ - where: { - subSpaceModel: { - uuid: subspaceModelUuid, - }, - }, - }); + const updatePromises = []; - if (subspaces.length > 0) { - await this.subspaceRepository.update(subspaces, { - subspaceName: model.subspaceName, - }); - } - } - } catch (innerError) { - this.logger.error( - `Error updating subspace model with UUID: ${model.uuid}`, - innerError.stack, + for (const model of models) { + const { uuid: subspaceModelUuid, subspaceName } = model; + + if (subspaceName) { + updatePromises.push( + this.subspaceRepository + .createQueryBuilder() + .update() + .set({ subspaceName }) + .where('subSpaceModelUuid = :uuid', { uuid: subspaceModelUuid }) + .execute(), ); } } + + await Promise.all(updatePromises); } catch (error) { this.logger.error('Error in updateSubspaces method', error.stack); - throw new Error('Failed to update subspaces.'); + } + } + + private async deleteSubspaces(models: IDeletedSubsaceModelInterface[]) { + try { + const updatePromises = []; + + for (const model of models) { + const { uuid: subspaceModelUuid } = model; + + if (subspaceModelUuid) { + updatePromises.push( + this.subspaceRepository + .createQueryBuilder() + .update() + .set({ disabled: true }) + .where('subSpaceModelUuid = :uuid', { uuid: subspaceModelUuid }) + .execute(), + ); + } + } + + await Promise.all(updatePromises); + } catch (error) { + this.logger.error('Error in delete subspace models', error.stack); } } diff --git a/src/space-model/services/space-product-model.service.ts b/src/space-model/services/space-product-model.service.ts index 0f1e93e..36695be 100644 --- a/src/space-model/services/space-product-model.service.ts +++ b/src/space-model/services/space-product-model.service.ts @@ -47,7 +47,7 @@ export class SpaceProductModelService extends BaseProductModelService { const savedModel = savedProductModels[index]; return this.spaceProductItemModelService.createProdutItemModel( dto.items, - savedModel, // Pass the saved model + savedModel, spaceModel, queryRunner, ); From 7a82d88aa54a201cace2d01be0a7756f482cb728 Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Thu, 19 Dec 2024 16:18:55 +0400 Subject: [PATCH 10/45] changed name of dtos --- .../dtos/create-space-model.dto.ts | 2 +- .../dtos/create-space-product-model.dto.ts | 28 +++++++++++++++++++ src/space-model/dtos/index.ts | 3 +- .../create-subspace-model.dto.ts | 2 +- .../dtos/subspaces-model-dtos/index.ts | 2 ++ .../update-subspace-model.dto.ts | 2 +- .../dtos/update-space-model.dto.ts | 12 ++++---- .../subspace/subspace-model.service.ts | 4 +-- 8 files changed, 43 insertions(+), 12 deletions(-) rename src/space-model/dtos/{ => subspaces-model-dtos}/create-subspace-model.dto.ts (89%) rename src/space-model/dtos/{ => subspaces-model-dtos}/update-subspace-model.dto.ts (89%) diff --git a/src/space-model/dtos/create-space-model.dto.ts b/src/space-model/dtos/create-space-model.dto.ts index 9506728..2988e53 100644 --- a/src/space-model/dtos/create-space-model.dto.ts +++ b/src/space-model/dtos/create-space-model.dto.ts @@ -1,7 +1,7 @@ import { ApiProperty } from '@nestjs/swagger'; import { IsNotEmpty, IsString, IsArray, ValidateNested } from 'class-validator'; import { Type } from 'class-transformer'; -import { CreateSubspaceModelDto } from './create-subspace-model.dto'; +import { CreateSubspaceModelDto } from './subspaces-model-dtos/create-subspace-model.dto'; import { CreateSpaceProductModelDto } from './create-space-product-model.dto'; export class CreateSpaceModelDto { 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 a4379a1..2dab9aa 100644 --- a/src/space-model/dtos/create-space-product-model.dto.ts +++ b/src/space-model/dtos/create-space-product-model.dto.ts @@ -37,3 +37,31 @@ export class CreateSpaceProductModelDto { @Type(() => CreateProductItemModelDto) items: CreateProductItemModelDto[]; } + +export class UpdateSpaceProductModelDto { + @ApiProperty({ + description: 'ID of the product model', + example: 'product-uuid', + }) + @IsNotEmpty() + @IsString() + productModelUuid: string; + + @ApiProperty({ + description: 'Number of products in the model', + example: 3, + }) + @IsNotEmpty() + @IsInt() + productCount: number; + + @ApiProperty({ + description: 'Specific names for each product item', + type: [CreateProductItemModelDto], + }) + @IsArray() + @ArrayNotEmpty() + @ValidateNested({ each: true }) + @Type(() => CreateProductItemModelDto) + items: CreateProductItemModelDto[]; +} diff --git a/src/space-model/dtos/index.ts b/src/space-model/dtos/index.ts index e7b2373..0f46c63 100644 --- a/src/space-model/dtos/index.ts +++ b/src/space-model/dtos/index.ts @@ -1,8 +1,7 @@ export * from './create-space-model.dto'; export * from './create-space-product-item-model.dto'; export * from './create-space-product-model.dto'; -export * from './create-subspace-model.dto'; export * from './project-param.dto'; export * from './update-space-model.dto'; export * from './space-model-param'; -export * from './update-subspace-model.dto'; +export * from './subspaces-model-dtos'; diff --git a/src/space-model/dtos/create-subspace-model.dto.ts b/src/space-model/dtos/subspaces-model-dtos/create-subspace-model.dto.ts similarity index 89% rename from src/space-model/dtos/create-subspace-model.dto.ts rename to src/space-model/dtos/subspaces-model-dtos/create-subspace-model.dto.ts index f8dbcdd..d14cb91 100644 --- a/src/space-model/dtos/create-subspace-model.dto.ts +++ b/src/space-model/dtos/subspaces-model-dtos/create-subspace-model.dto.ts @@ -6,7 +6,7 @@ import { IsString, ValidateNested, } from 'class-validator'; -import { CreateSpaceProductModelDto } from './create-space-product-model.dto'; +import { CreateSpaceProductModelDto } from '../create-space-product-model.dto'; import { Type } from 'class-transformer'; export class CreateSubspaceModelDto { diff --git a/src/space-model/dtos/subspaces-model-dtos/index.ts b/src/space-model/dtos/subspaces-model-dtos/index.ts index e70ef7b..698a520 100644 --- a/src/space-model/dtos/subspaces-model-dtos/index.ts +++ b/src/space-model/dtos/subspaces-model-dtos/index.ts @@ -1 +1,3 @@ export * from './delete-subspace-model.dto'; +export * from './create-subspace-model.dto'; +export * from './update-subspace-model.dto'; diff --git a/src/space-model/dtos/update-subspace-model.dto.ts b/src/space-model/dtos/subspaces-model-dtos/update-subspace-model.dto.ts similarity index 89% rename from src/space-model/dtos/update-subspace-model.dto.ts rename to src/space-model/dtos/subspaces-model-dtos/update-subspace-model.dto.ts index ee25bd2..962e979 100644 --- a/src/space-model/dtos/update-subspace-model.dto.ts +++ b/src/space-model/dtos/subspaces-model-dtos/update-subspace-model.dto.ts @@ -7,7 +7,7 @@ import { IsOptional, ValidateNested, } from 'class-validator'; -import { CreateSpaceProductModelDto } from './create-space-product-model.dto'; +import { CreateSpaceProductModelDto } from '../create-space-product-model.dto'; export class UpdateSubspaceModelDto { @ApiProperty({ diff --git a/src/space-model/dtos/update-space-model.dto.ts b/src/space-model/dtos/update-space-model.dto.ts index 62a6bc1..7a7051b 100644 --- a/src/space-model/dtos/update-space-model.dto.ts +++ b/src/space-model/dtos/update-space-model.dto.ts @@ -1,11 +1,13 @@ import { ApiProperty } from '@nestjs/swagger'; import { IsArray, IsOptional, IsString, ValidateNested } from 'class-validator'; -import { CreateSubspaceModelDto } from './create-subspace-model.dto'; +import { CreateSubspaceModelDto } from './subspaces-model-dtos/create-subspace-model.dto'; import { Type } from 'class-transformer'; -import { UpdateSubspaceModelDto } from './update-subspace-model.dto'; -import { DeleteSubspaceModelDto } from './subspaces-model-dtos'; +import { + DeleteSubspaceModelDto, + UpdateSubspaceModelDto, +} from './subspaces-model-dtos'; -export class UpdateSubspacesModelDto { +export class ModifySubspacesModelDto { @ApiProperty({ description: 'List of subspaces to add', type: [CreateSubspaceModelDto], @@ -50,5 +52,5 @@ export class UpdateSpaceModelDto { modelName?: string; @IsOptional() - subspaceModels?: UpdateSubspacesModelDto; + subspaceModels?: ModifySubspacesModelDto; } diff --git a/src/space-model/services/subspace/subspace-model.service.ts b/src/space-model/services/subspace/subspace-model.service.ts index bcf5433..305323d 100644 --- a/src/space-model/services/subspace/subspace-model.service.ts +++ b/src/space-model/services/subspace/subspace-model.service.ts @@ -6,7 +6,7 @@ import { HttpException, HttpStatus, Injectable } from '@nestjs/common'; import { CreateSubspaceModelDto, UpdateSubspaceModelDto, - UpdateSubspacesModelDto, + ModifySubspacesModelDto, } from '../../dtos'; import { QueryRunner } from 'typeorm'; import { SubspaceProductModelService } from './subspace-product-model.service'; @@ -178,7 +178,7 @@ export class SubSpaceModelService { } async modifySubSpaceModels( - dto: UpdateSubspacesModelDto, + dto: ModifySubspacesModelDto, spaceModel: SpaceModelEntity, queryRunner: QueryRunner, ) { From 038f93f16a49023d55f1a7ebcec91a5eaaa1ea8f Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Fri, 20 Dec 2024 08:03:25 +0400 Subject: [PATCH 11/45] fixed dtos --- .../dtos/create-space-product-model.dto.ts | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/space-model/dtos/create-space-product-model.dto.ts b/src/space-model/dtos/create-space-product-model.dto.ts index 2dab9aa..03da688 100644 --- a/src/space-model/dtos/create-space-product-model.dto.ts +++ b/src/space-model/dtos/create-space-product-model.dto.ts @@ -6,6 +6,7 @@ import { ValidateNested, IsInt, ArrayNotEmpty, + IsOptional, } from 'class-validator'; import { Type } from 'class-transformer'; import { CreateProductItemModelDto } from './create-space-product-item-model.dto'; @@ -65,3 +66,23 @@ export class UpdateSpaceProductModelDto { @Type(() => CreateProductItemModelDto) items: CreateProductItemModelDto[]; } + +export class ModifyProductItemModelDto { + @IsArray() + @ApiProperty({ + description: 'Create the product model ', + type: [CreateSpaceProductModelDto], + }) + @ValidateNested({ each: true }) + @IsOptional() + add?: CreateSpaceProductModelDto[]; + + @IsArray() + @ApiProperty({ + description: 'Update the product model ', + type: [UpdateSpaceProductModelDto], + }) + @ValidateNested({ each: true }) + @IsOptional() + update?: UpdateSpaceProductModelDto[]; +} From 9bb59f04742f8ce64b5e7428040e0ac64837cec4 Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Fri, 20 Dec 2024 08:05:04 +0400 Subject: [PATCH 12/45] added product model dtos --- .../dtos/create-space-product-model.dto.ts | 20 ++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) 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 03da688..3d88ca4 100644 --- a/src/space-model/dtos/create-space-product-model.dto.ts +++ b/src/space-model/dtos/create-space-product-model.dto.ts @@ -67,7 +67,17 @@ export class UpdateSpaceProductModelDto { items: CreateProductItemModelDto[]; } -export class ModifyProductItemModelDto { +export class DeleteProductModelDto { + @ApiProperty({ + description: 'ID of the product model', + example: 'product-uuid', + }) + @IsNotEmpty() + @IsString() + productModelUuid: string; +} + +export class ModifyProductModelDto { @IsArray() @ApiProperty({ description: 'Create the product model ', @@ -85,4 +95,12 @@ export class ModifyProductItemModelDto { @ValidateNested({ each: true }) @IsOptional() update?: UpdateSpaceProductModelDto[]; + + @IsArray() + @ApiProperty({ + description: 'Update the product model ', + type: [UpdateSpaceProductModelDto], + }) + @IsOptional() + delete?: DeleteProductModelDto[]; } From 7bfa0c32a8ad10b62dfba2eeb99c0ab560781547 Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Fri, 20 Dec 2024 08:08:17 +0400 Subject: [PATCH 13/45] changed dto name --- .../common/services/base-product-model.service.ts | 4 ++-- src/space-model/dtos/create-space-model.dto.ts | 8 ++++---- src/space-model/dtos/index.ts | 2 +- .../create-space-product-model.dto.ts | 8 ++++---- src/space-model/dtos/product-model-dtos/index.ts | 1 + .../subspaces-model-dtos/create-subspace-model.dto.ts | 8 ++++---- .../subspaces-model-dtos/update-subspace-model.dto.ts | 8 ++++---- src/space-model/services/space-product-model.service.ts | 4 ++-- .../services/subspace/subspace-product-model.service.ts | 4 ++-- 9 files changed, 24 insertions(+), 23 deletions(-) rename src/space-model/dtos/{ => product-model-dtos}/create-space-product-model.dto.ts (91%) create mode 100644 src/space-model/dtos/product-model-dtos/index.ts diff --git a/src/space-model/common/services/base-product-model.service.ts b/src/space-model/common/services/base-product-model.service.ts index b83511a..c3230a3 100644 --- a/src/space-model/common/services/base-product-model.service.ts +++ b/src/space-model/common/services/base-product-model.service.ts @@ -1,12 +1,12 @@ import { HttpException, HttpStatus } from '@nestjs/common'; -import { CreateSpaceProductModelDto } from 'src/space-model/dtos'; +import { CreateProductModelDto } from '../../dtos'; import { ProductService } from '../../../product/services'; export abstract class BaseProductModelService { constructor(private readonly productService: ProductService) {} protected async validateProductCount( - dto: CreateSpaceProductModelDto, + dto: CreateProductModelDto, ): Promise { const productItemCount = dto.items.length; if (dto.productCount !== productItemCount) { diff --git a/src/space-model/dtos/create-space-model.dto.ts b/src/space-model/dtos/create-space-model.dto.ts index 2988e53..31ff764 100644 --- a/src/space-model/dtos/create-space-model.dto.ts +++ b/src/space-model/dtos/create-space-model.dto.ts @@ -2,7 +2,7 @@ import { ApiProperty } from '@nestjs/swagger'; import { IsNotEmpty, IsString, IsArray, ValidateNested } from 'class-validator'; import { Type } from 'class-transformer'; import { CreateSubspaceModelDto } from './subspaces-model-dtos/create-subspace-model.dto'; -import { CreateSpaceProductModelDto } from './create-space-product-model.dto'; +import { CreateProductModelDto } from './product-model-dtos'; export class CreateSpaceModelDto { @ApiProperty({ @@ -24,10 +24,10 @@ export class CreateSpaceModelDto { @ApiProperty({ description: 'List of products included in the model', - type: [CreateSpaceProductModelDto], + type: [CreateProductModelDto], }) @IsArray() @ValidateNested({ each: true }) - @Type(() => CreateSpaceProductModelDto) - spaceProductModels?: CreateSpaceProductModelDto[]; + @Type(() => CreateProductModelDto) + spaceProductModels?: CreateProductModelDto[]; } diff --git a/src/space-model/dtos/index.ts b/src/space-model/dtos/index.ts index 0f46c63..20aaebc 100644 --- a/src/space-model/dtos/index.ts +++ b/src/space-model/dtos/index.ts @@ -1,6 +1,6 @@ export * from './create-space-model.dto'; export * from './create-space-product-item-model.dto'; -export * from './create-space-product-model.dto'; +export * from './product-model-dtos'; export * from './project-param.dto'; export * from './update-space-model.dto'; export * from './space-model-param'; diff --git a/src/space-model/dtos/create-space-product-model.dto.ts b/src/space-model/dtos/product-model-dtos/create-space-product-model.dto.ts similarity index 91% rename from src/space-model/dtos/create-space-product-model.dto.ts rename to src/space-model/dtos/product-model-dtos/create-space-product-model.dto.ts index 3d88ca4..2ff2a7f 100644 --- a/src/space-model/dtos/create-space-product-model.dto.ts +++ b/src/space-model/dtos/product-model-dtos/create-space-product-model.dto.ts @@ -9,9 +9,9 @@ import { IsOptional, } from 'class-validator'; import { Type } from 'class-transformer'; -import { CreateProductItemModelDto } from './create-space-product-item-model.dto'; +import { CreateProductItemModelDto } from '../create-space-product-item-model.dto'; -export class CreateSpaceProductModelDto { +export class CreateProductModelDto { @ApiProperty({ description: 'ID of the product associated with the model', example: 'product-uuid', @@ -81,11 +81,11 @@ export class ModifyProductModelDto { @IsArray() @ApiProperty({ description: 'Create the product model ', - type: [CreateSpaceProductModelDto], + type: [CreateProductModelDto], }) @ValidateNested({ each: true }) @IsOptional() - add?: CreateSpaceProductModelDto[]; + add?: CreateProductModelDto[]; @IsArray() @ApiProperty({ diff --git a/src/space-model/dtos/product-model-dtos/index.ts b/src/space-model/dtos/product-model-dtos/index.ts new file mode 100644 index 0000000..3fdad23 --- /dev/null +++ b/src/space-model/dtos/product-model-dtos/index.ts @@ -0,0 +1 @@ +export * from './create-space-product-model.dto'; diff --git a/src/space-model/dtos/subspaces-model-dtos/create-subspace-model.dto.ts b/src/space-model/dtos/subspaces-model-dtos/create-subspace-model.dto.ts index d14cb91..ce5d2d1 100644 --- a/src/space-model/dtos/subspaces-model-dtos/create-subspace-model.dto.ts +++ b/src/space-model/dtos/subspaces-model-dtos/create-subspace-model.dto.ts @@ -6,7 +6,7 @@ import { IsString, ValidateNested, } from 'class-validator'; -import { CreateSpaceProductModelDto } from '../create-space-product-model.dto'; +import { CreateProductModelDto } from '../product-model-dtos'; import { Type } from 'class-transformer'; export class CreateSubspaceModelDto { @@ -20,11 +20,11 @@ export class CreateSubspaceModelDto { @ApiProperty({ description: 'List of products included in the model', - type: [CreateSpaceProductModelDto], + type: [CreateProductModelDto], }) @IsArray() @IsOptional() @ValidateNested({ each: true }) - @Type(() => CreateSpaceProductModelDto) - spaceProductModels?: CreateSpaceProductModelDto[]; + @Type(() => CreateProductModelDto) + spaceProductModels?: CreateProductModelDto[]; } diff --git a/src/space-model/dtos/subspaces-model-dtos/update-subspace-model.dto.ts b/src/space-model/dtos/subspaces-model-dtos/update-subspace-model.dto.ts index 962e979..eb5305c 100644 --- a/src/space-model/dtos/subspaces-model-dtos/update-subspace-model.dto.ts +++ b/src/space-model/dtos/subspaces-model-dtos/update-subspace-model.dto.ts @@ -7,7 +7,7 @@ import { IsOptional, ValidateNested, } from 'class-validator'; -import { CreateSpaceProductModelDto } from '../create-space-product-model.dto'; +import { CreateProductModelDto } from '../product-model-dtos'; export class UpdateSubspaceModelDto { @ApiProperty({ @@ -24,11 +24,11 @@ export class UpdateSubspaceModelDto { @ApiProperty({ description: 'List of products included in the model', - type: [CreateSpaceProductModelDto], + type: [CreateProductModelDto], }) @IsArray() @IsOptional() @ValidateNested({ each: true }) - @Type(() => CreateSpaceProductModelDto) - spaceProductModels?: CreateSpaceProductModelDto[]; + @Type(() => CreateProductModelDto) + spaceProductModels?: CreateProductModelDto[]; } diff --git a/src/space-model/services/space-product-model.service.ts b/src/space-model/services/space-product-model.service.ts index 36695be..ccfa780 100644 --- a/src/space-model/services/space-product-model.service.ts +++ b/src/space-model/services/space-product-model.service.ts @@ -1,6 +1,6 @@ import { QueryRunner } from 'typeorm'; import { HttpException, HttpStatus, Injectable } from '@nestjs/common'; -import { CreateSpaceProductModelDto } from '../dtos'; +import { CreateProductModelDto } from '../dtos'; import { SpaceProductItemModelService } from './space-product-item-model.service'; import { BaseProductModelService } from '../common'; import { ProductService } from 'src/product/services'; @@ -20,7 +20,7 @@ export class SpaceProductModelService extends BaseProductModelService { } async createSpaceProductModels( - spaceProductModelDtos: CreateSpaceProductModelDto[], + spaceProductModelDtos: CreateProductModelDto[], spaceModel: SpaceModelEntity, queryRunner: QueryRunner, ) { diff --git a/src/space-model/services/subspace/subspace-product-model.service.ts b/src/space-model/services/subspace/subspace-product-model.service.ts index 8b3f9ac..9dfe733 100644 --- a/src/space-model/services/subspace/subspace-product-model.service.ts +++ b/src/space-model/services/subspace/subspace-product-model.service.ts @@ -5,7 +5,7 @@ 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 { CreateProductModelDto } from '../../dtos'; import { QueryRunner } from 'typeorm'; import { BaseProductModelService } from '../../common'; import { ProductService } from 'src/product/services'; @@ -21,7 +21,7 @@ export class SubspaceProductModelService extends BaseProductModelService { } async createSubspaceProductModels( - spaceProductModelDtos: CreateSpaceProductModelDto[], + spaceProductModelDtos: CreateProductModelDto[], spaceModel: SpaceModelEntity, subspaceModel: SubspaceModelEntity, queryRunner: QueryRunner, From bb63ac08e37182c520552eed5d93c01658155e65 Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Fri, 20 Dec 2024 08:18:25 +0400 Subject: [PATCH 14/45] updated product model dtos --- .../base-product-model.dto.ts | 12 ++ .../create-product-model.dto.ts | 39 +++++++ .../create-space-product-model.dto.ts | 106 ------------------ .../delete-product-model.dto.ts | 3 + .../dtos/product-model-dtos/index.ts | 5 +- .../product-model-modification.dto.ts | 34 ++++++ .../update-product-model.dto.ts | 32 ++++++ 7 files changed, 124 insertions(+), 107 deletions(-) create mode 100644 src/space-model/dtos/product-model-dtos/base-product-model.dto.ts create mode 100644 src/space-model/dtos/product-model-dtos/create-product-model.dto.ts delete mode 100644 src/space-model/dtos/product-model-dtos/create-space-product-model.dto.ts create mode 100644 src/space-model/dtos/product-model-dtos/delete-product-model.dto.ts create mode 100644 src/space-model/dtos/product-model-dtos/product-model-modification.dto.ts create mode 100644 src/space-model/dtos/product-model-dtos/update-product-model.dto.ts diff --git a/src/space-model/dtos/product-model-dtos/base-product-model.dto.ts b/src/space-model/dtos/product-model-dtos/base-product-model.dto.ts new file mode 100644 index 0000000..4a27559 --- /dev/null +++ b/src/space-model/dtos/product-model-dtos/base-product-model.dto.ts @@ -0,0 +1,12 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsNotEmpty, IsString } from 'class-validator'; + +export class BaseProductModelDto { + @ApiProperty({ + description: 'ID of the product model', + example: 'product-uuid', + }) + @IsNotEmpty() + @IsString() + productModelUuid: string; +} diff --git a/src/space-model/dtos/product-model-dtos/create-product-model.dto.ts b/src/space-model/dtos/product-model-dtos/create-product-model.dto.ts new file mode 100644 index 0000000..947fc59 --- /dev/null +++ b/src/space-model/dtos/product-model-dtos/create-product-model.dto.ts @@ -0,0 +1,39 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { + IsNotEmpty, + IsString, + IsArray, + ValidateNested, + IsInt, + ArrayNotEmpty, +} from 'class-validator'; +import { Type } from 'class-transformer'; +import { CreateProductItemModelDto } from '../create-space-product-item-model.dto'; + +export class CreateProductModelDto { + @ApiProperty({ + description: 'ID of the product associated with the model', + example: 'product-uuid', + }) + @IsNotEmpty() + @IsString() + productUuid: string; + + @ApiProperty({ + description: 'Number of products in the model', + example: 3, + }) + @IsNotEmpty() + @IsInt() + productCount: number; + + @ApiProperty({ + description: 'Specific names for each product item', + type: [CreateProductItemModelDto], + }) + @IsArray() + @ArrayNotEmpty() + @ValidateNested({ each: true }) + @Type(() => CreateProductItemModelDto) + items: CreateProductItemModelDto[]; +} diff --git a/src/space-model/dtos/product-model-dtos/create-space-product-model.dto.ts b/src/space-model/dtos/product-model-dtos/create-space-product-model.dto.ts deleted file mode 100644 index 2ff2a7f..0000000 --- a/src/space-model/dtos/product-model-dtos/create-space-product-model.dto.ts +++ /dev/null @@ -1,106 +0,0 @@ -import { ApiProperty } from '@nestjs/swagger'; -import { - IsNotEmpty, - IsString, - IsArray, - ValidateNested, - IsInt, - ArrayNotEmpty, - IsOptional, -} from 'class-validator'; -import { Type } from 'class-transformer'; -import { CreateProductItemModelDto } from '../create-space-product-item-model.dto'; - -export class CreateProductModelDto { - @ApiProperty({ - description: 'ID of the product associated with the model', - example: 'product-uuid', - }) - @IsNotEmpty() - @IsString() - productUuid: string; - - @ApiProperty({ - description: 'Number of products in the model', - example: 3, - }) - @IsNotEmpty() - @IsInt() - productCount: number; - - @ApiProperty({ - description: 'Specific names for each product item', - type: [CreateProductItemModelDto], - }) - @IsArray() - @ArrayNotEmpty() - @ValidateNested({ each: true }) - @Type(() => CreateProductItemModelDto) - items: CreateProductItemModelDto[]; -} - -export class UpdateSpaceProductModelDto { - @ApiProperty({ - description: 'ID of the product model', - example: 'product-uuid', - }) - @IsNotEmpty() - @IsString() - productModelUuid: string; - - @ApiProperty({ - description: 'Number of products in the model', - example: 3, - }) - @IsNotEmpty() - @IsInt() - productCount: number; - - @ApiProperty({ - description: 'Specific names for each product item', - type: [CreateProductItemModelDto], - }) - @IsArray() - @ArrayNotEmpty() - @ValidateNested({ each: true }) - @Type(() => CreateProductItemModelDto) - items: CreateProductItemModelDto[]; -} - -export class DeleteProductModelDto { - @ApiProperty({ - description: 'ID of the product model', - example: 'product-uuid', - }) - @IsNotEmpty() - @IsString() - productModelUuid: string; -} - -export class ModifyProductModelDto { - @IsArray() - @ApiProperty({ - description: 'Create the product model ', - type: [CreateProductModelDto], - }) - @ValidateNested({ each: true }) - @IsOptional() - add?: CreateProductModelDto[]; - - @IsArray() - @ApiProperty({ - description: 'Update the product model ', - type: [UpdateSpaceProductModelDto], - }) - @ValidateNested({ each: true }) - @IsOptional() - update?: UpdateSpaceProductModelDto[]; - - @IsArray() - @ApiProperty({ - description: 'Update the product model ', - type: [UpdateSpaceProductModelDto], - }) - @IsOptional() - delete?: DeleteProductModelDto[]; -} diff --git a/src/space-model/dtos/product-model-dtos/delete-product-model.dto.ts b/src/space-model/dtos/product-model-dtos/delete-product-model.dto.ts new file mode 100644 index 0000000..5653c8d --- /dev/null +++ b/src/space-model/dtos/product-model-dtos/delete-product-model.dto.ts @@ -0,0 +1,3 @@ +import { BaseProductModelDto } from './base-product-model.dto'; + +export class DeleteProductModelDto extends BaseProductModelDto {} diff --git a/src/space-model/dtos/product-model-dtos/index.ts b/src/space-model/dtos/product-model-dtos/index.ts index 3fdad23..b2dbd90 100644 --- a/src/space-model/dtos/product-model-dtos/index.ts +++ b/src/space-model/dtos/product-model-dtos/index.ts @@ -1 +1,4 @@ -export * from './create-space-product-model.dto'; +export * from './create-product-model.dto'; +export * from './delete-product-model.dto'; +export * from './update-product-model.dto'; +export * from './product-model-modification.dto'; diff --git a/src/space-model/dtos/product-model-dtos/product-model-modification.dto.ts b/src/space-model/dtos/product-model-dtos/product-model-modification.dto.ts new file mode 100644 index 0000000..8bf9133 --- /dev/null +++ b/src/space-model/dtos/product-model-dtos/product-model-modification.dto.ts @@ -0,0 +1,34 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsArray, ValidateNested, IsOptional } from 'class-validator'; + +import { CreateProductModelDto } from './create-product-model.dto'; +import { DeleteProductModelDto } from './delete-product-model.dto'; +import { UpdateProductModelDto } from './update-product-model.dto'; + +export class ProductModelModificationDto { + @IsArray() + @ApiProperty({ + description: 'Create the product model ', + type: [CreateProductModelDto], + }) + @ValidateNested({ each: true }) + @IsOptional() + add?: CreateProductModelDto[]; + + @IsArray() + @ApiProperty({ + description: 'Update the product model ', + type: [UpdateProductModelDto], + }) + @ValidateNested({ each: true }) + @IsOptional() + update?: UpdateProductModelDto[]; + + @IsArray() + @ApiProperty({ + description: 'Delete the product model ', + type: [DeleteProductModelDto], + }) + @IsOptional() + delete?: DeleteProductModelDto[]; +} diff --git a/src/space-model/dtos/product-model-dtos/update-product-model.dto.ts b/src/space-model/dtos/product-model-dtos/update-product-model.dto.ts new file mode 100644 index 0000000..0f0d64d --- /dev/null +++ b/src/space-model/dtos/product-model-dtos/update-product-model.dto.ts @@ -0,0 +1,32 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { Type } from 'class-transformer'; +import { + IsNotEmpty, + IsString, + IsInt, + IsArray, + ArrayNotEmpty, + ValidateNested, +} from 'class-validator'; +import { CreateProductItemModelDto } from '../create-space-product-item-model.dto'; +import { BaseProductModelDto } from './base-product-model.dto'; + +export class UpdateProductModelDto extends BaseProductModelDto { + @ApiProperty({ + description: 'Number of products in the model', + example: 3, + }) + @IsNotEmpty() + @IsInt() + productCount: number; + + @ApiProperty({ + description: 'Specific names for each product item', + type: [CreateProductItemModelDto], + }) + @IsArray() + @ArrayNotEmpty() + @ValidateNested({ each: true }) + @Type(() => CreateProductItemModelDto) + items: CreateProductItemModelDto[]; +} From e18214c717e775bca1db5f7049718656302e36a4 Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Fri, 20 Dec 2024 08:45:52 +0400 Subject: [PATCH 15/45] defined product model dto --- src/space-model/dtos/index.ts | 2 +- .../base-product-item-model.dto.ts | 12 +++++++ .../create-product-item-model.dto.ts} | 0 .../delete-product-item-model.dto.ts | 3 ++ .../dtos/product-item-model-dtos/index.ts | 4 +++ .../product-item-model-modification.ts | 34 +++++++++++++++++++ .../update-prodct-item-model.dto.ts | 13 +++++++ .../create-product-model.dto.ts | 2 +- .../update-product-model.dto.ts | 11 +++--- 9 files changed, 73 insertions(+), 8 deletions(-) create mode 100644 src/space-model/dtos/product-item-model-dtos/base-product-item-model.dto.ts rename src/space-model/dtos/{create-space-product-item-model.dto.ts => product-item-model-dtos/create-product-item-model.dto.ts} (100%) create mode 100644 src/space-model/dtos/product-item-model-dtos/delete-product-item-model.dto.ts create mode 100644 src/space-model/dtos/product-item-model-dtos/index.ts create mode 100644 src/space-model/dtos/product-item-model-dtos/product-item-model-modification.ts create mode 100644 src/space-model/dtos/product-item-model-dtos/update-prodct-item-model.dto.ts diff --git a/src/space-model/dtos/index.ts b/src/space-model/dtos/index.ts index 20aaebc..045d4aa 100644 --- a/src/space-model/dtos/index.ts +++ b/src/space-model/dtos/index.ts @@ -1,5 +1,5 @@ export * from './create-space-model.dto'; -export * from './create-space-product-item-model.dto'; +export * from './product-item-model-dtos'; export * from './product-model-dtos'; export * from './project-param.dto'; export * from './update-space-model.dto'; diff --git a/src/space-model/dtos/product-item-model-dtos/base-product-item-model.dto.ts b/src/space-model/dtos/product-item-model-dtos/base-product-item-model.dto.ts new file mode 100644 index 0000000..dc01713 --- /dev/null +++ b/src/space-model/dtos/product-item-model-dtos/base-product-item-model.dto.ts @@ -0,0 +1,12 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsNotEmpty, IsString } from 'class-validator'; + +export class BaseProductItemModelDto { + @ApiProperty({ + description: 'ID of the product item model', + example: 'product-item-uuid', + }) + @IsNotEmpty() + @IsString() + productModelUuid: string; +} diff --git a/src/space-model/dtos/create-space-product-item-model.dto.ts b/src/space-model/dtos/product-item-model-dtos/create-product-item-model.dto.ts similarity index 100% rename from src/space-model/dtos/create-space-product-item-model.dto.ts rename to src/space-model/dtos/product-item-model-dtos/create-product-item-model.dto.ts diff --git a/src/space-model/dtos/product-item-model-dtos/delete-product-item-model.dto.ts b/src/space-model/dtos/product-item-model-dtos/delete-product-item-model.dto.ts new file mode 100644 index 0000000..971a0d0 --- /dev/null +++ b/src/space-model/dtos/product-item-model-dtos/delete-product-item-model.dto.ts @@ -0,0 +1,3 @@ +import { BaseProductItemModelDto } from './base-product-item-model.dto'; + +export class DeleteProductItemModelDto extends BaseProductItemModelDto {} diff --git a/src/space-model/dtos/product-item-model-dtos/index.ts b/src/space-model/dtos/product-item-model-dtos/index.ts new file mode 100644 index 0000000..5225122 --- /dev/null +++ b/src/space-model/dtos/product-item-model-dtos/index.ts @@ -0,0 +1,4 @@ +export * from './create-product-item-model.dto'; +export * from './update-prodct-item-model.dto'; +export * from './delete-product-item-model.dto'; +export * from './product-item-model-modification' \ No newline at end of file diff --git a/src/space-model/dtos/product-item-model-dtos/product-item-model-modification.ts b/src/space-model/dtos/product-item-model-dtos/product-item-model-modification.ts new file mode 100644 index 0000000..04ca2d6 --- /dev/null +++ b/src/space-model/dtos/product-item-model-dtos/product-item-model-modification.ts @@ -0,0 +1,34 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsArray, ValidateNested, IsOptional } from 'class-validator'; + +import { CreateProductItemModelDto } from './create-product-item-model.dto'; +import { UpdateProductItemModelDto } from './update-prodct-item-model.dto'; +import { DeleteProductItemModelDto } from './delete-product-item-model.dto'; + +export class ProductItemModelModificationDto { + @IsArray() + @ApiProperty({ + description: 'Create the product item model ', + type: [CreateProductItemModelDto], + }) + @ValidateNested({ each: true }) + @IsOptional() + add?: CreateProductItemModelDto[]; + + @IsArray() + @ApiProperty({ + description: 'Update the product item model ', + type: [UpdateProductItemModelDto], + }) + @ValidateNested({ each: true }) + @IsOptional() + update?: UpdateProductItemModelDto[]; + + @IsArray() + @ApiProperty({ + description: 'Delete the product item model ', + type: [DeleteProductItemModelDto], + }) + @IsOptional() + delete?: DeleteProductItemModelDto[]; +} diff --git a/src/space-model/dtos/product-item-model-dtos/update-prodct-item-model.dto.ts b/src/space-model/dtos/product-item-model-dtos/update-prodct-item-model.dto.ts new file mode 100644 index 0000000..ef88432 --- /dev/null +++ b/src/space-model/dtos/product-item-model-dtos/update-prodct-item-model.dto.ts @@ -0,0 +1,13 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsNotEmpty, IsString } from 'class-validator'; +import { BaseProductItemModelDto } from './base-product-item-model.dto'; + +export class UpdateProductItemModelDto extends BaseProductItemModelDto { + @ApiProperty({ + description: 'Specific name for the product item', + example: 'Light 1', + }) + @IsNotEmpty() + @IsString() + tag: string; +} diff --git a/src/space-model/dtos/product-model-dtos/create-product-model.dto.ts b/src/space-model/dtos/product-model-dtos/create-product-model.dto.ts index 947fc59..0b4882e 100644 --- a/src/space-model/dtos/product-model-dtos/create-product-model.dto.ts +++ b/src/space-model/dtos/product-model-dtos/create-product-model.dto.ts @@ -8,7 +8,7 @@ import { ArrayNotEmpty, } from 'class-validator'; import { Type } from 'class-transformer'; -import { CreateProductItemModelDto } from '../create-space-product-item-model.dto'; +import { CreateProductItemModelDto } from '../product-item-model-dtos/create-product-item-model.dto'; export class CreateProductModelDto { @ApiProperty({ diff --git a/src/space-model/dtos/product-model-dtos/update-product-model.dto.ts b/src/space-model/dtos/product-model-dtos/update-product-model.dto.ts index 0f0d64d..60e7baf 100644 --- a/src/space-model/dtos/product-model-dtos/update-product-model.dto.ts +++ b/src/space-model/dtos/product-model-dtos/update-product-model.dto.ts @@ -2,14 +2,13 @@ import { ApiProperty } from '@nestjs/swagger'; import { Type } from 'class-transformer'; import { IsNotEmpty, - IsString, IsInt, IsArray, ArrayNotEmpty, ValidateNested, } from 'class-validator'; -import { CreateProductItemModelDto } from '../create-space-product-item-model.dto'; import { BaseProductModelDto } from './base-product-model.dto'; +import { ProductItemModelModificationDto } from '../product-item-model-dtos'; export class UpdateProductModelDto extends BaseProductModelDto { @ApiProperty({ @@ -21,12 +20,12 @@ export class UpdateProductModelDto extends BaseProductModelDto { productCount: number; @ApiProperty({ - description: 'Specific names for each product item', - type: [CreateProductItemModelDto], + description: 'Update product item', + type: [ProductItemModelModificationDto], }) @IsArray() @ArrayNotEmpty() @ValidateNested({ each: true }) - @Type(() => CreateProductItemModelDto) - items: CreateProductItemModelDto[]; + @Type(() => ProductItemModelModificationDto) + items: ProductItemModelModificationDto[]; } From 61c17c2e95284dc2355589d7ceacc2be3bddf0aa Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Fri, 20 Dec 2024 10:35:11 +0400 Subject: [PATCH 16/45] product item model --- .../base-product-item-model.service.ts | 19 +++ .../interfaces/update-subspace.interface.ts | 6 + .../subspace-product-item-model.service.ts | 146 ++++++++++++++++-- .../subspace-product-model.service.ts | 2 +- 4 files changed, 159 insertions(+), 14 deletions(-) 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 index 80c8119..4d67e6d 100644 --- a/src/space-model/common/services/base-product-item-model.service.ts +++ b/src/space-model/common/services/base-product-item-model.service.ts @@ -57,4 +57,23 @@ export abstract class BaseProductItemService { ); } } + + protected async saveProductItems( + productItems: T[], + targetRepository: any, + queryRunner: QueryRunner, + ): Promise { + try { + const savedItem = await queryRunner.manager.save( + targetRepository, + productItems, + ); + return savedItem; + } catch (error) { + throw new HttpException( + error.message || 'An error occurred while creating product items.', + HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + } } diff --git a/src/space-model/interfaces/update-subspace.interface.ts b/src/space-model/interfaces/update-subspace.interface.ts index a49491f..05982a4 100644 --- a/src/space-model/interfaces/update-subspace.interface.ts +++ b/src/space-model/interfaces/update-subspace.interface.ts @@ -21,6 +21,12 @@ export interface IModifySubspaceModelInterface { delete?: IDeletedSubsaceModelInterface[]; } +export interface IModifiedProductItemsModelsInterface { + new?: SubspaceProductItemModelEntity[]; + update?: SubspaceProductItemModelEntity[]; + delete?: string[]; +} + export interface IUpdateSubspaceModelInterface { subspaceName?: string; uuid: string; diff --git a/src/space-model/services/subspace/subspace-product-item-model.service.ts b/src/space-model/services/subspace/subspace-product-item-model.service.ts index 85989f8..cdd0688 100644 --- a/src/space-model/services/subspace/subspace-product-item-model.service.ts +++ b/src/space-model/services/subspace/subspace-product-item-model.service.ts @@ -7,7 +7,13 @@ import { SubspaceProductModelEntity, } from '@app/common/modules/space-model'; import { BaseProductItemService } from '../../common'; -import { CreateProductItemModelDto } from '../../dtos'; +import { + CreateProductItemModelDto, + DeleteProductItemModelDto, + ProductItemModelModificationDto, + UpdateProductItemModelDto, +} from '../../dtos'; +import { IModifiedProductItemsModelsInterface } from 'src/space-model/interfaces'; @Injectable() export class SubspaceProductItemModelService extends BaseProductItemService { @@ -17,7 +23,7 @@ export class SubspaceProductItemModelService extends BaseProductItemService { super(); } - async createProdutItemModel( + async createProductItemModel( itemModelDtos: CreateProductItemModelDto[], subspaceProductModel: SubspaceProductModelEntity, spaceModel: SpaceModelEntity, @@ -29,7 +35,9 @@ export class SubspaceProductItemModelService extends BaseProductItemService { HttpStatus.BAD_REQUEST, ); } + await this.validateTags(itemModelDtos, queryRunner, spaceModel); + try { const productItems = itemModelDtos.map((dto) => queryRunner.manager.create(this.subspaceProductItemRepository.target, { @@ -37,19 +45,131 @@ export class SubspaceProductItemModelService extends BaseProductItemService { subspaceProductModel, }), ); - - await queryRunner.manager.save(productItems); - return productItems; + return await this.saveProductItems( + productItems, + this.subspaceProductItemRepository, + queryRunner, + ); } 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, + this.handleException( + error, + 'An unexpected error occurred while creating product items.', ); } } + + async updateProductItemModel( + dtos: UpdateProductItemModelDto[], + spaceModel: SpaceModelEntity, + queryRunner: QueryRunner, + ): Promise { + try { + await this.validateTags(dtos, queryRunner, spaceModel); + + const productItemModels = await Promise.all( + dtos.map(async (dto) => { + const productItemModel = await this.findOne(dto.productModelUuid); + productItemModel.tag = dto.tag; + return productItemModel; + }), + ); + + return (await this.saveProductItems( + productItemModels, + this.subspaceProductItemRepository, + queryRunner, + )) as SubspaceProductItemModelEntity[]; + } catch (error) { + this.handleException( + error, + 'Failed to save Subspace Product Item Model.', + ); + } + } + + async deleteProductModel( + dtos: DeleteProductItemModelDto[], + queryRunner: QueryRunner, + ): Promise { + try { + const productItemModels = await Promise.all( + dtos.map(async (dto) => { + const productItemModel = await this.findOne(dto.productModelUuid); + productItemModel.disabled = true; + return productItemModel; + }), + ); + + const response = (await this.saveProductItems( + productItemModels, + this.subspaceProductItemRepository, + queryRunner, + )) as SubspaceProductItemModelEntity[]; + + return response.map((item) => item.uuid); + } catch (error) { + this.handleException(error, 'Failed to modify SpaceModels.'); + } + } + + async findOne(uuid: string): Promise { + const productItemModel = await this.subspaceProductItemRepository.findOne({ + where: { uuid }, + }); + + if (!productItemModel) { + throw new HttpException( + `Product item model not found for ${uuid}`, + HttpStatus.NOT_FOUND, + ); + } + + return productItemModel; + } + + async modifyProductItemModel( + dto: ProductItemModelModificationDto, + productModel: SubspaceProductModelEntity, + spaceModel: SpaceModelEntity, + queryRunner: QueryRunner, + ): Promise { + const productItemModels: IModifiedProductItemsModelsInterface = {}; + + try { + if (dto.add) { + productItemModels.new = await this.createProductItemModel( + dto.add, + productModel, + spaceModel, + queryRunner, + ); + } + if (dto.update) { + productItemModels.update = await this.updateProductItemModel( + dto.update, + spaceModel, + queryRunner, + ); + } + if (dto.delete) { + productItemModels.delete = await this.deleteProductModel( + dto.delete, + queryRunner, + ); + } + return productItemModels; + } catch (error) { + this.handleException(error, 'Failed to modify SpaceModels.'); + } + } + + private handleException(error: unknown, defaultMessage: string): never { + if (error instanceof HttpException) { + throw error; + } + throw new HttpException( + (error as Error).message || defaultMessage, + HttpStatus.INTERNAL_SERVER_ERROR, + ); + } } diff --git a/src/space-model/services/subspace/subspace-product-model.service.ts b/src/space-model/services/subspace/subspace-product-model.service.ts index 9dfe733..4e145bc 100644 --- a/src/space-model/services/subspace/subspace-product-model.service.ts +++ b/src/space-model/services/subspace/subspace-product-model.service.ts @@ -50,7 +50,7 @@ export class SubspaceProductModelService extends BaseProductModelService { spaceProductModelDtos.map(async (dto, index) => { const savedModel = savedProductModels[index]; const productItemModels = - await this.subspaceProductItemModelService.createProdutItemModel( + await this.subspaceProductItemModelService.createProductItemModel( dto.items, savedModel, spaceModel, From c78eeff7e6abbe0059fb6beec65fc20b5c15fa0b Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Fri, 20 Dec 2024 14:16:23 +0400 Subject: [PATCH 17/45] update product model --- .../update-product-model.dto.ts | 4 +- .../interfaces/update-subspace.interface.ts | 4 + .../subspace-product-item-model.service.ts | 2 +- .../subspace-product-model.service.ts | 79 +++++++++++++++++-- 4 files changed, 81 insertions(+), 8 deletions(-) diff --git a/src/space-model/dtos/product-model-dtos/update-product-model.dto.ts b/src/space-model/dtos/product-model-dtos/update-product-model.dto.ts index 60e7baf..224330e 100644 --- a/src/space-model/dtos/product-model-dtos/update-product-model.dto.ts +++ b/src/space-model/dtos/product-model-dtos/update-product-model.dto.ts @@ -12,7 +12,7 @@ import { ProductItemModelModificationDto } from '../product-item-model-dtos'; export class UpdateProductModelDto extends BaseProductModelDto { @ApiProperty({ - description: 'Number of products in the model', + description: 'Number of products to be modified in the model', example: 3, }) @IsNotEmpty() @@ -27,5 +27,5 @@ export class UpdateProductModelDto extends BaseProductModelDto { @ArrayNotEmpty() @ValidateNested({ each: true }) @Type(() => ProductItemModelModificationDto) - items: ProductItemModelModificationDto[]; + items: ProductItemModelModificationDto; } diff --git a/src/space-model/interfaces/update-subspace.interface.ts b/src/space-model/interfaces/update-subspace.interface.ts index 05982a4..7433c2d 100644 --- a/src/space-model/interfaces/update-subspace.interface.ts +++ b/src/space-model/interfaces/update-subspace.interface.ts @@ -35,3 +35,7 @@ export interface IUpdateSubspaceModelInterface { export interface IDeletedSubsaceModelInterface { uuid: string; } + +export interface IModifiedProductModelsInterface { + add?: ProductModelInterface[]; +} diff --git a/src/space-model/services/subspace/subspace-product-item-model.service.ts b/src/space-model/services/subspace/subspace-product-item-model.service.ts index cdd0688..27fc2b9 100644 --- a/src/space-model/services/subspace/subspace-product-item-model.service.ts +++ b/src/space-model/services/subspace/subspace-product-item-model.service.ts @@ -159,7 +159,7 @@ export class SubspaceProductItemModelService extends BaseProductItemService { } return productItemModels; } catch (error) { - this.handleException(error, 'Failed to modify SpaceModels.'); + this.handleException(error, 'Failed to modify Product Item Models.'); } } diff --git a/src/space-model/services/subspace/subspace-product-model.service.ts b/src/space-model/services/subspace/subspace-product-model.service.ts index 4e145bc..9743aca 100644 --- a/src/space-model/services/subspace/subspace-product-model.service.ts +++ b/src/space-model/services/subspace/subspace-product-model.service.ts @@ -1,14 +1,24 @@ import { SpaceModelEntity, SubspaceModelEntity, + SubspaceProductModelEntity, SubspaceProductModelRepository, } from '@app/common/modules/space-model'; -import { HttpException, HttpStatus, Injectable } from '@nestjs/common'; -import { SubspaceProductItemModelService } from './subspace-product-item-model.service'; -import { CreateProductModelDto } from '../../dtos'; import { QueryRunner } from 'typeorm'; +import { HttpException, HttpStatus, Injectable } from '@nestjs/common'; + +import { SubspaceProductItemModelService } from './subspace-product-item-model.service'; +import { + CreateProductModelDto, + ProductModelModificationDto, + UpdateProductModelDto, +} from '../../dtos'; import { BaseProductModelService } from '../../common'; import { ProductService } from 'src/product/services'; +import { + IModifiedProductModelsInterface, + ProductModelInterface, +} from '../../interfaces'; @Injectable() export class SubspaceProductModelService extends BaseProductModelService { @@ -25,7 +35,7 @@ export class SubspaceProductModelService extends BaseProductModelService { spaceModel: SpaceModelEntity, subspaceModel: SubspaceModelEntity, queryRunner: QueryRunner, - ) { + ): Promise { try { if (!spaceProductModelDtos?.length) return; @@ -59,7 +69,7 @@ export class SubspaceProductModelService extends BaseProductModelService { return { productModel: savedModel, productItemModels, - }; + } as ProductModelInterface; }), ); return newProductModels; @@ -74,4 +84,63 @@ export class SubspaceProductModelService extends BaseProductModelService { ); } } + + async updateSubspaceProductModels(dtos: UpdateProductModelDto[]) { + try { + for (const dto of dtos) { + await this.findOne(dto.productModelUuid); + const newCount = dto.productCount; + if ( + dto.items.add.length + + dto.items.delete.length + + dto.items.delete.length !== + newCount + ) { + throw new HttpException( + `Invalid list of items`, + HttpStatus.BAD_REQUEST, + ); + } + } + } catch (error) {} + } + + async modifySubspaceProductModels( + dto: ProductModelModificationDto, + spaceModel: SpaceModelEntity, + subspaceModel: SubspaceModelEntity, + queryRunner: QueryRunner, + ) { + const productItemModels: IModifiedProductModelsInterface = {}; + try { + productItemModels.add = await this.createSubspaceProductModels( + dto.add, + spaceModel, + subspaceModel, + queryRunner, + ); + } catch (error) { + if (error instanceof HttpException) { + throw error; + } + throw new HttpException( + 'Failed to modify Subspace product model', + HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + } + + async findOne(uuid: string): Promise { + const productModel = await this.subpaceProductModelRepository.findOne({ + where: { + uuid, + }, + }); + if (!productModel) + throw new HttpException( + `Subspace Product model with uuid ${uuid} not found`, + HttpStatus.NOT_FOUND, + ); + return productModel; + } } From 9245c47ef5673cab9275ebc3f573585825e21145 Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Fri, 20 Dec 2024 14:27:25 +0400 Subject: [PATCH 18/45] removed count from all entities --- .../dtos/space-product-model.dto.ts | 6 +----- .../subspace-product-model.dto.ts | 6 +----- .../entities/space-product-model.entity.ts | 6 ------ .../subspace-product-model.entity.ts | 6 ------ .../modules/space/dtos/space-product.dto.ts | 6 +----- .../space/entities/space-product.entity.ts | 6 ------ .../subspace/subspace-product.entity.ts | 6 ------ .../services/base-product-model.service.ts | 14 ------------- .../create-product-model.dto.ts | 9 --------- .../update-product-model.dto.ts | 16 +-------------- .../handlers/propate-subspace-handler.ts | 3 +-- .../services/space-product-model.service.ts | 2 -- .../subspace-product-model.service.ts | 14 ------------- src/space/dtos/add.space.dto.ts | 6 ------ .../space-products/space-products.service.ts | 20 ++----------------- .../subspace/subspace-product.service.ts | 13 ------------ 16 files changed, 7 insertions(+), 132 deletions(-) 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 7952a23..aa6a3a3 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,5 +1,5 @@ import { ApiProperty } from '@nestjs/swagger'; -import { IsNotEmpty, IsNumber, IsString } from 'class-validator'; +import { IsNotEmpty, IsString } from 'class-validator'; import { SpaceProductItemModelDto } from './space-product-item-model.dto'; export class SpaceProductModelDto { @@ -7,10 +7,6 @@ export class SpaceProductModelDto { @IsNotEmpty() uuid: string; - @IsNumber() - @IsNotEmpty() - productCount: number; - @IsString() @IsNotEmpty() productUuid: 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 index 4eebaae..c76d8d1 100644 --- 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 @@ -1,5 +1,5 @@ import { ApiProperty } from '@nestjs/swagger'; -import { IsNotEmpty, IsNumber, IsString } from 'class-validator'; +import { IsNotEmpty, IsString } from 'class-validator'; import { SubspaceProductItemModelDto } from './subspace-product-item-model.dto'; export class SubpaceProductModelDto { @@ -7,10 +7,6 @@ export class SubpaceProductModelDto { @IsNotEmpty() uuid: string; - @IsNumber() - @IsNotEmpty() - productCount: number; - @IsString() @IsNotEmpty() productUuid: string; 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 9fdcd83..b520313 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 @@ -8,12 +8,6 @@ import { SpaceProductEntity } from '../../space/entities'; @Entity({ name: 'space-product-model' }) export class SpaceProductModelEntity extends AbstractEntity { - @Column({ - nullable: false, - type: 'int', - }) - productCount: number; - @ManyToOne( () => SpaceModelEntity, (spaceModel) => spaceModel.spaceProductModels, 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 960c6fe..8aa53fb 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 @@ -8,12 +8,6 @@ import { SubspaceProductItemModelEntity } from './subspace-product-item-model.en @Entity({ name: 'subspace-product-model' }) export class SubspaceProductModelEntity extends AbstractEntity { - @Column({ - nullable: false, - type: 'int', - }) - productCount: number; - @Column({ nullable: false, default: false, diff --git a/libs/common/src/modules/space/dtos/space-product.dto.ts b/libs/common/src/modules/space/dtos/space-product.dto.ts index a57d29e..92f5f71 100644 --- a/libs/common/src/modules/space/dtos/space-product.dto.ts +++ b/libs/common/src/modules/space/dtos/space-product.dto.ts @@ -1,5 +1,5 @@ import { ApiProperty } from '@nestjs/swagger'; -import { IsString, IsNotEmpty, IsNumber } from 'class-validator'; +import { IsString, IsNotEmpty } from 'class-validator'; import { SpaceProductItemDto } from './space-product-item.dto'; export class SpaceProductModelDto { @@ -7,10 +7,6 @@ export class SpaceProductModelDto { @IsNotEmpty() uuid: string; - @IsNumber() - @IsNotEmpty() - productCount: number; - @IsString() @IsNotEmpty() productUuid: string; 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 c268302..ee2cb4f 100644 --- a/libs/common/src/modules/space/entities/space-product.entity.ts +++ b/libs/common/src/modules/space/entities/space-product.entity.ts @@ -21,12 +21,6 @@ export class SpaceProductEntity extends AbstractEntity { @JoinColumn({ name: 'product_uuid' }) product: ProductEntity; - @Column({ - nullable: false, - type: 'int', - }) - productCount: number; - @Column({ nullable: false, default: 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 501f539..77cafa3 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 @@ -15,12 +15,6 @@ export class SubspaceProductEntity extends AbstractEntity }) public uuid: string; - @Column({ - nullable: false, - type: 'int', - }) - productCount: number; - @Column({ nullable: false, default: false, diff --git a/src/space-model/common/services/base-product-model.service.ts b/src/space-model/common/services/base-product-model.service.ts index c3230a3..6ff8d91 100644 --- a/src/space-model/common/services/base-product-model.service.ts +++ b/src/space-model/common/services/base-product-model.service.ts @@ -1,22 +1,8 @@ -import { HttpException, HttpStatus } from '@nestjs/common'; -import { CreateProductModelDto } from '../../dtos'; import { ProductService } from '../../../product/services'; export abstract class BaseProductModelService { constructor(private readonly productService: ProductService) {} - protected async validateProductCount( - dto: CreateProductModelDto, - ): 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/dtos/product-model-dtos/create-product-model.dto.ts b/src/space-model/dtos/product-model-dtos/create-product-model.dto.ts index 0b4882e..b40e9d7 100644 --- a/src/space-model/dtos/product-model-dtos/create-product-model.dto.ts +++ b/src/space-model/dtos/product-model-dtos/create-product-model.dto.ts @@ -4,7 +4,6 @@ import { IsString, IsArray, ValidateNested, - IsInt, ArrayNotEmpty, } from 'class-validator'; import { Type } from 'class-transformer'; @@ -19,14 +18,6 @@ export class CreateProductModelDto { @IsString() productUuid: string; - @ApiProperty({ - description: 'Number of products in the model', - example: 3, - }) - @IsNotEmpty() - @IsInt() - productCount: number; - @ApiProperty({ description: 'Specific names for each product item', type: [CreateProductItemModelDto], diff --git a/src/space-model/dtos/product-model-dtos/update-product-model.dto.ts b/src/space-model/dtos/product-model-dtos/update-product-model.dto.ts index 224330e..9310b46 100644 --- a/src/space-model/dtos/product-model-dtos/update-product-model.dto.ts +++ b/src/space-model/dtos/product-model-dtos/update-product-model.dto.ts @@ -1,24 +1,10 @@ import { ApiProperty } from '@nestjs/swagger'; import { Type } from 'class-transformer'; -import { - IsNotEmpty, - IsInt, - IsArray, - ArrayNotEmpty, - ValidateNested, -} from 'class-validator'; +import { IsArray, ArrayNotEmpty, ValidateNested } from 'class-validator'; import { BaseProductModelDto } from './base-product-model.dto'; import { ProductItemModelModificationDto } from '../product-item-model-dtos'; export class UpdateProductModelDto extends BaseProductModelDto { - @ApiProperty({ - description: 'Number of products to be modified in the model', - example: 3, - }) - @IsNotEmpty() - @IsInt() - productCount: number; - @ApiProperty({ description: 'Update product item', type: [ProductItemModelModificationDto], diff --git a/src/space-model/handlers/propate-subspace-handler.ts b/src/space-model/handlers/propate-subspace-handler.ts index 82431bd..d3ff2a1 100644 --- a/src/space-model/handlers/propate-subspace-handler.ts +++ b/src/space-model/handlers/propate-subspace-handler.ts @@ -189,14 +189,13 @@ export class PropogateSubspaceHandler const subspaceProduct = this.productRepository.create({ product: productModel.productModel.product, subspace, - productCount: productModel.productModel.productCount, model: productModel.productModel, }); const createdSubspaceProduct = await this.productRepository.save(subspaceProduct); this.logger.log( - `Product added to subspace ${subspace.id} with count ${createdSubspaceProduct.productCount}`, + `Product added to subspace ${subspace.id} with count ${createdSubspaceProduct.items.length}`, ); return createdSubspaceProduct; } diff --git a/src/space-model/services/space-product-model.service.ts b/src/space-model/services/space-product-model.service.ts index ccfa780..ba14317 100644 --- a/src/space-model/services/space-product-model.service.ts +++ b/src/space-model/services/space-product-model.service.ts @@ -27,13 +27,11 @@ export class SpaceProductModelService extends BaseProductModelService { 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.spaceProductModelRepository.target, { product, - productCount: dto.productCount, spaceModel, }, ); diff --git a/src/space-model/services/subspace/subspace-product-model.service.ts b/src/space-model/services/subspace/subspace-product-model.service.ts index 9743aca..8b9d05f 100644 --- a/src/space-model/services/subspace/subspace-product-model.service.ts +++ b/src/space-model/services/subspace/subspace-product-model.service.ts @@ -41,13 +41,11 @@ export class SubspaceProductModelService extends BaseProductModelService { 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, }, ); @@ -89,18 +87,6 @@ export class SubspaceProductModelService extends BaseProductModelService { try { for (const dto of dtos) { await this.findOne(dto.productModelUuid); - const newCount = dto.productCount; - if ( - dto.items.add.length + - dto.items.delete.length + - dto.items.delete.length !== - newCount - ) { - throw new HttpException( - `Invalid list of items`, - HttpStatus.BAD_REQUEST, - ); - } } } catch (error) {} } diff --git a/src/space/dtos/add.space.dto.ts b/src/space/dtos/add.space.dto.ts index dba8e92..7c10c59 100644 --- a/src/space/dtos/add.space.dto.ts +++ b/src/space/dtos/add.space.dto.ts @@ -30,12 +30,6 @@ export class ProductAssignmentDto { @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], diff --git a/src/space/services/space-products/space-products.service.ts b/src/space/services/space-products/space-products.service.ts index 2fe75cb..6c5f1bd 100644 --- a/src/space/services/space-products/space-products.service.ts +++ b/src/space/services/space-products/space-products.service.ts @@ -31,7 +31,6 @@ export class SpaceProductService { queryRunner.manager.create(SpaceProductEntity, { space: space, product: spaceProductModel.product, - productCount: spaceProductModel.productCount, spaceProductModel: spaceProductModel, }), ); @@ -150,16 +149,13 @@ export class SpaceProductService { ): Promise { const updatedProducts = []; - for (const { productId, count } of uniqueProducts) { + for (const { productId } 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); - } + updatedProducts.push(existingProduct); } if (updatedProducts.length > 0) { @@ -180,13 +176,11 @@ export class SpaceProductService { for (const uniqueSpaceProduct of uniqueSpaceProducts) { const product = productEntities.get(uniqueSpaceProduct.productId); await this.getProduct(uniqueSpaceProduct.productId); - this.validateProductCount(uniqueSpaceProduct); newProducts.push( queryRunner.manager.create(SpaceProductEntity, { space, product, - productCount: uniqueSpaceProduct.count, }), ); } @@ -209,16 +203,6 @@ export class SpaceProductService { 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, - ); - } - } - async getProduct(productId: string): Promise { const product = await this.productService.findOne(productId); return product.data; diff --git a/src/space/services/subspace/subspace-product.service.ts b/src/space/services/subspace/subspace-product.service.ts index e789311..48852d7 100644 --- a/src/space/services/subspace/subspace-product.service.ts +++ b/src/space/services/subspace/subspace-product.service.ts @@ -64,7 +64,6 @@ export class SubspaceProductService { return { subspace, product: productModel.product, - productCount: productModel.productCount, model: productModel, }; } @@ -78,13 +77,10 @@ export class SubspaceProductService { 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, }); }), ); @@ -116,13 +112,4 @@ export class SubspaceProductService { 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, - ); - } - } } From 1df462712b91954982c1bb32ab41096795de0260 Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Sun, 22 Dec 2024 13:26:03 +0400 Subject: [PATCH 19/45] updated subspace models update/delete --- .../update-subspace-model.dto.ts | 11 +- .../interfaces/update-subspace.interface.ts | 8 + .../subspace/subspace-model.service.ts | 189 ++++++++++++------ .../subspace-product-item-model.service.ts | 22 +- .../subspace-product-model.service.ts | 147 ++++++++++++-- 5 files changed, 290 insertions(+), 87 deletions(-) diff --git a/src/space-model/dtos/subspaces-model-dtos/update-subspace-model.dto.ts b/src/space-model/dtos/subspaces-model-dtos/update-subspace-model.dto.ts index eb5305c..93ca12b 100644 --- a/src/space-model/dtos/subspaces-model-dtos/update-subspace-model.dto.ts +++ b/src/space-model/dtos/subspaces-model-dtos/update-subspace-model.dto.ts @@ -7,7 +7,7 @@ import { IsOptional, ValidateNested, } from 'class-validator'; -import { CreateProductModelDto } from '../product-model-dtos'; +import { ProductModelModificationDto } from '../product-model-dtos'; export class UpdateSubspaceModelDto { @ApiProperty({ @@ -23,12 +23,11 @@ export class UpdateSubspaceModelDto { subspaceUuid: string; @ApiProperty({ - description: 'List of products included in the model', - type: [CreateProductModelDto], + description: 'Products models modified in the model', + type: ProductModelModificationDto, }) - @IsArray() @IsOptional() @ValidateNested({ each: true }) - @Type(() => CreateProductModelDto) - spaceProductModels?: CreateProductModelDto[]; + @Type(() => ProductModelModificationDto) + updatedProductModels?: ProductModelModificationDto; } diff --git a/src/space-model/interfaces/update-subspace.interface.ts b/src/space-model/interfaces/update-subspace.interface.ts index 7433c2d..5898d34 100644 --- a/src/space-model/interfaces/update-subspace.interface.ts +++ b/src/space-model/interfaces/update-subspace.interface.ts @@ -30,12 +30,20 @@ export interface IModifiedProductItemsModelsInterface { export interface IUpdateSubspaceModelInterface { subspaceName?: string; uuid: string; + productModels?: IModifiedProductItemsModelsInterface[]; } export interface IDeletedSubsaceModelInterface { uuid: string; } +export interface IUpdatedProductModelInterface { + productModelUuid: string; + productModifiedItemModel: IModifiedProductItemsModelsInterface; +} + export interface IModifiedProductModelsInterface { add?: ProductModelInterface[]; + update?: IUpdatedProductModelInterface[]; + delete?: string[]; } diff --git a/src/space-model/services/subspace/subspace-model.service.ts b/src/space-model/services/subspace/subspace-model.service.ts index 305323d..caefce2 100644 --- a/src/space-model/services/subspace/subspace-model.service.ts +++ b/src/space-model/services/subspace/subspace-model.service.ts @@ -72,62 +72,126 @@ export class SubSpaceModelService { async updateSubspaceModels( updateDtos: UpdateSubspaceModelDto[], + spaceModel: SpaceModelEntity, queryRunner: QueryRunner, - ) { - const updateResults: IUpdateSubspaceModelInterface[] = []; + ): Promise { try { - for (const dto of updateDtos) { - const subspaceModel = await this.findOne(dto.subspaceUuid); - const updateResult: IUpdateSubspaceModelInterface = { - uuid: dto.subspaceUuid, - }; + const updateResults: IUpdateSubspaceModelInterface[] = []; - if (dto.subspaceName) { - subspaceModel.subspaceName = dto.subspaceName; - await queryRunner.manager.update( - this.subspaceModelRepository.target, - { uuid: dto.subspaceUuid }, - { subspaceName: dto.subspaceName }, + const updatePromises = updateDtos.map(async (dto) => { + try { + const subspaceModel = await this.findOne(dto.subspaceUuid); + + if (!subspaceModel) { + throw new HttpException( + `Subspace model with UUID ${dto.subspaceUuid} not found.`, + HttpStatus.NOT_FOUND, + ); + } + + const updateResult: IUpdateSubspaceModelInterface = { + uuid: dto.subspaceUuid, + }; + + if (dto.subspaceName) { + await this.updateSubspaceName( + dto.subspaceUuid, + dto.subspaceName, + queryRunner, + ); + subspaceModel.subspaceName = dto.subspaceName; + updateResult.subspaceName = dto.subspaceName; + } + + if (dto.updatedProductModels) { + await this.subSpaceProducetModelService.modifySubspaceProductModels( + dto.updatedProductModels, + spaceModel, + subspaceModel, + queryRunner, + ); + } + + updateResults.push(updateResult); + } catch (error) { + throw new HttpException( + `Failed to update subspace model with UUID ${dto.subspaceUuid}: ${error.message}`, + HttpStatus.INTERNAL_SERVER_ERROR, ); - updateResult.subspaceName = dto.subspaceName; } + }); - updateResults.push(updateResult); - } + await Promise.all(updatePromises); return updateResults; } catch (error) { - if (error instanceof HttpException) { - throw error; - } throw new HttpException( - error.message || 'Failed to update SpaceModels', + error.message || 'Failed to update subspace models.', HttpStatus.INTERNAL_SERVER_ERROR, ); } } - async deleteSubspaceModels( - dtos: DeleteSubspaceModelDto[], + private async updateSubspaceName( + uuid: string, + subspaceName: string, queryRunner: QueryRunner, - ) { - const deleteResults: IDeletedSubsaceModelInterface[] = []; + ): Promise { + await queryRunner.manager.update( + this.subspaceModelRepository.target, + { uuid }, + { subspaceName }, + ); + } + + async deleteSubspaceModels( + deleteDtos: DeleteSubspaceModelDto[], + queryRunner: QueryRunner, + ): Promise { try { - for (const dto of dtos) { - await this.findOne(dto.subspaceUuid); + const deleteResults: IDeletedSubsaceModelInterface[] = []; - await queryRunner.manager.update( - this.subspaceModelRepository.target, - { uuid: dto.subspaceUuid }, - { disabled: true }, - ); + const deletePromises = deleteDtos.map(async (dto) => { + try { + const subspaceModel = await this.findOne(dto.subspaceUuid); + + if (!subspaceModel) { + throw new HttpException( + `Subspace model with UUID ${dto.subspaceUuid} not found.`, + HttpStatus.NOT_FOUND, + ); + } + + await queryRunner.manager.update( + this.subspaceModelRepository.target, + { uuid: dto.subspaceUuid }, + { disabled: true }, + ); + + if (subspaceModel.productModels.length > 0) { + await this.subSpaceProducetModelService.disableProductModels( + subspaceModel.productModels, + queryRunner, + ); + } + + deleteResults.push({ uuid: dto.subspaceUuid }); + } catch (error) { + throw new HttpException( + `Failed to delete subspace model with UUID ${dto.subspaceUuid}: ${error.message}`, + HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + }); + + await Promise.all(deletePromises); - deleteResults.push({ uuid: dto.subspaceUuid }); - } return deleteResults; } catch (error) { - console.error(`Bulk delete operation failed: ${error.message}`); - throw new Error('Bulk delete operation failed.'); + throw new HttpException( + 'Bulk delete operation failed. Please try again.', + HttpStatus.INTERNAL_SERVER_ERROR, + ); } } @@ -136,6 +200,7 @@ export class SubSpaceModelService { where: { uuid: subspaceUuid, }, + relations: ['productModels', 'productModels.itemModels'], }); if (!subspace) { throw new HttpException( @@ -181,36 +246,48 @@ export class SubSpaceModelService { dto: ModifySubspacesModelDto, spaceModel: SpaceModelEntity, queryRunner: QueryRunner, - ) { + ): Promise { const subspaces: IModifySubspaceModelInterface = { spaceModelUuid: spaceModel.uuid, }; + try { + const actions = []; + if (dto.add) { - const addedSubspaces = await this.createSubSpaceModels( - dto.add, - spaceModel, - queryRunner, + actions.push( + this.createSubSpaceModels(dto.add, spaceModel, queryRunner).then( + (addedSubspaces) => { + subspaces.new = addedSubspaces; + }, + ), ); - subspaces.new = addedSubspaces; - } else if (dto.update) { - const updatedSubspaces = await this.updateSubspaceModels( - dto.update, - queryRunner, - ); - subspaces.update = updatedSubspaces; - } else if (dto.delete) { - const deletedSubspaces = await this.deleteSubspaceModels( - dto.delete, - queryRunner, - ); - subspaces.delete = deletedSubspaces; } + + if (dto.update) { + actions.push( + this.updateSubspaceModels(dto.update, spaceModel, queryRunner).then( + (updatedSubspaces) => { + subspaces.update = updatedSubspaces; + }, + ), + ); + } + + if (dto.delete) { + actions.push( + this.deleteSubspaceModels(dto.delete, queryRunner).then( + (deletedSubspaces) => { + subspaces.delete = deletedSubspaces; + }, + ), + ); + } + + await Promise.all(actions); + return subspaces; } catch (error) { - if (error instanceof HttpException) { - throw error; - } throw new HttpException( error.message || 'Failed to modify SpaceModels', HttpStatus.INTERNAL_SERVER_ERROR, diff --git a/src/space-model/services/subspace/subspace-product-item-model.service.ts b/src/space-model/services/subspace/subspace-product-item-model.service.ts index 27fc2b9..592e45c 100644 --- a/src/space-model/services/subspace/subspace-product-item-model.service.ts +++ b/src/space-model/services/subspace/subspace-product-item-model.service.ts @@ -92,6 +92,7 @@ export class SubspaceProductItemModelService extends BaseProductItemService { queryRunner: QueryRunner, ): Promise { try { + if (dtos.length === 0) return; const productItemModels = await Promise.all( dtos.map(async (dto) => { const productItemModel = await this.findOne(dto.productModelUuid); @@ -100,18 +101,29 @@ export class SubspaceProductItemModelService extends BaseProductItemService { }), ); - const response = (await this.saveProductItems( + const disabledItemModels = await this.disableProductItemModels( productItemModels, - this.subspaceProductItemRepository, queryRunner, - )) as SubspaceProductItemModelEntity[]; + ); - return response.map((item) => item.uuid); + return disabledItemModels.map((item) => item.uuid); } catch (error) { - this.handleException(error, 'Failed to modify SpaceModels.'); + this.handleException(error, 'Failed to delete SpaceModels.'); } } + async disableProductItemModels( + productItemModels: SubspaceProductItemModelEntity[], + queryRunner: QueryRunner, + ): Promise { + productItemModels.forEach((model) => (model.disabled = true)); + return (await this.saveProductItems( + productItemModels, + this.subspaceProductItemRepository, + queryRunner, + )) as SubspaceProductItemModelEntity[]; + } + async findOne(uuid: string): Promise { const productItemModel = await this.subspaceProductItemRepository.findOne({ where: { uuid }, diff --git a/src/space-model/services/subspace/subspace-product-model.service.ts b/src/space-model/services/subspace/subspace-product-model.service.ts index 8b9d05f..724234d 100644 --- a/src/space-model/services/subspace/subspace-product-model.service.ts +++ b/src/space-model/services/subspace/subspace-product-model.service.ts @@ -4,12 +4,13 @@ import { SubspaceProductModelEntity, SubspaceProductModelRepository, } from '@app/common/modules/space-model'; -import { QueryRunner } from 'typeorm'; +import { In, QueryRunner } from 'typeorm'; import { HttpException, HttpStatus, Injectable } from '@nestjs/common'; import { SubspaceProductItemModelService } from './subspace-product-item-model.service'; import { CreateProductModelDto, + DeleteProductModelDto, ProductModelModificationDto, UpdateProductModelDto, } from '../../dtos'; @@ -17,6 +18,7 @@ import { BaseProductModelService } from '../../common'; import { ProductService } from 'src/product/services'; import { IModifiedProductModelsInterface, + IUpdatedProductModelInterface, ProductModelInterface, } from '../../interfaces'; @@ -83,34 +85,138 @@ export class SubspaceProductModelService extends BaseProductModelService { } } - async updateSubspaceProductModels(dtos: UpdateProductModelDto[]) { - try { - for (const dto of dtos) { - await this.findOne(dto.productModelUuid); - } - } catch (error) {} - } - - async modifySubspaceProductModels( - dto: ProductModelModificationDto, + async updateSubspaceProductModels( + dtos: UpdateProductModelDto[], spaceModel: SpaceModelEntity, - subspaceModel: SubspaceModelEntity, queryRunner: QueryRunner, - ) { - const productItemModels: IModifiedProductModelsInterface = {}; + ): Promise { try { - productItemModels.add = await this.createSubspaceProductModels( - dto.add, - spaceModel, - subspaceModel, - queryRunner, + const updatedProductModels = await Promise.all( + dtos.map(async (dto) => { + const productModel = await this.findOne(dto.productModelUuid); + const productModifiedItemModel = + await this.subspaceProductItemModelService.modifyProductItemModel( + dto.items, + productModel, + spaceModel, + queryRunner, + ); + return { + productModelUuid: productModel.uuid, + productModifiedItemModel, + }; + }), ); + + return updatedProductModels; } catch (error) { if (error instanceof HttpException) { throw error; } throw new HttpException( - 'Failed to modify Subspace product model', + 'Failed to update Subspace product model', + HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + } + + async removeSubspaceProductModels( + deleteDtos: DeleteProductModelDto[], + transactionRunner: QueryRunner, + ): Promise { + try { + const productModels = await Promise.all( + deleteDtos.map((dto) => this.findOne(dto.productModelUuid)), + ); + + return await this.disableProductModels(productModels, transactionRunner); + } catch (error) { + throw new HttpException( + 'Failed to remove subspace product models', + HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + } + + async disableProductModels( + productModels: SubspaceProductModelEntity[], + transactionRunner: QueryRunner, + ): Promise { + try { + const productModelUuids = productModels.map((model) => model.uuid); + + await transactionRunner.manager.update( + this.subpaceProductModelRepository.target, + { uuid: In(productModelUuids) }, + { disabled: true }, + ); + + const itemModelDisables = productModels.map((model) => + model.itemModels.length > 0 + ? this.subspaceProductItemModelService.disableProductItemModels( + model.itemModels, + transactionRunner, + ) + : Promise.resolve(), + ); + + await Promise.all(itemModelDisables); + + return productModelUuids; + } catch (error) { + throw new HttpException( + 'Failed to disable product models', + HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + } + + async modifySubspaceProductModels( + modificationDto: ProductModelModificationDto, + spaceEntity: SpaceModelEntity, + subspaceEntity: SubspaceModelEntity, + transactionRunner: QueryRunner, + ): Promise { + const modifiedProductModels: IModifiedProductModelsInterface = {}; + + try { + await Promise.all([ + modificationDto.add + ? this.createSubspaceProductModels( + modificationDto.add, + spaceEntity, + subspaceEntity, + transactionRunner, + ).then((addedModels) => { + modifiedProductModels.add = addedModels; + }) + : Promise.resolve(), + modificationDto.update + ? this.updateSubspaceProductModels( + modificationDto.update, + spaceEntity, + transactionRunner, + ).then((updatedModels) => { + modifiedProductModels.update = updatedModels; + }) + : Promise.resolve(), + modificationDto.delete + ? this.removeSubspaceProductModels( + modificationDto.delete, + transactionRunner, + ).then((deletedModels) => { + modifiedProductModels.delete = deletedModels; + }) + : Promise.resolve(), + ]); + + return modifiedProductModels; + } catch (error) { + if (error instanceof HttpException) { + throw error; + } + throw new HttpException( + 'Failed to modify subspace product models', HttpStatus.INTERNAL_SERVER_ERROR, ); } @@ -121,6 +227,7 @@ export class SubspaceProductModelService extends BaseProductModelService { where: { uuid, }, + relations: ['itemModels'], }); if (!productModel) throw new HttpException( From 2dd48b7bc2bdfaa60016ca3ab52cfc0eece18490 Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Mon, 23 Dec 2024 08:32:02 +0400 Subject: [PATCH 20/45] clean model entities --- libs/common/src/common.module.ts | 9 ---- libs/common/src/database/database.module.ts | 10 +--- .../product/entities/product.entity.ts | 16 ++---- .../src/modules/space-model/dtos/index.ts | 2 - .../dtos/space-product-item-model.dto.ts | 15 ------ .../dtos/space-product-model.dto.ts | 19 ------- .../space-model/dtos/subspace-model/index.ts | 2 - .../subspace-product-item-model.dto.ts | 15 ------ .../subspace-product-model.dto.ts | 19 ------- .../modules/space-model/dtos/tag-model.dto.ts | 21 ++++++++ .../src/modules/space-model/entities/index.ts | 3 +- .../entities/space-model.entity.ts | 14 ++---- .../space-product-item-model.entity.ts | 35 ------------- .../entities/space-product-model.entity.ts | 50 ------------------- .../entities/subspace-model/index.ts | 3 +- .../subspace-model/subspace-model.entity.ts | 12 ++--- .../subspace-product-item-model.entity.ts | 33 ------------ .../subspace-product-model.entity.ts | 44 ---------------- .../space-model/entities/tag-model.entity.ts | 29 +++++++++++ .../repositories/space-model.repository.ts | 33 ++---------- .../space-model.repository.module.ts | 14 +----- .../entities/space-product-item.entity.ts | 10 ---- .../space/entities/space-product.entity.ts | 11 ---- .../subspace/subspace-product-item.entity.ts | 6 --- .../subspace/subspace-product.entity.ts | 11 +--- 25 files changed, 70 insertions(+), 366 deletions(-) delete mode 100644 libs/common/src/modules/space-model/dtos/space-product-item-model.dto.ts delete mode 100644 libs/common/src/modules/space-model/dtos/space-product-model.dto.ts delete mode 100644 libs/common/src/modules/space-model/dtos/subspace-model/subspace-product-item-model.dto.ts delete mode 100644 libs/common/src/modules/space-model/dtos/subspace-model/subspace-product-model.dto.ts create mode 100644 libs/common/src/modules/space-model/dtos/tag-model.dto.ts delete mode 100644 libs/common/src/modules/space-model/entities/space-product-item-model.entity.ts delete mode 100644 libs/common/src/modules/space-model/entities/space-product-model.entity.ts delete mode 100644 libs/common/src/modules/space-model/entities/subspace-model/subspace-product-item-model.entity.ts delete mode 100644 libs/common/src/modules/space-model/entities/subspace-model/subspace-product-model.entity.ts create mode 100644 libs/common/src/modules/space-model/entities/tag-model.entity.ts diff --git a/libs/common/src/common.module.ts b/libs/common/src/common.module.ts index 582510d..d63ff67 100644 --- a/libs/common/src/common.module.ts +++ b/libs/common/src/common.module.ts @@ -12,10 +12,7 @@ import { SceneDeviceRepository } from './modules/scene-device/repositories'; import { SpaceProductItemRepository, SpaceRepository } from './modules/space'; import { SpaceModelRepository, - SpaceProductModelRepository, SubspaceModelRepository, - SubspaceProductItemModelRepository, - SubspaceProductModelRepository, } from './modules/space-model'; import { SubspaceRepository } from './modules/space/repositories/subspace.repository'; @Module({ @@ -28,10 +25,7 @@ import { SubspaceRepository } from './modules/space/repositories/subspace.reposi SpaceRepository, SubspaceRepository, SubspaceModelRepository, - SubspaceProductModelRepository, - SubspaceProductItemModelRepository, SpaceModelRepository, - SpaceProductModelRepository, SpaceProductItemRepository, ], exports: [ @@ -45,10 +39,7 @@ import { SubspaceRepository } from './modules/space/repositories/subspace.reposi SpaceRepository, SubspaceRepository, SubspaceModelRepository, - SubspaceProductModelRepository, - SubspaceProductItemModelRepository, SpaceModelRepository, - SpaceProductModelRepository, ], imports: [ ConfigModule.forRoot({ diff --git a/libs/common/src/database/database.module.ts b/libs/common/src/database/database.module.ts index 5357904..8fa7f07 100644 --- a/libs/common/src/database/database.module.ts +++ b/libs/common/src/database/database.module.ts @@ -32,11 +32,8 @@ import { SpaceProductEntity } from '../modules/space/entities/space-product.enti import { ProjectEntity } from '../modules/project/entities'; import { SpaceModelEntity, - SpaceProductItemModelEntity, - SpaceProductModelEntity, SubspaceModelEntity, - SubspaceProductItemModelEntity, - SubspaceProductModelEntity, + TagModel, } from '../modules/space-model/entities'; import { InviteUserEntity, @@ -82,13 +79,10 @@ import { SceneIconEntity, SceneDeviceEntity, SpaceModelEntity, - SpaceProductModelEntity, - SpaceProductItemModelEntity, SubspaceModelEntity, + TagModel, SpaceProductEntity, SpaceProductItemEntity, - SubspaceProductModelEntity, - SubspaceProductItemModelEntity, SubspaceProductEntity, SubspaceProductItemEntity, InviteUserEntity, diff --git a/libs/common/src/modules/product/entities/product.entity.ts b/libs/common/src/modules/product/entities/product.entity.ts index 926d179..6de8ced 100644 --- a/libs/common/src/modules/product/entities/product.entity.ts +++ b/libs/common/src/modules/product/entities/product.entity.ts @@ -3,8 +3,7 @@ import { ProductDto } from '../dtos'; import { AbstractEntity } from '../../abstract/entities/abstract.entity'; import { DeviceEntity } from '../../device/entities'; import { SpaceProductEntity } from '../../space/entities/space-product.entity'; -import { SpaceProductModelEntity } from '../../space-model/entities'; -import { SubspaceProductModelEntity } from '../../space-model/entities/subspace-model/subspace-product-model.entity'; +import { TagModel } from '../../space-model'; @Entity({ name: 'product' }) export class ProductEntity extends AbstractEntity { @@ -32,17 +31,8 @@ export class ProductEntity extends AbstractEntity { @OneToMany(() => SpaceProductEntity, (spaceProduct) => spaceProduct.product) spaceProducts: SpaceProductEntity[]; - @OneToMany( - () => SpaceProductModelEntity, - (spaceProductModel) => spaceProductModel.product, - ) - spaceProductModels: SpaceProductModelEntity[]; - - @OneToMany( - () => SubspaceProductModelEntity, - (subspaceProductModel) => subspaceProductModel.product, - ) - subpaceProductModels: SubspaceProductModelEntity[]; + @OneToMany(() => TagModel, (tag) => tag.product) + tagModels: TagModel[]; @OneToMany( () => DeviceEntity, diff --git a/libs/common/src/modules/space-model/dtos/index.ts b/libs/common/src/modules/space-model/dtos/index.ts index 9ba8130..242564d 100644 --- a/libs/common/src/modules/space-model/dtos/index.ts +++ b/libs/common/src/modules/space-model/dtos/index.ts @@ -1,4 +1,2 @@ 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/space-product-item-model.dto.ts b/libs/common/src/modules/space-model/dtos/space-product-item-model.dto.ts deleted file mode 100644 index 5eb9bca..0000000 --- a/libs/common/src/modules/space-model/dtos/space-product-item-model.dto.ts +++ /dev/null @@ -1,15 +0,0 @@ -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 deleted file mode 100644 index aa6a3a3..0000000 --- a/libs/common/src/modules/space-model/dtos/space-product-model.dto.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { ApiProperty } from '@nestjs/swagger'; -import { IsNotEmpty, IsString } from 'class-validator'; -import { SpaceProductItemModelDto } from './space-product-item-model.dto'; - -export class SpaceProductModelDto { - @IsString() - @IsNotEmpty() - uuid: string; - - @IsString() - @IsNotEmpty() - productUuid: string; - - @ApiProperty({ - description: 'List of individual items with specific names for the product', - type: [SpaceProductItemModelDto], - }) - items: SpaceProductItemModelDto[]; -} 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 70337b6..fb0ac5a 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,3 +1 @@ export * from './subspace-model.dto'; -export * from './subspace-product-item-model.dto'; -export * from './subspace-product-model.dto'; 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 deleted file mode 100644 index 479642b..0000000 --- a/libs/common/src/modules/space-model/dtos/subspace-model/subspace-product-item-model.dto.ts +++ /dev/null @@ -1,15 +0,0 @@ -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 deleted file mode 100644 index c76d8d1..0000000 --- a/libs/common/src/modules/space-model/dtos/subspace-model/subspace-product-model.dto.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { ApiProperty } from '@nestjs/swagger'; -import { IsNotEmpty, IsString } from 'class-validator'; -import { SubspaceProductItemModelDto } from './subspace-product-item-model.dto'; - -export class SubpaceProductModelDto { - @IsString() - @IsNotEmpty() - uuid: string; - - @IsString() - @IsNotEmpty() - productUuid: string; - - @ApiProperty({ - description: 'List of individual items with specific names for the product', - type: [SubspaceProductItemModelDto], - }) - items: SubspaceProductItemModelDto[]; -} diff --git a/libs/common/src/modules/space-model/dtos/tag-model.dto.ts b/libs/common/src/modules/space-model/dtos/tag-model.dto.ts new file mode 100644 index 0000000..f98f160 --- /dev/null +++ b/libs/common/src/modules/space-model/dtos/tag-model.dto.ts @@ -0,0 +1,21 @@ +import { IsNotEmpty, IsString } from 'class-validator'; + +export class TagModelDto { + @IsString() + @IsNotEmpty() + public uuid: string; + + @IsString() + @IsNotEmpty() + public name: string; + + @IsString() + @IsNotEmpty() + public productUuid: string; + + @IsString() + spaceModelUuid: string; + + @IsString() + subspaceModelUuid: string; +} diff --git a/libs/common/src/modules/space-model/entities/index.ts b/libs/common/src/modules/space-model/entities/index.ts index ec19308..f4fffbd 100644 --- a/libs/common/src/modules/space-model/entities/index.ts +++ b/libs/common/src/modules/space-model/entities/index.ts @@ -1,4 +1,3 @@ export * from './space-model.entity'; -export * from './space-product-item-model.entity'; -export * from './space-product-model.entity'; export * from './subspace-model'; +export * from './tag-model.entity'; 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 4c90bcc..b108729 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 @@ -9,9 +9,9 @@ import { import { AbstractEntity } from '../../abstract/entities/abstract.entity'; import { SpaceModelDto } from '../dtos'; import { SubspaceModelEntity } from './subspace-model'; -import { SpaceProductModelEntity } from './space-product-model.entity'; import { ProjectEntity } from '../../project/entities'; import { SpaceEntity } from '../../space/entities'; +import { TagModel } from './tag-model.entity'; @Entity({ name: 'space-model' }) @Unique(['modelName', 'project']) @@ -51,17 +51,11 @@ export class SpaceModelEntity extends AbstractEntity { ) public subspaceModels: SubspaceModelEntity[]; - @OneToMany( - () => SpaceProductModelEntity, - (productModel) => productModel.spaceModel, - { - nullable: true, - }, - ) - public spaceProductModels: SpaceProductModelEntity[]; - @OneToMany(() => SpaceEntity, (space) => space.spaceModel, { cascade: true, }) public spaces: SpaceEntity[]; + + @OneToMany(() => TagModel, (tag) => tag.spaceModel) + tags: TagModel[]; } diff --git a/libs/common/src/modules/space-model/entities/space-product-item-model.entity.ts b/libs/common/src/modules/space-model/entities/space-product-item-model.entity.ts deleted file mode 100644 index 1d7bd09..0000000 --- a/libs/common/src/modules/space-model/entities/space-product-item-model.entity.ts +++ /dev/null @@ -1,35 +0,0 @@ -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 { - @Column({ - nullable: false, - }) - public tag: string; - - @ManyToOne( - () => SpaceProductModelEntity, - (spaceProductModel) => spaceProductModel.items, - { - nullable: false, - }, - ) - public spaceProductModel: SpaceProductModelEntity; - - @Column({ - nullable: false, - default: false, - }) - public disabled: boolean; - - @OneToMany( - () => 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 deleted file mode 100644 index b520313..0000000 --- a/libs/common/src/modules/space-model/entities/space-product-model.entity.ts +++ /dev/null @@ -1,50 +0,0 @@ -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-model.entity'; -import { SpaceProductModelDto } from '../dtos'; -import { SpaceProductEntity } from '../../space/entities'; - -@Entity({ name: 'space-product-model' }) -export class SpaceProductModelEntity extends AbstractEntity { - @ManyToOne( - () => SpaceModelEntity, - (spaceModel) => spaceModel.spaceProductModels, - { - nullable: false, - onDelete: 'CASCADE', - }, - ) - public spaceModel: SpaceModelEntity; - - @ManyToOne(() => ProductEntity, (product) => product.spaceProductModels, { - nullable: false, - onDelete: 'CASCADE', - }) - public product: ProductEntity; - - @Column({ - nullable: false, - default: false, - }) - public disabled: boolean; - - @OneToMany( - () => SpaceProductItemModelEntity, - (item) => item.spaceProductModel, - { - cascade: true, - }, - ) - public items: SpaceProductItemModelEntity[]; - - @OneToMany( - () => SpaceProductEntity, - (spaceProduct) => spaceProduct.spaceProductModel, - { - cascade: true, - }, - ) - public spaceProducts: SpaceProductEntity[]; -} 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 e39403f..262490e 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,3 +1,2 @@ export * from './subspace-model.entity'; -export * from './subspace-product-item-model.entity'; -export * from './subspace-product-model.entity'; + \ No newline at end of file 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 f166c73..becb2f8 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,7 +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'; +import { TagModel } from '../tag-model.entity'; @Entity({ name: 'subspace-model' }) @Unique(['subspaceName', 'spaceModel']) @@ -41,12 +41,6 @@ export class SubspaceModelEntity extends AbstractEntity { }) public disabled: boolean; - @OneToMany( - () => SubspaceProductModelEntity, - (productModel) => productModel.subspaceModel, - { - nullable: true, - }, - ) - public productModels: SubspaceProductModelEntity[]; + @OneToMany(() => TagModel, (tag) => tag.subspaceModel) + tags: TagModel[]; } 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 deleted file mode 100644 index 59d6db6..0000000 --- a/libs/common/src/modules/space-model/entities/subspace-model/subspace-product-item-model.entity.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { AbstractEntity } from '@app/common/modules/abstract/entities/abstract.entity'; -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 { - @Column({ - nullable: false, - }) - public tag: string; - - @Column({ - nullable: false, - default: false, - }) - public disabled: boolean; - - @ManyToOne( - () => SubspaceProductModelEntity, - (productModel) => productModel.itemModels, - { - nullable: false, - }, - ) - public subspaceProductModel: SubspaceProductModelEntity; - - @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 deleted file mode 100644 index 8aa53fb..0000000 --- a/libs/common/src/modules/space-model/entities/subspace-model/subspace-product-model.entity.ts +++ /dev/null @@ -1,44 +0,0 @@ -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 { SubspaceProductEntity } from '@app/common/modules/space/entities'; -import { SubspaceProductItemModelEntity } from './subspace-product-item-model.entity'; - -@Entity({ name: 'subspace-product-model' }) -export class SubspaceProductModelEntity extends AbstractEntity { - @Column({ - nullable: false, - default: false, - }) - public disabled: boolean; - - @ManyToOne( - () => SubspaceModelEntity, - (spaceModel) => spaceModel.productModels, - { - nullable: false, - }, - ) - public subspaceModel: SubspaceModelEntity; - - @ManyToOne(() => ProductEntity, (product) => product.subpaceProductModels, { - nullable: false, - }) - public product: ProductEntity; - - @OneToMany(() => SubspaceProductEntity, (product) => product.model, { - nullable: true, - }) - public subspaceProducts: SubspaceProductEntity[]; - - @OneToMany( - () => SubspaceProductItemModelEntity, - (product) => product.subspaceProductModel, - { - nullable: true, - }, - ) - public itemModels: SubspaceProductItemModelEntity[]; -} diff --git a/libs/common/src/modules/space-model/entities/tag-model.entity.ts b/libs/common/src/modules/space-model/entities/tag-model.entity.ts new file mode 100644 index 0000000..499ec58 --- /dev/null +++ b/libs/common/src/modules/space-model/entities/tag-model.entity.ts @@ -0,0 +1,29 @@ +import { Column, Entity, JoinColumn, ManyToOne, Unique } from 'typeorm'; +import { AbstractEntity } from '../../abstract/entities/abstract.entity'; +import { TagModelDto } from '../dtos/tag-model.dto'; +import { SpaceModelEntity } from './space-model.entity'; +import { SubspaceModelEntity } from './subspace-model'; +import { ProductEntity } from '../../product/entities'; + +@Entity({ name: 'tag_model' }) +@Unique(['tag', 'product', 'spaceModel', 'subspaceModel']) +export class TagModel extends AbstractEntity { + @Column({ type: 'varchar', length: 255 }) + tag: string; + + @ManyToOne(() => ProductEntity, (product) => product.tagModels, { + nullable: false, + }) + @JoinColumn({ name: 'product_id' }) + product: ProductEntity; + + @ManyToOne(() => SpaceModelEntity, (space) => space.tags, { nullable: true }) + @JoinColumn({ name: 'space_id' }) + spaceModel: SpaceModelEntity; + + @ManyToOne(() => SubspaceModelEntity, (subspace) => subspace.tags, { + nullable: true, + }) + @JoinColumn({ name: 'subspace_id' }) + subspaceModel: SubspaceModelEntity; +} 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 fc92f14..4af0d57 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 @@ -1,13 +1,6 @@ import { DataSource, Repository } from 'typeorm'; import { Injectable } from '@nestjs/common'; -import { - SpaceModelEntity, - SpaceProductItemModelEntity, - SpaceProductModelEntity, - SubspaceModelEntity, - SubspaceProductItemModelEntity, - SubspaceProductModelEntity, -} from '../entities'; +import { SpaceModelEntity, SubspaceModelEntity, TagModel } from '../entities'; @Injectable() export class SpaceModelRepository extends Repository { @@ -23,28 +16,8 @@ export class SubspaceModelRepository extends Repository { } @Injectable() -export class SubspaceProductModelRepository extends Repository { +export class TagModelRepository 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) { - super(SpaceProductModelEntity, dataSource.createEntityManager()); - } -} -@Injectable() -export class SpaceProductItemModelRepository extends Repository { - constructor(private dataSource: DataSource) { - super(SpaceProductItemModelEntity, dataSource.createEntityManager()); + super(TagModel, dataSource.createEntityManager()); } } diff --git a/libs/common/src/modules/space-model/space-model.repository.module.ts b/libs/common/src/modules/space-model/space-model.repository.module.ts index 573bba0..9a35d88 100644 --- a/libs/common/src/modules/space-model/space-model.repository.module.ts +++ b/libs/common/src/modules/space-model/space-model.repository.module.ts @@ -1,10 +1,5 @@ import { TypeOrmModule } from '@nestjs/typeorm'; -import { - SpaceModelEntity, - SpaceProductItemModelEntity, - SpaceProductModelEntity, - SubspaceModelEntity, -} from './entities'; +import { SpaceModelEntity, SubspaceModelEntity, TagModel } from './entities'; import { Module } from '@nestjs/common'; @Module({ @@ -12,12 +7,7 @@ import { Module } from '@nestjs/common'; exports: [], controllers: [], imports: [ - TypeOrmModule.forFeature([ - SpaceModelEntity, - SubspaceModelEntity, - SpaceProductModelEntity, - SpaceProductItemModelEntity, - ]), + TypeOrmModule.forFeature([SpaceModelEntity, SubspaceModelEntity, TagModel]), ], }) export class SpaceModelRepositoryModule {} 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 d9add42..8f93264 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,7 +2,6 @@ import { Column, Entity, ManyToOne, OneToOne } from 'typeorm'; import { SpaceProductEntity } from './space-product.entity'; import { AbstractEntity } from '../../abstract/entities/abstract.entity'; import { SpaceProductItemDto } from '../dtos'; -import { SpaceProductItemModelEntity } from '../../space-model'; import { DeviceEntity } from '../../device/entities'; @Entity({ name: 'space-product-item' }) @@ -23,15 +22,6 @@ export class SpaceProductItemEntity extends AbstractEntity }) public disabled: boolean; - @ManyToOne( - () => SpaceProductItemModelEntity, - (spaceProductItemModel) => spaceProductItemModel.items, - { - nullable: true, - }, - ) - public spaceProductItemModel?: SpaceProductItemModelEntity; - @OneToOne(() => DeviceEntity, (device) => device.tag, { nullable: true, }) 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 ee2cb4f..4e333cd 100644 --- a/libs/common/src/modules/space/entities/space-product.entity.ts +++ b/libs/common/src/modules/space/entities/space-product.entity.ts @@ -3,7 +3,6 @@ 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 { @@ -32,16 +31,6 @@ 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/subspace/subspace-product-item.entity.ts b/libs/common/src/modules/space/entities/subspace/subspace-product-item.entity.ts index 51e206f..eae8a75 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,7 +2,6 @@ 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 { @@ -26,11 +25,6 @@ export class SubspaceProductItemEntity extends AbstractEntity SubspaceProductItemModelEntity, (model) => model.items, { - nullable: true, - }) - model: SubspaceProductItemModelEntity; - constructor(partial: Partial) { 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 77cafa3..b5a16cc 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 @@ -3,7 +3,6 @@ import { Column, Entity, ManyToOne, OneToMany } from 'typeorm'; import { SubspaceEntity } from './subspace.entity'; import { AbstractEntity } from '@app/common/modules/abstract/entities/abstract.entity'; import { SubspaceProductItemEntity } from './subspace-product-item.entity'; -import { SubspaceProductModelEntity } from '@app/common/modules/space-model'; import { SpaceProductModelDto } from '../../dtos'; @Entity({ name: 'subspace-product' }) @@ -26,7 +25,7 @@ export class SubspaceProductEntity extends AbstractEntity }) public subspace: SubspaceEntity; - @ManyToOne(() => ProductEntity, (product) => product.subpaceProductModels, { + @ManyToOne(() => ProductEntity, (product) => product.spaceProducts, { nullable: false, }) public product: ProductEntity; @@ -36,12 +35,4 @@ export class SubspaceProductEntity extends AbstractEntity }) public items: SubspaceProductItemEntity[]; - @ManyToOne( - () => SubspaceProductModelEntity, - (model) => model.subspaceProducts, - { - nullable: true, - }, - ) - model: SubspaceProductModelEntity; } From 5d4f04611f3865b9360de9e45d7156ef73b95839 Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Mon, 23 Dec 2024 08:44:10 +0400 Subject: [PATCH 21/45] service clean up --- .../handlers/propate-subspace-handler.ts | 77 +----- .../interfaces/update-subspace.interface.ts | 14 +- src/space-model/services/index.ts | 2 - .../services/space-model.service.ts | 12 +- .../space-product-item-model.service.ts | 47 ---- .../services/space-product-model.service.ts | 65 ----- src/space-model/services/subspace/index.ts | 2 - .../subspace/subspace-model.service.ts | 27 +- .../subspace-product-item-model.service.ts | 187 -------------- .../subspace-product-model.service.ts | 239 ------------------ src/space-model/space-model.module.ts | 21 +- .../space-product-items.service.ts | 31 --- .../space-products/space-products.service.ts | 34 --- src/space/services/space.service.ts | 6 - .../subspace/subspace-product-item.service.ts | 42 --- .../subspace/subspace-product.service.ts | 50 ---- .../services/subspace/subspace.service.ts | 12 +- 17 files changed, 8 insertions(+), 860 deletions(-) delete mode 100644 src/space-model/services/space-product-item-model.service.ts delete mode 100644 src/space-model/services/space-product-model.service.ts delete mode 100644 src/space-model/services/subspace/subspace-product-item-model.service.ts delete mode 100644 src/space-model/services/subspace/subspace-product-model.service.ts diff --git a/src/space-model/handlers/propate-subspace-handler.ts b/src/space-model/handlers/propate-subspace-handler.ts index d3ff2a1..7d205f9 100644 --- a/src/space-model/handlers/propate-subspace-handler.ts +++ b/src/space-model/handlers/propate-subspace-handler.ts @@ -2,11 +2,7 @@ import { CommandHandler, ICommandHandler } from '@nestjs/cqrs'; import { PropogateSubspaceCommand } from '../commands'; import { Logger } from '@nestjs/common'; import { SpaceEntity, SpaceRepository } from '@app/common/modules/space'; -import { - SubspaceProductItemRepository, - SubspaceProductRepository, - SubspaceRepository, -} from '@app/common/modules/space/repositories/subspace.repository'; +import { SubspaceRepository } from '@app/common/modules/space/repositories/subspace.repository'; import { IDeletedSubsaceModelInterface, IUpdateSubspaceModelInterface, @@ -21,8 +17,6 @@ export class PropogateSubspaceHandler constructor( private readonly spaceRepository: SpaceRepository, private readonly subspaceRepository: SubspaceRepository, - private readonly productRepository: SubspaceProductRepository, - private readonly productItemRepository: SubspaceProductItemRepository, ) {} async execute(command: PropogateSubspaceCommand): Promise { @@ -130,13 +124,9 @@ export class PropogateSubspaceHandler for (const newSubspaceModel of newSubspaceModels) { for (const space of spaces) { try { - const subspace = await this.createSubspace(newSubspaceModel, space); + await this.createSubspace(newSubspaceModel, space); if (newSubspaceModel.productModels?.length > 0) { - await this.processProducts( - newSubspaceModel.productModels, - subspace, - ); } } catch (error) { this.logger.error( @@ -162,69 +152,6 @@ export class PropogateSubspaceHandler return createdSubspace; } - private async processProducts(productModels: any[], subspace: any) { - for (const productModel of productModels) { - try { - const subspaceProduct = await this.createSubspaceProduct( - productModel, - subspace, - ); - - if (productModel.productItemModels?.length > 0) { - await this.processProductItems( - productModel.productItemModels, - subspaceProduct, - ); - } - } catch (error) { - this.logger.error( - `Failed to create product for subspace ID: ${subspace.id}`, - error.stack, - ); - } - } - } - - private async createSubspaceProduct(productModel: any, subspace: any) { - const subspaceProduct = this.productRepository.create({ - product: productModel.productModel.product, - subspace, - model: productModel.productModel, - }); - - const createdSubspaceProduct = - await this.productRepository.save(subspaceProduct); - this.logger.log( - `Product added to subspace ${subspace.id} with count ${createdSubspaceProduct.items.length}`, - ); - return createdSubspaceProduct; - } - - private async processProductItems( - productItemModels: any[], - subspaceProduct: any, - ) { - for (const productItemModel of productItemModels) { - try { - const subspaceProductItem = this.productItemRepository.create({ - tag: productItemModel.tag, - subspaceProduct, - model: productItemModel, - }); - - await this.productItemRepository.save(subspaceProductItem); - this.logger.log( - `Product item added to subspace product ${subspaceProduct.id} with tag ${subspaceProductItem.tag}`, - ); - } catch (error) { - this.logger.error( - `Failed to create product item for subspace product ID: ${subspaceProduct.id}`, - error.stack, - ); - } - } - } - private async getSpacesByModel(uuid: string): Promise { try { return await this.spaceRepository.find({ diff --git a/src/space-model/interfaces/update-subspace.interface.ts b/src/space-model/interfaces/update-subspace.interface.ts index 5898d34..10332ad 100644 --- a/src/space-model/interfaces/update-subspace.interface.ts +++ b/src/space-model/interfaces/update-subspace.interface.ts @@ -1,18 +1,10 @@ -import { - SubspaceModelEntity, - SubspaceProductItemModelEntity, - SubspaceProductModelEntity, -} from '@app/common/modules/space-model'; +import { SubspaceModelEntity } from '@app/common/modules/space-model'; export interface AddSubspaceModelInterface { subspaceModel: SubspaceModelEntity; - productModels: ProductModelInterface[]; } -export interface ProductModelInterface { - productModel: SubspaceProductModelEntity; - productItemModels: SubspaceProductItemModelEntity[]; -} +export interface ProductModelInterface {} export interface IModifySubspaceModelInterface { spaceModelUuid: string; @@ -22,8 +14,6 @@ export interface IModifySubspaceModelInterface { } export interface IModifiedProductItemsModelsInterface { - new?: SubspaceProductItemModelEntity[]; - update?: SubspaceProductItemModelEntity[]; delete?: string[]; } diff --git a/src/space-model/services/index.ts b/src/space-model/services/index.ts index 5c39727..9999493 100644 --- a/src/space-model/services/index.ts +++ b/src/space-model/services/index.ts @@ -1,4 +1,2 @@ export * from './space-model.service'; -export * from './space-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 fff944b..e6156d9 100644 --- a/src/space-model/services/space-model.service.ts +++ b/src/space-model/services/space-model.service.ts @@ -7,7 +7,6 @@ import { CreateSpaceModelDto, UpdateSpaceModelDto } from '../dtos'; import { ProjectParam } from 'src/community/dtos'; import { SuccessResponseDto } from '@app/common/dto/success.response.dto'; import { SubSpaceModelService } from './subspace/subspace-model.service'; -import { SpaceProductModelService } from './space-product-model.service'; import { DataSource } from 'typeorm'; import { TypeORMCustomModel, @@ -29,7 +28,6 @@ export class SpaceModelService { private readonly spaceModelRepository: SpaceModelRepository, private readonly projectService: ProjectService, private readonly subSpaceModelService: SubSpaceModelService, - private readonly spaceProductModelService: SpaceProductModelService, private commandBus: CommandBus, ) {} @@ -37,8 +35,7 @@ export class SpaceModelService { createSpaceModelDto: CreateSpaceModelDto, params: ProjectParam, ) { - const { modelName, subspaceModels, spaceProductModels } = - createSpaceModelDto; + const { modelName, subspaceModels } = createSpaceModelDto; const project = await this.validateProject(params.projectUuid); const queryRunner = this.dataSource.createQueryRunner(); @@ -72,13 +69,6 @@ export class SpaceModelService { ); } - if (spaceProductModels) { - await this.spaceProductModelService.createSpaceProductModels( - spaceProductModels, - savedSpaceModel, - queryRunner, - ); - } await queryRunner.commitTransaction(); return new SuccessResponseDto({ diff --git a/src/space-model/services/space-product-item-model.service.ts b/src/space-model/services/space-product-item-model.service.ts deleted file mode 100644 index c69cae8..0000000 --- a/src/space-model/services/space-product-item-model.service.ts +++ /dev/null @@ -1,47 +0,0 @@ -import { - SpaceModelEntity, - SpaceProductItemModelRepository, - SpaceProductModelEntity, -} from '@app/common/modules/space-model'; -import { HttpException, HttpStatus, Injectable } from '@nestjs/common'; -import { CreateProductItemModelDto } from '../dtos'; -import { QueryRunner } from 'typeorm'; -import { BaseProductItemService } from '../common'; - -@Injectable() -export class SpaceProductItemModelService extends BaseProductItemService { - constructor( - private readonly spaceProductItemRepository: SpaceProductItemModelRepository, - ) { - super(); - } - - async createProdutItemModel( - itemModelDtos: CreateProductItemModelDto[], - spaceProductModel: SpaceProductModelEntity, - spaceModel: SpaceModelEntity, - queryRunner: QueryRunner, - ) { - await this.validateTags(itemModelDtos, queryRunner, spaceModel); - try { - const productItems = itemModelDtos.map((dto) => - queryRunner.manager.create(this.spaceProductItemRepository.target, { - tag: dto.tag, - spaceProductModel, - }), - ); - - 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/space-product-model.service.ts b/src/space-model/services/space-product-model.service.ts deleted file mode 100644 index ba14317..0000000 --- a/src/space-model/services/space-product-model.service.ts +++ /dev/null @@ -1,65 +0,0 @@ -import { QueryRunner } from 'typeorm'; -import { HttpException, HttpStatus, Injectable } from '@nestjs/common'; -import { CreateProductModelDto } 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'; - -@Injectable() -export class SpaceProductModelService extends BaseProductModelService { - constructor( - private readonly spaceProductModelRepository: SpaceProductModelRepository, - private readonly spaceProductItemModelService: SpaceProductItemModelService, - productService: ProductService, - ) { - super(productService); - } - - async createSpaceProductModels( - spaceProductModelDtos: CreateProductModelDto[], - spaceModel: SpaceModelEntity, - queryRunner: QueryRunner, - ) { - try { - const productModels = await Promise.all( - spaceProductModelDtos.map(async (dto) => { - const product = await this.getProduct(dto.productUuid); - return queryRunner.manager.create( - this.spaceProductModelRepository.target, - { - product, - spaceModel, - }, - ); - }), - ); - - const savedProductModels = await queryRunner.manager.save(productModels); - - await Promise.all( - spaceProductModelDtos.map((dto, index) => { - const savedModel = savedProductModels[index]; - return this.spaceProductItemModelService.createProdutItemModel( - dto.items, - savedModel, - 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/services/subspace/index.ts b/src/space-model/services/subspace/index.ts index 78d7cd3..3965e6d 100644 --- a/src/space-model/services/subspace/index.ts +++ b/src/space-model/services/subspace/index.ts @@ -1,3 +1 @@ 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/subspace-model.service.ts b/src/space-model/services/subspace/subspace-model.service.ts index caefce2..fad3ee2 100644 --- a/src/space-model/services/subspace/subspace-model.service.ts +++ b/src/space-model/services/subspace/subspace-model.service.ts @@ -9,7 +9,6 @@ import { ModifySubspacesModelDto, } from '../../dtos'; import { QueryRunner } from 'typeorm'; -import { SubspaceProductModelService } from './subspace-product-model.service'; import { IDeletedSubsaceModelInterface, IModifySubspaceModelInterface, @@ -21,7 +20,6 @@ import { DeleteSubspaceModelDto } from 'src/space-model/dtos/subspaces-model-dto export class SubSpaceModelService { constructor( private readonly subspaceModelRepository: SubspaceModelRepository, - private readonly subSpaceProducetModelService: SubspaceProductModelService, ) {} async createSubSpaceModels( @@ -44,16 +42,9 @@ export class SubSpaceModelService { const addedSubspaces = await Promise.all( subSpaceModelDtos.map(async (dto, index) => { const subspaceModel = newSubspaces[index]; - const productModels = - await this.subSpaceProducetModelService.createSubspaceProductModels( - dto.spaceProductModels, - spaceModel, - subspaceModel, - queryRunner, - ); + return { subspaceModel, - productModels, }; }), ); @@ -103,15 +94,6 @@ export class SubSpaceModelService { updateResult.subspaceName = dto.subspaceName; } - if (dto.updatedProductModels) { - await this.subSpaceProducetModelService.modifySubspaceProductModels( - dto.updatedProductModels, - spaceModel, - subspaceModel, - queryRunner, - ); - } - updateResults.push(updateResult); } catch (error) { throw new HttpException( @@ -168,13 +150,6 @@ export class SubSpaceModelService { { disabled: true }, ); - if (subspaceModel.productModels.length > 0) { - await this.subSpaceProducetModelService.disableProductModels( - subspaceModel.productModels, - queryRunner, - ); - } - deleteResults.push({ uuid: dto.subspaceUuid }); } catch (error) { throw new HttpException( diff --git a/src/space-model/services/subspace/subspace-product-item-model.service.ts b/src/space-model/services/subspace/subspace-product-item-model.service.ts deleted file mode 100644 index 592e45c..0000000 --- a/src/space-model/services/subspace/subspace-product-item-model.service.ts +++ /dev/null @@ -1,187 +0,0 @@ -import { HttpException, HttpStatus, Injectable } from '@nestjs/common'; -import { QueryRunner } from 'typeorm'; -import { - SpaceModelEntity, - SubspaceProductItemModelEntity, - SubspaceProductItemModelRepository, - SubspaceProductModelEntity, -} from '@app/common/modules/space-model'; -import { BaseProductItemService } from '../../common'; -import { - CreateProductItemModelDto, - DeleteProductItemModelDto, - ProductItemModelModificationDto, - UpdateProductItemModelDto, -} from '../../dtos'; -import { IModifiedProductItemsModelsInterface } from 'src/space-model/interfaces'; - -@Injectable() -export class SubspaceProductItemModelService extends BaseProductItemService { - constructor( - private readonly subspaceProductItemRepository: SubspaceProductItemModelRepository, - ) { - super(); - } - - async createProductItemModel( - itemModelDtos: CreateProductItemModelDto[], - subspaceProductModel: SubspaceProductModelEntity, - spaceModel: SpaceModelEntity, - queryRunner: QueryRunner, - ): Promise { - 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, - }), - ); - return await this.saveProductItems( - productItems, - this.subspaceProductItemRepository, - queryRunner, - ); - } catch (error) { - this.handleException( - error, - 'An unexpected error occurred while creating product items.', - ); - } - } - - async updateProductItemModel( - dtos: UpdateProductItemModelDto[], - spaceModel: SpaceModelEntity, - queryRunner: QueryRunner, - ): Promise { - try { - await this.validateTags(dtos, queryRunner, spaceModel); - - const productItemModels = await Promise.all( - dtos.map(async (dto) => { - const productItemModel = await this.findOne(dto.productModelUuid); - productItemModel.tag = dto.tag; - return productItemModel; - }), - ); - - return (await this.saveProductItems( - productItemModels, - this.subspaceProductItemRepository, - queryRunner, - )) as SubspaceProductItemModelEntity[]; - } catch (error) { - this.handleException( - error, - 'Failed to save Subspace Product Item Model.', - ); - } - } - - async deleteProductModel( - dtos: DeleteProductItemModelDto[], - queryRunner: QueryRunner, - ): Promise { - try { - if (dtos.length === 0) return; - const productItemModels = await Promise.all( - dtos.map(async (dto) => { - const productItemModel = await this.findOne(dto.productModelUuid); - productItemModel.disabled = true; - return productItemModel; - }), - ); - - const disabledItemModels = await this.disableProductItemModels( - productItemModels, - queryRunner, - ); - - return disabledItemModels.map((item) => item.uuid); - } catch (error) { - this.handleException(error, 'Failed to delete SpaceModels.'); - } - } - - async disableProductItemModels( - productItemModels: SubspaceProductItemModelEntity[], - queryRunner: QueryRunner, - ): Promise { - productItemModels.forEach((model) => (model.disabled = true)); - return (await this.saveProductItems( - productItemModels, - this.subspaceProductItemRepository, - queryRunner, - )) as SubspaceProductItemModelEntity[]; - } - - async findOne(uuid: string): Promise { - const productItemModel = await this.subspaceProductItemRepository.findOne({ - where: { uuid }, - }); - - if (!productItemModel) { - throw new HttpException( - `Product item model not found for ${uuid}`, - HttpStatus.NOT_FOUND, - ); - } - - return productItemModel; - } - - async modifyProductItemModel( - dto: ProductItemModelModificationDto, - productModel: SubspaceProductModelEntity, - spaceModel: SpaceModelEntity, - queryRunner: QueryRunner, - ): Promise { - const productItemModels: IModifiedProductItemsModelsInterface = {}; - - try { - if (dto.add) { - productItemModels.new = await this.createProductItemModel( - dto.add, - productModel, - spaceModel, - queryRunner, - ); - } - if (dto.update) { - productItemModels.update = await this.updateProductItemModel( - dto.update, - spaceModel, - queryRunner, - ); - } - if (dto.delete) { - productItemModels.delete = await this.deleteProductModel( - dto.delete, - queryRunner, - ); - } - return productItemModels; - } catch (error) { - this.handleException(error, 'Failed to modify Product Item Models.'); - } - } - - private handleException(error: unknown, defaultMessage: string): never { - if (error instanceof HttpException) { - throw error; - } - throw new HttpException( - (error as Error).message || defaultMessage, - HttpStatus.INTERNAL_SERVER_ERROR, - ); - } -} diff --git a/src/space-model/services/subspace/subspace-product-model.service.ts b/src/space-model/services/subspace/subspace-product-model.service.ts deleted file mode 100644 index 724234d..0000000 --- a/src/space-model/services/subspace/subspace-product-model.service.ts +++ /dev/null @@ -1,239 +0,0 @@ -import { - SpaceModelEntity, - SubspaceModelEntity, - SubspaceProductModelEntity, - SubspaceProductModelRepository, -} from '@app/common/modules/space-model'; -import { In, QueryRunner } from 'typeorm'; -import { HttpException, HttpStatus, Injectable } from '@nestjs/common'; - -import { SubspaceProductItemModelService } from './subspace-product-item-model.service'; -import { - CreateProductModelDto, - DeleteProductModelDto, - ProductModelModificationDto, - UpdateProductModelDto, -} from '../../dtos'; -import { BaseProductModelService } from '../../common'; -import { ProductService } from 'src/product/services'; -import { - IModifiedProductModelsInterface, - IUpdatedProductModelInterface, - ProductModelInterface, -} from '../../interfaces'; - -@Injectable() -export class SubspaceProductModelService extends BaseProductModelService { - constructor( - private readonly subpaceProductModelRepository: SubspaceProductModelRepository, - productService: ProductService, - private readonly subspaceProductItemModelService: SubspaceProductItemModelService, - ) { - super(productService); - } - - async createSubspaceProductModels( - spaceProductModelDtos: CreateProductModelDto[], - spaceModel: SpaceModelEntity, - subspaceModel: SubspaceModelEntity, - queryRunner: QueryRunner, - ): Promise { - try { - if (!spaceProductModelDtos?.length) return; - - const productModels = await Promise.all( - spaceProductModelDtos.map(async (dto) => { - const product = await this.getProduct(dto.productUuid); - return queryRunner.manager.create( - this.subpaceProductModelRepository.target, - { - product, - subspaceModel, - }, - ); - }), - ); - - const savedProductModels = await queryRunner.manager.save(productModels); - - const newProductModels = await Promise.all( - spaceProductModelDtos.map(async (dto, index) => { - const savedModel = savedProductModels[index]; - const productItemModels = - await this.subspaceProductItemModelService.createProductItemModel( - dto.items, - savedModel, - spaceModel, - queryRunner, - ); - return { - productModel: savedModel, - productItemModels, - } as ProductModelInterface; - }), - ); - return newProductModels; - } 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, - ); - } - } - - async updateSubspaceProductModels( - dtos: UpdateProductModelDto[], - spaceModel: SpaceModelEntity, - queryRunner: QueryRunner, - ): Promise { - try { - const updatedProductModels = await Promise.all( - dtos.map(async (dto) => { - const productModel = await this.findOne(dto.productModelUuid); - const productModifiedItemModel = - await this.subspaceProductItemModelService.modifyProductItemModel( - dto.items, - productModel, - spaceModel, - queryRunner, - ); - return { - productModelUuid: productModel.uuid, - productModifiedItemModel, - }; - }), - ); - - return updatedProductModels; - } catch (error) { - if (error instanceof HttpException) { - throw error; - } - throw new HttpException( - 'Failed to update Subspace product model', - HttpStatus.INTERNAL_SERVER_ERROR, - ); - } - } - - async removeSubspaceProductModels( - deleteDtos: DeleteProductModelDto[], - transactionRunner: QueryRunner, - ): Promise { - try { - const productModels = await Promise.all( - deleteDtos.map((dto) => this.findOne(dto.productModelUuid)), - ); - - return await this.disableProductModels(productModels, transactionRunner); - } catch (error) { - throw new HttpException( - 'Failed to remove subspace product models', - HttpStatus.INTERNAL_SERVER_ERROR, - ); - } - } - - async disableProductModels( - productModels: SubspaceProductModelEntity[], - transactionRunner: QueryRunner, - ): Promise { - try { - const productModelUuids = productModels.map((model) => model.uuid); - - await transactionRunner.manager.update( - this.subpaceProductModelRepository.target, - { uuid: In(productModelUuids) }, - { disabled: true }, - ); - - const itemModelDisables = productModels.map((model) => - model.itemModels.length > 0 - ? this.subspaceProductItemModelService.disableProductItemModels( - model.itemModels, - transactionRunner, - ) - : Promise.resolve(), - ); - - await Promise.all(itemModelDisables); - - return productModelUuids; - } catch (error) { - throw new HttpException( - 'Failed to disable product models', - HttpStatus.INTERNAL_SERVER_ERROR, - ); - } - } - - async modifySubspaceProductModels( - modificationDto: ProductModelModificationDto, - spaceEntity: SpaceModelEntity, - subspaceEntity: SubspaceModelEntity, - transactionRunner: QueryRunner, - ): Promise { - const modifiedProductModels: IModifiedProductModelsInterface = {}; - - try { - await Promise.all([ - modificationDto.add - ? this.createSubspaceProductModels( - modificationDto.add, - spaceEntity, - subspaceEntity, - transactionRunner, - ).then((addedModels) => { - modifiedProductModels.add = addedModels; - }) - : Promise.resolve(), - modificationDto.update - ? this.updateSubspaceProductModels( - modificationDto.update, - spaceEntity, - transactionRunner, - ).then((updatedModels) => { - modifiedProductModels.update = updatedModels; - }) - : Promise.resolve(), - modificationDto.delete - ? this.removeSubspaceProductModels( - modificationDto.delete, - transactionRunner, - ).then((deletedModels) => { - modifiedProductModels.delete = deletedModels; - }) - : Promise.resolve(), - ]); - - return modifiedProductModels; - } catch (error) { - if (error instanceof HttpException) { - throw error; - } - throw new HttpException( - 'Failed to modify subspace product models', - HttpStatus.INTERNAL_SERVER_ERROR, - ); - } - } - - async findOne(uuid: string): Promise { - const productModel = await this.subpaceProductModelRepository.findOne({ - where: { - uuid, - }, - relations: ['itemModels'], - }); - if (!productModel) - throw new HttpException( - `Subspace Product model with uuid ${uuid} not found`, - HttpStatus.NOT_FOUND, - ); - return productModel; - } -} diff --git a/src/space-model/space-model.module.ts b/src/space-model/space-model.module.ts index e1e4b56..2140d9c 100644 --- a/src/space-model/space-model.module.ts +++ b/src/space-model/space-model.module.ts @@ -2,24 +2,13 @@ import { SpaceRepositoryModule } from '@app/common/modules/space/space.repositor import { Module } from '@nestjs/common'; import { ConfigModule } from '@nestjs/config'; import { SpaceModelController } from './controllers'; -import { - SpaceModelService, - SpaceProductItemModelService, - SpaceProductModelService, - SubSpaceModelService, - SubspaceProductItemModelService, -} from './services'; +import { SpaceModelService, SubSpaceModelService } 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/subspace-product-model.service'; import { PropogateSubspaceHandler } from './handlers'; import { CqrsModule } from '@nestjs/cqrs'; import { SpaceRepository } from '@app/common/modules/space'; @@ -41,16 +30,8 @@ const CommandHandlers = [PropogateSubspaceHandler]; SpaceRepository, ProjectRepository, SubSpaceModelService, - SpaceProductModelService, SubspaceModelRepository, - SpaceProductModelRepository, ProductRepository, - SpaceProductItemModelService, - SpaceProductItemModelRepository, - SubspaceProductItemModelService, - SubspaceProductItemModelRepository, - SubspaceProductModelService, - SubspaceProductModelRepository, SubspaceRepository, SubspaceProductRepository, SubspaceProductItemRepository, 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 d4883b9..d9c141b 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 @@ -6,7 +6,6 @@ import { 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() @@ -54,34 +53,4 @@ export class SpaceProductItemService extends BaseProductItemService { ); } } - - async createSpaceProductItemFromModel( - spaceProduct: SpaceProductEntity, - spaceProductModel: SpaceProductModelEntity, - queryRunner: QueryRunner, - ) { - const spaceProductItemModels = spaceProductModel.items; - if (!spaceProductItemModels?.length) return; - - 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, - ); - } - } } diff --git a/src/space/services/space-products/space-products.service.ts b/src/space/services/space-products/space-products.service.ts index 6c5f1bd..4071428 100644 --- a/src/space/services/space-products/space-products.service.ts +++ b/src/space/services/space-products/space-products.service.ts @@ -5,7 +5,6 @@ import { SpaceProductEntity } from '@app/common/modules/space/entities/space-pro 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'; @@ -17,39 +16,6 @@ export class SpaceProductService { private readonly productService: ProductService, ) {} - async createFromModel( - 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, - 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: ProductAssignmentDto[], diff --git a/src/space/services/space.service.ts b/src/space/services/space.service.ts index df0b0a4..2cdf92a 100644 --- a/src/space/services/space.service.ts +++ b/src/space/services/space.service.ts @@ -108,12 +108,6 @@ export class SpaceService { products, queryRunner, ); - } else if (spaceModel && spaceModel.spaceProductModels.length) { - await this.spaceProductService.createFromModel( - spaceModel, - newSpace, - queryRunner, - ); } await queryRunner.commitTransaction(); diff --git a/src/space/services/subspace/subspace-product-item.service.ts b/src/space/services/subspace/subspace-product-item.service.ts index 647a597..3eefb2f 100644 --- a/src/space/services/subspace/subspace-product-item.service.ts +++ b/src/space/services/subspace/subspace-product-item.service.ts @@ -6,10 +6,6 @@ import { 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 '../../dtos'; import { BaseProductItemService } from '../../common'; @@ -22,44 +18,6 @@ export class SubspaceProductItemService extends BaseProductItemService { super(); } - 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: SubspaceProductItemModelEntity, - queryRunner: QueryRunner, - ): Partial { - return queryRunner.manager.create(this.productItemRepository.target, { - tag: model.tag, - product, - model, - }); - } - async createItemFromDtos( product: SubspaceProductEntity, itemDto: CreateSpaceProductItemDto[], diff --git a/src/space/services/subspace/subspace-product.service.ts b/src/space/services/subspace/subspace-product.service.ts index 48852d7..93ef045 100644 --- a/src/space/services/subspace/subspace-product.service.ts +++ b/src/space/services/subspace/subspace-product.service.ts @@ -6,10 +6,6 @@ import { SubspaceEntity, SubspaceProductEntity, } from '@app/common/modules/space'; -import { - SubspaceModelEntity, - 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'; @@ -22,52 +18,6 @@ export class SubspaceProductService { private readonly productService: ProductService, ) {} - 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, - model: productModel, - }; - } - async createFromDto( productDtos: ProductAssignmentDto[], subspace: SubspaceEntity, diff --git a/src/space/services/subspace/subspace.service.ts b/src/space/services/subspace/subspace.service.ts index 6b9a1a3..08e12e2 100644 --- a/src/space/services/subspace/subspace.service.ts +++ b/src/space/services/subspace/subspace.service.ts @@ -66,17 +66,7 @@ export class SubSpaceService { subSpaceModel, })); - const subspaces = await this.createSubspaces(subspaceData, queryRunner); - - await Promise.all( - subSpaceModels.map((model, index) => { - this.productService.createFromModel( - model, - subspaces[index], - queryRunner, - ); - }), - ); + await this.createSubspaces(subspaceData, queryRunner); } async createSubspacesFromDto( From a623028d7b81c63136458ef29295b68dda7380bc Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Mon, 23 Dec 2024 08:47:57 +0400 Subject: [PATCH 22/45] dto clean up --- .../dtos/create-space-model.dto.ts | 10 ---------- .../create-subspace-model.dto.ts | 20 +------------------ .../services/space-model.service.ts | 13 +----------- 3 files changed, 2 insertions(+), 41 deletions(-) diff --git a/src/space-model/dtos/create-space-model.dto.ts b/src/space-model/dtos/create-space-model.dto.ts index 31ff764..6ffaf26 100644 --- a/src/space-model/dtos/create-space-model.dto.ts +++ b/src/space-model/dtos/create-space-model.dto.ts @@ -2,7 +2,6 @@ import { ApiProperty } from '@nestjs/swagger'; import { IsNotEmpty, IsString, IsArray, ValidateNested } from 'class-validator'; import { Type } from 'class-transformer'; import { CreateSubspaceModelDto } from './subspaces-model-dtos/create-subspace-model.dto'; -import { CreateProductModelDto } from './product-model-dtos'; export class CreateSpaceModelDto { @ApiProperty({ @@ -21,13 +20,4 @@ export class CreateSpaceModelDto { @ValidateNested({ each: true }) @Type(() => CreateSubspaceModelDto) subspaceModels?: CreateSubspaceModelDto[]; - - @ApiProperty({ - description: 'List of products included in the model', - type: [CreateProductModelDto], - }) - @IsArray() - @ValidateNested({ each: true }) - @Type(() => CreateProductModelDto) - spaceProductModels?: CreateProductModelDto[]; } diff --git a/src/space-model/dtos/subspaces-model-dtos/create-subspace-model.dto.ts b/src/space-model/dtos/subspaces-model-dtos/create-subspace-model.dto.ts index ce5d2d1..a27ad3b 100644 --- a/src/space-model/dtos/subspaces-model-dtos/create-subspace-model.dto.ts +++ b/src/space-model/dtos/subspaces-model-dtos/create-subspace-model.dto.ts @@ -1,13 +1,5 @@ import { ApiProperty } from '@nestjs/swagger'; -import { - IsArray, - IsNotEmpty, - IsOptional, - IsString, - ValidateNested, -} from 'class-validator'; -import { CreateProductModelDto } from '../product-model-dtos'; -import { Type } from 'class-transformer'; +import { IsNotEmpty, IsString } from 'class-validator'; export class CreateSubspaceModelDto { @ApiProperty({ @@ -17,14 +9,4 @@ export class CreateSubspaceModelDto { @IsNotEmpty() @IsString() subspaceName: string; - - @ApiProperty({ - description: 'List of products included in the model', - type: [CreateProductModelDto], - }) - @IsArray() - @IsOptional() - @ValidateNested({ each: true }) - @Type(() => CreateProductModelDto) - spaceProductModels?: CreateProductModelDto[]; } diff --git a/src/space-model/services/space-model.service.ts b/src/space-model/services/space-model.service.ts index e6156d9..92301ad 100644 --- a/src/space-model/services/space-model.service.ts +++ b/src/space-model/services/space-model.service.ts @@ -17,9 +17,6 @@ import { SpaceModelDto } from '@app/common/modules/space-model/dtos'; import { SpaceModelParam } from '../dtos/space-model-param'; import { ProjectService } from 'src/project/services'; import { ProjectEntity } from '@app/common/modules/project/entities'; -import { IModifySubspaceModelInterface } from '../interfaces'; -import { CommandBus } from '@nestjs/cqrs'; -import { PropogateSubspaceCommand } from '../commands'; @Injectable() export class SpaceModelService { @@ -28,7 +25,6 @@ export class SpaceModelService { private readonly spaceModelRepository: SpaceModelRepository, private readonly projectService: ProjectService, private readonly subSpaceModelService: SubSpaceModelService, - private commandBus: CommandBus, ) {} async createSpaceModel( @@ -136,13 +132,12 @@ export class SpaceModelService { await queryRunner.startTransaction(); try { const { modelName } = dto; - let updatedSubspaces: IModifySubspaceModelInterface; if (modelName) spaceModel.modelName = modelName; await queryRunner.manager.save(spaceModel); if (dto.subspaceModels) { - updatedSubspaces = await this.subSpaceModelService.modifySubSpaceModels( + await this.subSpaceModelService.modifySubSpaceModels( dto.subspaceModels, spaceModel, queryRunner, @@ -151,12 +146,6 @@ export class SpaceModelService { await queryRunner.commitTransaction(); - if (updatedSubspaces) { - await this.commandBus.execute( - new PropogateSubspaceCommand(updatedSubspaces), - ); - } - return new SuccessResponseDto({ message: 'SpaceModel updated successfully', data: spaceModel, From 4545b2e84cc8b958957a12dbbd1f7aea7c2e4363 Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Mon, 23 Dec 2024 08:49:07 +0400 Subject: [PATCH 23/45] dto clean up --- .../base-product-item-model.dto.ts | 12 ------- .../create-product-item-model.dto.ts | 12 ------- .../delete-product-item-model.dto.ts | 3 -- .../dtos/product-item-model-dtos/index.ts | 4 --- .../product-item-model-modification.ts | 34 ------------------- .../update-prodct-item-model.dto.ts | 13 ------- .../base-product-model.dto.ts | 12 ------- .../create-product-model.dto.ts | 30 ---------------- .../delete-product-model.dto.ts | 3 -- .../dtos/product-model-dtos/index.ts | 4 --- .../product-model-modification.dto.ts | 34 ------------------- .../update-product-model.dto.ts | 17 ---------- .../update-subspace-model.dto.ts | 19 +---------- 13 files changed, 1 insertion(+), 196 deletions(-) delete mode 100644 src/space-model/dtos/product-item-model-dtos/base-product-item-model.dto.ts delete mode 100644 src/space-model/dtos/product-item-model-dtos/create-product-item-model.dto.ts delete mode 100644 src/space-model/dtos/product-item-model-dtos/delete-product-item-model.dto.ts delete mode 100644 src/space-model/dtos/product-item-model-dtos/index.ts delete mode 100644 src/space-model/dtos/product-item-model-dtos/product-item-model-modification.ts delete mode 100644 src/space-model/dtos/product-item-model-dtos/update-prodct-item-model.dto.ts delete mode 100644 src/space-model/dtos/product-model-dtos/base-product-model.dto.ts delete mode 100644 src/space-model/dtos/product-model-dtos/create-product-model.dto.ts delete mode 100644 src/space-model/dtos/product-model-dtos/delete-product-model.dto.ts delete mode 100644 src/space-model/dtos/product-model-dtos/index.ts delete mode 100644 src/space-model/dtos/product-model-dtos/product-model-modification.dto.ts delete mode 100644 src/space-model/dtos/product-model-dtos/update-product-model.dto.ts diff --git a/src/space-model/dtos/product-item-model-dtos/base-product-item-model.dto.ts b/src/space-model/dtos/product-item-model-dtos/base-product-item-model.dto.ts deleted file mode 100644 index dc01713..0000000 --- a/src/space-model/dtos/product-item-model-dtos/base-product-item-model.dto.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { ApiProperty } from '@nestjs/swagger'; -import { IsNotEmpty, IsString } from 'class-validator'; - -export class BaseProductItemModelDto { - @ApiProperty({ - description: 'ID of the product item model', - example: 'product-item-uuid', - }) - @IsNotEmpty() - @IsString() - productModelUuid: string; -} diff --git a/src/space-model/dtos/product-item-model-dtos/create-product-item-model.dto.ts b/src/space-model/dtos/product-item-model-dtos/create-product-item-model.dto.ts deleted file mode 100644 index e6a7e76..0000000 --- a/src/space-model/dtos/product-item-model-dtos/create-product-item-model.dto.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { ApiProperty } from '@nestjs/swagger'; -import { IsNotEmpty, IsString } from 'class-validator'; - -export class CreateProductItemModelDto { - @ApiProperty({ - description: 'Specific name for the product item', - example: 'Light 1', - }) - @IsNotEmpty() - @IsString() - tag: string; -} diff --git a/src/space-model/dtos/product-item-model-dtos/delete-product-item-model.dto.ts b/src/space-model/dtos/product-item-model-dtos/delete-product-item-model.dto.ts deleted file mode 100644 index 971a0d0..0000000 --- a/src/space-model/dtos/product-item-model-dtos/delete-product-item-model.dto.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { BaseProductItemModelDto } from './base-product-item-model.dto'; - -export class DeleteProductItemModelDto extends BaseProductItemModelDto {} diff --git a/src/space-model/dtos/product-item-model-dtos/index.ts b/src/space-model/dtos/product-item-model-dtos/index.ts deleted file mode 100644 index 5225122..0000000 --- a/src/space-model/dtos/product-item-model-dtos/index.ts +++ /dev/null @@ -1,4 +0,0 @@ -export * from './create-product-item-model.dto'; -export * from './update-prodct-item-model.dto'; -export * from './delete-product-item-model.dto'; -export * from './product-item-model-modification' \ No newline at end of file diff --git a/src/space-model/dtos/product-item-model-dtos/product-item-model-modification.ts b/src/space-model/dtos/product-item-model-dtos/product-item-model-modification.ts deleted file mode 100644 index 04ca2d6..0000000 --- a/src/space-model/dtos/product-item-model-dtos/product-item-model-modification.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { ApiProperty } from '@nestjs/swagger'; -import { IsArray, ValidateNested, IsOptional } from 'class-validator'; - -import { CreateProductItemModelDto } from './create-product-item-model.dto'; -import { UpdateProductItemModelDto } from './update-prodct-item-model.dto'; -import { DeleteProductItemModelDto } from './delete-product-item-model.dto'; - -export class ProductItemModelModificationDto { - @IsArray() - @ApiProperty({ - description: 'Create the product item model ', - type: [CreateProductItemModelDto], - }) - @ValidateNested({ each: true }) - @IsOptional() - add?: CreateProductItemModelDto[]; - - @IsArray() - @ApiProperty({ - description: 'Update the product item model ', - type: [UpdateProductItemModelDto], - }) - @ValidateNested({ each: true }) - @IsOptional() - update?: UpdateProductItemModelDto[]; - - @IsArray() - @ApiProperty({ - description: 'Delete the product item model ', - type: [DeleteProductItemModelDto], - }) - @IsOptional() - delete?: DeleteProductItemModelDto[]; -} diff --git a/src/space-model/dtos/product-item-model-dtos/update-prodct-item-model.dto.ts b/src/space-model/dtos/product-item-model-dtos/update-prodct-item-model.dto.ts deleted file mode 100644 index ef88432..0000000 --- a/src/space-model/dtos/product-item-model-dtos/update-prodct-item-model.dto.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { ApiProperty } from '@nestjs/swagger'; -import { IsNotEmpty, IsString } from 'class-validator'; -import { BaseProductItemModelDto } from './base-product-item-model.dto'; - -export class UpdateProductItemModelDto extends BaseProductItemModelDto { - @ApiProperty({ - description: 'Specific name for the product item', - example: 'Light 1', - }) - @IsNotEmpty() - @IsString() - tag: string; -} diff --git a/src/space-model/dtos/product-model-dtos/base-product-model.dto.ts b/src/space-model/dtos/product-model-dtos/base-product-model.dto.ts deleted file mode 100644 index 4a27559..0000000 --- a/src/space-model/dtos/product-model-dtos/base-product-model.dto.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { ApiProperty } from '@nestjs/swagger'; -import { IsNotEmpty, IsString } from 'class-validator'; - -export class BaseProductModelDto { - @ApiProperty({ - description: 'ID of the product model', - example: 'product-uuid', - }) - @IsNotEmpty() - @IsString() - productModelUuid: string; -} diff --git a/src/space-model/dtos/product-model-dtos/create-product-model.dto.ts b/src/space-model/dtos/product-model-dtos/create-product-model.dto.ts deleted file mode 100644 index b40e9d7..0000000 --- a/src/space-model/dtos/product-model-dtos/create-product-model.dto.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { ApiProperty } from '@nestjs/swagger'; -import { - IsNotEmpty, - IsString, - IsArray, - ValidateNested, - ArrayNotEmpty, -} from 'class-validator'; -import { Type } from 'class-transformer'; -import { CreateProductItemModelDto } from '../product-item-model-dtos/create-product-item-model.dto'; - -export class CreateProductModelDto { - @ApiProperty({ - description: 'ID of the product associated with the model', - example: 'product-uuid', - }) - @IsNotEmpty() - @IsString() - productUuid: string; - - @ApiProperty({ - description: 'Specific names for each product item', - type: [CreateProductItemModelDto], - }) - @IsArray() - @ArrayNotEmpty() - @ValidateNested({ each: true }) - @Type(() => CreateProductItemModelDto) - items: CreateProductItemModelDto[]; -} diff --git a/src/space-model/dtos/product-model-dtos/delete-product-model.dto.ts b/src/space-model/dtos/product-model-dtos/delete-product-model.dto.ts deleted file mode 100644 index 5653c8d..0000000 --- a/src/space-model/dtos/product-model-dtos/delete-product-model.dto.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { BaseProductModelDto } from './base-product-model.dto'; - -export class DeleteProductModelDto extends BaseProductModelDto {} diff --git a/src/space-model/dtos/product-model-dtos/index.ts b/src/space-model/dtos/product-model-dtos/index.ts deleted file mode 100644 index b2dbd90..0000000 --- a/src/space-model/dtos/product-model-dtos/index.ts +++ /dev/null @@ -1,4 +0,0 @@ -export * from './create-product-model.dto'; -export * from './delete-product-model.dto'; -export * from './update-product-model.dto'; -export * from './product-model-modification.dto'; diff --git a/src/space-model/dtos/product-model-dtos/product-model-modification.dto.ts b/src/space-model/dtos/product-model-dtos/product-model-modification.dto.ts deleted file mode 100644 index 8bf9133..0000000 --- a/src/space-model/dtos/product-model-dtos/product-model-modification.dto.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { ApiProperty } from '@nestjs/swagger'; -import { IsArray, ValidateNested, IsOptional } from 'class-validator'; - -import { CreateProductModelDto } from './create-product-model.dto'; -import { DeleteProductModelDto } from './delete-product-model.dto'; -import { UpdateProductModelDto } from './update-product-model.dto'; - -export class ProductModelModificationDto { - @IsArray() - @ApiProperty({ - description: 'Create the product model ', - type: [CreateProductModelDto], - }) - @ValidateNested({ each: true }) - @IsOptional() - add?: CreateProductModelDto[]; - - @IsArray() - @ApiProperty({ - description: 'Update the product model ', - type: [UpdateProductModelDto], - }) - @ValidateNested({ each: true }) - @IsOptional() - update?: UpdateProductModelDto[]; - - @IsArray() - @ApiProperty({ - description: 'Delete the product model ', - type: [DeleteProductModelDto], - }) - @IsOptional() - delete?: DeleteProductModelDto[]; -} diff --git a/src/space-model/dtos/product-model-dtos/update-product-model.dto.ts b/src/space-model/dtos/product-model-dtos/update-product-model.dto.ts deleted file mode 100644 index 9310b46..0000000 --- a/src/space-model/dtos/product-model-dtos/update-product-model.dto.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { ApiProperty } from '@nestjs/swagger'; -import { Type } from 'class-transformer'; -import { IsArray, ArrayNotEmpty, ValidateNested } from 'class-validator'; -import { BaseProductModelDto } from './base-product-model.dto'; -import { ProductItemModelModificationDto } from '../product-item-model-dtos'; - -export class UpdateProductModelDto extends BaseProductModelDto { - @ApiProperty({ - description: 'Update product item', - type: [ProductItemModelModificationDto], - }) - @IsArray() - @ArrayNotEmpty() - @ValidateNested({ each: true }) - @Type(() => ProductItemModelModificationDto) - items: ProductItemModelModificationDto; -} diff --git a/src/space-model/dtos/subspaces-model-dtos/update-subspace-model.dto.ts b/src/space-model/dtos/subspaces-model-dtos/update-subspace-model.dto.ts index 93ca12b..a386f4e 100644 --- a/src/space-model/dtos/subspaces-model-dtos/update-subspace-model.dto.ts +++ b/src/space-model/dtos/subspaces-model-dtos/update-subspace-model.dto.ts @@ -1,13 +1,5 @@ import { ApiProperty } from '@nestjs/swagger'; -import { Type } from 'class-transformer'; -import { - IsNotEmpty, - IsString, - IsArray, - IsOptional, - ValidateNested, -} from 'class-validator'; -import { ProductModelModificationDto } from '../product-model-dtos'; +import { IsNotEmpty, IsString } from 'class-validator'; export class UpdateSubspaceModelDto { @ApiProperty({ @@ -21,13 +13,4 @@ export class UpdateSubspaceModelDto { @IsNotEmpty() @IsString() subspaceUuid: string; - - @ApiProperty({ - description: 'Products models modified in the model', - type: ProductModelModificationDto, - }) - @IsOptional() - @ValidateNested({ each: true }) - @Type(() => ProductModelModificationDto) - updatedProductModels?: ProductModelModificationDto; } From 4b55c4e39cb755d2dc8a17fd8e084857b46aa407 Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Mon, 23 Dec 2024 08:53:01 +0400 Subject: [PATCH 24/45] service clean up --- src/space-model/common/index.ts | 1 - .../base-product-item-model.service.ts | 79 ------------------- .../services/base-product-model.service.ts | 10 --- src/space-model/common/services/index.ts | 2 - .../dtos/create-space-model.dto.ts | 10 +++ src/space-model/dtos/index.ts | 2 - .../create-subspace-model.dto.ts | 13 ++- .../tag-model-dtos/create-tag-model.dto.ts | 20 +++++ src/space-model/dtos/tag-model-dtos/index.ts | 0 9 files changed, 42 insertions(+), 95 deletions(-) delete mode 100644 src/space-model/common/index.ts delete mode 100644 src/space-model/common/services/base-product-item-model.service.ts delete mode 100644 src/space-model/common/services/base-product-model.service.ts delete mode 100644 src/space-model/common/services/index.ts create mode 100644 src/space-model/dtos/tag-model-dtos/create-tag-model.dto.ts create mode 100644 src/space-model/dtos/tag-model-dtos/index.ts diff --git a/src/space-model/common/index.ts b/src/space-model/common/index.ts deleted file mode 100644 index e371345..0000000 --- a/src/space-model/common/index.ts +++ /dev/null @@ -1 +0,0 @@ -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 deleted file mode 100644 index 4d67e6d..0000000 --- a/src/space-model/common/services/base-product-item-model.service.ts +++ /dev/null @@ -1,79 +0,0 @@ -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, - ); - } - } - - protected async saveProductItems( - productItems: T[], - targetRepository: any, - queryRunner: QueryRunner, - ): Promise { - try { - const savedItem = await queryRunner.manager.save( - targetRepository, - productItems, - ); - return savedItem; - } catch (error) { - throw new HttpException( - error.message || 'An error occurred while creating product items.', - HttpStatus.INTERNAL_SERVER_ERROR, - ); - } - } -} diff --git a/src/space-model/common/services/base-product-model.service.ts b/src/space-model/common/services/base-product-model.service.ts deleted file mode 100644 index 6ff8d91..0000000 --- a/src/space-model/common/services/base-product-model.service.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { ProductService } from '../../../product/services'; - -export abstract class BaseProductModelService { - constructor(private readonly productService: ProductService) {} - - 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 deleted file mode 100644 index d1cc61e..0000000 --- a/src/space-model/common/services/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './base-product-item-model.service'; -export * from './base-product-model.service'; diff --git a/src/space-model/dtos/create-space-model.dto.ts b/src/space-model/dtos/create-space-model.dto.ts index 6ffaf26..0c37779 100644 --- a/src/space-model/dtos/create-space-model.dto.ts +++ b/src/space-model/dtos/create-space-model.dto.ts @@ -2,6 +2,7 @@ import { ApiProperty } from '@nestjs/swagger'; import { IsNotEmpty, IsString, IsArray, ValidateNested } from 'class-validator'; import { Type } from 'class-transformer'; import { CreateSubspaceModelDto } from './subspaces-model-dtos/create-subspace-model.dto'; +import { CreateTagModelDto } from './tag-model-dtos/create-tag-model.dto'; export class CreateSpaceModelDto { @ApiProperty({ @@ -20,4 +21,13 @@ export class CreateSpaceModelDto { @ValidateNested({ each: true }) @Type(() => CreateSubspaceModelDto) subspaceModels?: CreateSubspaceModelDto[]; + + @ApiProperty({ + description: 'List of tags associated with the space model', + type: [CreateTagModelDto], + }) + @IsArray() + @ValidateNested({ each: true }) + @Type(() => CreateTagModelDto) + tags?: CreateTagModelDto[]; } diff --git a/src/space-model/dtos/index.ts b/src/space-model/dtos/index.ts index 045d4aa..11c49af 100644 --- a/src/space-model/dtos/index.ts +++ b/src/space-model/dtos/index.ts @@ -1,6 +1,4 @@ export * from './create-space-model.dto'; -export * from './product-item-model-dtos'; -export * from './product-model-dtos'; export * from './project-param.dto'; export * from './update-space-model.dto'; export * from './space-model-param'; diff --git a/src/space-model/dtos/subspaces-model-dtos/create-subspace-model.dto.ts b/src/space-model/dtos/subspaces-model-dtos/create-subspace-model.dto.ts index a27ad3b..24eacfb 100644 --- a/src/space-model/dtos/subspaces-model-dtos/create-subspace-model.dto.ts +++ b/src/space-model/dtos/subspaces-model-dtos/create-subspace-model.dto.ts @@ -1,5 +1,7 @@ import { ApiProperty } from '@nestjs/swagger'; -import { IsNotEmpty, IsString } from 'class-validator'; +import { IsArray, IsNotEmpty, IsString, ValidateNested } from 'class-validator'; +import { CreateTagModelDto } from '../tag-model-dtos/create-tag-model.dto'; +import { Type } from 'class-transformer'; export class CreateSubspaceModelDto { @ApiProperty({ @@ -9,4 +11,13 @@ export class CreateSubspaceModelDto { @IsNotEmpty() @IsString() subspaceName: string; + + @ApiProperty({ + description: 'List of tags associated with the subspace', + type: [CreateTagModelDto], + }) + @IsArray() + @ValidateNested({ each: true }) + @Type(() => CreateTagModelDto) + tags?: CreateTagModelDto[]; } diff --git a/src/space-model/dtos/tag-model-dtos/create-tag-model.dto.ts b/src/space-model/dtos/tag-model-dtos/create-tag-model.dto.ts new file mode 100644 index 0000000..65acf2a --- /dev/null +++ b/src/space-model/dtos/tag-model-dtos/create-tag-model.dto.ts @@ -0,0 +1,20 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsNotEmpty, IsString } from 'class-validator'; + +export class CreateTagModelDto { + @ApiProperty({ + description: 'Tag associated with the space or subspace', + example: 'Temperature Control', + }) + @IsNotEmpty() + @IsString() + tag: string; + + @ApiProperty({ + description: 'ID of the product associated with the tag', + example: '123e4567-e89b-12d3-a456-426614174000', + }) + @IsNotEmpty() + @IsString() + productUuid: string; +} diff --git a/src/space-model/dtos/tag-model-dtos/index.ts b/src/space-model/dtos/tag-model-dtos/index.ts new file mode 100644 index 0000000..e69de29 From a4740e8bbdc791610a0b23d0076cbe39b72f7fcd Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Mon, 23 Dec 2024 12:25:24 +0400 Subject: [PATCH 25/45] updated create space model --- src/space-model/dtos/index.ts | 1 + src/space-model/dtos/tag-model-dtos/index.ts | 2 + .../tag-model-dtos/update-tag-model.dto.ts | 21 +++ src/space-model/services/index.ts | 1 + .../services/space-model.service.ts | 48 ++++--- .../subspace/subspace-model.service.ts | 45 +++--- src/space-model/services/tag-model.service.ts | 128 ++++++++++++++++++ src/space-model/space-model.module.ts | 9 +- 8 files changed, 216 insertions(+), 39 deletions(-) create mode 100644 src/space-model/dtos/tag-model-dtos/update-tag-model.dto.ts create mode 100644 src/space-model/services/tag-model.service.ts diff --git a/src/space-model/dtos/index.ts b/src/space-model/dtos/index.ts index 11c49af..3a04fe1 100644 --- a/src/space-model/dtos/index.ts +++ b/src/space-model/dtos/index.ts @@ -3,3 +3,4 @@ export * from './project-param.dto'; export * from './update-space-model.dto'; export * from './space-model-param'; export * from './subspaces-model-dtos'; +export * from './tag-model-dtos'; diff --git a/src/space-model/dtos/tag-model-dtos/index.ts b/src/space-model/dtos/tag-model-dtos/index.ts index e69de29..75c60f8 100644 --- a/src/space-model/dtos/tag-model-dtos/index.ts +++ b/src/space-model/dtos/tag-model-dtos/index.ts @@ -0,0 +1,2 @@ +export * from './create-tag-model.dto'; +export * from './update-tag-model.dto'; diff --git a/src/space-model/dtos/tag-model-dtos/update-tag-model.dto.ts b/src/space-model/dtos/tag-model-dtos/update-tag-model.dto.ts new file mode 100644 index 0000000..ca5612f --- /dev/null +++ b/src/space-model/dtos/tag-model-dtos/update-tag-model.dto.ts @@ -0,0 +1,21 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsNotEmpty, IsOptional, IsString, IsUUID } from 'class-validator'; + +export class UpdateTagModelDto { + @ApiProperty({ + description: 'UUID of the tag to be updated', + example: '123e4567-e89b-12d3-a456-426614174000', + }) + @IsNotEmpty() + @IsUUID() + uuid: string; + + @ApiProperty({ + description: 'Updated name of the tag', + example: 'Updated Tag Name', + required: false, + }) + @IsOptional() + @IsString() + tag?: string; +} diff --git a/src/space-model/services/index.ts b/src/space-model/services/index.ts index 9999493..20dca88 100644 --- a/src/space-model/services/index.ts +++ b/src/space-model/services/index.ts @@ -1,2 +1,3 @@ export * from './space-model.service'; export * from './subspace'; +export * from './tag-model.service'; diff --git a/src/space-model/services/space-model.service.ts b/src/space-model/services/space-model.service.ts index 92301ad..3227f9b 100644 --- a/src/space-model/services/space-model.service.ts +++ b/src/space-model/services/space-model.service.ts @@ -17,6 +17,8 @@ import { SpaceModelDto } from '@app/common/modules/space-model/dtos'; import { SpaceModelParam } from '../dtos/space-model-param'; import { ProjectService } from 'src/project/services'; import { ProjectEntity } from '@app/common/modules/project/entities'; +import { TagModelService } from './tag-model.service'; +import { BaseResponseDto } from '@app/common/dto/base.response.dto'; @Injectable() export class SpaceModelService { @@ -25,21 +27,21 @@ export class SpaceModelService { private readonly spaceModelRepository: SpaceModelRepository, private readonly projectService: ProjectService, private readonly subSpaceModelService: SubSpaceModelService, + private readonly tagModelService: TagModelService, ) {} async createSpaceModel( createSpaceModelDto: CreateSpaceModelDto, params: ProjectParam, - ) { - const { modelName, subspaceModels } = createSpaceModelDto; - const project = await this.validateProject(params.projectUuid); - + ): Promise { + const { modelName, subspaceModels, tags } = createSpaceModelDto; const queryRunner = this.dataSource.createQueryRunner(); - await queryRunner.connect(); await queryRunner.startTransaction(); try { + const project = await this.validateProject(params.projectUuid); + const isModelExist = await this.validateName( modelName, params.projectUuid, @@ -55,13 +57,25 @@ export class SpaceModelService { modelName, project, }); + const savedSpaceModel = await queryRunner.manager.save(spaceModel); - if (subspaceModels) { - await this.subSpaceModelService.createSubSpaceModels( - subspaceModels, - savedSpaceModel, + if (subspaceModels?.length) { + savedSpaceModel.subspaceModels = + await this.subSpaceModelService.createSubSpaceModels( + subspaceModels, + savedSpaceModel, + queryRunner, + tags, + ); + } + + if (tags?.length) { + savedSpaceModel.tags = await this.tagModelService.createTags( + tags, queryRunner, + savedSpaceModel, + null, ); } @@ -75,14 +89,16 @@ export class SpaceModelService { } catch (error) { await queryRunner.rollbackTransaction(); - if (error instanceof HttpException) { - throw error; - } + const errorMessage = + error instanceof HttpException + ? error.message + : 'An unexpected error occurred'; + const statusCode = + error instanceof HttpException + ? error.getStatus() + : HttpStatus.INTERNAL_SERVER_ERROR; - throw new HttpException( - error.message || `An unexpected error occurred`, - HttpStatus.INTERNAL_SERVER_ERROR, - ); + throw new HttpException(errorMessage, statusCode); } finally { await queryRunner.release(); } diff --git a/src/space-model/services/subspace/subspace-model.service.ts b/src/space-model/services/subspace/subspace-model.service.ts index fad3ee2..6a719c0 100644 --- a/src/space-model/services/subspace/subspace-model.service.ts +++ b/src/space-model/services/subspace/subspace-model.service.ts @@ -1,5 +1,6 @@ import { SpaceModelEntity, + SubspaceModelEntity, SubspaceModelRepository, } from '@app/common/modules/space-model'; import { HttpException, HttpStatus, Injectable } from '@nestjs/common'; @@ -7,6 +8,7 @@ import { CreateSubspaceModelDto, UpdateSubspaceModelDto, ModifySubspacesModelDto, + CreateTagModelDto, } from '../../dtos'; import { QueryRunner } from 'typeorm'; import { @@ -15,47 +17,53 @@ import { IUpdateSubspaceModelInterface, } from 'src/space-model/interfaces'; import { DeleteSubspaceModelDto } from 'src/space-model/dtos/subspaces-model-dtos'; +import { TagModelService } from '../tag-model.service'; @Injectable() export class SubSpaceModelService { constructor( private readonly subspaceModelRepository: SubspaceModelRepository, + private readonly tagModelService: TagModelService, ) {} - async createSubSpaceModels( subSpaceModelDtos: CreateSubspaceModelDto[], spaceModel: SpaceModelEntity, queryRunner: QueryRunner, - ) { + otherTags: CreateTagModelDto[], + ): Promise { this.validateInputDtos(subSpaceModelDtos); try { const subspaces = subSpaceModelDtos.map((subspaceDto) => queryRunner.manager.create(this.subspaceModelRepository.target, { subspaceName: subspaceDto.subspaceName, - spaceModel: spaceModel, + spaceModel, }), ); - const newSubspaces = await queryRunner.manager.save(subspaces); + const savedSubspaces = await queryRunner.manager.save(subspaces); - const addedSubspaces = await Promise.all( + await Promise.all( subSpaceModelDtos.map(async (dto, index) => { - const subspaceModel = newSubspaces[index]; + const subspace = savedSubspaces[index]; - return { - subspaceModel, - }; + if (dto.tags && dto.tags.length > 0) { + const tagModels = await this.tagModelService.createTags( + dto.tags, + queryRunner, + null, + subspace, + otherTags, + ); + subspace.tags = tagModels; + } }), ); - return addedSubspaces; - } catch (error) { - if (error instanceof HttpException) { - throw error; - } + return savedSubspaces; + } catch (error) { throw new HttpException( - error.message || `An unexpected error occurred`, + error.message || 'Failed to create subspaces.', HttpStatus.INTERNAL_SERVER_ERROR, ); } @@ -230,13 +238,6 @@ export class SubSpaceModelService { const actions = []; if (dto.add) { - actions.push( - this.createSubSpaceModels(dto.add, spaceModel, queryRunner).then( - (addedSubspaces) => { - subspaces.new = addedSubspaces; - }, - ), - ); } if (dto.update) { diff --git a/src/space-model/services/tag-model.service.ts b/src/space-model/services/tag-model.service.ts new file mode 100644 index 0000000..6a057cf --- /dev/null +++ b/src/space-model/services/tag-model.service.ts @@ -0,0 +1,128 @@ +import { Injectable, HttpException, HttpStatus } from '@nestjs/common'; +import { QueryRunner } from 'typeorm'; +import { + SpaceModelEntity, + TagModel, +} from '@app/common/modules/space-model/entities'; +import { SubspaceModelEntity } from '@app/common/modules/space-model/entities'; +import { TagModelRepository } from '@app/common/modules/space-model'; +import { CreateTagModelDto, UpdateTagModelDto } from '../dtos'; +import { ProductService } from 'src/product/services'; + +@Injectable() +export class TagModelService { + constructor( + private readonly tagModelRepository: TagModelRepository, + private readonly productService: ProductService, + ) {} + + async createTags( + tags: CreateTagModelDto[], + queryRunner: QueryRunner, + spaceModel?: SpaceModelEntity, + subspaceModel?: SubspaceModelEntity, + otherTags?: CreateTagModelDto[], + ): Promise { + let alltags: CreateTagModelDto[] = []; + if (!tags.length) { + throw new HttpException('Tags cannot be empty.', HttpStatus.BAD_REQUEST); + } + if (otherTags) { + alltags = [...tags, ...otherTags]; + } + const duplicates = this.checkForDuplicates(alltags); + if (duplicates.length > 0) { + throw new HttpException( + `Duplicate tags found for the same product: ${duplicates.join(', ')}`, + HttpStatus.BAD_REQUEST, + ); + } + const tagEntities = await Promise.all( + tags.map(async (tagDto) => { + const product = await this.productService.findOne(tagDto.productUuid); + if (!product) { + throw new HttpException( + `Product with UUID ${tagDto.productUuid} not found.`, + HttpStatus.NOT_FOUND, + ); + } + + return queryRunner.manager.create(TagModel, { + tag: tagDto.tag, + product: product.data, + spaceModel, + subspaceModel, + }); + }), + ); + + try { + return await queryRunner.manager.save(tagEntities); + } catch (error) { + throw new HttpException( + 'Failed to save tag models due to an unexpected error.', + HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + } + + async updateTags(tags: UpdateTagModelDto[], queryRunner: QueryRunner) { + try { + const updatePromises = tags.map(async (tagDto) => { + const existingTag = await this.tagModelRepository.findOne({ + where: { uuid: tagDto.uuid }, + }); + + if (!existingTag) { + throw new HttpException( + `Tag with ID ${tagDto.uuid} not found`, + HttpStatus.NOT_FOUND, + ); + } + + existingTag.tag = tagDto.tag || existingTag.tag; + + return queryRunner.manager.save(existingTag); + }); + + return await Promise.all(updatePromises); + } catch (error) { + throw new HttpException( + error.message || 'Failed to update tags', + HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + } + + async deleteTags(tagUuids: string[], queryRunner: QueryRunner) { + try { + const deletePromises = tagUuids.map((id) => + queryRunner.manager.softDelete(this.tagModelRepository.target, id), + ); + + await Promise.all(deletePromises); + return { message: 'Tags deleted successfully', tagUuids }; + } catch (error) { + throw new HttpException( + error.message || 'Failed to delete tags', + HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + } + + private checkForDuplicates(tags: CreateTagModelDto[]): string[] { + const seen = new Map(); + const duplicates: string[] = []; + + tags.forEach((tagDto) => { + const key = `${tagDto.productUuid}-${tagDto.tag}`; + if (seen.has(key)) { + duplicates.push(`${tagDto.tag} for Product: ${tagDto.productUuid}`); + } else { + seen.set(key, true); + } + }); + + return duplicates; + } +} diff --git a/src/space-model/space-model.module.ts b/src/space-model/space-model.module.ts index 2140d9c..09cc8dd 100644 --- a/src/space-model/space-model.module.ts +++ b/src/space-model/space-model.module.ts @@ -2,10 +2,15 @@ import { SpaceRepositoryModule } from '@app/common/modules/space/space.repositor import { Module } from '@nestjs/common'; import { ConfigModule } from '@nestjs/config'; import { SpaceModelController } from './controllers'; -import { SpaceModelService, SubSpaceModelService } from './services'; +import { + SpaceModelService, + SubSpaceModelService, + TagModelService, +} from './services'; import { SpaceModelRepository, SubspaceModelRepository, + TagModelRepository, } from '@app/common/modules/space-model'; import { ProjectRepository } from '@app/common/modules/project/repositiories'; import { ProductRepository } from '@app/common/modules/product/repositories'; @@ -35,6 +40,8 @@ const CommandHandlers = [PropogateSubspaceHandler]; SubspaceRepository, SubspaceProductRepository, SubspaceProductItemRepository, + TagModelService, + TagModelRepository, ], exports: [CqrsModule], }) From b0eec7c38ee3ca7848bdc568d7c28abc1db84f65 Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Mon, 23 Dec 2024 20:34:44 +0400 Subject: [PATCH 26/45] added tag action --- .../src/constants/modify-action.enum.ts | 5 + .../space-model/entities/tag-model.entity.ts | 6 + .../dtos/subspaces-model-dtos/index.ts | 1 + .../modify-subspace-model.dto.ts | 40 ++++ src/space-model/dtos/tag-model-dtos/index.ts | 1 + .../tag-model-dtos/modify-tag-model.dto.ts | 37 ++++ .../dtos/update-space-model.dto.ts | 24 ++- .../services/space-model.service.ts | 32 ++- .../subspace/subspace-model.service.ts | 84 ++++---- src/space-model/services/tag-model.service.ts | 201 ++++++++++++++---- 10 files changed, 332 insertions(+), 99 deletions(-) create mode 100644 libs/common/src/constants/modify-action.enum.ts create mode 100644 src/space-model/dtos/subspaces-model-dtos/modify-subspace-model.dto.ts create mode 100644 src/space-model/dtos/tag-model-dtos/modify-tag-model.dto.ts diff --git a/libs/common/src/constants/modify-action.enum.ts b/libs/common/src/constants/modify-action.enum.ts new file mode 100644 index 0000000..28d6f77 --- /dev/null +++ b/libs/common/src/constants/modify-action.enum.ts @@ -0,0 +1,5 @@ +export enum ModifyAction { + ADD = 'add', + UPDATE = 'update', + DELETE = 'delete', +} diff --git a/libs/common/src/modules/space-model/entities/tag-model.entity.ts b/libs/common/src/modules/space-model/entities/tag-model.entity.ts index 499ec58..2ff86d4 100644 --- a/libs/common/src/modules/space-model/entities/tag-model.entity.ts +++ b/libs/common/src/modules/space-model/entities/tag-model.entity.ts @@ -26,4 +26,10 @@ export class TagModel extends AbstractEntity { }) @JoinColumn({ name: 'subspace_id' }) subspaceModel: SubspaceModelEntity; + + @Column({ + nullable: false, + default: false, + }) + public disabled: boolean; } diff --git a/src/space-model/dtos/subspaces-model-dtos/index.ts b/src/space-model/dtos/subspaces-model-dtos/index.ts index 698a520..28b01ef 100644 --- a/src/space-model/dtos/subspaces-model-dtos/index.ts +++ b/src/space-model/dtos/subspaces-model-dtos/index.ts @@ -1,3 +1,4 @@ export * from './delete-subspace-model.dto'; export * from './create-subspace-model.dto'; export * from './update-subspace-model.dto'; +export * from './modify-subspace-model.dto'; diff --git a/src/space-model/dtos/subspaces-model-dtos/modify-subspace-model.dto.ts b/src/space-model/dtos/subspaces-model-dtos/modify-subspace-model.dto.ts new file mode 100644 index 0000000..9b5a2a5 --- /dev/null +++ b/src/space-model/dtos/subspaces-model-dtos/modify-subspace-model.dto.ts @@ -0,0 +1,40 @@ +import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; +import { Type } from 'class-transformer'; +import { IsString, IsOptional, IsArray, ValidateNested } from 'class-validator'; +import { ModifyTagModelDto } from '../tag-model-dtos'; + +export class ModifySubspaceModelDto { + @ApiProperty({ + description: 'Action to perform: add, update, or delete', + example: 'add', + }) + @IsString() + action: 'add' | 'update' | 'delete'; + + @ApiPropertyOptional({ + description: 'UUID of the subspace (required for update/delete)', + example: '123e4567-e89b-12d3-a456-426614174000', + }) + @IsOptional() + @IsString() + uuid?: string; + + @ApiPropertyOptional({ + description: 'Name of the subspace (required for add/update)', + example: 'Living Room', + }) + @IsOptional() + @IsString() + subspaceName?: string; + + @ApiPropertyOptional({ + description: + 'List of tag modifications (add/update/delete) for the subspace', + type: [ModifyTagModelDto], + }) + @IsOptional() + @IsArray() + @ValidateNested({ each: true }) + @Type(() => ModifyTagModelDto) + tags?: ModifyTagModelDto[]; +} diff --git a/src/space-model/dtos/tag-model-dtos/index.ts b/src/space-model/dtos/tag-model-dtos/index.ts index 75c60f8..a0f136d 100644 --- a/src/space-model/dtos/tag-model-dtos/index.ts +++ b/src/space-model/dtos/tag-model-dtos/index.ts @@ -1,2 +1,3 @@ export * from './create-tag-model.dto'; export * from './update-tag-model.dto'; +export * from './modify-tag-model.dto'; diff --git a/src/space-model/dtos/tag-model-dtos/modify-tag-model.dto.ts b/src/space-model/dtos/tag-model-dtos/modify-tag-model.dto.ts new file mode 100644 index 0000000..e3d390d --- /dev/null +++ b/src/space-model/dtos/tag-model-dtos/modify-tag-model.dto.ts @@ -0,0 +1,37 @@ +import { ModifyAction } from '@app/common/constants/modify-action.enum'; +import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; +import { IsString, IsOptional, IsEnum } from 'class-validator'; + +export class ModifyTagModelDto { + @ApiProperty({ + description: 'Action to perform: add, update, or delete', + example: ModifyAction.ADD, + }) + @IsEnum(ModifyAction) + action: ModifyAction; + + @ApiPropertyOptional({ + description: 'UUID of the tag (required for update/delete)', + example: '123e4567-e89b-12d3-a456-426614174000', + }) + @IsOptional() + @IsString() + uuid?: string; + + @ApiPropertyOptional({ + description: 'Name of the tag (required for add/update)', + example: 'Temperature Sensor', + }) + @IsOptional() + @IsString() + tag?: string; + + @ApiPropertyOptional({ + description: + 'UUID of the product associated with the tag (required for add)', + example: 'c789a91e-549a-4753-9006-02f89e8170e0', + }) + @IsOptional() + @IsString() + productUuid?: string; +} diff --git a/src/space-model/dtos/update-space-model.dto.ts b/src/space-model/dtos/update-space-model.dto.ts index 7a7051b..d1110ea 100644 --- a/src/space-model/dtos/update-space-model.dto.ts +++ b/src/space-model/dtos/update-space-model.dto.ts @@ -1,11 +1,13 @@ -import { ApiProperty } from '@nestjs/swagger'; +import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; import { IsArray, IsOptional, IsString, ValidateNested } from 'class-validator'; import { CreateSubspaceModelDto } from './subspaces-model-dtos/create-subspace-model.dto'; import { Type } from 'class-transformer'; import { DeleteSubspaceModelDto, + ModifySubspaceModelDto, UpdateSubspaceModelDto, } from './subspaces-model-dtos'; +import { ModifyTagModelDto } from './tag-model-dtos'; export class ModifySubspacesModelDto { @ApiProperty({ @@ -51,6 +53,24 @@ export class UpdateSpaceModelDto { @IsString() modelName?: string; + @ApiPropertyOptional({ + description: 'List of subspace modifications (add/update/delete)', + type: [ModifySubspaceModelDto], + }) @IsOptional() - subspaceModels?: ModifySubspacesModelDto; + @IsArray() + @ValidateNested({ each: true }) + @Type(() => ModifySubspaceModelDto) + subspaceModels?: ModifySubspaceModelDto[]; + + @ApiPropertyOptional({ + description: + 'List of tag modifications (add/update/delete) for the space model', + type: [ModifyTagModelDto], + }) + @IsOptional() + @IsArray() + @ValidateNested({ each: true }) + @Type(() => ModifyTagModelDto) + tags?: ModifyTagModelDto[]; } diff --git a/src/space-model/services/space-model.service.ts b/src/space-model/services/space-model.service.ts index 3227f9b..8e08ae3 100644 --- a/src/space-model/services/space-model.service.ts +++ b/src/space-model/services/space-model.service.ts @@ -42,16 +42,7 @@ export class SpaceModelService { try { const project = await this.validateProject(params.projectUuid); - const isModelExist = await this.validateName( - modelName, - params.projectUuid, - ); - if (isModelExist) { - throw new HttpException( - `Model name "${modelName}" already exists in this project ${params.projectUuid}.`, - HttpStatus.CONFLICT, - ); - } + await this.validateName(modelName, params.projectUuid); const spaceModel = this.spaceModelRepository.create({ modelName, @@ -148,9 +139,11 @@ export class SpaceModelService { await queryRunner.startTransaction(); try { const { modelName } = dto; - if (modelName) spaceModel.modelName = modelName; - - await queryRunner.manager.save(spaceModel); + if (modelName) { + await this.validateName(modelName, param.projectUuid); + spaceModel.modelName = modelName; + await queryRunner.manager.save(spaceModel); + } if (dto.subspaceModels) { await this.subSpaceModelService.modifySubSpaceModels( @@ -159,7 +152,6 @@ export class SpaceModelService { queryRunner, ); } - await queryRunner.commitTransaction(); return new SuccessResponseDto({ @@ -177,11 +169,17 @@ export class SpaceModelService { } } - async validateName(modelName: string, projectUuid: string): Promise { - const isModelExist = await this.spaceModelRepository.exists({ + async validateName(modelName: string, projectUuid: string): Promise { + const isModelExist = await this.spaceModelRepository.findOne({ where: { modelName, project: { uuid: projectUuid } }, }); - return isModelExist; + + if (isModelExist) { + throw new HttpException( + `Model name ${modelName} already exists in the project with UUID ${projectUuid}.`, + HttpStatus.CONFLICT, + ); + } } async validateSpaceModel(uuid: string): Promise { diff --git a/src/space-model/services/subspace/subspace-model.service.ts b/src/space-model/services/subspace/subspace-model.service.ts index 6a719c0..4c57673 100644 --- a/src/space-model/services/subspace/subspace-model.service.ts +++ b/src/space-model/services/subspace/subspace-model.service.ts @@ -7,16 +7,17 @@ import { HttpException, HttpStatus, Injectable } from '@nestjs/common'; import { CreateSubspaceModelDto, UpdateSubspaceModelDto, - ModifySubspacesModelDto, CreateTagModelDto, } from '../../dtos'; import { QueryRunner } from 'typeorm'; import { IDeletedSubsaceModelInterface, - IModifySubspaceModelInterface, IUpdateSubspaceModelInterface, } from 'src/space-model/interfaces'; -import { DeleteSubspaceModelDto } from 'src/space-model/dtos/subspaces-model-dtos'; +import { + DeleteSubspaceModelDto, + ModifySubspaceModelDto, +} from 'src/space-model/dtos/subspaces-model-dtos'; import { TagModelService } from '../tag-model.service'; @Injectable() @@ -25,11 +26,12 @@ export class SubSpaceModelService { private readonly subspaceModelRepository: SubspaceModelRepository, private readonly tagModelService: TagModelService, ) {} + async createSubSpaceModels( subSpaceModelDtos: CreateSubspaceModelDto[], spaceModel: SpaceModelEntity, queryRunner: QueryRunner, - otherTags: CreateTagModelDto[], + otherTags?: CreateTagModelDto[], ): Promise { this.validateInputDtos(subSpaceModelDtos); @@ -62,6 +64,9 @@ export class SubSpaceModelService { return savedSubspaces; } catch (error) { + if (error instanceof HttpException) { + throw error; + } throw new HttpException( error.message || 'Failed to create subspaces.', HttpStatus.INTERNAL_SERVER_ERROR, @@ -183,7 +188,6 @@ export class SubSpaceModelService { where: { uuid: subspaceUuid, }, - relations: ['productModels', 'productModels.itemModels'], }); if (!subspace) { throw new HttpException( @@ -226,43 +230,49 @@ export class SubSpaceModelService { } async modifySubSpaceModels( - dto: ModifySubspacesModelDto, + subspaceDtos: ModifySubspaceModelDto[], spaceModel: SpaceModelEntity, queryRunner: QueryRunner, - ): Promise { - const subspaces: IModifySubspaceModelInterface = { - spaceModelUuid: spaceModel.uuid, - }; - + ) { try { - const actions = []; + for (const subspace of subspaceDtos) { + if (subspace.action === 'add') { + const createTagDtos: CreateTagModelDto[] = + subspace.tags?.map((tag) => ({ + tag: tag.tag as string, + productUuid: tag.productUuid as string, + })) || []; + await this.createSubSpaceModels( + [{ subspaceName: subspace.subspaceName, tags: createTagDtos }], + spaceModel, + queryRunner, + ); + } else if (subspace.action === 'update') { + const existingSubspace = await this.findOne(subspace.uuid); - if (dto.add) { + if (!existingSubspace) { + throw new HttpException( + `Subspace with ID ${subspace.uuid} not found.`, + HttpStatus.NOT_FOUND, + ); + } + + existingSubspace.subspaceName = + subspace.subspaceName || existingSubspace.subspaceName; + + const updatedSubspace = + await queryRunner.manager.save(existingSubspace); + + if (subspace.tags) { + await this.tagModelService.modifyTags( + subspace.tags, + queryRunner, + null, + updatedSubspace, + ); + } + } } - - if (dto.update) { - actions.push( - this.updateSubspaceModels(dto.update, spaceModel, queryRunner).then( - (updatedSubspaces) => { - subspaces.update = updatedSubspaces; - }, - ), - ); - } - - if (dto.delete) { - actions.push( - this.deleteSubspaceModels(dto.delete, queryRunner).then( - (deletedSubspaces) => { - subspaces.delete = deletedSubspaces; - }, - ), - ); - } - - await Promise.all(actions); - - return subspaces; } catch (error) { throw new HttpException( error.message || 'Failed to modify SpaceModels', diff --git a/src/space-model/services/tag-model.service.ts b/src/space-model/services/tag-model.service.ts index 6a057cf..21be316 100644 --- a/src/space-model/services/tag-model.service.ts +++ b/src/space-model/services/tag-model.service.ts @@ -6,7 +6,7 @@ import { } from '@app/common/modules/space-model/entities'; import { SubspaceModelEntity } from '@app/common/modules/space-model/entities'; import { TagModelRepository } from '@app/common/modules/space-model'; -import { CreateTagModelDto, UpdateTagModelDto } from '../dtos'; +import { CreateTagModelDto, ModifyTagModelDto } from '../dtos'; import { ProductService } from 'src/product/services'; @Injectable() @@ -21,44 +21,34 @@ export class TagModelService { queryRunner: QueryRunner, spaceModel?: SpaceModelEntity, subspaceModel?: SubspaceModelEntity, - otherTags?: CreateTagModelDto[], + additionalTags?: CreateTagModelDto[], ): Promise { - let alltags: CreateTagModelDto[] = []; if (!tags.length) { throw new HttpException('Tags cannot be empty.', HttpStatus.BAD_REQUEST); } - if (otherTags) { - alltags = [...tags, ...otherTags]; - } - const duplicates = this.checkForDuplicates(alltags); - if (duplicates.length > 0) { + + const combinedTags = additionalTags ? [...tags, ...additionalTags] : tags; + const duplicateTags = this.findDuplicateTags(combinedTags); + + if (duplicateTags.length > 0) { throw new HttpException( - `Duplicate tags found for the same product: ${duplicates.join(', ')}`, + `Duplicate tags found for the same product: ${duplicateTags.join(', ')}`, HttpStatus.BAD_REQUEST, ); } + const tagEntities = await Promise.all( - tags.map(async (tagDto) => { - const product = await this.productService.findOne(tagDto.productUuid); - if (!product) { - throw new HttpException( - `Product with UUID ${tagDto.productUuid} not found.`, - HttpStatus.NOT_FOUND, - ); - } - - return queryRunner.manager.create(TagModel, { - tag: tagDto.tag, - product: product.data, - spaceModel, - subspaceModel, - }); - }), + tags.map(async (tagDto) => + this.prepareTagEntity(tagDto, queryRunner, spaceModel, subspaceModel), + ), ); - try { return await queryRunner.manager.save(tagEntities); } catch (error) { + if (error instanceof HttpException) { + throw error; + } + throw new HttpException( 'Failed to save tag models due to an unexpected error.', HttpStatus.INTERNAL_SERVER_ERROR, @@ -66,27 +56,34 @@ export class TagModelService { } } - async updateTags(tags: UpdateTagModelDto[], queryRunner: QueryRunner) { + async updateTag( + tag: ModifyTagModelDto, + queryRunner: QueryRunner, + spaceModel?: SpaceModelEntity, + subspaceModel?: SubspaceModelEntity, + ): Promise { try { - const updatePromises = tags.map(async (tagDto) => { - const existingTag = await this.tagModelRepository.findOne({ - where: { uuid: tagDto.uuid }, - }); + const existingTag = await this.getTagByUuid(tag.uuid); - if (!existingTag) { - throw new HttpException( - `Tag with ID ${tagDto.uuid} not found`, - HttpStatus.NOT_FOUND, - ); - } + if (spaceModel) { + await this.checkTagReuse(tag.tag, existingTag.product.uuid, spaceModel); + } else { + await this.checkTagReuse( + tag.tag, + existingTag.product.uuid, + subspaceModel.spaceModel, + ); + } - existingTag.tag = tagDto.tag || existingTag.tag; + if (tag.tag) { + existingTag.tag = tag.tag; + } - return queryRunner.manager.save(existingTag); - }); - - return await Promise.all(updatePromises); + return await queryRunner.manager.save(existingTag); } catch (error) { + if (error instanceof HttpException) { + throw error; + } throw new HttpException( error.message || 'Failed to update tags', HttpStatus.INTERNAL_SERVER_ERROR, @@ -103,6 +100,9 @@ export class TagModelService { await Promise.all(deletePromises); return { message: 'Tags deleted successfully', tagUuids }; } catch (error) { + if (error instanceof HttpException) { + throw error; + } throw new HttpException( error.message || 'Failed to delete tags', HttpStatus.INTERNAL_SERVER_ERROR, @@ -110,7 +110,7 @@ export class TagModelService { } } - private checkForDuplicates(tags: CreateTagModelDto[]): string[] { + private findDuplicateTags(tags: CreateTagModelDto[]): string[] { const seen = new Map(); const duplicates: string[] = []; @@ -125,4 +125,119 @@ export class TagModelService { return duplicates; } + + async modifyTags( + tags: ModifyTagModelDto[], + queryRunner: QueryRunner, + spaceModel?: SpaceModelEntity, + subspaceModel?: SubspaceModelEntity, + ): Promise { + try { + for (const tag of tags) { + if (tag.action === 'add') { + const createTagDto: CreateTagModelDto = { + tag: tag.tag as string, + productUuid: tag.productUuid as string, + }; + + await this.createTags( + [createTagDto], + queryRunner, + spaceModel, + subspaceModel, + ); + } else if (tag.action === 'update') { + await this.updateTag(tag, queryRunner, spaceModel, subspaceModel); + } else if (tag.action === 'delete') { + await queryRunner.manager.update( + this.tagModelRepository.target, + { uuid: tag.uuid }, + { disabled: true }, + ); + } else { + throw new HttpException( + `Invalid action "${tag.action}" provided.`, + HttpStatus.BAD_REQUEST, + ); + } + } + } catch (error) { + if (error instanceof HttpException) { + throw error; + } + + throw new HttpException( + `An error occurred while modifying tags: ${error.message}`, + HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + } + + private async checkTagReuse( + tag: string, + productUuid: string, + spaceModel: SpaceModelEntity, + ): Promise { + const isTagInSpaceModel = await this.tagModelRepository.exists({ + where: { tag, spaceModel, product: { uuid: productUuid } }, + }); + const isTagInSubspaceModel = await this.tagModelRepository.exists({ + where: { + tag, + subspaceModel: { spaceModel }, + product: { uuid: productUuid }, + }, + }); + + if (isTagInSpaceModel || isTagInSubspaceModel) { + throw new HttpException(`Tag can't be reused`, HttpStatus.CONFLICT); + } + } + + private async prepareTagEntity( + tagDto: CreateTagModelDto, + queryRunner: QueryRunner, + spaceModel?: SpaceModelEntity, + subspaceModel?: SubspaceModelEntity, + ): Promise { + const product = await this.productService.findOne(tagDto.productUuid); + + if (!product) { + throw new HttpException( + `Product with UUID ${tagDto.productUuid} not found.`, + HttpStatus.NOT_FOUND, + ); + } + + if (spaceModel) { + await this.checkTagReuse(tagDto.tag, tagDto.productUuid, spaceModel); + } else { + await this.checkTagReuse( + tagDto.tag, + tagDto.productUuid, + subspaceModel.spaceModel, + ); + } + + return queryRunner.manager.create(TagModel, { + tag: tagDto.tag, + product: product.data, + spaceModel, + subspaceModel, + }); + } + + private async getTagByUuid(uuid: string): Promise { + const tag = await this.tagModelRepository.findOne({ + where: { uuid }, + relations: ['product'], + }); + if (!tag) { + throw new HttpException( + `Tag with ID ${uuid} not found.`, + HttpStatus.NOT_FOUND, + ); + } + return tag; + } } From 41c86b47eb9ef9e3795adcd520e4294c51652d0e Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Mon, 23 Dec 2024 20:49:48 +0400 Subject: [PATCH 27/45] space model update --- .../modify-subspace-model.dto.ts | 15 +- .../services/space-model.service.ts | 3 +- .../subspace/subspace-model.service.ts | 410 +++++++++--------- src/space-model/services/tag-model.service.ts | 8 +- 4 files changed, 217 insertions(+), 219 deletions(-) diff --git a/src/space-model/dtos/subspaces-model-dtos/modify-subspace-model.dto.ts b/src/space-model/dtos/subspaces-model-dtos/modify-subspace-model.dto.ts index 9b5a2a5..cbf021c 100644 --- a/src/space-model/dtos/subspaces-model-dtos/modify-subspace-model.dto.ts +++ b/src/space-model/dtos/subspaces-model-dtos/modify-subspace-model.dto.ts @@ -1,15 +1,22 @@ import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; import { Type } from 'class-transformer'; -import { IsString, IsOptional, IsArray, ValidateNested } from 'class-validator'; +import { + IsString, + IsOptional, + IsArray, + ValidateNested, + IsEnum, +} from 'class-validator'; import { ModifyTagModelDto } from '../tag-model-dtos'; +import { ModifyAction } from '@app/common/constants/modify-action.enum'; export class ModifySubspaceModelDto { @ApiProperty({ description: 'Action to perform: add, update, or delete', - example: 'add', + example: ModifyAction.ADD, }) - @IsString() - action: 'add' | 'update' | 'delete'; + @IsEnum(ModifyAction) + action: ModifyAction; @ApiPropertyOptional({ description: 'UUID of the subspace (required for update/delete)', diff --git a/src/space-model/services/space-model.service.ts b/src/space-model/services/space-model.service.ts index 8e08ae3..23e5d51 100644 --- a/src/space-model/services/space-model.service.ts +++ b/src/space-model/services/space-model.service.ts @@ -171,7 +171,7 @@ export class SpaceModelService { async validateName(modelName: string, projectUuid: string): Promise { const isModelExist = await this.spaceModelRepository.findOne({ - where: { modelName, project: { uuid: projectUuid } }, + where: { modelName, project: { uuid: projectUuid }, disabled: false }, }); if (isModelExist) { @@ -186,6 +186,7 @@ export class SpaceModelService { const spaceModel = await this.spaceModelRepository.findOne({ where: { uuid, + disabled: true, }, }); if (!spaceModel) { diff --git a/src/space-model/services/subspace/subspace-model.service.ts b/src/space-model/services/subspace/subspace-model.service.ts index 4c57673..0f59a0d 100644 --- a/src/space-model/services/subspace/subspace-model.service.ts +++ b/src/space-model/services/subspace/subspace-model.service.ts @@ -19,6 +19,7 @@ import { ModifySubspaceModelDto, } from 'src/space-model/dtos/subspaces-model-dtos'; import { TagModelService } from '../tag-model.service'; +import { ModifyAction } from '@app/common/constants/modify-action.enum'; @Injectable() export class SubSpaceModelService { @@ -35,94 +36,223 @@ export class SubSpaceModelService { ): Promise { this.validateInputDtos(subSpaceModelDtos); - try { - const subspaces = subSpaceModelDtos.map((subspaceDto) => - queryRunner.manager.create(this.subspaceModelRepository.target, { - subspaceName: subspaceDto.subspaceName, - spaceModel, - }), - ); + const subspaces = subSpaceModelDtos.map((subspaceDto) => + queryRunner.manager.create(this.subspaceModelRepository.target, { + subspaceName: subspaceDto.subspaceName, + spaceModel, + }), + ); - const savedSubspaces = await queryRunner.manager.save(subspaces); + const savedSubspaces = await queryRunner.manager.save(subspaces); - await Promise.all( - subSpaceModelDtos.map(async (dto, index) => { - const subspace = savedSubspaces[index]; + await Promise.all( + subSpaceModelDtos.map(async (dto, index) => { + const subspace = savedSubspaces[index]; + if (dto.tags?.length) { + subspace.tags = await this.tagModelService.createTags( + dto.tags, + queryRunner, + null, + subspace, + otherTags, + ); + } + }), + ); - if (dto.tags && dto.tags.length > 0) { - const tagModels = await this.tagModelService.createTags( - dto.tags, - queryRunner, - null, - subspace, - otherTags, - ); - subspace.tags = tagModels; - } - }), - ); - - return savedSubspaces; - } catch (error) { - if (error instanceof HttpException) { - throw error; - } - throw new HttpException( - error.message || 'Failed to create subspaces.', - HttpStatus.INTERNAL_SERVER_ERROR, - ); - } + return savedSubspaces; } async updateSubspaceModels( updateDtos: UpdateSubspaceModelDto[], - spaceModel: SpaceModelEntity, queryRunner: QueryRunner, ): Promise { - try { - const updateResults: IUpdateSubspaceModelInterface[] = []; + const updateResults: IUpdateSubspaceModelInterface[] = []; - const updatePromises = updateDtos.map(async (dto) => { - try { - const subspaceModel = await this.findOne(dto.subspaceUuid); + for (const dto of updateDtos) { + await this.findOne(dto.subspaceUuid); + const updateResult: IUpdateSubspaceModelInterface = { + uuid: dto.subspaceUuid, + }; - if (!subspaceModel) { - throw new HttpException( - `Subspace model with UUID ${dto.subspaceUuid} not found.`, - HttpStatus.NOT_FOUND, - ); - } + if (dto.subspaceName) { + await this.updateSubspaceName( + dto.subspaceUuid, + dto.subspaceName, + queryRunner, + ); + updateResult.subspaceName = dto.subspaceName; + } - const updateResult: IUpdateSubspaceModelInterface = { - uuid: dto.subspaceUuid, - }; + updateResults.push(updateResult); + } - if (dto.subspaceName) { - await this.updateSubspaceName( - dto.subspaceUuid, - dto.subspaceName, - queryRunner, - ); - subspaceModel.subspaceName = dto.subspaceName; - updateResult.subspaceName = dto.subspaceName; - } + return updateResults; + } - updateResults.push(updateResult); - } catch (error) { + async deleteSubspaceModels( + deleteDtos: DeleteSubspaceModelDto[], + queryRunner: QueryRunner, + ): Promise { + const deleteResults: IDeletedSubsaceModelInterface[] = []; + + for (const dto of deleteDtos) { + const subspaceModel = await this.findOne(dto.subspaceUuid); + + await queryRunner.manager.update( + this.subspaceModelRepository.target, + { uuid: dto.subspaceUuid }, + { disabled: true }, + ); + + if (subspaceModel.tags?.length) { + const modifyTagDtos = subspaceModel.tags.map((tag) => ({ + uuid: tag.uuid, + action: ModifyAction.DELETE, + })); + await this.tagModelService.modifyTags( + modifyTagDtos, + queryRunner, + null, + subspaceModel, + ); + } + + deleteResults.push({ uuid: dto.subspaceUuid }); + } + + return deleteResults; + } + + async modifySubSpaceModels( + subspaceDtos: ModifySubspaceModelDto[], + spaceModel: SpaceModelEntity, + queryRunner: QueryRunner, + ): Promise { + for (const subspace of subspaceDtos) { + switch (subspace.action) { + case ModifyAction.ADD: + await this.handleAddAction(subspace, spaceModel, queryRunner); + break; + case ModifyAction.UPDATE: + await this.handleUpdateAction(subspace, queryRunner); + break; + case ModifyAction.DELETE: + await this.handleDeleteAction(subspace, queryRunner); + break; + default: throw new HttpException( - `Failed to update subspace model with UUID ${dto.subspaceUuid}: ${error.message}`, - HttpStatus.INTERNAL_SERVER_ERROR, + `Invalid action "${subspace.action}".`, + HttpStatus.BAD_REQUEST, ); - } - }); + } + } + } - await Promise.all(updatePromises); + private async handleAddAction( + subspace: ModifySubspaceModelDto, + spaceModel: SpaceModelEntity, + queryRunner: QueryRunner, + ): Promise { + const createTagDtos: CreateTagModelDto[] = + subspace.tags?.map((tag) => ({ + tag: tag.tag as string, + productUuid: tag.productUuid as string, + })) || []; + await this.createSubSpaceModels( + [{ subspaceName: subspace.subspaceName, tags: createTagDtos }], + spaceModel, + queryRunner, + ); + } - return updateResults; - } catch (error) { + private async handleUpdateAction( + subspace: ModifySubspaceModelDto, + queryRunner: QueryRunner, + ): Promise { + const existingSubspace = await this.findOne(subspace.uuid); + + if (subspace.subspaceName) { + existingSubspace.subspaceName = subspace.subspaceName; + await queryRunner.manager.save(existingSubspace); + } + + if (subspace.tags?.length) { + await this.tagModelService.modifyTags( + subspace.tags, + queryRunner, + null, + existingSubspace, + ); + } + } + + private async handleDeleteAction( + subspace: ModifySubspaceModelDto, + queryRunner: QueryRunner, + ): Promise { + const subspaceModel = await this.findOne(subspace.uuid); + + await queryRunner.manager.update( + this.subspaceModelRepository.target, + { uuid: subspace.uuid }, + { disabled: true }, + ); + + if (subspaceModel.tags?.length) { + const modifyTagDtos = subspaceModel.tags.map((tag) => ({ + uuid: tag.uuid, + action: ModifyAction.DELETE, + })); + await this.tagModelService.modifyTags( + modifyTagDtos, + queryRunner, + null, + subspaceModel, + ); + } + } + + private async findOne(subspaceUuid: string): Promise { + const subspace = await this.subspaceModelRepository.findOne({ + where: { uuid: subspaceUuid }, + relations: ['tags'], + }); + if (!subspace) { throw new HttpException( - error.message || 'Failed to update subspace models.', - HttpStatus.INTERNAL_SERVER_ERROR, + `SubspaceModel with UUID ${subspaceUuid} not found.`, + HttpStatus.NOT_FOUND, + ); + } + return subspace; + } + + private validateInputDtos(subSpaceModelDtos: CreateSubspaceModelDto[]): void { + if (subSpaceModelDtos.length === 0) { + throw new HttpException( + 'Subspace models cannot be empty.', + HttpStatus.BAD_REQUEST, + ); + } + this.validateName(subSpaceModelDtos.map((dto) => dto.subspaceName)); + } + + private validateName(names: string[]): void { + const seenNames = new Set(); + const duplicateNames = new Set(); + + for (const name of names) { + if (seenNames.has(name)) { + duplicateNames.add(name); + } else { + seenNames.add(name); + } + } + + if (duplicateNames.size > 0) { + throw new HttpException( + `Duplicate subspace names found: ${[...duplicateNames].join(', ')}`, + HttpStatus.CONFLICT, ); } } @@ -138,146 +268,4 @@ export class SubSpaceModelService { { subspaceName }, ); } - - async deleteSubspaceModels( - deleteDtos: DeleteSubspaceModelDto[], - queryRunner: QueryRunner, - ): Promise { - try { - const deleteResults: IDeletedSubsaceModelInterface[] = []; - - const deletePromises = deleteDtos.map(async (dto) => { - try { - const subspaceModel = await this.findOne(dto.subspaceUuid); - - if (!subspaceModel) { - throw new HttpException( - `Subspace model with UUID ${dto.subspaceUuid} not found.`, - HttpStatus.NOT_FOUND, - ); - } - - await queryRunner.manager.update( - this.subspaceModelRepository.target, - { uuid: dto.subspaceUuid }, - { disabled: true }, - ); - - deleteResults.push({ uuid: dto.subspaceUuid }); - } catch (error) { - throw new HttpException( - `Failed to delete subspace model with UUID ${dto.subspaceUuid}: ${error.message}`, - HttpStatus.INTERNAL_SERVER_ERROR, - ); - } - }); - - await Promise.all(deletePromises); - - return deleteResults; - } catch (error) { - throw new HttpException( - 'Bulk delete operation failed. Please try again.', - HttpStatus.INTERNAL_SERVER_ERROR, - ); - } - } - - async findOne(subspaceUuid: string) { - const subspace = await this.subspaceModelRepository.findOne({ - where: { - uuid: subspaceUuid, - }, - }); - if (!subspace) { - throw new HttpException( - `SubspaceModel with ${subspaceUuid} not found`, - HttpStatus.NOT_FOUND, - ); - } - return subspace; - } - - private validateInputDtos(subSpaceModelDtos: CreateSubspaceModelDto[]) { - if (subSpaceModelDtos.length === 0) { - throw new HttpException( - 'Subspace models cannot be empty.', - HttpStatus.BAD_REQUEST, - ); - } - const incomingNames = subSpaceModelDtos.map((dto) => dto.subspaceName); - this.validateName(incomingNames); - } - - private validateName(names: string[]) { - const seenNames = new Set(); - const duplicateNames = new Set(); - - for (const name of names) { - if (seenNames.has(name)) { - duplicateNames.add(name); - } else { - seenNames.add(name); - } - } - - if (duplicateNames.size > 0) { - throw new HttpException( - `Duplicate subspace names found in request: ${[...duplicateNames].join(', ')}`, - HttpStatus.CONFLICT, - ); - } - } - - async modifySubSpaceModels( - subspaceDtos: ModifySubspaceModelDto[], - spaceModel: SpaceModelEntity, - queryRunner: QueryRunner, - ) { - try { - for (const subspace of subspaceDtos) { - if (subspace.action === 'add') { - const createTagDtos: CreateTagModelDto[] = - subspace.tags?.map((tag) => ({ - tag: tag.tag as string, - productUuid: tag.productUuid as string, - })) || []; - await this.createSubSpaceModels( - [{ subspaceName: subspace.subspaceName, tags: createTagDtos }], - spaceModel, - queryRunner, - ); - } else if (subspace.action === 'update') { - const existingSubspace = await this.findOne(subspace.uuid); - - if (!existingSubspace) { - throw new HttpException( - `Subspace with ID ${subspace.uuid} not found.`, - HttpStatus.NOT_FOUND, - ); - } - - existingSubspace.subspaceName = - subspace.subspaceName || existingSubspace.subspaceName; - - const updatedSubspace = - await queryRunner.manager.save(existingSubspace); - - if (subspace.tags) { - await this.tagModelService.modifyTags( - subspace.tags, - queryRunner, - null, - updatedSubspace, - ); - } - } - } - } catch (error) { - throw new HttpException( - error.message || 'Failed to modify SpaceModels', - HttpStatus.INTERNAL_SERVER_ERROR, - ); - } - } } diff --git a/src/space-model/services/tag-model.service.ts b/src/space-model/services/tag-model.service.ts index 21be316..500eed1 100644 --- a/src/space-model/services/tag-model.service.ts +++ b/src/space-model/services/tag-model.service.ts @@ -8,6 +8,7 @@ import { SubspaceModelEntity } from '@app/common/modules/space-model/entities'; import { TagModelRepository } from '@app/common/modules/space-model'; import { CreateTagModelDto, ModifyTagModelDto } from '../dtos'; import { ProductService } from 'src/product/services'; +import { ModifyAction } from '@app/common/constants/modify-action.enum'; @Injectable() export class TagModelService { @@ -133,8 +134,9 @@ export class TagModelService { subspaceModel?: SubspaceModelEntity, ): Promise { try { + console.log(tags); for (const tag of tags) { - if (tag.action === 'add') { + if (tag.action === ModifyAction.ADD) { const createTagDto: CreateTagModelDto = { tag: tag.tag as string, productUuid: tag.productUuid as string, @@ -146,9 +148,9 @@ export class TagModelService { spaceModel, subspaceModel, ); - } else if (tag.action === 'update') { + } else if (tag.action === ModifyAction.UPDATE) { await this.updateTag(tag, queryRunner, spaceModel, subspaceModel); - } else if (tag.action === 'delete') { + } else if (tag.action === ModifyAction.DELETE) { await queryRunner.manager.update( this.tagModelRepository.target, { uuid: tag.uuid }, From fef44b3c4fc95f17cfa874f66ed8bcdf3b541d46 Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Mon, 23 Dec 2024 22:13:24 +0400 Subject: [PATCH 28/45] added space model delete --- libs/common/src/constants/controller-route.ts | 4 ++ .../src/constants/permissions-mapping.ts | 3 +- libs/common/src/constants/role-permissions.ts | 6 ++- .../controllers/space-model.controller.ts | 16 +++++- .../services/space-model.service.ts | 52 +++++++++++++++++++ 5 files changed, 77 insertions(+), 4 deletions(-) diff --git a/libs/common/src/constants/controller-route.ts b/libs/common/src/constants/controller-route.ts index aace4d6..6a9b52d 100644 --- a/libs/common/src/constants/controller-route.ts +++ b/libs/common/src/constants/controller-route.ts @@ -281,6 +281,10 @@ export class ControllerRoute { public static readonly UPDATE_SPACE_MODEL_SUMMARY = 'Update Space Model'; public static readonly UPDATE_SPACE_MODEL_DESCRIPTION = 'This endpoint allows you to update a Space Model attributesas well as manage its associated Subspaces and Device'; + + public static readonly DELETE_SPACE_MODEL_SUMMARY = 'Delete Space Model'; + public static readonly DELETE_SPACE_MODEL_DESCRIPTION = + 'This endpoint allows you to delete a specified Space Model within a project. Deleting a Space Model disables the model and all its associated subspaces and tags, ensuring they are no longer active but remain in the system for auditing.'; }; }; diff --git a/libs/common/src/constants/permissions-mapping.ts b/libs/common/src/constants/permissions-mapping.ts index e940933..09a4914 100644 --- a/libs/common/src/constants/permissions-mapping.ts +++ b/libs/common/src/constants/permissions-mapping.ts @@ -12,7 +12,8 @@ export const PermissionMapping = { 'ADD', 'UPDATE', 'DELETE', - 'MODULE_ADD', + 'MODEL_ADD', + 'MODEL_DELETE', 'MODEL_VIEW', 'ASSIGN_USER_TO_SPACE', 'DELETE_USER_FROM_SPACE', diff --git a/libs/common/src/constants/role-permissions.ts b/libs/common/src/constants/role-permissions.ts index a7163c3..c457c3c 100644 --- a/libs/common/src/constants/role-permissions.ts +++ b/libs/common/src/constants/role-permissions.ts @@ -16,9 +16,10 @@ export const RolePermissions = { 'SPACE_ADD', 'SPACE_UPDATE', 'SPACE_DELETE', - 'SPACE_MODULE_ADD', + 'SPACE_MODEL_ADD', 'SPACE_MODEL_VIEW', 'SPACE_MODEL_UPDATE', + 'SPACE_MODEL_DELETE', 'ASSIGN_USER_TO_SPACE', 'DELETE_USER_FROM_SPACE', 'SUBSPACE_VIEW', @@ -61,9 +62,10 @@ export const RolePermissions = { 'SPACE_ADD', 'SPACE_UPDATE', 'SPACE_DELETE', - 'SPACE_MODULE_ADD', + 'SPACE_MODEL_ADD', 'SPACE_MODEL_VIEW', 'SPACE_MODEL_UPDATE', + 'SPACE_MODEL_DELETE', 'ASSIGN_USER_TO_SPACE', 'DELETE_USER_FROM_SPACE', 'SUBSPACE_VIEW', diff --git a/src/space-model/controllers/space-model.controller.ts b/src/space-model/controllers/space-model.controller.ts index ba47ae9..5708e3f 100644 --- a/src/space-model/controllers/space-model.controller.ts +++ b/src/space-model/controllers/space-model.controller.ts @@ -2,6 +2,7 @@ import { ControllerRoute } from '@app/common/constants/controller-route'; import { Body, Controller, + Delete, Get, Param, Post, @@ -32,7 +33,7 @@ export class SpaceModelController { @ApiBearerAuth() @UseGuards(PermissionsGuard) - @Permissions('SPACE_MODULE_ADD') + @Permissions('SPACE_MODEL_ADD') @ApiOperation({ summary: ControllerRoute.SPACE_MODEL.ACTIONS.CREATE_SPACE_MODEL_SUMMARY, description: @@ -80,4 +81,17 @@ export class SpaceModelController { ): Promise { return await this.spaceModelService.update(dto, param); } + + @ApiBearerAuth() + @UseGuards(PermissionsGuard) + @Permissions('SPACE_MODEL_DELETE') + @ApiOperation({ + summary: ControllerRoute.SPACE_MODEL.ACTIONS.DELETE_SPACE_MODEL_SUMMARY, + description: + ControllerRoute.SPACE_MODEL.ACTIONS.DELETE_SPACE_MODEL_DESCRIPTION, + }) + @Delete(':spaceModelUuid') + async delete(@Param() param: SpaceModelParam): Promise { + return await this.spaceModelService.deleteSpaceModel(param); + } } diff --git a/src/space-model/services/space-model.service.ts b/src/space-model/services/space-model.service.ts index 23e5d51..0f62a0f 100644 --- a/src/space-model/services/space-model.service.ts +++ b/src/space-model/services/space-model.service.ts @@ -169,6 +169,58 @@ export class SpaceModelService { } } + async deleteSpaceModel(param: SpaceModelParam): Promise { + const queryRunner = this.dataSource.createQueryRunner(); + await queryRunner.connect(); + await queryRunner.startTransaction(); + + try { + await this.validateProject(param.projectUuid); + const spaceModel = await this.validateSpaceModel(param.spaceModelUuid); + + if (spaceModel.subspaceModels?.length) { + const deleteSubspaceDtos = spaceModel.subspaceModels.map( + (subspace) => ({ + subspaceUuid: subspace.uuid, + }), + ); + + await this.subSpaceModelService.deleteSubspaceModels( + deleteSubspaceDtos, + queryRunner, + ); + } + + await queryRunner.manager.update( + this.spaceModelRepository.target, + { uuid: param.spaceModelUuid }, + { disabled: true }, + ); + + await queryRunner.commitTransaction(); + + return new SuccessResponseDto({ + message: `SpaceModel with UUID ${param.spaceModelUuid} deleted successfully.`, + statusCode: HttpStatus.OK, + }); + } catch (error) { + await queryRunner.rollbackTransaction(); + + const errorMessage = + error instanceof HttpException + ? error.message + : 'An unexpected error occurred while deleting the SpaceModel'; + const statusCode = + error instanceof HttpException + ? error.getStatus() + : HttpStatus.INTERNAL_SERVER_ERROR; + + throw new HttpException(errorMessage, statusCode); + } finally { + await queryRunner.release(); + } + } + async validateName(modelName: string, projectUuid: string): Promise { const isModelExist = await this.spaceModelRepository.findOne({ where: { modelName, project: { uuid: projectUuid }, disabled: false }, From 97b53bf41755004a1cbee615b83c82b208a4c7de Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Tue, 24 Dec 2024 08:12:56 +0400 Subject: [PATCH 29/45] space cleanup --- libs/common/src/common.module.ts | 3 +- libs/common/src/database/database.module.ts | 9 - .../modules/device/entities/device.entity.ts | 12 +- .../product/entities/product.entity.ts | 4 - libs/common/src/modules/space/dtos/index.ts | 2 - .../space/dtos/space-product-item.dto.ts | 15 -- .../modules/space/dtos/space-product.dto.ts | 19 -- .../src/modules/space/entities/index.ts | 2 - .../entities/space-product-item.entity.ts | 29 --- .../space/entities/space-product.entity.ts | 38 ---- .../modules/space/entities/space.entity.ts | 4 - .../modules/space/entities/subspace/index.ts | 2 - .../subspace/subspace-product-item.entity.ts | 32 ---- .../subspace/subspace-product.entity.ts | 38 ---- .../entities/subspace/subspace.entity.ts | 10 - .../space/repositories/space.repository.ts | 20 +- .../space/repositories/subspace.repository.ts | 20 +- .../modules/space/space.repository.module.ts | 10 +- src/space-model/space-model.module.ts | 8 +- src/space/dtos/add.space.dto.ts | 40 ---- src/space/dtos/subspace/add.subspace.dto.ts | 21 +-- src/space/services/index.ts | 2 - .../services/space-product-items/index.ts | 1 - .../space-product-items.service.ts | 56 ------ src/space/services/space-products/index.ts | 1 - .../space-products/space-products.service.ts | 176 ------------------ src/space/services/space.service.ts | 16 +- src/space/services/subspace/index.ts | 2 - .../subspace/subspace-product-item.service.ts | 50 ----- .../subspace/subspace-product.service.ts | 65 ------- .../services/subspace/subspace.service.ts | 13 -- src/space/space.module.ts | 24 +-- 32 files changed, 12 insertions(+), 732 deletions(-) delete mode 100644 libs/common/src/modules/space/dtos/space-product-item.dto.ts delete mode 100644 libs/common/src/modules/space/dtos/space-product.dto.ts delete mode 100644 libs/common/src/modules/space/entities/space-product-item.entity.ts delete mode 100644 libs/common/src/modules/space/entities/space-product.entity.ts delete mode 100644 libs/common/src/modules/space/entities/subspace/subspace-product-item.entity.ts delete mode 100644 libs/common/src/modules/space/entities/subspace/subspace-product.entity.ts delete mode 100644 src/space/services/space-product-items/index.ts delete mode 100644 src/space/services/space-product-items/space-product-items.service.ts delete mode 100644 src/space/services/space-products/index.ts delete mode 100644 src/space/services/space-products/space-products.service.ts delete mode 100644 src/space/services/subspace/subspace-product-item.service.ts delete mode 100644 src/space/services/subspace/subspace-product.service.ts diff --git a/libs/common/src/common.module.ts b/libs/common/src/common.module.ts index d63ff67..91e5507 100644 --- a/libs/common/src/common.module.ts +++ b/libs/common/src/common.module.ts @@ -9,7 +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 } from './modules/space'; +import { SpaceRepository } from './modules/space'; import { SpaceModelRepository, SubspaceModelRepository, @@ -26,7 +26,6 @@ import { SubspaceRepository } from './modules/space/repositories/subspace.reposi SubspaceRepository, SubspaceModelRepository, SpaceModelRepository, - SpaceProductItemRepository, ], exports: [ CommonService, diff --git a/libs/common/src/database/database.module.ts b/libs/common/src/database/database.module.ts index 8fa7f07..7e8dbb6 100644 --- a/libs/common/src/database/database.module.ts +++ b/libs/common/src/database/database.module.ts @@ -11,10 +11,7 @@ import { PermissionTypeEntity } from '../modules/permission/entities'; import { SpaceEntity, SpaceLinkEntity, - SpaceProductItemEntity, SubspaceEntity, - SubspaceProductEntity, - SubspaceProductItemEntity, } from '../modules/space/entities'; import { UserSpaceEntity } from '../modules/user/entities'; import { DeviceUserPermissionEntity } from '../modules/device/entities'; @@ -28,7 +25,6 @@ import { CommunityEntity } from '../modules/community/entities'; import { DeviceStatusLogEntity } from '../modules/device-status-log/entities'; import { SceneEntity, SceneIconEntity } from '../modules/scene/entities'; import { SceneDeviceEntity } from '../modules/scene-device/entities'; -import { SpaceProductEntity } from '../modules/space/entities/space-product.entity'; import { ProjectEntity } from '../modules/project/entities'; import { SpaceModelEntity, @@ -65,7 +61,6 @@ import { SpaceEntity, SpaceLinkEntity, SubspaceEntity, - SpaceProductEntity, UserSpaceEntity, DeviceUserPermissionEntity, RoleTypeEntity, @@ -81,10 +76,6 @@ import { SpaceModelEntity, SubspaceModelEntity, TagModel, - SpaceProductEntity, - SpaceProductItemEntity, - SubspaceProductEntity, - SubspaceProductItemEntity, InviteUserEntity, InviteUserSpaceEntity, ], diff --git a/libs/common/src/modules/device/entities/device.entity.ts b/libs/common/src/modules/device/entities/device.entity.ts index ade7d99..9a75950 100644 --- a/libs/common/src/modules/device/entities/device.entity.ts +++ b/libs/common/src/modules/device/entities/device.entity.ts @@ -6,15 +6,10 @@ import { Unique, Index, JoinColumn, - OneToOne, } from 'typeorm'; import { AbstractEntity } from '../../abstract/entities/abstract.entity'; import { DeviceDto, DeviceUserPermissionDto } from '../dtos/device.dto'; -import { - SpaceEntity, - SpaceProductItemEntity, - SubspaceEntity, -} from '../../space/entities'; +import { SpaceEntity, SubspaceEntity } from '../../space/entities'; import { ProductEntity } from '../../product/entities'; import { UserEntity } from '../../user/entities'; import { DeviceNotificationDto } from '../dtos'; @@ -79,11 +74,6 @@ export class DeviceEntity extends AbstractEntity { @OneToMany(() => SceneDeviceEntity, (sceneDevice) => sceneDevice.device, {}) sceneDevices: SceneDeviceEntity[]; - @OneToOne(() => SpaceProductItemEntity, (tag) => tag.device, { - nullable: true, - }) - public tag?: SpaceProductItemEntity; - constructor(partial: Partial) { super(); Object.assign(this, partial); diff --git a/libs/common/src/modules/product/entities/product.entity.ts b/libs/common/src/modules/product/entities/product.entity.ts index 6de8ced..79ca211 100644 --- a/libs/common/src/modules/product/entities/product.entity.ts +++ b/libs/common/src/modules/product/entities/product.entity.ts @@ -2,7 +2,6 @@ import { Column, Entity, OneToMany } from 'typeorm'; import { ProductDto } from '../dtos'; import { AbstractEntity } from '../../abstract/entities/abstract.entity'; import { DeviceEntity } from '../../device/entities'; -import { SpaceProductEntity } from '../../space/entities/space-product.entity'; import { TagModel } from '../../space-model'; @Entity({ name: 'product' }) @@ -28,9 +27,6 @@ export class ProductEntity extends AbstractEntity { }) public prodType: string; - @OneToMany(() => SpaceProductEntity, (spaceProduct) => spaceProduct.product) - spaceProducts: SpaceProductEntity[]; - @OneToMany(() => TagModel, (tag) => tag.product) tagModels: TagModel[]; diff --git a/libs/common/src/modules/space/dtos/index.ts b/libs/common/src/modules/space/dtos/index.ts index b470336..fcc0fdd 100644 --- a/libs/common/src/modules/space/dtos/index.ts +++ b/libs/common/src/modules/space/dtos/index.ts @@ -1,4 +1,2 @@ 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/dtos/space-product-item.dto.ts b/libs/common/src/modules/space/dtos/space-product-item.dto.ts deleted file mode 100644 index 8973c1a..0000000 --- a/libs/common/src/modules/space/dtos/space-product-item.dto.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { IsString, IsNotEmpty } from 'class-validator'; - -export class SpaceProductItemDto { - @IsString() - @IsNotEmpty() - uuid: string; - - @IsString() - @IsNotEmpty() - tag: string; - - @IsString() - @IsNotEmpty() - 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 deleted file mode 100644 index 92f5f71..0000000 --- a/libs/common/src/modules/space/dtos/space-product.dto.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { ApiProperty } from '@nestjs/swagger'; -import { IsString, IsNotEmpty } from 'class-validator'; -import { SpaceProductItemDto } from './space-product-item.dto'; - -export class SpaceProductModelDto { - @IsString() - @IsNotEmpty() - uuid: string; - - @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 f07ec93..1d25b03 100644 --- a/libs/common/src/modules/space/entities/index.ts +++ b/libs/common/src/modules/space/entities/index.ts @@ -1,5 +1,3 @@ export * from './space.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 deleted file mode 100644 index 8f93264..0000000 --- a/libs/common/src/modules/space/entities/space-product-item.entity.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { Column, Entity, ManyToOne, OneToOne } from 'typeorm'; -import { SpaceProductEntity } from './space-product.entity'; -import { AbstractEntity } from '../../abstract/entities/abstract.entity'; -import { SpaceProductItemDto } from '../dtos'; -import { DeviceEntity } from '../../device/entities'; - -@Entity({ name: 'space-product-item' }) -export class SpaceProductItemEntity extends AbstractEntity { - @Column({ - nullable: false, - }) - public tag: string; - - @ManyToOne(() => SpaceProductEntity, (spaceProduct) => spaceProduct.items, { - nullable: false, - }) - public spaceProduct: SpaceProductEntity; - - @Column({ - nullable: false, - default: false, - }) - public disabled: boolean; - - @OneToOne(() => DeviceEntity, (device) => device.tag, { - nullable: true, - }) - public device?: DeviceEntity; -} diff --git a/libs/common/src/modules/space/entities/space-product.entity.ts b/libs/common/src/modules/space/entities/space-product.entity.ts deleted file mode 100644 index 4e333cd..0000000 --- a/libs/common/src/modules/space/entities/space-product.entity.ts +++ /dev/null @@ -1,38 +0,0 @@ -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 { - @ManyToOne(() => SpaceEntity, (space) => space.spaceProducts, { - nullable: false, - onDelete: 'CASCADE', - }) - @JoinColumn({ name: 'space_uuid' }) - space: SpaceEntity; - - @ManyToOne(() => ProductEntity, (product) => product.spaceProducts, { - nullable: false, - onDelete: 'CASCADE', - }) - @JoinColumn({ name: 'product_uuid' }) - product: ProductEntity; - - @Column({ - nullable: false, - default: false, - }) - public disabled: boolean; - - @OneToMany(() => SpaceProductItemEntity, (item) => item.spaceProduct, { - cascade: true, - }) - public items: SpaceProductItemEntity[]; - - 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 d6133da..9061df1 100644 --- a/libs/common/src/modules/space/entities/space.entity.ts +++ b/libs/common/src/modules/space/entities/space.entity.ts @@ -13,7 +13,6 @@ import { DeviceEntity } from '../../device/entities'; import { CommunityEntity } from '../../community/entities'; import { SubspaceEntity } from './subspace'; import { SpaceLinkEntity } from './space-link.entity'; -import { SpaceProductEntity } from './space-product.entity'; import { SceneEntity } from '../../scene/entities'; import { SpaceModelEntity } from '../../space-model'; import { InviteUserSpaceEntity } from '../../Invite-user/entities'; @@ -100,9 +99,6 @@ export class SpaceEntity extends AbstractEntity { }) public icon: string; - @OneToMany(() => SpaceProductEntity, (spaceProduct) => spaceProduct.space) - spaceProducts: SpaceProductEntity[]; - @OneToMany(() => SceneEntity, (scene) => scene.space) scenes: SceneEntity[]; diff --git a/libs/common/src/modules/space/entities/subspace/index.ts b/libs/common/src/modules/space/entities/subspace/index.ts index 471b7b1..be13961 100644 --- a/libs/common/src/modules/space/entities/subspace/index.ts +++ b/libs/common/src/modules/space/entities/subspace/index.ts @@ -1,3 +1 @@ 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 deleted file mode 100644 index eae8a75..0000000 --- a/libs/common/src/modules/space/entities/subspace/subspace-product-item.entity.ts +++ /dev/null @@ -1,32 +0,0 @@ -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; - - @Column({ - nullable: false, - default: false, - }) - public disabled: boolean; - - constructor(partial: Partial) { - 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 deleted file mode 100644 index b5a16cc..0000000 --- a/libs/common/src/modules/space/entities/subspace/subspace-product.entity.ts +++ /dev/null @@ -1,38 +0,0 @@ -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 { SubspaceProductItemEntity } from './subspace-product-item.entity'; -import { SpaceProductModelDto } from '../../dtos'; - -@Entity({ name: 'subspace-product' }) -export class SubspaceProductEntity extends AbstractEntity { - @Column({ - type: 'uuid', - default: () => 'gen_random_uuid()', - nullable: false, - }) - public uuid: string; - - @Column({ - nullable: false, - default: false, - }) - public disabled: boolean; - - @ManyToOne(() => SubspaceEntity, (subspace) => subspace.subspaceProducts, { - nullable: false, - }) - public subspace: SubspaceEntity; - - @ManyToOne(() => ProductEntity, (product) => product.spaceProducts, { - nullable: false, - }) - public product: ProductEntity; - - @OneToMany(() => SubspaceProductItemEntity, (item) => item.subspaceProduct, { - nullable: true, - }) - public items: SubspaceProductItemEntity[]; - -} 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 6ad7751..036df46 100644 --- a/libs/common/src/modules/space/entities/subspace/subspace.entity.ts +++ b/libs/common/src/modules/space/entities/subspace/subspace.entity.ts @@ -4,7 +4,6 @@ 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 { @@ -41,15 +40,6 @@ 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/repositories/space.repository.ts b/libs/common/src/modules/space/repositories/space.repository.ts index 66f96a4..a769302 100644 --- a/libs/common/src/modules/space/repositories/space.repository.ts +++ b/libs/common/src/modules/space/repositories/space.repository.ts @@ -1,11 +1,6 @@ import { DataSource, Repository } from 'typeorm'; import { Injectable } from '@nestjs/common'; -import { SpaceProductEntity } from '../entities/space-product.entity'; -import { - SpaceEntity, - SpaceLinkEntity, - SpaceProductItemEntity, -} from '../entities'; +import { SpaceEntity, SpaceLinkEntity } from '../entities'; @Injectable() export class SpaceRepository extends Repository { @@ -20,16 +15,3 @@ export class SpaceLinkRepository extends Repository { super(SpaceLinkEntity, dataSource.createEntityManager()); } } -@Injectable() -export class SpaceProductRepository extends Repository { - constructor(private dataSource: DataSource) { - super(SpaceProductEntity, dataSource.createEntityManager()); - } -} - -@Injectable() -export class SpaceProductItemRepository extends Repository { - constructor(private dataSource: DataSource) { - super(SpaceProductItemEntity, dataSource.createEntityManager()); - } -} diff --git a/libs/common/src/modules/space/repositories/subspace.repository.ts b/libs/common/src/modules/space/repositories/subspace.repository.ts index 3682c05..5897510 100644 --- a/libs/common/src/modules/space/repositories/subspace.repository.ts +++ b/libs/common/src/modules/space/repositories/subspace.repository.ts @@ -1,9 +1,5 @@ import { DataSource, Repository } from 'typeorm'; -import { - SubspaceEntity, - SubspaceProductEntity, - SubspaceProductItemEntity, -} from '../entities'; +import { SubspaceEntity } from '../entities'; import { Injectable } from '@nestjs/common'; @Injectable() @@ -12,17 +8,3 @@ export class SubspaceRepository extends Repository { 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/libs/common/src/modules/space/space.repository.module.ts b/libs/common/src/modules/space/space.repository.module.ts index b39f98d..90916c2 100644 --- a/libs/common/src/modules/space/space.repository.module.ts +++ b/libs/common/src/modules/space/space.repository.module.ts @@ -1,17 +1,11 @@ import { Module } from '@nestjs/common'; import { TypeOrmModule } from '@nestjs/typeorm'; -import { SpaceEntity, SubspaceEntity, SubspaceProductEntity } from './entities'; +import { SpaceEntity, SubspaceEntity } from './entities'; @Module({ providers: [], exports: [], controllers: [], - imports: [ - TypeOrmModule.forFeature([ - SpaceEntity, - SubspaceEntity, - SubspaceProductEntity, - ]), - ], + imports: [TypeOrmModule.forFeature([SpaceEntity, SubspaceEntity])], }) export class SpaceRepositoryModule {} diff --git a/src/space-model/space-model.module.ts b/src/space-model/space-model.module.ts index 09cc8dd..736245b 100644 --- a/src/space-model/space-model.module.ts +++ b/src/space-model/space-model.module.ts @@ -17,11 +17,7 @@ import { ProductRepository } from '@app/common/modules/product/repositories'; import { PropogateSubspaceHandler } from './handlers'; import { CqrsModule } from '@nestjs/cqrs'; import { SpaceRepository } from '@app/common/modules/space'; -import { - SubspaceProductItemRepository, - SubspaceProductRepository, - SubspaceRepository, -} from '@app/common/modules/space/repositories/subspace.repository'; +import { SubspaceRepository } from '@app/common/modules/space/repositories/subspace.repository'; const CommandHandlers = [PropogateSubspaceHandler]; @@ -38,8 +34,6 @@ const CommandHandlers = [PropogateSubspaceHandler]; SubspaceModelRepository, ProductRepository, SubspaceRepository, - SubspaceProductRepository, - SubspaceProductItemRepository, TagModelService, TagModelRepository, ], diff --git a/src/space/dtos/add.space.dto.ts b/src/space/dtos/add.space.dto.ts index 7c10c59..353acf2 100644 --- a/src/space/dtos/add.space.dto.ts +++ b/src/space/dtos/add.space.dto.ts @@ -12,35 +12,6 @@ import { } from 'class-validator'; import { AddSubspaceDto } from './subspace'; -export class CreateSpaceProductItemDto { - @ApiProperty({ - description: 'Specific name for the product item', - example: 'Light 1', - }) - @IsNotEmpty() - @IsString() - tag: string; -} - -export class ProductAssignmentDto { - @ApiProperty({ - description: 'UUID of the product to be assigned', - example: 'prod-uuid-1234', - }) - @IsNotEmpty() - productId: string; - - @ApiProperty({ - description: 'Specific names for each product item', - type: [CreateSpaceProductItemDto], - example: [{ tag: 'Light 1' }, { tag: 'Light 2' }, { tag: 'Light 3' }], - }) - @IsArray() - @ValidateNested({ each: true }) - @Type(() => CreateSpaceProductItemDto) - items: CreateSpaceProductItemDto[]; -} - export class AddSpaceDto { @ApiProperty({ description: 'Name of the space (e.g., Floor 1, Unit 101)', @@ -97,17 +68,6 @@ export class AddSpaceDto { @IsOptional() direction?: string; - @ApiProperty({ - description: 'List of products assigned to this space', - type: [ProductAssignmentDto], - required: false, - }) - @IsArray() - @ValidateNested({ each: true }) - @IsOptional() - @Type(() => ProductAssignmentDto) - products?: ProductAssignmentDto[]; - @ApiProperty({ description: 'List of subspaces included in the model', type: [AddSubspaceDto], diff --git a/src/space/dtos/subspace/add.subspace.dto.ts b/src/space/dtos/subspace/add.subspace.dto.ts index 6b5078b..a2b12e2 100644 --- a/src/space/dtos/subspace/add.subspace.dto.ts +++ b/src/space/dtos/subspace/add.subspace.dto.ts @@ -1,13 +1,5 @@ import { ApiProperty } from '@nestjs/swagger'; -import { Type } from 'class-transformer'; -import { - IsArray, - IsNotEmpty, - IsOptional, - IsString, - ValidateNested, -} from 'class-validator'; -import { ProductAssignmentDto } from '../add.space.dto'; +import { IsNotEmpty, IsString } from 'class-validator'; export class AddSubspaceDto { @ApiProperty({ @@ -17,15 +9,4 @@ 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 c67ccae..5f86e3d 100644 --- a/src/space/services/index.ts +++ b/src/space/services/index.ts @@ -4,6 +4,4 @@ export * from './space-device.service'; export * from './subspace'; 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-product-items/index.ts b/src/space/services/space-product-items/index.ts deleted file mode 100644 index fff8634..0000000 --- a/src/space/services/space-product-items/index.ts +++ /dev/null @@ -1 +0,0 @@ -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 deleted file mode 100644 index d9c141b..0000000 --- a/src/space/services/space-product-items/space-product-items.service.ts +++ /dev/null @@ -1,56 +0,0 @@ -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 { BaseProductItemService } from '../../common'; - -@Injectable() -export class SpaceProductItemService extends BaseProductItemService { - constructor( - private readonly spaceProductItemRepository: SpaceProductItemRepository, - ) { - super(); - } - - async createProductItem( - itemModelDtos: CreateSpaceProductItemDto[], - spaceProduct: SpaceProductEntity, - space: SpaceEntity, - queryRunner: QueryRunner, - ) { - if (!itemModelDtos?.length) return; - - const incomingTags = itemModelDtos.map((item) => item.tag); - - await this.validateTags(incomingTags, queryRunner, space.uuid); - - try { - const productItems = itemModelDtos.map((dto) => - queryRunner.manager.create(this.spaceProductItemRepository.target, { - tag: dto.tag, - spaceProduct, - }), - ); - - await this.saveProductItems( - productItems, - this.spaceProductItemRepository.target, - queryRunner, - ); - } 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/services/space-products/index.ts b/src/space/services/space-products/index.ts deleted file mode 100644 index d0b92d2..0000000 --- a/src/space/services/space-products/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './space-products.service'; diff --git a/src/space/services/space-products/space-products.service.ts b/src/space/services/space-products/space-products.service.ts deleted file mode 100644 index 4071428..0000000 --- a/src/space/services/space-products/space-products.service.ts +++ /dev/null @@ -1,176 +0,0 @@ -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 { In, QueryRunner } from 'typeorm'; -import { ProductAssignmentDto } from '../../dtos'; -import { SpaceProductItemService } from '../space-product-items'; -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 assignProductsToSpace( - space: SpaceEntity, - 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, - ); - - if (existingSpaceProducts) { - updatedProducts = await this.updateExistingProducts( - existingSpaceProducts, - uniqueProducts, - productEntities, - queryRunner, - ); - } - - const newProducts = await this.createNewProducts( - uniqueProducts, - productEntities, - space, - queryRunner, - ); - - return [...updatedProducts, ...newProducts]; - } catch (error) { - if (!(error instanceof HttpException)) { - throw new HttpException( - `An error occurred while assigning products to the space ${error}`, - HttpStatus.INTERNAL_SERVER_ERROR, - ); - } - throw error; - } - } - - private validateUniqueProducts( - products: ProductAssignmentDto[], - ): ProductAssignmentDto[] { - const productIds = new Set(); - const uniqueProducts = []; - - for (const product of products) { - if (productIds.has(product.productId)) { - throw new HttpException( - `Duplicate product ID found: ${product.productId}`, - HttpStatus.BAD_REQUEST, - ); - } - productIds.add(product.productId); - uniqueProducts.push(product); - } - - return uniqueProducts; - } - - private async getProductEntities( - 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); - throw new HttpException( - 'Failed to fetch product entities', - HttpStatus.INTERNAL_SERVER_ERROR, - ); - } - } - - 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 } of uniqueProducts) { - productEntities.get(productId); - const existingProduct = existingSpaceProducts.find( - (spaceProduct) => spaceProduct.product.uuid === productId, - ); - - 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); - await this.getProduct(uniqueSpaceProduct.productId); - - newProducts.push( - queryRunner.manager.create(SpaceProductEntity, { - space, - product, - }), - ); - } - 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; - } - - async getProduct(productId: string): Promise { - const product = await this.productService.findOne(productId); - return product.data; - } -} diff --git a/src/space/services/space.service.ts b/src/space/services/space.service.ts index 2cdf92a..2bd5308 100644 --- a/src/space/services/space.service.ts +++ b/src/space/services/space.service.ts @@ -17,7 +17,6 @@ import { BaseResponseDto } from '@app/common/dto/base.response.dto'; 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 { CreateSubspaceModelDto } from 'src/space-model/dtos'; import { SubSpaceService } from './subspace'; import { DataSource, Not } from 'typeorm'; @@ -30,7 +29,6 @@ export class SpaceService { private readonly dataSource: DataSource, private readonly spaceRepository: SpaceRepository, private readonly spaceLinkService: SpaceLinkService, - private readonly spaceProductService: SpaceProductService, private readonly subSpaceService: SubSpaceService, private readonly validationService: ValidationService, ) {} @@ -102,13 +100,6 @@ export class SpaceService { ); } - if (products && products.length > 0) { - await this.spaceProductService.assignProductsToSpace( - newSpace, - products, - queryRunner, - ); - } await queryRunner.commitTransaction(); return new SuccessResponseDto({ @@ -264,14 +255,9 @@ export class SpaceService { Object.assign(space, updateSpaceDto, { parent }); // Save the updated space - const updatedSpace = await queryRunner.manager.save(space); + await queryRunner.manager.save(space); if (products && products.length > 0) { - await this.spaceProductService.assignProductsToSpace( - updatedSpace, - products, - queryRunner, - ); } await queryRunner.commitTransaction(); diff --git a/src/space/services/subspace/index.ts b/src/space/services/subspace/index.ts index b51a84a..973d199 100644 --- a/src/space/services/subspace/index.ts +++ b/src/space/services/subspace/index.ts @@ -1,4 +1,2 @@ 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 deleted file mode 100644 index 3eefb2f..0000000 --- a/src/space/services/subspace/subspace-product-item.service.ts +++ /dev/null @@ -1,50 +0,0 @@ -import { Injectable, HttpException, HttpStatus } from '@nestjs/common'; -import { QueryRunner } from 'typeorm'; - -import { - SpaceEntity, - SubspaceProductEntity, - SubspaceProductItemEntity, -} from '@app/common/modules/space'; -import { SubspaceProductItemRepository } from '@app/common/modules/space/repositories/subspace.repository'; -import { CreateSpaceProductItemDto } from '../../dtos'; -import { BaseProductItemService } from '../../common'; - -@Injectable() -export class SubspaceProductItemService extends BaseProductItemService { - constructor( - private readonly productItemRepository: SubspaceProductItemRepository, - ) { - super(); - } - - async createItemFromDtos( - product: SubspaceProductEntity, - itemDto: CreateSpaceProductItemDto[], - queryRunner: QueryRunner, - space: SpaceEntity, - ) { - if (!itemDto?.length) return; - const incomingTags = itemDto.map((item) => item.tag); - await this.validateTags(incomingTags, queryRunner, space.uuid); - - try { - 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, - ); - } - } -} diff --git a/src/space/services/subspace/subspace-product.service.ts b/src/space/services/subspace/subspace-product.service.ts deleted file mode 100644 index 93ef045..0000000 --- a/src/space/services/subspace/subspace-product.service.ts +++ /dev/null @@ -1,65 +0,0 @@ -import { HttpException, HttpStatus, Injectable } from '@nestjs/common'; -import { QueryRunner } from 'typeorm'; - -import { - SpaceEntity, - SubspaceEntity, - SubspaceProductEntity, -} from '@app/common/modules/space'; -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 createFromDto( - productDtos: ProductAssignmentDto[], - subspace: SubspaceEntity, - queryRunner: QueryRunner, - space: SpaceEntity, - ): Promise { - try { - const newSpaceProducts = await Promise.all( - productDtos.map(async (dto) => { - const product = await this.getProduct(dto.productId); - return queryRunner.manager.create(SubspaceProductEntity, { - subspace, - product, - }); - }), - ); - - 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; - } -} diff --git a/src/space/services/subspace/subspace.service.ts b/src/space/services/subspace/subspace.service.ts index 08e12e2..9f553fd 100644 --- a/src/space/services/subspace/subspace.service.ts +++ b/src/space/services/subspace/subspace.service.ts @@ -19,14 +19,12 @@ import { } 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( @@ -82,17 +80,6 @@ export class SubSpaceService { 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( diff --git a/src/space/space.module.ts b/src/space/space.module.ts index 81d99e2..5d16689 100644 --- a/src/space/space.module.ts +++ b/src/space/space.module.ts @@ -12,20 +12,15 @@ import { import { SpaceDeviceService, SpaceLinkService, - SpaceProductItemService, - SpaceProductService, SpaceSceneService, SpaceService, SpaceUserService, SubspaceDeviceService, - SubspaceProductItemService, SubSpaceService, } from './services'; import { - SpaceProductRepository, SpaceRepository, SpaceLinkRepository, - SpaceProductItemRepository, } from '@app/common/modules/space/repositories'; import { CommunityRepository } from '@app/common/modules/community/repositories'; import { @@ -48,12 +43,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'; -import { - SubspaceProductItemRepository, - SubspaceProductRepository, - SubspaceRepository, -} from '@app/common/modules/space/repositories/subspace.repository'; -import { SubspaceProductService } from './services'; +import { SubspaceRepository } from '@app/common/modules/space/repositories/subspace.repository'; @Module({ imports: [ConfigModule, SpaceRepositoryModule, CommunityModule], @@ -75,9 +65,9 @@ import { SubspaceProductService } from './services'; SpaceLinkService, SubspaceDeviceService, SpaceRepository, + SubspaceRepository, DeviceRepository, CommunityRepository, - SubspaceRepository, SpaceLinkRepository, UserSpaceRepository, UserRepository, @@ -88,19 +78,11 @@ import { SubspaceProductService } from './services'; SceneRepository, DeviceService, DeviceStatusFirebaseService, - SubspaceProductItemRepository, DeviceStatusLogRepository, SceneDeviceRepository, - SpaceProductService, - SpaceProductRepository, + ProjectRepository, SpaceModelRepository, - SubspaceRepository, - SpaceProductItemService, - SpaceProductItemRepository, - SubspaceProductService, - SubspaceProductItemService, - SubspaceProductRepository, ], exports: [SpaceService], }) From 1e47fffc0add6cd001059e1f2df7c28975359edf Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Tue, 24 Dec 2024 08:43:25 +0400 Subject: [PATCH 30/45] cleaned space entities --- libs/common/src/database/database.module.ts | 2 + .../product/entities/product.entity.ts | 5 ++- .../space-model/entities/tag-model.entity.ts | 17 ++++++-- libs/common/src/modules/space/dtos/index.ts | 1 + libs/common/src/modules/space/dtos/tag.dto.ts | 21 ++++++++++ .../src/modules/space/entities/index.ts | 1 + .../modules/space/entities/space.entity.ts | 5 +++ .../entities/subspace/subspace.entity.ts | 4 ++ .../src/modules/space/entities/tag.entity.ts | 39 +++++++++++++++++++ .../space/repositories/space.repository.ts | 9 ++++- .../modules/space/space.repository.module.ts | 4 +- src/space/services/space.service.ts | 18 +++------ 12 files changed, 106 insertions(+), 20 deletions(-) create mode 100644 libs/common/src/modules/space/dtos/tag.dto.ts create mode 100644 libs/common/src/modules/space/entities/tag.entity.ts diff --git a/libs/common/src/database/database.module.ts b/libs/common/src/database/database.module.ts index 7e8dbb6..472772b 100644 --- a/libs/common/src/database/database.module.ts +++ b/libs/common/src/database/database.module.ts @@ -12,6 +12,7 @@ import { SpaceEntity, SpaceLinkEntity, SubspaceEntity, + TagEntity, } from '../modules/space/entities'; import { UserSpaceEntity } from '../modules/user/entities'; import { DeviceUserPermissionEntity } from '../modules/device/entities'; @@ -61,6 +62,7 @@ import { SpaceEntity, SpaceLinkEntity, SubspaceEntity, + TagEntity, UserSpaceEntity, DeviceUserPermissionEntity, RoleTypeEntity, diff --git a/libs/common/src/modules/product/entities/product.entity.ts b/libs/common/src/modules/product/entities/product.entity.ts index 79ca211..dd7a1e5 100644 --- a/libs/common/src/modules/product/entities/product.entity.ts +++ b/libs/common/src/modules/product/entities/product.entity.ts @@ -3,7 +3,7 @@ import { ProductDto } from '../dtos'; import { AbstractEntity } from '../../abstract/entities/abstract.entity'; import { DeviceEntity } from '../../device/entities'; import { TagModel } from '../../space-model'; - +import { TagEntity } from '../../space/entities/tag.entity'; @Entity({ name: 'product' }) export class ProductEntity extends AbstractEntity { @Column({ @@ -30,6 +30,9 @@ export class ProductEntity extends AbstractEntity { @OneToMany(() => TagModel, (tag) => tag.product) tagModels: TagModel[]; + @OneToMany(() => TagEntity, (tag) => tag.product) + tags: TagEntity[]; + @OneToMany( () => DeviceEntity, (devicesProductEntity) => devicesProductEntity.productDevice, diff --git a/libs/common/src/modules/space-model/entities/tag-model.entity.ts b/libs/common/src/modules/space-model/entities/tag-model.entity.ts index 2ff86d4..3f7805c 100644 --- a/libs/common/src/modules/space-model/entities/tag-model.entity.ts +++ b/libs/common/src/modules/space-model/entities/tag-model.entity.ts @@ -1,9 +1,17 @@ -import { Column, Entity, JoinColumn, ManyToOne, Unique } from 'typeorm'; +import { + Column, + Entity, + JoinColumn, + ManyToOne, + OneToMany, + Unique, +} from 'typeorm'; import { AbstractEntity } from '../../abstract/entities/abstract.entity'; import { TagModelDto } from '../dtos/tag-model.dto'; import { SpaceModelEntity } from './space-model.entity'; import { SubspaceModelEntity } from './subspace-model'; import { ProductEntity } from '../../product/entities'; +import { TagEntity } from '../../space/entities/tag.entity'; @Entity({ name: 'tag_model' }) @Unique(['tag', 'product', 'spaceModel', 'subspaceModel']) @@ -18,13 +26,13 @@ export class TagModel extends AbstractEntity { product: ProductEntity; @ManyToOne(() => SpaceModelEntity, (space) => space.tags, { nullable: true }) - @JoinColumn({ name: 'space_id' }) + @JoinColumn({ name: 'space_model_id' }) spaceModel: SpaceModelEntity; @ManyToOne(() => SubspaceModelEntity, (subspace) => subspace.tags, { nullable: true, }) - @JoinColumn({ name: 'subspace_id' }) + @JoinColumn({ name: 'subspace_model_id' }) subspaceModel: SubspaceModelEntity; @Column({ @@ -32,4 +40,7 @@ export class TagModel extends AbstractEntity { default: false, }) public disabled: boolean; + + @OneToMany(() => TagEntity, (tag) => tag.model) + tags: TagEntity[]; } diff --git a/libs/common/src/modules/space/dtos/index.ts b/libs/common/src/modules/space/dtos/index.ts index fcc0fdd..c511f8c 100644 --- a/libs/common/src/modules/space/dtos/index.ts +++ b/libs/common/src/modules/space/dtos/index.ts @@ -1,2 +1,3 @@ export * from './space.dto'; export * from './subspace.dto'; +export * from './tag.dto'; diff --git a/libs/common/src/modules/space/dtos/tag.dto.ts b/libs/common/src/modules/space/dtos/tag.dto.ts new file mode 100644 index 0000000..d988c62 --- /dev/null +++ b/libs/common/src/modules/space/dtos/tag.dto.ts @@ -0,0 +1,21 @@ +import { IsNotEmpty, IsString } from 'class-validator'; + +export class TagDto { + @IsString() + @IsNotEmpty() + public uuid: string; + + @IsString() + @IsNotEmpty() + public name: string; + + @IsString() + @IsNotEmpty() + public productUuid: string; + + @IsString() + spaceUuid: string; + + @IsString() + subspaceUuid: string; +} diff --git a/libs/common/src/modules/space/entities/index.ts b/libs/common/src/modules/space/entities/index.ts index 1d25b03..5a514e6 100644 --- a/libs/common/src/modules/space/entities/index.ts +++ b/libs/common/src/modules/space/entities/index.ts @@ -1,3 +1,4 @@ export * from './space.entity'; export * from './subspace'; export * from './space-link.entity'; +export * from './tag.entity'; diff --git a/libs/common/src/modules/space/entities/space.entity.ts b/libs/common/src/modules/space/entities/space.entity.ts index 9061df1..889363f 100644 --- a/libs/common/src/modules/space/entities/space.entity.ts +++ b/libs/common/src/modules/space/entities/space.entity.ts @@ -16,6 +16,7 @@ import { SpaceLinkEntity } from './space-link.entity'; import { SceneEntity } from '../../scene/entities'; import { SpaceModelEntity } from '../../space-model'; import { InviteUserSpaceEntity } from '../../Invite-user/entities'; +import { TagEntity } from './tag.entity'; @Entity({ name: 'space' }) @Unique(['invitationCode']) @@ -111,6 +112,10 @@ export class SpaceEntity extends AbstractEntity { (inviteUserSpace) => inviteUserSpace.space, ) invitedUsers: InviteUserSpaceEntity[]; + + @OneToMany(() => TagEntity, (tag) => tag.space) + tags: TagEntity[]; + constructor(partial: Partial) { super(); Object.assign(this, partial); 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 036df46..d2f86d9 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 { TagEntity } from '../tag.entity'; @Entity({ name: 'subspace' }) export class SubspaceEntity extends AbstractEntity { @@ -40,6 +41,9 @@ export class SubspaceEntity extends AbstractEntity { @JoinColumn({ name: 'subspace_model_uuid' }) subSpaceModel?: SubspaceModelEntity; + @OneToMany(() => TagEntity, (tag) => tag.subspace) + tags: TagEntity[]; + constructor(partial: Partial) { super(); Object.assign(this, partial); diff --git a/libs/common/src/modules/space/entities/tag.entity.ts b/libs/common/src/modules/space/entities/tag.entity.ts new file mode 100644 index 0000000..77e79cc --- /dev/null +++ b/libs/common/src/modules/space/entities/tag.entity.ts @@ -0,0 +1,39 @@ +import { Entity, Column, ManyToOne, JoinColumn, Unique } from 'typeorm'; +import { AbstractEntity } from '../../abstract/entities/abstract.entity'; +import { ProductEntity } from '../../product/entities'; +import { TagDto } from '../dtos'; +import { TagModel } from '../../space-model/entities/tag-model.entity'; +import { SpaceEntity } from './space.entity'; +import { SubspaceEntity } from './subspace'; + +@Entity({ name: 'tag' }) +@Unique(['tag', 'product', 'space', 'subspace']) +export class TagEntity extends AbstractEntity { + @Column({ type: 'varchar', length: 255 }) + tag: string; + + @ManyToOne(() => TagModel, (model) => model.tags, { + nullable: true, + }) + model: TagModel; + + @ManyToOne(() => ProductEntity, (product) => product.tags, { + nullable: false, + }) + product: ProductEntity; + + @ManyToOne(() => SpaceEntity, (space) => space.tags, { nullable: true }) + space: SpaceEntity; + + @ManyToOne(() => SubspaceEntity, (subspace) => subspace.tags, { + nullable: true, + }) + @JoinColumn({ name: 'subspace_id' }) + subspace: SubspaceEntity; + + @Column({ + nullable: false, + default: false, + }) + public disabled: boolean; +} diff --git a/libs/common/src/modules/space/repositories/space.repository.ts b/libs/common/src/modules/space/repositories/space.repository.ts index a769302..b2aacc0 100644 --- a/libs/common/src/modules/space/repositories/space.repository.ts +++ b/libs/common/src/modules/space/repositories/space.repository.ts @@ -1,6 +1,6 @@ import { DataSource, Repository } from 'typeorm'; import { Injectable } from '@nestjs/common'; -import { SpaceEntity, SpaceLinkEntity } from '../entities'; +import { SpaceEntity, SpaceLinkEntity, TagEntity } from '../entities'; @Injectable() export class SpaceRepository extends Repository { @@ -15,3 +15,10 @@ export class SpaceLinkRepository extends Repository { super(SpaceLinkEntity, dataSource.createEntityManager()); } } + +@Injectable() +export class TagRepository extends Repository { + constructor(private dataSource: DataSource) { + super(TagEntity, dataSource.createEntityManager()); + } +} diff --git a/libs/common/src/modules/space/space.repository.module.ts b/libs/common/src/modules/space/space.repository.module.ts index 90916c2..030c684 100644 --- a/libs/common/src/modules/space/space.repository.module.ts +++ b/libs/common/src/modules/space/space.repository.module.ts @@ -1,11 +1,11 @@ import { Module } from '@nestjs/common'; import { TypeOrmModule } from '@nestjs/typeorm'; -import { SpaceEntity, SubspaceEntity } from './entities'; +import { SpaceEntity, SubspaceEntity, TagEntity } from './entities'; @Module({ providers: [], exports: [], controllers: [], - imports: [TypeOrmModule.forFeature([SpaceEntity, SubspaceEntity])], + imports: [TypeOrmModule.forFeature([SpaceEntity, SubspaceEntity, TagEntity])], }) export class SpaceRepositoryModule {} diff --git a/src/space/services/space.service.ts b/src/space/services/space.service.ts index 2bd5308..43b8f4e 100644 --- a/src/space/services/space.service.ts +++ b/src/space/services/space.service.ts @@ -9,7 +9,6 @@ import { AddSpaceDto, CommunitySpaceParam, GetSpaceParam, - ProductAssignmentDto, UpdateSpaceDto, } from '../dtos'; import { SuccessResponseDto } from '@app/common/dto/success.response.dto'; @@ -37,8 +36,7 @@ export class SpaceService { addSpaceDto: AddSpaceDto, params: CommunitySpaceParam, ): Promise { - const { parentUuid, direction, products, spaceModelUuid, subspaces } = - addSpaceDto; + const { parentUuid, direction, spaceModelUuid, subspaces } = addSpaceDto; const { communityUuid, projectUuid } = params; if (addSpaceDto.spaceName === ORPHAN_SPACE_NAME) { @@ -58,7 +56,7 @@ export class SpaceService { projectUuid, ); - this.validateSpaceCreation(spaceModelUuid, products, subspaces); + this.validateSpaceCreation(spaceModelUuid); const parent = parentUuid ? await this.validationService.validateSpace(parentUuid) @@ -246,7 +244,7 @@ export class SpaceService { } // If a parentId is provided, check if the parent exists - const { parentUuid, products } = updateSpaceDto; + const { parentUuid } = updateSpaceDto; const parent = parentUuid ? await this.validationService.validateSpace(parentUuid) : null; @@ -257,8 +255,6 @@ export class SpaceService { // Save the updated space await queryRunner.manager.save(space); - if (products && products.length > 0) { - } await queryRunner.commitTransaction(); return new SuccessResponseDto({ @@ -370,12 +366,8 @@ export class SpaceService { return rootSpaces; } - private validateSpaceCreation( - spaceModelUuid?: string, - products?: ProductAssignmentDto[], - subSpaces?: CreateSubspaceModelDto[], - ) { - if (spaceModelUuid && (products?.length || subSpaces?.length)) { + private validateSpaceCreation(spaceModelUuid?: string) { + if (spaceModelUuid) { throw new HttpException( 'For space creation choose either space model or products and subspace', HttpStatus.CONFLICT, From cb2778dce5fb21206d0e893bc2cd12718d64dc91 Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Tue, 24 Dec 2024 11:33:58 +0400 Subject: [PATCH 31/45] update in tag service --- .../create-subspace-model.dto.ts | 2 +- .../tag-model-dtos/create-tag-model.dto.ts | 2 +- .../tag-model-dtos/modify-tag-model.dto.ts | 4 +- .../services/space-model.service.ts | 8 + .../subspace/subspace-model.service.ts | 70 ++---- src/space-model/services/tag-model.service.ts | 4 +- src/space/dtos/add.space.dto.ts | 10 + src/space/dtos/index.ts | 1 + src/space/dtos/subspace/add.subspace.dto.ts | 13 +- .../dtos/subspace/delete.subspace.dto.ts | 12 + src/space/dtos/subspace/index.ts | 3 + .../dtos/subspace/modify.subspace.dto.ts | 47 ++++ .../dtos/subspace/update.subspace.dto.ts | 16 ++ src/space/dtos/tag/create-tag-dto.ts | 20 ++ src/space/dtos/tag/index.ts | 1 + src/space/dtos/tag/modify-tag.dto.ts | 37 +++ src/space/dtos/update.space.dto.ts | 61 ++++- src/space/services/space.service.ts | 50 ++-- .../services/subspace/subspace.service.ts | 213 +++++++++++++--- src/space/services/tag/index.ts | 1 + src/space/services/tag/tag.service.ts | 237 ++++++++++++++++++ src/space/space.module.ts | 10 +- 22 files changed, 705 insertions(+), 117 deletions(-) create mode 100644 src/space/dtos/subspace/delete.subspace.dto.ts create mode 100644 src/space/dtos/subspace/modify.subspace.dto.ts create mode 100644 src/space/dtos/subspace/update.subspace.dto.ts create mode 100644 src/space/dtos/tag/create-tag-dto.ts create mode 100644 src/space/dtos/tag/index.ts create mode 100644 src/space/dtos/tag/modify-tag.dto.ts create mode 100644 src/space/services/tag/index.ts create mode 100644 src/space/services/tag/tag.service.ts diff --git a/src/space-model/dtos/subspaces-model-dtos/create-subspace-model.dto.ts b/src/space-model/dtos/subspaces-model-dtos/create-subspace-model.dto.ts index 24eacfb..1397edc 100644 --- a/src/space-model/dtos/subspaces-model-dtos/create-subspace-model.dto.ts +++ b/src/space-model/dtos/subspaces-model-dtos/create-subspace-model.dto.ts @@ -13,7 +13,7 @@ export class CreateSubspaceModelDto { subspaceName: string; @ApiProperty({ - description: 'List of tags associated with the subspace', + description: 'List of tag models associated with the subspace', type: [CreateTagModelDto], }) @IsArray() diff --git a/src/space-model/dtos/tag-model-dtos/create-tag-model.dto.ts b/src/space-model/dtos/tag-model-dtos/create-tag-model.dto.ts index 65acf2a..5f4ec66 100644 --- a/src/space-model/dtos/tag-model-dtos/create-tag-model.dto.ts +++ b/src/space-model/dtos/tag-model-dtos/create-tag-model.dto.ts @@ -3,7 +3,7 @@ import { IsNotEmpty, IsString } from 'class-validator'; export class CreateTagModelDto { @ApiProperty({ - description: 'Tag associated with the space or subspace', + description: 'Tag models associated with the space or subspace models', example: 'Temperature Control', }) @IsNotEmpty() diff --git a/src/space-model/dtos/tag-model-dtos/modify-tag-model.dto.ts b/src/space-model/dtos/tag-model-dtos/modify-tag-model.dto.ts index e3d390d..2b64fe3 100644 --- a/src/space-model/dtos/tag-model-dtos/modify-tag-model.dto.ts +++ b/src/space-model/dtos/tag-model-dtos/modify-tag-model.dto.ts @@ -11,7 +11,7 @@ export class ModifyTagModelDto { action: ModifyAction; @ApiPropertyOptional({ - description: 'UUID of the tag (required for update/delete)', + description: 'UUID of the tag model (required for update/delete)', example: '123e4567-e89b-12d3-a456-426614174000', }) @IsOptional() @@ -19,7 +19,7 @@ export class ModifyTagModelDto { uuid?: string; @ApiPropertyOptional({ - description: 'Name of the tag (required for add/update)', + description: 'Name of the tag model (required for add/update)', example: 'Temperature Sensor', }) @IsOptional() diff --git a/src/space-model/services/space-model.service.ts b/src/space-model/services/space-model.service.ts index 0f62a0f..40d9970 100644 --- a/src/space-model/services/space-model.service.ts +++ b/src/space-model/services/space-model.service.ts @@ -152,6 +152,14 @@ export class SpaceModelService { queryRunner, ); } + + if (dto.tags) { + await this.tagModelService.modifyTags( + dto.tags, + queryRunner, + spaceModel, + ); + } await queryRunner.commitTransaction(); return new SuccessResponseDto({ diff --git a/src/space-model/services/subspace/subspace-model.service.ts b/src/space-model/services/subspace/subspace-model.service.ts index 0f59a0d..507a16a 100644 --- a/src/space-model/services/subspace/subspace-model.service.ts +++ b/src/space-model/services/subspace/subspace-model.service.ts @@ -4,16 +4,9 @@ import { SubspaceModelRepository, } from '@app/common/modules/space-model'; import { HttpException, HttpStatus, Injectable } from '@nestjs/common'; -import { - CreateSubspaceModelDto, - UpdateSubspaceModelDto, - CreateTagModelDto, -} from '../../dtos'; +import { CreateSubspaceModelDto, CreateTagModelDto } from '../../dtos'; import { QueryRunner } from 'typeorm'; -import { - IDeletedSubsaceModelInterface, - IUpdateSubspaceModelInterface, -} from 'src/space-model/interfaces'; +import { IDeletedSubsaceModelInterface } from 'src/space-model/interfaces'; import { DeleteSubspaceModelDto, ModifySubspaceModelDto, @@ -63,33 +56,6 @@ export class SubSpaceModelService { return savedSubspaces; } - async updateSubspaceModels( - updateDtos: UpdateSubspaceModelDto[], - queryRunner: QueryRunner, - ): Promise { - const updateResults: IUpdateSubspaceModelInterface[] = []; - - for (const dto of updateDtos) { - await this.findOne(dto.subspaceUuid); - const updateResult: IUpdateSubspaceModelInterface = { - uuid: dto.subspaceUuid, - }; - - if (dto.subspaceName) { - await this.updateSubspaceName( - dto.subspaceUuid, - dto.subspaceName, - queryRunner, - ); - updateResult.subspaceName = dto.subspaceName; - } - - updateResults.push(updateResult); - } - - return updateResults; - } - async deleteSubspaceModels( deleteDtos: DeleteSubspaceModelDto[], queryRunner: QueryRunner, @@ -167,22 +133,23 @@ export class SubSpaceModelService { } private async handleUpdateAction( - subspace: ModifySubspaceModelDto, + modifyDto: ModifySubspaceModelDto, queryRunner: QueryRunner, ): Promise { - const existingSubspace = await this.findOne(subspace.uuid); + const subspace = await this.findOne(modifyDto.uuid); - if (subspace.subspaceName) { - existingSubspace.subspaceName = subspace.subspaceName; - await queryRunner.manager.save(existingSubspace); - } + await this.updateSubspaceName( + queryRunner, + subspace, + modifyDto.subspaceName, + ); - if (subspace.tags?.length) { + if (modifyDto.tags?.length) { await this.tagModelService.modifyTags( - subspace.tags, + modifyDto.tags, queryRunner, null, - existingSubspace, + subspace, ); } } @@ -258,14 +225,13 @@ export class SubSpaceModelService { } private async updateSubspaceName( - uuid: string, - subspaceName: string, queryRunner: QueryRunner, + subSpaceModel: SubspaceModelEntity, + subspaceName?: string, ): Promise { - await queryRunner.manager.update( - this.subspaceModelRepository.target, - { uuid }, - { subspaceName }, - ); + if (subspaceName) { + subSpaceModel.subspaceName = subspaceName; + await queryRunner.manager.save(subSpaceModel); + } } } diff --git a/src/space-model/services/tag-model.service.ts b/src/space-model/services/tag-model.service.ts index 500eed1..8207e0b 100644 --- a/src/space-model/services/tag-model.service.ts +++ b/src/space-model/services/tag-model.service.ts @@ -169,7 +169,7 @@ export class TagModelService { } throw new HttpException( - `An error occurred while modifying tags: ${error.message}`, + `An error occurred while modifying tag models: ${error.message}`, HttpStatus.INTERNAL_SERVER_ERROR, ); } @@ -236,7 +236,7 @@ export class TagModelService { }); if (!tag) { throw new HttpException( - `Tag with ID ${uuid} not found.`, + `Tag model with ID ${uuid} not found.`, HttpStatus.NOT_FOUND, ); } diff --git a/src/space/dtos/add.space.dto.ts b/src/space/dtos/add.space.dto.ts index 353acf2..e682b11 100644 --- a/src/space/dtos/add.space.dto.ts +++ b/src/space/dtos/add.space.dto.ts @@ -11,6 +11,7 @@ import { ValidateNested, } from 'class-validator'; import { AddSubspaceDto } from './subspace'; +import { CreateTagDto } from './tag'; export class AddSpaceDto { @ApiProperty({ @@ -77,6 +78,15 @@ export class AddSpaceDto { @ValidateNested({ each: true }) @Type(() => AddSubspaceDto) subspaces?: AddSubspaceDto[]; + + @ApiProperty({ + description: 'List of tags associated with the space model', + type: [CreateTagDto], + }) + @IsArray() + @ValidateNested({ each: true }) + @Type(() => CreateTagDto) + tags?: CreateTagDto[]; } export class AddUserSpaceDto { diff --git a/src/space/dtos/index.ts b/src/space/dtos/index.ts index 3c85266..506efa9 100644 --- a/src/space/dtos/index.ts +++ b/src/space/dtos/index.ts @@ -5,3 +5,4 @@ export * from './user-space.param'; export * from './subspace'; export * from './project.param.dto'; export * from './update.space.dto'; +export * from './tag'; diff --git a/src/space/dtos/subspace/add.subspace.dto.ts b/src/space/dtos/subspace/add.subspace.dto.ts index a2b12e2..98b381a 100644 --- a/src/space/dtos/subspace/add.subspace.dto.ts +++ b/src/space/dtos/subspace/add.subspace.dto.ts @@ -1,5 +1,7 @@ import { ApiProperty } from '@nestjs/swagger'; -import { IsNotEmpty, IsString } from 'class-validator'; +import { IsArray, IsNotEmpty, IsString, ValidateNested } from 'class-validator'; +import { CreateTagDto } from '../tag'; +import { Type } from 'class-transformer'; export class AddSubspaceDto { @ApiProperty({ @@ -9,4 +11,13 @@ export class AddSubspaceDto { @IsNotEmpty() @IsString() subspaceName: string; + + @ApiProperty({ + description: 'List of tags associated with the subspace', + type: [CreateTagDto], + }) + @IsArray() + @ValidateNested({ each: true }) + @Type(() => CreateTagDto) + tags?: CreateTagDto[]; } diff --git a/src/space/dtos/subspace/delete.subspace.dto.ts b/src/space/dtos/subspace/delete.subspace.dto.ts new file mode 100644 index 0000000..3329a14 --- /dev/null +++ b/src/space/dtos/subspace/delete.subspace.dto.ts @@ -0,0 +1,12 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsNotEmpty, IsString } from 'class-validator'; + +export class DeleteSubspaceDto { + @ApiProperty({ + description: 'Uuid of the subspace model need to be deleted', + example: '982fc3a3-64dc-4afb-a5b5-65ee8fef0424', + }) + @IsNotEmpty() + @IsString() + subspaceUuid: string; +} diff --git a/src/space/dtos/subspace/index.ts b/src/space/dtos/subspace/index.ts index 0463a85..0071b44 100644 --- a/src/space/dtos/subspace/index.ts +++ b/src/space/dtos/subspace/index.ts @@ -1,3 +1,6 @@ export * from './add.subspace.dto'; export * from './get.subspace.param'; export * from './add.subspace-device.param'; +export * from './update.subspace.dto'; +export * from './delete.subspace.dto'; +export * from './modify.subspace.dto'; diff --git a/src/space/dtos/subspace/modify.subspace.dto.ts b/src/space/dtos/subspace/modify.subspace.dto.ts new file mode 100644 index 0000000..39a40e6 --- /dev/null +++ b/src/space/dtos/subspace/modify.subspace.dto.ts @@ -0,0 +1,47 @@ +import { ModifyAction } from '@app/common/constants/modify-action.enum'; +import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; +import { Type } from 'class-transformer'; +import { + IsEnum, + IsOptional, + IsString, + IsArray, + ValidateNested, +} from 'class-validator'; +import { ModifyTagDto } from '../tag/modify-tag.dto'; + +export class ModifySubspaceDto { + @ApiProperty({ + description: 'Action to perform: add, update, or delete', + example: ModifyAction.ADD, + }) + @IsEnum(ModifyAction) + action: ModifyAction; + + @ApiPropertyOptional({ + description: 'UUID of the subspace (required for update/delete)', + example: '123e4567-e89b-12d3-a456-426614174000', + }) + @IsOptional() + @IsString() + uuid?: string; + + @ApiPropertyOptional({ + description: 'Name of the subspace (required for add/update)', + example: 'Living Room', + }) + @IsOptional() + @IsString() + subspaceName?: string; + + @ApiPropertyOptional({ + description: + 'List of tag modifications (add/update/delete) for the subspace', + type: [ModifyTagDto], + }) + @IsOptional() + @IsArray() + @ValidateNested({ each: true }) + @Type(() => ModifyTagDto) + tags?: ModifyTagDto[]; +} diff --git a/src/space/dtos/subspace/update.subspace.dto.ts b/src/space/dtos/subspace/update.subspace.dto.ts new file mode 100644 index 0000000..0931d9e --- /dev/null +++ b/src/space/dtos/subspace/update.subspace.dto.ts @@ -0,0 +1,16 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsNotEmpty, IsString } from 'class-validator'; + +export class UpdateSubspaceDto { + @ApiProperty({ + description: 'Name of the subspace', + example: 'Living Room', + }) + @IsNotEmpty() + @IsString() + subspaceName?: string; + + @IsNotEmpty() + @IsString() + subspaceUuid: string; +} diff --git a/src/space/dtos/tag/create-tag-dto.ts b/src/space/dtos/tag/create-tag-dto.ts new file mode 100644 index 0000000..8e89155 --- /dev/null +++ b/src/space/dtos/tag/create-tag-dto.ts @@ -0,0 +1,20 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsNotEmpty, IsString } from 'class-validator'; + +export class CreateTagDto { + @ApiProperty({ + description: 'Tag associated with the space or subspace', + example: 'Temperature Control', + }) + @IsNotEmpty() + @IsString() + tag: string; + + @ApiProperty({ + description: 'ID of the product associated with the tag', + example: '123e4567-e89b-12d3-a456-426614174000', + }) + @IsNotEmpty() + @IsString() + productUuid: string; +} diff --git a/src/space/dtos/tag/index.ts b/src/space/dtos/tag/index.ts new file mode 100644 index 0000000..599ba4b --- /dev/null +++ b/src/space/dtos/tag/index.ts @@ -0,0 +1 @@ +export * from './create-tag-dto'; diff --git a/src/space/dtos/tag/modify-tag.dto.ts b/src/space/dtos/tag/modify-tag.dto.ts new file mode 100644 index 0000000..6088a2a --- /dev/null +++ b/src/space/dtos/tag/modify-tag.dto.ts @@ -0,0 +1,37 @@ +import { ModifyAction } from '@app/common/constants/modify-action.enum'; +import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; +import { IsEnum, IsOptional, IsString } from 'class-validator'; + +export class ModifyTagDto { + @ApiProperty({ + description: 'Action to perform: add, update, or delete', + example: ModifyAction.ADD, + }) + @IsEnum(ModifyAction) + action: ModifyAction; + + @ApiPropertyOptional({ + description: 'UUID of the tag (required for update/delete)', + example: '123e4567-e89b-12d3-a456-426614174000', + }) + @IsOptional() + @IsString() + uuid?: string; + + @ApiPropertyOptional({ + description: 'Name of the tag (required for add/update)', + example: 'Temperature Sensor', + }) + @IsOptional() + @IsString() + tag?: string; + + @ApiPropertyOptional({ + description: + 'UUID of the product associated with the tag (required for add)', + example: 'c789a91e-549a-4753-9006-02f89e8170e0', + }) + @IsOptional() + @IsString() + productUuid?: string; +} diff --git a/src/space/dtos/update.space.dto.ts b/src/space/dtos/update.space.dto.ts index d40476b..ae50dea 100644 --- a/src/space/dtos/update.space.dto.ts +++ b/src/space/dtos/update.space.dto.ts @@ -1,4 +1,59 @@ -import { PartialType } from '@nestjs/swagger'; -import { AddSpaceDto } from './add.space.dto'; +import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; +import { + IsArray, + IsNumber, + IsOptional, + IsString, + ValidateNested, +} from 'class-validator'; +import { ModifySubspaceDto } from './subspace'; +import { Type } from 'class-transformer'; +import { ModifyTagDto } from './tag/modify-tag.dto'; -export class UpdateSpaceDto extends PartialType(AddSpaceDto) {} +export class UpdateSpaceDto { + @ApiProperty({ + description: 'Updated name of the space ', + example: 'New Space Name', + }) + @IsOptional() + @IsString() + spaceName?: string; + + @ApiProperty({ + description: 'Icon identifier for the space', + example: 'assets/location', + required: false, + }) + @IsString() + @IsOptional() + public icon?: string; + + @ApiProperty({ description: 'X position on canvas', example: 120 }) + @IsNumber() + x: number; + + @ApiProperty({ description: 'Y position on canvas', example: 200 }) + @IsNumber() + y: number; + + @ApiPropertyOptional({ + description: 'List of subspace modifications (add/update/delete)', + type: [ModifySubspaceDto], + }) + @IsOptional() + @IsArray() + @ValidateNested({ each: true }) + @Type(() => ModifySubspaceDto) + subspaceModels?: ModifySubspaceDto[]; + + @ApiPropertyOptional({ + description: + 'List of tag modifications (add/update/delete) for the space model', + type: [ModifyTagDto], + }) + @IsOptional() + @IsArray() + @ValidateNested({ each: true }) + @Type(() => ModifyTagDto) + tags?: ModifyTagDto[]; +} diff --git a/src/space/services/space.service.ts b/src/space/services/space.service.ts index 43b8f4e..7498981 100644 --- a/src/space/services/space.service.ts +++ b/src/space/services/space.service.ts @@ -16,11 +16,11 @@ import { BaseResponseDto } from '@app/common/dto/base.response.dto'; import { SpaceEntity } from '@app/common/modules/space/entities'; import { generateRandomString } from '@app/common/helper/randomString'; import { SpaceLinkService } from './space-link'; -import { CreateSubspaceModelDto } from 'src/space-model/dtos'; import { SubSpaceService } from './subspace'; import { DataSource, Not } from 'typeorm'; import { ValidationService } from './space-validation.service'; import { ORPHAN_SPACE_NAME } from '@app/common/constants/orphan-constant'; +import { TagService } from './tag'; @Injectable() export class SpaceService { @@ -30,13 +30,15 @@ export class SpaceService { private readonly spaceLinkService: SpaceLinkService, private readonly subSpaceService: SubSpaceService, private readonly validationService: ValidationService, + private readonly tagService: TagService, ) {} async createSpace( addSpaceDto: AddSpaceDto, params: CommunitySpaceParam, ): Promise { - const { parentUuid, direction, spaceModelUuid, subspaces } = addSpaceDto; + const { parentUuid, direction, spaceModelUuid, subspaces, tags } = + addSpaceDto; const { communityUuid, projectUuid } = params; if (addSpaceDto.spaceName === ORPHAN_SPACE_NAME) { @@ -67,14 +69,14 @@ export class SpaceService { : null; try { - const newSpace = queryRunner.manager.create(SpaceEntity, { + const space = queryRunner.manager.create(SpaceEntity, { ...addSpaceDto, spaceModel, parent: parentUuid ? parent : null, community, }); - await queryRunner.manager.save(newSpace); + const newSpace = await queryRunner.manager.save(space); if (direction && parent) { await this.spaceLinkService.saveSpaceLink( @@ -90,11 +92,14 @@ export class SpaceService { newSpace, queryRunner, ); - } else if (spaceModel && spaceModel.subspaceModels.length) { - await this.subSpaceService.createSubSpaceFromModel( - spaceModel, - newSpace, + } + + if (tags?.length) { + newSpace.tags = await this.tagService.createTags( + tags, queryRunner, + newSpace, + null, ); } @@ -242,19 +247,32 @@ export class SpaceService { HttpStatus.BAD_REQUEST, ); } + if (updateSpaceDto.spaceName) space.spaceName = updateSpaceDto.spaceName; - // If a parentId is provided, check if the parent exists - const { parentUuid } = updateSpaceDto; - const parent = parentUuid - ? await this.validationService.validateSpace(parentUuid) - : null; + if (updateSpaceDto.x) space.x = updateSpaceDto.x; - // Update other space properties from updateSpaceDto - Object.assign(space, updateSpaceDto, { parent }); + if (updateSpaceDto.y) space.y = updateSpaceDto.y; + if (updateSpaceDto.icon) space.icon = updateSpaceDto.icon; + if (updateSpaceDto.icon) space.icon = updateSpaceDto.icon; - // Save the updated space await queryRunner.manager.save(space); + if (updateSpaceDto.subspaceModels) { + await this.subSpaceService.modifySubSpace( + updateSpaceDto.subspaceModels, + space, + queryRunner, + ); + } + + if (updateSpaceDto.tags) { + await this.tagService.modifyTags( + updateSpaceDto.tags, + queryRunner, + space, + ); + } + await queryRunner.commitTransaction(); return new SuccessResponseDto({ diff --git a/src/space/services/subspace/subspace.service.ts b/src/space/services/subspace/subspace.service.ts index 9f553fd..8795ff7 100644 --- a/src/space/services/subspace/subspace.service.ts +++ b/src/space/services/subspace/subspace.service.ts @@ -1,6 +1,13 @@ import { BaseResponseDto } from '@app/common/dto/base.response.dto'; import { HttpException, HttpStatus, Injectable } from '@nestjs/common'; -import { AddSubspaceDto, GetSpaceParam, GetSubSpaceParam } from '../../dtos'; +import { + AddSubspaceDto, + CreateTagDto, + DeleteSubspaceDto, + GetSpaceParam, + GetSubSpaceParam, + ModifySubspaceDto, +} from '../../dtos'; import { SuccessResponseDto } from '@app/common/dto/success.response.dto'; import { TypeORMCustomModel, @@ -19,12 +26,15 @@ import { } from '@app/common/modules/space-model'; import { ValidationService } from '../space-validation.service'; import { SubspaceRepository } from '@app/common/modules/space/repositories/subspace.repository'; +import { TagService } from '../tag'; +import { ModifyAction } from '@app/common/constants/modify-action.enum'; @Injectable() export class SubSpaceService { constructor( private readonly subspaceRepository: SubspaceRepository, private readonly validationService: ValidationService, + private readonly tagService: TagService, ) {} async createSubspaces( @@ -71,6 +81,7 @@ export class SubSpaceService { addSubspaceDtos: AddSubspaceDto[], space: SpaceEntity, queryRunner: QueryRunner, + otherTags?: CreateTagDto[], ): Promise { try { const subspaceData = addSubspaceDtos.map((dto) => ({ @@ -80,6 +91,20 @@ export class SubSpaceService { const subspaces = await this.createSubspaces(subspaceData, queryRunner); + await Promise.all( + addSubspaceDtos.map(async (dto, index) => { + const subspace = subspaces[index]; + if (dto.tags?.length) { + subspace.tags = await this.tagService.createTags( + dto.tags, + queryRunner, + null, + subspace, + otherTags, + ); + } + }), + ); return subspaces; } catch (error) { throw new Error( @@ -144,43 +169,6 @@ export class SubSpaceService { } } - async findOne(params: GetSubSpaceParam): Promise { - const { communityUuid, subSpaceUuid, spaceUuid, projectUuid } = params; - await this.validationService.validateSpaceWithinCommunityAndProject( - communityUuid, - projectUuid, - spaceUuid, - ); - try { - const subSpace = await this.subspaceRepository.findOne({ - where: { - uuid: subSpaceUuid, - }, - }); - - // If space is not found, throw a NotFoundException - if (!subSpace) { - throw new HttpException( - `Sub Space with UUID ${subSpaceUuid} not found`, - HttpStatus.NOT_FOUND, - ); - } - return new SuccessResponseDto({ - message: `Subspace with ID ${subSpaceUuid} successfully fetched`, - data: subSpace, - }); - } catch (error) { - if (error instanceof HttpException) { - throw error; // If it's an HttpException, rethrow it - } else { - throw new HttpException( - 'An error occurred while deleting the subspace', - HttpStatus.INTERNAL_SERVER_ERROR, - ); - } - } - } - async updateSubSpace( params: GetSubSpaceParam, updateSubSpaceDto: AddSubspaceDto, @@ -256,4 +244,153 @@ export class SubSpaceService { ); } } + + async deleteSubspaces( + deleteDtos: DeleteSubspaceDto[], + queryRunner: QueryRunner, + ) { + const deleteResults: { uuid: string }[] = []; + + for (const dto of deleteDtos) { + const subspaceModel = await this.findOne(dto.subspaceUuid); + + await queryRunner.manager.update( + this.subspaceRepository.target, + { uuid: dto.subspaceUuid }, + { disabled: true }, + ); + + if (subspaceModel.tags?.length) { + const modifyTagDtos = subspaceModel.tags.map((tag) => ({ + uuid: tag.uuid, + action: ModifyAction.DELETE, + })); + await this.tagService.modifyTags( + modifyTagDtos, + queryRunner, + null, + subspaceModel, + ); + } + + deleteResults.push({ uuid: dto.subspaceUuid }); + } + + return deleteResults; + } + + async modifySubSpace( + subspaceDtos: ModifySubspaceDto[], + space: SpaceEntity, + queryRunner: QueryRunner, + ): Promise { + for (const subspace of subspaceDtos) { + switch (subspace.action) { + case ModifyAction.ADD: + await this.handleAddAction(subspace, space, queryRunner); + break; + case ModifyAction.UPDATE: + await this.handleUpdateAction(subspace, queryRunner); + break; + case ModifyAction.DELETE: + await this.handleDeleteAction(subspace, queryRunner); + break; + default: + throw new HttpException( + `Invalid action "${subspace.action}".`, + HttpStatus.BAD_REQUEST, + ); + } + } + } + + private async handleAddAction( + subspace: ModifySubspaceDto, + space: SpaceEntity, + queryRunner: QueryRunner, + ): Promise { + const createTagDtos: CreateTagDto[] = + subspace.tags?.map((tag) => ({ + tag: tag.tag as string, + productUuid: tag.productUuid as string, + })) || []; + await this.createSubspacesFromDto( + [{ subspaceName: subspace.subspaceName, tags: createTagDtos }], + space, + queryRunner, + ); + } + + private async handleUpdateAction( + modifyDto: ModifySubspaceDto, + queryRunner: QueryRunner, + ): Promise { + const subspace = await this.findOne(modifyDto.uuid); + + await this.updateSubspaceName( + queryRunner, + subspace, + modifyDto.subspaceName, + ); + + if (modifyDto.tags?.length) { + await this.tagService.modifyTags( + modifyDto.tags, + queryRunner, + null, + subspace, + ); + } + } + + private async handleDeleteAction( + subspace: ModifySubspaceDto, + queryRunner: QueryRunner, + ): Promise { + const subspaceModel = await this.findOne(subspace.uuid); + + await queryRunner.manager.update( + this.subspaceRepository.target, + { uuid: subspace.uuid }, + { disabled: true }, + ); + + if (subspaceModel.tags?.length) { + const modifyTagDtos = subspaceModel.tags.map((tag) => ({ + uuid: tag.uuid, + action: ModifyAction.DELETE, + })); + await this.tagService.modifyTags( + modifyTagDtos, + queryRunner, + null, + subspaceModel, + ); + } + } + + private async findOne(subspaceUuid: string): Promise { + const subspace = await this.subspaceRepository.findOne({ + where: { uuid: subspaceUuid }, + relations: ['tags'], + }); + if (!subspace) { + throw new HttpException( + `SubspaceModel with UUID ${subspaceUuid} not found.`, + HttpStatus.NOT_FOUND, + ); + } + return subspace; + } + + private async updateSubspaceName( + queryRunner: QueryRunner, + subSpace: SubspaceEntity, + subspaceName?: string, + ): Promise { + if (subspaceName) { + subSpace.subspaceName = subspaceName; + await queryRunner.manager.save(subSpace); + } + } } diff --git a/src/space/services/tag/index.ts b/src/space/services/tag/index.ts new file mode 100644 index 0000000..0cbeec4 --- /dev/null +++ b/src/space/services/tag/index.ts @@ -0,0 +1 @@ +export * from './tag.service'; diff --git a/src/space/services/tag/tag.service.ts b/src/space/services/tag/tag.service.ts new file mode 100644 index 0000000..9514cc4 --- /dev/null +++ b/src/space/services/tag/tag.service.ts @@ -0,0 +1,237 @@ +import { ModifyAction } from '@app/common/constants/modify-action.enum'; +import { + SpaceEntity, + SubspaceEntity, + TagEntity, + TagRepository, +} from '@app/common/modules/space'; +import { HttpException, HttpStatus, Injectable } from '@nestjs/common'; +import { ProductService } from 'src/product/services'; +import { CreateTagDto } from 'src/space/dtos'; +import { ModifyTagDto } from 'src/space/dtos/tag/modify-tag.dto'; +import { QueryRunner } from 'typeorm'; + +@Injectable() +export class TagService { + constructor( + private readonly tagRepository: TagRepository, + private readonly productService: ProductService, + ) {} + + async createTags( + tags: CreateTagDto[], + queryRunner: QueryRunner, + space?: SpaceEntity, + subspace?: SubspaceEntity, + additionalTags?: CreateTagDto[], + ): Promise { + if (!tags.length) { + throw new HttpException('Tags cannot be empty.', HttpStatus.BAD_REQUEST); + } + + const combinedTags = additionalTags ? [...tags, ...additionalTags] : tags; + const duplicateTags = this.findDuplicateTags(combinedTags); + + if (duplicateTags.length > 0) { + throw new HttpException( + `Duplicate tags found for the same product: ${duplicateTags.join(', ')}`, + HttpStatus.BAD_REQUEST, + ); + } + + const tagEntities = await Promise.all( + tags.map(async (tagDto) => + this.prepareTagEntity(tagDto, queryRunner, space, subspace), + ), + ); + try { + return await queryRunner.manager.save(tagEntities); + } catch (error) { + if (error instanceof HttpException) { + throw error; + } + + throw new HttpException( + 'Failed to save tag models due to an unexpected error.', + HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + } + + async updateTag( + tag: ModifyTagDto, + queryRunner: QueryRunner, + space?: SpaceEntity, + subspace?: SubspaceEntity, + ): Promise { + try { + const existingTag = await this.getTagByUuid(tag.uuid); + + if (space) { + await this.checkTagReuse(tag.tag, existingTag.product.uuid, space); + } else { + await this.checkTagReuse( + tag.tag, + existingTag.product.uuid, + subspace.space, + ); + } + + if (tag.tag) { + existingTag.tag = tag.tag; + } + + return await queryRunner.manager.save(existingTag); + } catch (error) { + if (error instanceof HttpException) { + throw error; + } + throw new HttpException( + error.message || 'Failed to update tags', + HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + } + + async deleteTags(tagUuids: string[], queryRunner: QueryRunner) { + try { + const deletePromises = tagUuids.map((id) => + queryRunner.manager.softDelete(this.tagRepository.target, id), + ); + + await Promise.all(deletePromises); + return { message: 'Tags deleted successfully', tagUuids }; + } catch (error) { + if (error instanceof HttpException) { + throw error; + } + throw new HttpException( + error.message || 'Failed to delete tags', + HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + } + + private findDuplicateTags(tags: CreateTagDto[]): string[] { + const seen = new Map(); + const duplicates: string[] = []; + + tags.forEach((tagDto) => { + const key = `${tagDto.productUuid}-${tagDto.tag}`; + if (seen.has(key)) { + duplicates.push(`${tagDto.tag} for Product: ${tagDto.productUuid}`); + } else { + seen.set(key, true); + } + }); + + return duplicates; + } + + async modifyTags( + tags: ModifyTagDto[], + queryRunner: QueryRunner, + space?: SpaceEntity, + subspace?: SubspaceEntity, + ): Promise { + try { + console.log(tags); + for (const tag of tags) { + if (tag.action === ModifyAction.ADD) { + const createTagDto: CreateTagDto = { + tag: tag.tag as string, + productUuid: tag.productUuid as string, + }; + + await this.createTags([createTagDto], queryRunner, space, subspace); + } else if (tag.action === ModifyAction.UPDATE) { + await this.updateTag(tag, queryRunner, space, subspace); + } else if (tag.action === ModifyAction.DELETE) { + await queryRunner.manager.update( + this.tagRepository.target, + { uuid: tag.uuid }, + { disabled: true }, + ); + } else { + throw new HttpException( + `Invalid action "${tag.action}" provided.`, + HttpStatus.BAD_REQUEST, + ); + } + } + } catch (error) { + if (error instanceof HttpException) { + throw error; + } + + throw new HttpException( + `An error occurred while modifying tags: ${error.message}`, + HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + } + + private async checkTagReuse( + tag: string, + productUuid: string, + space: SpaceEntity, + ): Promise { + const isTagInSpace = await this.tagRepository.exists({ + where: { tag, space, product: { uuid: productUuid } }, + }); + const isTagInSubspace = await this.tagRepository.exists({ + where: { + tag, + subspace: { space }, + product: { uuid: productUuid }, + }, + }); + + if (isTagInSpace || isTagInSubspace) { + throw new HttpException(`Tag can't be reused`, HttpStatus.CONFLICT); + } + } + + private async prepareTagEntity( + tagDto: CreateTagDto, + queryRunner: QueryRunner, + space?: SpaceEntity, + subspace?: SubspaceEntity, + ): Promise { + const product = await this.productService.findOne(tagDto.productUuid); + + if (!product) { + throw new HttpException( + `Product with UUID ${tagDto.productUuid} not found.`, + HttpStatus.NOT_FOUND, + ); + } + + if (space) { + await this.checkTagReuse(tagDto.tag, tagDto.productUuid, space); + } else { + await this.checkTagReuse(tagDto.tag, tagDto.productUuid, subspace.space); + } + + return queryRunner.manager.create(TagEntity, { + tag: tagDto.tag, + product: product.data, + space, + subspace, + }); + } + + private async getTagByUuid(uuid: string): Promise { + const tag = await this.tagRepository.findOne({ + where: { uuid }, + relations: ['product'], + }); + if (!tag) { + throw new HttpException( + `Tag with ID ${uuid} not found.`, + HttpStatus.NOT_FOUND, + ); + } + return tag; + } +} diff --git a/src/space/space.module.ts b/src/space/space.module.ts index 5d16689..a17d95e 100644 --- a/src/space/space.module.ts +++ b/src/space/space.module.ts @@ -21,6 +21,7 @@ import { import { SpaceRepository, SpaceLinkRepository, + TagRepository, } from '@app/common/modules/space/repositories'; import { CommunityRepository } from '@app/common/modules/community/repositories'; import { @@ -40,10 +41,14 @@ 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 { + SpaceModelRepository, + TagModelRepository, +} from '@app/common/modules/space-model'; import { CommunityModule } from 'src/community/community.module'; import { ValidationService } from './services'; import { SubspaceRepository } from '@app/common/modules/space/repositories/subspace.repository'; +import { TagService } from './services/tag'; @Module({ imports: [ConfigModule, SpaceRepositoryModule, CommunityModule], @@ -57,8 +62,11 @@ import { SubspaceRepository } from '@app/common/modules/space/repositories/subsp ], providers: [ ValidationService, + TagModelRepository, + TagRepository, SpaceService, TuyaService, + TagService, ProductRepository, SubSpaceService, SpaceDeviceService, From 2e46176fd51d4bf254601e465f551aa0069bb729 Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Tue, 24 Dec 2024 14:51:06 +0400 Subject: [PATCH 32/45] added space delete --- .../services/space-model.service.ts | 7 ++ .../subspace/subspace-model.service.ts | 52 ++++++++++--- src/space-model/services/tag-model.service.ts | 26 ++++--- .../subspace/subspace.controller.ts | 2 +- src/space/dtos/update.space.dto.ts | 6 +- .../services/space-validation.service.ts | 1 + src/space/services/space.service.ts | 44 +++++++++-- .../services/subspace/subspace.service.ts | 77 +++++++++++++++++-- src/space/services/tag/tag.service.ts | 29 ++++--- 9 files changed, 200 insertions(+), 44 deletions(-) diff --git a/src/space-model/services/space-model.service.ts b/src/space-model/services/space-model.service.ts index 40d9970..ad89d37 100644 --- a/src/space-model/services/space-model.service.ts +++ b/src/space-model/services/space-model.service.ts @@ -199,6 +199,12 @@ export class SpaceModelService { ); } + if (spaceModel.tags?.length) { + const deleteSpaceTagsDtos = spaceModel.tags.map((tag) => tag.uuid); + + await this.tagModelService.deleteTags(deleteSpaceTagsDtos, queryRunner); + } + await queryRunner.manager.update( this.spaceModelRepository.target, { uuid: param.spaceModelUuid }, @@ -248,6 +254,7 @@ export class SpaceModelService { uuid, disabled: true, }, + relations: ['subspaceModels', 'tags'], }); if (!spaceModel) { throw new HttpException('space model not found', HttpStatus.NOT_FOUND); diff --git a/src/space-model/services/subspace/subspace-model.service.ts b/src/space-model/services/subspace/subspace-model.service.ts index 507a16a..6b8cb9f 100644 --- a/src/space-model/services/subspace/subspace-model.service.ts +++ b/src/space-model/services/subspace/subspace-model.service.ts @@ -5,7 +5,7 @@ import { } from '@app/common/modules/space-model'; import { HttpException, HttpStatus, Injectable } from '@nestjs/common'; import { CreateSubspaceModelDto, CreateTagModelDto } from '../../dtos'; -import { QueryRunner } from 'typeorm'; +import { In, QueryRunner } from 'typeorm'; import { IDeletedSubsaceModelInterface } from 'src/space-model/interfaces'; import { DeleteSubspaceModelDto, @@ -27,7 +27,7 @@ export class SubSpaceModelService { queryRunner: QueryRunner, otherTags?: CreateTagModelDto[], ): Promise { - this.validateInputDtos(subSpaceModelDtos); + this.validateInputDtos(subSpaceModelDtos, spaceModel); const subspaces = subSpaceModelDtos.map((subspaceDto) => queryRunner.manager.create(this.subspaceModelRepository.target, { @@ -41,13 +41,18 @@ export class SubSpaceModelService { await Promise.all( subSpaceModelDtos.map(async (dto, index) => { const subspace = savedSubspaces[index]; + + const otherDtoTags = subSpaceModelDtos + .filter((_, i) => i !== index) + .flatMap((otherDto) => otherDto.tags || []); + if (dto.tags?.length) { subspace.tags = await this.tagModelService.createTags( dto.tags, queryRunner, null, subspace, - otherTags, + [...(otherTags || []), ...otherDtoTags], ); } }), @@ -194,34 +199,61 @@ export class SubSpaceModelService { return subspace; } - private validateInputDtos(subSpaceModelDtos: CreateSubspaceModelDto[]): void { + private validateInputDtos( + subSpaceModelDtos: CreateSubspaceModelDto[], + spaceModel: SpaceModelEntity, + ): void { if (subSpaceModelDtos.length === 0) { throw new HttpException( 'Subspace models cannot be empty.', HttpStatus.BAD_REQUEST, ); } - this.validateName(subSpaceModelDtos.map((dto) => dto.subspaceName)); + this.validateName( + subSpaceModelDtos.map((dto) => dto.subspaceName), + spaceModel, + ); } - private validateName(names: string[]): void { + private async validateName( + names: string[], + spaceModel: SpaceModelEntity, + ): Promise { const seenNames = new Set(); const duplicateNames = new Set(); for (const name of names) { - if (seenNames.has(name)) { + if (!seenNames.add(name)) { duplicateNames.add(name); - } else { - seenNames.add(name); } } if (duplicateNames.size > 0) { throw new HttpException( - `Duplicate subspace names found: ${[...duplicateNames].join(', ')}`, + `Duplicate subspace model names found: ${[...duplicateNames].join(', ')}`, HttpStatus.CONFLICT, ); } + + const existingNames = await this.subspaceModelRepository.find({ + select: ['subspaceName'], + where: { + subspaceName: In([...seenNames]), + spaceModel: { + uuid: spaceModel.uuid, + }, + }, + }); + + if (existingNames.length > 0) { + const existingNamesList = existingNames + .map((e) => e.subspaceName) + .join(', '); + throw new HttpException( + `Subspace model names already exist in the space: ${existingNamesList}`, + HttpStatus.BAD_REQUEST, + ); + } } private async updateSubspaceName( diff --git a/src/space-model/services/tag-model.service.ts b/src/space-model/services/tag-model.service.ts index 8207e0b..5682bc2 100644 --- a/src/space-model/services/tag-model.service.ts +++ b/src/space-model/services/tag-model.service.ts @@ -180,18 +180,24 @@ export class TagModelService { productUuid: string, spaceModel: SpaceModelEntity, ): Promise { - const isTagInSpaceModel = await this.tagModelRepository.exists({ - where: { tag, spaceModel, product: { uuid: productUuid } }, - }); - const isTagInSubspaceModel = await this.tagModelRepository.exists({ - where: { - tag, - subspaceModel: { spaceModel }, - product: { uuid: productUuid }, - }, + const tagExists = await this.tagModelRepository.exists({ + where: [ + { + tag, + spaceModel: { uuid: spaceModel.uuid }, + product: { uuid: productUuid }, + disabled: false, + }, + { + tag, + subspaceModel: { spaceModel: { uuid: spaceModel.uuid } }, + product: { uuid: productUuid }, + disabled: false, + }, + ], }); - if (isTagInSpaceModel || isTagInSubspaceModel) { + if (tagExists) { throw new HttpException(`Tag can't be reused`, HttpStatus.CONFLICT); } } diff --git a/src/space/controllers/subspace/subspace.controller.ts b/src/space/controllers/subspace/subspace.controller.ts index 37e264b..3ed36af 100644 --- a/src/space/controllers/subspace/subspace.controller.ts +++ b/src/space/controllers/subspace/subspace.controller.ts @@ -65,7 +65,7 @@ export class SubSpaceController { }) @Get(':subSpaceUuid') async findOne(@Param() params: GetSubSpaceParam): Promise { - return this.subSpaceService.findOne(params); + return this.subSpaceService.getOne(params); } @ApiBearerAuth() diff --git a/src/space/dtos/update.space.dto.ts b/src/space/dtos/update.space.dto.ts index ae50dea..9bc7950 100644 --- a/src/space/dtos/update.space.dto.ts +++ b/src/space/dtos/update.space.dto.ts @@ -30,11 +30,13 @@ export class UpdateSpaceDto { @ApiProperty({ description: 'X position on canvas', example: 120 }) @IsNumber() - x: number; + @IsOptional() + x?: number; @ApiProperty({ description: 'Y position on canvas', example: 200 }) @IsNumber() - y: number; + @IsOptional() + y?: number; @ApiPropertyOptional({ description: 'List of subspace modifications (add/update/delete)', diff --git a/src/space/services/space-validation.service.ts b/src/space/services/space-validation.service.ts index fa4a5f7..77ff011 100644 --- a/src/space/services/space-validation.service.ts +++ b/src/space/services/space-validation.service.ts @@ -44,6 +44,7 @@ export class ValidationService { async validateSpace(spaceUuid: string): Promise { const space = await this.spaceRepository.findOne({ where: { uuid: spaceUuid }, + relations: ['subspaces', 'tags'], }); if (!space) { diff --git a/src/space/services/space.service.ts b/src/space/services/space.service.ts index 7498981..0bdbff9 100644 --- a/src/space/services/space.service.ts +++ b/src/space/services/space.service.ts @@ -58,7 +58,7 @@ export class SpaceService { projectUuid, ); - this.validateSpaceCreation(spaceModelUuid); + this.validateSpaceCreation(addSpaceDto, spaceModelUuid); const parent = parentUuid ? await this.validationService.validateSpace(parentUuid) @@ -91,6 +91,7 @@ export class SpaceService { subspaces, newSpace, queryRunner, + tags, ); } @@ -189,6 +190,10 @@ export class SpaceService { } async delete(params: GetSpaceParam): Promise { + const queryRunner = this.dataSource.createQueryRunner(); + + await queryRunner.connect(); + await queryRunner.startTransaction(); try { const { communityUuid, spaceUuid, projectUuid } = params; @@ -205,14 +210,38 @@ export class SpaceService { HttpStatus.BAD_REQUEST, ); } - // Delete the space - await this.spaceRepository.remove(space); + + if (space.tags?.length) { + const deleteSpaceTagsDtos = space.tags.map((tag) => tag.uuid); + await this.tagService.deleteTags(deleteSpaceTagsDtos, queryRunner); + } + + if (space.subspaces?.length) { + const deleteSubspaceDtos = space.subspaces.map((subspace) => ({ + subspaceUuid: subspace.uuid, + })); + + await this.subSpaceService.deleteSubspaces( + deleteSubspaceDtos, + queryRunner, + ); + } + + await queryRunner.manager.update( + this.spaceRepository.target, + { uuid: params.spaceUuid }, + { disabled: true }, + ); + + await queryRunner.commitTransaction(); return new SuccessResponseDto({ message: `Space with ID ${spaceUuid} successfully deleted`, statusCode: HttpStatus.OK, }); } catch (error) { + await queryRunner.rollbackTransaction(); + if (error instanceof HttpException) { throw error; } @@ -220,6 +249,8 @@ export class SpaceService { 'An error occurred while deleting the space', HttpStatus.INTERNAL_SERVER_ERROR, ); + } finally { + await queryRunner.release(); } } @@ -384,8 +415,11 @@ export class SpaceService { return rootSpaces; } - private validateSpaceCreation(spaceModelUuid?: string) { - if (spaceModelUuid) { + private validateSpaceCreation( + addSpaceDto: AddSpaceDto, + spaceModelUuid?: string, + ) { + if (spaceModelUuid && (addSpaceDto.tags || addSpaceDto.subspaces)) { throw new HttpException( '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 8795ff7..519608d 100644 --- a/src/space/services/subspace/subspace.service.ts +++ b/src/space/services/subspace/subspace.service.ts @@ -15,7 +15,7 @@ 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 { QueryRunner } from 'typeorm'; +import { In, QueryRunner } from 'typeorm'; import { SpaceEntity, SubspaceEntity, @@ -84,6 +84,11 @@ export class SubSpaceService { otherTags?: CreateTagDto[], ): Promise { try { + await this.validateName( + addSubspaceDtos.map((dto) => dto.subspaceName), + space, + ); + const subspaceData = addSubspaceDtos.map((dto) => ({ subspaceName: dto.subspaceName, space, @@ -93,6 +98,9 @@ export class SubSpaceService { await Promise.all( addSubspaceDtos.map(async (dto, index) => { + const otherDtoTags = addSubspaceDtos + .filter((_, i) => i !== index) + .flatMap((otherDto) => otherDto.tags || []); const subspace = subspaces[index]; if (dto.tags?.length) { subspace.tags = await this.tagService.createTags( @@ -100,15 +108,20 @@ export class SubSpaceService { queryRunner, null, subspace, - otherTags, + [...(otherTags || []), ...otherDtoTags], ); } }), ); return subspaces; } catch (error) { - throw new Error( - `Transaction failed: Unable to create subspaces and products. ${error.message}`, + if (error instanceof HttpException) { + throw error; + } + + throw new HttpException( + 'Failed to save subspaces due to an unexpected error.', + HttpStatus.INTERNAL_SERVER_ERROR, ); } } @@ -304,6 +317,19 @@ export class SubSpaceService { } } + async getOne(params: GetSubSpaceParam): Promise { + await this.validationService.validateSpaceWithinCommunityAndProject( + params.communityUuid, + params.projectUuid, + params.spaceUuid, + ); + const subspace = await this.findOne(params.subSpaceUuid); + return new SuccessResponseDto({ + message: `Successfully retrieved subspace`, + data: subspace, + }); + } + private async handleAddAction( subspace: ModifySubspaceDto, space: SpaceEntity, @@ -372,7 +398,7 @@ export class SubSpaceService { private async findOne(subspaceUuid: string): Promise { const subspace = await this.subspaceRepository.findOne({ where: { uuid: subspaceUuid }, - relations: ['tags'], + relations: ['tags', 'space'], }); if (!subspace) { throw new HttpException( @@ -393,4 +419,45 @@ export class SubSpaceService { await queryRunner.manager.save(subSpace); } } + + private async validateName( + names: string[], + space: SpaceEntity, + ): Promise { + const seenNames = new Set(); + const duplicateNames = new Set(); + + for (const name of names) { + if (!seenNames.add(name)) { + duplicateNames.add(name); + } + } + + if (duplicateNames.size > 0) { + throw new HttpException( + `Duplicate subspace names found: ${[...duplicateNames].join(', ')}`, + HttpStatus.CONFLICT, + ); + } + + const existingNames = await this.subspaceRepository.find({ + select: ['subspaceName'], + where: { + subspaceName: In([...seenNames]), + space: { + uuid: space.uuid, + }, + }, + }); + + if (existingNames.length > 0) { + const existingNamesList = existingNames + .map((e) => e.subspaceName) + .join(', '); + throw new HttpException( + `Subspace names already exist in the space: ${existingNamesList}`, + HttpStatus.BAD_REQUEST, + ); + } + } } diff --git a/src/space/services/tag/tag.service.ts b/src/space/services/tag/tag.service.ts index 9514cc4..173d91d 100644 --- a/src/space/services/tag/tag.service.ts +++ b/src/space/services/tag/tag.service.ts @@ -135,7 +135,6 @@ export class TagService { subspace?: SubspaceEntity, ): Promise { try { - console.log(tags); for (const tag of tags) { if (tag.action === ModifyAction.ADD) { const createTagDto: CreateTagDto = { @@ -176,18 +175,26 @@ export class TagService { productUuid: string, space: SpaceEntity, ): Promise { - const isTagInSpace = await this.tagRepository.exists({ - where: { tag, space, product: { uuid: productUuid } }, - }); - const isTagInSubspace = await this.tagRepository.exists({ - where: { - tag, - subspace: { space }, - product: { uuid: productUuid }, - }, + const { uuid: spaceUuid } = space; + + const tagExists = await this.tagRepository.exists({ + where: [ + { + tag, + space: { uuid: spaceUuid }, + product: { uuid: productUuid }, + disabled: false, + }, + { + tag, + subspace: { space: { uuid: spaceUuid } }, + product: { uuid: productUuid }, + disabled: false, + }, + ], }); - if (isTagInSpace || isTagInSubspace) { + if (tagExists) { throw new HttpException(`Tag can't be reused`, HttpStatus.CONFLICT); } } From a74a0db26e3a3878448bbddadc816443828c029c Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Wed, 25 Dec 2024 10:52:48 +0400 Subject: [PATCH 33/45] updated space update --- .../entities/space-model.entity.ts | 5 + .../subspace-model/subspace-model.entity.ts | 4 +- .../modules/space/entities/space.entity.ts | 5 +- .../entities/subspace/subspace.entity.ts | 5 +- .../services/space-model.service.ts | 13 +- src/space-model/space-model.module.ts | 2 +- src/space/dtos/add.space.dto.ts | 2 - src/space/dtos/update.space.dto.ts | 2 +- .../services/space-validation.service.ts | 11 +- src/space/services/space.service.ts | 143 ++++++++--- .../services/subspace/subspace.service.ts | 53 +++- src/space/services/tag/tag.service.ts | 243 +++++++++++------- src/space/space.module.ts | 11 +- 13 files changed, 331 insertions(+), 168 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 b108729..18d0688 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 @@ -58,4 +58,9 @@ export class SpaceModelEntity extends AbstractEntity { @OneToMany(() => TagModel, (tag) => tag.spaceModel) tags: TagModel[]; + + constructor(partial: Partial) { + super(); + Object.assign(this, partial); + } } 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 becb2f8..e1d0be0 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 @@ -30,10 +30,10 @@ export class SubspaceModelEntity extends AbstractEntity { ) public spaceModel: SpaceModelEntity; - @OneToMany(() => SubspaceEntity, (space) => space.subSpaceModel, { + @OneToMany(() => SubspaceEntity, (subspace) => subspace.subSpaceModel, { cascade: true, }) - public spaces: SubspaceEntity[]; + public subspaceModel: SubspaceEntity[]; @Column({ nullable: false, diff --git a/libs/common/src/modules/space/entities/space.entity.ts b/libs/common/src/modules/space/entities/space.entity.ts index 889363f..8ecbc70 100644 --- a/libs/common/src/modules/space/entities/space.entity.ts +++ b/libs/common/src/modules/space/entities/space.entity.ts @@ -103,8 +103,9 @@ export class SpaceEntity extends AbstractEntity { @OneToMany(() => SceneEntity, (scene) => scene.space) scenes: SceneEntity[]; - @ManyToOne(() => SpaceModelEntity, { nullable: true }) - @JoinColumn({ name: 'space_model_uuid' }) + @ManyToOne(() => SpaceModelEntity, (spaceModel) => spaceModel.spaces, { + nullable: true, + }) spaceModel?: SpaceModelEntity; @OneToMany( 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 d2f86d9..c7247cc 100644 --- a/libs/common/src/modules/space/entities/subspace/subspace.entity.ts +++ b/libs/common/src/modules/space/entities/subspace/subspace.entity.ts @@ -37,8 +37,9 @@ export class SubspaceEntity extends AbstractEntity { }) devices: DeviceEntity[]; - @ManyToOne(() => SubspaceModelEntity, { nullable: true }) - @JoinColumn({ name: 'subspace_model_uuid' }) + @ManyToOne(() => SubspaceModelEntity, (subspace) => subspace.subspaceModel, { + nullable: true, + }) subSpaceModel?: SubspaceModelEntity; @OneToMany(() => TagEntity, (tag) => tag.subspace) diff --git a/src/space-model/services/space-model.service.ts b/src/space-model/services/space-model.service.ts index ad89d37..7704fd1 100644 --- a/src/space-model/services/space-model.service.ts +++ b/src/space-model/services/space-model.service.ts @@ -106,8 +106,7 @@ export class SpaceModelService { pageable.where = { project: { uuid: param.projectUuid }, }; - pageable.include = - 'subspaceModels,spaceProductModels,subspaceModels.productModels,subspaceModels.productModels.itemModels,spaceProductModels.items'; + pageable.include = 'subspaceModels,tags,subspaceModels.tags'; const customModel = TypeORMCustomModel(this.spaceModelRepository); @@ -252,9 +251,15 @@ export class SpaceModelService { const spaceModel = await this.spaceModelRepository.findOne({ where: { uuid, - disabled: true, + disabled: false, }, - relations: ['subspaceModels', 'tags'], + relations: [ + 'subspaceModels', + 'tags', + 'tags.product', + 'subspaceModels.tags', + 'subspaceModels.tags.product', + ], }); if (!spaceModel) { throw new HttpException('space model not found', HttpStatus.NOT_FOUND); diff --git a/src/space-model/space-model.module.ts b/src/space-model/space-model.module.ts index 736245b..bc3398d 100644 --- a/src/space-model/space-model.module.ts +++ b/src/space-model/space-model.module.ts @@ -37,6 +37,6 @@ const CommandHandlers = [PropogateSubspaceHandler]; TagModelService, TagModelRepository, ], - exports: [CqrsModule], + exports: [CqrsModule, SpaceModelService], }) export class SpaceModelModule {} diff --git a/src/space/dtos/add.space.dto.ts b/src/space/dtos/add.space.dto.ts index e682b11..a3c9a3b 100644 --- a/src/space/dtos/add.space.dto.ts +++ b/src/space/dtos/add.space.dto.ts @@ -74,7 +74,6 @@ export class AddSpaceDto { type: [AddSubspaceDto], }) @IsOptional() - @IsArray() @ValidateNested({ each: true }) @Type(() => AddSubspaceDto) subspaces?: AddSubspaceDto[]; @@ -83,7 +82,6 @@ export class AddSpaceDto { description: 'List of tags associated with the space model', type: [CreateTagDto], }) - @IsArray() @ValidateNested({ each: true }) @Type(() => CreateTagDto) tags?: CreateTagDto[]; diff --git a/src/space/dtos/update.space.dto.ts b/src/space/dtos/update.space.dto.ts index 9bc7950..02efb86 100644 --- a/src/space/dtos/update.space.dto.ts +++ b/src/space/dtos/update.space.dto.ts @@ -46,7 +46,7 @@ export class UpdateSpaceDto { @IsArray() @ValidateNested({ each: true }) @Type(() => ModifySubspaceDto) - subspaceModels?: ModifySubspaceDto[]; + subspace?: ModifySubspaceDto[]; @ApiPropertyOptional({ description: diff --git a/src/space/services/space-validation.service.ts b/src/space/services/space-validation.service.ts index 77ff011..e8bfd32 100644 --- a/src/space/services/space-validation.service.ts +++ b/src/space/services/space-validation.service.ts @@ -43,7 +43,7 @@ export class ValidationService { async validateSpace(spaceUuid: string): Promise { const space = await this.spaceRepository.findOne({ - where: { uuid: spaceUuid }, + where: { uuid: spaceUuid, disabled: false }, relations: ['subspaces', 'tags'], }); @@ -62,11 +62,10 @@ export class ValidationService { where: { uuid: spaceModelUuid }, relations: [ 'subspaceModels', - 'subspaceModels.productModels.product', - 'subspaceModels.productModels', - 'spaceProductModels', - 'spaceProductModels.product', - 'spaceProductModels.items', + 'subspaceModels.tags', + 'tags', + 'subspaceModels.tags.product', + 'tags.product', ], }); diff --git a/src/space/services/space.service.ts b/src/space/services/space.service.ts index 0bdbff9..d7ff346 100644 --- a/src/space/services/space.service.ts +++ b/src/space/services/space.service.ts @@ -7,7 +7,9 @@ import { } from '@nestjs/common'; import { AddSpaceDto, + AddSubspaceDto, CommunitySpaceParam, + CreateTagDto, GetSpaceParam, UpdateSpaceDto, } from '../dtos'; @@ -17,10 +19,11 @@ import { SpaceEntity } from '@app/common/modules/space/entities'; import { generateRandomString } from '@app/common/helper/randomString'; import { SpaceLinkService } from './space-link'; import { SubSpaceService } from './subspace'; -import { DataSource, Not } from 'typeorm'; +import { DataSource, Not, QueryRunner } from 'typeorm'; import { ValidationService } from './space-validation.service'; import { ORPHAN_SPACE_NAME } from '@app/common/constants/orphan-constant'; import { TagService } from './tag'; +import { SpaceModelService } from 'src/space-model/services'; @Injectable() export class SpaceService { @@ -31,6 +34,7 @@ export class SpaceService { private readonly subSpaceService: SubSpaceService, private readonly validationService: ValidationService, private readonly tagService: TagService, + private readonly spaceModelService: SpaceModelService, ) {} async createSpace( @@ -78,31 +82,23 @@ export class SpaceService { const newSpace = await queryRunner.manager.save(space); - if (direction && parent) { - await this.spaceLinkService.saveSpaceLink( - parent.uuid, - newSpace.uuid, - direction, - ); - } - - if (subspaces?.length) { - await this.subSpaceService.createSubspacesFromDto( - subspaces, - newSpace, - queryRunner, - tags, - ); - } - - if (tags?.length) { - newSpace.tags = await this.tagService.createTags( - tags, - queryRunner, - newSpace, - null, - ); - } + await Promise.all([ + spaceModelUuid && + this.createFromModel(spaceModelUuid, queryRunner, newSpace), + direction && parent + ? this.spaceLinkService.saveSpaceLink( + parent.uuid, + newSpace.uuid, + direction, + ) + : Promise.resolve(), + subspaces?.length + ? this.createSubspaces(subspaces, newSpace, queryRunner, tags) + : Promise.resolve(), + tags?.length + ? this.createTags(tags, queryRunner, newSpace) + : Promise.resolve(), + ]); await queryRunner.commitTransaction(); @@ -123,6 +119,41 @@ export class SpaceService { } } + async createFromModel( + spaceModelUuid: string, + queryRunner: QueryRunner, + space: SpaceEntity, + ) { + try { + const spaceModel = + await this.spaceModelService.validateSpaceModel(spaceModelUuid); + + space.spaceModel = spaceModel; + await queryRunner.manager.save(SpaceEntity, space); + + await this.subSpaceService.createSubSpaceFromModel( + spaceModel.subspaceModels, + space, + queryRunner, + ); + + await this.tagService.createTagsFromModel( + queryRunner, + spaceModel.tags, + space, + null, + ); + } catch (error) { + if (error instanceof HttpException) { + throw error; + } + throw new HttpException( + 'An error occurred while creating the space from space model', + HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + } + async getSpacesHierarchyForCommunity( params: CommunitySpaceParam, ): Promise { @@ -274,29 +305,32 @@ export class SpaceService { if (space.spaceName === ORPHAN_SPACE_NAME) { throw new HttpException( - `space ${ORPHAN_SPACE_NAME} cannot be updated`, + `Space "${ORPHAN_SPACE_NAME}" cannot be updated`, HttpStatus.BAD_REQUEST, ); } - if (updateSpaceDto.spaceName) space.spaceName = updateSpaceDto.spaceName; - if (updateSpaceDto.x) space.x = updateSpaceDto.x; - - if (updateSpaceDto.y) space.y = updateSpaceDto.y; - if (updateSpaceDto.icon) space.icon = updateSpaceDto.icon; - if (updateSpaceDto.icon) space.icon = updateSpaceDto.icon; + this.updateSpaceProperties(space, updateSpaceDto); await queryRunner.manager.save(space); - if (updateSpaceDto.subspaceModels) { + const hasSubspace = updateSpaceDto.subspace?.length > 0; + const hasTags = updateSpaceDto.tags?.length > 0; + + if (hasSubspace || hasTags) { + await this.tagService.unlinkModels(space.tags, queryRunner); + await this.subSpaceService.unlinkModels(space.subspaces, queryRunner); + } + + if (hasSubspace) { await this.subSpaceService.modifySubSpace( - updateSpaceDto.subspaceModels, + updateSpaceDto.subspace, space, queryRunner, ); } - if (updateSpaceDto.tags) { + if (hasTags) { await this.tagService.modifyTags( updateSpaceDto.tags, queryRunner, @@ -317,6 +351,7 @@ export class SpaceService { if (error instanceof HttpException) { throw error; } + throw new HttpException( 'An error occurred while updating the space', HttpStatus.INTERNAL_SERVER_ERROR, @@ -326,6 +361,18 @@ export class SpaceService { } } + private updateSpaceProperties( + space: SpaceEntity, + updateSpaceDto: UpdateSpaceDto, + ): void { + const { spaceName, x, y, icon } = updateSpaceDto; + + if (spaceName) space.spaceName = spaceName; + if (x) space.x = x; + if (y) space.y = y; + if (icon) space.icon = icon; + } + async getSpacesHierarchyForSpace( params: GetSpaceParam, ): Promise { @@ -339,7 +386,7 @@ export class SpaceService { try { // Get all spaces that are children of the provided space, including the parent-child relations const spaces = await this.spaceRepository.find({ - where: { parent: { uuid: spaceUuid } }, + where: { parent: { uuid: spaceUuid }, disabled: false }, relations: ['parent', 'children'], // Include parent and children relations }); @@ -426,4 +473,26 @@ export class SpaceService { ); } } + + private async createSubspaces( + subspaces: AddSubspaceDto[], + space: SpaceEntity, + queryRunner: QueryRunner, + tags: CreateTagDto[], + ): Promise { + space.subspaces = await this.subSpaceService.createSubspacesFromDto( + subspaces, + space, + queryRunner, + tags, + ); + } + + private async createTags( + tags: CreateTagDto[], + queryRunner: QueryRunner, + space: SpaceEntity, + ): Promise { + space.tags = await this.tagService.createTags(tags, queryRunner, space); + } } diff --git a/src/space/services/subspace/subspace.service.ts b/src/space/services/subspace/subspace.service.ts index 519608d..01c3bd5 100644 --- a/src/space/services/subspace/subspace.service.ts +++ b/src/space/services/subspace/subspace.service.ts @@ -19,11 +19,9 @@ import { In, QueryRunner } from 'typeorm'; import { SpaceEntity, SubspaceEntity, + TagEntity, } from '@app/common/modules/space/entities'; -import { - SpaceModelEntity, - SubspaceModelEntity, -} from '@app/common/modules/space-model'; +import { SubspaceModelEntity } from '@app/common/modules/space-model'; import { ValidationService } from '../space-validation.service'; import { SubspaceRepository } from '@app/common/modules/space/repositories/subspace.repository'; import { TagService } from '../tag'; @@ -49,7 +47,6 @@ export class SubSpaceService { const subspaces = subspaceData.map((data) => queryRunner.manager.create(this.subspaceRepository.target, data), ); - return await queryRunner.manager.save(subspaces); } catch (error) { throw new HttpException( @@ -60,21 +57,30 @@ export class SubSpaceService { } async createSubSpaceFromModel( - spaceModel: SpaceModelEntity, + subspaceModels: SubspaceModelEntity[], space: SpaceEntity, queryRunner: QueryRunner, ): Promise { - const subSpaceModels = spaceModel.subspaceModels; + if (!subspaceModels?.length) return; - if (!subSpaceModels?.length) return; - - const subspaceData = subSpaceModels.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.tagService.createTagsFromModel( + queryRunner, + model.tags || [], + null, + subspaces[index], + ), + ), + ); } async createSubspacesFromDto( @@ -317,6 +323,31 @@ export class SubSpaceService { } } + async unlinkModels( + subspaces: SubspaceEntity[], + queryRunner: QueryRunner, + ): Promise { + if (!subspaces || subspaces.length === 0) { + return; + } + try { + const allTags = subspaces.flatMap((subSpace) => { + subSpace.subSpaceModel = null; + return subSpace.tags || []; + }); + + await this.tagService.unlinkModels(allTags, queryRunner); + + await queryRunner.manager.save(subspaces); + } catch (error) { + if (error instanceof HttpException) throw error; + throw new HttpException( + `Failed to unlink subspace models: ${error.message}`, + HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + } + async getOne(params: GetSubSpaceParam): Promise { await this.validationService.validateSpaceWithinCommunityAndProject( params.communityUuid, diff --git a/src/space/services/tag/tag.service.ts b/src/space/services/tag/tag.service.ts index 173d91d..c9ccc1c 100644 --- a/src/space/services/tag/tag.service.ts +++ b/src/space/services/tag/tag.service.ts @@ -5,6 +5,7 @@ import { TagEntity, TagRepository, } from '@app/common/modules/space'; +import { TagModel } from '@app/common/modules/space-model'; import { HttpException, HttpStatus, Injectable } from '@nestjs/common'; import { ProductService } from 'src/product/services'; import { CreateTagDto } from 'src/space/dtos'; @@ -25,39 +26,43 @@ export class TagService { subspace?: SubspaceEntity, additionalTags?: CreateTagDto[], ): Promise { - if (!tags.length) { - throw new HttpException('Tags cannot be empty.', HttpStatus.BAD_REQUEST); - } + this.validateTagsInput(tags); - const combinedTags = additionalTags ? [...tags, ...additionalTags] : tags; - const duplicateTags = this.findDuplicateTags(combinedTags); + const combinedTags = this.combineTags(tags, additionalTags); + this.ensureNoDuplicateTags(combinedTags); - if (duplicateTags.length > 0) { - throw new HttpException( - `Duplicate tags found for the same product: ${duplicateTags.join(', ')}`, - HttpStatus.BAD_REQUEST, - ); - } - - const tagEntities = await Promise.all( - tags.map(async (tagDto) => - this.prepareTagEntity(tagDto, queryRunner, space, subspace), - ), - ); try { + const tagEntities = await Promise.all( + tags.map(async (tagDto) => + this.prepareTagEntity(tagDto, queryRunner, space, subspace), + ), + ); + return await queryRunner.manager.save(tagEntities); } catch (error) { - if (error instanceof HttpException) { - throw error; - } - - throw new HttpException( - 'Failed to save tag models due to an unexpected error.', - HttpStatus.INTERNAL_SERVER_ERROR, - ); + throw this.handleUnexpectedError('Failed to save tags', error); } } + async createTagsFromModel( + queryRunner: QueryRunner, + tagModels: TagModel[], + space?: SpaceEntity, + subspace?: SubspaceEntity, + ): Promise { + if (!tagModels?.length) return; + + const tags = tagModels.map((model) => + queryRunner.manager.create(this.tagRepository.target, { + tag: model.tag, + space: space || undefined, + subspace: subspace || undefined, + product: model.product, + }), + ); + + await queryRunner.manager.save(tags); + } async updateTag( tag: ModifyTagDto, queryRunner: QueryRunner, @@ -67,48 +72,94 @@ export class TagService { try { const existingTag = await this.getTagByUuid(tag.uuid); - if (space) { - await this.checkTagReuse(tag.tag, existingTag.product.uuid, space); - } else { + const contextSpace = space ?? subspace?.space; + + if (contextSpace) { await this.checkTagReuse( tag.tag, existingTag.product.uuid, - subspace.space, + contextSpace, ); } - if (tag.tag) { - existingTag.tag = tag.tag; - } - - return await queryRunner.manager.save(existingTag); - } catch (error) { - if (error instanceof HttpException) { - throw error; - } - throw new HttpException( - error.message || 'Failed to update tags', - HttpStatus.INTERNAL_SERVER_ERROR, + return await queryRunner.manager.save( + Object.assign(existingTag, { tag: tag.tag }), ); + } catch (error) { + throw this.handleUnexpectedError('Failed to update tags', error); } } async deleteTags(tagUuids: string[], queryRunner: QueryRunner) { - try { - const deletePromises = tagUuids.map((id) => - queryRunner.manager.softDelete(this.tagRepository.target, id), - ); + if (!tagUuids?.length) return; - await Promise.all(deletePromises); + try { + await Promise.all( + tagUuids.map((id) => + queryRunner.manager.update( + this.tagRepository.target, + { uuid: id }, + { disabled: true }, + ), + ), + ); return { message: 'Tags deleted successfully', tagUuids }; } catch (error) { - if (error instanceof HttpException) { - throw error; - } - throw new HttpException( - error.message || 'Failed to delete tags', - HttpStatus.INTERNAL_SERVER_ERROR, + throw this.handleUnexpectedError('Failed to update tags', error); + } + } + + async modifyTags( + tags: ModifyTagDto[], + queryRunner: QueryRunner, + space?: SpaceEntity, + subspace?: SubspaceEntity, + ): Promise { + if (!tags?.length) return; + + try { + await Promise.all( + tags.map(async (tag) => { + switch (tag.action) { + case ModifyAction.ADD: + await this.createTags( + [{ tag: tag.tag, productUuid: tag.productUuid }], + queryRunner, + space, + subspace, + ); + break; + case ModifyAction.UPDATE: + await this.updateTag(tag, queryRunner, space, subspace); + break; + case ModifyAction.DELETE: + await this.deleteTags([tag.uuid], queryRunner); + + break; + default: + throw new HttpException( + `Invalid action "${tag.action}" provided.`, + HttpStatus.BAD_REQUEST, + ); + } + }), ); + } catch (error) { + throw this.handleUnexpectedError('Failed to modify tags', error); + } + } + + async unlinkModels(tags: TagEntity[], queryRunner: QueryRunner) { + if (!tags?.length) return; + + try { + tags.forEach((tag) => { + tag.model = null; + }); + + await queryRunner.manager.save(tags); + } catch (error) { + throw this.handleUnexpectedError('Failed to unlink tag models', error); } } @@ -128,48 +179,6 @@ export class TagService { return duplicates; } - async modifyTags( - tags: ModifyTagDto[], - queryRunner: QueryRunner, - space?: SpaceEntity, - subspace?: SubspaceEntity, - ): Promise { - try { - for (const tag of tags) { - if (tag.action === ModifyAction.ADD) { - const createTagDto: CreateTagDto = { - tag: tag.tag as string, - productUuid: tag.productUuid as string, - }; - - await this.createTags([createTagDto], queryRunner, space, subspace); - } else if (tag.action === ModifyAction.UPDATE) { - await this.updateTag(tag, queryRunner, space, subspace); - } else if (tag.action === ModifyAction.DELETE) { - await queryRunner.manager.update( - this.tagRepository.target, - { uuid: tag.uuid }, - { disabled: true }, - ); - } else { - throw new HttpException( - `Invalid action "${tag.action}" provided.`, - HttpStatus.BAD_REQUEST, - ); - } - } - } catch (error) { - if (error instanceof HttpException) { - throw error; - } - - throw new HttpException( - `An error occurred while modifying tags: ${error.message}`, - HttpStatus.INTERNAL_SERVER_ERROR, - ); - } - } - private async checkTagReuse( tag: string, productUuid: string, @@ -214,11 +223,11 @@ export class TagService { ); } - if (space) { - await this.checkTagReuse(tagDto.tag, tagDto.productUuid, space); - } else { - await this.checkTagReuse(tagDto.tag, tagDto.productUuid, subspace.space); - } + await this.checkTagReuse( + tagDto.tag, + tagDto.productUuid, + space ?? subspace.space, + ); return queryRunner.manager.create(TagEntity, { tag: tagDto.tag, @@ -233,6 +242,7 @@ export class TagService { where: { uuid }, relations: ['product'], }); + if (!tag) { throw new HttpException( `Tag with ID ${uuid} not found.`, @@ -241,4 +251,39 @@ export class TagService { } return tag; } + + private handleUnexpectedError( + message: string, + error: unknown, + ): HttpException { + if (error instanceof HttpException) throw error; + return new HttpException( + `${message}: ${(error as Error)?.message || 'Unknown error'}`, + HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + + private combineTags( + primaryTags: CreateTagDto[], + additionalTags?: CreateTagDto[], + ): CreateTagDto[] { + return additionalTags ? [...primaryTags, ...additionalTags] : primaryTags; + } + + private ensureNoDuplicateTags(tags: CreateTagDto[]): void { + const duplicates = this.findDuplicateTags(tags); + + if (duplicates.length > 0) { + throw new HttpException( + `Duplicate tags found: ${duplicates.join(', ')}`, + HttpStatus.BAD_REQUEST, + ); + } + } + + private validateTagsInput(tags: CreateTagDto[]): void { + if (!tags?.length) { + return; + } + } } diff --git a/src/space/space.module.ts b/src/space/space.module.ts index a17d95e..7f707e8 100644 --- a/src/space/space.module.ts +++ b/src/space/space.module.ts @@ -43,12 +43,18 @@ import { SceneDeviceRepository } from '@app/common/modules/scene-device/reposito import { ProjectRepository } from '@app/common/modules/project/repositiories'; import { SpaceModelRepository, + SubspaceModelRepository, TagModelRepository, } from '@app/common/modules/space-model'; import { CommunityModule } from 'src/community/community.module'; import { ValidationService } from './services'; import { SubspaceRepository } from '@app/common/modules/space/repositories/subspace.repository'; import { TagService } from './services/tag'; +import { + SpaceModelService, + SubSpaceModelService, + TagModelService, +} from 'src/space-model/services'; @Module({ imports: [ConfigModule, SpaceRepositoryModule, CommunityModule], @@ -88,9 +94,12 @@ import { TagService } from './services/tag'; DeviceStatusFirebaseService, DeviceStatusLogRepository, SceneDeviceRepository, - + SpaceModelService, + SubSpaceModelService, + TagModelService, ProjectRepository, SpaceModelRepository, + SubspaceModelRepository, ], exports: [SpaceService], }) From ba00a8591012533dac0c0d0172a7bdbf8f95ef2b Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Wed, 25 Dec 2024 11:18:52 +0400 Subject: [PATCH 34/45] filter space links --- src/space/services/space-link/space-link.service.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/space/services/space-link/space-link.service.ts b/src/space/services/space-link/space-link.service.ts index 0acece0..a20f76f 100644 --- a/src/space/services/space-link/space-link.service.ts +++ b/src/space/services/space-link/space-link.service.ts @@ -22,6 +22,7 @@ export class SpaceLinkService { where: { startSpace: { uuid: startSpaceId }, endSpace: { uuid: endSpaceId }, + disabled: false, }, }); From f9fe2090767319e5fc578a3025f1130c2e8155ca Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Wed, 25 Dec 2024 11:46:24 +0400 Subject: [PATCH 35/45] use query runner for space link --- .../services/space-link/space-link.service.ts | 22 +++++++++----- src/space/services/space.service.ts | 29 +++++++++++++++++++ 2 files changed, 43 insertions(+), 8 deletions(-) diff --git a/src/space/services/space-link/space-link.service.ts b/src/space/services/space-link/space-link.service.ts index a20f76f..55c40ab 100644 --- a/src/space/services/space-link/space-link.service.ts +++ b/src/space/services/space-link/space-link.service.ts @@ -1,8 +1,10 @@ +import { SpaceEntity, SpaceLinkEntity } from '@app/common/modules/space'; import { SpaceLinkRepository, SpaceRepository, } from '@app/common/modules/space/repositories'; import { HttpException, HttpStatus, Injectable } from '@nestjs/common'; +import { QueryRunner } from 'typeorm'; @Injectable() export class SpaceLinkService { @@ -15,10 +17,11 @@ export class SpaceLinkService { startSpaceId: string, endSpaceId: string, direction: string, + queryRunner: QueryRunner, ): Promise { try { // Check if a link between the startSpace and endSpace already exists - const existingLink = await this.spaceLinkRepository.findOne({ + const existingLink = await queryRunner.manager.findOne(SpaceLinkEntity, { where: { startSpace: { uuid: startSpaceId }, endSpace: { uuid: endSpaceId }, @@ -29,13 +32,16 @@ export class SpaceLinkService { if (existingLink) { // Update the direction if the link exists existingLink.direction = direction; - await this.spaceLinkRepository.save(existingLink); + await queryRunner.manager.save(SpaceLinkEntity, existingLink); return; } - const existingEndSpaceLink = await this.spaceLinkRepository.findOne({ - where: { endSpace: { uuid: endSpaceId } }, - }); + const existingEndSpaceLink = await queryRunner.manager.findOne( + SpaceLinkEntity, + { + where: { endSpace: { uuid: endSpaceId } }, + }, + ); if ( existingEndSpaceLink && @@ -47,7 +53,7 @@ export class SpaceLinkService { } // Find start space - const startSpace = await this.spaceRepository.findOne({ + const startSpace = await queryRunner.manager.findOne(SpaceEntity, { where: { uuid: startSpaceId }, }); @@ -59,7 +65,7 @@ export class SpaceLinkService { } // Find end space - const endSpace = await this.spaceRepository.findOne({ + const endSpace = await queryRunner.manager.findOne(SpaceEntity, { where: { uuid: endSpaceId }, }); @@ -77,7 +83,7 @@ export class SpaceLinkService { direction, }); - await this.spaceLinkRepository.save(spaceLink); + await queryRunner.manager.save(SpaceLinkEntity, spaceLink); } catch (error) { throw new HttpException( error.message || diff --git a/src/space/services/space.service.ts b/src/space/services/space.service.ts index d7ff346..db41d6b 100644 --- a/src/space/services/space.service.ts +++ b/src/space/services/space.service.ts @@ -90,6 +90,7 @@ export class SpaceService { parent.uuid, newSpace.uuid, direction, + queryRunner, ) : Promise.resolve(), subspaces?.length @@ -361,6 +362,34 @@ export class SpaceService { } } + async deleteSpace(params: GetSpaceParam) { + try { + const { communityUuid, spaceUuid, projectUuid } = params; + const space = + await this.validationService.validateSpaceWithinCommunityAndProject( + communityUuid, + projectUuid, + spaceUuid, + ); + const spaces = await this.spaceRepository.find({ + where: { parent: { uuid: spaceUuid }, disabled: false }, + relations: ['parent', 'children'], // Include parent and children relations + }); + console.log(spaces); + // space.disabled = true; + await this.spaceRepository.update({ uuid: space.uuid }, space); + } catch (error) { + if (error instanceof HttpException) { + throw error; + } + + throw new HttpException( + 'An error occurred while deleting the space', + HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + } + private updateSpaceProperties( space: SpaceEntity, updateSpaceDto: UpdateSpaceDto, From f7b75a630a8dd06f2cfca5e423838bc14b9aa52a Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Wed, 25 Dec 2024 16:23:09 +0400 Subject: [PATCH 36/45] disabling space --- .../entities/scene-device.entity.ts | 6 ++ .../modules/scene/entities/scene.entity.ts | 6 ++ src/device/services/device.service.ts | 27 +++++++- src/scene/services/scene.service.ts | 16 +++-- src/space/controllers/space.controller.ts | 4 +- .../services/space-link/space-link.service.ts | 31 +++++++++ src/space/services/space-scene.service.ts | 31 +++++++++ .../services/space-validation.service.ts | 8 +-- src/space/services/space.service.ts | 67 +++++++++++++++---- .../services/subspace/subspace.service.ts | 1 - src/space/space.module.ts | 12 +++- src/users/services/user-space.service.ts | 16 +++++ 12 files changed, 198 insertions(+), 27 deletions(-) diff --git a/libs/common/src/modules/scene-device/entities/scene-device.entity.ts b/libs/common/src/modules/scene-device/entities/scene-device.entity.ts index 9814fdf..a5f1fb5 100644 --- a/libs/common/src/modules/scene-device/entities/scene-device.entity.ts +++ b/libs/common/src/modules/scene-device/entities/scene-device.entity.ts @@ -44,6 +44,12 @@ export class SceneDeviceEntity extends AbstractEntity { @JoinColumn({ name: 'scene_uuid' }) scene: SceneEntity; + @Column({ + nullable: false, + default: false, + }) + public disabled: boolean; + constructor(partial: Partial) { super(); Object.assign(this, partial); diff --git a/libs/common/src/modules/scene/entities/scene.entity.ts b/libs/common/src/modules/scene/entities/scene.entity.ts index 5daa690..86b1beb 100644 --- a/libs/common/src/modules/scene/entities/scene.entity.ts +++ b/libs/common/src/modules/scene/entities/scene.entity.ts @@ -59,6 +59,12 @@ export class SceneEntity extends AbstractEntity { @JoinColumn({ name: 'space_uuid' }) space: SpaceEntity; + @Column({ + nullable: false, + default: false, + }) + public disabled: boolean; + @ManyToOne(() => SceneIconEntity, (icon) => icon.scenesIconEntity, { nullable: false, }) diff --git a/src/device/services/device.service.ts b/src/device/services/device.service.ts index add62c9..4846cd3 100644 --- a/src/device/services/device.service.ts +++ b/src/device/services/device.service.ts @@ -41,7 +41,7 @@ import { import { convertKeysToCamelCase } from '@app/common/helper/camelCaseConverter'; import { DeviceRepository } from '@app/common/modules/device/repositories'; import { PermissionType } from '@app/common/constants/permission-type.enum'; -import { In } from 'typeorm'; +import { In, QueryRunner } from 'typeorm'; import { ProductType } from '@app/common/constants/product-type.enum'; import { SpaceRepository } from '@app/common/modules/space/repositories'; import { DeviceStatusFirebaseService } from '@app/common/firebase/devices-status/services/devices-status.service'; @@ -59,6 +59,7 @@ import { DeviceSceneParamDto } from '../dtos/device.param.dto'; import { BaseResponseDto } from '@app/common/dto/base.response.dto'; import { SuccessResponseDto } from '@app/common/dto/success.response.dto'; import { DeleteSceneFromSceneDeviceDto } from '../dtos/delete.device.dto'; +import { DeviceEntity } from '@app/common/modules/device/entities'; @Injectable() export class DeviceService { @@ -83,6 +84,7 @@ export class DeviceService { secretKey, }); } + async getDeviceByDeviceUuid( deviceUuid: string, withProductDevice: boolean = true, @@ -98,6 +100,29 @@ export class DeviceService { relations, }); } + async deleteDevice( + devices: DeviceEntity[], + orphanSpace: SpaceEntity, + queryRunner: QueryRunner, + ): Promise { + try { + const deviceIds = devices.map((device) => device.uuid); + + if (deviceIds.length > 0) { + await queryRunner.manager + .createQueryBuilder() + .update(DeviceEntity) + .set({ spaceDevice: orphanSpace }) + .whereInIds(deviceIds) + .execute(); + } + } catch (error) { + throw new HttpException( + `Failed to update devices to orphan space: ${error.message}`, + HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + } async getDeviceByDeviceTuyaUuid(deviceTuyaUuid: string) { return await this.deviceRepository.findOne({ diff --git a/src/scene/services/scene.service.ts b/src/scene/services/scene.service.ts index 691fbe1..498d157 100644 --- a/src/scene/services/scene.service.ts +++ b/src/scene/services/scene.service.ts @@ -495,12 +495,16 @@ export class SceneService { const space = await this.getSpaceByUuid(scene.space.uuid); await this.delete(scene.sceneTuyaUuid, space.spaceTuyaUuid); - await this.sceneDeviceRepository.delete({ - scene: { uuid: sceneUuid }, - }); - await this.sceneRepository.delete({ - uuid: sceneUuid, - }); + await this.sceneDeviceRepository.update( + { uuid: sceneUuid }, + { disabled: true }, + ); + await this.sceneRepository.update( + { + uuid: sceneUuid, + }, + { disabled: true }, + ); return new SuccessResponseDto({ message: `Scene with ID ${sceneUuid} deleted successfully`, }); diff --git a/src/space/controllers/space.controller.ts b/src/space/controllers/space.controller.ts index 18d556d..9ada727 100644 --- a/src/space/controllers/space.controller.ts +++ b/src/space/controllers/space.controller.ts @@ -67,8 +67,8 @@ export class SpaceController { description: ControllerRoute.SPACE.ACTIONS.DELETE_SPACE_DESCRIPTION, }) @Delete('/:spaceUuid') - async deleteSpace(@Param() params: GetSpaceParam): Promise { - return this.spaceService.delete(params); + async deleteSpace(@Param() params: GetSpaceParam){ + return await this.spaceService.deleteSpace(params); } @ApiBearerAuth() diff --git a/src/space/services/space-link/space-link.service.ts b/src/space/services/space-link/space-link.service.ts index 55c40ab..8ad797e 100644 --- a/src/space/services/space-link/space-link.service.ts +++ b/src/space/services/space-link/space-link.service.ts @@ -92,4 +92,35 @@ export class SpaceLinkService { ); } } + async deleteSpaceLink( + space: SpaceEntity, + queryRunner: QueryRunner, + ): Promise { + try { + const spaceLinks = await queryRunner.manager.find(SpaceLinkEntity, { + where: [ + { startSpace: space, disabled: false }, + { endSpace: space, disabled: false }, + ], + }); + + if (spaceLinks.length === 0) { + return; + } + + const linkIds = spaceLinks.map((link) => link.uuid); + + await queryRunner.manager + .createQueryBuilder() + .update(SpaceLinkEntity) + .set({ disabled: true }) + .whereInIds(linkIds) + .execute(); + } catch (error) { + throw new HttpException( + `Failed to disable space links for the given space: ${error.message}`, + HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + } } diff --git a/src/space/services/space-scene.service.ts b/src/space/services/space-scene.service.ts index 4e77158..6c6b528 100644 --- a/src/space/services/space-scene.service.ts +++ b/src/space/services/space-scene.service.ts @@ -5,6 +5,9 @@ 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'; +import { SpaceEntity } from '@app/common/modules/space'; +import { QueryRunner } from 'typeorm'; +import { SceneEntity } from '@app/common/modules/scene/entities'; @Injectable() export class SpaceSceneService { @@ -48,4 +51,32 @@ export class SpaceSceneService { } } } + + async deleteScenes( + space: SpaceEntity, + queryRunner: QueryRunner, + ): Promise { + try { + const scenes = await queryRunner.manager.find(SceneEntity, { + where: { space }, + }); + + if (scenes.length === 0) { + return; + } + + const sceneUuids = scenes.map((scene) => scene.uuid); + + await Promise.all( + sceneUuids.map((uuid) => + this.sceneSevice.deleteScene({ sceneUuid: uuid }), + ), + ); + } catch (error) { + throw new HttpException( + `Failed to delete scenes: ${error.message}`, + HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + } } diff --git a/src/space/services/space-validation.service.ts b/src/space/services/space-validation.service.ts index e8bfd32..0fc7673 100644 --- a/src/space/services/space-validation.service.ts +++ b/src/space/services/space-validation.service.ts @@ -1,4 +1,3 @@ -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'; @@ -21,16 +20,17 @@ export class ValidationService { async validateCommunityAndProject( communityUuid: string, projectUuid: string, - ): Promise { - await this.projectService.findOne(projectUuid); + ) { + const project = await this.projectService.findOne(projectUuid); const community = await this.communityService.getCommunityById({ communityUuid, projectUuid, }); - return community.data; + return { community: community.data, project: project }; } + async validateSpaceWithinCommunityAndProject( communityUuid: string, projectUuid: string, diff --git a/src/space/services/space.service.ts b/src/space/services/space.service.ts index db41d6b..685ae99 100644 --- a/src/space/services/space.service.ts +++ b/src/space/services/space.service.ts @@ -24,6 +24,9 @@ import { ValidationService } from './space-validation.service'; import { ORPHAN_SPACE_NAME } from '@app/common/constants/orphan-constant'; import { TagService } from './tag'; import { SpaceModelService } from 'src/space-model/services'; +import { UserSpaceService } from 'src/users/services'; +import { DeviceService } from 'src/device/services'; +import { SpaceSceneService } from './space-scene.service'; @Injectable() export class SpaceService { @@ -35,6 +38,9 @@ export class SpaceService { private readonly validationService: ValidationService, private readonly tagService: TagService, private readonly spaceModelService: SpaceModelService, + private readonly userService: UserSpaceService, + private readonly deviceService: DeviceService, + private readonly sceneService: SpaceSceneService, ) {} async createSpace( @@ -363,33 +369,70 @@ export class SpaceService { } async deleteSpace(params: GetSpaceParam) { + const queryRunner = this.dataSource.createQueryRunner(); + const { spaceUuid } = params; + try { - const { communityUuid, spaceUuid, projectUuid } = params; - const space = - await this.validationService.validateSpaceWithinCommunityAndProject( - communityUuid, - projectUuid, - spaceUuid, - ); + await queryRunner.connect(); + await queryRunner.startTransaction(); + const spaces = await this.spaceRepository.find({ where: { parent: { uuid: spaceUuid }, disabled: false }, - relations: ['parent', 'children'], // Include parent and children relations + relations: [ + 'parent', + 'children', + 'subspaces', + 'tags', + 'subspaces.tags', + 'devices', + ], // Include parent and children relations }); - console.log(spaces); - // space.disabled = true; - await this.spaceRepository.update({ uuid: space.uuid }, space); + //this.disableSpace(space, orphanSpace); + return spaces; } catch (error) { if (error instanceof HttpException) { throw error; } throw new HttpException( - 'An error occurred while deleting the space', + `An error occurred while deleting the space ${error}`, HttpStatus.INTERNAL_SERVER_ERROR, ); } } + async disableSpace(space: SpaceEntity, orphanSpace: SpaceEntity) { + const queryRunner = this.dataSource.createQueryRunner(); + await queryRunner.connect(); + await queryRunner.startTransaction(); + try { + const deleteSubspaceDtos = space.subspaces?.map((subspace) => ({ + subspaceUuid: subspace.uuid, + })); + const deleteSpaceTagsDtos = space.tags?.map((tag) => tag.uuid); + + await this.userService.deleteUserSpace(space.uuid); + await this.subSpaceService.deleteSubspaces( + deleteSubspaceDtos, + queryRunner, + ); + + await this.tagService.deleteTags(deleteSpaceTagsDtos, queryRunner); + + await this.deviceService.deleteDevice( + space.devices, + orphanSpace, + queryRunner, + ); + await this.spaceLinkService.deleteSpaceLink(space, queryRunner); + await this.sceneService.deleteScenes(space, queryRunner); + } catch (error) { + await queryRunner.rollbackTransaction(); + } finally { + await queryRunner.release(); + } + } + private updateSpaceProperties( space: SpaceEntity, updateSpaceDto: UpdateSpaceDto, diff --git a/src/space/services/subspace/subspace.service.ts b/src/space/services/subspace/subspace.service.ts index 01c3bd5..e826071 100644 --- a/src/space/services/subspace/subspace.service.ts +++ b/src/space/services/subspace/subspace.service.ts @@ -19,7 +19,6 @@ import { In, QueryRunner } from 'typeorm'; import { SpaceEntity, SubspaceEntity, - TagEntity, } from '@app/common/modules/space/entities'; import { SubspaceModelEntity } from '@app/common/modules/space-model'; import { ValidationService } from '../space-validation.service'; diff --git a/src/space/space.module.ts b/src/space/space.module.ts index 7f707e8..c3b29d3 100644 --- a/src/space/space.module.ts +++ b/src/space/space.module.ts @@ -28,7 +28,10 @@ import { UserRepository, UserSpaceRepository, } from '@app/common/modules/user/repositories'; -import { DeviceRepository } from '@app/common/modules/device/repositories'; +import { + DeviceRepository, + DeviceUserPermissionRepository, +} from '@app/common/modules/device/repositories'; import { TuyaService } from '@app/common/integrations/tuya/services/tuya.service'; import { ProductRepository } from '@app/common/modules/product/repositories'; import { SceneService } from '../scene/services'; @@ -55,6 +58,9 @@ import { SubSpaceModelService, TagModelService, } from 'src/space-model/services'; +import { UserSpaceService } from 'src/users/services'; +import { UserDevicePermissionService } from 'src/user-device-permission/services'; +import { PermissionTypeRepository } from '@app/common/modules/permission/repositories'; @Module({ imports: [ConfigModule, SpaceRepositoryModule, CommunityModule], @@ -100,6 +106,10 @@ import { ProjectRepository, SpaceModelRepository, SubspaceModelRepository, + UserSpaceService, + UserDevicePermissionService, + DeviceUserPermissionRepository, + PermissionTypeRepository, ], exports: [SpaceService], }) diff --git a/src/users/services/user-space.service.ts b/src/users/services/user-space.service.ts index 1c7ac6e..0ee9af5 100644 --- a/src/users/services/user-space.service.ts +++ b/src/users/services/user-space.service.ts @@ -150,4 +150,20 @@ export class UserSpaceService { await Promise.all(permissionPromises); } + + async deleteUserSpace(spaceUuid: string) { + try { + await this.userSpaceRepository + .createQueryBuilder() + .delete() + .where('spaceUuid = :spaceUuid', { spaceUuid }) + .execute(); + } catch (error) { + console.error(`Error deleting user-space associations: ${error.message}`); + throw new HttpException( + `Failed to delete user-space associations: ${error.message}`, + HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + } } From 77d2e18b3663672ae55300a4a2baf1f5d7fb7953 Mon Sep 17 00:00:00 2001 From: faris Aljohari <83524184+farisaljohari@users.noreply.github.com> Date: Wed, 25 Dec 2024 19:58:37 -0600 Subject: [PATCH 37/45] Make jobTitle and phoneNumber optional in InviteUserEntity and DTO --- .../modules/Invite-user/entities/Invite-user.entity.ts | 4 ++-- src/invite-user/dtos/add.invite-user.dto.ts | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/libs/common/src/modules/Invite-user/entities/Invite-user.entity.ts b/libs/common/src/modules/Invite-user/entities/Invite-user.entity.ts index c93f94b..3eac7d6 100644 --- a/libs/common/src/modules/Invite-user/entities/Invite-user.entity.ts +++ b/libs/common/src/modules/Invite-user/entities/Invite-user.entity.ts @@ -34,7 +34,7 @@ export class InviteUserEntity extends AbstractEntity { email: string; @Column({ - nullable: false, + nullable: true, }) jobTitle: string; @@ -52,7 +52,7 @@ export class InviteUserEntity extends AbstractEntity { }) public lastName: string; @Column({ - nullable: false, + nullable: true, }) public phoneNumber: string; diff --git a/src/invite-user/dtos/add.invite-user.dto.ts b/src/invite-user/dtos/add.invite-user.dto.ts index 94f2e8a..0d9acbc 100644 --- a/src/invite-user/dtos/add.invite-user.dto.ts +++ b/src/invite-user/dtos/add.invite-user.dto.ts @@ -38,16 +38,16 @@ export class AddUserInvitationDto { @ApiProperty({ description: 'The job title of the user', example: 'Software Engineer', - required: true, + required: false, }) @IsString() - @IsNotEmpty() - public jobTitle: string; + @IsOptional() + public jobTitle?: string; @ApiProperty({ description: 'The phone number of the user', example: '+1234567890', - required: true, + required: false, }) @IsString() @IsOptional() From 6aff5e54e924ed6ef7e88804cbebeacc001a53ff Mon Sep 17 00:00:00 2001 From: faris Aljohari <83524184+farisaljohari@users.noreply.github.com> Date: Wed, 25 Dec 2024 19:58:44 -0600 Subject: [PATCH 38/45] Add email and project validation before inviting user --- src/invite-user/services/invite-user.service.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/invite-user/services/invite-user.service.ts b/src/invite-user/services/invite-user.service.ts index 2180d0b..b7b5ad0 100644 --- a/src/invite-user/services/invite-user.service.ts +++ b/src/invite-user/services/invite-user.service.ts @@ -46,6 +46,7 @@ export class InviteUserService { try { const userRepo = queryRunner.manager.getRepository(UserEntity); + await this.checkEmailAndProject({ email }); const user = await userRepo.findOne({ where: { From fcf19645995061107b4741d9f14a2e56ddeec6d9 Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Fri, 27 Dec 2024 09:02:47 +0400 Subject: [PATCH 39/45] delete propogation --- src/space/commands/disable-space.command.ts | 10 ++ src/space/commands/index.ts | 1 + src/space/controllers/space.controller.ts | 4 +- src/space/handlers/disable-space.handler.ts | 100 +++++++++++++ src/space/handlers/index.ts | 1 + .../services/space-validation.service.ts | 10 +- src/space/services/space.service.ts | 132 ++++-------------- src/space/space.module.ts | 7 +- 8 files changed, 154 insertions(+), 111 deletions(-) create mode 100644 src/space/commands/disable-space.command.ts create mode 100644 src/space/commands/index.ts create mode 100644 src/space/handlers/disable-space.handler.ts create mode 100644 src/space/handlers/index.ts diff --git a/src/space/commands/disable-space.command.ts b/src/space/commands/disable-space.command.ts new file mode 100644 index 0000000..1c66f21 --- /dev/null +++ b/src/space/commands/disable-space.command.ts @@ -0,0 +1,10 @@ +import { SpaceEntity } from '@app/common/modules/space'; + +export class DisableSpaceCommand { + constructor( + public readonly param: { + spaceUuid: string; + orphanSpace: SpaceEntity; + }, + ) {} +} diff --git a/src/space/commands/index.ts b/src/space/commands/index.ts new file mode 100644 index 0000000..a9a7b85 --- /dev/null +++ b/src/space/commands/index.ts @@ -0,0 +1 @@ +export * from './disable-space.command'; diff --git a/src/space/controllers/space.controller.ts b/src/space/controllers/space.controller.ts index 9ada727..f54a3ea 100644 --- a/src/space/controllers/space.controller.ts +++ b/src/space/controllers/space.controller.ts @@ -67,8 +67,8 @@ export class SpaceController { description: ControllerRoute.SPACE.ACTIONS.DELETE_SPACE_DESCRIPTION, }) @Delete('/:spaceUuid') - async deleteSpace(@Param() params: GetSpaceParam){ - return await this.spaceService.deleteSpace(params); + async deleteSpace(@Param() params: GetSpaceParam) { + return await this.spaceService.delete(params); } @ApiBearerAuth() diff --git a/src/space/handlers/disable-space.handler.ts b/src/space/handlers/disable-space.handler.ts new file mode 100644 index 0000000..64669fa --- /dev/null +++ b/src/space/handlers/disable-space.handler.ts @@ -0,0 +1,100 @@ +import { SpaceEntity } from '@app/common/modules/space'; +import { HttpException, HttpStatus } from '@nestjs/common'; +import { CommandHandler, ICommandHandler } from '@nestjs/cqrs'; +import { DeviceService } from 'src/device/services'; +import { UserSpaceService } from 'src/users/services'; +import { DataSource } from 'typeorm'; +import { DisableSpaceCommand } from '../commands'; +import { + SubSpaceService, + SpaceLinkService, + SpaceSceneService, +} from '../services'; +import { TagService } from '../services/tag'; + +@CommandHandler(DisableSpaceCommand) +export class DisableSpaceHandler + implements ICommandHandler +{ + constructor( + private readonly subSpaceService: SubSpaceService, + private readonly userService: UserSpaceService, + private readonly tagService: TagService, + private readonly deviceService: DeviceService, + private readonly spaceLinkService: SpaceLinkService, + private readonly sceneService: SpaceSceneService, + private readonly dataSource: DataSource, + ) {} + + async execute(command: DisableSpaceCommand): Promise { + const queryRunner = this.dataSource.createQueryRunner(); + + await queryRunner.connect(); + await queryRunner.startTransaction(); + + try { + const { spaceUuid, orphanSpace } = command.param; + + const space = await queryRunner.manager.findOne(SpaceEntity, { + where: { uuid: spaceUuid, disabled: false }, + relations: [ + 'subspaces', + 'parent', + 'tags', + 'devices', + 'outgoingConnections', + 'incomingConnections', + 'scenes', + 'children', + 'userSpaces', + ], + }); + + if (!space) { + throw new HttpException( + `Space with UUID ${spaceUuid} not found`, + HttpStatus.NOT_FOUND, + ); + } + + if (space.children && space.children.length > 0) { + for (const child of space.children) { + await this.execute( + new DisableSpaceCommand({ spaceUuid: child.uuid, orphanSpace }), + ); + } + } + + const tagUuids = space.tags?.map((tag) => tag.uuid) || []; + const subspaceDtos = + space.subspaces?.map((subspace) => ({ + subspaceUuid: subspace.uuid, + })) || []; + const deletionTasks = [ + this.subSpaceService.deleteSubspaces(subspaceDtos, queryRunner), + this.userService.deleteUserSpace(space.uuid), + this.tagService.deleteTags(tagUuids, queryRunner), + this.deviceService.deleteDevice( + space.devices, + orphanSpace, + queryRunner, + ), + this.spaceLinkService.deleteSpaceLink(space, queryRunner), + this.sceneService.deleteScenes(space, queryRunner), + ]; + + await Promise.all(deletionTasks); + + // Mark space as disabled + space.disabled = true; + await queryRunner.manager.save(space); + + await queryRunner.commitTransaction(); + } catch (error) { + await queryRunner.rollbackTransaction(); + console.error(`Failed to disable space: ${error.message}`); + } finally { + await queryRunner.release(); + } + } +} diff --git a/src/space/handlers/index.ts b/src/space/handlers/index.ts new file mode 100644 index 0000000..ceb1ca9 --- /dev/null +++ b/src/space/handlers/index.ts @@ -0,0 +1 @@ +export * from './disable-space.handler'; diff --git a/src/space/services/space-validation.service.ts b/src/space/services/space-validation.service.ts index 0fc7673..f58f5c3 100644 --- a/src/space/services/space-validation.service.ts +++ b/src/space/services/space-validation.service.ts @@ -30,7 +30,6 @@ export class ValidationService { return { community: community.data, project: project }; } - async validateSpaceWithinCommunityAndProject( communityUuid: string, projectUuid: string, @@ -44,7 +43,14 @@ export class ValidationService { async validateSpace(spaceUuid: string): Promise { const space = await this.spaceRepository.findOne({ where: { uuid: spaceUuid, disabled: false }, - relations: ['subspaces', 'tags'], + relations: [ + 'parent', + 'children', + 'subspaces', + 'tags', + 'subspaces.tags', + 'devices', + ], }); if (!space) { diff --git a/src/space/services/space.service.ts b/src/space/services/space.service.ts index 685ae99..8fba2d9 100644 --- a/src/space/services/space.service.ts +++ b/src/space/services/space.service.ts @@ -21,13 +21,14 @@ import { SpaceLinkService } from './space-link'; import { SubSpaceService } from './subspace'; import { DataSource, Not, QueryRunner } from 'typeorm'; import { ValidationService } from './space-validation.service'; -import { ORPHAN_SPACE_NAME } from '@app/common/constants/orphan-constant'; +import { + ORPHAN_COMMUNITY_NAME, + ORPHAN_SPACE_NAME, +} from '@app/common/constants/orphan-constant'; +import { CommandBus } from '@nestjs/cqrs'; import { TagService } from './tag'; import { SpaceModelService } from 'src/space-model/services'; -import { UserSpaceService } from 'src/users/services'; -import { DeviceService } from 'src/device/services'; -import { SpaceSceneService } from './space-scene.service'; - +import { DisableSpaceCommand } from '../commands'; @Injectable() export class SpaceService { constructor( @@ -38,9 +39,7 @@ export class SpaceService { private readonly validationService: ValidationService, private readonly tagService: TagService, private readonly spaceModelService: SpaceModelService, - private readonly userService: UserSpaceService, - private readonly deviceService: DeviceService, - private readonly sceneService: SpaceSceneService, + private commandBus: CommandBus, ) {} async createSpace( @@ -228,20 +227,17 @@ export class SpaceService { } async delete(params: GetSpaceParam): Promise { - const queryRunner = this.dataSource.createQueryRunner(); - - await queryRunner.connect(); - await queryRunner.startTransaction(); try { const { communityUuid, spaceUuid, projectUuid } = params; - const space = - await this.validationService.validateSpaceWithinCommunityAndProject( + const { project } = + await this.validationService.validateCommunityAndProject( communityUuid, projectUuid, - spaceUuid, ); + const space = await this.validationService.validateSpace(spaceUuid); + if (space.spaceName === ORPHAN_SPACE_NAME) { throw new HttpException( `space ${ORPHAN_SPACE_NAME} cannot be deleted`, @@ -249,37 +245,22 @@ export class SpaceService { ); } - if (space.tags?.length) { - const deleteSpaceTagsDtos = space.tags.map((tag) => tag.uuid); - await this.tagService.deleteTags(deleteSpaceTagsDtos, queryRunner); - } + const orphanSpace = await this.spaceRepository.findOne({ + where: { + community: { + uuid: `${ORPHAN_COMMUNITY_NAME}-${project.name}`, + }, + spaceName: ORPHAN_SPACE_NAME, + }, + }); - if (space.subspaces?.length) { - const deleteSubspaceDtos = space.subspaces.map((subspace) => ({ - subspaceUuid: subspace.uuid, - })); - - await this.subSpaceService.deleteSubspaces( - deleteSubspaceDtos, - queryRunner, - ); - } - - await queryRunner.manager.update( - this.spaceRepository.target, - { uuid: params.spaceUuid }, - { disabled: true }, - ); - - await queryRunner.commitTransaction(); + await this.disableSpace(space, orphanSpace); return new SuccessResponseDto({ message: `Space with ID ${spaceUuid} successfully deleted`, statusCode: HttpStatus.OK, }); } catch (error) { - await queryRunner.rollbackTransaction(); - if (error instanceof HttpException) { throw error; } @@ -287,11 +268,15 @@ export class SpaceService { 'An error occurred while deleting the space', HttpStatus.INTERNAL_SERVER_ERROR, ); - } finally { - await queryRunner.release(); } } + async disableSpace(space: SpaceEntity, orphanSpace: SpaceEntity) { + await this.commandBus.execute( + new DisableSpaceCommand({ spaceUuid: space.uuid, orphanSpace }), + ); + } + async updateSpace( params: GetSpaceParam, updateSpaceDto: UpdateSpaceDto, @@ -368,71 +353,6 @@ export class SpaceService { } } - async deleteSpace(params: GetSpaceParam) { - const queryRunner = this.dataSource.createQueryRunner(); - const { spaceUuid } = params; - - try { - await queryRunner.connect(); - await queryRunner.startTransaction(); - - const spaces = await this.spaceRepository.find({ - where: { parent: { uuid: spaceUuid }, disabled: false }, - relations: [ - 'parent', - 'children', - 'subspaces', - 'tags', - 'subspaces.tags', - 'devices', - ], // Include parent and children relations - }); - //this.disableSpace(space, orphanSpace); - return spaces; - } catch (error) { - if (error instanceof HttpException) { - throw error; - } - - throw new HttpException( - `An error occurred while deleting the space ${error}`, - HttpStatus.INTERNAL_SERVER_ERROR, - ); - } - } - - async disableSpace(space: SpaceEntity, orphanSpace: SpaceEntity) { - const queryRunner = this.dataSource.createQueryRunner(); - await queryRunner.connect(); - await queryRunner.startTransaction(); - try { - const deleteSubspaceDtos = space.subspaces?.map((subspace) => ({ - subspaceUuid: subspace.uuid, - })); - const deleteSpaceTagsDtos = space.tags?.map((tag) => tag.uuid); - - await this.userService.deleteUserSpace(space.uuid); - await this.subSpaceService.deleteSubspaces( - deleteSubspaceDtos, - queryRunner, - ); - - await this.tagService.deleteTags(deleteSpaceTagsDtos, queryRunner); - - await this.deviceService.deleteDevice( - space.devices, - orphanSpace, - queryRunner, - ); - await this.spaceLinkService.deleteSpaceLink(space, queryRunner); - await this.sceneService.deleteScenes(space, queryRunner); - } catch (error) { - await queryRunner.rollbackTransaction(); - } finally { - await queryRunner.release(); - } - } - private updateSpaceProperties( space: SpaceEntity, updateSpaceDto: UpdateSpaceDto, diff --git a/src/space/space.module.ts b/src/space/space.module.ts index c3b29d3..f9a2317 100644 --- a/src/space/space.module.ts +++ b/src/space/space.module.ts @@ -61,9 +61,13 @@ import { import { UserSpaceService } from 'src/users/services'; import { UserDevicePermissionService } from 'src/user-device-permission/services'; import { PermissionTypeRepository } from '@app/common/modules/permission/repositories'; +import { CqrsModule } from '@nestjs/cqrs'; +import { DisableSpaceHandler } from './handlers'; + +export const CommandHandlers = [DisableSpaceHandler]; @Module({ - imports: [ConfigModule, SpaceRepositoryModule, CommunityModule], + imports: [ConfigModule, SpaceRepositoryModule, CommunityModule, CqrsModule], controllers: [ SpaceController, SpaceUserController, @@ -110,6 +114,7 @@ import { PermissionTypeRepository } from '@app/common/modules/permission/reposit UserDevicePermissionService, DeviceUserPermissionRepository, PermissionTypeRepository, + ...CommandHandlers, ], exports: [SpaceService], }) From 30d0866579ff2e62c6663b7b85aa21bdaf083ce4 Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Fri, 27 Dec 2024 09:32:20 +0400 Subject: [PATCH 40/45] subspace check --- .../services/subspace/subspace.service.ts | 31 ++++++++++++++++--- 1 file changed, 26 insertions(+), 5 deletions(-) diff --git a/src/space/services/subspace/subspace.service.ts b/src/space/services/subspace/subspace.service.ts index e826071..08a7ef1 100644 --- a/src/space/services/subspace/subspace.service.ts +++ b/src/space/services/subspace/subspace.service.ts @@ -43,6 +43,12 @@ export class SubSpaceService { queryRunner: QueryRunner, ): Promise { try { + const subspaceNames = subspaceData.map((data) => data.subspaceName); + + await this.checkExistingNamesInSpace( + subspaceNames, + subspaceData[0].space, + ); const subspaces = subspaceData.map((data) => queryRunner.manager.create(this.subspaceRepository.target, data), ); @@ -143,6 +149,10 @@ export class SubSpaceService { ); try { + await this.checkExistingNamesInSpace( + [addSubspaceDto.subspaceName], + space, + ); const newSubspace = this.subspaceRepository.create({ ...addSubspaceDto, space, @@ -450,10 +460,7 @@ export class SubSpaceService { } } - private async validateName( - names: string[], - space: SpaceEntity, - ): Promise { + private async checkForDuplicateNames(names: string[]): Promise { const seenNames = new Set(); const duplicateNames = new Set(); @@ -469,14 +476,20 @@ export class SubSpaceService { HttpStatus.CONFLICT, ); } + } + private async checkExistingNamesInSpace( + names: string[], + space: SpaceEntity, + ): Promise { const existingNames = await this.subspaceRepository.find({ select: ['subspaceName'], where: { - subspaceName: In([...seenNames]), + subspaceName: In(names), space: { uuid: space.uuid, }, + disabled: false, }, }); @@ -490,4 +503,12 @@ export class SubSpaceService { ); } } + + private async validateName( + names: string[], + space: SpaceEntity, + ): Promise { + await this.checkForDuplicateNames(names); + await this.checkExistingNamesInSpace(names, space); + } } From 2e623d945e4334770970787f3aaf9465f21246d4 Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Fri, 27 Dec 2024 09:33:13 +0400 Subject: [PATCH 41/45] remove unused --- src/space/services/space-link/space-link.service.ts | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/src/space/services/space-link/space-link.service.ts b/src/space/services/space-link/space-link.service.ts index 8ad797e..80d18ed 100644 --- a/src/space/services/space-link/space-link.service.ts +++ b/src/space/services/space-link/space-link.service.ts @@ -1,17 +1,11 @@ import { SpaceEntity, SpaceLinkEntity } from '@app/common/modules/space'; -import { - SpaceLinkRepository, - SpaceRepository, -} from '@app/common/modules/space/repositories'; +import { SpaceLinkRepository } from '@app/common/modules/space/repositories'; import { HttpException, HttpStatus, Injectable } from '@nestjs/common'; import { QueryRunner } from 'typeorm'; @Injectable() export class SpaceLinkService { - constructor( - private readonly spaceRepository: SpaceRepository, - private readonly spaceLinkRepository: SpaceLinkRepository, - ) {} + constructor(private readonly spaceLinkRepository: SpaceLinkRepository) {} async saveSpaceLink( startSpaceId: string, From cc5fd68adb78784326227a15afdba7f807d0485c Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Fri, 27 Dec 2024 09:40:05 +0400 Subject: [PATCH 42/45] remove device-tag relation --- .../src/modules/device/entities/device.entity.ts | 9 ++++++++- .../src/modules/space/entities/tag.entity.ts | 15 ++++++++++++++- src/space/services/tag/tag.service.ts | 2 +- 3 files changed, 23 insertions(+), 3 deletions(-) diff --git a/libs/common/src/modules/device/entities/device.entity.ts b/libs/common/src/modules/device/entities/device.entity.ts index 9a75950..bdd1ce5 100644 --- a/libs/common/src/modules/device/entities/device.entity.ts +++ b/libs/common/src/modules/device/entities/device.entity.ts @@ -6,10 +6,11 @@ import { Unique, Index, JoinColumn, + OneToOne, } from 'typeorm'; import { AbstractEntity } from '../../abstract/entities/abstract.entity'; import { DeviceDto, DeviceUserPermissionDto } from '../dtos/device.dto'; -import { SpaceEntity, SubspaceEntity } from '../../space/entities'; +import { SpaceEntity, SubspaceEntity, TagEntity } from '../../space/entities'; import { ProductEntity } from '../../product/entities'; import { UserEntity } from '../../user/entities'; import { DeviceNotificationDto } from '../dtos'; @@ -74,6 +75,11 @@ export class DeviceEntity extends AbstractEntity { @OneToMany(() => SceneDeviceEntity, (sceneDevice) => sceneDevice.device, {}) sceneDevices: SceneDeviceEntity[]; + @OneToOne(() => TagEntity, (tag) => tag.device, { + nullable: true, + }) + tag: TagEntity; + constructor(partial: Partial) { super(); Object.assign(this, partial); @@ -102,6 +108,7 @@ export class DeviceNotificationEntity extends AbstractEntity) { super(); Object.assign(this, partial); diff --git a/libs/common/src/modules/space/entities/tag.entity.ts b/libs/common/src/modules/space/entities/tag.entity.ts index 77e79cc..e7f8599 100644 --- a/libs/common/src/modules/space/entities/tag.entity.ts +++ b/libs/common/src/modules/space/entities/tag.entity.ts @@ -1,10 +1,18 @@ -import { Entity, Column, ManyToOne, JoinColumn, Unique } from 'typeorm'; +import { + Entity, + Column, + ManyToOne, + JoinColumn, + Unique, + OneToOne, +} from 'typeorm'; import { AbstractEntity } from '../../abstract/entities/abstract.entity'; import { ProductEntity } from '../../product/entities'; import { TagDto } from '../dtos'; import { TagModel } from '../../space-model/entities/tag-model.entity'; import { SpaceEntity } from './space.entity'; import { SubspaceEntity } from './subspace'; +import { DeviceEntity } from '../../device/entities'; @Entity({ name: 'tag' }) @Unique(['tag', 'product', 'space', 'subspace']) @@ -36,4 +44,9 @@ export class TagEntity extends AbstractEntity { default: false, }) public disabled: boolean; + + @OneToOne(() => DeviceEntity, (device) => device.tag, { + nullable: true, + }) + device: DeviceEntity; } diff --git a/src/space/services/tag/tag.service.ts b/src/space/services/tag/tag.service.ts index c9ccc1c..b89dc9c 100644 --- a/src/space/services/tag/tag.service.ts +++ b/src/space/services/tag/tag.service.ts @@ -99,7 +99,7 @@ export class TagService { queryRunner.manager.update( this.tagRepository.target, { uuid: id }, - { disabled: true }, + { disabled: true, device: null }, ), ), ); From 0d539883f16ff8699380c6a0cfd927ee3a4ba047 Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Fri, 27 Dec 2024 09:57:37 +0400 Subject: [PATCH 43/45] delete subspace devices --- .../services/space-validation.service.ts | 1 + .../subspace/subspace-device.service.ts | 26 +++++++++++++++++++ .../services/subspace/subspace.service.ts | 16 +++++++++--- 3 files changed, 39 insertions(+), 4 deletions(-) diff --git a/src/space/services/space-validation.service.ts b/src/space/services/space-validation.service.ts index f58f5c3..89a539a 100644 --- a/src/space/services/space-validation.service.ts +++ b/src/space/services/space-validation.service.ts @@ -49,6 +49,7 @@ export class ValidationService { 'subspaces', 'tags', 'subspaces.tags', + 'subspaces.devices', 'devices', ], }); diff --git a/src/space/services/subspace/subspace-device.service.ts b/src/space/services/subspace/subspace-device.service.ts index d6f6320..130da1d 100644 --- a/src/space/services/subspace/subspace-device.service.ts +++ b/src/space/services/subspace/subspace-device.service.ts @@ -9,6 +9,8 @@ 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'; +import { In, QueryRunner } from 'typeorm'; +import { DeviceEntity } from '@app/common/modules/device/entities'; @Injectable() export class SubspaceDeviceService { @@ -173,6 +175,30 @@ export class SubspaceDeviceService { return device; } + async deleteSubspaceDevices( + devices: DeviceEntity[], + queryRunner: QueryRunner, + ): Promise { + const deviceUuids = devices.map((device) => device.uuid); + + try { + if (deviceUuids.length === 0) { + return; + } + + await queryRunner.manager.update( + this.deviceRepository.target, + { uuid: In(deviceUuids) }, + { subspace: null }, + ); + } catch (error) { + throw new HttpException( + `Failed to delete devices with IDs ${deviceUuids.join(', ')}: ${error.message}`, + HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + } + 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 08a7ef1..dbaa3b3 100644 --- a/src/space/services/subspace/subspace.service.ts +++ b/src/space/services/subspace/subspace.service.ts @@ -25,6 +25,7 @@ import { ValidationService } from '../space-validation.service'; import { SubspaceRepository } from '@app/common/modules/space/repositories/subspace.repository'; import { TagService } from '../tag'; import { ModifyAction } from '@app/common/constants/modify-action.enum'; +import { SubspaceDeviceService } from './subspace-device.service'; @Injectable() export class SubSpaceService { @@ -32,6 +33,7 @@ export class SubSpaceService { private readonly subspaceRepository: SubspaceRepository, private readonly validationService: ValidationService, private readonly tagService: TagService, + public readonly deviceService: SubspaceDeviceService, ) {} async createSubspaces( @@ -280,7 +282,7 @@ export class SubSpaceService { const deleteResults: { uuid: string }[] = []; for (const dto of deleteDtos) { - const subspaceModel = await this.findOne(dto.subspaceUuid); + const subspace = await this.findOne(dto.subspaceUuid); await queryRunner.manager.update( this.subspaceRepository.target, @@ -288,8 +290,8 @@ export class SubSpaceService { { disabled: true }, ); - if (subspaceModel.tags?.length) { - const modifyTagDtos = subspaceModel.tags.map((tag) => ({ + if (subspace.tags?.length) { + const modifyTagDtos = subspace.tags.map((tag) => ({ uuid: tag.uuid, action: ModifyAction.DELETE, })); @@ -297,10 +299,16 @@ export class SubSpaceService { modifyTagDtos, queryRunner, null, - subspaceModel, + subspace, ); } + if (subspace.devices) + await this.deviceService.deleteSubspaceDevices( + subspace.devices, + queryRunner, + ); + deleteResults.push({ uuid: dto.subspaceUuid }); } From a96dccd19d78e7de69310f7d604fad63d1368f87 Mon Sep 17 00:00:00 2001 From: faris Aljohari <83524184+farisaljohari@users.noreply.github.com> Date: Sun, 29 Dec 2024 22:31:56 -0600 Subject: [PATCH 44/45] Add endpoint to get users by project --- libs/common/src/constants/controller-route.ts | 6 ++ src/project/controllers/project.controller.ts | 14 ++++ src/project/project.module.ts | 4 + src/project/services/project.service.ts | 78 +++++++++++++++++++ 4 files changed, 102 insertions(+) diff --git a/libs/common/src/constants/controller-route.ts b/libs/common/src/constants/controller-route.ts index 7ef8808..80088aa 100644 --- a/libs/common/src/constants/controller-route.ts +++ b/libs/common/src/constants/controller-route.ts @@ -21,6 +21,11 @@ export class ControllerRoute { public static readonly DELETE_PROJECT_SUMMARY = 'Delete a project'; public static readonly DELETE_PROJECT_DESCRIPTION = 'This endpoint deletes an existing project by its unique identifier (UUID).'; + + public static readonly GET_USERS_BY_PROJECT_SUMMARY = + 'Get users by project'; + public static readonly GET_USERS_BY_PROJECT_DESCRIPTION = + 'This endpoint retrieves all users associated with a specific project.'; }; }; @@ -733,6 +738,7 @@ export class ControllerRoute { public static readonly CREATE_USER_INVITATION_DESCRIPTION = 'This endpoint creates an invitation for a user to assign to role and spaces.'; + public static readonly CHECK_EMAIL_SUMMARY = 'Check email'; public static readonly CHECK_EMAIL_DESCRIPTION = diff --git a/src/project/controllers/project.controller.ts b/src/project/controllers/project.controller.ts index a39ced1..c888acb 100644 --- a/src/project/controllers/project.controller.ts +++ b/src/project/controllers/project.controller.ts @@ -86,4 +86,18 @@ export class ProjectController { async findOne(@Param() params: GetProjectParam): Promise { return this.projectService.getProject(params.projectUuid); } + + @ApiBearerAuth() + @UseGuards(JwtAuthGuard) + @ApiOperation({ + summary: ControllerRoute.PROJECT.ACTIONS.GET_USERS_BY_PROJECT_SUMMARY, + description: + ControllerRoute.PROJECT.ACTIONS.GET_USERS_BY_PROJECT_DESCRIPTION, + }) + @Get(':projectUuid/users') + async findUsersByProject( + @Param() params: GetProjectParam, + ): Promise { + return this.projectService.getUsersByProject(params.projectUuid); + } } diff --git a/src/project/project.module.ts b/src/project/project.module.ts index 8711499..0afbd1c 100644 --- a/src/project/project.module.ts +++ b/src/project/project.module.ts @@ -6,6 +6,8 @@ import { ProjectRepository } from '@app/common/modules/project/repositiories'; import { CreateOrphanSpaceHandler } from './handler'; import { SpaceRepository } from '@app/common/modules/space'; import { CommunityRepository } from '@app/common/modules/community/repositories'; +import { InviteUserRepository } from '@app/common/modules/Invite-user/repositiories'; +import { UserRepository } from '@app/common/modules/user/repositories'; const CommandHandlers = [CreateOrphanSpaceHandler]; @@ -19,6 +21,8 @@ const CommandHandlers = [CreateOrphanSpaceHandler]; CommunityRepository, ProjectService, ProjectRepository, + InviteUserRepository, + UserRepository, ], exports: [ProjectService, CqrsModule], }) diff --git a/src/project/services/project.service.ts b/src/project/services/project.service.ts index 75aa67a..1d5e609 100644 --- a/src/project/services/project.service.ts +++ b/src/project/services/project.service.ts @@ -12,11 +12,17 @@ import { ProjectDto } from '@app/common/modules/project/dtos'; import { PageResponse } from '@app/common/dto/pagination.response.dto'; import { CommandBus } from '@nestjs/cqrs'; import { CreateOrphanSpaceCommand } from '../command/create-orphan-space-command'; +import { InviteUserRepository } from '@app/common/modules/Invite-user/repositiories'; +import { UserRepository } from '@app/common/modules/user/repositories'; +import { UserStatusEnum } from '@app/common/constants/user-status.enum'; +import { RoleType } from '@app/common/constants/role.type.enum'; @Injectable() export class ProjectService { constructor( private readonly projectRepository: ProjectRepository, + private readonly inviteUserRepository: InviteUserRepository, + private readonly userRepository: UserRepository, private commandBus: CommandBus, ) {} @@ -181,6 +187,78 @@ export class ProjectService { } } + async getUsersByProject(uuid: string): Promise { + try { + // Fetch invited users + const invitedUsers = await this.inviteUserRepository.find({ + where: { project: { uuid }, isActive: true }, + select: [ + 'firstName', + 'lastName', + 'email', + 'createdAt', + 'status', + 'phoneNumber', + 'jobTitle', + 'invitedBy', + 'isEnabled', + ], + relations: ['roleType'], + }); + + // Fetch project users + const users = await this.userRepository.find({ + where: { project: { uuid }, isActive: true }, + select: ['firstName', 'lastName', 'email', 'createdAt'], + relations: ['roleType'], + }); + + // Combine both arrays + const allUsers = [...users, ...invitedUsers]; + + const normalizedUsers = allUsers.map((user) => { + const createdAt = new Date(user.createdAt); + const createdDate = createdAt.toLocaleDateString(); + const createdTime = createdAt.toLocaleTimeString(); + + // Normalize user properties + const normalizedProps = this.normalizeUserProperties(user); + + // Return the normalized user object + return { + ...user, + createdDate, + createdTime, + ...normalizedProps, + }; + }); + + return new SuccessResponseDto({ + message: `Users in project with ID ${uuid} retrieved successfully`, + data: normalizedUsers, + statusCode: HttpStatus.OK, + }); + } catch (error) { + if (error instanceof HttpException) { + throw error; + } + + throw new HttpException( + `An error occurred while retrieving users in the project with id ${uuid}`, + HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + } + normalizeUserProperties(user: any) { + return { + status: user.status ?? UserStatusEnum.ACTIVE, + invitedBy: user.invitedBy ?? RoleType.SPACE_MEMBER, + isEnabled: user.isEnabled ?? true, + phoneNumber: user.phoneNumber ?? null, + jobTitle: user.jobTitle ?? null, + roleType: user.roleType?.type ?? null, + }; + } async findOne(uuid: string): Promise { const project = await this.projectRepository.findOne({ where: { uuid } }); return project; From 6a89a17ce92b07e45a44308ac5b2f900bbdd3ef5 Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Mon, 30 Dec 2024 10:06:33 +0400 Subject: [PATCH 45/45] disable space --- src/space-model/commands/index.ts | 1 + .../propagate-space-model-deletion.command.ts | 9 + .../propogate-subspace-update-command.ts | 12 +- src/space-model/handlers/index.ts | 1 + .../handlers/propate-subspace-handler.ts | 340 +++++++++++------- .../propogate-space-model-deletion.handler.ts | 57 +++ src/space-model/interfaces/index.ts | 3 +- .../interfaces/modify-subspace.interface.ts | 24 ++ .../services/space-model.service.ts | 34 +- .../subspace/subspace-model.service.ts | 57 ++- src/space-model/services/tag-model.service.ts | 54 ++- src/space-model/space-model.module.ts | 40 ++- .../interfaces/add-subspace.interface.ts | 5 + src/space/interfaces/index.ts | 0 src/space/services/space.service.ts | 33 +- .../services/subspace/subspace.service.ts | 50 ++- src/space/services/tag/tag.service.ts | 61 ++++ 17 files changed, 604 insertions(+), 177 deletions(-) create mode 100644 src/space-model/commands/propagate-space-model-deletion.command.ts create mode 100644 src/space-model/handlers/propogate-space-model-deletion.handler.ts create mode 100644 src/space-model/interfaces/modify-subspace.interface.ts create mode 100644 src/space/interfaces/add-subspace.interface.ts create mode 100644 src/space/interfaces/index.ts diff --git a/src/space-model/commands/index.ts b/src/space-model/commands/index.ts index 716d458..da760ea 100644 --- a/src/space-model/commands/index.ts +++ b/src/space-model/commands/index.ts @@ -1 +1,2 @@ export * from './propogate-subspace-update-command'; +export * from './propagate-space-model-deletion.command'; diff --git a/src/space-model/commands/propagate-space-model-deletion.command.ts b/src/space-model/commands/propagate-space-model-deletion.command.ts new file mode 100644 index 0000000..0cacd5b --- /dev/null +++ b/src/space-model/commands/propagate-space-model-deletion.command.ts @@ -0,0 +1,9 @@ +import { SpaceModelEntity } from '@app/common/modules/space-model'; + +export class PropogateDeleteSpaceModelCommand { + constructor( + public readonly param: { + spaceModel: SpaceModelEntity; + }, + ) {} +} diff --git a/src/space-model/commands/propogate-subspace-update-command.ts b/src/space-model/commands/propogate-subspace-update-command.ts index 96276ee..3b8ccdb 100644 --- a/src/space-model/commands/propogate-subspace-update-command.ts +++ b/src/space-model/commands/propogate-subspace-update-command.ts @@ -1,6 +1,12 @@ import { ICommand } from '@nestjs/cqrs'; -import { IModifySubspaceModelInterface } from '../interfaces'; +import { SpaceModelEntity } from '@app/common/modules/space-model'; +import { ModifyspaceModelPayload } from '../interfaces'; -export class PropogateSubspaceCommand implements ICommand { - constructor(public readonly param: IModifySubspaceModelInterface) {} +export class PropogateUpdateSpaceModelCommand implements ICommand { + constructor( + public readonly param: { + spaceModel: SpaceModelEntity; + modifiedSpaceModels: ModifyspaceModelPayload; + }, + ) {} } diff --git a/src/space-model/handlers/index.ts b/src/space-model/handlers/index.ts index 7dfa7f5..d7bb550 100644 --- a/src/space-model/handlers/index.ts +++ b/src/space-model/handlers/index.ts @@ -1 +1,2 @@ export * from './propate-subspace-handler'; +export * from './propogate-space-model-deletion.handler'; diff --git a/src/space-model/handlers/propate-subspace-handler.ts b/src/space-model/handlers/propate-subspace-handler.ts index 7d205f9..f59aeb2 100644 --- a/src/space-model/handlers/propate-subspace-handler.ts +++ b/src/space-model/handlers/propate-subspace-handler.ts @@ -1,170 +1,250 @@ import { CommandHandler, ICommandHandler } from '@nestjs/cqrs'; -import { PropogateSubspaceCommand } from '../commands'; -import { Logger } from '@nestjs/common'; +import { PropogateUpdateSpaceModelCommand } from '../commands'; import { SpaceEntity, SpaceRepository } from '@app/common/modules/space'; import { SubspaceRepository } from '@app/common/modules/space/repositories/subspace.repository'; import { - IDeletedSubsaceModelInterface, - IUpdateSubspaceModelInterface, -} from '../interfaces'; + SpaceModelEntity, + SubspaceModelEntity, + TagModel, +} from '@app/common/modules/space-model'; +import { DataSource, QueryRunner } from 'typeorm'; +import { SubSpaceService } from 'src/space/services'; +import { TagService } from 'src/space/services/tag'; +import { TagModelService } from '../services'; +import { UpdatedSubspaceModelPayload } from '../interfaces'; +import { ModifyAction } from '@app/common/constants/modify-action.enum'; +import { ModifySubspaceDto } from 'src/space/dtos'; -@CommandHandler(PropogateSubspaceCommand) -export class PropogateSubspaceHandler - implements ICommandHandler +@CommandHandler(PropogateUpdateSpaceModelCommand) +export class PropogateUpdateSpaceModelHandler + implements ICommandHandler { - private readonly logger = new Logger(PropogateSubspaceHandler.name); - constructor( private readonly spaceRepository: SpaceRepository, private readonly subspaceRepository: SubspaceRepository, + private readonly dataSource: DataSource, + private readonly subSpaceService: SubSpaceService, + private readonly tagService: TagService, + private readonly tagModelService: TagModelService, ) {} - async execute(command: PropogateSubspaceCommand): Promise { + async execute(command: PropogateUpdateSpaceModelCommand): Promise { + const { spaceModel, modifiedSpaceModels } = command.param; + const queryRunner = this.dataSource.createQueryRunner(); + try { - const newSubspaceModels = command.param?.new; - const updateSubspaceModels = command.param?.update; - const deleteSubspaceModels = command.param?.delete; - - if (!newSubspaceModels && !updateSubspaceModels) { - this.logger.warn('No new or updated subspace models provided.'); - return; - } - - const spaceModelUuid = command.param.spaceModelUuid; - - if (!spaceModelUuid) { - this.logger.error( - 'Space model UUID is missing in the command parameters.', + await queryRunner.connect(); + await queryRunner.startTransaction(); + const spaces = await this.spaceRepository.find({ + where: { spaceModel }, + }); + if ( + modifiedSpaceModels.modifiedSubspaceModels.addedSubspaceModels.length > + 0 + ) { + await this.addSubspaceModels( + modifiedSpaceModels.modifiedSubspaceModels.addedSubspaceModels, + spaces, + queryRunner, ); - return; + } else if ( + modifiedSpaceModels.modifiedSubspaceModels.updatedSubspaceModels + .length > 0 + ) { + await this.updateSubspaceModels( + modifiedSpaceModels.modifiedSubspaceModels.updatedSubspaceModels, + queryRunner, + ); + } + if ( + modifiedSpaceModels.modifiedSubspaceModels.deletedSubspaceModels?.length + ) { + const dtos: ModifySubspaceDto[] = + modifiedSpaceModels.modifiedSubspaceModels.deletedSubspaceModels.map( + (model) => ({ + action: ModifyAction.DELETE, + uuid: model, + }), + ); + await this.subSpaceService.modifySubSpace(dtos, queryRunner); } - const spaces = await this.getSpacesByModel(spaceModelUuid); - - if (spaces.length === 0) { - this.logger.warn(`No spaces found for model UUID: ${spaceModelUuid}`); - return; + if (modifiedSpaceModels.modifiedTags.added.length > 0) { + await this.createTags( + modifiedSpaceModels.modifiedTags.added, + queryRunner, + null, + spaceModel, + ); } - if (newSubspaceModels && newSubspaceModels.length > 0) { - await this.processNewSubspaces(newSubspaceModels, spaces); + if (modifiedSpaceModels.modifiedTags.updated.length > 0) { + await this.updateTags( + modifiedSpaceModels.modifiedTags.updated, + queryRunner, + ); } - if (updateSubspaceModels && updateSubspaceModels.length > 0) { - await this.updateSubspaces(updateSubspaceModels); + if (modifiedSpaceModels.modifiedTags.deleted.length > 0) { + await this.deleteTags( + modifiedSpaceModels.modifiedTags.deleted, + queryRunner, + ); } - if (deleteSubspaceModels && deleteSubspaceModels.length > 0) { - await this.deleteSubspaces(deleteSubspaceModels); - } + await queryRunner.commitTransaction(); } catch (error) { - this.logger.error( - 'Error in PropogateSubspaceHandler execution', - error.stack, + await queryRunner.rollbackTransaction(); + throw error; + } finally { + await queryRunner.release(); + } + } + + async addSubspaceModels( + subspaceModels: SubspaceModelEntity[], + spaces: SpaceEntity[], + queryRunner: QueryRunner, + ) { + for (const space of spaces) { + await this.subSpaceService.createSubSpaceFromModel( + subspaceModels, + space, + queryRunner, ); } } - private async updateSubspaces( - models: IUpdateSubspaceModelInterface[], + async updateSubspaceModels( + subspaceModels: UpdatedSubspaceModelPayload[], + queryRunner: QueryRunner, ): Promise { - try { - const updatePromises = []; + const subspaceUpdatePromises = subspaceModels.map(async (model) => { + const { + updated: tagsToUpdate, + deleted: tagsToDelete, + added: tagsToAdd, + } = model.modifiedTags; - for (const model of models) { - const { uuid: subspaceModelUuid, subspaceName } = model; + // Perform tag operations concurrently + await Promise.all([ + tagsToUpdate?.length && this.updateTags(tagsToUpdate, queryRunner), + tagsToDelete?.length && this.deleteTags(tagsToDelete, queryRunner), + tagsToAdd?.length && + this.createTags( + tagsToAdd, + queryRunner, + model.subspaceModelUuid, + null, + ), + ]); - if (subspaceName) { - updatePromises.push( - this.subspaceRepository - .createQueryBuilder() - .update() - .set({ subspaceName }) - .where('subSpaceModelUuid = :uuid', { uuid: subspaceModelUuid }) - .execute(), - ); - } + // Update subspace names + const subspaces = await queryRunner.manager.find( + this.subspaceRepository.target, + { + where: { + subSpaceModel: { + uuid: model.subspaceModelUuid, + }, + }, + }, + ); + + if (subspaces.length > 0) { + const updateSubspacePromises = subspaces.map((subspace) => + queryRunner.manager.update( + this.subspaceRepository.target, + { uuid: subspace.uuid }, + { subspaceName: model.subspaceName }, + ), + ); + await Promise.all(updateSubspacePromises); } - - await Promise.all(updatePromises); - } catch (error) { - this.logger.error('Error in updateSubspaces method', error.stack); - } - } - - private async deleteSubspaces(models: IDeletedSubsaceModelInterface[]) { - try { - const updatePromises = []; - - for (const model of models) { - const { uuid: subspaceModelUuid } = model; - - if (subspaceModelUuid) { - updatePromises.push( - this.subspaceRepository - .createQueryBuilder() - .update() - .set({ disabled: true }) - .where('subSpaceModelUuid = :uuid', { uuid: subspaceModelUuid }) - .execute(), - ); - } - } - - await Promise.all(updatePromises); - } catch (error) { - this.logger.error('Error in delete subspace models', error.stack); - } - } - - private async processNewSubspaces( - newSubspaceModels: any[], - spaces: SpaceEntity[], - ) { - for (const newSubspaceModel of newSubspaceModels) { - for (const space of spaces) { - try { - await this.createSubspace(newSubspaceModel, space); - - if (newSubspaceModel.productModels?.length > 0) { - } - } catch (error) { - this.logger.error( - `Failed to create subspace for space ID: ${space.uuid}`, - error.stack, - ); - } - } - } - } - - private async createSubspace(newSubspaceModel: any, space: SpaceEntity) { - const subspace = this.subspaceRepository.create({ - subspaceName: newSubspaceModel.subspaceModel.subspaceName, - space, - subSpaceModel: newSubspaceModel.subspaceModel, }); - const createdSubspace = await this.subspaceRepository.save(subspace); - this.logger.log( - `Subspace created for space ${space.uuid} with name ${createdSubspace.subspaceName}`, + // Wait for all subspace model updates to complete + await Promise.all(subspaceUpdatePromises); + } + async updateTags(models: TagModel[], queryRunner: QueryRunner) { + if (!models?.length) return; + + const updatePromises = models.map((model) => + this.tagService.updateTagsFromModel(model, queryRunner), ); - return createdSubspace; + await Promise.all(updatePromises); } - private async getSpacesByModel(uuid: string): Promise { - try { - return await this.spaceRepository.find({ - where: { - spaceModel: { uuid }, + async deleteTags(uuids: string[], queryRunner: QueryRunner) { + const deletePromises = uuids.map((uuid) => + this.tagService.deleteTagFromModel(uuid, queryRunner), + ); + await Promise.all(deletePromises); + } + + async createTags( + models: TagModel[], + queryRunner: QueryRunner, + subspaceModelUuid?: string, + spaceModel?: SpaceModelEntity, + ): Promise { + if (!models.length) { + return; + } + + if (subspaceModelUuid) { + await this.processSubspaces(subspaceModelUuid, models, queryRunner); + } + + if (spaceModel) { + await this.processSpaces(spaceModel.uuid, models, queryRunner); + } + } + + private async processSubspaces( + subspaceModelUuid: string, + models: TagModel[], + queryRunner: QueryRunner, + ): Promise { + const subspaces = await this.subspaceRepository.find({ + where: { + subSpaceModel: { + uuid: subspaceModelUuid, }, - }); - } catch (error) { - this.logger.error( - `Failed to fetch spaces for model UUID: ${uuid}`, - error.stack, + }, + }); + + if (subspaces.length > 0) { + const subspacePromises = subspaces.map((subspace) => + this.tagService.createTagsFromModel( + queryRunner, + models, + null, + subspace, + ), ); - throw error; + await Promise.all(subspacePromises); + } + } + + private async processSpaces( + spaceModelUuid: string, + models: TagModel[], + queryRunner: QueryRunner, + ): Promise { + const spaces = await this.spaceRepository.find({ + where: { + spaceModel: { + uuid: spaceModelUuid, + }, + }, + }); + + if (spaces.length > 0) { + const spacePromises = spaces.map((space) => + this.tagService.createTagsFromModel(queryRunner, models, space, null), + ); + await Promise.all(spacePromises); } } } diff --git a/src/space-model/handlers/propogate-space-model-deletion.handler.ts b/src/space-model/handlers/propogate-space-model-deletion.handler.ts new file mode 100644 index 0000000..acd690e --- /dev/null +++ b/src/space-model/handlers/propogate-space-model-deletion.handler.ts @@ -0,0 +1,57 @@ +import { CommandHandler, ICommandHandler } from '@nestjs/cqrs'; +import { Logger } from '@nestjs/common'; + +import { PropogateDeleteSpaceModelCommand } from '../commands'; +import { SpaceRepository } from '@app/common/modules/space'; +import { SpaceService } from '../../space/services/space.service'; +import { DataSource } from 'typeorm'; + +@CommandHandler(PropogateDeleteSpaceModelCommand) +export class PropogateDeleteSpaceModelHandler + implements ICommandHandler +{ + private readonly logger = new Logger(PropogateDeleteSpaceModelHandler.name); + + constructor( + private readonly spaceRepository: SpaceRepository, + private readonly spaceService: SpaceService, + private readonly dataSource: DataSource, + ) {} + + async execute(command: PropogateDeleteSpaceModelCommand): Promise { + const { spaceModel } = command.param; + const queryRunner = this.dataSource.createQueryRunner(); + + try { + await queryRunner.connect(); + await queryRunner.startTransaction(); + const spaces = await this.spaceRepository.find({ + where: { + spaceModel: { + uuid: spaceModel.uuid, + }, + }, + relations: ['subspaces', 'tags', 'subspaces.tags'], + }); + + for (const space of spaces) { + try { + await this.spaceService.unlinkSpaceFromModel(space, queryRunner); + } catch (innerError) { + this.logger.error( + `Error unlinking space model for space with UUID ${space.uuid}:`, + innerError.stack || innerError, + ); + } + } + } catch (error) { + await queryRunner.rollbackTransaction(); + this.logger.error( + 'Error propagating delete space model:', + error.stack || error, + ); + } finally { + await queryRunner.release(); + } + } +} diff --git a/src/space-model/interfaces/index.ts b/src/space-model/interfaces/index.ts index 606eb46..0bacfcd 100644 --- a/src/space-model/interfaces/index.ts +++ b/src/space-model/interfaces/index.ts @@ -1 +1,2 @@ -export * from './update-subspace.interface' \ No newline at end of file +export * from './update-subspace.interface'; +export * from './modify-subspace.interface'; diff --git a/src/space-model/interfaces/modify-subspace.interface.ts b/src/space-model/interfaces/modify-subspace.interface.ts new file mode 100644 index 0000000..8969baf --- /dev/null +++ b/src/space-model/interfaces/modify-subspace.interface.ts @@ -0,0 +1,24 @@ +import { SubspaceModelEntity, TagModel } from '@app/common/modules/space-model'; + +export interface ModifyspaceModelPayload { + modifiedSubspaceModels?: ModifySubspaceModelPayload; + modifiedTags?: ModifiedTagsModelPayload; +} + +export interface ModifySubspaceModelPayload { + addedSubspaceModels?: SubspaceModelEntity[]; + updatedSubspaceModels?: UpdatedSubspaceModelPayload[]; + deletedSubspaceModels?: string[]; +} + +export interface UpdatedSubspaceModelPayload { + subspaceName?: string; + modifiedTags?: ModifiedTagsModelPayload; + subspaceModelUuid: string; +} + +export interface ModifiedTagsModelPayload { + added?: TagModel[]; + updated?: TagModel[]; + deleted?: string[]; +} diff --git a/src/space-model/services/space-model.service.ts b/src/space-model/services/space-model.service.ts index 7704fd1..89e9edb 100644 --- a/src/space-model/services/space-model.service.ts +++ b/src/space-model/services/space-model.service.ts @@ -19,6 +19,12 @@ import { ProjectService } from 'src/project/services'; import { ProjectEntity } from '@app/common/modules/project/entities'; import { TagModelService } from './tag-model.service'; import { BaseResponseDto } from '@app/common/dto/base.response.dto'; +import { CommandBus } from '@nestjs/cqrs'; +import { PropogateUpdateSpaceModelCommand } from '../commands'; +import { + ModifiedTagsModelPayload, + ModifySubspaceModelPayload, +} from '../interfaces'; @Injectable() export class SpaceModelService { @@ -28,6 +34,7 @@ export class SpaceModelService { private readonly projectService: ProjectService, private readonly subSpaceModelService: SubSpaceModelService, private readonly tagModelService: TagModelService, + private commandBus: CommandBus, ) {} async createSpaceModel( @@ -136,6 +143,9 @@ export class SpaceModelService { const queryRunner = this.dataSource.createQueryRunner(); await queryRunner.connect(); await queryRunner.startTransaction(); + + let modifiedSubspaceModels: ModifySubspaceModelPayload = {}; + let modifiedTagsModelPayload: ModifiedTagsModelPayload = {}; try { const { modelName } = dto; if (modelName) { @@ -145,22 +155,34 @@ export class SpaceModelService { } if (dto.subspaceModels) { - await this.subSpaceModelService.modifySubSpaceModels( - dto.subspaceModels, - spaceModel, - queryRunner, - ); + modifiedSubspaceModels = + await this.subSpaceModelService.modifySubSpaceModels( + dto.subspaceModels, + spaceModel, + queryRunner, + ); } if (dto.tags) { - await this.tagModelService.modifyTags( + modifiedTagsModelPayload = await this.tagModelService.modifyTags( dto.tags, queryRunner, spaceModel, ); } + await queryRunner.commitTransaction(); + await this.commandBus.execute( + new PropogateUpdateSpaceModelCommand({ + spaceModel: spaceModel, + modifiedSpaceModels: { + modifiedSubspaceModels, + modifiedTags: modifiedTagsModelPayload, + }, + }), + ); + return new SuccessResponseDto({ message: 'SpaceModel updated successfully', data: spaceModel, diff --git a/src/space-model/services/subspace/subspace-model.service.ts b/src/space-model/services/subspace/subspace-model.service.ts index 6b8cb9f..e8a7bd0 100644 --- a/src/space-model/services/subspace/subspace-model.service.ts +++ b/src/space-model/services/subspace/subspace-model.service.ts @@ -6,7 +6,11 @@ import { import { HttpException, HttpStatus, Injectable } from '@nestjs/common'; import { CreateSubspaceModelDto, CreateTagModelDto } from '../../dtos'; import { In, QueryRunner } from 'typeorm'; -import { IDeletedSubsaceModelInterface } from 'src/space-model/interfaces'; +import { + IDeletedSubsaceModelInterface, + ModifySubspaceModelPayload, + UpdatedSubspaceModelPayload, +} from 'src/space-model/interfaces'; import { DeleteSubspaceModelDto, ModifySubspaceModelDto, @@ -99,17 +103,30 @@ export class SubSpaceModelService { subspaceDtos: ModifySubspaceModelDto[], spaceModel: SpaceModelEntity, queryRunner: QueryRunner, - ): Promise { + ): Promise { + const modifiedSubspaceModels: ModifySubspaceModelPayload = {}; for (const subspace of subspaceDtos) { switch (subspace.action) { case ModifyAction.ADD: - await this.handleAddAction(subspace, spaceModel, queryRunner); + const subspaceModel = await this.handleAddAction( + subspace, + spaceModel, + queryRunner, + ); + modifiedSubspaceModels.addedSubspaceModels.push(subspaceModel); break; case ModifyAction.UPDATE: - await this.handleUpdateAction(subspace, queryRunner); + const updatedSubspaceModel = await this.handleUpdateAction( + subspace, + queryRunner, + ); + modifiedSubspaceModels.updatedSubspaceModels.push( + updatedSubspaceModel, + ); break; case ModifyAction.DELETE: await this.handleDeleteAction(subspace, queryRunner); + modifiedSubspaceModels.deletedSubspaceModels.push(subspace.uuid); break; default: throw new HttpException( @@ -118,29 +135,42 @@ export class SubSpaceModelService { ); } } + return modifiedSubspaceModels; } private async handleAddAction( subspace: ModifySubspaceModelDto, spaceModel: SpaceModelEntity, queryRunner: QueryRunner, - ): Promise { + ): Promise { const createTagDtos: CreateTagModelDto[] = subspace.tags?.map((tag) => ({ - tag: tag.tag as string, - productUuid: tag.productUuid as string, + tag: tag.tag, + productUuid: tag.productUuid, })) || []; - await this.createSubSpaceModels( - [{ subspaceName: subspace.subspaceName, tags: createTagDtos }], + + const [createdSubspaceModel] = await this.createSubSpaceModels( + [ + { + subspaceName: subspace.subspaceName, + tags: createTagDtos, + }, + ], spaceModel, queryRunner, ); + + return createdSubspaceModel; } private async handleUpdateAction( modifyDto: ModifySubspaceModelDto, queryRunner: QueryRunner, - ): Promise { + ): Promise { + const updatePayload: UpdatedSubspaceModelPayload = { + subspaceModelUuid: modifyDto.uuid, + }; + const subspace = await this.findOne(modifyDto.uuid); await this.updateSubspaceName( @@ -148,21 +178,24 @@ export class SubSpaceModelService { subspace, modifyDto.subspaceName, ); + updatePayload.subspaceName = modifyDto.subspaceName; if (modifyDto.tags?.length) { - await this.tagModelService.modifyTags( + updatePayload.modifiedTags = await this.tagModelService.modifyTags( modifyDto.tags, queryRunner, null, subspace, ); } + + return updatePayload; } private async handleDeleteAction( subspace: ModifySubspaceModelDto, queryRunner: QueryRunner, - ): Promise { + ) { const subspaceModel = await this.findOne(subspace.uuid); await queryRunner.manager.update( diff --git a/src/space-model/services/tag-model.service.ts b/src/space-model/services/tag-model.service.ts index 5682bc2..d08fe38 100644 --- a/src/space-model/services/tag-model.service.ts +++ b/src/space-model/services/tag-model.service.ts @@ -9,6 +9,7 @@ import { TagModelRepository } from '@app/common/modules/space-model'; import { CreateTagModelDto, ModifyTagModelDto } from '../dtos'; import { ProductService } from 'src/product/services'; import { ModifyAction } from '@app/common/constants/modify-action.enum'; +import { ModifiedTagsModelPayload } from '../interfaces'; @Injectable() export class TagModelService { @@ -132,9 +133,9 @@ export class TagModelService { queryRunner: QueryRunner, spaceModel?: SpaceModelEntity, subspaceModel?: SubspaceModelEntity, - ): Promise { + ): Promise { + const modifiedTagModels: ModifiedTagsModelPayload = {}; try { - console.log(tags); for (const tag of tags) { if (tag.action === ModifyAction.ADD) { const createTagDto: CreateTagModelDto = { @@ -142,20 +143,28 @@ export class TagModelService { productUuid: tag.productUuid as string, }; - await this.createTags( + const newModel = await this.createTags( [createTagDto], queryRunner, spaceModel, subspaceModel, ); + modifiedTagModels.added.push(...newModel); } else if (tag.action === ModifyAction.UPDATE) { - await this.updateTag(tag, queryRunner, spaceModel, subspaceModel); + const updatedModel = await this.updateTag( + tag, + queryRunner, + spaceModel, + subspaceModel, + ); + modifiedTagModels.updated.push(updatedModel); } else if (tag.action === ModifyAction.DELETE) { await queryRunner.manager.update( this.tagModelRepository.target, { uuid: tag.uuid }, { disabled: true }, ); + modifiedTagModels.deleted.push(tag.uuid); } else { throw new HttpException( `Invalid action "${tag.action}" provided.`, @@ -163,6 +172,7 @@ export class TagModelService { ); } } + return modifiedTagModels; } catch (error) { if (error instanceof HttpException) { throw error; @@ -235,7 +245,7 @@ export class TagModelService { }); } - private async getTagByUuid(uuid: string): Promise { + async getTagByUuid(uuid: string): Promise { const tag = await this.tagModelRepository.findOne({ where: { uuid }, relations: ['product'], @@ -248,4 +258,38 @@ export class TagModelService { } return tag; } + + async getTagByName( + tag: string, + subspaceUuid?: string, + spaceUuid?: string, + ): Promise { + const queryConditions: any = { tag }; + + if (spaceUuid) { + queryConditions.spaceModel = { uuid: spaceUuid }; + } else if (subspaceUuid) { + queryConditions.subspaceModel = { uuid: subspaceUuid }; + } else { + throw new HttpException( + 'Either spaceUuid or subspaceUuid must be provided.', + HttpStatus.BAD_REQUEST, + ); + } + queryConditions.disabled = false; + + const existingTag = await this.tagModelRepository.findOne({ + where: queryConditions, + relations: ['product'], + }); + + if (!existingTag) { + throw new HttpException( + `Tag model with tag "${tag}" not found.`, + HttpStatus.NOT_FOUND, + ); + } + + return existingTag; + } } diff --git a/src/space-model/space-model.module.ts b/src/space-model/space-model.module.ts index bc3398d..7f72860 100644 --- a/src/space-model/space-model.module.ts +++ b/src/space-model/space-model.module.ts @@ -14,12 +14,34 @@ import { } from '@app/common/modules/space-model'; import { ProjectRepository } from '@app/common/modules/project/repositiories'; import { ProductRepository } from '@app/common/modules/product/repositories'; -import { PropogateSubspaceHandler } from './handlers'; +import { + PropogateDeleteSpaceModelHandler, + PropogateUpdateSpaceModelHandler, +} from './handlers'; import { CqrsModule } from '@nestjs/cqrs'; -import { SpaceRepository } from '@app/common/modules/space'; +import { + SpaceLinkRepository, + SpaceRepository, + TagRepository, +} from '@app/common/modules/space'; import { SubspaceRepository } from '@app/common/modules/space/repositories/subspace.repository'; +import { + SpaceLinkService, + SpaceService, + SubspaceDeviceService, + SubSpaceService, + ValidationService, +} from 'src/space/services'; +import { TagService } from 'src/space/services/tag'; +import { CommunityService } from 'src/community/services'; +import { DeviceRepository } from '@app/common/modules/device/repositories'; +import { TuyaService } from '@app/common/integrations/tuya/services/tuya.service'; +import { CommunityRepository } from '@app/common/modules/community/repositories'; -const CommandHandlers = [PropogateSubspaceHandler]; +const CommandHandlers = [ + PropogateUpdateSpaceModelHandler, + PropogateDeleteSpaceModelHandler, +]; @Module({ imports: [ConfigModule, SpaceRepositoryModule, CqrsModule], @@ -27,6 +49,7 @@ const CommandHandlers = [PropogateSubspaceHandler]; providers: [ ...CommandHandlers, SpaceModelService, + SpaceService, SpaceModelRepository, SpaceRepository, ProjectRepository, @@ -36,6 +59,17 @@ const CommandHandlers = [PropogateSubspaceHandler]; SubspaceRepository, TagModelService, TagModelRepository, + SubSpaceService, + ValidationService, + TagService, + SubspaceDeviceService, + CommunityService, + TagRepository, + DeviceRepository, + TuyaService, + CommunityRepository, + SpaceLinkService, + SpaceLinkRepository, ], exports: [CqrsModule, SpaceModelService], }) diff --git a/src/space/interfaces/add-subspace.interface.ts b/src/space/interfaces/add-subspace.interface.ts new file mode 100644 index 0000000..207622c --- /dev/null +++ b/src/space/interfaces/add-subspace.interface.ts @@ -0,0 +1,5 @@ +import { SubspaceEntity } from '@app/common/modules/space'; + +export interface ModifySubspacePayload { + addedSubspaces?: SubspaceEntity[]; +} diff --git a/src/space/interfaces/index.ts b/src/space/interfaces/index.ts new file mode 100644 index 0000000..e69de29 diff --git a/src/space/services/space.service.ts b/src/space/services/space.service.ts index 8fba2d9..9badabb 100644 --- a/src/space/services/space.service.ts +++ b/src/space/services/space.service.ts @@ -317,8 +317,8 @@ export class SpaceService { if (hasSubspace) { await this.subSpaceService.modifySubSpace( updateSpaceDto.subspace, - space, queryRunner, + space, ); } @@ -353,6 +353,37 @@ export class SpaceService { } } + async unlinkSpaceFromModel( + space: SpaceEntity, + queryRunner: QueryRunner, + ): Promise { + try { + await queryRunner.manager.update( + this.spaceRepository.target, + { uuid: space.uuid }, + { + spaceModel: null, + }, + ); + + // Unlink subspaces and tags if they exist + if (space.subspaces || space.tags) { + if (space.tags) { + await this.tagService.unlinkModels(space.tags, queryRunner); + } + + if (space.subspaces) { + await this.subSpaceService.unlinkModels(space.subspaces, queryRunner); + } + } + } catch (error) { + throw new HttpException( + `Failed to unlink space model: ${error.message}`, + HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + } + private updateSpaceProperties( space: SpaceEntity, updateSpaceDto: UpdateSpaceDto, diff --git a/src/space/services/subspace/subspace.service.ts b/src/space/services/subspace/subspace.service.ts index dbaa3b3..7c5cf6d 100644 --- a/src/space/services/subspace/subspace.service.ts +++ b/src/space/services/subspace/subspace.service.ts @@ -26,6 +26,7 @@ import { SubspaceRepository } from '@app/common/modules/space/repositories/subsp import { TagService } from '../tag'; import { ModifyAction } from '@app/common/constants/modify-action.enum'; import { SubspaceDeviceService } from './subspace-device.service'; +import { ModifyTagDto } from 'src/space/dtos/tag/modify-tag.dto'; @Injectable() export class SubSpaceService { @@ -317,9 +318,9 @@ export class SubSpaceService { async modifySubSpace( subspaceDtos: ModifySubspaceDto[], - space: SpaceEntity, queryRunner: QueryRunner, - ): Promise { + space?: SpaceEntity, + ) { for (const subspace of subspaceDtos) { switch (subspace.action) { case ModifyAction.ADD: @@ -382,17 +383,18 @@ export class SubSpaceService { subspace: ModifySubspaceDto, space: SpaceEntity, queryRunner: QueryRunner, - ): Promise { + ): Promise { const createTagDtos: CreateTagDto[] = subspace.tags?.map((tag) => ({ tag: tag.tag as string, productUuid: tag.productUuid as string, })) || []; - await this.createSubspacesFromDto( + const subSpace = await this.createSubspacesFromDto( [{ subspaceName: subspace.subspaceName, tags: createTagDtos }], space, queryRunner, ); + return subSpace[0]; } private async handleUpdateAction( @@ -400,16 +402,25 @@ export class SubSpaceService { queryRunner: QueryRunner, ): Promise { const subspace = await this.findOne(modifyDto.uuid); - - await this.updateSubspaceName( + await this.update( queryRunner, subspace, modifyDto.subspaceName, + modifyDto.tags, ); + } - if (modifyDto.tags?.length) { + async update( + queryRunner: QueryRunner, + subspace: SubspaceEntity, + subspaceName?: string, + modifyTagDto?: ModifyTagDto[], + ) { + await this.updateSubspaceName(queryRunner, subspace, subspaceName); + + if (modifyTagDto?.length) { await this.tagService.modifyTags( - modifyDto.tags, + modifyTagDto, queryRunner, null, subspace, @@ -417,11 +428,11 @@ export class SubSpaceService { } } - private async handleDeleteAction( - subspace: ModifySubspaceDto, + async handleDeleteAction( + modifyDto: ModifySubspaceDto, queryRunner: QueryRunner, ): Promise { - const subspaceModel = await this.findOne(subspace.uuid); + const subspace = await this.findOne(modifyDto.uuid); await queryRunner.manager.update( this.subspaceRepository.target, @@ -429,8 +440,8 @@ export class SubSpaceService { { disabled: true }, ); - if (subspaceModel.tags?.length) { - const modifyTagDtos = subspaceModel.tags.map((tag) => ({ + if (subspace.tags?.length) { + const modifyTagDtos = subspace.tags.map((tag) => ({ uuid: tag.uuid, action: ModifyAction.DELETE, })); @@ -438,7 +449,14 @@ export class SubSpaceService { modifyTagDtos, queryRunner, null, - subspaceModel, + subspace, + ); + } + + if (subspace.devices.length > 0) { + await this.deviceService.deleteSubspaceDevices( + subspace.devices, + queryRunner, ); } } @@ -446,7 +464,7 @@ export class SubSpaceService { private async findOne(subspaceUuid: string): Promise { const subspace = await this.subspaceRepository.findOne({ where: { uuid: subspaceUuid }, - relations: ['tags', 'space'], + relations: ['tags', 'space', 'devices', 'tags.product', 'tags.device'], }); if (!subspace) { throw new HttpException( @@ -457,7 +475,7 @@ export class SubSpaceService { return subspace; } - private async updateSubspaceName( + async updateSubspaceName( queryRunner: QueryRunner, subSpace: SubspaceEntity, subspaceName?: string, diff --git a/src/space/services/tag/tag.service.ts b/src/space/services/tag/tag.service.ts index b89dc9c..4c3bb8f 100644 --- a/src/space/services/tag/tag.service.ts +++ b/src/space/services/tag/tag.service.ts @@ -63,6 +63,7 @@ export class TagService { await queryRunner.manager.save(tags); } + async updateTag( tag: ModifyTagDto, queryRunner: QueryRunner, @@ -90,6 +91,38 @@ export class TagService { } } + async updateTagsFromModel( + model: TagModel, + queryRunner: QueryRunner, + ): Promise { + try { + const tags = await this.tagRepository.find({ + where: { + model: { + uuid: model.uuid, + }, + }, + }); + + if (!tags.length) return; + + await queryRunner.manager.update( + this.tagRepository.target, + { model: { uuid: model.uuid } }, + { tag: model.tag }, + ); + } catch (error) { + if (error instanceof HttpException) { + throw error; + } + + throw new HttpException( + `Failed to update tags for model with UUID: ${model.uuid}. Reason: ${error.message}`, + HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + } + async deleteTags(tagUuids: string[], queryRunner: QueryRunner) { if (!tagUuids?.length) return; @@ -109,6 +142,34 @@ export class TagService { } } + async deleteTagFromModel(modelUuid: string, queryRunner: QueryRunner) { + try { + const tags = await this.tagRepository.find({ + where: { + model: { + uuid: modelUuid, + }, + }, + }); + + if (!tags.length) return; + + await queryRunner.manager.update( + this.tagRepository.target, + { model: { uuid: modelUuid } }, + { disabled: true, device: null }, + ); + } catch (error) { + if (error instanceof HttpException) { + throw error; + } + + throw new HttpException( + `Failed to update tags for model with UUID: ${modelUuid}. Reason: ${error.message}`, + HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + } async modifyTags( tags: ModifyTagDto[], queryRunner: QueryRunner,