From 5db88021c402ece1a121a2d6fef40c5412665c70 Mon Sep 17 00:00:00 2001 From: yassinedorbozgithub Date: Fri, 13 Jun 2025 11:53:59 +0100 Subject: [PATCH 1/6] fix(api): align eventEmitter update events --- .../nlp/repositories/nlp-entity.repository.ts | 26 +------- .../nlp/repositories/nlp-value.repository.ts | 31 +-------- api/src/nlp/services/nlp.service.ts | 63 +++++++++++++------ api/types/event-emitter.d.ts | 7 ++- 4 files changed, 51 insertions(+), 76 deletions(-) diff --git a/api/src/nlp/repositories/nlp-entity.repository.ts b/api/src/nlp/repositories/nlp-entity.repository.ts index e39acaf0..e27041fd 100644 --- a/api/src/nlp/repositories/nlp-entity.repository.ts +++ b/api/src/nlp/repositories/nlp-entity.repository.ts @@ -8,7 +8,7 @@ import { Injectable } from '@nestjs/common'; import { InjectModel } from '@nestjs/mongoose'; -import { Document, Model, Query } from 'mongoose'; +import { Model } from 'mongoose'; import { BaseRepository } from '@/utils/generics/base-repository'; @@ -30,28 +30,4 @@ export class NlpEntityRepository extends BaseRepository< constructor(@InjectModel(NlpEntity.name) readonly model: Model) { super(model, NlpEntity, NLP_ENTITY_POPULATE, NlpEntityFull); } - - /** - * Post-update hook that triggers after an NLP entity is updated. - * Emits an event to notify other parts of the system about the update. - * Bypasses built-in entities. - * - * @param query - The query used to find and update the entity. - * @param updated - The updated NLP entity document. - */ - async postUpdate( - _query: Query< - Document, - Document, - unknown, - NlpEntity, - 'findOneAndUpdate' - >, - updated: NlpEntity, - ): Promise { - if (!updated?.builtin) { - // Bypass builtin entities (probably fixtures) - this.eventEmitter.emit('hook:nlpEntity:update', updated); - } - } } diff --git a/api/src/nlp/repositories/nlp-value.repository.ts b/api/src/nlp/repositories/nlp-value.repository.ts index 84ab6152..16b9afb6 100644 --- a/api/src/nlp/repositories/nlp-value.repository.ts +++ b/api/src/nlp/repositories/nlp-value.repository.ts @@ -9,14 +9,7 @@ import { Injectable } from '@nestjs/common'; import { InjectModel } from '@nestjs/mongoose'; import { plainToInstance } from 'class-transformer'; -import { - Document, - Model, - PipelineStage, - Query, - SortOrder, - Types, -} from 'mongoose'; +import { Model, PipelineStage, SortOrder, Types } from 'mongoose'; import { BaseRepository } from '@/utils/generics/base-repository'; import { PageQueryDto } from '@/utils/pagination/pagination-query.dto'; @@ -45,28 +38,6 @@ export class NlpValueRepository extends BaseRepository< super(model, NlpValue, NLP_VALUE_POPULATE, NlpValueFull); } - /** - * Emits an event after an NLP value is updated, bypassing built-in values. - * - * @param query - The query that was used to update the NLP value. - * @param updated - The updated NLP value document. - */ - async postUpdate( - _query: Query< - Document, - Document, - unknown, - NlpValue, - 'findOneAndUpdate' - >, - updated: NlpValue, - ): Promise { - if (!updated?.builtin) { - // Bypass builtin entities (probably fixtures) - this.eventEmitter.emit('hook:nlpValue:update', updated); - } - } - private getSortDirection(sortOrder: SortOrder) { return typeof sortOrder === 'number' ? sortOrder diff --git a/api/src/nlp/services/nlp.service.ts b/api/src/nlp/services/nlp.service.ts index f2f9db0a..fa55d199 100644 --- a/api/src/nlp/services/nlp.service.ts +++ b/api/src/nlp/services/nlp.service.ts @@ -8,6 +8,7 @@ import { Injectable, NotFoundException } from '@nestjs/common'; import { OnEvent } from '@nestjs/event-emitter'; +import { Document, Query } from 'mongoose'; import { HelperService } from '@/helper/helper.service'; import { HelperType, NLU } from '@/helper/types'; @@ -96,15 +97,28 @@ export class NlpService { * * @param entity - The NLP entity to be updated. */ - @OnEvent('hook:nlpEntity:update') - async handleEntityUpdate(entity: NlpEntity) { - // Synchonize new entity with NLP provider - try { - const helper = await this.helperService.getDefaultNluHelper(); - await helper.updateEntity(entity); - this.logger.debug('Updated entity successfully synced!', entity); - } catch (err) { - this.logger.error('Unable to sync updated entity', err); + @OnEvent('hook:nlpEntity:postUpdate') + async handleEntityPostUpdate( + _query: Query< + Document, + Document, + unknown, + NlpEntity, + 'findOneAndUpdate' + >, + updated: NlpEntity, + ) { + if (!updated?.builtin) { + // Synchonize new entity with NLP provider + try { + const helper = await this.helperService.getDefaultHelper( + HelperType.NLU, + ); + await helper.updateEntity(updated); + this.logger.debug('Updated entity successfully synced!', updated); + } catch (err) { + this.logger.error('Unable to sync updated entity', err); + } } } @@ -174,15 +188,28 @@ export class NlpService { * * @param value - The NLP value to be updated. */ - @OnEvent('hook:nlpValue:update') - async handleValueUpdate(value: NlpValue) { - // Synchonize new value with NLP provider - try { - const helper = await this.helperService.getDefaultNluHelper(); - await helper.updateValue(value); - this.logger.debug('Updated value successfully synced!', value); - } catch (err) { - this.logger.error('Unable to sync updated value', err); + @OnEvent('hook:nlpValue:postUpdate') + async handleValueUpdate( + _query: Query< + Document, + Document, + unknown, + NlpValue, + 'findOneAndUpdate' + >, + updated: NlpValue, + ) { + if (!updated?.builtin) { + // Synchonize new value with NLP provider + try { + const helper = await this.helperService.getDefaultHelper( + HelperType.NLU, + ); + await helper.updateValue(updated); + this.logger.debug('Updated value successfully synced!', updated); + } catch (err) { + this.logger.error('Unable to sync updated value', err); + } } } diff --git a/api/types/event-emitter.d.ts b/api/types/event-emitter.d.ts index 98dd6315..740a4a7e 100644 --- a/api/types/event-emitter.d.ts +++ b/api/types/event-emitter.d.ts @@ -132,10 +132,10 @@ declare module '@nestjs/event-emitter' { menu: TDefinition; language: TDefinition; translation: TDefinition; - nlpEntity: TDefinition; + nlpEntity: TDefinition; nlpSampleEntity: TDefinition; nlpSample: TDefinition; - nlpValue: TDefinition; + nlpValue: TDefinition; setting: TDefinition; invitation: TDefinition; model: TDefinition; @@ -189,7 +189,8 @@ declare module '@nestjs/event-emitter' { type TPostUpdateValidate = FilterQuery; - type TPostUpdate = THydratedDocument; + // TODO this type will be optimized soon in a separated PR + type TPostUpdate = T & any; type TPostDelete = DeleteResult; From ab4a8931364a9ae44148e2b83f94b46529b9becf Mon Sep 17 00:00:00 2001 From: yassinedorbozgithub Date: Fri, 13 Jun 2025 11:57:49 +0100 Subject: [PATCH 2/6] fix(api): update NlpValue OnEvent method name --- api/src/nlp/services/nlp.service.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/src/nlp/services/nlp.service.ts b/api/src/nlp/services/nlp.service.ts index fa55d199..1c557381 100644 --- a/api/src/nlp/services/nlp.service.ts +++ b/api/src/nlp/services/nlp.service.ts @@ -189,7 +189,7 @@ export class NlpService { * @param value - The NLP value to be updated. */ @OnEvent('hook:nlpValue:postUpdate') - async handleValueUpdate( + async handleValuePostUpdate( _query: Query< Document, Document, From 2d133450f19f63fc0d248549e7483927583a9056 Mon Sep 17 00:00:00 2001 From: yassinedorbozgithub Date: Tue, 17 Jun 2025 11:38:37 +0100 Subject: [PATCH 3/6] fix(api): enhance NlpValue postUpdate unit tests --- .../nlp-entity.repository.spec.ts | 24 +++++++++++++++++++ .../repositories/nlp-value.repository.spec.ts | 24 +++++++++++++++++++ 2 files changed, 48 insertions(+) diff --git a/api/src/nlp/repositories/nlp-entity.repository.spec.ts b/api/src/nlp/repositories/nlp-entity.repository.spec.ts index 96920e70..ed4141be 100644 --- a/api/src/nlp/repositories/nlp-entity.repository.spec.ts +++ b/api/src/nlp/repositories/nlp-entity.repository.spec.ts @@ -228,4 +228,28 @@ describe('NlpEntityRepository', () => { expect(intentNlpEntity).toEqualPayload(result); }); }); + + describe('postUpdate', () => { + it('should update an NlpValue and trigger a postUpdate event', async () => { + nlpEntityRepository.eventEmitter.once( + 'hook:nlpEntity:postUpdate', + async (...[query, updated]) => { + const spy1 = jest.spyOn(llmNluHelper, 'updateEntity'); + await nlpService.handleEntityPostUpdate(query, updated); + + expect(spy1).toHaveBeenCalledWith(updated); + }, + ); + + const updatedNlpEntity = await nlpEntityRepository.updateOne( + { + name: 'test2', + }, + { value: 'test3' }, + ); + const result = await nlpEntityRepository.findOne(updatedNlpEntity.id); + + expect(result).toEqualPayload(updatedNlpEntity); + }); + }); }); diff --git a/api/src/nlp/repositories/nlp-value.repository.spec.ts b/api/src/nlp/repositories/nlp-value.repository.spec.ts index 48689727..dec67457 100644 --- a/api/src/nlp/repositories/nlp-value.repository.spec.ts +++ b/api/src/nlp/repositories/nlp-value.repository.spec.ts @@ -250,4 +250,28 @@ describe('NlpValueRepository', () => { expect(intentNlpEntity).toEqualPayload(result); }); }); + + describe('postUpdate', () => { + it('should update an NlpValue and trigger a postUpdate event', async () => { + nlpValueRepository.eventEmitter.once( + 'hook:nlpValue:postUpdate', + async (...[query, updated]) => { + const spy1 = jest.spyOn(llmNluHelper, 'updateValue'); + await nlpService.handleValuePostUpdate(query, updated); + + expect(spy1).toHaveBeenCalledWith(updated); + }, + ); + + const updatedNlpEntity = await nlpValueRepository.updateOne( + { + value: 'test', + }, + { value: 'test2' }, + ); + const result = await nlpValueRepository.findOne(updatedNlpEntity.id); + + expect(result).toEqualPayload(updatedNlpEntity); + }); + }); }); From d7e0d4a5d6a6bc77ef82460ec46e1ea36a1f9422 Mon Sep 17 00:00:00 2001 From: yassinedorbozgithub Date: Tue, 17 Jun 2025 11:47:17 +0100 Subject: [PATCH 4/6] fix(api): enhance postUpdate unit tests --- api/src/nlp/repositories/nlp-entity.repository.spec.ts | 2 +- api/src/nlp/repositories/nlp-value.repository.spec.ts | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/api/src/nlp/repositories/nlp-entity.repository.spec.ts b/api/src/nlp/repositories/nlp-entity.repository.spec.ts index ed4141be..e48ccf69 100644 --- a/api/src/nlp/repositories/nlp-entity.repository.spec.ts +++ b/api/src/nlp/repositories/nlp-entity.repository.spec.ts @@ -230,7 +230,7 @@ describe('NlpEntityRepository', () => { }); describe('postUpdate', () => { - it('should update an NlpValue and trigger a postUpdate event', async () => { + it('should update an NlpEntity and trigger a postUpdate event', async () => { nlpEntityRepository.eventEmitter.once( 'hook:nlpEntity:postUpdate', async (...[query, updated]) => { diff --git a/api/src/nlp/repositories/nlp-value.repository.spec.ts b/api/src/nlp/repositories/nlp-value.repository.spec.ts index dec67457..6256dea0 100644 --- a/api/src/nlp/repositories/nlp-value.repository.spec.ts +++ b/api/src/nlp/repositories/nlp-value.repository.spec.ts @@ -263,15 +263,15 @@ describe('NlpValueRepository', () => { }, ); - const updatedNlpEntity = await nlpValueRepository.updateOne( + const updatedNlpValue = await nlpValueRepository.updateOne( { value: 'test', }, { value: 'test2' }, ); - const result = await nlpValueRepository.findOne(updatedNlpEntity.id); + const result = await nlpValueRepository.findOne(updatedNlpValue.id); - expect(result).toEqualPayload(updatedNlpEntity); + expect(result).toEqualPayload(updatedNlpValue); }); }); }); From 9be40457a11d3f3d3994d485995e44e62762e852 Mon Sep 17 00:00:00 2001 From: yassinedorbozgithub Date: Tue, 17 Jun 2025 15:34:17 +0100 Subject: [PATCH 5/6] fix(api): apply feedback --- api/src/nlp/repositories/nlp-entity.repository.spec.ts | 4 ++-- api/src/nlp/repositories/nlp-value.repository.spec.ts | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/api/src/nlp/repositories/nlp-entity.repository.spec.ts b/api/src/nlp/repositories/nlp-entity.repository.spec.ts index f3a1441d..fee29fc6 100644 --- a/api/src/nlp/repositories/nlp-entity.repository.spec.ts +++ b/api/src/nlp/repositories/nlp-entity.repository.spec.ts @@ -234,10 +234,10 @@ describe('NlpEntityRepository', () => { nlpEntityRepository.eventEmitter.once( 'hook:nlpEntity:postUpdate', async (...[query, updated]) => { - const spy1 = jest.spyOn(llmNluHelper, 'updateEntity'); + jest.spyOn(llmNluHelper, 'updateEntity'); await nlpService.handleEntityPostUpdate(query, updated); - expect(spy1).toHaveBeenCalledWith(updated); + expect(llmNluHelper.updateEntity).toHaveBeenCalledWith(updated); }, ); diff --git a/api/src/nlp/repositories/nlp-value.repository.spec.ts b/api/src/nlp/repositories/nlp-value.repository.spec.ts index 7e07a983..98f8db3c 100644 --- a/api/src/nlp/repositories/nlp-value.repository.spec.ts +++ b/api/src/nlp/repositories/nlp-value.repository.spec.ts @@ -256,10 +256,10 @@ describe('NlpValueRepository', () => { nlpValueRepository.eventEmitter.once( 'hook:nlpValue:postUpdate', async (...[query, updated]) => { - const spy1 = jest.spyOn(llmNluHelper, 'updateValue'); + jest.spyOn(llmNluHelper, 'updateValue'); await nlpService.handleValuePostUpdate(query, updated); - expect(spy1).toHaveBeenCalledWith(updated); + expect(llmNluHelper.updateValue).toHaveBeenCalledWith(updated); }, ); From 848a1b37672ded333acaa91f321297160e2c7398 Mon Sep 17 00:00:00 2001 From: yassinedorbozgithub Date: Wed, 18 Jun 2025 09:06:32 +0100 Subject: [PATCH 6/6] fix(api): enhance postUpdate unit tests --- api/src/nlp/repositories/nlp-entity.repository.spec.ts | 9 +++++++-- api/src/nlp/repositories/nlp-value.repository.spec.ts | 8 +++++++- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/api/src/nlp/repositories/nlp-entity.repository.spec.ts b/api/src/nlp/repositories/nlp-entity.repository.spec.ts index 8c77bc7b..76a99d4a 100644 --- a/api/src/nlp/repositories/nlp-entity.repository.spec.ts +++ b/api/src/nlp/repositories/nlp-entity.repository.spec.ts @@ -231,12 +231,13 @@ describe('NlpEntityRepository', () => { describe('postUpdate', () => { it('should update an NlpEntity and trigger a postUpdate event', async () => { + jest.spyOn(nlpService, 'handleEntityPostUpdate'); + jest.spyOn(llmNluHelper, 'updateEntity'); + nlpEntityRepository.eventEmitter.once( 'hook:nlpEntity:postUpdate', async (...[query, updated]) => { - jest.spyOn(llmNluHelper, 'updateEntity'); await nlpService.handleEntityPostUpdate(query, updated); - expect(llmNluHelper.updateEntity).toHaveBeenCalledWith(updated); }, ); @@ -247,6 +248,10 @@ describe('NlpEntityRepository', () => { }, { value: 'test3' }, ); + + expect(nlpService.handleEntityPostUpdate).toHaveBeenCalledTimes(1); + expect(llmNluHelper.updateEntity).toHaveBeenCalledTimes(1); + const result = await nlpEntityRepository.findOne(updatedNlpEntity.id); expect(result).toEqualPayload(updatedNlpEntity); diff --git a/api/src/nlp/repositories/nlp-value.repository.spec.ts b/api/src/nlp/repositories/nlp-value.repository.spec.ts index 5774407e..7cc9a482 100644 --- a/api/src/nlp/repositories/nlp-value.repository.spec.ts +++ b/api/src/nlp/repositories/nlp-value.repository.spec.ts @@ -253,10 +253,12 @@ describe('NlpValueRepository', () => { describe('postUpdate', () => { it('should update an NlpValue and trigger a postUpdate event', async () => { + jest.spyOn(nlpService, 'handleValuePostUpdate'); + jest.spyOn(llmNluHelper, 'updateValue'); + nlpValueRepository.eventEmitter.once( 'hook:nlpValue:postUpdate', async (...[query, updated]) => { - jest.spyOn(llmNluHelper, 'updateValue'); await nlpService.handleValuePostUpdate(query, updated); expect(llmNluHelper.updateValue).toHaveBeenCalledWith(updated); @@ -269,6 +271,10 @@ describe('NlpValueRepository', () => { }, { value: 'test2' }, ); + + expect(nlpService.handleValuePostUpdate).toHaveBeenCalledTimes(1); + expect(llmNluHelper.updateValue).toHaveBeenCalledTimes(1); + const result = await nlpValueRepository.findOne(updatedNlpValue.id); expect(result).toEqualPayload(updatedNlpValue);