UNPKG

6.03 kBJavaScriptView Raw
1"use strict";
2Object.defineProperty(exports, "__esModule", { value: true });
3const Promise = require("bluebird");
4const botbuilder_1 = require("botbuilder");
5/**
6 * Service that provides session for addresses. Injects code into a Universal bot to allow session save and load listeners to work
7 */
8class SessionService {
9 //tslint:enable
10 constructor(bot) {
11 this.bot = bot;
12 this.applySessionSaveListener();
13 this.applySessionLoadListener();
14 this.savePerformed = Promise.resolve();
15 }
16 /**
17 * fetches a session for a particular address
18 *
19 * @param addr address of session to load
20 */
21 getSession(addr) {
22 return new Promise((res, rej) => {
23 this.createSessionWrapperWithLoadMessageOverride(addr);
24 this.currentSessionLoadResolver = res;
25 this.loadSession(addr);
26 }).bind(this);
27 }
28 /**
29 * a message internal to the BotTester framework. Due limitations in the botbuilder framework, this message is sent to the bot whenever
30 * session.save is called. This allows the framework to know when session.save is called and resolve accordingly. Without this, the
31 * session state may not be persisted before the next test step is run
32 * @param address address that the save message should come from
33 */
34 getInternalSaveMessage(address) {
35 const saveEvent = new botbuilder_1.Message()
36 .address(address)
37 .toMessage();
38 saveEvent.type = '__save__';
39 return saveEvent;
40 }
41 /**
42 * This is a delicate hack that relies on accessing and modifying the private field createSession on UniversalBot.
43 * This makes the bot's next createSession call result in a session with a message of type 'load'. This allows the sessionLoadListener
44 * to know if a message that the bot thinks it received was actually the result of a call to bot.createSession
45 * @param addr address of the message that is being loaded
46 */
47 createSessionWrapperWithLoadMessageOverride(addr) {
48 // tslint:disable
49 const createSessionOriginal = this.bot['createSession'];
50 this.bot['createSession'] = function () {
51 //tslint:enable
52 const createSessionArgs = Array.prototype.slice.call(arguments);
53 const loadMsg = new botbuilder_1.Message()
54 .address(addr)
55 .toMessage();
56 loadMsg.type = 'load';
57 createSessionArgs[1] = loadMsg;
58 //tslint:disable
59 this.bot['createSession'] = createSessionOriginal;
60 createSessionOriginal.apply(this.bot, createSessionArgs);
61 }.bind(this);
62 }
63 /**
64 * Loads a session associated with an address.
65 * @param address address to be loaded
66 */
67 loadSession(address) {
68 this.savePerformed
69 .then(() => {
70 //tslint:disable
71 // this callback will never actually get called, but it sets off the events allowing
72 // for the encapsulating promise to resolve
73 this.bot.loadSession(address, (a) => { });
74 //tslint:enable
75 });
76 }
77 /**
78 * adds middleware to the bot that checks for incoming load messages sent by createSessionWrapperWithLoadMessageOverride's
79 * bot.createSession wrapper. This lets us know that the message was never meant to go through the bot's middelware and ignore it.
80 * The session loaded into this message is then used as the value that the Promise returned from getSession resolves to
81 */
82 applySessionLoadListener() {
83 this.bot.use({
84 botbuilder: (session, next) => {
85 // TODO add in address comparison with address encoded in createSessionWrapperWithLoadMessageOverride's wrapper to
86 // createSession
87 if (session.message.type === 'load') {
88 //tslint:disable
89 // its not actually supposed to be in the middleware, so unset this
90 session['inMiddleware'] = false;
91 //tslint:enable
92 this.currentSessionLoadResolver(session);
93 }
94 else {
95 next();
96 }
97 }
98 });
99 }
100 /**
101 * Adds a routing event listner which is the first execution path called after a session has been successfully loaded. If this session
102 * has not already gone through this listener, then it wraps session.save in a function that sends the internal save message that is
103 * mocked to be sent to the user, and thereby intercepted by the MessageService. This ensures that the test runner does not continue
104 * preemptively. When the session's save method is called, the message is actually sent and alerts the BotTester framework
105 */
106 applySessionSaveListener() {
107 // this is a critical hack that attaches an extra field onto session. Gonna just ignore this lint issue for now
108 this.bot.on('routing', (session) => {
109 // tslint:enable
110 // if session.saveUpdated === true, then we should ignore this session being routed because it has already had its save method
111 // hijacked
112 if (!session.saveUpdated) {
113 session.saveUpdated = true;
114 const saveEvent = this.getInternalSaveMessage(session.message.address);
115 const save = session.save.bind(session);
116 session.save = function () {
117 save();
118 session.send(saveEvent);
119 // ensure that the state is saved before any session loading occurs (if use calls getSession, we want it to
120 // load only after the session state is saved)
121 this.savePerformed = Promise.promisify(session.sendBatch).call(session);
122 return session;
123 };
124 }
125 });
126 }
127}
128exports.SessionService = SessionService;
129//# sourceMappingURL=SessionService.js.map
\No newline at end of file