mirror of
https://github.com/SyncrowIOT/backend.git
synced 2025-11-26 09:44:55 +00:00
update
This commit is contained in:
4865
package-lock.json
generated
4865
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -2,7 +2,7 @@ import { ApiProperty } from '@nestjs/swagger';
|
|||||||
import { IsNotEmpty, IsString, IsArray, ValidateNested } from 'class-validator';
|
import { IsNotEmpty, IsString, IsArray, ValidateNested } from 'class-validator';
|
||||||
import { Type } from 'class-transformer';
|
import { Type } from 'class-transformer';
|
||||||
import { CreateSubspaceModelDto } from './subspaces-model-dtos/create-subspace-model.dto';
|
import { CreateSubspaceModelDto } from './subspaces-model-dtos/create-subspace-model.dto';
|
||||||
import { CreateTagModelDto } from './tag-model-dtos/create-tag-model.dto';
|
import { ProcessTagDto } from 'src/tags/dtos';
|
||||||
|
|
||||||
export class CreateSpaceModelDto {
|
export class CreateSpaceModelDto {
|
||||||
@ApiProperty({
|
@ApiProperty({
|
||||||
@ -24,10 +24,10 @@ export class CreateSpaceModelDto {
|
|||||||
|
|
||||||
@ApiProperty({
|
@ApiProperty({
|
||||||
description: 'List of tags associated with the space model',
|
description: 'List of tags associated with the space model',
|
||||||
type: [CreateTagModelDto],
|
type: [ProcessTagDto],
|
||||||
})
|
})
|
||||||
@IsArray()
|
@IsArray()
|
||||||
@ValidateNested({ each: true })
|
@ValidateNested({ each: true })
|
||||||
@Type(() => CreateTagModelDto)
|
@Type(() => ProcessTagDto)
|
||||||
tags?: CreateTagModelDto[];
|
tags?: ProcessTagDto[];
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import { ApiProperty } from '@nestjs/swagger';
|
import { ApiProperty } from '@nestjs/swagger';
|
||||||
import { IsArray, IsNotEmpty, IsString, ValidateNested } from 'class-validator';
|
import { IsArray, IsNotEmpty, IsString, ValidateNested } from 'class-validator';
|
||||||
import { CreateTagModelDto } from '../tag-model-dtos/create-tag-model.dto';
|
|
||||||
import { Type } from 'class-transformer';
|
import { Type } from 'class-transformer';
|
||||||
|
import { CreateTagDto, ProcessTagDto } from 'src/tags/dtos';
|
||||||
|
|
||||||
export class CreateSubspaceModelDto {
|
export class CreateSubspaceModelDto {
|
||||||
@ApiProperty({
|
@ApiProperty({
|
||||||
@ -14,10 +14,10 @@ export class CreateSubspaceModelDto {
|
|||||||
|
|
||||||
@ApiProperty({
|
@ApiProperty({
|
||||||
description: 'List of tag models associated with the subspace',
|
description: 'List of tag models associated with the subspace',
|
||||||
type: [CreateTagModelDto],
|
type: [ProcessTagDto],
|
||||||
})
|
})
|
||||||
@IsArray()
|
@IsArray()
|
||||||
@ValidateNested({ each: true })
|
@ValidateNested({ each: true })
|
||||||
@Type(() => CreateTagModelDto)
|
@Type(() => ProcessTagDto)
|
||||||
tags?: CreateTagModelDto[];
|
tags?: ProcessTagDto[];
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,9 +1,15 @@
|
|||||||
import {
|
import {
|
||||||
SpaceModelEntity,
|
SpaceModelEntity,
|
||||||
|
SpaceModelProductAllocationEntity,
|
||||||
|
SpaceModelProductAllocationRepoitory,
|
||||||
SpaceModelRepository,
|
SpaceModelRepository,
|
||||||
} from '@app/common/modules/space-model';
|
} from '@app/common/modules/space-model';
|
||||||
import { HttpException, HttpStatus, Injectable } from '@nestjs/common';
|
import { HttpException, HttpStatus, Injectable } from '@nestjs/common';
|
||||||
import { CreateSpaceModelDto, UpdateSpaceModelDto } from '../dtos';
|
import {
|
||||||
|
CreateSpaceModelDto,
|
||||||
|
CreateSubspaceModelDto,
|
||||||
|
UpdateSpaceModelDto,
|
||||||
|
} from '../dtos';
|
||||||
import { ProjectParam } from 'src/community/dtos';
|
import { ProjectParam } from 'src/community/dtos';
|
||||||
import { SuccessResponseDto } from '@app/common/dto/success.response.dto';
|
import { SuccessResponseDto } from '@app/common/dto/success.response.dto';
|
||||||
import { SubSpaceModelService } from './subspace/subspace-model.service';
|
import { SubSpaceModelService } from './subspace/subspace-model.service';
|
||||||
@ -25,6 +31,10 @@ import {
|
|||||||
ModifySubspaceModelPayload,
|
ModifySubspaceModelPayload,
|
||||||
} from '../interfaces';
|
} from '../interfaces';
|
||||||
import { SpaceModelDto } from '@app/common/modules/space-model/dtos';
|
import { SpaceModelDto } from '@app/common/modules/space-model/dtos';
|
||||||
|
import { TagService as NewTagService } from 'src/tags/services';
|
||||||
|
import { ProductRepository } from '@app/common/modules/product/repositories';
|
||||||
|
import { NewTagEntity } from '@app/common/modules/tag';
|
||||||
|
import { ProcessTagDto } from 'src/tags/dtos';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class SpaceModelService {
|
export class SpaceModelService {
|
||||||
@ -35,6 +45,9 @@ export class SpaceModelService {
|
|||||||
private readonly subSpaceModelService: SubSpaceModelService,
|
private readonly subSpaceModelService: SubSpaceModelService,
|
||||||
private readonly tagModelService: TagModelService,
|
private readonly tagModelService: TagModelService,
|
||||||
private commandBus: CommandBus,
|
private commandBus: CommandBus,
|
||||||
|
private readonly tagService: NewTagService,
|
||||||
|
private readonly spaceModelProductAllocationRepository: SpaceModelProductAllocationRepoitory,
|
||||||
|
private readonly productRepository: ProductRepository,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
async createSpaceModel(
|
async createSpaceModel(
|
||||||
@ -62,23 +75,27 @@ export class SpaceModelService {
|
|||||||
|
|
||||||
const savedSpaceModel = await queryRunner.manager.save(spaceModel);
|
const savedSpaceModel = await queryRunner.manager.save(spaceModel);
|
||||||
|
|
||||||
|
this.validateUniqueTags(
|
||||||
|
tags,
|
||||||
|
this.subSpaceModelService.extractSubspaceTags(subspaceModels),
|
||||||
|
);
|
||||||
|
|
||||||
if (subspaceModels?.length) {
|
if (subspaceModels?.length) {
|
||||||
savedSpaceModel.subspaceModels =
|
savedSpaceModel.subspaceModels =
|
||||||
await this.subSpaceModelService.createSubSpaceModels(
|
await this.subSpaceModelService.createModels(
|
||||||
subspaceModels,
|
|
||||||
savedSpaceModel,
|
savedSpaceModel,
|
||||||
|
subspaceModels,
|
||||||
queryRunner,
|
queryRunner,
|
||||||
tags,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tags?.length) {
|
if (tags?.length) {
|
||||||
savedSpaceModel.tags = await this.tagModelService.createTags(
|
const processedTags = await this.tagService.processTags(
|
||||||
tags,
|
tags,
|
||||||
queryRunner,
|
params.projectUuid,
|
||||||
savedSpaceModel,
|
|
||||||
null,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
|
await this.createProductAllocations(spaceModel, processedTags);
|
||||||
}
|
}
|
||||||
|
|
||||||
await queryRunner.commitTransaction();
|
await queryRunner.commitTransaction();
|
||||||
@ -397,4 +414,83 @@ export class SpaceModelService {
|
|||||||
}
|
}
|
||||||
return spaceModel;
|
return spaceModel;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async createProductAllocations(
|
||||||
|
spaceModel: SpaceModelEntity,
|
||||||
|
tags: NewTagEntity[],
|
||||||
|
): Promise<void> {
|
||||||
|
const productAllocations: SpaceModelProductAllocationEntity[] = [];
|
||||||
|
|
||||||
|
for (const tag of tags) {
|
||||||
|
const product = await this.productRepository.findOne({
|
||||||
|
where: { uuid: tag.product.uuid },
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!product) {
|
||||||
|
throw new HttpException(
|
||||||
|
`Product with UUID ${tag.product.uuid} not found.`,
|
||||||
|
HttpStatus.NOT_FOUND,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const existingAllocation =
|
||||||
|
await this.spaceModelProductAllocationRepository.findOne({
|
||||||
|
where: { spaceModel, product },
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!existingAllocation) {
|
||||||
|
const productAllocation =
|
||||||
|
this.spaceModelProductAllocationRepository.create({
|
||||||
|
spaceModel,
|
||||||
|
product,
|
||||||
|
tags: [tag],
|
||||||
|
});
|
||||||
|
|
||||||
|
productAllocations.push(productAllocation);
|
||||||
|
} else {
|
||||||
|
if (
|
||||||
|
!existingAllocation.tags.some(
|
||||||
|
(existingTag) => existingTag.uuid === tag.uuid,
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
existingAllocation.tags.push(tag);
|
||||||
|
await this.spaceModelProductAllocationRepository.save(
|
||||||
|
existingAllocation,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (productAllocations.length > 0) {
|
||||||
|
await this.spaceModelProductAllocationRepository.save(productAllocations);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private validateUniqueTags(
|
||||||
|
spaceTags: ProcessTagDto[],
|
||||||
|
subspaceTags: ProcessTagDto[],
|
||||||
|
) {
|
||||||
|
const allTags = [...spaceTags, ...subspaceTags];
|
||||||
|
|
||||||
|
const tagUuidSet = new Set<string>();
|
||||||
|
const tagNameProductSet = new Set<string>();
|
||||||
|
|
||||||
|
for (const tag of allTags) {
|
||||||
|
if (tag.uuid) {
|
||||||
|
if (tagUuidSet.has(tag.uuid)) {
|
||||||
|
throw new HttpException(
|
||||||
|
`Duplicate tag UUID found: ${tag.uuid}`,
|
||||||
|
HttpStatus.BAD_REQUEST,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const tagKey = `${tag.name}-${tag.productUuid}`;
|
||||||
|
if (tagNameProductSet.has(tagKey)) {
|
||||||
|
throw new HttpException(
|
||||||
|
`Duplicate tag found with name "${tag.name}" and product "${tag.productUuid}".`,
|
||||||
|
HttpStatus.BAD_REQUEST,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,8 @@
|
|||||||
import {
|
import {
|
||||||
SpaceModelEntity,
|
SpaceModelEntity,
|
||||||
SubspaceModelEntity,
|
SubspaceModelEntity,
|
||||||
|
SubspaceModelProductAllocationEntity,
|
||||||
|
SubspaceModelProductAllocationRepoitory,
|
||||||
SubspaceModelRepository,
|
SubspaceModelRepository,
|
||||||
} from '@app/common/modules/space-model';
|
} from '@app/common/modules/space-model';
|
||||||
import { HttpException, HttpStatus, Injectable } from '@nestjs/common';
|
import { HttpException, HttpStatus, Injectable } from '@nestjs/common';
|
||||||
@ -17,13 +19,57 @@ import {
|
|||||||
} from 'src/space-model/dtos/subspaces-model-dtos';
|
} from 'src/space-model/dtos/subspaces-model-dtos';
|
||||||
import { TagModelService } from '../tag-model.service';
|
import { TagModelService } from '../tag-model.service';
|
||||||
import { ModifyAction } from '@app/common/constants/modify-action.enum';
|
import { ModifyAction } from '@app/common/constants/modify-action.enum';
|
||||||
|
import { ProcessTagDto } from 'src/tags/dtos';
|
||||||
|
import { TagService } from 'src/tags/services';
|
||||||
|
import { NewTagEntity } from '@app/common/modules/tag';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class SubSpaceModelService {
|
export class SubSpaceModelService {
|
||||||
constructor(
|
constructor(
|
||||||
private readonly subspaceModelRepository: SubspaceModelRepository,
|
private readonly subspaceModelRepository: SubspaceModelRepository,
|
||||||
private readonly tagModelService: TagModelService,
|
private readonly tagModelService: TagModelService,
|
||||||
|
private readonly tagService: TagService,
|
||||||
|
private readonly subspaceModelProductAllocationRepository: SubspaceModelProductAllocationRepoitory,
|
||||||
) {}
|
) {}
|
||||||
|
async createModels(
|
||||||
|
spaceModel: SpaceModelEntity,
|
||||||
|
dtos: CreateSubspaceModelDto[],
|
||||||
|
queryRunner: QueryRunner,
|
||||||
|
) {
|
||||||
|
this.validateNamesInDTO(dtos.map((dto) => dto.subspaceName));
|
||||||
|
|
||||||
|
const subspaceEntities: SubspaceModelEntity[] = [];
|
||||||
|
|
||||||
|
for (const dto of dtos) {
|
||||||
|
// Create Subspace Model entity
|
||||||
|
const subspaceModel = queryRunner.manager.create(
|
||||||
|
this.subspaceModelRepository.target,
|
||||||
|
{
|
||||||
|
subspaceName: dto.subspaceName,
|
||||||
|
spaceModel,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
subspaceEntities.push(subspaceModel);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save all subspaces in bulk
|
||||||
|
const savedSubspaces = await queryRunner.manager.save(subspaceEntities);
|
||||||
|
|
||||||
|
for (const [index, dto] of dtos.entries()) {
|
||||||
|
const subspaceModel = savedSubspaces[index];
|
||||||
|
|
||||||
|
// Process tags for this subspace
|
||||||
|
const processedTags = await this.tagService.processTags(
|
||||||
|
dto.tags,
|
||||||
|
spaceModel.project.uuid,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Create Product Allocations for Subspace Model
|
||||||
|
await this.createSubspaceProductAllocations(subspaceModel, processedTags);
|
||||||
|
}
|
||||||
|
return savedSubspaces;
|
||||||
|
}
|
||||||
|
|
||||||
async createSubSpaceModels(
|
async createSubSpaceModels(
|
||||||
subSpaceModelDtos: CreateSubspaceModelDto[],
|
subSpaceModelDtos: CreateSubspaceModelDto[],
|
||||||
@ -327,10 +373,7 @@ export class SubSpaceModelService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async validateName(
|
private async validateNamesInDTO(names: string[]) {
|
||||||
names: string[],
|
|
||||||
spaceModel: SpaceModelEntity,
|
|
||||||
): Promise<void> {
|
|
||||||
const seenNames = new Set<string>();
|
const seenNames = new Set<string>();
|
||||||
const duplicateNames = new Set<string>();
|
const duplicateNames = new Set<string>();
|
||||||
|
|
||||||
@ -346,7 +389,13 @@ export class SubSpaceModelService {
|
|||||||
HttpStatus.CONFLICT,
|
HttpStatus.CONFLICT,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async validateName(
|
||||||
|
names: string[],
|
||||||
|
spaceModel: SpaceModelEntity,
|
||||||
|
): Promise<void> {
|
||||||
|
this.validateNamesInDTO(names);
|
||||||
for (const name of names) {
|
for (const name of names) {
|
||||||
await this.checkDuplicateNames(name, spaceModel.uuid);
|
await this.checkDuplicateNames(name, spaceModel.uuid);
|
||||||
}
|
}
|
||||||
@ -368,4 +417,51 @@ export class SubSpaceModelService {
|
|||||||
await queryRunner.manager.save(subSpaceModel);
|
await queryRunner.manager.save(subSpaceModel);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extractSubspaceTags(
|
||||||
|
subspaceModels: CreateSubspaceModelDto[],
|
||||||
|
): ProcessTagDto[] {
|
||||||
|
return subspaceModels.flatMap((subspace) => subspace.tags || []);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async createSubspaceProductAllocations(
|
||||||
|
subspaceModel: SubspaceModelEntity,
|
||||||
|
tags: NewTagEntity[],
|
||||||
|
): Promise<void> {
|
||||||
|
const allocations: SubspaceModelProductAllocationEntity[] = [];
|
||||||
|
|
||||||
|
for (const tag of tags) {
|
||||||
|
const existingAllocation =
|
||||||
|
await this.subspaceModelProductAllocationRepository.findOne({
|
||||||
|
where: { subspaceModel, product: tag.product },
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!existingAllocation) {
|
||||||
|
const allocation = this.subspaceModelProductAllocationRepository.create(
|
||||||
|
{
|
||||||
|
subspaceModel,
|
||||||
|
product: tag.product,
|
||||||
|
tags: [tag],
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
allocations.push(allocation);
|
||||||
|
} else {
|
||||||
|
if (
|
||||||
|
!existingAllocation.tags.some(
|
||||||
|
(existingTag) => existingTag.uuid === tag.uuid,
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
existingAllocation.tags.push(tag);
|
||||||
|
await this.subspaceModelProductAllocationRepository.save(
|
||||||
|
existingAllocation,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (allocations.length > 0) {
|
||||||
|
await this.subspaceModelProductAllocationRepository.save(allocations);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,2 +1,3 @@
|
|||||||
export * from './tags.dto';
|
export * from './tags.dto';
|
||||||
export * from './bulk-create-tag.dto';
|
export * from './bulk-create-tag.dto';
|
||||||
|
export * from './process-tag-dto';
|
||||||
|
|||||||
36
src/tags/dtos/process-tag-dto.ts
Normal file
36
src/tags/dtos/process-tag-dto.ts
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
import { ApiProperty } from '@nestjs/swagger';
|
||||||
|
import { IsOptional, IsString, IsUUID } from 'class-validator';
|
||||||
|
|
||||||
|
export class ProcessTagDto {
|
||||||
|
@ApiProperty({
|
||||||
|
description: 'The name of the tag',
|
||||||
|
example: 'New Tag',
|
||||||
|
})
|
||||||
|
@IsString()
|
||||||
|
@IsOptional()
|
||||||
|
name: string;
|
||||||
|
|
||||||
|
@ApiProperty({
|
||||||
|
description: 'UUID of the product associated with the tag',
|
||||||
|
example: '550e8400-e29b-41d4-a716-446655440000',
|
||||||
|
})
|
||||||
|
@IsUUID()
|
||||||
|
@IsOptional()
|
||||||
|
productUuid: string;
|
||||||
|
|
||||||
|
@ApiProperty({
|
||||||
|
description: 'UUID of the project associated with the tag',
|
||||||
|
example: '123e4567-e89b-12d3-a456-426614174000',
|
||||||
|
})
|
||||||
|
@IsUUID()
|
||||||
|
@IsOptional()
|
||||||
|
projectUuid: string;
|
||||||
|
|
||||||
|
@ApiProperty({
|
||||||
|
description: 'UUID of the tag',
|
||||||
|
example: '123e4567-e89b-12d3-a456-426614174000',
|
||||||
|
})
|
||||||
|
@IsUUID()
|
||||||
|
@IsOptional()
|
||||||
|
uuid: string;
|
||||||
|
}
|
||||||
@ -5,6 +5,8 @@ import {
|
|||||||
Injectable,
|
Injectable,
|
||||||
ConflictException,
|
ConflictException,
|
||||||
NotFoundException,
|
NotFoundException,
|
||||||
|
HttpException,
|
||||||
|
HttpStatus,
|
||||||
} from '@nestjs/common';
|
} from '@nestjs/common';
|
||||||
import { CreateTagDto } from '../dtos/tags.dto';
|
import { CreateTagDto } from '../dtos/tags.dto';
|
||||||
import { ProductEntity } from '@app/common/modules/product/entities';
|
import { ProductEntity } from '@app/common/modules/product/entities';
|
||||||
@ -13,7 +15,7 @@ import { SuccessResponseDto } from '@app/common/dto/success.response.dto';
|
|||||||
import { BaseResponseDto } from '@app/common/dto/base.response.dto';
|
import { BaseResponseDto } from '@app/common/dto/base.response.dto';
|
||||||
import { NewTagEntity } from '@app/common/modules/tag';
|
import { NewTagEntity } from '@app/common/modules/tag';
|
||||||
import { GetTagsParam } from '../dtos/get-tags.param';
|
import { GetTagsParam } from '../dtos/get-tags.param';
|
||||||
import { BulkCreateTagsDto } from '../dtos';
|
import { BulkCreateTagsDto, ProcessTagDto } from '../dtos';
|
||||||
import { In } from 'typeorm';
|
import { In } from 'typeorm';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
@ -61,6 +63,42 @@ export class TagService {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async processTags(
|
||||||
|
tags: ProcessTagDto[],
|
||||||
|
projectUuid: string,
|
||||||
|
): Promise<NewTagEntity[]> {
|
||||||
|
if (!tags || tags.length === 0) return [];
|
||||||
|
|
||||||
|
const newTags: CreateTagDto[] = [];
|
||||||
|
const existingTagUuids: string[] = [];
|
||||||
|
|
||||||
|
for (const tag of tags) {
|
||||||
|
if (tag.uuid) {
|
||||||
|
existingTagUuids.push(tag.uuid);
|
||||||
|
} else {
|
||||||
|
newTags.push(tag);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const existingTags = await this.tagRepository.find({
|
||||||
|
where: { uuid: In(existingTagUuids), project: { uuid: projectUuid } },
|
||||||
|
relations: ['product'],
|
||||||
|
});
|
||||||
|
|
||||||
|
if (existingTags.length !== existingTagUuids.length) {
|
||||||
|
throw new HttpException(
|
||||||
|
`Some provided tag UUIDs do not exist in the project.`,
|
||||||
|
HttpStatus.NOT_FOUND,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const createdTags = newTags.length
|
||||||
|
? (await this.bulkCreateTags({ projectUuid, tags: newTags })).data
|
||||||
|
: [];
|
||||||
|
|
||||||
|
return [...existingTags, ...createdTags];
|
||||||
|
}
|
||||||
|
|
||||||
async bulkCreateTags(dto: BulkCreateTagsDto): Promise<BaseResponseDto> {
|
async bulkCreateTags(dto: BulkCreateTagsDto): Promise<BaseResponseDto> {
|
||||||
const { projectUuid, tags } = dto;
|
const { projectUuid, tags } = dto;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user