mirror of
https://github.com/SyncrowIOT/backend.git
synced 2026-10-23 11:57:54 +00:00
removed tag service
This commit is contained in:
@@ -10,7 +10,6 @@ import {
|
||||
import { DataSource, QueryRunner } from 'typeorm';
|
||||
import { SubSpaceService } from 'src/space/services';
|
||||
import { TagService } from 'src/space/services/tag';
|
||||
import { TagModelService } from '../services';
|
||||
import { UpdatedSubspaceModelPayload } from '../interfaces';
|
||||
import { ModifyAction } from '@app/common/constants/modify-action.enum';
|
||||
import { ModifySubspaceDto } from 'src/space/dtos';
|
||||
@@ -26,7 +25,6 @@ export class PropogateUpdateSpaceModelHandler
|
||||
private readonly dataSource: DataSource,
|
||||
private readonly subSpaceService: SubSpaceService,
|
||||
private readonly tagService: TagService,
|
||||
private readonly tagModelService: TagModelService,
|
||||
) {}
|
||||
|
||||
async execute(command: PropogateUpdateSpaceModelCommand): Promise<void> {
|
||||
|
||||
@@ -1,579 +0,0 @@
|
||||
import { Injectable, HttpException, HttpStatus } from '@nestjs/common';
|
||||
import { QueryRunner } from 'typeorm';
|
||||
import {
|
||||
SpaceModelEntity,
|
||||
TagModel,
|
||||
} from '@app/common/modules/space-model/entities';
|
||||
import { SubspaceModelEntity } from '@app/common/modules/space-model/entities';
|
||||
import { TagModelRepository } from '@app/common/modules/space-model';
|
||||
import {
|
||||
CreateTagModelDto,
|
||||
ModifySubspaceModelDto,
|
||||
ModifyTagModelDto,
|
||||
} from '../dtos';
|
||||
import { ProductService } from 'src/product/services';
|
||||
import { ModifyAction } from '@app/common/constants/modify-action.enum';
|
||||
import { ModifiedTagsModelPayload } from '../interfaces';
|
||||
import { NewTagRepository } from '@app/common/modules/tag/repositories/tag-repository';
|
||||
|
||||
@Injectable()
|
||||
export class TagModelService {
|
||||
constructor(
|
||||
private readonly tagModelRepository: TagModelRepository,
|
||||
private readonly productService: ProductService,
|
||||
private readonly tagRepository: NewTagRepository,
|
||||
) {}
|
||||
|
||||
async createTags(
|
||||
tags: CreateTagModelDto[],
|
||||
queryRunner: QueryRunner,
|
||||
spaceModel?: SpaceModelEntity,
|
||||
subspaceModel?: SubspaceModelEntity,
|
||||
additionalTags?: CreateTagModelDto[],
|
||||
tagsToDelete?: ModifyTagModelDto[],
|
||||
): Promise<TagModel[]> {
|
||||
if (!tags.length) {
|
||||
throw new HttpException('Tags cannot be empty.', HttpStatus.BAD_REQUEST);
|
||||
}
|
||||
|
||||
const combinedTags = additionalTags ? [...tags, ...additionalTags] : tags;
|
||||
const duplicateTags = this.findDuplicateTags(combinedTags);
|
||||
|
||||
if (duplicateTags.length > 0) {
|
||||
throw new HttpException(
|
||||
`Duplicate tags found for the same product: ${duplicateTags.join(', ')}`,
|
||||
HttpStatus.BAD_REQUEST,
|
||||
);
|
||||
}
|
||||
|
||||
const tagEntitiesToCreate = tags.filter((tagDto) => !tagDto.uuid);
|
||||
const tagEntitiesToUpdate = tags.filter((tagDto) => !!tagDto.uuid);
|
||||
|
||||
try {
|
||||
const createdTags = await this.bulkSaveTags(
|
||||
tagEntitiesToCreate,
|
||||
queryRunner,
|
||||
spaceModel,
|
||||
subspaceModel,
|
||||
tagsToDelete,
|
||||
);
|
||||
|
||||
// Update existing tags
|
||||
const updatedTags = await this.moveTags(
|
||||
tagEntitiesToUpdate,
|
||||
queryRunner,
|
||||
spaceModel,
|
||||
subspaceModel,
|
||||
);
|
||||
|
||||
// Combine created and updated tags
|
||||
return [...createdTags, ...updatedTags];
|
||||
} catch (error) {
|
||||
if (error instanceof HttpException) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
throw new HttpException(
|
||||
`Failed to create tag models due to an unexpected error.: ${error}`,
|
||||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
async bulkSaveTags(
|
||||
tags: CreateTagModelDto[],
|
||||
queryRunner: QueryRunner,
|
||||
spaceModel?: SpaceModelEntity,
|
||||
subspaceModel?: SubspaceModelEntity,
|
||||
tagsToDelete?: ModifyTagModelDto[],
|
||||
): Promise<TagModel[]> {
|
||||
if (!tags.length) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const tagEntities = await Promise.all(
|
||||
tags.map((tagDto) =>
|
||||
this.prepareTagEntity(
|
||||
tagDto,
|
||||
queryRunner,
|
||||
spaceModel,
|
||||
subspaceModel,
|
||||
tagsToDelete,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
try {
|
||||
return await queryRunner.manager.save(tagEntities);
|
||||
} catch (error) {
|
||||
if (error instanceof HttpException) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
throw new HttpException(
|
||||
`Failed to save tag models due to an unexpected error: ${error.message}`,
|
||||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
async moveTags(
|
||||
tags: CreateTagModelDto[],
|
||||
queryRunner: QueryRunner,
|
||||
spaceModel?: SpaceModelEntity,
|
||||
subspaceModel?: SubspaceModelEntity,
|
||||
): Promise<TagModel[]> {
|
||||
if (!tags.length) {
|
||||
return [];
|
||||
}
|
||||
|
||||
try {
|
||||
return await Promise.all(
|
||||
tags.map(async (tagDto) => {
|
||||
try {
|
||||
const tag = await this.getTagByUuid(tagDto.uuid);
|
||||
if (!tag) {
|
||||
throw new HttpException(
|
||||
`Tag with UUID ${tagDto.uuid} not found.`,
|
||||
HttpStatus.NOT_FOUND,
|
||||
);
|
||||
}
|
||||
|
||||
if (subspaceModel && subspaceModel.spaceModel) {
|
||||
await queryRunner.manager.update(
|
||||
this.tagModelRepository.target,
|
||||
{ uuid: tag.uuid },
|
||||
{ subspaceModel, spaceModel: null },
|
||||
);
|
||||
tag.subspaceModel = subspaceModel;
|
||||
}
|
||||
|
||||
if (!subspaceModel && spaceModel) {
|
||||
await queryRunner.manager.update(
|
||||
this.tagModelRepository.target,
|
||||
{ uuid: tag.uuid },
|
||||
{ subspaceModel: null, spaceModel: spaceModel },
|
||||
);
|
||||
tag.subspaceModel = null;
|
||||
tag.spaceModel = spaceModel;
|
||||
}
|
||||
|
||||
return tag;
|
||||
} catch (error) {
|
||||
console.error(
|
||||
`Error moving tag with UUID ${tagDto.uuid}: ${error.message}`,
|
||||
);
|
||||
throw error; // Re-throw the error to propagate it to the parent Promise.all
|
||||
}
|
||||
}),
|
||||
);
|
||||
} catch (error) {
|
||||
console.error(`Error in moveTags: ${error.message}`);
|
||||
throw new HttpException(
|
||||
`Failed to move tags due to an unexpected error: ${error.message}`,
|
||||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
async updateTag(
|
||||
tag: ModifyTagModelDto,
|
||||
queryRunner: QueryRunner,
|
||||
spaceModel?: SpaceModelEntity,
|
||||
subspaceModel?: SubspaceModelEntity,
|
||||
): Promise<TagModel> {
|
||||
try {
|
||||
const existingTag = await this.getTagByUuid(tag.uuid);
|
||||
|
||||
if (tag.tag !== existingTag.tag) {
|
||||
if (spaceModel) {
|
||||
await this.checkTagReuse(
|
||||
tag.tag,
|
||||
existingTag.product.uuid,
|
||||
spaceModel,
|
||||
);
|
||||
} else {
|
||||
await this.checkTagReuse(
|
||||
tag.tag,
|
||||
existingTag.product.uuid,
|
||||
subspaceModel.spaceModel,
|
||||
);
|
||||
}
|
||||
|
||||
if (tag.tag) {
|
||||
existingTag.tag = tag.tag;
|
||||
}
|
||||
}
|
||||
return await queryRunner.manager.save(existingTag);
|
||||
} catch (error) {
|
||||
if (error instanceof HttpException) {
|
||||
throw error;
|
||||
}
|
||||
throw new HttpException(
|
||||
error.message || 'Failed to update tags',
|
||||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
async deleteTags(tagUuids: string[], queryRunner: QueryRunner) {
|
||||
try {
|
||||
const deletePromises = tagUuids.map((id) =>
|
||||
queryRunner.manager.update(
|
||||
this.tagModelRepository.target,
|
||||
{ uuid: id },
|
||||
{ disabled: true },
|
||||
),
|
||||
);
|
||||
|
||||
await Promise.all(deletePromises);
|
||||
return { message: 'Tags deleted successfully', tagUuids };
|
||||
} catch (error) {
|
||||
if (error instanceof HttpException) {
|
||||
throw error;
|
||||
}
|
||||
throw new HttpException(
|
||||
error.message || 'Failed to delete tags',
|
||||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private findDuplicateTags(tags: CreateTagModelDto[]): string[] {
|
||||
const seen = new Map<string, boolean>();
|
||||
const duplicates: string[] = [];
|
||||
|
||||
tags.forEach((tagDto) => {
|
||||
const key = `${tagDto.productUuid}-${tagDto.tag}`;
|
||||
if (seen.has(key)) {
|
||||
duplicates.push(`${tagDto.tag} for Product: ${tagDto.productUuid}`);
|
||||
} else {
|
||||
seen.set(key, true);
|
||||
}
|
||||
});
|
||||
|
||||
return duplicates;
|
||||
}
|
||||
|
||||
async modifyTags(
|
||||
tags: ModifyTagModelDto[],
|
||||
queryRunner: QueryRunner,
|
||||
spaceModel?: SpaceModelEntity,
|
||||
subspaceModel?: SubspaceModelEntity,
|
||||
): Promise<ModifiedTagsModelPayload> {
|
||||
const modifiedTagModels: ModifiedTagsModelPayload = {
|
||||
added: [],
|
||||
updated: [],
|
||||
deleted: [],
|
||||
};
|
||||
try {
|
||||
const tagsToDelete = tags.filter(
|
||||
(tag) => tag.action === ModifyAction.DELETE,
|
||||
);
|
||||
|
||||
for (const tag of tags) {
|
||||
if (tag.action === ModifyAction.ADD) {
|
||||
const createTagDto: CreateTagModelDto = {
|
||||
tag: tag.tag as string,
|
||||
uuid: tag.uuid,
|
||||
productUuid: tag.productUuid as string,
|
||||
};
|
||||
|
||||
const newModel = await this.createTags(
|
||||
[createTagDto],
|
||||
queryRunner,
|
||||
spaceModel,
|
||||
subspaceModel,
|
||||
null,
|
||||
tagsToDelete,
|
||||
);
|
||||
modifiedTagModels.added.push(...newModel);
|
||||
} else if (tag.action === ModifyAction.UPDATE) {
|
||||
const updatedModel = await this.updateTag(
|
||||
tag,
|
||||
queryRunner,
|
||||
spaceModel,
|
||||
subspaceModel,
|
||||
);
|
||||
modifiedTagModels.updated.push(updatedModel);
|
||||
} else if (tag.action === ModifyAction.DELETE) {
|
||||
await queryRunner.manager.update(
|
||||
this.tagModelRepository.target,
|
||||
{ uuid: tag.uuid },
|
||||
{ disabled: true },
|
||||
);
|
||||
modifiedTagModels.deleted.push(tag.uuid);
|
||||
} else {
|
||||
throw new HttpException(
|
||||
`Invalid action "${tag.action}" provided.`,
|
||||
HttpStatus.BAD_REQUEST,
|
||||
);
|
||||
}
|
||||
}
|
||||
return modifiedTagModels;
|
||||
} catch (error) {
|
||||
if (error instanceof HttpException) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
throw new HttpException(
|
||||
`An error occurred while modifying tag models: ${error.message}`,
|
||||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private async checkTagReuse(
|
||||
tag: string,
|
||||
productUuid: string,
|
||||
spaceModel: SpaceModelEntity,
|
||||
tagsToDelete?: ModifyTagModelDto[],
|
||||
): Promise<void> {
|
||||
try {
|
||||
// Query to find existing tags
|
||||
const tagExists = await this.tagModelRepository.find({
|
||||
where: [
|
||||
{
|
||||
tag,
|
||||
spaceModel: { uuid: spaceModel.uuid },
|
||||
product: { uuid: productUuid },
|
||||
disabled: false,
|
||||
},
|
||||
{
|
||||
tag,
|
||||
subspaceModel: { spaceModel: { uuid: spaceModel.uuid } },
|
||||
product: { uuid: productUuid },
|
||||
disabled: false,
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
// Remove tags that are marked for deletion
|
||||
const filteredTagExists = tagExists.filter(
|
||||
(existingTag) =>
|
||||
!tagsToDelete?.some(
|
||||
(deleteTag) => deleteTag.uuid === existingTag.uuid,
|
||||
),
|
||||
);
|
||||
|
||||
// If any tags remain, throw an exception
|
||||
if (filteredTagExists.length > 0) {
|
||||
throw new HttpException(
|
||||
`Tag ${tag} can't be reused`,
|
||||
HttpStatus.CONFLICT,
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
if (error instanceof HttpException) {
|
||||
throw error;
|
||||
}
|
||||
console.error(`Error while checking tag reuse: ${error.message}`);
|
||||
throw new HttpException(
|
||||
`An error occurred while checking tag reuse: ${error.message}`,
|
||||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private async prepareTagEntity(
|
||||
tagDto: CreateTagModelDto,
|
||||
queryRunner: QueryRunner,
|
||||
spaceModel?: SpaceModelEntity,
|
||||
subspaceModel?: SubspaceModelEntity,
|
||||
tagsToDelete?: ModifyTagModelDto[],
|
||||
): Promise<TagModel> {
|
||||
try {
|
||||
const product = await this.productService.findOne(tagDto.productUuid);
|
||||
|
||||
if (!product) {
|
||||
throw new HttpException(
|
||||
`Product with UUID ${tagDto.productUuid} not found.`,
|
||||
HttpStatus.NOT_FOUND,
|
||||
);
|
||||
}
|
||||
|
||||
if (spaceModel) {
|
||||
await this.checkTagReuse(
|
||||
tagDto.tag,
|
||||
tagDto.productUuid,
|
||||
spaceModel,
|
||||
tagsToDelete,
|
||||
);
|
||||
} else if (subspaceModel && subspaceModel.spaceModel) {
|
||||
await this.checkTagReuse(
|
||||
tagDto.tag,
|
||||
tagDto.productUuid,
|
||||
subspaceModel.spaceModel,
|
||||
);
|
||||
} else {
|
||||
throw new HttpException(
|
||||
`Invalid subspaceModel or spaceModel provided.`,
|
||||
HttpStatus.BAD_REQUEST,
|
||||
);
|
||||
}
|
||||
|
||||
return queryRunner.manager.create(TagModel, {
|
||||
tag: tagDto.tag,
|
||||
product: product.data,
|
||||
spaceModel: spaceModel,
|
||||
subspaceModel: subspaceModel,
|
||||
});
|
||||
} catch (error) {
|
||||
if (error instanceof HttpException) {
|
||||
throw error;
|
||||
}
|
||||
throw new HttpException(
|
||||
`An error occurred while preparing the tag entity: ${error.message}`,
|
||||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
async getTagByUuid(uuid: string): Promise<TagModel> {
|
||||
const tag = await this.tagModelRepository.findOne({
|
||||
where: { uuid, disabled: false },
|
||||
relations: ['product'],
|
||||
});
|
||||
if (!tag) {
|
||||
throw new HttpException(
|
||||
`Tag model with ID ${uuid} not found.`,
|
||||
HttpStatus.NOT_FOUND,
|
||||
);
|
||||
}
|
||||
return tag;
|
||||
}
|
||||
|
||||
async getTagByName(
|
||||
tag: string,
|
||||
subspaceUuid?: string,
|
||||
spaceUuid?: string,
|
||||
): Promise<TagModel> {
|
||||
const queryConditions: any = { tag };
|
||||
|
||||
if (spaceUuid) {
|
||||
queryConditions.spaceModel = { uuid: spaceUuid };
|
||||
} else if (subspaceUuid) {
|
||||
queryConditions.subspaceModel = { uuid: subspaceUuid };
|
||||
} else {
|
||||
throw new HttpException(
|
||||
'Either spaceUuid or subspaceUuid must be provided.',
|
||||
HttpStatus.BAD_REQUEST,
|
||||
);
|
||||
}
|
||||
queryConditions.disabled = false;
|
||||
|
||||
const existingTag = await this.tagModelRepository.findOne({
|
||||
where: queryConditions,
|
||||
relations: ['product'],
|
||||
});
|
||||
|
||||
if (!existingTag) {
|
||||
throw new HttpException(
|
||||
`Tag model with tag "${tag}" not found.`,
|
||||
HttpStatus.NOT_FOUND,
|
||||
);
|
||||
}
|
||||
|
||||
return existingTag;
|
||||
}
|
||||
|
||||
getSubspaceTagsToBeAdded(
|
||||
spaceTags?: ModifyTagModelDto[],
|
||||
subspaceModels?: ModifySubspaceModelDto[],
|
||||
): ModifyTagModelDto[] {
|
||||
if (!subspaceModels || subspaceModels.length === 0) {
|
||||
return spaceTags;
|
||||
}
|
||||
|
||||
const spaceTagsToDelete = spaceTags?.filter(
|
||||
(tag) => tag.action === 'delete',
|
||||
);
|
||||
|
||||
const tagsToAdd = subspaceModels.flatMap(
|
||||
(subspace) => subspace.tags?.filter((tag) => tag.action === 'add') || [],
|
||||
);
|
||||
|
||||
const commonTagUuids = new Set(
|
||||
tagsToAdd
|
||||
.filter((tagToAdd) =>
|
||||
spaceTagsToDelete.some(
|
||||
(tagToDelete) => tagToAdd.uuid === tagToDelete.uuid,
|
||||
),
|
||||
)
|
||||
.map((tag) => tag.uuid),
|
||||
);
|
||||
|
||||
const remainingTags = spaceTags.filter(
|
||||
(tag) => !commonTagUuids.has(tag.uuid), // Exclude tags in commonTagUuids
|
||||
);
|
||||
|
||||
return remainingTags;
|
||||
}
|
||||
|
||||
getModifiedSubspaces(
|
||||
spaceTags: ModifyTagModelDto[],
|
||||
subspaceModels: ModifySubspaceModelDto[],
|
||||
): ModifySubspaceModelDto[] {
|
||||
if (!subspaceModels || subspaceModels.length === 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
// Extract tags marked for addition in spaceTags
|
||||
const spaceTagsToAdd = spaceTags.filter((tag) => tag.action === 'add');
|
||||
|
||||
const subspaceTagsToAdd = subspaceModels.flatMap(
|
||||
(subspace) => subspace.tags?.filter((tag) => tag.action === 'add') || [],
|
||||
);
|
||||
|
||||
const subspaceTagsToDelete = subspaceModels.flatMap(
|
||||
(subspace) =>
|
||||
subspace.tags?.filter((tag) => tag.action === 'delete') || [],
|
||||
);
|
||||
|
||||
const subspaceTagsToDeleteUuids = new Set(
|
||||
subspaceTagsToDelete.map((tag) => tag.uuid),
|
||||
);
|
||||
|
||||
const commonTagsInSubspaces = subspaceTagsToAdd.filter((tag) =>
|
||||
subspaceTagsToDeleteUuids.has(tag.uuid),
|
||||
);
|
||||
|
||||
// Find UUIDs of tags that are common between spaceTagsToAdd and subspace tags marked for deletion
|
||||
const commonTagUuids = new Set(
|
||||
spaceTagsToAdd
|
||||
.flatMap((tagToAdd) =>
|
||||
subspaceModels.flatMap(
|
||||
(subspace) =>
|
||||
subspace.tags?.filter(
|
||||
(tagToDelete) =>
|
||||
tagToDelete.action === 'delete' &&
|
||||
tagToAdd.uuid === tagToDelete.uuid,
|
||||
) || [],
|
||||
),
|
||||
)
|
||||
.map((tag) => tag.uuid),
|
||||
);
|
||||
|
||||
// Modify subspaceModels by removing tags with UUIDs present in commonTagUuids
|
||||
let modifiedSubspaces = subspaceModels.map((subspace) => ({
|
||||
...subspace,
|
||||
tags: subspace.tags?.filter((tag) => !commonTagUuids.has(tag.uuid)) || [],
|
||||
}));
|
||||
|
||||
modifiedSubspaces = modifiedSubspaces.map((subspace) => ({
|
||||
...subspace,
|
||||
tags:
|
||||
subspace.tags?.filter(
|
||||
(tag) =>
|
||||
!(
|
||||
tag.action === 'delete' &&
|
||||
commonTagsInSubspaces.some(
|
||||
(commonTag) => commonTag.uuid === tag.uuid,
|
||||
)
|
||||
),
|
||||
) || [],
|
||||
}));
|
||||
|
||||
return modifiedSubspaces;
|
||||
}
|
||||
}
|
||||
@@ -59,7 +59,6 @@ import { TagService } from './services/tag';
|
||||
import {
|
||||
SpaceModelService,
|
||||
SubSpaceModelService,
|
||||
TagModelService,
|
||||
} from 'src/space-model/services';
|
||||
import { UserService, UserSpaceService } from 'src/users/services';
|
||||
import { UserDevicePermissionService } from 'src/user-device-permission/services';
|
||||
@@ -120,7 +119,6 @@ export const CommandHandlers = [DisableSpaceHandler];
|
||||
SceneDeviceRepository,
|
||||
SpaceModelService,
|
||||
SubSpaceModelService,
|
||||
TagModelService,
|
||||
ProjectRepository,
|
||||
SpaceModelRepository,
|
||||
SubspaceModelRepository,
|
||||
|
||||
Reference in New Issue
Block a user