diff --git a/src/bp/core/api.ts b/src/bp/core/api.ts index bfdf5d99dd0..b2115127832 100644 --- a/src/bp/core/api.ts +++ b/src/bp/core/api.ts @@ -27,6 +27,7 @@ import { JobService } from './services/job-service' import { KeyValueStore } from './services/kvs' import MediaService from './services/media' import { EventEngine } from './services/middleware/event-engine' +import { StateManager } from './services/middleware/state-manager' import { NotificationsService } from './services/notification/service' import RealtimeService from './services/realtime' import { WorkspaceService } from './services/workspace-service' @@ -76,20 +77,12 @@ const event = (eventEngine: EventEngine, eventRepo: EventRepository): typeof sdk } } -const dialog = (dialogEngine: DialogEngine, sessionRepo: SessionRepository): typeof sdk.dialog => { +const dialog = (dialogEngine: DialogEngine, stateManager: StateManager): typeof sdk.dialog => { return { - createId(eventDestination: sdk.IO.EventDestination) { - return SessionIdFactory.createIdFromEvent(eventDestination) - }, - async processEvent(sessionId: string, event: sdk.IO.IncomingEvent): Promise { - return dialogEngine.processEvent(sessionId, event) - }, - async deleteSession(userId: string): Promise { - await sessionRepo.delete(userId) - }, - async jumpTo(sessionId: string, event: any, flowName: string, nodeName?: string): Promise { - await dialogEngine.jumpTo(sessionId, event, flowName, nodeName) - } + createId: SessionIdFactory.createIdFromEvent.bind(SessionIdFactory), + processEvent: dialogEngine.processEvent.bind(dialogEngine), + deleteSession: stateManager.deleteDialogSession.bind(stateManager), + jumpTo: dialogEngine.jumpTo.bind(dialogEngine) } } @@ -277,7 +270,6 @@ export class BotpressAPIProvider { @inject(TYPES.HTTPServer) httpServer: HTTPServer, @inject(TYPES.UserRepository) userRepo: UserRepository, @inject(TYPES.RealtimeService) realtimeService: RealtimeService, - @inject(TYPES.SessionRepository) sessionRepo: SessionRepository, @inject(TYPES.KeyValueStore) keyValueStore: KeyValueStore, @inject(TYPES.NotificationsService) notificationService: NotificationsService, @inject(TYPES.BotService) botService: BotService, @@ -288,11 +280,12 @@ export class BotpressAPIProvider { @inject(TYPES.HookService) hookService: HookService, @inject(TYPES.EventRepository) eventRepo: EventRepository, @inject(TYPES.WorkspaceService) workspaceService: WorkspaceService, - @inject(TYPES.JobService) jobService: JobService + @inject(TYPES.JobService) jobService: JobService, + @inject(TYPES.StateManager) stateManager: StateManager ) { this.http = http(httpServer) this.events = event(eventEngine, eventRepo) - this.dialog = dialog(dialogEngine, sessionRepo) + this.dialog = dialog(dialogEngine, stateManager) this.config = config(moduleLoader, configProvider) this.realtime = new RealTimeAPI(realtimeService) this.database = db.knex diff --git a/src/bp/core/services/middleware/state-manager.ts b/src/bp/core/services/middleware/state-manager.ts index 309c0c83477..48ae3e60834 100644 --- a/src/bp/core/services/middleware/state-manager.ts +++ b/src/bp/core/services/middleware/state-manager.ts @@ -28,6 +28,7 @@ export class StateManager { private batch!: { event: sdk.IO.IncomingEvent; ignoreContext?: boolean }[] private knex!: Knex & KnexExtension private currentPromise + private useRedis constructor( @inject(TYPES.Logger) @@ -39,10 +40,12 @@ export class StateManager { @inject(TYPES.KeyValueStore) private kvs: KeyValueStore, @inject(TYPES.Database) private database: Database, @inject(TYPES.JobService) private jobService: JobService - ) {} + ) { + this.useRedis = process.CLUSTER_ENABLED && !process.core_env.BP_NO_REDIS_STATE + } public initialize() { - if (!process.CLUSTER_ENABLED || process.core_env.BP_NO_REDIS_STATE) { + if (!this.useRedis) { return } @@ -63,7 +66,7 @@ export class StateManager { public async restore(event: sdk.IO.IncomingEvent) { const sessionId = SessionIdFactory.createIdFromEvent(event) - if (process.CLUSTER_ENABLED && !process.core_env.BP_NO_REDIS_STATE) { + if (this.useRedis) { try { const userState = await this._redisClient.get(getRedisSessionKey(sessionId)) if (userState) { @@ -93,10 +96,10 @@ export class StateManager { public async persist(event: sdk.IO.IncomingEvent, ignoreContext: boolean) { const sessionId = SessionIdFactory.createIdFromEvent(event) - if (process.CLUSTER_ENABLED && !process.core_env.BP_NO_REDIS_STATE) { + if (this.useRedis) { await this._redisClient.set( getRedisSessionKey(sessionId), - JSON.stringify(event.state), + JSON.stringify(_.omit(event.state, ['__stacktrace', '__error'])), 'PX', REDIS_MEMORY_DURATION ) @@ -107,6 +110,14 @@ export class StateManager { await this._saveState(event, ignoreContext) } + public async deleteDialogSession(sessionId: string) { + await this.sessionRepo.delete(sessionId) + + if (this.useRedis) { + await this._redisClient.del(getRedisSessionKey(sessionId)) + } + } + private async _saveState(event: sdk.IO.IncomingEvent, ignoreContext?: boolean, trx?: Knex.Transaction) { const { user, context, session, temp } = event.state const sessionId = SessionIdFactory.createIdFromEvent(event) diff --git a/src/bp/sdk/botpress.d.ts b/src/bp/sdk/botpress.d.ts index 69167e92e01..72113309ab4 100644 --- a/src/bp/sdk/botpress.d.ts +++ b/src/bp/sdk/botpress.d.ts @@ -1437,7 +1437,12 @@ declare module 'botpress/sdk' { * @param nodeName The name of the optionnal node to jump to. * The node will default to the starting node of the flow if this value is omitted. */ - export function jumpTo(sessionId: string, event: IO.Event, flowName: string, nodeName?: string): Promise + export function jumpTo( + sessionId: string, + event: IO.IncomingEvent, + flowName: string, + nodeName?: string + ): Promise } export namespace config {