From 6e8a2a3d48941691d9bd4dcd41007ef6e881c867 Mon Sep 17 00:00:00 2001 From: medtaher Date: Mon, 23 Sep 2024 00:27:15 +0100 Subject: [PATCH] feat: add permanent option context var (unit tests fail) --- api/src/chat/schemas/context-var.schema.ts | 6 +++ api/src/chat/schemas/subscriber.schema.ts | 7 ++++ .../chat/schemas/types/subscriberContext.ts | 3 ++ api/src/chat/services/conversation.service.ts | 37 +++++++++++++++++-- 4 files changed, 50 insertions(+), 3 deletions(-) create mode 100644 api/src/chat/schemas/types/subscriberContext.ts diff --git a/api/src/chat/schemas/context-var.schema.ts b/api/src/chat/schemas/context-var.schema.ts index 4757391..60fcc7c 100644 --- a/api/src/chat/schemas/context-var.schema.ts +++ b/api/src/chat/schemas/context-var.schema.ts @@ -28,6 +28,12 @@ export class ContextVar extends BaseSchema { match: /^[a-z_0-9]+$/, }) name: string; + + @Prop({ + type: Boolean, + default: false, + }) + permanent?: boolean; } export const ContextVarModel: ModelDefinition = { diff --git a/api/src/chat/schemas/subscriber.schema.ts b/api/src/chat/schemas/subscriber.schema.ts index 1cee129..f0e451b 100644 --- a/api/src/chat/schemas/subscriber.schema.ts +++ b/api/src/chat/schemas/subscriber.schema.ts @@ -19,6 +19,7 @@ import { TFilterPopulateFields } from '@/utils/types/filter.types'; import { Label } from './label.schema'; import { ChannelData } from './types/channel'; +import { SubscriberContext } from './types/subscriberContext'; @Schema({ timestamps: true }) export class SubscriberStub extends BaseSchema { @@ -107,6 +108,12 @@ export class SubscriberStub extends BaseSchema { default: null, }) avatar?: unknown; + + @Prop({ + type: Object, + default: { vars: {} }, //TODO: add this to the migration + }) + context?: SubscriberContext; } @Schema({ timestamps: true }) diff --git a/api/src/chat/schemas/types/subscriberContext.ts b/api/src/chat/schemas/types/subscriberContext.ts new file mode 100644 index 0000000..18108d3 --- /dev/null +++ b/api/src/chat/schemas/types/subscriberContext.ts @@ -0,0 +1,3 @@ +export interface SubscriberContext { + [key: string]: any; +} diff --git a/api/src/chat/services/conversation.service.ts b/api/src/chat/services/conversation.service.ts index ecf45fc..319d31e 100644 --- a/api/src/chat/services/conversation.service.ts +++ b/api/src/chat/services/conversation.service.ts @@ -7,16 +7,19 @@ * 3. SaaS Restriction: This software, or any derivative of it, may not be used to offer a competing product or service (SaaS) without prior written consent from Hexastack. Offering the software as a service or using it in a commercial cloud environment without express permission is strictly prohibited. */ -import { Injectable } from '@nestjs/common'; +import { Injectable, Logger } from '@nestjs/common'; import { FilterQuery } from 'mongoose'; import EventWrapper from '@/channel/lib/EventWrapper'; import { LoggerService } from '@/logger/logger.service'; import { BaseService } from '@/utils/generics/base-service'; +import { ContextVarService } from './context-var.service'; +import { SubscriberService } from './subscriber.service'; import { VIEW_MORE_PAYLOAD } from '../helpers/constants'; import { ConversationRepository } from '../repositories/conversation.repository'; import { Block, BlockFull } from '../schemas/block.schema'; +import { ContextVar } from '../schemas/context-var.schema'; import { Conversation, ConversationFull, @@ -34,6 +37,8 @@ export class ConversationService extends BaseService< constructor( readonly repository: ConversationRepository, private readonly logger: LoggerService, + private readonly contextVarService: ContextVarService, + private readonly subscriberService: SubscriberService, ) { super(repository); } @@ -79,6 +84,7 @@ export class ConversationService extends BaseService< captureVars: boolean = false, ) { const msgType = event.getMessageType(); + const profile = event.getSender(); // Capture channel specific context data convo.context.channel = event.getHandler().getChannel(); convo.context.text = event.getText(); @@ -86,6 +92,18 @@ export class ConversationService extends BaseService< convo.context.nlp = event.getNLP(); convo.context.vars = convo.context.vars || {}; + const contextVars = ( + await this.contextVarService.find({ + name: { $in: next.capture_vars.map((c) => c.context_var) }, + }) + ).reduce( + (acc, cv) => { + acc[cv.name] = cv; + return acc; + }, + {} as Record, + ); + // Capture user entry in context vars if (captureVars && next.capture_vars && next.capture_vars.length > 0) { next.capture_vars.forEach((capture) => { @@ -121,12 +139,18 @@ export class ConversationService extends BaseService< contextValue = typeof contextValue === 'string' ? contextValue.trim() : contextValue; - convo.context.vars[capture.context_var] = contextValue; + if (contextVars[capture.context_var]?.permanent) { + Logger.debug( + `Adding context var to subscriber: ${capture.context_var} = ${contextValue}`, + ); + profile.context.vars[capture.context_var] = contextValue; + } else { + convo.context.vars[capture.context_var] = contextValue; + } }); } // Store user infos - const profile = event.getSender(); if (profile) { // @ts-expect-error : id needs to remain readonly convo.context.user.id = profile.id; @@ -182,6 +206,13 @@ export class ConversationService extends BaseService< 'Conversation Model : No conversation has been updated', ); } + + //TODO: add check if nothing changed don't update + + this.subscriberService.updateOne(convo.sender, { + context: profile.context, + }); + return updatedConversation; } catch (err) { this.logger.error('Conversation Model : Unable to store context', err);