mirror of
https://github.com/SyncrowIOT/backend.git
synced 2026-10-25 04:07:54 +00:00
update in tag service
This commit is contained in:
@@ -16,11 +16,11 @@ import { BaseResponseDto } from '@app/common/dto/base.response.dto';
|
||||
import { SpaceEntity } from '@app/common/modules/space/entities';
|
||||
import { generateRandomString } from '@app/common/helper/randomString';
|
||||
import { SpaceLinkService } from './space-link';
|
||||
import { CreateSubspaceModelDto } from 'src/space-model/dtos';
|
||||
import { SubSpaceService } from './subspace';
|
||||
import { DataSource, Not } from 'typeorm';
|
||||
import { ValidationService } from './space-validation.service';
|
||||
import { ORPHAN_SPACE_NAME } from '@app/common/constants/orphan-constant';
|
||||
import { TagService } from './tag';
|
||||
|
||||
@Injectable()
|
||||
export class SpaceService {
|
||||
@@ -30,13 +30,15 @@ export class SpaceService {
|
||||
private readonly spaceLinkService: SpaceLinkService,
|
||||
private readonly subSpaceService: SubSpaceService,
|
||||
private readonly validationService: ValidationService,
|
||||
private readonly tagService: TagService,
|
||||
) {}
|
||||
|
||||
async createSpace(
|
||||
addSpaceDto: AddSpaceDto,
|
||||
params: CommunitySpaceParam,
|
||||
): Promise<BaseResponseDto> {
|
||||
const { parentUuid, direction, spaceModelUuid, subspaces } = addSpaceDto;
|
||||
const { parentUuid, direction, spaceModelUuid, subspaces, tags } =
|
||||
addSpaceDto;
|
||||
const { communityUuid, projectUuid } = params;
|
||||
|
||||
if (addSpaceDto.spaceName === ORPHAN_SPACE_NAME) {
|
||||
@@ -67,14 +69,14 @@ export class SpaceService {
|
||||
: null;
|
||||
|
||||
try {
|
||||
const newSpace = queryRunner.manager.create(SpaceEntity, {
|
||||
const space = queryRunner.manager.create(SpaceEntity, {
|
||||
...addSpaceDto,
|
||||
spaceModel,
|
||||
parent: parentUuid ? parent : null,
|
||||
community,
|
||||
});
|
||||
|
||||
await queryRunner.manager.save(newSpace);
|
||||
const newSpace = await queryRunner.manager.save(space);
|
||||
|
||||
if (direction && parent) {
|
||||
await this.spaceLinkService.saveSpaceLink(
|
||||
@@ -90,11 +92,14 @@ export class SpaceService {
|
||||
newSpace,
|
||||
queryRunner,
|
||||
);
|
||||
} else if (spaceModel && spaceModel.subspaceModels.length) {
|
||||
await this.subSpaceService.createSubSpaceFromModel(
|
||||
spaceModel,
|
||||
newSpace,
|
||||
}
|
||||
|
||||
if (tags?.length) {
|
||||
newSpace.tags = await this.tagService.createTags(
|
||||
tags,
|
||||
queryRunner,
|
||||
newSpace,
|
||||
null,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -242,19 +247,32 @@ export class SpaceService {
|
||||
HttpStatus.BAD_REQUEST,
|
||||
);
|
||||
}
|
||||
if (updateSpaceDto.spaceName) space.spaceName = updateSpaceDto.spaceName;
|
||||
|
||||
// If a parentId is provided, check if the parent exists
|
||||
const { parentUuid } = updateSpaceDto;
|
||||
const parent = parentUuid
|
||||
? await this.validationService.validateSpace(parentUuid)
|
||||
: null;
|
||||
if (updateSpaceDto.x) space.x = updateSpaceDto.x;
|
||||
|
||||
// Update other space properties from updateSpaceDto
|
||||
Object.assign(space, updateSpaceDto, { parent });
|
||||
if (updateSpaceDto.y) space.y = updateSpaceDto.y;
|
||||
if (updateSpaceDto.icon) space.icon = updateSpaceDto.icon;
|
||||
if (updateSpaceDto.icon) space.icon = updateSpaceDto.icon;
|
||||
|
||||
// Save the updated space
|
||||
await queryRunner.manager.save(space);
|
||||
|
||||
if (updateSpaceDto.subspaceModels) {
|
||||
await this.subSpaceService.modifySubSpace(
|
||||
updateSpaceDto.subspaceModels,
|
||||
space,
|
||||
queryRunner,
|
||||
);
|
||||
}
|
||||
|
||||
if (updateSpaceDto.tags) {
|
||||
await this.tagService.modifyTags(
|
||||
updateSpaceDto.tags,
|
||||
queryRunner,
|
||||
space,
|
||||
);
|
||||
}
|
||||
|
||||
await queryRunner.commitTransaction();
|
||||
|
||||
return new SuccessResponseDto({
|
||||
|
||||
@@ -1,6 +1,13 @@
|
||||
import { BaseResponseDto } from '@app/common/dto/base.response.dto';
|
||||
import { HttpException, HttpStatus, Injectable } from '@nestjs/common';
|
||||
import { AddSubspaceDto, GetSpaceParam, GetSubSpaceParam } from '../../dtos';
|
||||
import {
|
||||
AddSubspaceDto,
|
||||
CreateTagDto,
|
||||
DeleteSubspaceDto,
|
||||
GetSpaceParam,
|
||||
GetSubSpaceParam,
|
||||
ModifySubspaceDto,
|
||||
} from '../../dtos';
|
||||
import { SuccessResponseDto } from '@app/common/dto/success.response.dto';
|
||||
import {
|
||||
TypeORMCustomModel,
|
||||
@@ -19,12 +26,15 @@ import {
|
||||
} from '@app/common/modules/space-model';
|
||||
import { ValidationService } from '../space-validation.service';
|
||||
import { SubspaceRepository } from '@app/common/modules/space/repositories/subspace.repository';
|
||||
import { TagService } from '../tag';
|
||||
import { ModifyAction } from '@app/common/constants/modify-action.enum';
|
||||
|
||||
@Injectable()
|
||||
export class SubSpaceService {
|
||||
constructor(
|
||||
private readonly subspaceRepository: SubspaceRepository,
|
||||
private readonly validationService: ValidationService,
|
||||
private readonly tagService: TagService,
|
||||
) {}
|
||||
|
||||
async createSubspaces(
|
||||
@@ -71,6 +81,7 @@ export class SubSpaceService {
|
||||
addSubspaceDtos: AddSubspaceDto[],
|
||||
space: SpaceEntity,
|
||||
queryRunner: QueryRunner,
|
||||
otherTags?: CreateTagDto[],
|
||||
): Promise<SubspaceEntity[]> {
|
||||
try {
|
||||
const subspaceData = addSubspaceDtos.map((dto) => ({
|
||||
@@ -80,6 +91,20 @@ export class SubSpaceService {
|
||||
|
||||
const subspaces = await this.createSubspaces(subspaceData, queryRunner);
|
||||
|
||||
await Promise.all(
|
||||
addSubspaceDtos.map(async (dto, index) => {
|
||||
const subspace = subspaces[index];
|
||||
if (dto.tags?.length) {
|
||||
subspace.tags = await this.tagService.createTags(
|
||||
dto.tags,
|
||||
queryRunner,
|
||||
null,
|
||||
subspace,
|
||||
otherTags,
|
||||
);
|
||||
}
|
||||
}),
|
||||
);
|
||||
return subspaces;
|
||||
} catch (error) {
|
||||
throw new Error(
|
||||
@@ -144,43 +169,6 @@ export class SubSpaceService {
|
||||
}
|
||||
}
|
||||
|
||||
async findOne(params: GetSubSpaceParam): Promise<BaseResponseDto> {
|
||||
const { communityUuid, subSpaceUuid, spaceUuid, projectUuid } = params;
|
||||
await this.validationService.validateSpaceWithinCommunityAndProject(
|
||||
communityUuid,
|
||||
projectUuid,
|
||||
spaceUuid,
|
||||
);
|
||||
try {
|
||||
const subSpace = await this.subspaceRepository.findOne({
|
||||
where: {
|
||||
uuid: subSpaceUuid,
|
||||
},
|
||||
});
|
||||
|
||||
// If space is not found, throw a NotFoundException
|
||||
if (!subSpace) {
|
||||
throw new HttpException(
|
||||
`Sub Space with UUID ${subSpaceUuid} not found`,
|
||||
HttpStatus.NOT_FOUND,
|
||||
);
|
||||
}
|
||||
return new SuccessResponseDto({
|
||||
message: `Subspace with ID ${subSpaceUuid} successfully fetched`,
|
||||
data: subSpace,
|
||||
});
|
||||
} catch (error) {
|
||||
if (error instanceof HttpException) {
|
||||
throw error; // If it's an HttpException, rethrow it
|
||||
} else {
|
||||
throw new HttpException(
|
||||
'An error occurred while deleting the subspace',
|
||||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async updateSubSpace(
|
||||
params: GetSubSpaceParam,
|
||||
updateSubSpaceDto: AddSubspaceDto,
|
||||
@@ -256,4 +244,153 @@ export class SubSpaceService {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
async deleteSubspaces(
|
||||
deleteDtos: DeleteSubspaceDto[],
|
||||
queryRunner: QueryRunner,
|
||||
) {
|
||||
const deleteResults: { uuid: string }[] = [];
|
||||
|
||||
for (const dto of deleteDtos) {
|
||||
const subspaceModel = await this.findOne(dto.subspaceUuid);
|
||||
|
||||
await queryRunner.manager.update(
|
||||
this.subspaceRepository.target,
|
||||
{ uuid: dto.subspaceUuid },
|
||||
{ disabled: true },
|
||||
);
|
||||
|
||||
if (subspaceModel.tags?.length) {
|
||||
const modifyTagDtos = subspaceModel.tags.map((tag) => ({
|
||||
uuid: tag.uuid,
|
||||
action: ModifyAction.DELETE,
|
||||
}));
|
||||
await this.tagService.modifyTags(
|
||||
modifyTagDtos,
|
||||
queryRunner,
|
||||
null,
|
||||
subspaceModel,
|
||||
);
|
||||
}
|
||||
|
||||
deleteResults.push({ uuid: dto.subspaceUuid });
|
||||
}
|
||||
|
||||
return deleteResults;
|
||||
}
|
||||
|
||||
async modifySubSpace(
|
||||
subspaceDtos: ModifySubspaceDto[],
|
||||
space: SpaceEntity,
|
||||
queryRunner: QueryRunner,
|
||||
): Promise<void> {
|
||||
for (const subspace of subspaceDtos) {
|
||||
switch (subspace.action) {
|
||||
case ModifyAction.ADD:
|
||||
await this.handleAddAction(subspace, space, queryRunner);
|
||||
break;
|
||||
case ModifyAction.UPDATE:
|
||||
await this.handleUpdateAction(subspace, queryRunner);
|
||||
break;
|
||||
case ModifyAction.DELETE:
|
||||
await this.handleDeleteAction(subspace, queryRunner);
|
||||
break;
|
||||
default:
|
||||
throw new HttpException(
|
||||
`Invalid action "${subspace.action}".`,
|
||||
HttpStatus.BAD_REQUEST,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async handleAddAction(
|
||||
subspace: ModifySubspaceDto,
|
||||
space: SpaceEntity,
|
||||
queryRunner: QueryRunner,
|
||||
): Promise<void> {
|
||||
const createTagDtos: CreateTagDto[] =
|
||||
subspace.tags?.map((tag) => ({
|
||||
tag: tag.tag as string,
|
||||
productUuid: tag.productUuid as string,
|
||||
})) || [];
|
||||
await this.createSubspacesFromDto(
|
||||
[{ subspaceName: subspace.subspaceName, tags: createTagDtos }],
|
||||
space,
|
||||
queryRunner,
|
||||
);
|
||||
}
|
||||
|
||||
private async handleUpdateAction(
|
||||
modifyDto: ModifySubspaceDto,
|
||||
queryRunner: QueryRunner,
|
||||
): Promise<void> {
|
||||
const subspace = await this.findOne(modifyDto.uuid);
|
||||
|
||||
await this.updateSubspaceName(
|
||||
queryRunner,
|
||||
subspace,
|
||||
modifyDto.subspaceName,
|
||||
);
|
||||
|
||||
if (modifyDto.tags?.length) {
|
||||
await this.tagService.modifyTags(
|
||||
modifyDto.tags,
|
||||
queryRunner,
|
||||
null,
|
||||
subspace,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private async handleDeleteAction(
|
||||
subspace: ModifySubspaceDto,
|
||||
queryRunner: QueryRunner,
|
||||
): Promise<void> {
|
||||
const subspaceModel = await this.findOne(subspace.uuid);
|
||||
|
||||
await queryRunner.manager.update(
|
||||
this.subspaceRepository.target,
|
||||
{ uuid: subspace.uuid },
|
||||
{ disabled: true },
|
||||
);
|
||||
|
||||
if (subspaceModel.tags?.length) {
|
||||
const modifyTagDtos = subspaceModel.tags.map((tag) => ({
|
||||
uuid: tag.uuid,
|
||||
action: ModifyAction.DELETE,
|
||||
}));
|
||||
await this.tagService.modifyTags(
|
||||
modifyTagDtos,
|
||||
queryRunner,
|
||||
null,
|
||||
subspaceModel,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private async findOne(subspaceUuid: string): Promise<SubspaceEntity> {
|
||||
const subspace = await this.subspaceRepository.findOne({
|
||||
where: { uuid: subspaceUuid },
|
||||
relations: ['tags'],
|
||||
});
|
||||
if (!subspace) {
|
||||
throw new HttpException(
|
||||
`SubspaceModel with UUID ${subspaceUuid} not found.`,
|
||||
HttpStatus.NOT_FOUND,
|
||||
);
|
||||
}
|
||||
return subspace;
|
||||
}
|
||||
|
||||
private async updateSubspaceName(
|
||||
queryRunner: QueryRunner,
|
||||
subSpace: SubspaceEntity,
|
||||
subspaceName?: string,
|
||||
): Promise<void> {
|
||||
if (subspaceName) {
|
||||
subSpace.subspaceName = subspaceName;
|
||||
await queryRunner.manager.save(subSpace);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
export * from './tag.service';
|
||||
@@ -0,0 +1,237 @@
|
||||
import { ModifyAction } from '@app/common/constants/modify-action.enum';
|
||||
import {
|
||||
SpaceEntity,
|
||||
SubspaceEntity,
|
||||
TagEntity,
|
||||
TagRepository,
|
||||
} from '@app/common/modules/space';
|
||||
import { HttpException, HttpStatus, Injectable } from '@nestjs/common';
|
||||
import { ProductService } from 'src/product/services';
|
||||
import { CreateTagDto } from 'src/space/dtos';
|
||||
import { ModifyTagDto } from 'src/space/dtos/tag/modify-tag.dto';
|
||||
import { QueryRunner } from 'typeorm';
|
||||
|
||||
@Injectable()
|
||||
export class TagService {
|
||||
constructor(
|
||||
private readonly tagRepository: TagRepository,
|
||||
private readonly productService: ProductService,
|
||||
) {}
|
||||
|
||||
async createTags(
|
||||
tags: CreateTagDto[],
|
||||
queryRunner: QueryRunner,
|
||||
space?: SpaceEntity,
|
||||
subspace?: SubspaceEntity,
|
||||
additionalTags?: CreateTagDto[],
|
||||
): Promise<TagEntity[]> {
|
||||
if (!tags.length) {
|
||||
throw new HttpException('Tags cannot be empty.', HttpStatus.BAD_REQUEST);
|
||||
}
|
||||
|
||||
const combinedTags = additionalTags ? [...tags, ...additionalTags] : tags;
|
||||
const duplicateTags = this.findDuplicateTags(combinedTags);
|
||||
|
||||
if (duplicateTags.length > 0) {
|
||||
throw new HttpException(
|
||||
`Duplicate tags found for the same product: ${duplicateTags.join(', ')}`,
|
||||
HttpStatus.BAD_REQUEST,
|
||||
);
|
||||
}
|
||||
|
||||
const tagEntities = await Promise.all(
|
||||
tags.map(async (tagDto) =>
|
||||
this.prepareTagEntity(tagDto, queryRunner, space, subspace),
|
||||
),
|
||||
);
|
||||
try {
|
||||
return await queryRunner.manager.save(tagEntities);
|
||||
} catch (error) {
|
||||
if (error instanceof HttpException) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
throw new HttpException(
|
||||
'Failed to save tag models due to an unexpected error.',
|
||||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
async updateTag(
|
||||
tag: ModifyTagDto,
|
||||
queryRunner: QueryRunner,
|
||||
space?: SpaceEntity,
|
||||
subspace?: SubspaceEntity,
|
||||
): Promise<TagEntity> {
|
||||
try {
|
||||
const existingTag = await this.getTagByUuid(tag.uuid);
|
||||
|
||||
if (space) {
|
||||
await this.checkTagReuse(tag.tag, existingTag.product.uuid, space);
|
||||
} else {
|
||||
await this.checkTagReuse(
|
||||
tag.tag,
|
||||
existingTag.product.uuid,
|
||||
subspace.space,
|
||||
);
|
||||
}
|
||||
|
||||
if (tag.tag) {
|
||||
existingTag.tag = tag.tag;
|
||||
}
|
||||
|
||||
return await queryRunner.manager.save(existingTag);
|
||||
} catch (error) {
|
||||
if (error instanceof HttpException) {
|
||||
throw error;
|
||||
}
|
||||
throw new HttpException(
|
||||
error.message || 'Failed to update tags',
|
||||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
async deleteTags(tagUuids: string[], queryRunner: QueryRunner) {
|
||||
try {
|
||||
const deletePromises = tagUuids.map((id) =>
|
||||
queryRunner.manager.softDelete(this.tagRepository.target, id),
|
||||
);
|
||||
|
||||
await Promise.all(deletePromises);
|
||||
return { message: 'Tags deleted successfully', tagUuids };
|
||||
} catch (error) {
|
||||
if (error instanceof HttpException) {
|
||||
throw error;
|
||||
}
|
||||
throw new HttpException(
|
||||
error.message || 'Failed to delete tags',
|
||||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private findDuplicateTags(tags: CreateTagDto[]): string[] {
|
||||
const seen = new Map<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: ModifyTagDto[],
|
||||
queryRunner: QueryRunner,
|
||||
space?: SpaceEntity,
|
||||
subspace?: SubspaceEntity,
|
||||
): Promise<void> {
|
||||
try {
|
||||
console.log(tags);
|
||||
for (const tag of tags) {
|
||||
if (tag.action === ModifyAction.ADD) {
|
||||
const createTagDto: CreateTagDto = {
|
||||
tag: tag.tag as string,
|
||||
productUuid: tag.productUuid as string,
|
||||
};
|
||||
|
||||
await this.createTags([createTagDto], queryRunner, space, subspace);
|
||||
} else if (tag.action === ModifyAction.UPDATE) {
|
||||
await this.updateTag(tag, queryRunner, space, subspace);
|
||||
} else if (tag.action === ModifyAction.DELETE) {
|
||||
await queryRunner.manager.update(
|
||||
this.tagRepository.target,
|
||||
{ uuid: tag.uuid },
|
||||
{ disabled: true },
|
||||
);
|
||||
} else {
|
||||
throw new HttpException(
|
||||
`Invalid action "${tag.action}" provided.`,
|
||||
HttpStatus.BAD_REQUEST,
|
||||
);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
if (error instanceof HttpException) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
throw new HttpException(
|
||||
`An error occurred while modifying tags: ${error.message}`,
|
||||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private async checkTagReuse(
|
||||
tag: string,
|
||||
productUuid: string,
|
||||
space: SpaceEntity,
|
||||
): Promise<void> {
|
||||
const isTagInSpace = await this.tagRepository.exists({
|
||||
where: { tag, space, product: { uuid: productUuid } },
|
||||
});
|
||||
const isTagInSubspace = await this.tagRepository.exists({
|
||||
where: {
|
||||
tag,
|
||||
subspace: { space },
|
||||
product: { uuid: productUuid },
|
||||
},
|
||||
});
|
||||
|
||||
if (isTagInSpace || isTagInSubspace) {
|
||||
throw new HttpException(`Tag can't be reused`, HttpStatus.CONFLICT);
|
||||
}
|
||||
}
|
||||
|
||||
private async prepareTagEntity(
|
||||
tagDto: CreateTagDto,
|
||||
queryRunner: QueryRunner,
|
||||
space?: SpaceEntity,
|
||||
subspace?: SubspaceEntity,
|
||||
): Promise<TagEntity> {
|
||||
const product = await this.productService.findOne(tagDto.productUuid);
|
||||
|
||||
if (!product) {
|
||||
throw new HttpException(
|
||||
`Product with UUID ${tagDto.productUuid} not found.`,
|
||||
HttpStatus.NOT_FOUND,
|
||||
);
|
||||
}
|
||||
|
||||
if (space) {
|
||||
await this.checkTagReuse(tagDto.tag, tagDto.productUuid, space);
|
||||
} else {
|
||||
await this.checkTagReuse(tagDto.tag, tagDto.productUuid, subspace.space);
|
||||
}
|
||||
|
||||
return queryRunner.manager.create(TagEntity, {
|
||||
tag: tagDto.tag,
|
||||
product: product.data,
|
||||
space,
|
||||
subspace,
|
||||
});
|
||||
}
|
||||
|
||||
private async getTagByUuid(uuid: string): Promise<TagEntity> {
|
||||
const tag = await this.tagRepository.findOne({
|
||||
where: { uuid },
|
||||
relations: ['product'],
|
||||
});
|
||||
if (!tag) {
|
||||
throw new HttpException(
|
||||
`Tag with ID ${uuid} not found.`,
|
||||
HttpStatus.NOT_FOUND,
|
||||
);
|
||||
}
|
||||
return tag;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user