fix: apply strict null checks updates to the NLP Module

This commit is contained in:
yassinedorbozgithub 2025-01-09 12:31:25 +01:00
parent 3c1f115a7c
commit 1128fbf379
15 changed files with 237 additions and 157 deletions

View File

@ -27,15 +27,16 @@ import {
closeInMongodConnection,
rootMongooseTestModule,
} from '@/utils/test/test';
import { TFixtures } from '@/utils/test/types';
import { NlpEntityCreateDto } from '../dto/nlp-entity.dto';
import { NlpEntityRepository } from '../repositories/nlp-entity.repository';
import { NlpSampleEntityRepository } from '../repositories/nlp-sample-entity.repository';
import { NlpValueRepository } from '../repositories/nlp-value.repository';
import {
NlpEntityModel,
NlpEntity,
NlpEntityFull,
NlpEntityModel,
} from '../schemas/nlp-entity.schema';
import { NlpSampleEntityModel } from '../schemas/nlp-sample-entity.schema';
import { NlpValueModel } from '../schemas/nlp-value.schema';
@ -48,8 +49,8 @@ describe('NlpEntityController', () => {
let nlpEntityController: NlpEntityController;
let nlpValueService: NlpValueService;
let nlpEntityService: NlpEntityService;
let intentEntityId: string;
let buitInEntityId: string;
let intentEntityId: string | null;
let buitInEntityId: string | null;
beforeAll(async () => {
const module: TestingModule = await Test.createTestingModule({
@ -76,16 +77,18 @@ describe('NlpEntityController', () => {
nlpValueService = module.get<NlpValueService>(NlpValueService);
nlpEntityService = module.get<NlpEntityService>(NlpEntityService);
intentEntityId = (
await nlpEntityService.findOne({
name: 'intent',
})
).id;
buitInEntityId = (
await nlpEntityService.findOne({
name: 'built_in',
})
).id;
intentEntityId =
(
await nlpEntityService.findOne({
name: 'intent',
})
)?.id || null;
buitInEntityId =
(
await nlpEntityService.findOne({
name: 'built_in',
})
)?.id || null;
});
afterAll(async () => {
await closeInMongodConnection();
@ -107,11 +110,11 @@ describe('NlpEntityController', () => {
...curr,
values: nlpValueFixtures.filter(
({ entity }) => parseInt(entity) === index,
),
) as NlpEntityFull['values'],
});
return acc;
},
[],
[] as TFixtures<NlpEntityFull>[],
);
expect(result).toEqualPayload(
entitiesWithValues.sort((a, b) => {
@ -170,19 +173,19 @@ describe('NlpEntityController', () => {
describe('deleteOne', () => {
it('should delete a nlp entity', async () => {
const result = await nlpEntityController.deleteOne(intentEntityId);
const result = await nlpEntityController.deleteOne(intentEntityId!);
expect(result.deletedCount).toEqual(1);
});
it('should throw exception when nlp entity id not found', async () => {
await expect(
nlpEntityController.deleteOne(intentEntityId),
nlpEntityController.deleteOne(intentEntityId!),
).rejects.toThrow(NotFoundException);
});
it('should throw exception when nlp entity is builtin', async () => {
await expect(
nlpEntityController.deleteOne(buitInEntityId),
nlpEntityController.deleteOne(buitInEntityId!),
).rejects.toThrow(MethodNotAllowedException);
});
});
@ -192,10 +195,10 @@ describe('NlpEntityController', () => {
const firstNameEntity = await nlpEntityService.findOne({
name: 'first_name',
});
const result = await nlpEntityController.findOne(firstNameEntity.id, []);
const result = await nlpEntityController.findOne(firstNameEntity!.id, []);
expect(result).toEqualPayload(
nlpEntityFixtures.find(({ name }) => name === 'first_name'),
nlpEntityFixtures.find(({ name }) => name === 'first_name')!,
);
});
@ -206,9 +209,13 @@ describe('NlpEntityController', () => {
const firstNameValues = await nlpValueService.findOne({ value: 'jhon' });
const firstNameWithValues: NlpEntityFull = {
...firstNameEntity,
values: [firstNameValues],
values: firstNameValues ? [firstNameValues] : [],
name: firstNameEntity!.name,
id: firstNameEntity!.id,
createdAt: firstNameEntity!.createdAt,
updatedAt: firstNameEntity!.updatedAt,
};
const result = await nlpEntityController.findOne(firstNameEntity.id, [
const result = await nlpEntityController.findOne(firstNameEntity!.id, [
'values',
]);
expect(result).toEqualPayload(firstNameWithValues);
@ -216,7 +223,7 @@ describe('NlpEntityController', () => {
it('should throw NotFoundException when Id does not exist', async () => {
await expect(
nlpEntityController.findOne(intentEntityId, ['values']),
nlpEntityController.findOne(intentEntityId!, ['values']),
).rejects.toThrow(NotFoundException);
});
});
@ -233,7 +240,7 @@ describe('NlpEntityController', () => {
builtin: false,
};
const result = await nlpEntityController.updateOne(
firstNameEntity.id,
firstNameEntity!.id,
updatedNlpEntity,
);
expect(result).toEqualPayload(updatedNlpEntity);
@ -247,7 +254,7 @@ describe('NlpEntityController', () => {
builtin: false,
};
await expect(
nlpEntityController.updateOne(intentEntityId, updateNlpEntity),
nlpEntityController.updateOne(intentEntityId!, updateNlpEntity),
).rejects.toThrow(NotFoundException);
});
@ -259,7 +266,7 @@ describe('NlpEntityController', () => {
builtin: false,
};
await expect(
nlpEntityController.updateOne(buitInEntityId, updateNlpEntity),
nlpEntityController.updateOne(buitInEntityId!, updateNlpEntity),
).rejects.toThrow(MethodNotAllowedException);
});
});
@ -276,7 +283,7 @@ describe('NlpEntityController', () => {
name: 'updated',
})
)?.id,
];
] as string[];
const result = await nlpEntityController.deleteMany(entitiesToDelete);

View File

@ -30,6 +30,7 @@ import {
closeInMongodConnection,
rootMongooseTestModule,
} from '@/utils/test/test';
import { TFixtures } from '@/utils/test/types';
import { NlpSampleDto } from '../dto/nlp-sample.dto';
import { NlpEntityRepository } from '../repositories/nlp-entity.repository';
@ -38,7 +39,11 @@ import { NlpSampleRepository } from '../repositories/nlp-sample.repository';
import { NlpValueRepository } from '../repositories/nlp-value.repository';
import { NlpEntityModel } from '../schemas/nlp-entity.schema';
import { NlpSampleEntityModel } from '../schemas/nlp-sample-entity.schema';
import { NlpSample, NlpSampleModel } from '../schemas/nlp-sample.schema';
import {
NlpSample,
NlpSampleFull,
NlpSampleModel,
} from '../schemas/nlp-sample.schema';
import { NlpValueModel } from '../schemas/nlp-value.schema';
import { NlpSampleState } from '../schemas/types';
import { NlpEntityService } from '../services/nlp-entity.service';
@ -55,7 +60,7 @@ describe('NlpSampleController', () => {
let nlpEntityService: NlpEntityService;
let nlpValueService: NlpValueService;
let languageService: LanguageService;
let byeJhonSampleId: string;
let byeJhonSampleId: string | null;
let languages: Language[];
beforeAll(async () => {
@ -115,11 +120,12 @@ describe('NlpSampleController', () => {
nlpSampleService = module.get<NlpSampleService>(NlpSampleService);
nlpEntityService = module.get<NlpEntityService>(NlpEntityService);
nlpValueService = module.get<NlpValueService>(NlpValueService);
byeJhonSampleId = (
await nlpSampleService.findOne({
text: 'Bye Jhon',
})
).id;
byeJhonSampleId =
(
await nlpSampleService.findOne({
text: 'Bye Jhon',
})
)?.id || null;
languageService = module.get<LanguageService>(LanguageService);
languages = await languageService.findAll();
});
@ -143,15 +149,16 @@ describe('NlpSampleController', () => {
(acc, currSample) => {
const sampleWithEntities = {
...currSample,
entities: nlpSampleEntities.filter((currSampleEntity) => {
return currSampleEntity.sample === currSample.id;
}),
language: languages.find((lang) => lang.id === currSample.language),
entities: nlpSampleEntities.filter(
(currSampleEntity) => currSampleEntity.sample === currSample.id,
),
language:
languages.find((lang) => lang.id === currSample.language) || null,
};
acc.push(sampleWithEntities);
return acc;
},
[],
[] as TFixtures<NlpSampleFull>[],
);
expect(result).toEqualPayload(nlpSampleFixturesWithEntities);
@ -167,7 +174,7 @@ describe('NlpSampleController', () => {
expect(result).toEqualPayload(
nlpSampleFixtures.map((sample) => ({
...sample,
language: languages[sample.language].id,
language: sample.language ? languages[sample.language].id : null,
})),
);
});
@ -201,13 +208,13 @@ describe('NlpSampleController', () => {
describe('deleteOne', () => {
it('should delete a nlp sample', async () => {
const result = await nlpSampleController.deleteOne(byeJhonSampleId);
const result = await nlpSampleController.deleteOne(byeJhonSampleId!);
expect(result.deletedCount).toEqual(1);
});
it('should throw exception when nlp sample id not found', async () => {
await expect(
nlpSampleController.deleteOne(byeJhonSampleId),
nlpSampleController.deleteOne(byeJhonSampleId!),
).rejects.toThrow(NotFoundException);
});
});
@ -217,12 +224,14 @@ describe('NlpSampleController', () => {
const yessSample = await nlpSampleService.findOne({
text: 'yess',
});
const result = await nlpSampleController.findOne(yessSample.id, [
const result = await nlpSampleController.findOne(yessSample!.id, [
'invalidCreteria',
]);
expect(result).toEqualPayload({
...nlpSampleFixtures[0],
language: languages[nlpSampleFixtures[0].language].id,
language: nlpSampleFixtures[0].language
? languages[nlpSampleFixtures?.[0]?.language]?.id
: null,
});
});
@ -231,22 +240,24 @@ describe('NlpSampleController', () => {
text: 'yess',
});
const yessSampleEntity = await nlpSampleEntityService.findOne({
sample: yessSample.id,
sample: yessSample!.id,
});
const result = await nlpSampleController.findOne(yessSample.id, [
const result = await nlpSampleController.findOne(yessSample!.id, [
'entities',
]);
const samplesWithEntities = {
...nlpSampleFixtures[0],
entities: [yessSampleEntity],
language: languages[nlpSampleFixtures[0].language],
language: nlpSampleFixtures[0].language
? languages[nlpSampleFixtures[0].language]
: null,
};
expect(result).toEqualPayload(samplesWithEntities);
});
it('should throw NotFoundException when Id does not exist', async () => {
await expect(
nlpSampleController.findOne(byeJhonSampleId, ['entities']),
nlpSampleController.findOne(byeJhonSampleId!, ['entities']),
).rejects.toThrow(NotFoundException);
});
});
@ -259,7 +270,7 @@ describe('NlpSampleController', () => {
const frLang = await languageService.findOne({
code: 'fr',
});
const result = await nlpSampleController.updateOne(yessSample.id, {
const result = await nlpSampleController.updateOne(yessSample!.id, {
text: 'updated',
trained: true,
type: NlpSampleState.test,
@ -288,12 +299,12 @@ describe('NlpSampleController', () => {
expect(result.type).toEqual(updatedSample.type);
expect(result.trained).toEqual(updatedSample.trained);
expect(result.entities).toMatchObject(updatedSample.entities);
expect(result.language).toEqualPayload(updatedSample.language);
expect(result.language).toEqualPayload(updatedSample.language!);
});
it('should throw exception when nlp sample id not found', async () => {
await expect(
nlpSampleController.updateOne(byeJhonSampleId, {
nlpSampleController.updateOne(byeJhonSampleId!, {
text: 'updated',
trained: true,
type: NlpSampleState.test,
@ -366,13 +377,13 @@ describe('NlpSampleController', () => {
value: 'price',
expressions: [],
builtin: false,
entity: priceValueEntity.id,
entity: priceValueEntity!.id,
};
const textSample = {
text: 'How much does a BMW cost?',
trained: false,
type: 'train',
language: language.id,
language: language!.id,
};
expect(intentEntityResult).toEqualPayload(intentEntity);
@ -389,13 +400,13 @@ describe('NlpSampleController', () => {
await nlpSampleService.findOne({
text: 'How much does a BMW cost?',
})
).id,
)?.id,
(
await nlpSampleService.findOne({
text: 'text1',
})
).id,
];
)?.id,
] as string[];
const result = await nlpSampleController.deleteMany(samplesToDelete);

View File

@ -91,7 +91,7 @@ export class NlpSampleController extends BaseController<
);
const entities = await this.nlpEntityService.findAllAndPopulate();
const helper = await this.helperService.getDefaultNluHelper();
const result = await helper.format(samples, entities);
const result = await helper.format?.(samples, entities);
// Sending the JSON data as a file
const buffer = Buffer.from(JSON.stringify(result));
@ -128,15 +128,23 @@ export class NlpSampleController extends BaseController<
}: NlpSampleDto,
): Promise<NlpSampleFull> {
const language = await this.languageService.getLanguageByCode(languageCode);
if (!language)
throw new NotFoundException(
`Language with code ${languageCode} not found`,
);
const nlpSample = await this.nlpSampleService.create({
...createNlpSampleDto,
language: language.id,
});
const entities = await this.nlpSampleEntityService.storeSampleEntities(
nlpSample,
nlpEntities,
);
const entities = nlpEntities
? await this.nlpSampleEntityService.storeSampleEntities(
nlpSample,
nlpEntities,
)
: [];
return {
...nlpSample,
@ -202,7 +210,7 @@ export class NlpSampleController extends BaseController<
try {
const helper = await this.helperService.getDefaultNluHelper();
const response = await helper.train(samples, entities);
const response = await helper.train?.(samples, entities);
// Mark samples as trained
await this.nlpSampleService.updateMany(
{ type: 'train' },
@ -228,7 +236,7 @@ export class NlpSampleController extends BaseController<
await this.getSamplesAndEntitiesByType('test');
const helper = await this.helperService.getDefaultNluHelper();
return await helper.evaluate(samples, entities);
return await helper.evaluate?.(samples, entities);
}
/**
@ -294,6 +302,14 @@ export class NlpSampleController extends BaseController<
@Body() { entities, language: languageCode, ...sampleAttrs }: NlpSampleDto,
): Promise<NlpSampleFull> {
const language = await this.languageService.getLanguageByCode(languageCode);
if (!language) {
this.logger.warn(`Unable to Language by languageCode ${languageCode}`);
throw new NotFoundException(
`Language with languageCode ${languageCode} not found`,
);
}
const sample = await this.nlpSampleService.updateOne(id, {
...sampleAttrs,
language: language.id,
@ -308,7 +324,10 @@ export class NlpSampleController extends BaseController<
await this.nlpSampleEntityService.deleteMany({ sample: id });
const updatedSampleEntities =
await this.nlpSampleEntityService.storeSampleEntities(sample, entities);
await this.nlpSampleEntityService.storeSampleEntities(
sample,
entities || [],
);
return {
...sample,

View File

@ -22,6 +22,7 @@ import {
closeInMongodConnection,
rootMongooseTestModule,
} from '@/utils/test/test';
import { TFixtures } from '@/utils/test/types';
import { NlpValueCreateDto } from '../dto/nlp-value.dto';
import { NlpEntityRepository } from '../repositories/nlp-entity.repository';
@ -29,7 +30,11 @@ import { NlpSampleEntityRepository } from '../repositories/nlp-sample-entity.rep
import { NlpValueRepository } from '../repositories/nlp-value.repository';
import { NlpEntityModel } from '../schemas/nlp-entity.schema';
import { NlpSampleEntityModel } from '../schemas/nlp-sample-entity.schema';
import { NlpValueModel, NlpValue } from '../schemas/nlp-value.schema';
import {
NlpValue,
NlpValueFull,
NlpValueModel,
} from '../schemas/nlp-value.schema';
import { NlpEntityService } from '../services/nlp-entity.service';
import { NlpValueService } from '../services/nlp-value.service';
@ -39,9 +44,9 @@ describe('NlpValueController', () => {
let nlpValueController: NlpValueController;
let nlpValueService: NlpValueService;
let nlpEntityService: NlpEntityService;
let jhonNlpValue: NlpValue;
let positiveValue: NlpValue;
let negativeValue: NlpValue;
let jhonNlpValue: NlpValue | null;
let positiveValue: NlpValue | null;
let negativeValue: NlpValue | null;
beforeAll(async () => {
const module: TestingModule = await Test.createTestingModule({
@ -92,11 +97,13 @@ describe('NlpValueController', () => {
(acc, curr) => {
acc.push({
...curr,
entity: nlpEntityFixtures[parseInt(curr.entity)],
entity: nlpEntityFixtures[
parseInt(curr.entity)
] as NlpValueFull['entity'],
});
return acc;
},
[],
[] as TFixtures<NlpValueFull>[],
);
expect(result).toEqualPayload(nlpValueFixturesWithEntities);
});
@ -120,7 +127,7 @@ describe('NlpValueController', () => {
acc.push(ValueWithEntities);
return acc;
},
[],
[] as TFixtures<NlpValue>[],
);
expect(result).toEqualPayload(nlpValueFixturesWithEntities);
});
@ -151,20 +158,20 @@ describe('NlpValueController', () => {
describe('deleteOne', () => {
it('should delete a nlp Value', async () => {
const result = await nlpValueController.deleteOne(jhonNlpValue.id);
const result = await nlpValueController.deleteOne(jhonNlpValue!.id);
expect(result.deletedCount).toEqual(1);
});
it('should throw exception when nlp Value id not found', async () => {
await expect(
nlpValueController.deleteOne(jhonNlpValue.id),
nlpValueController.deleteOne(jhonNlpValue!.id),
).rejects.toThrow(NotFoundException);
});
});
describe('findOne', () => {
it('should get a nlp Value', async () => {
const result = await nlpValueController.findOne(positiveValue.id, [
const result = await nlpValueController.findOne(positiveValue!.id, [
'invalidCreteria',
]);
const intentNlpEntity = await nlpEntityService.findOne({
@ -172,7 +179,7 @@ describe('NlpValueController', () => {
});
const valueWithEntity = {
...nlpValueFixtures[0],
entity: intentNlpEntity.id,
entity: intentNlpEntity!.id,
};
expect(result).toEqualPayload(valueWithEntity);
@ -182,7 +189,7 @@ describe('NlpValueController', () => {
const intentNlpEntity = await nlpEntityService.findOne({
name: 'intent',
});
const result = await nlpValueController.findOne(positiveValue.id, [
const result = await nlpValueController.findOne(positiveValue!.id, [
'entity',
]);
const valueWithEntity = {
@ -194,7 +201,7 @@ describe('NlpValueController', () => {
it('should throw NotFoundException when Id does not exist', async () => {
await expect(
nlpValueController.findOne(jhonNlpValue.id, ['entity']),
nlpValueController.findOne(jhonNlpValue!.id, ['entity']),
).rejects.toThrow(NotFoundException);
});
});
@ -205,13 +212,13 @@ describe('NlpValueController', () => {
name: 'intent',
});
const updatedValue = {
entity: intentNlpEntity.id,
entity: intentNlpEntity!.id,
value: 'updated',
expressions: [],
builtin: true,
};
const result = await nlpValueController.updateOne(
positiveValue.id,
positiveValue!.id,
updatedValue,
);
expect(result).toEqualPayload(updatedValue);
@ -222,8 +229,8 @@ describe('NlpValueController', () => {
name: 'intent',
});
await expect(
nlpValueController.updateOne(jhonNlpValue.id, {
entity: intentNlpEntity.id,
nlpValueController.updateOne(jhonNlpValue!.id, {
entity: intentNlpEntity!.id,
value: 'updated',
expressions: [],
builtin: true,
@ -233,7 +240,7 @@ describe('NlpValueController', () => {
});
describe('deleteMany', () => {
it('should delete multiple nlp values', async () => {
const valuesToDelete = [positiveValue.id, negativeValue.id];
const valuesToDelete = [positiveValue!.id, negativeValue!.id];
const result = await nlpValueController.deleteMany(valuesToDelete);

View File

@ -18,7 +18,7 @@ import {
rootMongooseTestModule,
} from '@/utils/test/test';
import { NlpEntityModel, NlpEntity } from '../schemas/nlp-entity.schema';
import { NlpEntity, NlpEntityModel } from '../schemas/nlp-entity.schema';
import { NlpSampleEntityModel } from '../schemas/nlp-sample-entity.schema';
import { NlpValueModel } from '../schemas/nlp-value.schema';
@ -29,7 +29,7 @@ import { NlpValueRepository } from './nlp-value.repository';
describe('NlpEntityRepository', () => {
let nlpEntityRepository: NlpEntityRepository;
let nlpValueRepository: NlpValueRepository;
let firstNameNlpEntity: NlpEntity;
let firstNameNlpEntity: NlpEntity | null;
beforeAll(async () => {
const module: TestingModule = await Test.createTestingModule({
@ -66,12 +66,12 @@ describe('NlpEntityRepository', () => {
const intentNlpEntity = await nlpEntityRepository.findOne({
name: 'intent',
});
const result = await nlpEntityRepository.deleteOne(intentNlpEntity.id);
const result = await nlpEntityRepository.deleteOne(intentNlpEntity!.id);
expect(result.deletedCount).toEqual(1);
const intentNlpValues = await nlpValueRepository.find({
entity: intentNlpEntity.id,
entity: intentNlpEntity!.id,
});
expect(intentNlpValues.length).toEqual(0);
@ -81,10 +81,10 @@ describe('NlpEntityRepository', () => {
describe('findOneAndPopulate', () => {
it('should return a nlp entity with populate', async () => {
const firstNameValues = await nlpValueRepository.find({
entity: firstNameNlpEntity.id,
entity: firstNameNlpEntity!.id,
});
const result = await nlpEntityRepository.findOneAndPopulate(
firstNameNlpEntity.id,
firstNameNlpEntity!.id,
);
expect(result).toEqualPayload({
...nlpEntityFixtures[1],
@ -99,15 +99,15 @@ describe('NlpEntityRepository', () => {
sort: ['name', 'desc'],
});
const firstNameValues = await nlpValueRepository.find({
entity: firstNameNlpEntity.id,
entity: firstNameNlpEntity!.id,
});
const result = await nlpEntityRepository.findPageAndPopulate(
{ _id: firstNameNlpEntity.id },
{ _id: firstNameNlpEntity!.id },
pageQuery,
);
expect(result).toEqualPayload([
{
id: firstNameNlpEntity.id,
id: firstNameNlpEntity!.id,
...nlpEntityFixtures[1],
values: firstNameValues,
},

View File

@ -23,14 +23,16 @@ import {
closeInMongodConnection,
rootMongooseTestModule,
} from '@/utils/test/test';
import { TFixtures } from '@/utils/test/types';
import { NlpEntityModel, NlpEntity } from '../schemas/nlp-entity.schema';
import { NlpEntity, NlpEntityModel } from '../schemas/nlp-entity.schema';
import {
NlpSampleEntityModel,
NlpSampleEntity,
NlpSampleEntityFull,
NlpSampleEntityModel,
} from '../schemas/nlp-sample-entity.schema';
import { NlpSampleModel } from '../schemas/nlp-sample.schema';
import { NlpValueModel } from '../schemas/nlp-value.schema';
import { NlpValueModel, NlpValueStub } from '../schemas/nlp-value.schema';
import { NlpEntityRepository } from './nlp-entity.repository';
import { NlpSampleEntityRepository } from './nlp-sample-entity.repository';
@ -91,7 +93,7 @@ describe('NlpSampleEntityRepository', () => {
value: { ...nlpValueFixtures[0], entity: nlpEntities[0].id },
sample: {
...nlpSampleFixtures[0],
language: languages[nlpSampleFixtures[0].language].id,
language: languages[nlpSampleFixtures[0].language!].id,
},
});
});
@ -115,7 +117,7 @@ describe('NlpSampleEntityRepository', () => {
acc.push(ValueWithEntities);
return acc;
},
[],
[] as TFixtures<NlpValueStub>[],
);
nlpValueFixturesWithEntities[2] = {
...nlpValueFixturesWithEntities[2],
@ -135,7 +137,7 @@ describe('NlpSampleEntityRepository', () => {
};
acc.push(sampleEntityWithPopulate);
return acc;
}, []);
}, [] as TFixtures<NlpSampleEntityFull>[]);
expect(result).toEqualPayload(nlpSampleEntityFixturesWithPopulate);
});
});

View File

@ -19,12 +19,17 @@ import {
closeInMongodConnection,
rootMongooseTestModule,
} from '@/utils/test/test';
import { TFixtures } from '@/utils/test/types';
import {
NlpSampleEntityModel,
NlpSampleEntity,
NlpSampleEntityModel,
} from '../schemas/nlp-sample-entity.schema';
import { NlpSampleModel, NlpSample } from '../schemas/nlp-sample.schema';
import {
NlpSample,
NlpSampleFull,
NlpSampleModel,
} from '../schemas/nlp-sample.schema';
import { NlpSampleEntityRepository } from './nlp-sample-entity.repository';
import { NlpSampleRepository } from './nlp-sample.repository';
@ -33,8 +38,8 @@ describe('NlpSampleRepository', () => {
let nlpSampleRepository: NlpSampleRepository;
let nlpSampleEntityRepository: NlpSampleEntityRepository;
let languageRepository: LanguageRepository;
let nlpSampleEntity: NlpSampleEntity;
let noNlpSample: NlpSample;
let nlpSampleEntity: NlpSampleEntity | null;
let noNlpSample: NlpSample | null;
let languages: Language[];
beforeAll(async () => {
@ -61,7 +66,7 @@ describe('NlpSampleRepository', () => {
languageRepository = module.get<LanguageRepository>(LanguageRepository);
noNlpSample = await nlpSampleRepository.findOne({ text: 'No' });
nlpSampleEntity = await nlpSampleEntityRepository.findOne({
sample: noNlpSample.id,
sample: noNlpSample!.id,
});
languages = await languageRepository.findAll();
});
@ -75,12 +80,12 @@ describe('NlpSampleRepository', () => {
describe('findOneAndPopulate', () => {
it('should return a nlp Sample with populate', async () => {
const result = await nlpSampleRepository.findOneAndPopulate(
noNlpSample.id,
noNlpSample!.id,
);
expect(result).toEqualPayload({
...nlpSampleFixtures[1],
entities: [nlpSampleEntity],
language: languages[nlpSampleFixtures[1].language],
language: languages[nlpSampleFixtures[1].language!],
});
});
});
@ -104,12 +109,13 @@ describe('NlpSampleRepository', () => {
entities: nlpSampleEntities.filter((currSampleEntity) => {
return currSampleEntity.sample === currSample.id;
}),
language: languages.find((lang) => currSample.language === lang.id),
language:
languages.find((lang) => currSample.language === lang.id) || null,
};
acc.push(sampleWithEntities);
return acc;
},
[],
[] as TFixtures<NlpSampleFull>[],
);
expect(result).toEqualPayload(nlpSampleFixturesWithEntities);
});
@ -130,10 +136,10 @@ describe('NlpSampleRepository', () => {
describe('The deleteCascadeOne function', () => {
it('should delete a nlp Sample', async () => {
const result = await nlpSampleRepository.deleteOne(noNlpSample.id);
const result = await nlpSampleRepository.deleteOne(noNlpSample!.id);
expect(result.deletedCount).toEqual(1);
const sampleEntities = await nlpSampleEntityRepository.find({
sample: noNlpSample.id,
sample: noNlpSample!.id,
});
expect(sampleEntities.length).toEqual(0);
});

View File

@ -18,10 +18,15 @@ import {
closeInMongodConnection,
rootMongooseTestModule,
} from '@/utils/test/test';
import { TFixtures } from '@/utils/test/types';
import { NlpEntityModel } from '../schemas/nlp-entity.schema';
import { NlpSampleEntityModel } from '../schemas/nlp-sample-entity.schema';
import { NlpValue, NlpValueModel } from '../schemas/nlp-value.schema';
import {
NlpValue,
NlpValueFull,
NlpValueModel,
} from '../schemas/nlp-value.schema';
import { NlpSampleEntityRepository } from './nlp-sample-entity.repository';
import { NlpValueRepository } from './nlp-value.repository';
@ -81,12 +86,14 @@ describe('NlpValueRepository', () => {
(acc, curr) => {
const ValueWithEntities = {
...curr,
entity: nlpEntityFixtures[parseInt(curr.entity)],
entity: nlpEntityFixtures[
parseInt(curr.entity)
] as NlpValueFull['entity'],
};
acc.push(ValueWithEntities);
return acc;
},
[],
[] as TFixtures<NlpValueFull>[],
);
expect(result).toEqualPayload(nlpValueFixturesWithEntities);
});

View File

@ -21,7 +21,7 @@ import {
import { NlpEntityRepository } from '../repositories/nlp-entity.repository';
import { NlpSampleEntityRepository } from '../repositories/nlp-sample-entity.repository';
import { NlpValueRepository } from '../repositories/nlp-value.repository';
import { NlpEntityModel, NlpEntity } from '../schemas/nlp-entity.schema';
import { NlpEntity, NlpEntityModel } from '../schemas/nlp-entity.schema';
import { NlpSampleEntityModel } from '../schemas/nlp-sample-entity.schema';
import { NlpValueModel } from '../schemas/nlp-value.schema';
@ -69,7 +69,7 @@ describe('nlpEntityService', () => {
name: 'intent',
});
const result = await nlpEntityService.deleteCascadeOne(
intentNlpEntity.id,
intentNlpEntity!.id,
);
expect(result.deletedCount).toEqual(1);
});
@ -81,13 +81,13 @@ describe('nlpEntityService', () => {
name: 'first_name',
});
const result = await nlpEntityService.findOneAndPopulate(
firstNameNlpEntity.id,
firstNameNlpEntity!.id,
);
const firstNameValues = await nlpValueRepository.findOne({
entity: firstNameNlpEntity.id,
entity: firstNameNlpEntity!.id,
});
const entityWithValues = {
id: firstNameNlpEntity.id,
id: firstNameNlpEntity!.id,
...nlpEntityFixtures[1],
values: [firstNameValues],
};
@ -102,15 +102,15 @@ describe('nlpEntityService', () => {
name: 'first_name',
});
const result = await nlpEntityService.findPageAndPopulate(
{ _id: firstNameNlpEntity.id },
{ _id: firstNameNlpEntity!.id },
pageQuery,
);
const firstNameValues = await nlpValueRepository.findOne({
entity: firstNameNlpEntity.id,
entity: firstNameNlpEntity!.id,
});
const entitiesWithValues = [
{
id: firstNameNlpEntity.id,
id: firstNameNlpEntity!.id,
...nlpEntityFixtures[1],
values: [firstNameValues],
},
@ -139,12 +139,12 @@ describe('nlpEntityService', () => {
const deValue = await nlpValueRepository.findOne({ value: 'de' });
const storedEntites = [
{
entity: intentEntity.id,
value: nameValue.id,
entity: intentEntity!.id,
value: nameValue!.id,
},
{
entity: languageEntity.id,
value: deValue.id,
entity: languageEntity!.id,
value: deValue!.id,
},
];

View File

@ -23,17 +23,23 @@ import {
closeInMongodConnection,
rootMongooseTestModule,
} from '@/utils/test/test';
import { TFixtures } from '@/utils/test/types';
import { NlpEntityRepository } from '../repositories/nlp-entity.repository';
import { NlpSampleEntityRepository } from '../repositories/nlp-sample-entity.repository';
import { NlpValueRepository } from '../repositories/nlp-value.repository';
import { NlpEntityModel, NlpEntity } from '../schemas/nlp-entity.schema';
import { NlpEntity, NlpEntityModel } from '../schemas/nlp-entity.schema';
import {
NlpSampleEntityModel,
NlpSampleEntity,
NlpSampleEntityFull,
NlpSampleEntityModel,
} from '../schemas/nlp-sample-entity.schema';
import { NlpSample, NlpSampleModel } from '../schemas/nlp-sample.schema';
import { NlpValue, NlpValueModel } from '../schemas/nlp-value.schema';
import {
NlpValue,
NlpValueModel,
NlpValueStub,
} from '../schemas/nlp-value.schema';
import { NlpEntityService } from './nlp-entity.service';
import { NlpSampleEntityService } from './nlp-sample-entity.service';
@ -91,9 +97,7 @@ describe('NlpSampleEntityService', () => {
languages = await languageRepository.findAll();
});
afterAll(async () => {
await closeInMongodConnection();
});
afterAll(closeInMongodConnection);
afterEach(jest.clearAllMocks);
@ -108,7 +112,7 @@ describe('NlpSampleEntityService', () => {
value: { ...nlpValueFixtures[0], entity: nlpEntities[0].id },
sample: {
...nlpSampleFixtures[0],
language: languages[nlpSampleFixtures[0].language].id,
language: languages[nlpSampleFixtures[0].language!].id,
},
};
expect(result).toEqualPayload(sampleEntityWithPopulate);
@ -133,7 +137,7 @@ describe('NlpSampleEntityService', () => {
acc.push(ValueWithEntities);
return acc;
},
[],
[] as TFixtures<NlpValueStub>[],
);
nlpValueFixturesWithEntities[2] = {
...nlpValueFixturesWithEntities[2],
@ -153,7 +157,7 @@ describe('NlpSampleEntityService', () => {
};
acc.push(sampleEntityWithPopulate);
return acc;
}, []);
}, [] as TFixtures<NlpSampleEntityFull>[]);
expect(result).toEqualPayload(nlpSampleEntityFixturesWithPopulate);
});
});

View File

@ -33,7 +33,11 @@ import {
NlpSampleEntity,
NlpSampleEntityModel,
} from '../schemas/nlp-sample-entity.schema';
import { NlpSample, NlpSampleModel } from '../schemas/nlp-sample.schema';
import {
NlpSample,
NlpSampleFull,
NlpSampleModel,
} from '../schemas/nlp-sample.schema';
import { NlpValueModel } from '../schemas/nlp-value.schema';
import { NlpEntityService } from './nlp-entity.service';
@ -49,8 +53,8 @@ describe('NlpSampleService', () => {
let nlpSampleEntityRepository: NlpSampleEntityRepository;
let nlpSampleRepository: NlpSampleRepository;
let languageRepository: LanguageRepository;
let noNlpSample: NlpSample;
let nlpSampleEntity: NlpSampleEntity;
let noNlpSample: NlpSample | null;
let nlpSampleEntity: NlpSampleEntity | null;
let languages: Language[];
beforeAll(async () => {
@ -104,7 +108,7 @@ describe('NlpSampleService', () => {
languageRepository = module.get<LanguageRepository>(LanguageRepository);
noNlpSample = await nlpSampleService.findOne({ text: 'No' });
nlpSampleEntity = await nlpSampleEntityRepository.findOne({
sample: noNlpSample.id,
sample: noNlpSample!.id,
});
languages = await languageRepository.findAll();
});
@ -117,11 +121,11 @@ describe('NlpSampleService', () => {
describe('findOneAndPopulate', () => {
it('should return a nlp Sample with populate', async () => {
const result = await nlpSampleService.findOneAndPopulate(noNlpSample.id);
const result = await nlpSampleService.findOneAndPopulate(noNlpSample!.id);
const sampleWithEntities = {
...nlpSampleFixtures[1],
entities: [nlpSampleEntity],
language: languages[nlpSampleFixtures[1].language],
language: languages[nlpSampleFixtures[1].language!],
};
expect(result).toEqualPayload(sampleWithEntities);
});
@ -141,12 +145,13 @@ describe('NlpSampleService', () => {
entities: nlpSampleEntities.filter((currSampleEntity) => {
return currSampleEntity.sample === currSample.id;
}),
language: languages.find((lang) => lang.id === currSample.language),
language:
languages.find((lang) => lang.id === currSample.language) || null,
};
acc.push(sampleWithEntities);
return acc;
},
[],
[] as NlpSampleFull[],
);
expect(result).toEqualPayload(nlpSampleFixturesWithEntities);
});
@ -167,7 +172,7 @@ describe('NlpSampleService', () => {
describe('The deleteCascadeOne function', () => {
it('should delete a nlp Sample', async () => {
const result = await nlpSampleService.deleteOne(noNlpSample.id);
const result = await nlpSampleService.deleteOne(noNlpSample!.id);
expect(result.deletedCount).toEqual(1);
});
});

View File

@ -10,6 +10,7 @@ import { EventEmitter2 } from '@nestjs/event-emitter';
import { MongooseModule } from '@nestjs/mongoose';
import { Test, TestingModule } from '@nestjs/testing';
import { BaseSchema } from '@/utils/generics/base-schema';
import { nlpEntityFixtures } from '@/utils/test/fixtures/nlpentity';
import {
installNlpValueFixtures,
@ -26,7 +27,11 @@ import { NlpSampleEntityRepository } from '../repositories/nlp-sample-entity.rep
import { NlpValueRepository } from '../repositories/nlp-value.repository';
import { NlpEntity, NlpEntityModel } from '../schemas/nlp-entity.schema';
import { NlpSampleEntityModel } from '../schemas/nlp-sample-entity.schema';
import { NlpValue, NlpValueModel } from '../schemas/nlp-value.schema';
import {
NlpValue,
NlpValueFull,
NlpValueModel,
} from '../schemas/nlp-value.schema';
import { NlpEntityService } from './nlp-entity.service';
import { NlpValueService } from './nlp-value.service';
@ -89,12 +94,14 @@ describe('NlpValueService', () => {
(acc, curr) => {
const ValueWithEntities = {
...curr,
entity: nlpEntityFixtures[parseInt(curr.entity)],
entity: nlpEntityFixtures[
parseInt(curr.entity)
] as NlpValueFull['entity'],
};
acc.push(ValueWithEntities);
return acc;
},
[],
[] as Omit<NlpValueFull, keyof BaseSchema>[],
);
expect(result).toEqualPayload(nlpValueFixturesWithEntities);
});
@ -130,12 +137,12 @@ describe('NlpValueService', () => {
const jhonValue = await nlpValueRepository.findOne({ value: 'jhon' });
const storedValues = [
{
entity: intentEntity.id,
value: greetingValue.id,
entity: intentEntity!.id,
value: greetingValue!.id,
},
{
entity: firstNameEntity.id,
value: jhonValue.id,
entity: firstNameEntity!.id,
value: jhonValue!.id,
},
];

View File

@ -123,7 +123,7 @@ export class NlpValueService extends BaseService<
if ('start' in e && 'end' in e) {
const word = sampleText.slice(e.start, e.end);
return (
word !== e.value && vMap[e.value].expressions.indexOf(word) === -1
word !== e.value && vMap[e.value].expressions?.indexOf(word) === -1
);
}
return false;
@ -131,7 +131,7 @@ export class NlpValueService extends BaseService<
.map((e) => {
return this.updateOne(vMap[e.value].id, {
...vMap[e.value],
expressions: vMap[e.value].expressions.concat([
expressions: vMap[e.value].expressions?.concat([
sampleText.slice(e.start, e.end),
]),
} as NlpValueUpdateDto);
@ -202,9 +202,11 @@ export class NlpValueService extends BaseService<
const promises = valuesToAdd.map(async (v) => {
const createdOrFound = await this.findOneOrCreate({ value: v.value }, v);
// If value is found in database, then update it's synonyms
const expressions = createdOrFound.expressions
.concat(v.expressions) // Add new synonyms
.filter((v, i, a) => a.indexOf(v) === i); // Filter unique values
const expressions = v.expressions
? createdOrFound.expressions
?.concat(v.expressions) // Add new synonyms
.filter((v, i, a) => a.indexOf(v) === i)
: createdOrFound.expressions?.filter((v, i, a) => a.indexOf(v) === i); // Filter unique values
// Update expressions
const result = await this.updateOne({ value: v.value }, { expressions });

View File

@ -31,7 +31,7 @@ export class SearchFilterPipe<T>
private readonly props: {
allowedFields: TFilterNestedKeysOfType<
T,
undefined | string | string[]
null | undefined | string | string[]
>[];
},
) {}
@ -48,7 +48,10 @@ export class SearchFilterPipe<T>
private isAllowedField(field: string) {
if (
this.props.allowedFields.includes(
field as TFilterNestedKeysOfType<T, undefined | string | string[]>,
field as TFilterNestedKeysOfType<
T,
null | undefined | string | string[]
>,
)
)
return true;

View File

@ -9,7 +9,7 @@
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 { getFixturesWithDefaultValues } from '../defaultValues';
@ -56,7 +56,7 @@ export const installNlpSampleFixtures = async () => {
nlpSampleFixtures.map((v) => {
return {
...v,
language: languages[parseInt(v.language)].id,
language: v.language ? languages[parseInt(v.language)].id : null,
};
}),
);