mirror of
https://github.com/SyncrowIOT/backend.git
synced 2025-11-27 06:44:55 +00:00
updated space update
This commit is contained in:
@ -58,4 +58,9 @@ export class SpaceModelEntity extends AbstractEntity<SpaceModelDto> {
|
|||||||
|
|
||||||
@OneToMany(() => TagModel, (tag) => tag.spaceModel)
|
@OneToMany(() => TagModel, (tag) => tag.spaceModel)
|
||||||
tags: TagModel[];
|
tags: TagModel[];
|
||||||
|
|
||||||
|
constructor(partial: Partial<SpaceModelEntity>) {
|
||||||
|
super();
|
||||||
|
Object.assign(this, partial);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -30,10 +30,10 @@ export class SubspaceModelEntity extends AbstractEntity<SubSpaceModelDto> {
|
|||||||
)
|
)
|
||||||
public spaceModel: SpaceModelEntity;
|
public spaceModel: SpaceModelEntity;
|
||||||
|
|
||||||
@OneToMany(() => SubspaceEntity, (space) => space.subSpaceModel, {
|
@OneToMany(() => SubspaceEntity, (subspace) => subspace.subSpaceModel, {
|
||||||
cascade: true,
|
cascade: true,
|
||||||
})
|
})
|
||||||
public spaces: SubspaceEntity[];
|
public subspaceModel: SubspaceEntity[];
|
||||||
|
|
||||||
@Column({
|
@Column({
|
||||||
nullable: false,
|
nullable: false,
|
||||||
|
|||||||
@ -103,8 +103,9 @@ export class SpaceEntity extends AbstractEntity<SpaceDto> {
|
|||||||
@OneToMany(() => SceneEntity, (scene) => scene.space)
|
@OneToMany(() => SceneEntity, (scene) => scene.space)
|
||||||
scenes: SceneEntity[];
|
scenes: SceneEntity[];
|
||||||
|
|
||||||
@ManyToOne(() => SpaceModelEntity, { nullable: true })
|
@ManyToOne(() => SpaceModelEntity, (spaceModel) => spaceModel.spaces, {
|
||||||
@JoinColumn({ name: 'space_model_uuid' })
|
nullable: true,
|
||||||
|
})
|
||||||
spaceModel?: SpaceModelEntity;
|
spaceModel?: SpaceModelEntity;
|
||||||
|
|
||||||
@OneToMany(
|
@OneToMany(
|
||||||
|
|||||||
@ -37,8 +37,9 @@ export class SubspaceEntity extends AbstractEntity<SubspaceDto> {
|
|||||||
})
|
})
|
||||||
devices: DeviceEntity[];
|
devices: DeviceEntity[];
|
||||||
|
|
||||||
@ManyToOne(() => SubspaceModelEntity, { nullable: true })
|
@ManyToOne(() => SubspaceModelEntity, (subspace) => subspace.subspaceModel, {
|
||||||
@JoinColumn({ name: 'subspace_model_uuid' })
|
nullable: true,
|
||||||
|
})
|
||||||
subSpaceModel?: SubspaceModelEntity;
|
subSpaceModel?: SubspaceModelEntity;
|
||||||
|
|
||||||
@OneToMany(() => TagEntity, (tag) => tag.subspace)
|
@OneToMany(() => TagEntity, (tag) => tag.subspace)
|
||||||
|
|||||||
@ -106,8 +106,7 @@ export class SpaceModelService {
|
|||||||
pageable.where = {
|
pageable.where = {
|
||||||
project: { uuid: param.projectUuid },
|
project: { uuid: param.projectUuid },
|
||||||
};
|
};
|
||||||
pageable.include =
|
pageable.include = 'subspaceModels,tags,subspaceModels.tags';
|
||||||
'subspaceModels,spaceProductModels,subspaceModels.productModels,subspaceModels.productModels.itemModels,spaceProductModels.items';
|
|
||||||
|
|
||||||
const customModel = TypeORMCustomModel(this.spaceModelRepository);
|
const customModel = TypeORMCustomModel(this.spaceModelRepository);
|
||||||
|
|
||||||
@ -252,9 +251,15 @@ export class SpaceModelService {
|
|||||||
const spaceModel = await this.spaceModelRepository.findOne({
|
const spaceModel = await this.spaceModelRepository.findOne({
|
||||||
where: {
|
where: {
|
||||||
uuid,
|
uuid,
|
||||||
disabled: true,
|
disabled: false,
|
||||||
},
|
},
|
||||||
relations: ['subspaceModels', 'tags'],
|
relations: [
|
||||||
|
'subspaceModels',
|
||||||
|
'tags',
|
||||||
|
'tags.product',
|
||||||
|
'subspaceModels.tags',
|
||||||
|
'subspaceModels.tags.product',
|
||||||
|
],
|
||||||
});
|
});
|
||||||
if (!spaceModel) {
|
if (!spaceModel) {
|
||||||
throw new HttpException('space model not found', HttpStatus.NOT_FOUND);
|
throw new HttpException('space model not found', HttpStatus.NOT_FOUND);
|
||||||
|
|||||||
@ -37,6 +37,6 @@ const CommandHandlers = [PropogateSubspaceHandler];
|
|||||||
TagModelService,
|
TagModelService,
|
||||||
TagModelRepository,
|
TagModelRepository,
|
||||||
],
|
],
|
||||||
exports: [CqrsModule],
|
exports: [CqrsModule, SpaceModelService],
|
||||||
})
|
})
|
||||||
export class SpaceModelModule {}
|
export class SpaceModelModule {}
|
||||||
|
|||||||
@ -74,7 +74,6 @@ export class AddSpaceDto {
|
|||||||
type: [AddSubspaceDto],
|
type: [AddSubspaceDto],
|
||||||
})
|
})
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
@IsArray()
|
|
||||||
@ValidateNested({ each: true })
|
@ValidateNested({ each: true })
|
||||||
@Type(() => AddSubspaceDto)
|
@Type(() => AddSubspaceDto)
|
||||||
subspaces?: AddSubspaceDto[];
|
subspaces?: AddSubspaceDto[];
|
||||||
@ -83,7 +82,6 @@ export class AddSpaceDto {
|
|||||||
description: 'List of tags associated with the space model',
|
description: 'List of tags associated with the space model',
|
||||||
type: [CreateTagDto],
|
type: [CreateTagDto],
|
||||||
})
|
})
|
||||||
@IsArray()
|
|
||||||
@ValidateNested({ each: true })
|
@ValidateNested({ each: true })
|
||||||
@Type(() => CreateTagDto)
|
@Type(() => CreateTagDto)
|
||||||
tags?: CreateTagDto[];
|
tags?: CreateTagDto[];
|
||||||
|
|||||||
@ -46,7 +46,7 @@ export class UpdateSpaceDto {
|
|||||||
@IsArray()
|
@IsArray()
|
||||||
@ValidateNested({ each: true })
|
@ValidateNested({ each: true })
|
||||||
@Type(() => ModifySubspaceDto)
|
@Type(() => ModifySubspaceDto)
|
||||||
subspaceModels?: ModifySubspaceDto[];
|
subspace?: ModifySubspaceDto[];
|
||||||
|
|
||||||
@ApiPropertyOptional({
|
@ApiPropertyOptional({
|
||||||
description:
|
description:
|
||||||
|
|||||||
@ -43,7 +43,7 @@ export class ValidationService {
|
|||||||
|
|
||||||
async validateSpace(spaceUuid: string): Promise<SpaceEntity> {
|
async validateSpace(spaceUuid: string): Promise<SpaceEntity> {
|
||||||
const space = await this.spaceRepository.findOne({
|
const space = await this.spaceRepository.findOne({
|
||||||
where: { uuid: spaceUuid },
|
where: { uuid: spaceUuid, disabled: false },
|
||||||
relations: ['subspaces', 'tags'],
|
relations: ['subspaces', 'tags'],
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -62,11 +62,10 @@ export class ValidationService {
|
|||||||
where: { uuid: spaceModelUuid },
|
where: { uuid: spaceModelUuid },
|
||||||
relations: [
|
relations: [
|
||||||
'subspaceModels',
|
'subspaceModels',
|
||||||
'subspaceModels.productModels.product',
|
'subspaceModels.tags',
|
||||||
'subspaceModels.productModels',
|
'tags',
|
||||||
'spaceProductModels',
|
'subspaceModels.tags.product',
|
||||||
'spaceProductModels.product',
|
'tags.product',
|
||||||
'spaceProductModels.items',
|
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -7,7 +7,9 @@ import {
|
|||||||
} from '@nestjs/common';
|
} from '@nestjs/common';
|
||||||
import {
|
import {
|
||||||
AddSpaceDto,
|
AddSpaceDto,
|
||||||
|
AddSubspaceDto,
|
||||||
CommunitySpaceParam,
|
CommunitySpaceParam,
|
||||||
|
CreateTagDto,
|
||||||
GetSpaceParam,
|
GetSpaceParam,
|
||||||
UpdateSpaceDto,
|
UpdateSpaceDto,
|
||||||
} from '../dtos';
|
} from '../dtos';
|
||||||
@ -17,10 +19,11 @@ import { SpaceEntity } from '@app/common/modules/space/entities';
|
|||||||
import { generateRandomString } from '@app/common/helper/randomString';
|
import { generateRandomString } from '@app/common/helper/randomString';
|
||||||
import { SpaceLinkService } from './space-link';
|
import { SpaceLinkService } from './space-link';
|
||||||
import { SubSpaceService } from './subspace';
|
import { SubSpaceService } from './subspace';
|
||||||
import { DataSource, Not } from 'typeorm';
|
import { DataSource, Not, QueryRunner } from 'typeorm';
|
||||||
import { ValidationService } from './space-validation.service';
|
import { ValidationService } from './space-validation.service';
|
||||||
import { ORPHAN_SPACE_NAME } from '@app/common/constants/orphan-constant';
|
import { ORPHAN_SPACE_NAME } from '@app/common/constants/orphan-constant';
|
||||||
import { TagService } from './tag';
|
import { TagService } from './tag';
|
||||||
|
import { SpaceModelService } from 'src/space-model/services';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class SpaceService {
|
export class SpaceService {
|
||||||
@ -31,6 +34,7 @@ export class SpaceService {
|
|||||||
private readonly subSpaceService: SubSpaceService,
|
private readonly subSpaceService: SubSpaceService,
|
||||||
private readonly validationService: ValidationService,
|
private readonly validationService: ValidationService,
|
||||||
private readonly tagService: TagService,
|
private readonly tagService: TagService,
|
||||||
|
private readonly spaceModelService: SpaceModelService,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
async createSpace(
|
async createSpace(
|
||||||
@ -78,31 +82,23 @@ export class SpaceService {
|
|||||||
|
|
||||||
const newSpace = await queryRunner.manager.save(space);
|
const newSpace = await queryRunner.manager.save(space);
|
||||||
|
|
||||||
if (direction && parent) {
|
await Promise.all([
|
||||||
await this.spaceLinkService.saveSpaceLink(
|
spaceModelUuid &&
|
||||||
parent.uuid,
|
this.createFromModel(spaceModelUuid, queryRunner, newSpace),
|
||||||
newSpace.uuid,
|
direction && parent
|
||||||
direction,
|
? this.spaceLinkService.saveSpaceLink(
|
||||||
);
|
parent.uuid,
|
||||||
}
|
newSpace.uuid,
|
||||||
|
direction,
|
||||||
if (subspaces?.length) {
|
)
|
||||||
await this.subSpaceService.createSubspacesFromDto(
|
: Promise.resolve(),
|
||||||
subspaces,
|
subspaces?.length
|
||||||
newSpace,
|
? this.createSubspaces(subspaces, newSpace, queryRunner, tags)
|
||||||
queryRunner,
|
: Promise.resolve(),
|
||||||
tags,
|
tags?.length
|
||||||
);
|
? this.createTags(tags, queryRunner, newSpace)
|
||||||
}
|
: Promise.resolve(),
|
||||||
|
]);
|
||||||
if (tags?.length) {
|
|
||||||
newSpace.tags = await this.tagService.createTags(
|
|
||||||
tags,
|
|
||||||
queryRunner,
|
|
||||||
newSpace,
|
|
||||||
null,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
await queryRunner.commitTransaction();
|
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(
|
async getSpacesHierarchyForCommunity(
|
||||||
params: CommunitySpaceParam,
|
params: CommunitySpaceParam,
|
||||||
): Promise<BaseResponseDto> {
|
): Promise<BaseResponseDto> {
|
||||||
@ -274,29 +305,32 @@ export class SpaceService {
|
|||||||
|
|
||||||
if (space.spaceName === ORPHAN_SPACE_NAME) {
|
if (space.spaceName === ORPHAN_SPACE_NAME) {
|
||||||
throw new HttpException(
|
throw new HttpException(
|
||||||
`space ${ORPHAN_SPACE_NAME} cannot be updated`,
|
`Space "${ORPHAN_SPACE_NAME}" cannot be updated`,
|
||||||
HttpStatus.BAD_REQUEST,
|
HttpStatus.BAD_REQUEST,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (updateSpaceDto.spaceName) space.spaceName = updateSpaceDto.spaceName;
|
|
||||||
|
|
||||||
if (updateSpaceDto.x) space.x = updateSpaceDto.x;
|
this.updateSpaceProperties(space, updateSpaceDto);
|
||||||
|
|
||||||
if (updateSpaceDto.y) space.y = updateSpaceDto.y;
|
|
||||||
if (updateSpaceDto.icon) space.icon = updateSpaceDto.icon;
|
|
||||||
if (updateSpaceDto.icon) space.icon = updateSpaceDto.icon;
|
|
||||||
|
|
||||||
await queryRunner.manager.save(space);
|
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(
|
await this.subSpaceService.modifySubSpace(
|
||||||
updateSpaceDto.subspaceModels,
|
updateSpaceDto.subspace,
|
||||||
space,
|
space,
|
||||||
queryRunner,
|
queryRunner,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (updateSpaceDto.tags) {
|
if (hasTags) {
|
||||||
await this.tagService.modifyTags(
|
await this.tagService.modifyTags(
|
||||||
updateSpaceDto.tags,
|
updateSpaceDto.tags,
|
||||||
queryRunner,
|
queryRunner,
|
||||||
@ -317,6 +351,7 @@ export class SpaceService {
|
|||||||
if (error instanceof HttpException) {
|
if (error instanceof HttpException) {
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new HttpException(
|
throw new HttpException(
|
||||||
'An error occurred while updating the space',
|
'An error occurred while updating the space',
|
||||||
HttpStatus.INTERNAL_SERVER_ERROR,
|
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(
|
async getSpacesHierarchyForSpace(
|
||||||
params: GetSpaceParam,
|
params: GetSpaceParam,
|
||||||
): Promise<BaseResponseDto> {
|
): Promise<BaseResponseDto> {
|
||||||
@ -339,7 +386,7 @@ export class SpaceService {
|
|||||||
try {
|
try {
|
||||||
// Get all spaces that are children of the provided space, including the parent-child relations
|
// Get all spaces that are children of the provided space, including the parent-child relations
|
||||||
const spaces = await this.spaceRepository.find({
|
const spaces = await this.spaceRepository.find({
|
||||||
where: { parent: { uuid: spaceUuid } },
|
where: { parent: { uuid: spaceUuid }, disabled: false },
|
||||||
relations: ['parent', 'children'], // Include parent and children relations
|
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<void> {
|
||||||
|
space.subspaces = await this.subSpaceService.createSubspacesFromDto(
|
||||||
|
subspaces,
|
||||||
|
space,
|
||||||
|
queryRunner,
|
||||||
|
tags,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async createTags(
|
||||||
|
tags: CreateTagDto[],
|
||||||
|
queryRunner: QueryRunner,
|
||||||
|
space: SpaceEntity,
|
||||||
|
): Promise<void> {
|
||||||
|
space.tags = await this.tagService.createTags(tags, queryRunner, space);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -19,11 +19,9 @@ import { In, QueryRunner } from 'typeorm';
|
|||||||
import {
|
import {
|
||||||
SpaceEntity,
|
SpaceEntity,
|
||||||
SubspaceEntity,
|
SubspaceEntity,
|
||||||
|
TagEntity,
|
||||||
} from '@app/common/modules/space/entities';
|
} from '@app/common/modules/space/entities';
|
||||||
import {
|
import { SubspaceModelEntity } from '@app/common/modules/space-model';
|
||||||
SpaceModelEntity,
|
|
||||||
SubspaceModelEntity,
|
|
||||||
} from '@app/common/modules/space-model';
|
|
||||||
import { ValidationService } from '../space-validation.service';
|
import { ValidationService } from '../space-validation.service';
|
||||||
import { SubspaceRepository } from '@app/common/modules/space/repositories/subspace.repository';
|
import { SubspaceRepository } from '@app/common/modules/space/repositories/subspace.repository';
|
||||||
import { TagService } from '../tag';
|
import { TagService } from '../tag';
|
||||||
@ -49,7 +47,6 @@ export class SubSpaceService {
|
|||||||
const subspaces = subspaceData.map((data) =>
|
const subspaces = subspaceData.map((data) =>
|
||||||
queryRunner.manager.create(this.subspaceRepository.target, data),
|
queryRunner.manager.create(this.subspaceRepository.target, data),
|
||||||
);
|
);
|
||||||
|
|
||||||
return await queryRunner.manager.save(subspaces);
|
return await queryRunner.manager.save(subspaces);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
throw new HttpException(
|
throw new HttpException(
|
||||||
@ -60,21 +57,30 @@ export class SubSpaceService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async createSubSpaceFromModel(
|
async createSubSpaceFromModel(
|
||||||
spaceModel: SpaceModelEntity,
|
subspaceModels: SubspaceModelEntity[],
|
||||||
space: SpaceEntity,
|
space: SpaceEntity,
|
||||||
queryRunner: QueryRunner,
|
queryRunner: QueryRunner,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
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,
|
subspaceName: subSpaceModel.subspaceName,
|
||||||
space,
|
space,
|
||||||
subSpaceModel,
|
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(
|
async createSubspacesFromDto(
|
||||||
@ -317,6 +323,31 @@ export class SubSpaceService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async unlinkModels(
|
||||||
|
subspaces: SubspaceEntity[],
|
||||||
|
queryRunner: QueryRunner,
|
||||||
|
): Promise<void> {
|
||||||
|
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<BaseResponseDto> {
|
async getOne(params: GetSubSpaceParam): Promise<BaseResponseDto> {
|
||||||
await this.validationService.validateSpaceWithinCommunityAndProject(
|
await this.validationService.validateSpaceWithinCommunityAndProject(
|
||||||
params.communityUuid,
|
params.communityUuid,
|
||||||
|
|||||||
@ -5,6 +5,7 @@ import {
|
|||||||
TagEntity,
|
TagEntity,
|
||||||
TagRepository,
|
TagRepository,
|
||||||
} from '@app/common/modules/space';
|
} from '@app/common/modules/space';
|
||||||
|
import { TagModel } from '@app/common/modules/space-model';
|
||||||
import { HttpException, HttpStatus, Injectable } from '@nestjs/common';
|
import { HttpException, HttpStatus, Injectable } from '@nestjs/common';
|
||||||
import { ProductService } from 'src/product/services';
|
import { ProductService } from 'src/product/services';
|
||||||
import { CreateTagDto } from 'src/space/dtos';
|
import { CreateTagDto } from 'src/space/dtos';
|
||||||
@ -25,39 +26,43 @@ export class TagService {
|
|||||||
subspace?: SubspaceEntity,
|
subspace?: SubspaceEntity,
|
||||||
additionalTags?: CreateTagDto[],
|
additionalTags?: CreateTagDto[],
|
||||||
): Promise<TagEntity[]> {
|
): Promise<TagEntity[]> {
|
||||||
if (!tags.length) {
|
this.validateTagsInput(tags);
|
||||||
throw new HttpException('Tags cannot be empty.', HttpStatus.BAD_REQUEST);
|
|
||||||
}
|
|
||||||
|
|
||||||
const combinedTags = additionalTags ? [...tags, ...additionalTags] : tags;
|
const combinedTags = this.combineTags(tags, additionalTags);
|
||||||
const duplicateTags = this.findDuplicateTags(combinedTags);
|
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 {
|
try {
|
||||||
|
const tagEntities = await Promise.all(
|
||||||
|
tags.map(async (tagDto) =>
|
||||||
|
this.prepareTagEntity(tagDto, queryRunner, space, subspace),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
return await queryRunner.manager.save(tagEntities);
|
return await queryRunner.manager.save(tagEntities);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (error instanceof HttpException) {
|
throw this.handleUnexpectedError('Failed to save tags', error);
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new HttpException(
|
|
||||||
'Failed to save tag models due to an unexpected error.',
|
|
||||||
HttpStatus.INTERNAL_SERVER_ERROR,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async createTagsFromModel(
|
||||||
|
queryRunner: QueryRunner,
|
||||||
|
tagModels: TagModel[],
|
||||||
|
space?: SpaceEntity,
|
||||||
|
subspace?: SubspaceEntity,
|
||||||
|
): Promise<void> {
|
||||||
|
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(
|
async updateTag(
|
||||||
tag: ModifyTagDto,
|
tag: ModifyTagDto,
|
||||||
queryRunner: QueryRunner,
|
queryRunner: QueryRunner,
|
||||||
@ -67,48 +72,94 @@ export class TagService {
|
|||||||
try {
|
try {
|
||||||
const existingTag = await this.getTagByUuid(tag.uuid);
|
const existingTag = await this.getTagByUuid(tag.uuid);
|
||||||
|
|
||||||
if (space) {
|
const contextSpace = space ?? subspace?.space;
|
||||||
await this.checkTagReuse(tag.tag, existingTag.product.uuid, space);
|
|
||||||
} else {
|
if (contextSpace) {
|
||||||
await this.checkTagReuse(
|
await this.checkTagReuse(
|
||||||
tag.tag,
|
tag.tag,
|
||||||
existingTag.product.uuid,
|
existingTag.product.uuid,
|
||||||
subspace.space,
|
contextSpace,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tag.tag) {
|
return await queryRunner.manager.save(
|
||||||
existingTag.tag = tag.tag;
|
Object.assign(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,
|
|
||||||
);
|
);
|
||||||
|
} catch (error) {
|
||||||
|
throw this.handleUnexpectedError('Failed to update tags', error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async deleteTags(tagUuids: string[], queryRunner: QueryRunner) {
|
async deleteTags(tagUuids: string[], queryRunner: QueryRunner) {
|
||||||
try {
|
if (!tagUuids?.length) return;
|
||||||
const deletePromises = tagUuids.map((id) =>
|
|
||||||
queryRunner.manager.softDelete(this.tagRepository.target, id),
|
|
||||||
);
|
|
||||||
|
|
||||||
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 };
|
return { message: 'Tags deleted successfully', tagUuids };
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (error instanceof HttpException) {
|
throw this.handleUnexpectedError('Failed to update tags', error);
|
||||||
throw error;
|
}
|
||||||
}
|
}
|
||||||
throw new HttpException(
|
|
||||||
error.message || 'Failed to delete tags',
|
async modifyTags(
|
||||||
HttpStatus.INTERNAL_SERVER_ERROR,
|
tags: ModifyTagDto[],
|
||||||
|
queryRunner: QueryRunner,
|
||||||
|
space?: SpaceEntity,
|
||||||
|
subspace?: SubspaceEntity,
|
||||||
|
): Promise<void> {
|
||||||
|
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;
|
return duplicates;
|
||||||
}
|
}
|
||||||
|
|
||||||
async modifyTags(
|
|
||||||
tags: ModifyTagDto[],
|
|
||||||
queryRunner: QueryRunner,
|
|
||||||
space?: SpaceEntity,
|
|
||||||
subspace?: SubspaceEntity,
|
|
||||||
): Promise<void> {
|
|
||||||
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(
|
private async checkTagReuse(
|
||||||
tag: string,
|
tag: string,
|
||||||
productUuid: string,
|
productUuid: string,
|
||||||
@ -214,11 +223,11 @@ export class TagService {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (space) {
|
await this.checkTagReuse(
|
||||||
await this.checkTagReuse(tagDto.tag, tagDto.productUuid, space);
|
tagDto.tag,
|
||||||
} else {
|
tagDto.productUuid,
|
||||||
await this.checkTagReuse(tagDto.tag, tagDto.productUuid, subspace.space);
|
space ?? subspace.space,
|
||||||
}
|
);
|
||||||
|
|
||||||
return queryRunner.manager.create(TagEntity, {
|
return queryRunner.manager.create(TagEntity, {
|
||||||
tag: tagDto.tag,
|
tag: tagDto.tag,
|
||||||
@ -233,6 +242,7 @@ export class TagService {
|
|||||||
where: { uuid },
|
where: { uuid },
|
||||||
relations: ['product'],
|
relations: ['product'],
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!tag) {
|
if (!tag) {
|
||||||
throw new HttpException(
|
throw new HttpException(
|
||||||
`Tag with ID ${uuid} not found.`,
|
`Tag with ID ${uuid} not found.`,
|
||||||
@ -241,4 +251,39 @@ export class TagService {
|
|||||||
}
|
}
|
||||||
return tag;
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -43,12 +43,18 @@ import { SceneDeviceRepository } from '@app/common/modules/scene-device/reposito
|
|||||||
import { ProjectRepository } from '@app/common/modules/project/repositiories';
|
import { ProjectRepository } from '@app/common/modules/project/repositiories';
|
||||||
import {
|
import {
|
||||||
SpaceModelRepository,
|
SpaceModelRepository,
|
||||||
|
SubspaceModelRepository,
|
||||||
TagModelRepository,
|
TagModelRepository,
|
||||||
} from '@app/common/modules/space-model';
|
} from '@app/common/modules/space-model';
|
||||||
import { CommunityModule } from 'src/community/community.module';
|
import { CommunityModule } from 'src/community/community.module';
|
||||||
import { ValidationService } from './services';
|
import { ValidationService } from './services';
|
||||||
import { SubspaceRepository } from '@app/common/modules/space/repositories/subspace.repository';
|
import { SubspaceRepository } from '@app/common/modules/space/repositories/subspace.repository';
|
||||||
import { TagService } from './services/tag';
|
import { TagService } from './services/tag';
|
||||||
|
import {
|
||||||
|
SpaceModelService,
|
||||||
|
SubSpaceModelService,
|
||||||
|
TagModelService,
|
||||||
|
} from 'src/space-model/services';
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [ConfigModule, SpaceRepositoryModule, CommunityModule],
|
imports: [ConfigModule, SpaceRepositoryModule, CommunityModule],
|
||||||
@ -88,9 +94,12 @@ import { TagService } from './services/tag';
|
|||||||
DeviceStatusFirebaseService,
|
DeviceStatusFirebaseService,
|
||||||
DeviceStatusLogRepository,
|
DeviceStatusLogRepository,
|
||||||
SceneDeviceRepository,
|
SceneDeviceRepository,
|
||||||
|
SpaceModelService,
|
||||||
|
SubSpaceModelService,
|
||||||
|
TagModelService,
|
||||||
ProjectRepository,
|
ProjectRepository,
|
||||||
SpaceModelRepository,
|
SpaceModelRepository,
|
||||||
|
SubspaceModelRepository,
|
||||||
],
|
],
|
||||||
exports: [SpaceService],
|
exports: [SpaceService],
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user