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',
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: [],
},
});

View File

@ -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;
}>;

View File

@ -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;
}>;

View File

@ -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;
}>;

View File

@ -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,

View File

@ -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,

View File

@ -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,

View File

@ -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

View File

@ -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.

View File

@ -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<string, any>;
metadata: Record<string, any>;
/**
* 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.

View File

@ -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);

View File

@ -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<NlpValue, keyof BaseSchema>[]): Promise<boolean> {
async seed(models: NlpValueCreateDto[]): Promise<boolean> {
if (await this.isEmpty()) {
const entities = await this.nlpEntityRepository.findAll();
const modelDtos = models.map((v) => ({

View File

@ -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,

View File

@ -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,

View File

@ -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,

View File

@ -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<Role, RolePopulate, RoleFull> {
export class RoleSeeder extends BaseSeeder<
Role,
RolePopulate,
RoleFull,
RoleDTOMapActions
> {
constructor(private readonly roleRepository: RoleRepository) {
super(roleRepository);
}

View File

@ -19,7 +19,9 @@ export abstract class BaseSeeder<
TFull extends Omit<T, P> = never,
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[]> {
return await this.repository.findAll();

View File

@ -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<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',
language: '0',
@ -38,14 +49,9 @@ const nlpSamples: NlpSampleCreateDto[] = [
},
];
export const nlpSampleDefaultValues: TFixturesDefaultValues<NlpSample> = {
type: NlpSampleState.train,
trained: false,
};
export const nlpSampleFixtures = getFixturesWithDefaultValues<NlpSample>({
export const nlpSampleFixtures = getFixturesWithDefaultValues<TNlpSample>({
fixtures: nlpSamples,
defaultValues: nlpSampleDefaultValues,
defaultValues: fieldsWithDefaultValues,
});
export const installNlpSampleFixtures = async () => {