From 77f23b3c05fac2b95a811d7e8ea823efb608c712 Mon Sep 17 00:00:00 2001 From: Julien Date: Tue, 16 Aug 2016 11:03:59 -0700 Subject: [PATCH] reset when new messages or errors (#78) * reset when new messages or errors * addressing feedback, fixing 'this' reference * address feedback about interactive doc updates (#79) * reset when new messages or errors * addressing feedback, fixing 'this' reference * updating CHANGES to highlight converse update * restoring arrow notation --- CHANGES.md | 6 +++--- README.md | 2 ++ lib/wit.js | 57 ++++++++++++++++++++++++++++++++++++++++-------------- 3 files changed, 47 insertions(+), 18 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 19e1aed..959cbd8 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,10 +1,10 @@ -## v4.1.0 - - Support for different JS environments +- `converse` now takes `reset` as an optional parameter ### Breaking changes -- interactive is no longer a function on the `Wit` client. Instead, you require it from the library. `require('node-wit').interactive` +- `interactive` is no longer a function on the `Wit` client. Instead, you require it from the library: `require('node-wit').interactive` +- `runActions` now resets the last turn on new messages and errors. ## v4.0.0 diff --git a/README.md b/README.md index 701fb34..795c9d9 100644 --- a/README.md +++ b/README.md @@ -110,6 +110,7 @@ client.message('what is the weather in London?', {}) ### runActions A higher-level method to the Wit converse API. +`runActions` resets the last turn on new messages and errors. Takes the following parameters: * `sessionId` - a unique identifier describing the user session @@ -145,6 +146,7 @@ Takes the following parameters: * `sessionId` - a unique identifier describing the user session * `message` - the text received from the user * `context` - the object representing the session state +* `reset` - (optional) whether to reset the last turn Example: ```js diff --git a/lib/wit.js b/lib/wit.js index 370204f..f1e201b 100644 --- a/lib/wit.js +++ b/lib/wit.js @@ -20,6 +20,8 @@ function Wit(opts) { accessToken, apiVersion, actions, headers, logger, witURL } = this.config = Object.freeze(validate(opts)); + this._sessions = {}; + this.message = (message, context) => { let qs = 'q=' + encodeURIComponent(message); if (context) { @@ -38,11 +40,14 @@ function Wit(opts) { ; }; - this.converse = (sessionId, message, context) => { + this.converse = (sessionId, message, context, reset) => { let qs = 'session_id=' + encodeURIComponent(sessionId); if (message) { qs += '&q=' + encodeURIComponent(message); } + if (reset) { + qs += '&reset=true'; + } const method = 'POST'; const fullURL = witURL + '/converse?' + qs; const handler = makeWitResponseHandler(logger, 'converse'); @@ -57,12 +62,15 @@ function Wit(opts) { ; }; - const continueRunActions = (sessionId, message, prevContext, i) => { + const continueRunActions = (sessionId, currentRequest, message, prevContext, i) => { return (json) => { if (i < 0) { logger.warn('Max steps reached, stopping.'); return prevContext; } + if (currentRequest !== this._sessions[sessionId]) { + return prevContext; + } if (!json.type) { throw new Error('Couldn\'t find type in Wit response'); } @@ -70,7 +78,7 @@ function Wit(opts) { logger.debug('Context: ' + JSON.stringify(prevContext)); logger.debug('Response type: ' + json.type); - // backwards-cpmpatibility with API version 20160516 + // backwards-compatibility with API version 20160516 if (json.type === 'merge') { json.type = 'action'; json.action = 'merge'; @@ -97,36 +105,55 @@ function Wit(opts) { text: json.msg, quickreplies: json.quickreplies, }; - return actions.send(request, response).then((ctx) => { + return actions.send(request, response).then(ctx => { if (ctx) { throw new Error('Cannot update context after \'send\' action'); } + if (currentRequest !== this._sessions[sessionId]) { + return ctx; + } return this.converse(sessionId, null, prevContext).then( - continueRunActions(sessionId, message, prevContext, i - 1) + continueRunActions(sessionId, currentRequest, message, prevContext, i - 1) ); }); } else if (json.type === 'action') { - const action = json.action; - throwIfActionMissing(actions, action); - return actions[action](request).then((ctx) => { + throwIfActionMissing(actions, json.action); + return actions[json.action](request).then(ctx => { const nextContext = ctx || {}; + if (currentRequest !== this._sessions[sessionId]) { + return nextContext; + } return this.converse(sessionId, null, nextContext).then( - continueRunActions(sessionId, message, nextContext, i - 1) + continueRunActions(sessionId, currentRequest, message, nextContext, i - 1) ); }); } else { - logger.debug('unknown response type', json); + logger.debug('unknown response type ' + json.type); throw new Error('unknown response type ' + json.type); } - } + }; }; - this.runActions = (sessionId, message, context, maxSteps) => { + this.runActions = function(sessionId, message, context, maxSteps) { if (!actions) throwMustHaveActions(); const steps = maxSteps ? maxSteps : DEFAULT_MAX_STEPS; - return this.converse(sessionId, message, context).then( - continueRunActions(sessionId, message, context, steps) - ); + + // Figuring out whether we need to reset the last turn. + // Each new call increments an index for the session. + // We only care about the last call to runActions. + // All the previous ones are discarded (preemptive exit). + const currentRequest = (this._sessions[sessionId] || 0) + 1; + this._sessions[sessionId] = currentRequest; + const cleanup = ctx => { + if (currentRequest === this._sessions[sessionId]) { + delete this._sessions[sessionId]; + } + return ctx; + }; + + return this.converse(sessionId, message, context, currentRequest > 1).then( + continueRunActions(sessionId, currentRequest, message, context, steps) + ).then(cleanup); }; };