From a1f74938fccf929c6edd6e468f320fe60da4d637 Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Fri, 15 Nov 2024 13:27:20 +0400 Subject: [PATCH 01/12] removed unused entity --- libs/common/src/database/database.module.ts | 2 - .../src/modules/space/dtos/space.dto.ts | 10 ---- .../modules/space/entities/space.entity.ts | 29 +++-------- .../space/repositories/space.repository.ts | 10 +--- .../modules/space/space.repository.module.ts | 6 +-- libs/common/src/seed/seeder.module.ts | 4 -- .../src/seed/services/seeder.service.ts | 3 -- .../src/seed/services/space.type.seeder.ts | 52 ------------------- 8 files changed, 11 insertions(+), 105 deletions(-) delete mode 100644 libs/common/src/seed/services/space.type.seeder.ts diff --git a/libs/common/src/database/database.module.ts b/libs/common/src/database/database.module.ts index 44e69c9..1f30866 100644 --- a/libs/common/src/database/database.module.ts +++ b/libs/common/src/database/database.module.ts @@ -9,7 +9,6 @@ import { ProductEntity } from '../modules/product/entities'; import { DeviceEntity } from '../modules/device/entities'; import { PermissionTypeEntity } from '../modules/permission/entities'; import { SpaceEntity, SubspaceEntity } from '../modules/space/entities'; -import { SpaceTypeEntity } from '../modules/space/entities'; import { UserSpaceEntity } from '../modules/user/entities'; import { DeviceUserPermissionEntity } from '../modules/device/entities'; import { UserRoleEntity } from '../modules/user/entities'; @@ -47,7 +46,6 @@ import { SceneEntity, SceneIconEntity } from '../modules/scene/entities'; CommunityEntity, SpaceEntity, SubspaceEntity, - SpaceTypeEntity, UserSpaceEntity, DeviceUserPermissionEntity, UserRoleEntity, diff --git a/libs/common/src/modules/space/dtos/space.dto.ts b/libs/common/src/modules/space/dtos/space.dto.ts index 04072fd..98706d0 100644 --- a/libs/common/src/modules/space/dtos/space.dto.ts +++ b/libs/common/src/modules/space/dtos/space.dto.ts @@ -21,13 +21,3 @@ export class SpaceDto { @IsNotEmpty() public invitationCode: string; } - -export class SpaceTypeDto { - @IsString() - @IsNotEmpty() - public uuid: string; - - @IsString() - @IsNotEmpty() - public type: string; -} diff --git a/libs/common/src/modules/space/entities/space.entity.ts b/libs/common/src/modules/space/entities/space.entity.ts index 196a522..04ebf83 100644 --- a/libs/common/src/modules/space/entities/space.entity.ts +++ b/libs/common/src/modules/space/entities/space.entity.ts @@ -6,33 +6,13 @@ import { OneToMany, Unique, } from 'typeorm'; -import { SpaceDto, SpaceTypeDto } from '../dtos'; +import { SpaceDto } from '../dtos'; import { AbstractEntity } from '../../abstract/entities/abstract.entity'; import { UserSpaceEntity } from '../../user/entities'; import { DeviceEntity } from '../../device/entities'; import { CommunityEntity } from '../../community/entities'; import { SubspaceEntity } from './subspace.entity'; -@Entity({ name: 'space-type' }) -export class SpaceTypeEntity extends AbstractEntity { - @Column({ - type: 'uuid', - default: () => 'gen_random_uuid()', - nullable: false, - }) - public uuid: string; - - @Column({ - nullable: false, - }) - type: string; - - constructor(partial: Partial) { - super(); - Object.assign(this, partial); - } -} - @Entity({ name: 'space' }) @Unique(['invitationCode']) export class SpaceEntity extends AbstractEntity { @@ -77,6 +57,13 @@ export class SpaceEntity extends AbstractEntity { }) subspaces?: SubspaceEntity[]; + // Position columns + @Column({ type: 'float', nullable: false, default: 0 }) + public x: number; // X coordinate for position + + @Column({ type: 'float', nullable: false, default: 0 }) + public y: number; // Y coordinate for position + @OneToMany( () => DeviceEntity, (devicesSpaceEntity) => devicesSpaceEntity.spaceDevice, diff --git a/libs/common/src/modules/space/repositories/space.repository.ts b/libs/common/src/modules/space/repositories/space.repository.ts index 5e0962c..2806c0e 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, SpaceTypeEntity, SubspaceEntity } from '../entities'; +import { SpaceEntity, SubspaceEntity } from '../entities'; @Injectable() export class SpaceRepository extends Repository { @@ -8,14 +8,6 @@ export class SpaceRepository extends Repository { super(SpaceEntity, dataSource.createEntityManager()); } } - -@Injectable() -export class SpaceTypeRepository extends Repository { - constructor(private dataSource: DataSource) { - super(SpaceTypeEntity, dataSource.createEntityManager()); - } -} - @Injectable() export class SubspaceRepository extends Repository { constructor(private dataSource: DataSource) { diff --git a/libs/common/src/modules/space/space.repository.module.ts b/libs/common/src/modules/space/space.repository.module.ts index ea3c606..90916c2 100644 --- a/libs/common/src/modules/space/space.repository.module.ts +++ b/libs/common/src/modules/space/space.repository.module.ts @@ -1,13 +1,11 @@ import { Module } from '@nestjs/common'; import { TypeOrmModule } from '@nestjs/typeorm'; -import { SpaceEntity, SpaceTypeEntity, SubspaceEntity } from './entities'; +import { SpaceEntity, SubspaceEntity } from './entities'; @Module({ providers: [], exports: [], controllers: [], - imports: [ - TypeOrmModule.forFeature([SpaceEntity, SpaceTypeEntity, SubspaceEntity]), - ], + imports: [TypeOrmModule.forFeature([SpaceEntity, SubspaceEntity])], }) export class SpaceRepositoryModule {} diff --git a/libs/common/src/seed/seeder.module.ts b/libs/common/src/seed/seeder.module.ts index 0c89c5c..eeb245b 100644 --- a/libs/common/src/seed/seeder.module.ts +++ b/libs/common/src/seed/seeder.module.ts @@ -7,8 +7,6 @@ import { ConfigModule } from '@nestjs/config'; import { RoleTypeRepositoryModule } from '../modules/role-type/role.type.repository.module'; import { RoleTypeRepository } from '../modules/role-type/repositories'; import { RoleTypeSeeder } from './services/role.type.seeder'; -import { SpaceTypeRepository } from '../modules/space/repositories'; -import { SpaceTypeSeeder } from './services/space.type.seeder'; import { SpaceRepositoryModule } from '../modules/space/space.repository.module'; import { SuperAdminSeeder } from './services/supper.admin.seeder'; import { UserRepository } from '../modules/user/repositories'; @@ -25,11 +23,9 @@ import { SceneIconRepository } from '../modules/scene/repositories'; providers: [ PermissionTypeSeeder, RoleTypeSeeder, - SpaceTypeSeeder, SeederService, PermissionTypeRepository, RoleTypeRepository, - SpaceTypeRepository, SuperAdminSeeder, UserRepository, UserRoleRepository, diff --git a/libs/common/src/seed/services/seeder.service.ts b/libs/common/src/seed/services/seeder.service.ts index e627e44..09fb91d 100644 --- a/libs/common/src/seed/services/seeder.service.ts +++ b/libs/common/src/seed/services/seeder.service.ts @@ -1,7 +1,6 @@ import { Injectable } from '@nestjs/common'; import { PermissionTypeSeeder } from './permission.type.seeder'; import { RoleTypeSeeder } from './role.type.seeder'; -import { SpaceTypeSeeder } from './space.type.seeder'; import { SuperAdminSeeder } from './supper.admin.seeder'; import { RegionSeeder } from './regions.seeder'; import { TimeZoneSeeder } from './timezone.seeder'; @@ -11,7 +10,6 @@ export class SeederService { constructor( private readonly permissionTypeSeeder: PermissionTypeSeeder, private readonly roleTypeSeeder: RoleTypeSeeder, - private readonly spaceTypeSeeder: SpaceTypeSeeder, private readonly regionSeeder: RegionSeeder, private readonly timeZoneSeeder: TimeZoneSeeder, private readonly superAdminSeeder: SuperAdminSeeder, @@ -21,7 +19,6 @@ export class SeederService { async seed() { await this.permissionTypeSeeder.addPermissionTypeDataIfNotFound(); await this.roleTypeSeeder.addRoleTypeDataIfNotFound(); - await this.spaceTypeSeeder.addSpaceTypeDataIfNotFound(); await this.regionSeeder.addRegionDataIfNotFound(); await this.timeZoneSeeder.addTimeZoneDataIfNotFound(); await this.superAdminSeeder.createSuperAdminIfNotFound(); diff --git a/libs/common/src/seed/services/space.type.seeder.ts b/libs/common/src/seed/services/space.type.seeder.ts deleted file mode 100644 index 56c2ee8..0000000 --- a/libs/common/src/seed/services/space.type.seeder.ts +++ /dev/null @@ -1,52 +0,0 @@ -import { Injectable } from '@nestjs/common'; -import { SpaceType } from '../../constants/space-type.enum'; -import { SpaceTypeRepository } from '../../modules/space/repositories'; - -@Injectable() -export class SpaceTypeSeeder { - constructor(private readonly spaceTypeRepository: SpaceTypeRepository) {} - - async addSpaceTypeDataIfNotFound(): Promise { - try { - const existingSpaceTypes = await this.spaceTypeRepository.find(); - - const spaceTypeNames = existingSpaceTypes.map((pt) => pt.type); - - const missingSpaceTypes = []; - if (!spaceTypeNames.includes(SpaceType.COMMUNITY)) { - missingSpaceTypes.push(SpaceType.COMMUNITY); - } - if (!spaceTypeNames.includes(SpaceType.BUILDING)) { - missingSpaceTypes.push(SpaceType.BUILDING); - } - if (!spaceTypeNames.includes(SpaceType.FLOOR)) { - missingSpaceTypes.push(SpaceType.FLOOR); - } - if (!spaceTypeNames.includes(SpaceType.UNIT)) { - missingSpaceTypes.push(SpaceType.UNIT); - } - if (!spaceTypeNames.includes(SpaceType.ROOM)) { - missingSpaceTypes.push(SpaceType.ROOM); - } - if (missingSpaceTypes.length > 0) { - await this.addSpaceTypeData(missingSpaceTypes); - } - } catch (err) { - console.error('Error while checking space type data:', err); - throw err; - } - } - - private async addSpaceTypeData(spaceTypes: string[]): Promise { - try { - const spaceTypeEntities = spaceTypes.map((type) => ({ - type, - })); - - await this.spaceTypeRepository.save(spaceTypeEntities); - } catch (err) { - console.error('Error while adding space type data:', err); - throw err; - } - } -} From 891129e7e3bdf86e15afc9fe8aa8be4ebcc273a5 Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Wed, 20 Nov 2024 09:11:11 +0400 Subject: [PATCH 02/12] added product controller --- src/product/controllers/index.ts | 1 + src/product/controllers/product.controller.ts | 27 +++++++++++++++++++ src/product/index.ts | 1 + src/product/product.module.ts | 10 +++++++ src/product/services/index.ts | 1 + src/product/services/product.service.ts | 24 +++++++++++++++++ 6 files changed, 64 insertions(+) create mode 100644 src/product/controllers/index.ts create mode 100644 src/product/controllers/product.controller.ts create mode 100644 src/product/index.ts create mode 100644 src/product/product.module.ts create mode 100644 src/product/services/index.ts create mode 100644 src/product/services/product.service.ts diff --git a/src/product/controllers/index.ts b/src/product/controllers/index.ts new file mode 100644 index 0000000..eead553 --- /dev/null +++ b/src/product/controllers/index.ts @@ -0,0 +1 @@ +export * from './product.controller'; diff --git a/src/product/controllers/product.controller.ts b/src/product/controllers/product.controller.ts new file mode 100644 index 0000000..f5fdf53 --- /dev/null +++ b/src/product/controllers/product.controller.ts @@ -0,0 +1,27 @@ +import { Controller, Get, UseGuards } from '@nestjs/common'; +import { ApiTags, ApiBearerAuth, ApiOperation } from '@nestjs/swagger'; +import { JwtAuthGuard } from '@app/common/guards/jwt.auth.guard'; +import { EnableDisableStatusEnum } from '@app/common/constants/days.enum'; +import { BaseResponseDto } from '@app/common/dto/base.response.dto'; +import { ControllerRoute } from '@app/common/constants/controller-route'; +import { ProductService } from '../services'; + +@ApiTags('Product Module') +@Controller({ + version: EnableDisableStatusEnum.ENABLED, + path: ControllerRoute.PRODUCT.ROUTE, +}) +export class ProductController { + constructor(private readonly productService: ProductService) {} + + @ApiBearerAuth() + @UseGuards(JwtAuthGuard) + @Get() + @ApiOperation({ + summary: ControllerRoute.PRODUCT.ACTIONS.LIST_PRODUCT_SUMMARY, + description: ControllerRoute.PRODUCT.ACTIONS.LIST_PRODUCT_DESCRIPTION, + }) + async getProducts(): Promise { + return await this.productService.list(); + } +} diff --git a/src/product/index.ts b/src/product/index.ts new file mode 100644 index 0000000..8b3162c --- /dev/null +++ b/src/product/index.ts @@ -0,0 +1 @@ +export * from './product.module'; diff --git a/src/product/product.module.ts b/src/product/product.module.ts new file mode 100644 index 0000000..e5ffd25 --- /dev/null +++ b/src/product/product.module.ts @@ -0,0 +1,10 @@ +import { Module } from '@nestjs/common'; +import { ProductService } from './services'; +import { ProductRepository } from '@app/common/modules/product/repositories'; +import { ProductController } from './controllers'; + +@Module({ + controllers: [ProductController], + providers: [ProductService, ProductRepository], +}) +export class ProductModule {} diff --git a/src/product/services/index.ts b/src/product/services/index.ts new file mode 100644 index 0000000..59832ef --- /dev/null +++ b/src/product/services/index.ts @@ -0,0 +1 @@ +export * from './product.service'; diff --git a/src/product/services/product.service.ts b/src/product/services/product.service.ts new file mode 100644 index 0000000..cebf9ba --- /dev/null +++ b/src/product/services/product.service.ts @@ -0,0 +1,24 @@ +import { BaseResponseDto } from '@app/common/dto/base.response.dto'; +import { SuccessResponseDto } from '@app/common/dto/success.response.dto'; +import { ProductRepository } from '@app/common/modules/product/repositories'; +import { HttpException, HttpStatus, Injectable } from '@nestjs/common'; + +@Injectable() +export class ProductService { + constructor(private readonly productRepository: ProductRepository) {} + + async list(): Promise { + const products = await this.productRepository.find(); + + if (!products) { + throw new HttpException( + `No products found in the system`, + HttpStatus.NOT_FOUND, + ); + } + return new SuccessResponseDto({ + data: products, + message: 'List of products retrieved successfully', + }); + } +} From 857b02b97b10bd6112c2a7e1250d3a1a8b484614 Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Wed, 20 Nov 2024 09:11:22 +0400 Subject: [PATCH 03/12] added space link entity --- libs/common/src/constants/controller-route.ts | 9 +++++++ libs/common/src/database/database.module.ts | 3 ++- .../src/modules/space/entities/index.ts | 1 + .../space/entities/space-link.entity.ts | 26 +++++++++++++++++++ .../modules/space/entities/space.entity.ts | 17 ++++++++++++ .../space/repositories/space.repository.ts | 9 ++++++- src/app.module.ts | 2 ++ src/space/dtos/add.space.dto.ts | 18 +++++++++++++ src/space/services/space.service.ts | 20 +++++++++++--- src/space/space.module.ts | 2 ++ 10 files changed, 102 insertions(+), 5 deletions(-) create mode 100644 libs/common/src/modules/space/entities/space-link.entity.ts diff --git a/libs/common/src/constants/controller-route.ts b/libs/common/src/constants/controller-route.ts index 7f84bcc..fdf0435 100644 --- a/libs/common/src/constants/controller-route.ts +++ b/libs/common/src/constants/controller-route.ts @@ -235,4 +235,13 @@ export class ControllerRoute { 'Removes the association of a device from a specific subspace, making it no longer managed within that subspace.'; }; }; + + static PRODUCT = class { + public static readonly ROUTE = 'products'; + static ACTIONS = class { + public static readonly LIST_PRODUCT_SUMMARY = 'Retrieve all products'; + public static readonly LIST_PRODUCT_DESCRIPTION = + 'Fetches a list of all products along with their associated device details'; + }; + }; } diff --git a/libs/common/src/database/database.module.ts b/libs/common/src/database/database.module.ts index 1f30866..904cac7 100644 --- a/libs/common/src/database/database.module.ts +++ b/libs/common/src/database/database.module.ts @@ -8,7 +8,7 @@ import { UserOtpEntity } from '../modules/user/entities'; import { ProductEntity } from '../modules/product/entities'; import { DeviceEntity } from '../modules/device/entities'; import { PermissionTypeEntity } from '../modules/permission/entities'; -import { SpaceEntity, SubspaceEntity } from '../modules/space/entities'; +import { SpaceEntity, SpaceLinkEntity, SubspaceEntity } from '../modules/space/entities'; import { UserSpaceEntity } from '../modules/user/entities'; import { DeviceUserPermissionEntity } from '../modules/device/entities'; import { UserRoleEntity } from '../modules/user/entities'; @@ -45,6 +45,7 @@ import { SceneEntity, SceneIconEntity } from '../modules/scene/entities'; PermissionTypeEntity, CommunityEntity, SpaceEntity, + SpaceLinkEntity, SubspaceEntity, UserSpaceEntity, DeviceUserPermissionEntity, diff --git a/libs/common/src/modules/space/entities/index.ts b/libs/common/src/modules/space/entities/index.ts index e551c0a..f720cf4 100644 --- a/libs/common/src/modules/space/entities/index.ts +++ b/libs/common/src/modules/space/entities/index.ts @@ -1,2 +1,3 @@ export * from './space.entity'; export * from './subspace.entity'; +export * from './space-link.entity'; diff --git a/libs/common/src/modules/space/entities/space-link.entity.ts b/libs/common/src/modules/space/entities/space-link.entity.ts new file mode 100644 index 0000000..6e2524f --- /dev/null +++ b/libs/common/src/modules/space/entities/space-link.entity.ts @@ -0,0 +1,26 @@ +import { Column, Entity, JoinColumn, ManyToOne } from 'typeorm'; +import { AbstractEntity } from '../../abstract/entities/abstract.entity'; +import { SpaceEntity } from './space.entity'; + +@Entity({ name: 'space_link' }) +export class SpaceLinkEntity extends AbstractEntity { + @ManyToOne(() => SpaceEntity, { nullable: false, onDelete: 'CASCADE' }) + @JoinColumn({ name: 'start_space_id' }) + public startSpace: SpaceEntity; + + @ManyToOne(() => SpaceEntity, { nullable: false, onDelete: 'CASCADE' }) + @JoinColumn({ name: 'end_space_id' }) + public endSpace: SpaceEntity; + + @Column({ + type: 'varchar', + length: 10, + nullable: false, + }) + direction: string; + + 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 04ebf83..f7034e3 100644 --- a/libs/common/src/modules/space/entities/space.entity.ts +++ b/libs/common/src/modules/space/entities/space.entity.ts @@ -12,6 +12,7 @@ import { UserSpaceEntity } from '../../user/entities'; import { DeviceEntity } from '../../device/entities'; import { CommunityEntity } from '../../community/entities'; import { SubspaceEntity } from './subspace.entity'; +import { SpaceLinkEntity } from './space-link.entity'; @Entity({ name: 'space' }) @Unique(['invitationCode']) @@ -70,6 +71,22 @@ export class SpaceEntity extends AbstractEntity { ) devices: DeviceEntity[]; + @OneToMany(() => SpaceLinkEntity, (connection) => connection.startSpace, { + nullable: true, + }) + public outgoingConnections: SpaceLinkEntity[]; + + @OneToMany(() => SpaceLinkEntity, (connection) => connection.endSpace, { + nullable: true, + }) + public incomingConnections: SpaceLinkEntity[]; + + @Column({ + nullable: true, + type: 'text', + }) + public icon: string; + 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 2806c0e..01e8c2d 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, SubspaceEntity } from '../entities'; +import { SpaceEntity, SpaceLinkEntity, SubspaceEntity } from '../entities'; @Injectable() export class SpaceRepository extends Repository { @@ -14,3 +14,10 @@ export class SubspaceRepository extends Repository { super(SubspaceEntity, dataSource.createEntityManager()); } } + +@Injectable() +export class SpaceLinkRepository extends Repository { + constructor(private dataSource: DataSource) { + super(SpaceLinkEntity, dataSource.createEntityManager()); + } +} diff --git a/src/app.module.ts b/src/app.module.ts index 820602b..609147c 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -21,6 +21,7 @@ import { TimeZoneModule } from './timezone/timezone.module'; import { VisitorPasswordModule } from './vistor-password/visitor-password.module'; import { ScheduleModule } from './schedule/schedule.module'; import { SpaceModule } from './space/space.module'; +import { ProductModule } from './product'; @Module({ imports: [ ConfigModule.forRoot({ @@ -46,6 +47,7 @@ import { SpaceModule } from './space/space.module'; TimeZoneModule, VisitorPasswordModule, ScheduleModule, + ProductModule, ], providers: [ { diff --git a/src/space/dtos/add.space.dto.ts b/src/space/dtos/add.space.dto.ts index a33bb34..e59d9d1 100644 --- a/src/space/dtos/add.space.dto.ts +++ b/src/space/dtos/add.space.dto.ts @@ -2,6 +2,7 @@ import { ApiProperty } from '@nestjs/swagger'; import { IsBoolean, IsNotEmpty, + IsNumber, IsOptional, IsString, IsUUID, @@ -25,6 +26,10 @@ export class AddSpaceDto { @IsOptional() parentUuid?: string; + @IsString() + @IsNotEmpty() + public icon: string; + @ApiProperty({ description: 'Indicates whether the space is private or public', example: false, @@ -32,6 +37,19 @@ export class AddSpaceDto { }) @IsBoolean() isPrivate: boolean; + + @ApiProperty({ description: 'X position on canvas', example: 120 }) + @IsNumber() + x: number; + + @ApiProperty({ description: 'Y position on canvas', example: 200 }) + @IsNumber() + y: number; + + @ApiProperty({ description: 'Y position on canvas', example: 200 }) + @IsString() + @IsOptional() + direction: string; } export class AddUserSpaceDto { diff --git a/src/space/services/space.service.ts b/src/space/services/space.service.ts index 78d45d7..864ffbb 100644 --- a/src/space/services/space.service.ts +++ b/src/space/services/space.service.ts @@ -1,4 +1,7 @@ -import { SpaceRepository } from '@app/common/modules/space/repositories'; +import { + SpaceLinkRepository, + SpaceRepository, +} from '@app/common/modules/space/repositories'; import { BadRequestException, HttpException, @@ -16,6 +19,7 @@ import { generateRandomString } from '@app/common/helper/randomString'; export class SpaceService { constructor( private readonly spaceRepository: SpaceRepository, + private readonly spaceLinkRepository: SpaceLinkRepository, private readonly communityRepository: CommunityRepository, ) {} @@ -25,7 +29,7 @@ export class SpaceService { ): Promise { let parent: SpaceEntity | null = null; - const { parentUuid } = addSpaceDto; + const { parentUuid, direction } = addSpaceDto; const community = await this.communityRepository.findOne({ where: { uuid: communityId }, }); @@ -60,6 +64,16 @@ export class SpaceService { await this.spaceRepository.save(newSpace); + if (direction && parent) { + const spaceLink = await this.spaceLinkRepository.create({ + direction, + endSpace: newSpace, + startSpace: parent, + }); + + await this.spaceLinkRepository.save(spaceLink); + } + return new SuccessResponseDto({ statusCode: HttpStatus.CREATED, data: newSpace, @@ -88,7 +102,7 @@ export class SpaceService { // Get all spaces related to the community, including the parent-child relations const spaces = await this.spaceRepository.find({ where: { community: { uuid: communityUuid } }, - relations: ['parent', 'children'], // Include parent and children relations + relations: ['parent', 'children', 'incomingConnections'], // Include parent and children relations }); // Organize spaces into a hierarchical structure diff --git a/src/space/space.module.ts b/src/space/space.module.ts index cf6b766..2d95d7a 100644 --- a/src/space/space.module.ts +++ b/src/space/space.module.ts @@ -20,6 +20,7 @@ import { import { SpaceRepository, SubspaceRepository, + SpaceLinkRepository, } from '@app/common/modules/space/repositories'; import { CommunityRepository } from '@app/common/modules/community/repositories'; import { @@ -59,6 +60,7 @@ import { DeviceStatusLogRepository } from '@app/common/modules/device-status-log DeviceRepository, CommunityRepository, SubspaceRepository, + SpaceLinkRepository, UserSpaceRepository, UserRepository, SpaceUserService, From 4a98d94ddae95af73662320b27834b20ec91a415 Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Wed, 20 Nov 2024 14:55:32 +0400 Subject: [PATCH 04/12] added name to product --- libs/common/src/modules/product/entities/product.entity.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/libs/common/src/modules/product/entities/product.entity.ts b/libs/common/src/modules/product/entities/product.entity.ts index 6553dc1..6158aa9 100644 --- a/libs/common/src/modules/product/entities/product.entity.ts +++ b/libs/common/src/modules/product/entities/product.entity.ts @@ -16,6 +16,11 @@ export class ProductEntity extends AbstractEntity { }) public prodId: string; + @Column({ + nullable: true, + }) + public name: string; + @Column({ nullable: false, }) From c7404972a27c3928e4d53648643573f41ccdbe8c Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Wed, 20 Nov 2024 16:01:51 +0400 Subject: [PATCH 05/12] added space link service --- libs/common/src/constants/direction.enum.ts | 5 + .../space/entities/space-link.entity.ts | 6 +- src/space/services/index.ts | 1 + src/space/services/space-link/index.ts | 1 + .../services/space-link/space-link.service.ts | 88 ++++++++ src/space/services/space.service.ts | 201 ++++-------------- src/space/space.module.ts | 2 + 7 files changed, 145 insertions(+), 159 deletions(-) create mode 100644 libs/common/src/constants/direction.enum.ts create mode 100644 src/space/services/space-link/index.ts create mode 100644 src/space/services/space-link/space-link.service.ts diff --git a/libs/common/src/constants/direction.enum.ts b/libs/common/src/constants/direction.enum.ts new file mode 100644 index 0000000..5791b57 --- /dev/null +++ b/libs/common/src/constants/direction.enum.ts @@ -0,0 +1,5 @@ +export enum Direction { + LEFT = 'left', + RIGHT = 'right', + DOWN = 'down', +} 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 6e2524f..a62ce4f 100644 --- a/libs/common/src/modules/space/entities/space-link.entity.ts +++ b/libs/common/src/modules/space/entities/space-link.entity.ts @@ -1,8 +1,9 @@ import { Column, Entity, JoinColumn, ManyToOne } from 'typeorm'; import { AbstractEntity } from '../../abstract/entities/abstract.entity'; import { SpaceEntity } from './space.entity'; +import { Direction } from '@app/common/constants/direction.enum'; -@Entity({ name: 'space_link' }) +@Entity({ name: 'space-link' }) export class SpaceLinkEntity extends AbstractEntity { @ManyToOne(() => SpaceEntity, { nullable: false, onDelete: 'CASCADE' }) @JoinColumn({ name: 'start_space_id' }) @@ -13,9 +14,8 @@ export class SpaceLinkEntity extends AbstractEntity { public endSpace: SpaceEntity; @Column({ - type: 'varchar', - length: 10, nullable: false, + enum: Object.values(Direction), }) direction: string; diff --git a/src/space/services/index.ts b/src/space/services/index.ts index c4e9405..1dde0ae 100644 --- a/src/space/services/index.ts +++ b/src/space/services/index.ts @@ -2,4 +2,5 @@ export * from './space.service'; export * from './space-user.service'; export * from './space-device.service'; export * from './subspace'; +export * from './space-link'; export * from './space-scene.service'; diff --git a/src/space/services/space-link/index.ts b/src/space/services/space-link/index.ts new file mode 100644 index 0000000..ad41b9a --- /dev/null +++ b/src/space/services/space-link/index.ts @@ -0,0 +1 @@ +export * from './space-link.service'; diff --git a/src/space/services/space-link/space-link.service.ts b/src/space/services/space-link/space-link.service.ts new file mode 100644 index 0000000..0acece0 --- /dev/null +++ b/src/space/services/space-link/space-link.service.ts @@ -0,0 +1,88 @@ +import { + SpaceLinkRepository, + SpaceRepository, +} from '@app/common/modules/space/repositories'; +import { HttpException, HttpStatus, Injectable } from '@nestjs/common'; + +@Injectable() +export class SpaceLinkService { + constructor( + private readonly spaceRepository: SpaceRepository, + private readonly spaceLinkRepository: SpaceLinkRepository, + ) {} + + async saveSpaceLink( + startSpaceId: string, + endSpaceId: string, + direction: string, + ): Promise { + try { + // Check if a link between the startSpace and endSpace already exists + const existingLink = await this.spaceLinkRepository.findOne({ + where: { + startSpace: { uuid: startSpaceId }, + endSpace: { uuid: endSpaceId }, + }, + }); + + if (existingLink) { + // Update the direction if the link exists + existingLink.direction = direction; + await this.spaceLinkRepository.save(existingLink); + return; + } + + const existingEndSpaceLink = await this.spaceLinkRepository.findOne({ + where: { endSpace: { uuid: endSpaceId } }, + }); + + if ( + existingEndSpaceLink && + existingEndSpaceLink.startSpace.uuid !== startSpaceId + ) { + throw new Error( + `Space with ID ${endSpaceId} is already an endSpace in another link and cannot be reused.`, + ); + } + + // Find start space + const startSpace = await this.spaceRepository.findOne({ + where: { uuid: startSpaceId }, + }); + + if (!startSpace) { + throw new HttpException( + `Start space with ID ${startSpaceId} not found.`, + HttpStatus.NOT_FOUND, + ); + } + + // Find end space + const endSpace = await this.spaceRepository.findOne({ + where: { uuid: endSpaceId }, + }); + + if (!endSpace) { + throw new HttpException( + `End space with ID ${endSpaceId} not found.`, + HttpStatus.NOT_FOUND, + ); + } + + // Create and save the space link + const spaceLink = this.spaceLinkRepository.create({ + startSpace, + endSpace, + direction, + }); + + await this.spaceLinkRepository.save(spaceLink); + } catch (error) { + throw new HttpException( + error.message || + `Failed to save space link. Internal Server Error: ${error.message}`, + error.status || HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + } +} diff --git a/src/space/services/space.service.ts b/src/space/services/space.service.ts index 864ffbb..e9dedb5 100644 --- a/src/space/services/space.service.ts +++ b/src/space/services/space.service.ts @@ -1,7 +1,4 @@ -import { - SpaceLinkRepository, - SpaceRepository, -} from '@app/common/modules/space/repositories'; +import { SpaceRepository } from '@app/common/modules/space/repositories'; import { BadRequestException, HttpException, @@ -14,47 +11,25 @@ import { BaseResponseDto } from '@app/common/dto/base.response.dto'; import { CommunityRepository } from '@app/common/modules/community/repositories'; import { SpaceEntity } from '@app/common/modules/space/entities'; import { generateRandomString } from '@app/common/helper/randomString'; +import { SpaceLinkService } from './space-link'; @Injectable() export class SpaceService { constructor( private readonly spaceRepository: SpaceRepository, - private readonly spaceLinkRepository: SpaceLinkRepository, private readonly communityRepository: CommunityRepository, + private readonly spaceLinkService: SpaceLinkService, ) {} async createSpace( addSpaceDto: AddSpaceDto, communityId: string, ): Promise { - let parent: SpaceEntity | null = null; - const { parentUuid, direction } = addSpaceDto; - const community = await this.communityRepository.findOne({ - where: { uuid: communityId }, - }); - // If the community doesn't exist, throw a 404 error - if (!community) { - throw new HttpException( - `Community with ID ${communityId} not found`, - HttpStatus.NOT_FOUND, - ); - } + const community = await this.validateCommunity(communityId); - if (parentUuid) { - parent = await this.spaceRepository.findOne({ - where: { uuid: parentUuid }, - }); - - // If the community doesn't exist, throw a 404 error - if (!parent) { - throw new HttpException( - `Parent with ID ${parentUuid} not found`, - HttpStatus.NOT_FOUND, - ); - } - } + const parent = parentUuid ? await this.validateSpace(parentUuid) : null; try { const newSpace = this.spaceRepository.create({ ...addSpaceDto, @@ -65,13 +40,11 @@ export class SpaceService { await this.spaceRepository.save(newSpace); if (direction && parent) { - const spaceLink = await this.spaceLinkRepository.create({ + await this.spaceLinkService.saveSpaceLink( + parent.uuid, + newSpace.uuid, direction, - endSpace: newSpace, - startSpace: parent, - }); - - await this.spaceLinkRepository.save(spaceLink); + ); } return new SuccessResponseDto({ @@ -87,17 +60,7 @@ export class SpaceService { async getSpacesHierarchyForCommunity( communityUuid: string, ): Promise { - const community = await this.communityRepository.findOne({ - where: { uuid: communityUuid }, - }); - - // If the community doesn't exist, throw a 404 error - if (!community) { - throw new HttpException( - `Community with ID ${communityUuid} not found`, - HttpStatus.NOT_FOUND, - ); - } + await this.validateCommunity(communityUuid); try { // Get all spaces related to the community, including the parent-child relations const spaces = await this.spaceRepository.find({ @@ -123,19 +86,8 @@ export class SpaceService { async findOne(spaceUuid: string): Promise { try { - const space = await this.spaceRepository.findOne({ - where: { - uuid: spaceUuid, - }, - }); + const space = await this.validateSpace(spaceUuid); - // If space is not found, throw a NotFoundException - if (!space) { - throw new HttpException( - `Space with UUID ${spaceUuid} not found`, - HttpStatus.NOT_FOUND, - ); - } return new SuccessResponseDto({ message: `Space with ID ${spaceUuid} successfully fetched`, data: space, @@ -158,28 +110,10 @@ export class SpaceService { ): Promise { try { // First, check if the community exists - const community = await this.communityRepository.findOne({ - where: { uuid: communityUuid }, - }); - - if (!community) { - throw new HttpException( - `Community with ID ${communityUuid} not found`, - HttpStatus.NOT_FOUND, - ); - } - - // Check if the space exists - const space = await this.spaceRepository.findOne({ - where: { uuid: spaceUuid, community: { uuid: communityUuid } }, - }); - - if (!space) { - throw new HttpException( - `Space with ID ${spaceUuid} not found`, - HttpStatus.NOT_FOUND, - ); - } + const space = await this.validateCommunityAndSpace( + spaceUuid, + communityUuid, + ); // Delete the space await this.spaceRepository.remove(space); @@ -205,54 +139,17 @@ export class SpaceService { updateSpaceDto: AddSpaceDto, ): Promise { try { - // First, check if the community exists - const community = await this.communityRepository.findOne({ - where: { uuid: communityId }, - }); - - if (!community) { - throw new HttpException( - `Community with ID ${communityId} not found`, - HttpStatus.NOT_FOUND, - ); - } - - // Check if the space exists - const space = await this.spaceRepository.findOne({ - where: { uuid: spaceUuid, community: { uuid: communityId } }, - }); - - if (!space) { - throw new HttpException( - `Space with ID ${spaceUuid} not found`, - HttpStatus.NOT_FOUND, - ); - } + const space = await this.validateCommunityAndSpace( + communityId, + spaceUuid, + ); // If a parentId is provided, check if the parent exists const { parentUuid } = updateSpaceDto; - let parent: SpaceEntity | null = null; - if (parentUuid) { - parent = await this.spaceRepository.findOne({ - where: { uuid: parentUuid, community: { uuid: communityId } }, - }); - - // If the parent doesn't exist, throw a 404 error - if (!parent) { - throw new HttpException( - `Parent space with ID ${parentUuid} not found`, - HttpStatus.NOT_FOUND, - ); - } - - // Set the parent of the current space - space.parent = parent; - } else { - space.parent = null; // If no parent is provided, clear the parent - } + const parent = parentUuid ? await this.validateSpace(parentUuid) : null; // Update other space properties from updateSpaceDto - Object.assign(space, updateSpaceDto); + Object.assign(space, updateSpaceDto, { parent }); // Save the updated space await this.spaceRepository.save(space); @@ -276,17 +173,7 @@ export class SpaceService { async getSpacesHierarchyForSpace( spaceUuid: string, ): Promise { - const space = await this.spaceRepository.findOne({ - where: { uuid: spaceUuid }, - }); - - // If the space doesn't exist, throw a 404 error - if (!space) { - throw new HttpException( - `Space with ID ${spaceUuid} not found`, - HttpStatus.NOT_FOUND, - ); - } + await this.validateSpace(spaceUuid); try { // Get all spaces that are children of the provided space, including the parent-child relations @@ -315,18 +202,7 @@ export class SpaceService { try { const invitationCode = generateRandomString(6); - const space = await this.spaceRepository.findOne({ - where: { - uuid: spaceUuid, - }, - }); - - if (!space) { - throw new HttpException( - `Space with ID ${spaceUuid} not found`, - HttpStatus.NOT_FOUND, - ); - } + const space = await this.validateSpace(spaceUuid); space.invitationCode = invitationCode; await this.spaceRepository.save(space); @@ -369,24 +245,37 @@ export class SpaceService { } }); - return rootSpaces; // Return the root spaces with children nested within them + return rootSpaces; + } + + private async validateCommunity(communityId: string) { + const community = await this.communityRepository.findOne({ + where: { uuid: communityId }, + }); + if (!community) { + throw new HttpException( + `Community with ID ${communityId} not found`, + HttpStatus.NOT_FOUND, + ); + } + return community; } async validateCommunityAndSpace(communityUuid: string, spaceUuid: string) { - const community = await this.communityRepository.findOne({ - where: { uuid: communityUuid }, - }); + const community = await this.validateCommunity(communityUuid); if (!community) { this.throwNotFound('Community', communityUuid); } + const space = await this.validateSpace(spaceUuid); + return space; + } + + private async validateSpace(spaceUuid: string) { const space = await this.spaceRepository.findOne({ - where: { uuid: spaceUuid, community: { uuid: communityUuid } }, - relations: ['devices', 'devices.productDevice'], + where: { uuid: spaceUuid }, }); - if (!space) { - this.throwNotFound('Space', spaceUuid); - } + if (!space) this.throwNotFound('Space', spaceUuid); return space; } diff --git a/src/space/space.module.ts b/src/space/space.module.ts index 2d95d7a..8d3bc6e 100644 --- a/src/space/space.module.ts +++ b/src/space/space.module.ts @@ -11,6 +11,7 @@ import { } from './controllers'; import { SpaceDeviceService, + SpaceLinkService, SpaceSceneService, SpaceService, SpaceUserService, @@ -55,6 +56,7 @@ import { DeviceStatusLogRepository } from '@app/common/modules/device-status-log ProductRepository, SubSpaceService, SpaceDeviceService, + SpaceLinkService, SubspaceDeviceService, SpaceRepository, DeviceRepository, From 2ed5d9dce7a05c1517021ef5d5d6b4329f9aee97 Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Wed, 20 Nov 2024 16:16:17 +0400 Subject: [PATCH 06/12] Added space product relation --- libs/common/src/database/database.module.ts | 2 ++ .../product/entities/product.entity.ts | 4 +++ .../space/entities/space-product.entity.ts | 32 +++++++++++++++++++ .../modules/space/entities/space.entity.ts | 4 +++ .../space/repositories/space.repository.ts | 8 +++++ 5 files changed, 50 insertions(+) create mode 100644 libs/common/src/modules/space/entities/space-product.entity.ts diff --git a/libs/common/src/database/database.module.ts b/libs/common/src/database/database.module.ts index 868ef5a..92f3db6 100644 --- a/libs/common/src/database/database.module.ts +++ b/libs/common/src/database/database.module.ts @@ -23,6 +23,7 @@ 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'; @Module({ imports: [ @@ -49,6 +50,7 @@ import { SceneDeviceEntity } from '../modules/scene-device/entities'; SpaceEntity, SubspaceEntity, SpaceTypeEntity, + SpaceProductEntity, UserSpaceEntity, DeviceUserPermissionEntity, UserRoleEntity, diff --git a/libs/common/src/modules/product/entities/product.entity.ts b/libs/common/src/modules/product/entities/product.entity.ts index 6553dc1..a7cf957 100644 --- a/libs/common/src/modules/product/entities/product.entity.ts +++ b/libs/common/src/modules/product/entities/product.entity.ts @@ -2,6 +2,7 @@ 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'; @Entity({ name: 'product' }) export class ProductEntity extends AbstractEntity { @@ -21,6 +22,9 @@ export class ProductEntity extends AbstractEntity { }) public prodType: string; + @OneToMany(() => SpaceProductEntity, (spaceProduct) => spaceProduct.product) + spaceProducts: SpaceProductEntity[]; + @OneToMany( () => DeviceEntity, (devicesProductEntity) => devicesProductEntity.productDevice, diff --git a/libs/common/src/modules/space/entities/space-product.entity.ts b/libs/common/src/modules/space/entities/space-product.entity.ts new file mode 100644 index 0000000..3dd6cbe --- /dev/null +++ b/libs/common/src/modules/space/entities/space-product.entity.ts @@ -0,0 +1,32 @@ +import { Column, Entity, ManyToOne, JoinColumn } from 'typeorm'; +import { SpaceEntity } from './space.entity'; +import { AbstractEntity } from '../../abstract/entities/abstract.entity'; +import { ProductEntity } from '../../product/entities'; + +@Entity({ name: 'space-product' }) +export class SpaceProductEntity extends AbstractEntity { + @ManyToOne(() => SpaceEntity, (space) => space.spaceProducts, { + nullable: false, + onDelete: 'CASCADE', + }) + @JoinColumn({ name: 'space_id' }) + space: SpaceEntity; + + @ManyToOne(() => ProductEntity, (product) => product.spaceProducts, { + nullable: false, + onDelete: 'CASCADE', + }) + @JoinColumn({ name: 'product_id' }) + product: ProductEntity; + + @Column({ + nullable: false, + type: 'int', + }) + productCount: number; + + 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 196a522..837b875 100644 --- a/libs/common/src/modules/space/entities/space.entity.ts +++ b/libs/common/src/modules/space/entities/space.entity.ts @@ -12,6 +12,7 @@ import { UserSpaceEntity } from '../../user/entities'; import { DeviceEntity } from '../../device/entities'; import { CommunityEntity } from '../../community/entities'; import { SubspaceEntity } from './subspace.entity'; +import { SpaceProductEntity } from './space-product.entity'; @Entity({ name: 'space-type' }) export class SpaceTypeEntity extends AbstractEntity { @@ -83,6 +84,9 @@ export class SpaceEntity extends AbstractEntity { ) devices: DeviceEntity[]; + @OneToMany(() => SpaceProductEntity, (spaceProduct) => spaceProduct.space) + spaceProducts: SpaceProductEntity[]; + 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 5e0962c..d34fc24 100644 --- a/libs/common/src/modules/space/repositories/space.repository.ts +++ b/libs/common/src/modules/space/repositories/space.repository.ts @@ -1,6 +1,7 @@ import { DataSource, Repository } from 'typeorm'; import { Injectable } from '@nestjs/common'; import { SpaceEntity, SpaceTypeEntity, SubspaceEntity } from '../entities'; +import { SpaceProductEntity } from '../entities/space-product.entity'; @Injectable() export class SpaceRepository extends Repository { @@ -22,3 +23,10 @@ export class SubspaceRepository extends Repository { super(SubspaceEntity, dataSource.createEntityManager()); } } + +@Injectable() +export class SpaceProductRepository extends Repository { + constructor(private dataSource: DataSource) { + super(SpaceProductEntity, dataSource.createEntityManager()); + } +} From bc1638aba488daf69cff691e477279968cecb042 Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Wed, 20 Nov 2024 16:16:58 +0400 Subject: [PATCH 07/12] changed naming --- .../common/src/modules/space/entities/space-product.entity.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 3dd6cbe..92ad411 100644 --- a/libs/common/src/modules/space/entities/space-product.entity.ts +++ b/libs/common/src/modules/space/entities/space-product.entity.ts @@ -9,14 +9,14 @@ export class SpaceProductEntity extends AbstractEntity { nullable: false, onDelete: 'CASCADE', }) - @JoinColumn({ name: 'space_id' }) + @JoinColumn({ name: 'space_uuid' }) space: SpaceEntity; @ManyToOne(() => ProductEntity, (product) => product.spaceProducts, { nullable: false, onDelete: 'CASCADE', }) - @JoinColumn({ name: 'product_id' }) + @JoinColumn({ name: 'product_uuid' }) product: ProductEntity; @Column({ From 11564eece780f565876f3eef7bb2bd7852762f26 Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Wed, 20 Nov 2024 16:34:44 +0400 Subject: [PATCH 08/12] added space product service --- src/space/dtos/add.space.dto.ts | 17 ++++ src/space/services/index.ts | 1 + src/space/services/space-products/index.ts | 1 + .../space-products/space-products.service.ts | 95 +++++++++++++++++++ src/space/services/space.service.ts | 11 ++- src/space/space.module.ts | 4 + 6 files changed, 128 insertions(+), 1 deletion(-) create mode 100644 src/space/services/space-products/index.ts create mode 100644 src/space/services/space-products/space-products.service.ts diff --git a/src/space/dtos/add.space.dto.ts b/src/space/dtos/add.space.dto.ts index a33bb34..832287f 100644 --- a/src/space/dtos/add.space.dto.ts +++ b/src/space/dtos/add.space.dto.ts @@ -1,10 +1,13 @@ import { ApiProperty } from '@nestjs/swagger'; +import { Type } from 'class-transformer'; import { + IsArray, IsBoolean, IsNotEmpty, IsOptional, IsString, IsUUID, + ValidateNested, } from 'class-validator'; export class AddSpaceDto { @@ -32,6 +35,11 @@ export class AddSpaceDto { }) @IsBoolean() isPrivate: boolean; + + @IsArray() + @ValidateNested({ each: true }) + @Type(() => ProductAssignmentDto) + products: ProductAssignmentDto[]; } export class AddUserSpaceDto { @@ -69,7 +77,16 @@ export class AddUserSpaceUsingCodeDto { @IsString() @IsNotEmpty() public inviteCode: string; + constructor(dto: Partial) { Object.assign(this, dto); } } + +class ProductAssignmentDto { + @IsNotEmpty() + productId: string; + + @IsNotEmpty() + count: number; +} diff --git a/src/space/services/index.ts b/src/space/services/index.ts index c4e9405..89f8d9e 100644 --- a/src/space/services/index.ts +++ b/src/space/services/index.ts @@ -3,3 +3,4 @@ export * from './space-user.service'; export * from './space-device.service'; export * from './subspace'; export * from './space-scene.service'; +export * from './space-products'; diff --git a/src/space/services/space-products/index.ts b/src/space/services/space-products/index.ts new file mode 100644 index 0000000..d0b92d2 --- /dev/null +++ b/src/space/services/space-products/index.ts @@ -0,0 +1 @@ +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 new file mode 100644 index 0000000..7c4d634 --- /dev/null +++ b/src/space/services/space-products/space-products.service.ts @@ -0,0 +1,95 @@ +import { Injectable, HttpException, HttpStatus } from '@nestjs/common'; +import { ProductRepository } from '@app/common/modules/product/repositories'; +import { SpaceEntity } from '@app/common/modules/space/entities'; +import { SpaceProductEntity } from '@app/common/modules/space/entities/space-product.entity'; +import { SpaceProductRepository } from '@app/common/modules/space/repositories'; +import { In } from 'typeorm'; + +@Injectable() +export class SpaceProductService { + constructor( + private readonly productRepository: ProductRepository, + private readonly spaceProductRepository: SpaceProductRepository, + ) {} + + async assignProductsToSpace( + space: SpaceEntity, + products: { productId: string; count: number }[], + ): Promise { + try { + const uniqueProducts = this.validateUniqueProducts(products); + const productEntities = await this.getProductEntities(uniqueProducts); + + const spaceProductEntities = uniqueProducts.map( + ({ productId, count }) => { + const product = productEntities.get(productId); + if (!product) { + throw new HttpException( + `Product with ID ${productId} not found`, + HttpStatus.NOT_FOUND, + ); + } + + return this.spaceProductRepository.create({ + space, + product, + productCount: count, + }); + }, + ); + + return await this.spaceProductRepository.save(spaceProductEntities); + } catch (error) { + console.error('Error assigning products to space:', error); + + if (!(error instanceof HttpException)) { + throw new HttpException( + 'An error occurred while assigning products to the space', + HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + + throw error; + } + } + + private validateUniqueProducts( + products: { productId: string; count: number }[], + ): { productId: string; count: number }[] { + 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: { productId: string; count: number }[], + ): Promise> { + try { + const productIds = products.map((p) => p.productId); + + const productEntities = await this.productRepository.find({ + where: { prodId: In(productIds) }, + }); + + return new Map(productEntities.map((p) => [p.prodId, p])); + } catch (error) { + console.error('Error fetching product entities:', error); + throw new HttpException( + 'Failed to fetch product entities', + HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + } +} diff --git a/src/space/services/space.service.ts b/src/space/services/space.service.ts index 78d45d7..7340c7c 100644 --- a/src/space/services/space.service.ts +++ b/src/space/services/space.service.ts @@ -11,12 +11,14 @@ import { BaseResponseDto } from '@app/common/dto/base.response.dto'; import { CommunityRepository } from '@app/common/modules/community/repositories'; import { SpaceEntity } from '@app/common/modules/space/entities'; import { generateRandomString } from '@app/common/helper/randomString'; +import { SpaceProductService } from './space-products'; @Injectable() export class SpaceService { constructor( private readonly spaceRepository: SpaceRepository, private readonly communityRepository: CommunityRepository, + private readonly spaceProductService: SpaceProductService, ) {} async createSpace( @@ -25,7 +27,7 @@ export class SpaceService { ): Promise { let parent: SpaceEntity | null = null; - const { parentUuid } = addSpaceDto; + const { parentUuid, products } = addSpaceDto; const community = await this.communityRepository.findOne({ where: { uuid: communityId }, }); @@ -60,6 +62,13 @@ export class SpaceService { await this.spaceRepository.save(newSpace); + if (products && products.length > 0) { + await this.spaceProductService.assignProductsToSpace( + newSpace, + products, + ); + } + return new SuccessResponseDto({ statusCode: HttpStatus.CREATED, data: newSpace, diff --git a/src/space/space.module.ts b/src/space/space.module.ts index c840bd9..030cb95 100644 --- a/src/space/space.module.ts +++ b/src/space/space.module.ts @@ -11,6 +11,7 @@ import { } from './controllers'; import { SpaceDeviceService, + SpaceProductService, SpaceSceneService, SpaceService, SpaceUserService, @@ -18,6 +19,7 @@ import { SubSpaceService, } from './services'; import { + SpaceProductRepository, SpaceRepository, SubspaceRepository, } from '@app/common/modules/space/repositories'; @@ -71,6 +73,8 @@ import { SceneDeviceRepository } from '@app/common/modules/scene-device/reposito DeviceStatusFirebaseService, DeviceStatusLogRepository, SceneDeviceRepository, + SpaceProductService, + SpaceProductRepository, ], exports: [SpaceService], }) From c0b8e0d779edc5a37f4855892fd1051ed919b5b9 Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Thu, 21 Nov 2024 08:20:33 +0400 Subject: [PATCH 09/12] merged with space-product --- libs/common/src/modules/space/entities/space.entity.ts | 1 + src/space/services/space.service.ts | 7 ++++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/libs/common/src/modules/space/entities/space.entity.ts b/libs/common/src/modules/space/entities/space.entity.ts index 16d5786..38d4c5e 100644 --- a/libs/common/src/modules/space/entities/space.entity.ts +++ b/libs/common/src/modules/space/entities/space.entity.ts @@ -87,6 +87,7 @@ export class SpaceEntity extends AbstractEntity { type: 'text', }) public icon: string; + @OneToMany(() => SpaceProductEntity, (spaceProduct) => spaceProduct.space) spaceProducts: SpaceProductEntity[]; diff --git a/src/space/services/space.service.ts b/src/space/services/space.service.ts index f8dcf71..7e22f40 100644 --- a/src/space/services/space.service.ts +++ b/src/space/services/space.service.ts @@ -74,7 +74,12 @@ export class SpaceService { // Get all spaces related to the community, including the parent-child relations const spaces = await this.spaceRepository.find({ where: { community: { uuid: communityUuid } }, - relations: ['parent', 'children', 'incomingConnections'], // Include parent and children relations + relations: [ + 'parent', + 'children', + 'incomingConnections', + 'spaceProducts', + ], // Include parent and children relations }); // Organize spaces into a hierarchical structure From 7d627029c470481769042a9dee8117e688afaa80 Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Thu, 21 Nov 2024 09:14:29 +0400 Subject: [PATCH 10/12] fixed uuid --- src/space/services/space-products/space-products.service.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/space/services/space-products/space-products.service.ts b/src/space/services/space-products/space-products.service.ts index 7c4d634..8484426 100644 --- a/src/space/services/space-products/space-products.service.ts +++ b/src/space/services/space-products/space-products.service.ts @@ -80,10 +80,10 @@ export class SpaceProductService { const productIds = products.map((p) => p.productId); const productEntities = await this.productRepository.find({ - where: { prodId: In(productIds) }, + where: { uuid: In(productIds) }, }); - return new Map(productEntities.map((p) => [p.prodId, p])); + return new Map(productEntities.map((p) => [p.uuid, p])); } catch (error) { console.error('Error fetching product entities:', error); throw new HttpException( From 3f5ae396e3c7f4d5ea82079e49398ccff3b538a0 Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Thu, 21 Nov 2024 10:44:55 +0400 Subject: [PATCH 11/12] added product details --- src/space/services/space.service.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/space/services/space.service.ts b/src/space/services/space.service.ts index 7e22f40..7ee2041 100644 --- a/src/space/services/space.service.ts +++ b/src/space/services/space.service.ts @@ -79,6 +79,7 @@ export class SpaceService { 'children', 'incomingConnections', 'spaceProducts', + 'spaceProducts.product', ], // Include parent and children relations }); From 67c5ebaa41e1e1d22b9547e158dffe1bcd5e4042 Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Thu, 21 Nov 2024 10:59:58 +0400 Subject: [PATCH 12/12] product update --- .../space-products/space-products.service.ts | 70 ++++++++++++++----- src/space/services/space.service.ts | 11 ++- 2 files changed, 62 insertions(+), 19 deletions(-) diff --git a/src/space/services/space-products/space-products.service.ts b/src/space/services/space-products/space-products.service.ts index 8484426..5b218e4 100644 --- a/src/space/services/space-products/space-products.service.ts +++ b/src/space/services/space-products/space-products.service.ts @@ -20,25 +20,61 @@ export class SpaceProductService { const uniqueProducts = this.validateUniqueProducts(products); const productEntities = await this.getProductEntities(uniqueProducts); - const spaceProductEntities = uniqueProducts.map( - ({ productId, count }) => { - const product = productEntities.get(productId); - if (!product) { - throw new HttpException( - `Product with ID ${productId} not found`, - HttpStatus.NOT_FOUND, - ); - } - - return this.spaceProductRepository.create({ - space, - product, - productCount: count, - }); + // Fetch existing space products + const existingSpaceProducts = await this.spaceProductRepository.find({ + where: { + space: { + uuid: space.uuid, + }, }, - ); + relations: ['product'], + }); - return await this.spaceProductRepository.save(spaceProductEntities); + const updatedProducts = []; + const newProducts = []; + + for (const { productId, count } of uniqueProducts) { + const product = productEntities.get(productId); + if (!product) { + throw new HttpException( + `Product with ID ${productId} not found`, + HttpStatus.NOT_FOUND, + ); + } + + // Check if product already exists in the space + const existingProduct = existingSpaceProducts.find( + (spaceProduct) => spaceProduct.product.uuid === productId, + ); + + if (existingProduct) { + // If count is different, update the existing record + if (existingProduct.productCount !== count) { + existingProduct.productCount = count; + updatedProducts.push(existingProduct); + } + } else { + // Add new product if it doesn't exist + newProducts.push( + this.spaceProductRepository.create({ + space, + product, + productCount: count, + }), + ); + } + } + + // Save updates and new records + if (updatedProducts.length > 0) { + await this.spaceProductRepository.save(updatedProducts); + } + + if (newProducts.length > 0) { + await this.spaceProductRepository.save(newProducts); + } + + return [...updatedProducts, ...newProducts]; } catch (error) { console.error('Error assigning products to space:', error); diff --git a/src/space/services/space.service.ts b/src/space/services/space.service.ts index 7ee2041..44d27ba 100644 --- a/src/space/services/space.service.ts +++ b/src/space/services/space.service.ts @@ -160,14 +160,21 @@ export class SpaceService { ); // If a parentId is provided, check if the parent exists - const { parentUuid } = updateSpaceDto; + const { parentUuid, products } = updateSpaceDto; const parent = parentUuid ? await this.validateSpace(parentUuid) : null; // Update other space properties from updateSpaceDto Object.assign(space, updateSpaceDto, { parent }); // Save the updated space - await this.spaceRepository.save(space); + const updatedSpace = await this.spaceRepository.save(space); + + if (products && products.length > 0) { + await this.spaceProductService.assignProductsToSpace( + updatedSpace, + products, + ); + } return new SuccessResponseDto({ message: `Space with ID ${spaceUuid} successfully updated`,