feat: implement dynamic create DTO for NLP Module

This commit is contained in:
yassinedorbozgithub 2025-01-10 19:24:49 +01:00
parent 571ba1d568
commit 61f159010e
18 changed files with 102 additions and 41 deletions

View File

@ -137,12 +137,16 @@ describe('BaseNlpHelper', () => {
name: 'intent', name: 'intent',
createdAt: new Date(), createdAt: new Date(),
updatedAt: new Date(), updatedAt: new Date(),
builtin: false,
lookups: [],
}, },
entity2: { entity2: {
id: new ObjectId().toString(), id: new ObjectId().toString(),
name: 'test-entity', name: 'test-entity',
createdAt: new Date(), createdAt: new Date(),
updatedAt: new Date(), updatedAt: new Date(),
builtin: false,
lookups: [],
}, },
}); });
jest.spyOn(NlpValue, 'getValueMap').mockReturnValue({ jest.spyOn(NlpValue, 'getValueMap').mockReturnValue({
@ -152,6 +156,9 @@ describe('BaseNlpHelper', () => {
entity: 'entity1', // Add the required entity field entity: 'entity1', // Add the required entity field
createdAt: new Date(), createdAt: new Date(),
updatedAt: new Date(), updatedAt: new Date(),
builtin: false,
expressions: [],
metadata: [],
}, },
value2: { value2: {
id: new ObjectId().toString(), id: new ObjectId().toString(),
@ -159,6 +166,9 @@ describe('BaseNlpHelper', () => {
entity: 'entity2', // Add the required entity field entity: 'entity2', // Add the required entity field
createdAt: new Date(), createdAt: new Date(),
updatedAt: new Date(), updatedAt: new Date(),
builtin: false,
expressions: [],
metadata: [],
}, },
}); });
@ -195,6 +205,8 @@ describe('BaseNlpHelper', () => {
name: 'test-entity', name: 'test-entity',
createdAt: new Date(), createdAt: new Date(),
updatedAt: new Date(), updatedAt: new Date(),
builtin: false,
lookups: [],
}, },
}); });

View File

@ -8,15 +8,17 @@
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
import { import {
IsString,
IsOptional,
IsBoolean,
IsArray, IsArray,
Matches, IsBoolean,
IsIn, IsIn,
IsNotEmpty, IsNotEmpty,
IsOptional,
IsString,
Matches,
} from 'class-validator'; } from 'class-validator';
import { DtoConfig } from '@/utils/types/dto.types';
export type Lookup = 'keywords' | 'trait' | 'free-text'; export type Lookup = 'keywords' | 'trait' | 'free-text';
export class NlpEntityCreateDto { export class NlpEntityCreateDto {
@ -46,3 +48,7 @@ export class NlpEntityCreateDto {
@IsOptional() @IsOptional()
builtin?: boolean; builtin?: boolean;
} }
export type NlpEntityDTOMapActions = DtoConfig<{
create: NlpEntityCreateDto;
}>;

View File

@ -15,6 +15,7 @@ import {
IsString, IsString,
} from 'class-validator'; } from 'class-validator';
import { DtoConfig } from '@/utils/types/dto.types';
import { IsObjectId } from '@/utils/validation-rules/is-object-id'; import { IsObjectId } from '@/utils/validation-rules/is-object-id';
import { NlpSampleEntityValue, NlpSampleState } from '../schemas/types'; import { NlpSampleEntityValue, NlpSampleState } from '../schemas/types';
@ -40,7 +41,7 @@ export class NlpSampleCreateDto {
@IsString() @IsString()
@IsIn(Object.values(NlpSampleState)) @IsIn(Object.values(NlpSampleState))
@IsOptional() @IsOptional()
type?: NlpSampleState; type?: keyof typeof NlpSampleState;
@ApiProperty({ description: 'NLP sample language id', type: String }) @ApiProperty({ description: 'NLP sample language id', type: String })
@IsString() @IsString()
@ -63,3 +64,7 @@ export class NlpSampleDto extends NlpSampleCreateDto {
} }
export class NlpSampleUpdateDto extends PartialType(NlpSampleCreateDto) {} export class NlpSampleUpdateDto extends PartialType(NlpSampleCreateDto) {}
export type NlpSampleDTOMapActions = DtoConfig<{
create: NlpSampleCreateDto;
}>;

