From 61f159010ea42ff0e295b97518a3d8c17970ab36 Mon Sep 17 00:00:00 2001 From: yassinedorbozgithub Date: Fri, 10 Jan 2025 19:24:49 +0100 Subject: [PATCH] feat: implement dynamic create DTO for NLP Module --- .../lib/__test__/base-nlp-helper.spec.ts | 12 ++++++++ api/src/nlp/dto/nlp-entity.dto.ts | 14 +++++++--- api/src/nlp/dto/nlp-sample.dto.ts | 7 ++++- api/src/nlp/dto/nlp-value.dto.ts | 11 ++++++-- .../nlp/repositories/nlp-entity.repository.ts | 4 ++- .../nlp/repositories/nlp-sample.repository.ts | 4 ++- .../nlp/repositories/nlp-value.repository.ts | 4 ++- api/src/nlp/schemas/nlp-entity.schema.ts | 4 +-- api/src/nlp/schemas/nlp-sample.schema.ts | 4 +-- api/src/nlp/schemas/nlp-value.schema.ts | 6 ++-- api/src/nlp/seeds/nlp-entity.seed.ts | 4 ++- api/src/nlp/seeds/nlp-value.seed.ts | 7 +++-- api/src/nlp/services/nlp-entity.service.ts | 5 ++-- api/src/nlp/services/nlp-sample.service.ts | 8 ++++-- api/src/nlp/services/nlp-value.service.ts | 9 ++++-- api/src/user/seeds/role.seed.ts | 8 +++++- api/src/utils/generics/base-seeder.ts | 4 ++- api/src/utils/test/fixtures/nlpsample.ts | 28 +++++++++++-------- 18 files changed, 102 insertions(+), 41 deletions(-) diff --git a/api/src/helper/lib/__test__/base-nlp-helper.spec.ts b/api/src/helper/lib/__test__/base-nlp-helper.spec.ts index fe547613..4f2f8ddb 100644 --- a/api/src/helper/lib/__test__/base-nlp-helper.spec.ts +++ b/api/src/helper/lib/__test__/base-nlp-helper.spec.ts @@ -137,12 +137,16 @@ describe('BaseNlpHelper', () => { name: 'intent', createdAt: new Date(), updatedAt: new Date(), + builtin: false, + lookups: [], }, entity2: { id: new ObjectId().toString(), name: 'test-entity', createdAt: new Date(), updatedAt: new Date(), + builtin: false, + lookups: [], }, }); jest.spyOn(NlpValue, 'getValueMap').mockReturnValue({ @@ -152,6 +156,9 @@ describe('BaseNlpHelper', () => { entity: 'entity1', // Add the required entity field createdAt: new Date(), updatedAt: new Date(), + builtin: false, + expressions: [], + metadata: [], }, value2: { id: new ObjectId().toString(), @@ -159,6 +166,9 @@ describe('BaseNlpHelper', () => { entity: 'entity2', // Add the required entity field createdAt: new Date(), updatedAt: new Date(), + builtin: false, + expressions: [], + metadata: [], }, }); @@ -195,6 +205,8 @@ describe('BaseNlpHelper', () => { name: 'test-entity', createdAt: new Date(), updatedAt: new Date(), + builtin: false, + lookups: [], }, }); diff --git a/api/src/nlp/dto/nlp-entity.dto.ts b/api/src/nlp/dto/nlp-entity.dto.ts index 5ef92d0a..4390b4f1 100644 --- a/api/src/nlp/dto/nlp-entity.dto.ts +++ b/api/src/nlp/dto/nlp-entity.dto.ts @@ -8,15 +8,17 @@ import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; import { - IsString, - IsOptional, - IsBoolean, IsArray, - Matches, + IsBoolean, IsIn, IsNotEmpty, + IsOptional, + IsString, + Matches, } from 'class-validator'; +import { DtoConfig } from '@/utils/types/dto.types'; + export type Lookup = 'keywords' | 'trait' | 'free-text'; export class NlpEntityCreateDto { @@ -46,3 +48,7 @@ export class NlpEntityCreateDto { @IsOptional() builtin?: boolean; } + +export type NlpEntityDTOMapActions = DtoConfig<{ + create: NlpEntityCreateDto; +}>; diff --git a/api/src/nlp/dto/nlp-sample.dto.ts b/api/src/nlp/dto/nlp-sample.dto.ts index 762be371..ea24cc75 100644 --- a/api/src/nlp/dto/nlp-sample.dto.ts +++ b/api/src/nlp/dto/nlp-sample.dto.ts @@ -15,6 +15,7 @@ import { IsString, } from 'class-validator'; +import { DtoConfig } from '@/utils/types/dto.types'; import { IsObjectId } from '@/utils/validation-rules/is-object-id'; import { NlpSampleEntityValue, NlpSampleState } from '../schemas/types'; @@ -40,7 +41,7 @@ export class NlpSampleCreateDto { @IsString() @IsIn(Object.values(NlpSampleState)) @IsOptional() - type?: NlpSampleState; + type?: keyof typeof NlpSampleState; @ApiProperty({ description: 'NLP sample language id', type: String }) @IsString() @@ -63,3 +64,7 @@ export class NlpSampleDto extends NlpSampleCreateDto { } export class NlpSampleUpdateDto extends PartialType(NlpSampleCreateDto) {} + +export type NlpSampleDTOMapActions = DtoConfig<{ + create: NlpSampleCreateDto; +}>; diff --git a/api/src/nlp/dto/nlp-value.dto.ts b/api/src/nlp/dto/nlp-value.dto.ts index 215c5f1c..46d2deca 100644 --- a/api/src/nlp/dto/nlp-value.dto.ts +++ b/api/src/nlp/dto/nlp-value.dto.ts @@ -9,14 +9,15 @@ import { PartialType } from '@nestjs/mapped-types'; import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; import { - IsString, - IsBoolean, IsArray, - IsObject, + IsBoolean, IsNotEmpty, + IsObject, IsOptional, + IsString, } from 'class-validator'; +import { DtoConfig } from '@/utils/types/dto.types'; import { IsObjectId } from '@/utils/validation-rules/is-object-id'; export class NlpValueCreateDto { @@ -52,3 +53,7 @@ export class NlpValueCreateDto { } export class NlpValueUpdateDto extends PartialType(NlpValueCreateDto) {} + +export type NlpValueDTOMapActions = DtoConfig<{ + create: NlpValueCreateDto; +}>; diff --git a/api/src/nlp/repositories/nlp-entity.repository.ts b/api/src/nlp/repositories/nlp-entity.repository.ts index fdaac277..b9a0c10a 100644 --- a/api/src/nlp/repositories/nlp-entity.repository.ts +++ b/api/src/nlp/repositories/nlp-entity.repository.ts @@ -14,6 +14,7 @@ import { Document, Model, Query } from 'mongoose'; import { BaseRepository, DeleteResult } from '@/utils/generics/base-repository'; import { TFilterQuery } from '@/utils/types/filter.types'; +import { NlpEntityDTOMapActions } from '../dto/nlp-entity.dto'; import { NLP_ENTITY_POPULATE, NlpEntity, @@ -29,7 +30,8 @@ import { NlpValueRepository } from './nlp-value.repository'; export class NlpEntityRepository extends BaseRepository< NlpEntity, NlpEntityPopulate, - NlpEntityFull + NlpEntityFull, + NlpEntityDTOMapActions > { constructor( readonly eventEmitter: EventEmitter2, diff --git a/api/src/nlp/repositories/nlp-sample.repository.ts b/api/src/nlp/repositories/nlp-sample.repository.ts index f5f953d1..3d300966 100644 --- a/api/src/nlp/repositories/nlp-sample.repository.ts +++ b/api/src/nlp/repositories/nlp-sample.repository.ts @@ -14,6 +14,7 @@ import { Document, Model, Query } from 'mongoose'; import { BaseRepository, DeleteResult } from '@/utils/generics/base-repository'; import { TFilterQuery } from '@/utils/types/filter.types'; +import { NlpSampleDTOMapActions } from '../dto/nlp-sample.dto'; import { NLP_SAMPLE_POPULATE, NlpSample, @@ -27,7 +28,8 @@ import { NlpSampleEntityRepository } from './nlp-sample-entity.repository'; export class NlpSampleRepository extends BaseRepository< NlpSample, NlpSamplePopulate, - NlpSampleFull + NlpSampleFull, + NlpSampleDTOMapActions > { constructor( readonly eventEmitter: EventEmitter2, diff --git a/api/src/nlp/repositories/nlp-value.repository.ts b/api/src/nlp/repositories/nlp-value.repository.ts index a09b7cde..a99f5d22 100644 --- a/api/src/nlp/repositories/nlp-value.repository.ts +++ b/api/src/nlp/repositories/nlp-value.repository.ts @@ -14,6 +14,7 @@ import { Document, Model, Query } from 'mongoose'; import { BaseRepository, DeleteResult } from '@/utils/generics/base-repository'; import { TFilterQuery } from '@/utils/types/filter.types'; +import { NlpValueDTOMapActions } from '../dto/nlp-value.dto'; import { NLP_VALUE_POPULATE, NlpValue, @@ -28,7 +29,8 @@ import { NlpSampleEntityRepository } from './nlp-sample-entity.repository'; export class NlpValueRepository extends BaseRepository< NlpValue, NlpValuePopulate, - NlpValueFull + NlpValueFull, + NlpValueDTOMapActions > { constructor( readonly eventEmitter: EventEmitter2, diff --git a/api/src/nlp/schemas/nlp-entity.schema.ts b/api/src/nlp/schemas/nlp-entity.schema.ts index 77012174..86085d05 100644 --- a/api/src/nlp/schemas/nlp-entity.schema.ts +++ b/api/src/nlp/schemas/nlp-entity.schema.ts @@ -44,7 +44,7 @@ export class NlpEntityStub extends BaseSchema { * Lookup strategy can contain : keywords, trait, free-text */ @Prop({ type: [String], default: ['keywords'] }) - lookups?: Lookup[]; + lookups: Lookup[]; /** * Description of the entity purpose. @@ -56,7 +56,7 @@ export class NlpEntityStub extends BaseSchema { * Either or not this entity a built-in (either fixtures or shipped along with the 3rd party ai). */ @Prop({ type: Boolean, default: false }) - builtin?: boolean; + builtin: boolean; /** * Returns a map object for entities diff --git a/api/src/nlp/schemas/nlp-sample.schema.ts b/api/src/nlp/schemas/nlp-sample.schema.ts index 72d1900e..2a33ab96 100644 --- a/api/src/nlp/schemas/nlp-sample.schema.ts +++ b/api/src/nlp/schemas/nlp-sample.schema.ts @@ -33,7 +33,7 @@ export class NlpSampleStub extends BaseSchema { * Either or not this sample was used for traning already. */ @Prop({ type: Boolean, default: false }) - trained?: boolean; + trained: boolean; /** * From where this sample was provided. @@ -43,7 +43,7 @@ export class NlpSampleStub extends BaseSchema { enum: Object.values(NlpSampleState), default: NlpSampleState.train, }) - type?: keyof typeof NlpSampleState; + type: keyof typeof NlpSampleState; /** * The language of the sample. diff --git a/api/src/nlp/schemas/nlp-value.schema.ts b/api/src/nlp/schemas/nlp-value.schema.ts index 7469fc87..a5f2e4be 100644 --- a/api/src/nlp/schemas/nlp-value.schema.ts +++ b/api/src/nlp/schemas/nlp-value.schema.ts @@ -38,19 +38,19 @@ export class NlpValueStub extends BaseSchema { * An array of synonyms or equivalent words that fits this value. */ @Prop({ type: [String], default: [] }) - expressions?: string[]; + expressions: string[]; /** * Metadata are additional data that can be associated to this values, most of the time, the metadata contains system values or ids (e.g: value: "coffee", metadata: "item_11") . */ @Prop({ type: JSON, default: {} }) - metadata?: Record; + metadata: Record; /** * Either or not this value a built-in (either fixtures or shipped along with the 3rd party ai). */ @Prop({ type: Boolean, default: false }) - builtin?: boolean; + builtin: boolean; /** * The entity to which this value belongs to. diff --git a/api/src/nlp/seeds/nlp-entity.seed.ts b/api/src/nlp/seeds/nlp-entity.seed.ts index c5523c7a..4e4e008d 100644 --- a/api/src/nlp/seeds/nlp-entity.seed.ts +++ b/api/src/nlp/seeds/nlp-entity.seed.ts @@ -10,6 +10,7 @@ import { Injectable } from '@nestjs/common'; import { BaseSeeder } from '@/utils/generics/base-seeder'; +import { NlpEntityDTOMapActions } from '../dto/nlp-entity.dto'; import { NlpEntityRepository } from '../repositories/nlp-entity.repository'; import { NlpEntity, @@ -21,7 +22,8 @@ import { export class NlpEntitySeeder extends BaseSeeder< NlpEntity, NlpEntityPopulate, - NlpEntityFull + NlpEntityFull, + NlpEntityDTOMapActions > { constructor(nlpEntityRepository: NlpEntityRepository) { super(nlpEntityRepository); diff --git a/api/src/nlp/seeds/nlp-value.seed.ts b/api/src/nlp/seeds/nlp-value.seed.ts index cc0856d1..3caba199 100644 --- a/api/src/nlp/seeds/nlp-value.seed.ts +++ b/api/src/nlp/seeds/nlp-value.seed.ts @@ -8,9 +8,9 @@ import { Injectable } from '@nestjs/common'; -import { BaseSchema } from '@/utils/generics/base-schema'; import { BaseSeeder } from '@/utils/generics/base-seeder'; +import { NlpValueCreateDto, NlpValueDTOMapActions } from '../dto/nlp-value.dto'; import { NlpEntityRepository } from '../repositories/nlp-entity.repository'; import { NlpValueRepository } from '../repositories/nlp-value.repository'; import { @@ -23,7 +23,8 @@ import { export class NlpValueSeeder extends BaseSeeder< NlpValue, NlpValuePopulate, - NlpValueFull + NlpValueFull, + NlpValueDTOMapActions > { constructor( nlpValueRepository: NlpValueRepository, @@ -32,7 +33,7 @@ export class NlpValueSeeder extends BaseSeeder< super(nlpValueRepository); } - async seed(models: Omit[]): Promise { + async seed(models: NlpValueCreateDto[]): Promise { if (await this.isEmpty()) { const entities = await this.nlpEntityRepository.findAll(); const modelDtos = models.map((v) => ({ diff --git a/api/src/nlp/services/nlp-entity.service.ts b/api/src/nlp/services/nlp-entity.service.ts index e119bf87..766b5791 100644 --- a/api/src/nlp/services/nlp-entity.service.ts +++ b/api/src/nlp/services/nlp-entity.service.ts @@ -10,7 +10,7 @@ import { Injectable } from '@nestjs/common'; import { BaseService } from '@/utils/generics/base-service'; -import { Lookup } from '../dto/nlp-entity.dto'; +import { Lookup, NlpEntityDTOMapActions } from '../dto/nlp-entity.dto'; import { NlpEntityRepository } from '../repositories/nlp-entity.repository'; import { NlpEntity, @@ -25,7 +25,8 @@ import { NlpValueService } from './nlp-value.service'; export class NlpEntityService extends BaseService< NlpEntity, NlpEntityPopulate, - NlpEntityFull + NlpEntityFull, + NlpEntityDTOMapActions > { constructor( readonly repository: NlpEntityRepository, diff --git a/api/src/nlp/services/nlp-sample.service.ts b/api/src/nlp/services/nlp-sample.service.ts index ca7384b1..b66e1cf3 100644 --- a/api/src/nlp/services/nlp-sample.service.ts +++ b/api/src/nlp/services/nlp-sample.service.ts @@ -21,7 +21,10 @@ import { LoggerService } from '@/logger/logger.service'; import { BaseService } from '@/utils/generics/base-service'; import { THydratedDocument } from '@/utils/types/filter.types'; -import { NlpSampleCreateDto } from '../dto/nlp-sample.dto'; +import { + NlpSampleCreateDto, + NlpSampleDTOMapActions, +} from '../dto/nlp-sample.dto'; import { NlpSampleRepository } from '../repositories/nlp-sample.repository'; import { NlpSample, @@ -37,7 +40,8 @@ import { NlpSampleEntityService } from './nlp-sample-entity.service'; export class NlpSampleService extends BaseService< NlpSample, NlpSamplePopulate, - NlpSampleFull + NlpSampleFull, + NlpSampleDTOMapActions > { constructor( readonly repository: NlpSampleRepository, diff --git a/api/src/nlp/services/nlp-value.service.ts b/api/src/nlp/services/nlp-value.service.ts index 2b2bcc82..13423967 100644 --- a/api/src/nlp/services/nlp-value.service.ts +++ b/api/src/nlp/services/nlp-value.service.ts @@ -11,7 +11,11 @@ import { forwardRef, Inject, Injectable } from '@nestjs/common'; import { DeleteResult } from '@/utils/generics/base-repository'; import { BaseService } from '@/utils/generics/base-service'; -import { NlpValueCreateDto, NlpValueUpdateDto } from '../dto/nlp-value.dto'; +import { + NlpValueCreateDto, + NlpValueDTOMapActions, + NlpValueUpdateDto, +} from '../dto/nlp-value.dto'; import { NlpValueRepository } from '../repositories/nlp-value.repository'; import { NlpEntity } from '../schemas/nlp-entity.schema'; import { @@ -27,7 +31,8 @@ import { NlpEntityService } from './nlp-entity.service'; export class NlpValueService extends BaseService< NlpValue, NlpValuePopulate, - NlpValueFull + NlpValueFull, + NlpValueDTOMapActions > { constructor( readonly repository: NlpValueRepository, diff --git a/api/src/user/seeds/role.seed.ts b/api/src/user/seeds/role.seed.ts index d741c69a..364f8c01 100644 --- a/api/src/user/seeds/role.seed.ts +++ b/api/src/user/seeds/role.seed.ts @@ -10,11 +10,17 @@ import { Injectable } from '@nestjs/common'; import { BaseSeeder } from '@/utils/generics/base-seeder'; +import { RoleDTOMapActions } from '../dto/role.dto'; import { RoleRepository } from '../repositories/role.repository'; import { Role, RoleFull, RolePopulate } from '../schemas/role.schema'; @Injectable() -export class RoleSeeder extends BaseSeeder { +export class RoleSeeder extends BaseSeeder< + Role, + RolePopulate, + RoleFull, + RoleDTOMapActions +> { constructor(private readonly roleRepository: RoleRepository) { super(roleRepository); } diff --git a/api/src/utils/generics/base-seeder.ts b/api/src/utils/generics/base-seeder.ts index 338ac855..c1e160ac 100644 --- a/api/src/utils/generics/base-seeder.ts +++ b/api/src/utils/generics/base-seeder.ts @@ -19,7 +19,9 @@ export abstract class BaseSeeder< TFull extends Omit = never, DTOCruds extends DtoProps = unknown, > { - constructor(protected readonly repository: BaseRepository) {} + constructor( + protected readonly repository: BaseRepository, + ) {} async findAll(): Promise { return await this.repository.findAll(); diff --git a/api/src/utils/test/fixtures/nlpsample.ts b/api/src/utils/test/fixtures/nlpsample.ts index 9be261a1..7ee11dcc 100644 --- a/api/src/utils/test/fixtures/nlpsample.ts +++ b/api/src/utils/test/fixtures/nlpsample.ts @@ -8,16 +8,27 @@ import mongoose from 'mongoose'; -import { NlpSampleCreateDto } from '@/nlp/dto/nlp-sample.dto'; -import { NlpSampleModel, NlpSample } from '@/nlp/schemas/nlp-sample.schema'; +import { NlpSample, NlpSampleModel } from '@/nlp/schemas/nlp-sample.schema'; import { NlpSampleState } from '@/nlp/schemas/types'; +import { BaseSchema } from '@/utils/generics/base-schema'; import { getFixturesWithDefaultValues } from '../defaultValues'; -import { TFixturesDefaultValues } from '../types'; import { installLanguageFixtures } from './language'; -const nlpSamples: NlpSampleCreateDto[] = [ +export const fieldsWithDefaultValues = { + type: NlpSampleState.train, + trained: false, +} satisfies Partial; + +type TFieldWithDefaultValues = + | keyof typeof fieldsWithDefaultValues + | keyof BaseSchema; +type TTransformedField = Omit & + Partial>; +type TNlpSample = TTransformedField; + +const nlpSamples: TNlpSample[] = [ { text: 'yess', language: '0', @@ -38,14 +49,9 @@ const nlpSamples: NlpSampleCreateDto[] = [ }, ]; -export const nlpSampleDefaultValues: TFixturesDefaultValues = { - type: NlpSampleState.train, - trained: false, -}; - -export const nlpSampleFixtures = getFixturesWithDefaultValues({ +export const nlpSampleFixtures = getFixturesWithDefaultValues({ fixtures: nlpSamples, - defaultValues: nlpSampleDefaultValues, + defaultValues: fieldsWithDefaultValues, }); export const installNlpSampleFixtures = async () => {