From 9bd708dbd89f51711290c8a25ebb2e1f5f18f99a Mon Sep 17 00:00:00 2001 From: medtaher Date: Tue, 11 Feb 2025 18:49:28 +0100 Subject: [PATCH 1/6] feat: change mid to string array --- api/src/channel/lib/Handler.ts | 2 +- api/src/chat/dto/message.dto.ts | 6 +++--- api/src/chat/schemas/message.schema.ts | 6 +++--- api/src/extensions/channels/web/base-web-channel.ts | 4 ++-- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/api/src/channel/lib/Handler.ts b/api/src/channel/lib/Handler.ts index e1dd33ef..0f2af8fd 100644 --- a/api/src/channel/lib/Handler.ts +++ b/api/src/channel/lib/Handler.ts @@ -214,7 +214,7 @@ export default abstract class ChannelHandler< envelope: StdOutgoingEnvelope, options: any, context: any, - ): Promise<{ mid: string }>; + ): Promise<{ mid: string | string[] }>; /** * Calls the channel handler to fetch attachments and stores them diff --git a/api/src/chat/dto/message.dto.ts b/api/src/chat/dto/message.dto.ts index a5855bde..3e1b6e14 100644 --- a/api/src/chat/dto/message.dto.ts +++ b/api/src/chat/dto/message.dto.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. @@ -11,8 +11,8 @@ import { IsBoolean, IsNotEmpty, IsObject, - IsString, IsOptional, + IsString, } from 'class-validator'; import { IsObjectId } from '@/utils/validation-rules/is-object-id'; @@ -27,7 +27,7 @@ export class MessageCreateDto { @ApiProperty({ description: 'Message id', type: String }) @IsOptional() @IsString() - mid?: string; + mid?: string | string[]; @ApiProperty({ description: 'Reply to Message id', type: String }) @IsOptional() diff --git a/api/src/chat/schemas/message.schema.ts b/api/src/chat/schemas/message.schema.ts index 302db508..ed91b87a 100644 --- a/api/src/chat/schemas/message.schema.ts +++ b/api/src/chat/schemas/message.schema.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. @@ -20,11 +20,11 @@ import { StdIncomingMessage, StdOutgoingMessage } from './types/message'; @Schema({ timestamps: true }) export class MessageStub extends BaseSchema { @Prop({ - type: String, + type: [String], required: false, //TODO : add default value for mid }) - mid?: string; + mid?: string | string[]; @Prop({ type: MongooseSchema.Types.ObjectId, diff --git a/api/src/extensions/channels/web/base-web-channel.ts b/api/src/extensions/channels/web/base-web-channel.ts index a2079181..c9f2556f 100644 --- a/api/src/extensions/channels/web/base-web-channel.ts +++ b/api/src/extensions/channels/web/base-web-channel.ts @@ -242,7 +242,7 @@ export default abstract class BaseWebChannelHandler< ...message, author: anyMessage.sender, read: true, // Temporary fix as read is false in the bd - mid: anyMessage.mid, + mid: anyMessage.mid?.[0], createdAt: anyMessage.createdAt, }); } else { @@ -251,7 +251,7 @@ export default abstract class BaseWebChannelHandler< ...message, author: 'chatbot', read: true, // Temporary fix as read is false in the bd - mid: anyMessage.mid || this.generateId(), + mid: anyMessage.mid?.[0] || this.generateId(), handover: !!anyMessage.handover, createdAt: anyMessage.createdAt, }); From 0453ffb46c2e8729572ce0e70f5107749cba9d55 Mon Sep 17 00:00:00 2001 From: medtaher Date: Wed, 12 Feb 2025 02:23:42 +0100 Subject: [PATCH 2/6] test: adjust unit tests --- .../repositories/message.repository.spec.ts | 40 +++++++++++++++++-- 1 file changed, 37 insertions(+), 3 deletions(-) diff --git a/api/src/chat/repositories/message.repository.spec.ts b/api/src/chat/repositories/message.repository.spec.ts index f817f2a9..c6529850 100644 --- a/api/src/chat/repositories/message.repository.spec.ts +++ b/api/src/chat/repositories/message.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. @@ -60,7 +60,11 @@ describe('MessageRepository', () => { afterAll(closeInMongodConnection); describe('findOneAndPopulate', () => { - it('should find one message by id, and populate its sender and recipient', async () => { + function toArray(value?: string | string[]): string[] { + return value ? (Array.isArray(value) ? value : [value]) : []; + } + + it('should find one message by id, and populate its sender and recipient (with mid being a string)', async () => { jest.spyOn(messageModel, 'findById'); const message = (await messageRepository.findOne({ mid: 'mid-1' }))!; const sender = await subscriberRepository.findOne(message!['sender']); @@ -71,8 +75,38 @@ describe('MessageRepository', () => { const result = await messageRepository.findOneAndPopulate(message.id); expect(messageModel.findById).toHaveBeenCalledWith(message.id, undefined); + + const expectedFixture = messageFixtures.find( + ({ mid }) => + JSON.stringify(toArray(mid)) === JSON.stringify(message.mid), + ); expect(result).toEqualPayload({ - ...messageFixtures.find(({ mid }) => mid === message.mid), + ...expectedFixture, + mid: toArray(expectedFixture?.mid), + sender, + recipient, + sentBy: user.id, + }); + }); + it('should find one message by id, and populate its sender and recipient (with mid being a string array)', async () => { + jest.spyOn(messageModel, 'findById'); + const message = (await messageRepository.findOne({ mid: 'mid-2' }))!; + const sender = await subscriberRepository.findOne(message!['sender']); + const recipient = await subscriberRepository.findOne( + message!['recipient'], + ); + const user = (await userRepository.findOne(message!['sentBy']))!; + const result = await messageRepository.findOneAndPopulate(message.id); + + expect(messageModel.findById).toHaveBeenCalledWith(message.id, undefined); + + const expectedFixture = messageFixtures.find( + ({ mid }) => + JSON.stringify(toArray(mid)) === JSON.stringify(message.mid), + ); + expect(result).toEqualPayload({ + ...expectedFixture, + mid: toArray(expectedFixture?.mid), sender, recipient, sentBy: user.id, From fbd630ceae95d093d997534f51b2a036d32fa14e Mon Sep 17 00:00:00 2001 From: medtaher Date: Wed, 12 Feb 2025 02:24:37 +0100 Subject: [PATCH 3/6] test: adjust unit tests --- api/src/utils/test/fixtures/message.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/api/src/utils/test/fixtures/message.ts b/api/src/utils/test/fixtures/message.ts index 5f70796a..d2496bbd 100644 --- a/api/src/utils/test/fixtures/message.ts +++ b/api/src/utils/test/fixtures/message.ts @@ -9,7 +9,7 @@ import mongoose from 'mongoose'; import { MessageCreateDto } from '@/chat/dto/message.dto'; -import { MessageModel, Message } from '@/chat/schemas/message.schema'; +import { Message, MessageModel } from '@/chat/schemas/message.schema'; import { getFixturesWithDefaultValues } from '../defaultValues'; import { TFixturesDefaultValues } from '../types'; @@ -27,7 +27,7 @@ const messages: MessageCreateDto[] = [ delivery: true, }, { - mid: 'mid-2', + mid: ['mid-2', 'mid-2.1'], sender: '1', recipient: '1', sentBy: '0', From d7f9ecd43da9979b6f3e0099a9c6e0595151d958 Mon Sep 17 00:00:00 2001 From: medtaher Date: Wed, 12 Feb 2025 02:41:29 +0100 Subject: [PATCH 4/6] test: adjust unit tests --- .../controllers/message.controller.spec.ts | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/api/src/chat/controllers/message.controller.spec.ts b/api/src/chat/controllers/message.controller.spec.ts index d2e9c2d3..2640db4f 100644 --- a/api/src/chat/controllers/message.controller.spec.ts +++ b/api/src/chat/controllers/message.controller.spec.ts @@ -141,6 +141,10 @@ describe('MessageController', () => { afterAll(closeInMongodConnection); + function toArray(value?: string | string[]): string[] { + return value ? (Array.isArray(value) ? value : [value]) : []; + } + describe('count', () => { it('should count messages', async () => { jest.spyOn(messageService, 'count'); @@ -162,8 +166,13 @@ describe('MessageController', () => { expect(messageService.findOneAndPopulate).toHaveBeenCalledWith( message.id, ); + const expectedFixture = messageFixtures.find( + ({ mid }) => + JSON.stringify(toArray(mid)) === JSON.stringify(message.mid), + ); expect(result).toEqualPayload({ - ...messageFixtures.find(({ mid }) => mid === message.mid), + ...expectedFixture, + mid: toArray(expectedFixture?.mid), sender, recipient, sentBy: user.id, @@ -174,8 +183,13 @@ describe('MessageController', () => { const result = await messageController.findOne(message.id, []); expect(messageService.findOne).toHaveBeenCalledWith(message.id); + const expectedFixture = messageFixtures.find( + ({ mid }) => + JSON.stringify(toArray(mid)) === JSON.stringify(message.mid), + ); expect(result).toEqualPayload({ - ...messageFixtures.find(({ mid }) => mid === message.mid), + ...expectedFixture, + mid: toArray(expectedFixture?.mid), sender: sender.id, recipient: recipient.id, sentBy: user.id, From b86fbe79187b144e8de54fce968a4c9620137827 Mon Sep 17 00:00:00 2001 From: medtaher Date: Wed, 12 Feb 2025 02:45:32 +0100 Subject: [PATCH 5/6] test: adjust unit tests --- api/src/chat/services/message.service.spec.ts | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/api/src/chat/services/message.service.spec.ts b/api/src/chat/services/message.service.spec.ts index 1e7899b8..09c6ff75 100644 --- a/api/src/chat/services/message.service.spec.ts +++ b/api/src/chat/services/message.service.spec.ts @@ -105,6 +105,9 @@ describe('MessageService', () => { afterEach(jest.clearAllMocks); afterAll(closeInMongodConnection); + function toArray(value?: string | string[]): string[] { + return value ? (Array.isArray(value) ? value : [value]) : []; + } describe('findOneAndPopulate', () => { it('should find message by id, and populate its corresponding sender and recipient', async () => { jest.spyOn(messageRepository, 'findOneAndPopulate'); @@ -114,8 +117,13 @@ describe('MessageService', () => { message.id, undefined, ); + const expectedFixture = messageFixtures.find( + ({ mid }) => + JSON.stringify(toArray(mid)) === JSON.stringify(message.mid), + ); expect(result).toEqualPayload({ - ...messageFixtures.find(({ mid }) => mid === message.mid), + ...expectedFixture, + mid: toArray(expectedFixture?.mid), sender, recipient, sentBy: user.id, From 6ecfbc5ffb9e5abfb7eaa489cd2d83017bd9190a Mon Sep 17 00:00:00 2001 From: medtaher Date: Fri, 14 Feb 2025 04:02:42 +0100 Subject: [PATCH 6/6] feat: add index on mid in Message schema --- api/src/chat/schemas/message.schema.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/api/src/chat/schemas/message.schema.ts b/api/src/chat/schemas/message.schema.ts index ed91b87a..4200e367 100644 --- a/api/src/chat/schemas/message.schema.ts +++ b/api/src/chat/schemas/message.schema.ts @@ -96,9 +96,12 @@ export class MessageFull extends MessageStub { sentBy?: string; // sendBy is never populate } +const MessageSchema = SchemaFactory.createForClass(MessageStub); +MessageSchema.index({ mid: 1 }); + export const MessageModel: ModelDefinition = LifecycleHookManager.attach({ name: Message.name, - schema: SchemaFactory.createForClass(MessageStub), + schema: MessageSchema, }); export default MessageModel.schema;