View File

@ -9,14 +9,15 @@
import { PartialType } from '@nestjs/mapped-types'; import { PartialType } from '@nestjs/mapped-types';
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
import { import {
IsString,
IsBoolean,
IsArray, IsArray,
IsObject, IsBoolean,
IsNotEmpty, IsNotEmpty,
IsObject,
IsOptional, IsOptional,
IsString,
} from 'class-validator'; } from 'class-validator';
import { DtoConfig } from '@/utils/types/dto.types';
import { IsObjectId } from '@/utils/validation-rules/is-object-id'; import { IsObjectId } from '@/utils/validation-rules/is-object-id';
export class NlpValueCreateDto { export class NlpValueCreateDto {
@ -52,3 +53,7 @@ export class NlpValueCreateDto {
} }
export class NlpValueUpdateDto extends PartialType(NlpValueCreateDto) {} export class NlpValueUpdateDto extends PartialType(NlpValueCreateDto) {}
export type NlpValueDTOMapActions = DtoConfig<{
create: NlpValueCreateDto;
}>;

View File

@ -14,6 +14,7 @@ import { Document, Model, Query } from 'mongoose';
import { BaseRepository, DeleteResult } from '@/utils/generics/base-repository'; import { BaseRepository, DeleteResult } from '@/utils/generics/base-repository';
import { TFilterQuery } from '@/utils/types/filter.types'; import { TFilterQuery } from '@/utils/types/filter.types';
import { NlpEntityDTOMapActions } from '../dto/nlp-entity.dto';
import { import {
NLP_ENTITY_POPULATE, NLP_ENTITY_POPULATE,
NlpEntity, NlpEntity,
@ -29,7 +30,8 @@ import { NlpValueRepository } from './nlp-value.repository';
export class NlpEntityRepository extends BaseRepository< export class NlpEntityRepository extends BaseRepository<
NlpEntity, NlpEntity,
NlpEntityPopulate, NlpEntityPopulate,
NlpEntityFull NlpEntityFull,
NlpEntityDTOMapActions
> { > {
constructor( constructor(
readonly eventEmitter: EventEmitter2, readonly eventEmitter: EventEmitter2,

View File

@ -14,6 +14,7 @@ import { Document, Model, Query } from 'mongoose';
import { BaseRepository, DeleteResult } from '@/utils/generics/base-repository'; import { BaseRepository, DeleteResult } from '@/utils/generics/base-repository';
import { TFilterQuery } from '@/utils/types/filter.types'; import { TFilterQuery } from '@/utils/types/filter.types';
import { NlpSampleDTOMapActions } from '../dto/nlp-sample.dto';
import { import {
NLP_SAMPLE_POPULATE, NLP_SAMPLE_POPULATE,
NlpSample, NlpSample,
@ -27,7 +28,8 @@ import { NlpSampleEntityRepository } from './nlp-sample-entity.repository';
export class NlpSampleRepository extends BaseRepository< export class NlpSampleRepository extends BaseRepository<
NlpSample, NlpSample,
NlpSamplePopulate, NlpSamplePopulate,
NlpSampleFull NlpSampleFull,
NlpSampleDTOMapActions
> { > {
constructor( constructor(
readonly eventEmitter: EventEmitter2, readonly eventEmitter: EventEmitter2,

View File

@ -14,6 +14,7 @@ import { Document, Model, Query } from 'mongoose';
import { BaseRepository, DeleteResult } from '@/utils/generics/base-repository'; import { BaseRepository, DeleteResult } from '@/utils/generics/base-repository';
import { TFilterQuery } from '@/utils/types/filter.types'; import { TFilterQuery } from '@/utils/types/filter.types';
import { NlpValueDTOMapActions } from '../dto/nlp-value.dto';
import { import {
NLP_VALUE_POPULATE, NLP_VALUE_POPULATE,
NlpValue, NlpValue,
@ -28,7 +29,8 @@ import { NlpSampleEntityRepository } from './nlp-sample-entity.repository';
export class NlpValueRepository extends BaseRepository< export class NlpValueRepository extends BaseRepository<
NlpValue, NlpValue,
NlpValuePopulate, NlpValuePopulate,
NlpValueFull NlpValueFull,
NlpValueDTOMapActions
> { > {
constructor( constructor(
readonly eventEmitter: EventEmitter2, readonly eventEmitter: EventEmitter2,

View File

@ -44,7 +44,7 @@ export class NlpEntityStub extends BaseSchema {
* Lookup strategy can contain : keywords, trait, free-text * Lookup strategy can contain : keywords, trait, free-text
*/ */
@Prop({ type: [String], default: ['keywords'] }) @Prop({ type: [String], default: ['keywords'] })
lookups?: Lookup[]; lookups: Lookup[];
/** /**
* Description of the entity purpose. * 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). * Either or not this entity a built-in (either fixtures or shipped along with the 3rd party ai).
*/ */
@Prop({ type: Boolean, default: false }) @Prop({ type: Boolean, default: false })
builtin?: boolean; builtin: boolean;
/** /**
* Returns a map object for entities * Returns a map object for entities

View File

@ -33,7 +33,7 @@ export class NlpSampleStub extends BaseSchema {
* Either or not this sample was used for traning already. * Either or not this sample was used for traning already.
*/ */
@Prop({ type: Boolean, default: false }) @Prop({ type: Boolean, default: false })
trained?: boolean; trained: boolean;
/** /**
* From where this sample was provided. * From where this sample was provided.
@ -43,7 +43,7 @@ export class NlpSampleStub extends BaseSchema {
enum: Object.values(NlpSampleState), enum: Object.values(NlpSampleState),
default: NlpSampleState.train, default: NlpSampleState.train,
}) })
type?: keyof typeof NlpSampleState; type: keyof typeof NlpSampleState;
/** /**
* The language of the sample. * The language of the sample.

View File

@ -38,19 +38,19 @@ export class NlpValueStub extends BaseSchema {
* An array of synonyms or equivalent words that fits this value. * An array of synonyms or equivalent words that fits this value.
*/ */
@Prop({ type: [String], default: [] }) @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") . * 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: {} }) @Prop({ type: JSON, default: {} })
metadata?: Record<string, any>; metadata: Record<string, any>;
/** /**
* Either or not this value a built-in (either fixtures or shipped along with the 3rd party ai). * Either or not this value a built-in (either fixtures or shipped along with the 3rd party ai).
*/ */
@Prop({ type: Boolean, default: false }) @Prop({ type: Boolean, default: false })
builtin?: boolean; builtin: boolean;
/** /**
* The entity to which this value belongs to. * The entity to which this value belongs to.

View File

@ -10,6 +10,7 @@ import { Injectable } from '@nestjs/common';
import { BaseSeeder } from '@/utils/generics/base-seeder'; import { BaseSeeder } from '@/utils/generics/base-seeder';
import { NlpEntityDTOMapActions } from '../dto/nlp-entity.dto';
import { NlpEntityRepository } from '../repositories/nlp-entity.repository'; import { NlpEntityRepository } from '../repositories/nlp-entity.repository';
import { import {
NlpEntity, NlpEntity,
@ -21,7 +22,8 @@ import {
export class NlpEntitySeeder extends BaseSeeder< export class NlpEntitySeeder extends BaseSeeder<
NlpEntity, NlpEntity,
NlpEntityPopulate, NlpEntityPopulate,
NlpEntityFull NlpEntityFull,
NlpEntityDTOMapActions
> { > {
constructor(nlpEntityRepository: NlpEntityRepository) { constructor(nlpEntityRepository: NlpEntityRepository) {
super(nlpEntityRepository); super(nlpEntityRepository);

View File

@ -8,9 +8,9 @@
import { Injectable } from '@nestjs/common'; import { Injectable } from '@nestjs/common';
import { BaseSchema } from '@/utils/generics/base-schema';
import { BaseSeeder } from '@/utils/generics/base-seeder'; import { BaseSeeder } from '@/utils/generics/base-seeder';
import { NlpValueCreateDto, NlpValueDTOMapActions } from '../dto/nlp-value.dto';
import { NlpEntityRepository } from '../repositories/nlp-entity.repository'; import { NlpEntityRepository } from '../repositories/nlp-entity.repository';
import { NlpValueRepository } from '../repositories/nlp-value.repository'; import { NlpValueRepository } from '../repositories/nlp-value.repository';
import { import {
@ -23,7 +23,8 @@ import {
export class NlpValueSeeder extends BaseSeeder< export class NlpValueSeeder extends BaseSeeder<
NlpValue, NlpValue,
NlpValuePopulate, NlpValuePopulate,
NlpValueFull NlpValueFull,
NlpValueDTOMapActions
> { > {
constructor( constructor(
nlpValueRepository: NlpValueRepository, nlpValueRepository: NlpValueRepository,
@ -32,7 +33,7 @@ export class NlpValueSeeder extends BaseSeeder<
super(nlpValueRepository); super(nlpValueRepository);
} }
async seed(models: Omit<NlpValue, keyof BaseSchema>[]): Promise<boolean> { async seed(models: NlpValueCreateDto[]): Promise<boolean> {
if (await this.isEmpty()) { if (await this.isEmpty()) {
const entities = await this.nlpEntityRepository.findAll(); const entities = await this.nlpEntityRepository.findAll();
const modelDtos = models.map((v) => ({ const modelDtos = models.map((v) => ({

View File

@ -10,7 +10,7 @@ import { Injectable } from '@nestjs/common';
import { BaseService } from '@/utils/generics/base-service'; 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 { NlpEntityRepository } from '../repositories/nlp-entity.repository';
import { import {
NlpEntity, NlpEntity,
@ -25,7 +25,8 @@ import { NlpValueService } from './nlp-value.service';
export class NlpEntityService extends BaseService< export class NlpEntityService extends BaseService<
NlpEntity, NlpEntity,
NlpEntityPopulate, NlpEntityPopulate,
NlpEntityFull NlpEntityFull,
NlpEntityDTOMapActions
> { > {
constructor( constructor(
readonly repository: NlpEntityRepository, readonly repository: NlpEntityRepository,

View File

@ -21,7 +21,10 @@ import { LoggerService } from '@/logger/logger.service';
import { BaseService } from '@/utils/generics/base-service'; import { BaseService } from '@/utils/generics/base-service';
import { THydratedDocument } from '@/utils/types/filter.types'; 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 { NlpSampleRepository } from '../repositories/nlp-sample.repository';
import { import {
NlpSample, NlpSample,
@ -37,7 +40,8 @@ import { NlpSampleEntityService } from './nlp-sample-entity.service';
export class NlpSampleService extends BaseService< export class NlpSampleService extends BaseService<
NlpSample, NlpSample,
NlpSamplePopulate, NlpSamplePopulate,
NlpSampleFull NlpSampleFull,
NlpSampleDTOMapActions
> { > {
constructor( constructor(
readonly repository: NlpSampleRepository, readonly repository: NlpSampleRepository,

View File

@ -11,7 +11,11 @@ import { forwardRef, Inject, Injectable } from '@nestjs/common';
import { DeleteResult } from '@/utils/generics/base-repository'; import { DeleteResult } from '@/utils/generics/base-repository';
import { BaseService } from '@/utils/generics/base-service'; 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 { NlpValueRepository } from '../repositories/nlp-value.repository';
import { NlpEntity } from '../schemas/nlp-entity.schema'; import { NlpEntity } from '../schemas/nlp-entity.schema';
import { import {
@ -27,7 +31,8 @@ import { NlpEntityService } from './nlp-entity.service';
export class NlpValueService extends BaseService< export class NlpValueService extends BaseService<
NlpValue, NlpValue,
NlpValuePopulate, NlpValuePopulate,
NlpValueFull NlpValueFull,
NlpValueDTOMapActions
> { > {
constructor( constructor(
readonly repository: NlpValueRepository, readonly repository: NlpValueRepository,

View File

@ -10,11 +10,17 @@ import { Injectable } from '@nestjs/common';
import { BaseSeeder } from '@/utils/generics/base-seeder'; import { BaseSeeder } from '@/utils/generics/base-seeder';
import { RoleDTOMapActions } from '../dto/role.dto';
import { RoleRepository } from '../repositories/role.repository'; import { RoleRepository } from '../repositories/role.repository';
import { Role, RoleFull, RolePopulate } from '../schemas/role.schema'; import { Role, RoleFull, RolePopulate } from '../schemas/role.schema';
@Injectable() @Injectable()
export class RoleSeeder extends BaseSeeder<Role, RolePopulate, RoleFull> { export class RoleSeeder extends BaseSeeder<
Role,
RolePopulate,
RoleFull,
RoleDTOMapActions
> {
constructor(private readonly roleRepository: RoleRepository) { constructor(private readonly roleRepository: RoleRepository) {
super(roleRepository); super(roleRepository);
} }

View File

@ -19,7 +19,9 @@ export abstract class BaseSeeder<
TFull extends Omit<T, P> = never, TFull extends Omit<T, P> = never,
DTOCruds extends DtoProps<any> = unknown, DTOCruds extends DtoProps<any> = unknown,
> { > {
constructor(protected readonly repository: BaseRepository<T, P, TFull>) {} constructor(
protected readonly repository: BaseRepository<T, P, TFull, DTOCruds>,
) {}
async findAll(): Promise<T[]> { async findAll(): Promise<T[]> {
return await this.repository.findAll(); return await this.repository.findAll();

View File

@ -8,16 +8,27 @@
import mongoose from 'mongoose'; import mongoose from 'mongoose';
import { NlpSampleCreateDto } from '@/nlp/dto/nlp-sample.dto'; import { NlpSample, NlpSampleModel } from '@/nlp/schemas/nlp-sample.schema';
import { NlpSampleModel, NlpSample } from '@/nlp/schemas/nlp-sample.schema';
import { NlpSampleState } from '@/nlp/schemas/types'; import { NlpSampleState } from '@/nlp/schemas/types';
import { BaseSchema } from '@/utils/generics/base-schema';
import { getFixturesWithDefaultValues } from '../defaultValues'; import { getFixturesWithDefaultValues } from '../defaultValues';
import { TFixturesDefaultValues } from '../types';
import { installLanguageFixtures } from './language'; import { installLanguageFixtures } from './language';
const nlpSamples: NlpSampleCreateDto[] = [ export const fieldsWithDefaultValues = {
type: NlpSampleState.train,
trained: false,
} satisfies Partial<NlpSample>;
type TFieldWithDefaultValues =
| keyof typeof fieldsWithDefaultValues
| keyof BaseSchema;
type TTransformedField<T> = Omit<T, TFieldWithDefaultValues> &
Partial<Pick<NlpSample, TFieldWithDefaultValues>>;
type TNlpSample = TTransformedField<NlpSample>;
const nlpSamples: TNlpSample[] = [
{ {
text: 'yess', text: 'yess',
language: '0', language: '0',
@ -38,14 +49,9 @@ const nlpSamples: NlpSampleCreateDto[] = [
}, },
]; ];
export const nlpSampleDefaultValues: TFixturesDefaultValues<NlpSample> = { export const nlpSampleFixtures = getFixturesWithDefaultValues<TNlpSample>({
type: NlpSampleState.train,
trained: false,
};
export const nlpSampleFixtures = getFixturesWithDefaultValues<NlpSample>({
fixtures: nlpSamples, fixtures: nlpSamples,
defaultValues: nlpSampleDefaultValues, defaultValues: fieldsWithDefaultValues,
}); });
export const installNlpSampleFixtures = async () => { export const installNlpSampleFixtures = async () => {