diff --git a/src/tags/dtos/bulk-create-tag.dto.ts b/src/tags/dtos/bulk-create-tag.dto.ts new file mode 100644 index 0000000..a7e3e44 --- /dev/null +++ b/src/tags/dtos/bulk-create-tag.dto.ts @@ -0,0 +1,23 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { Type } from 'class-transformer'; +import { IsArray, IsNotEmpty, IsString, ValidateNested } from 'class-validator'; +import { CreateTagDto } from './tags.dto'; + +export class BulkCreateTagsDto { + @ApiProperty({ + description: 'Project UUID for which the tags are being created', + example: '760e8400-e29b-41d4-a716-446655440001', + }) + @IsNotEmpty() + @IsString() + projectUuid: string; + + @ApiProperty({ + description: 'List of tags to be created', + type: [CreateTagDto], + }) + @IsArray() + @ValidateNested({ each: true }) + @Type(() => CreateTagDto) + tags: CreateTagDto[]; +} diff --git a/src/tags/dtos/index.ts b/src/tags/dtos/index.ts index 5bbb3c4..850cc06 100644 --- a/src/tags/dtos/index.ts +++ b/src/tags/dtos/index.ts @@ -1 +1,2 @@ export * from './tags.dto'; +export * from './bulk-create-tag.dto'; \ No newline at end of file diff --git a/src/tags/services/tags.service.ts b/src/tags/services/tags.service.ts index 466fb23..5754d8c 100644 --- a/src/tags/services/tags.service.ts +++ b/src/tags/services/tags.service.ts @@ -13,6 +13,8 @@ import { SuccessResponseDto } from '@app/common/dto/success.response.dto'; import { BaseResponseDto } from '@app/common/dto/base.response.dto'; import { NewTagEntity } from '@app/common/modules/tag'; import { GetTagsParam } from '../dtos/get-tags.param'; +import { BulkCreateTagsDto } from '../dtos'; +import { In } from 'typeorm'; @Injectable() export class TagService { @@ -21,6 +23,7 @@ export class TagService { private readonly productRepository: ProductRepository, private readonly projectRepository: ProjectRepository, ) {} + async getTagsByProjectUuid(params: GetTagsParam): Promise { const { projectUuid } = params; await this.getProjectByUuid(projectUuid); @@ -58,6 +61,36 @@ export class TagService { }); } + async bulkCreateTags(dto: BulkCreateTagsDto): Promise { + const { projectUuid, tags } = dto; + + const project = await this.getProjectByUuid(projectUuid); + + const productUuids = tags.map((tag) => tag.productUuid); + const productMap = await this.getProductMap(productUuids); + + const existingTagNames = await this.getExistingTagNames(projectUuid); + + const newTags: NewTagEntity[] = tags + .filter((tag) => !existingTagNames.has(tag.name)) + .map((tag) => + this.tagRepository.create({ + name: tag.name, + product: productMap.get(tag.productUuid), + project, + }), + ); + + if (newTags.length > 0) { + await this.tagRepository.save(newTags); + } + + return new SuccessResponseDto({ + message: `Tags processed successfully`, + data: newTags, + }); + } + private async getProductByUuid(uuid: string): Promise { const product = await this.productRepository.findOne({ where: { uuid } }); if (!product) { @@ -88,4 +121,31 @@ export class TagService { ); } } + + private async getProductMap( + uuids: string[], + ): Promise> { + const products = await this.productRepository.find({ + where: { uuid: In(uuids) }, + }); + + if (products.length !== uuids.length) { + const foundUuids = new Set(products.map((p) => p.uuid)); + const missingUuids = uuids.filter((id) => !foundUuids.has(id)); + throw new NotFoundException( + `Products not found for UUIDs: ${missingUuids.join(', ')}`, + ); + } + + return; + } + + private async getExistingTagNames(projectUuid: string): Promise> { + const tags = await this.tagRepository.find({ + where: { project: { uuid: projectUuid } }, + select: ['name'], + }); + + return new Set(tags.map((tag) => tag.name)); + } }