space cleanup

This commit is contained in:
hannathkadher
2024-12-24 08:12:56 +04:00
parent fef44b3c4f
commit 97b53bf417
32 changed files with 12 additions and 732 deletions

View File

@ -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,
],

View File

@ -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],

View File

@ -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[];
}

View File

@ -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';

View File

@ -1 +0,0 @@
export * from './space-product-items.service';

View File

@ -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,
);
}
}
}

View File

@ -1 +0,0 @@
export * from './space-products.service';

View File

@ -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<SpaceProductEntity[]> {
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<Map<string, any>> {
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<SpaceProductEntity[]> {
return queryRunner.manager.find(SpaceProductEntity, {
where: { space: { uuid: space.uuid } },
relations: ['product'],
});
}
private async updateExistingProducts(
existingSpaceProducts: SpaceProductEntity[],
uniqueProducts: ProductAssignmentDto[],
productEntities: Map<string, any>,
queryRunner: QueryRunner,
): Promise<SpaceProductEntity[]> {
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<string, any>,
space: SpaceEntity,
queryRunner: QueryRunner,
): Promise<SpaceProductEntity[]> {
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<ProductEntity> {
const product = await this.productService.findOne(productId);
return product.data;
}
}

View File

@ -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();

View File

@ -1,4 +1,2 @@
export * from './subspace.service';
export * from './subspace-device.service';
export * from './subspace-product-item.service';
export * from './subspace-product.service';

View File

@ -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,
);
}
}
}

View File

@ -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<void> {
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<ProductEntity> {
const product = await this.productService.findOne(productId);
return product.data;
}
}

View File

@ -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(

View File

@ -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],
})