From 321c6377bb207233e5aa3f52e27f44527c7b89fc Mon Sep 17 00:00:00 2001 From: omarNaifer12 Date: Thu, 9 Jan 2025 21:43:39 +0100 Subject: [PATCH 1/4] fix(api): optimize performance in preUpdateMany --- .../repositories/block.repository.spec.ts | 33 +++++++++++-------- api/src/chat/repositories/block.repository.ts | 9 ++--- 2 files changed, 25 insertions(+), 17 deletions(-) diff --git a/api/src/chat/repositories/block.repository.spec.ts b/api/src/chat/repositories/block.repository.spec.ts index 472a3659c..d2f709490 100644 --- a/api/src/chat/repositories/block.repository.spec.ts +++ b/api/src/chat/repositories/block.repository.spec.ts @@ -1,5 +1,5 @@ /* - * Copyright © 2024 Hexastack. All rights reserved. + * Copyright © 2025 Hexastack. All rights reserved. * * Licensed under the GNU Affero General Public License v3.0 (AGPLv3) with the following additional terms: * 1. The name "Hexabot" is a trademark of Hexastack. You may not use this name in derivative works without express written permission. @@ -165,12 +165,14 @@ describe('BlockRepository', () => { describe('prepareBlocksInCategoryUpdateScope', () => { it('should update blocks within the scope based on category and ids', async () => { - jest.spyOn(blockRepository, 'findOne').mockResolvedValue({ - id: validIds[0], - category: 'oldCategory', - nextBlocks: [validIds[1]], - attachedBlock: validIds[1], - } as Block); + const mockFind = jest.spyOn(blockRepository, 'find').mockResolvedValue([ + { + id: validIds[0], + category: 'oldCategory', + nextBlocks: [validIds[1]], + attachedBlock: validIds[1], + }, + ] as Block[]); const mockUpdateOne = jest.spyOn(blockRepository, 'updateOne'); @@ -178,6 +180,7 @@ describe('BlockRepository', () => { validCategory, validIds, ); + expect(mockFind).toHaveBeenCalled(); expect(mockUpdateOne).toHaveBeenCalledWith(validIds[0], { nextBlocks: [validIds[1]], @@ -186,12 +189,14 @@ describe('BlockRepository', () => { }); it('should not update blocks if the category already matches', async () => { - jest.spyOn(blockRepository, 'findOne').mockResolvedValue({ - id: validIds[0], - category: validCategory, - nextBlocks: [], - attachedBlock: null, - } as Block); + const mockFind = jest.spyOn(blockRepository, 'find').mockResolvedValue([ + { + id: validIds[0], + category: validCategory, + nextBlocks: [], + attachedBlock: null, + }, + ] as Block[]); const mockUpdateOne = jest.spyOn(blockRepository, 'updateOne'); @@ -200,6 +205,8 @@ describe('BlockRepository', () => { validIds, ); + expect(mockFind).toHaveBeenCalled(); + expect(mockUpdateOne).not.toHaveBeenCalled(); }); }); diff --git a/api/src/chat/repositories/block.repository.ts b/api/src/chat/repositories/block.repository.ts index 11987943c..07df666c9 100644 --- a/api/src/chat/repositories/block.repository.ts +++ b/api/src/chat/repositories/block.repository.ts @@ -1,5 +1,5 @@ /* - * Copyright © 2024 Hexastack. All rights reserved. + * Copyright © 2025 Hexastack. All rights reserved. * * Licensed under the GNU Affero General Public License v3.0 (AGPLv3) with the following additional terms: * 1. The name "Hexabot" is a trademark of Hexastack. You may not use this name in derivative works without express written permission. @@ -177,8 +177,9 @@ export class BlockRepository extends BaseRepository< category: string, ids: string[], ): Promise { - for (const id of ids) { - const oldState: Block = await this.findOne(id); + const oldStates: Block[] = await this.find({ _id: { $in: ids } }); + + for (const oldState of oldStates) { if (oldState.category !== category) { const updatedNextBlocks = oldState.nextBlocks.filter((nextBlock) => ids.includes(nextBlock), @@ -188,7 +189,7 @@ export class BlockRepository extends BaseRepository< ? oldState.attachedBlock : null; - await this.updateOne(id, { + await this.updateOne(oldState.id, { nextBlocks: updatedNextBlocks, attachedBlock: updatedAttachedBlock, }); From 0930f2657682cd00f6256b3224dc655010c6aac0 Mon Sep 17 00:00:00 2001 From: omarNaifer12 Date: Sat, 11 Jan 2025 14:47:05 +0100 Subject: [PATCH 2/4] fix(api): execute fallback message when no Global Fallback --- api/src/chat/services/bot.service.ts | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/api/src/chat/services/bot.service.ts b/api/src/chat/services/bot.service.ts index 94e2e31e4..fc3390aae 100644 --- a/api/src/chat/services/bot.service.ts +++ b/api/src/chat/services/bot.service.ts @@ -1,5 +1,5 @@ /* - * Copyright © 2024 Hexastack. All rights reserved. + * Copyright © 2025 Hexastack. All rights reserved. * * Licensed under the GNU Affero General Public License v3.0 (AGPLv3) with the following additional terms: * 1. The name "Hexabot" is a trademark of Hexastack. You may not use this name in derivative works without express written permission. @@ -454,7 +454,8 @@ export class BotService { this.logger.debug('No message blocks available!'); if ( settings.chatbot_settings && - settings.chatbot_settings.global_fallback + settings.chatbot_settings.global_fallback && + settings.chatbot_settings.fallback_block ) { this.eventEmitter.emit('hook:analytics:fallback-global', event); this.logger.debug('Sending global fallback message ...'); @@ -485,7 +486,22 @@ export class BotService { } as any as BlockFull); } } - // Do nothing ... + this.sendMessageToSubscriber(event, { + id: 'global-fallback', + name: 'Global Fallback', + message: settings.chatbot_settings.fallback_message, + options: {}, + patterns: [], + assign_labels: [], + starts_conversation: false, + position: { x: 0, y: 0 }, + capture_vars: [], + builtin: true, + createdAt: new Date(), + updatedAt: new Date(), + attachedBlock: null, + } as any as BlockFull); + return; } From e9b6350a73cbc506a87a64a7bf21d8654339c2cd Mon Sep 17 00:00:00 2001 From: omarNaifer12 Date: Sat, 11 Jan 2025 15:06:51 +0100 Subject: [PATCH 3/4] fix(api): execute fallback message when no Global Fallback --- .../repositories/block.repository.spec.ts | 31 +++++++------------ api/src/chat/repositories/block.repository.ts | 9 +++--- 2 files changed, 16 insertions(+), 24 deletions(-) diff --git a/api/src/chat/repositories/block.repository.spec.ts b/api/src/chat/repositories/block.repository.spec.ts index d2f709490..127e27951 100644 --- a/api/src/chat/repositories/block.repository.spec.ts +++ b/api/src/chat/repositories/block.repository.spec.ts @@ -165,14 +165,12 @@ describe('BlockRepository', () => { describe('prepareBlocksInCategoryUpdateScope', () => { it('should update blocks within the scope based on category and ids', async () => { - const mockFind = jest.spyOn(blockRepository, 'find').mockResolvedValue([ - { - id: validIds[0], - category: 'oldCategory', - nextBlocks: [validIds[1]], - attachedBlock: validIds[1], - }, - ] as Block[]); + jest.spyOn(blockRepository, 'findOne').mockResolvedValue({ + id: validIds[0], + category: 'oldCategory', + nextBlocks: [validIds[1]], + attachedBlock: validIds[1], + } as Block); const mockUpdateOne = jest.spyOn(blockRepository, 'updateOne'); @@ -180,7 +178,6 @@ describe('BlockRepository', () => { validCategory, validIds, ); - expect(mockFind).toHaveBeenCalled(); expect(mockUpdateOne).toHaveBeenCalledWith(validIds[0], { nextBlocks: [validIds[1]], @@ -189,14 +186,12 @@ describe('BlockRepository', () => { }); it('should not update blocks if the category already matches', async () => { - const mockFind = jest.spyOn(blockRepository, 'find').mockResolvedValue([ - { - id: validIds[0], - category: validCategory, - nextBlocks: [], - attachedBlock: null, - }, - ] as Block[]); + jest.spyOn(blockRepository, 'findOne').mockResolvedValue({ + id: validIds[0], + category: validCategory, + nextBlocks: [], + attachedBlock: null, + } as Block); const mockUpdateOne = jest.spyOn(blockRepository, 'updateOne'); @@ -205,8 +200,6 @@ describe('BlockRepository', () => { validIds, ); - expect(mockFind).toHaveBeenCalled(); - expect(mockUpdateOne).not.toHaveBeenCalled(); }); }); diff --git a/api/src/chat/repositories/block.repository.ts b/api/src/chat/repositories/block.repository.ts index 07df666c9..077eb15e9 100644 --- a/api/src/chat/repositories/block.repository.ts +++ b/api/src/chat/repositories/block.repository.ts @@ -56,7 +56,7 @@ export class BlockRepository extends BaseRepository< block.message.attachment.payload && 'url' in block.message.attachment.payload ) { - this.logger.error( + this.logger?.error( 'NOTE: `url` payload has been deprecated in favor of `attachment_id`', block.name, ); @@ -177,9 +177,8 @@ export class BlockRepository extends BaseRepository< category: string, ids: string[], ): Promise { - const oldStates: Block[] = await this.find({ _id: { $in: ids } }); - - for (const oldState of oldStates) { + for (const id of ids) { + const oldState = await this.findOne(id); if (oldState.category !== category) { const updatedNextBlocks = oldState.nextBlocks.filter((nextBlock) => ids.includes(nextBlock), @@ -189,7 +188,7 @@ export class BlockRepository extends BaseRepository< ? oldState.attachedBlock : null; - await this.updateOne(oldState.id, { + await this.updateOne(id, { nextBlocks: updatedNextBlocks, attachedBlock: updatedAttachedBlock, }); From 3b920a15b65f09386eed2838fd3021dbdc2e9f2f Mon Sep 17 00:00:00 2001 From: omarNaifer12 Date: Sat, 11 Jan 2025 15:13:59 +0100 Subject: [PATCH 4/4] fix(api): execute fallback message when no Global Fallback --- api/src/chat/repositories/block.repository.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/api/src/chat/repositories/block.repository.ts b/api/src/chat/repositories/block.repository.ts index 077eb15e9..4bac81830 100644 --- a/api/src/chat/repositories/block.repository.ts +++ b/api/src/chat/repositories/block.repository.ts @@ -56,7 +56,7 @@ export class BlockRepository extends BaseRepository< block.message.attachment.payload && 'url' in block.message.attachment.payload ) { - this.logger?.error( + this.logger.error( 'NOTE: `url` payload has been deprecated in favor of `attachment_id`', block.name, ); @@ -95,7 +95,6 @@ export class BlockRepository extends BaseRepository< | UpdateQuery>, ): Promise { const update: BlockUpdateDto = updates?.['$set']; - if (update?.category) { const movedBlock: Block = await this.findOne(criteria); @@ -178,7 +177,7 @@ export class BlockRepository extends BaseRepository< ids: string[], ): Promise { for (const id of ids) { - const oldState = await this.findOne(id); + const oldState: Block = await this.findOne(id); if (oldState.category !== category) { const updatedNextBlocks = oldState.nextBlocks.filter((nextBlock) => ids.includes(nextBlock),