wingbot 3.36.1 → 3.37.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -5,15 +5,17 @@ on:
5
5
  branches:
6
6
  - master
7
7
  - production
8
+ permissions:
9
+ contents: write
8
10
  jobs:
9
11
 
10
- test:
11
- name: test
12
+ test-and-build-gatsby:
13
+ name: test-and-deploy-documentation
12
14
  runs-on: ubuntu-latest
13
15
 
14
16
  strategy:
15
17
  matrix:
16
- node-version: [12.x]
18
+ node-version: [14.x]
17
19
 
18
20
  steps:
19
21
 
@@ -27,10 +29,24 @@ jobs:
27
29
  cache: 'npm'
28
30
 
29
31
  - name: Npm install
30
- run: npm install
32
+ run: npm install && cd documentation && npm install --legacy-peer-deps && cd ..
31
33
 
32
34
  - name: Test
33
35
  run: npm run test
36
+
37
+ - name: Rewrite gatsby confing inside smoothdoc
38
+ run: cp documentation/smooth-doc-gatsby-config.js documentation/node_modules/smooth-doc/gatsby-config.js
39
+
40
+ - name: Generate mdx documentation files
41
+ run: node bin/makeApiDoc
42
+
43
+ - name: Build documentation website with gatsby
44
+ run: cd documentation && npm run build
45
+
46
+ - name: Deploy documentation to github pages
47
+ uses: JamesIves/github-pages-deploy-action@v4
48
+ with:
49
+ folder: documentation/public # The folder the action should deploy.
34
50
 
35
51
  # deploy:
36
52
  # name: deploy
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wingbot",
3
- "version": "3.36.1",
3
+ "version": "3.37.1",
4
4
  "description": "Enterprise Messaging Bot Conversation Engine",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -117,7 +117,7 @@ class BotAppSender extends ReturnSender {
117
117
 
118
118
  headers.set('Authorization', token);
119
119
 
120
- const response = await this._fetch(this._apiUrl, {
120
+ const response = await this._fetch(`${this._apiUrl}/${this._pageId}`, {
121
121
  headers, body: formData, agent, method: 'POST'
122
122
  })
123
123
  .then((r) => r.json());
@@ -17,7 +17,12 @@ const { shouldExecuteResolver } = require('./resolvers/resolverTags');
17
17
 
18
18
  const MESSAGE_RESOLVER_NAME = 'botbuild.message';
19
19
 
20
- /** @typedef {import('./Router').Middleware} Middleware */
20
+ /**
21
+ *
22
+ * @template {object} [S=object]
23
+ * @template {object} [C=object]
24
+ * @typedef {import('./Router').Middleware<S,C>} Middleware
25
+ */
21
26
 
22
27
  /**
23
28
  * @typedef {object} Resolver
@@ -89,8 +94,7 @@ const DUMMY_ROUTE = { id: 0, path: null, resolvers: [] };
89
94
  /**
90
95
  * @typedef {object} RouteConfig
91
96
  * @prop {string} path
92
- * @prop {boolean} enabled
93
- * @prop {object} configuration
97
+ * @prop {boolean} [enabled]
94
98
  */
95
99
 
96
100
  /**
@@ -104,12 +108,13 @@ const DUMMY_ROUTE = { id: 0, path: null, resolvers: [] };
104
108
  */
105
109
 
106
110
  /**
111
+ * @template {object} [C=object]
107
112
  * @typedef {object} BotContext
108
113
  * @prop {LinkTranslator} [linksTranslator] - function, that translates links globally
109
114
  * @prop {ConfigStorage} [configStorage] - function, that translates links globally
110
115
  * @prop {boolean} [allowForbiddenSnippetWords] - disable security rule
111
116
  * @prop {RouteConfig[]} [routeConfigs] - list of disabled routes
112
- * @prop {object} [configuration] - context data
117
+ * @prop {C} [configuration] - context data
113
118
  * @prop {LinksMap} [linksMap]
114
119
  * @prop {boolean} [isLastIndex]
115
120
  * @prop {boolean} [isLastMessage]
@@ -127,24 +132,13 @@ const DUMMY_ROUTE = { id: 0, path: null, resolvers: [] };
127
132
  * @prop {object} [BuildRouter]
128
133
  */
129
134
 
130
- function deepFreeze (object) {
131
- const propNames = Object.getOwnPropertyNames(object);
132
-
133
- for (const name of propNames) {
134
- const value = object[name];
135
-
136
- if (value && typeof value === 'object') {
137
- deepFreeze(value);
138
- }
139
- }
140
-
141
- return Object.freeze(object);
142
- }
143
-
144
135
  /**
145
136
  * Build bot from Wingbot configuration file or snapshot url
146
137
  *
138
+ * @template {object} [S=object]
139
+ * @template {object} [C=object]
147
140
  * @class BuildRouter
141
+ * @extends {Router<S,C>}
148
142
  */
149
143
  class BuildRouter extends Router {
150
144
 
@@ -159,7 +153,7 @@ class BuildRouter extends Router {
159
153
  * @param {ConfigStorage} [context.configStorage] - function, that translates links globally
160
154
  * @param {boolean} [context.allowForbiddenSnippetWords] - disable security rule
161
155
  * @param {RouteConfig[]} [context.routeConfigs] - list of disabled routes
162
- * @param {object} [context.configuration]
156
+ * @param {C} [context.configuration]
163
157
  * @param {fetch} [fetchFn] - override a request function
164
158
  * @example
165
159
  *
@@ -190,9 +184,13 @@ class BuildRouter extends Router {
190
184
 
191
185
  this._plugins = plugins;
192
186
 
193
- /** @type {BotContext} */
187
+ /** @type {BotContext<C>} */
194
188
  this._context = context;
195
189
 
190
+ /** @type {C} */
191
+ // @ts-ignore
192
+ this._configuration = context.configuration || {};
193
+
196
194
  /** @type {LinksMap} */
197
195
  this._linksMap = new Map();
198
196
 
@@ -239,6 +237,13 @@ class BuildRouter extends Router {
239
237
  }
240
238
  }
241
239
 
240
+ /**
241
+ * @returns {C}
242
+ */
243
+ get configuration () {
244
+ return this._configuration;
245
+ }
246
+
242
247
  _validateBlock (block, action = 'build') {
243
248
  // @ts-ignore
244
249
  assert.ok(block && typeof block === 'object', `Bot ${action} failed: expected the block to be an object, ${typeof block} given`);
@@ -554,7 +559,7 @@ class BuildRouter extends Router {
554
559
  * @param {RouteConfig} routeConfig
555
560
  */
556
561
  _enabledByRouteConfig (routeConfig) {
557
- return !this._context.routeConfigs || (routeConfig && routeConfig.enabled);
562
+ return !this._context.routeConfigs || !routeConfig || routeConfig.enabled !== false;
558
563
  }
559
564
 
560
565
  _joinPaths (...args) {
@@ -567,7 +572,8 @@ class BuildRouter extends Router {
567
572
  : routePath;
568
573
 
569
574
  return joined
570
- .replace(/^\/?|(_responder)?\/?$/g, '/');
575
+ .replace(/^\/?|(_responder)?\/?$/g, '/')
576
+ .replace(/\/\/+$/, '/');
571
577
  }
572
578
 
573
579
  /**
@@ -624,6 +630,7 @@ class BuildRouter extends Router {
624
630
  }
625
631
 
626
632
  const routePath = this._normalizeConfigPath(rPath, ctxPath);
633
+
627
634
  return routeConfigs.find((config) => {
628
635
  const configPath = this._normalizeConfigPath(config.path);
629
636
 
@@ -633,27 +640,10 @@ class BuildRouter extends Router {
633
640
 
634
641
  /**
635
642
  *
636
- * @param {string} routePath
637
643
  * @returns {object}
638
644
  */
639
- getConfiguration (routePath) {
640
- const { path: ctxPath, routeConfigs, configuration = {} } = this._context;
641
- if (!routeConfigs || !routePath) {
642
- return deepFreeze(configuration);
643
- }
644
- const normalized = this._normalizeConfigPath(routePath, ctxPath);
645
-
646
- const configs = routeConfigs
647
- .filter((config) => {
648
- const configPath = this._normalizeConfigPath(config.path);
649
- return normalized.startsWith(configPath);
650
- })
651
- .sort((a, z) => a.path.length - z.path.length);
652
-
653
- return deepFreeze(configs.reduce((cfg, config) => ({
654
- ...cfg,
655
- ...config.configuration
656
- }), configuration));
645
+ getConfiguration () {
646
+ return this._configuration;
657
647
  }
658
648
 
659
649
  /**
@@ -683,7 +673,7 @@ class BuildRouter extends Router {
683
673
  *
684
674
  * @param {TransformedRoute} route
685
675
  * @param {boolean} nextRouteIsSameResponder
686
- * @returns {Middleware[]}
676
+ * @returns {Middleware<S,C>[]}
687
677
  */
688
678
  _buildRouteHead (route, nextRouteIsSameResponder) {
689
679
  const resolvers = [];
@@ -798,24 +788,17 @@ class BuildRouter extends Router {
798
788
  * @param {Resolver[]} resolvers
799
789
  * @param {TransformedRoute} route
800
790
  * @param {*} buildInfo
801
- * @returns {Middleware[]}
791
+ * @returns {Middleware<S,C>[]}
802
792
  */
803
793
  buildResolvers (resolvers, route = DUMMY_ROUTE, buildInfo = {}) {
804
794
  const {
805
795
  path: ctxPath, isFallback, isResponder, expectedPath, id
806
796
  } = route;
807
797
 
808
- const routeConfig = this._getRouteConfig(route);
809
- const routeConfigData = routeConfig && routeConfig.configuration;
810
-
811
798
  const lastMessageIndex = this._lastMessageIndex(resolvers);
812
799
  const lastIndex = resolvers.length - 1;
813
800
 
814
801
  return resolvers.map((resolver, i) => {
815
- const configuration = deepFreeze({
816
- ...this._context.configuration,
817
- ...routeConfigData
818
- });
819
802
 
820
803
  const context = {
821
804
  ...this._context,
@@ -828,13 +811,16 @@ class BuildRouter extends Router {
828
811
  isResponder,
829
812
  expectedPath,
830
813
  routeId: id,
831
- configuration
814
+ configuration: this._configuration
832
815
  };
833
816
 
834
817
  const resFn = this._resolverFactory(resolver, context, buildInfo);
835
818
 
836
819
  // @ts-ignore
837
- resFn.configuration = configuration;
820
+ if (typeof resFn.configuration === 'undefined') {
821
+ // @ts-ignore
822
+ resFn.configuration = this._configuration;
823
+ }
838
824
  return resFn;
839
825
  });
840
826
  }
@@ -842,9 +828,9 @@ class BuildRouter extends Router {
842
828
  /**
843
829
  *
844
830
  * @param {Resolver} resolver
845
- * @param {BotContext} context
831
+ * @param {BotContext<C>} context
846
832
  * @param {*} buildInfo
847
- * @returns {Middleware}
833
+ * @returns {Middleware<S,C>}
848
834
  */
849
835
  _resolverFactory (resolver, context, buildInfo) {
850
836
  const { type } = resolver;
@@ -906,9 +892,10 @@ class BuildRouter extends Router {
906
892
  }
907
893
 
908
894
  /**
895
+ * @template {object} [C=object]
909
896
  * @param {Block[]} blocks - blocks list
910
897
  * @param {Plugins} [plugins]
911
- * @param {BotContext} [context]
898
+ * @param {BotContext<C>} [context]
912
899
  */
913
900
  BuildRouter.fromData = function fromData (blocks, plugins = new Plugins(), context = {}) {
914
901
 
@@ -69,11 +69,15 @@ const DEFAULT_TEXT_THRESHOLD = 0.8;
69
69
  * @prop {List[]} lists
70
70
  */
71
71
 
72
+ /** @typedef {import('./ReducerWrapper')} ReducerWrapper */
73
+ /** @typedef {import('./Router')} Router */
74
+ /** @typedef {import('./BuildRouter')} BuildRouter */
75
+
72
76
  /**
73
77
  * Callback for getting a tester
74
78
  *
75
79
  * @callback testerFactory
76
- * @param {Router|ReducerWrapper} bot - the chatbot itself
80
+ * @param {Router|ReducerWrapper|BuildRouter} bot - the chatbot itself
77
81
  * @param {TestsGroup} test - the chatbot itself
78
82
  * @returns {Tester}
79
83
  */
@@ -90,6 +94,12 @@ const DEFAULT_TEXT_THRESHOLD = 0.8;
90
94
  * @prop {number} stepCount
91
95
  */
92
96
 
97
+ /**
98
+ * @typedef {object} Logger
99
+ * @prop {Function} log
100
+ * @prop {Function} error
101
+ */
102
+
93
103
  /**
94
104
  * Automated Conversation tests runner
95
105
  */
@@ -109,6 +119,7 @@ class ConversationTester {
109
119
  * @param {number} [options.textCasesPerStep]
110
120
  * @param {number} [options.textCaseParallel]
111
121
  * @param {boolean} [options.allowEmptyResponse]
122
+ * @param {Logger} [options.log]
112
123
  * @param {testerFactory} [options.testerFactory]
113
124
  */
114
125
  constructor (testsSource, botFactory, options = {}) {
@@ -122,6 +133,7 @@ class ConversationTester {
122
133
  };
123
134
  this._output = '';
124
135
  this._cachedBot = null;
136
+ this._log = options.log || console;
125
137
  }
126
138
 
127
139
  /**
@@ -159,18 +171,21 @@ class ConversationTester {
159
171
  this._cachedBot = null;
160
172
  }
161
173
  this._output = '';
162
- const testCases = await this._getTestCases(lang);
163
- const groups = this._getGroups(testCases);
164
- const testsGroups = this._getTestsGroups(groups, step);
165
- const stepCount = Number.isInteger(step) ? groups.length : 1;
166
174
 
167
175
  const botconfig = validationRequestBody;
168
176
  let failed = 0;
169
177
  let passed = 0;
170
178
  let skipped = 0;
171
179
  let summaryOutput = '';
180
+ let stepCount = 0;
181
+ let testsGroups = [];
172
182
 
173
183
  try {
184
+ const testCases = await this._getTestCases(lang);
185
+ const groups = this._getGroups(testCases);
186
+ testsGroups = this._getTestsGroups(groups, step);
187
+ stepCount = Number.isInteger(step) ? groups.length : 1;
188
+
174
189
  const results = [];
175
190
  for (const testsGroup of testsGroups) {
176
191
  let stepResult;
@@ -224,6 +239,7 @@ class ConversationTester {
224
239
  }
225
240
 
226
241
  } catch (e) {
242
+ this._log.error('#Tester failed', e, { output: this._output, passed });
227
243
  this._output += `\nBot test failed: ${e.message}\n`;
228
244
  }
229
245
  return {
package/src/Plugins.js CHANGED
@@ -11,16 +11,39 @@ const RouterWrap = require('./RouterWrap');
11
11
  const wrapPluginFunction = require('./utils/wrapPluginFunction');
12
12
 
13
13
  /**
14
+ * @template {object} [S=object]
15
+ * @template {object} [C=object]
16
+ * @typedef {import('./Router').Middleware<S,C>} Middleware
17
+ */
18
+
19
+ /**
20
+ *
21
+ * @template {object} [S=object]
22
+ * @template {object} [C=object]
14
23
  * @callback Plugin
15
- * @param {Request} req
24
+ * @param {Request<S,C>} req
16
25
  * @param {Responder} res
17
26
  * @param {Function} [postBack]
18
27
  * @param {{isLastIndex:boolean,path:string,expectedPath:string}} [context]
19
28
  * @param {object} [paramsData]
20
29
  */
21
30
 
31
+ /**
32
+ *
33
+ * @template {object} [S=object]
34
+ * @template {object} [C=object]
35
+ * @callback PluginFactory
36
+ * @param {object} params
37
+ * @param {C} configuration
38
+ * @returns {Router<S,C>|Middleware<S,C>}
39
+ */
40
+
22
41
  /**
23
42
  * Custom code plugins for BuildRouter and wingbot.ai
43
+ *
44
+ * @template {object} [S=object]
45
+ * @template {object} C=Object
46
+ * @class Plugins
24
47
  */
25
48
  class Plugins {
26
49
 
@@ -28,7 +51,7 @@ class Plugins {
28
51
  this._plugins = new Map();
29
52
  }
30
53
 
31
- getPluginFactory (name, paramsData = {}) {
54
+ getPluginFactory (name, paramsData = {}, configuration = {}) {
32
55
  let plugin;
33
56
  if (plugins.has(name)) {
34
57
  plugin = plugins.get(name);
@@ -38,7 +61,7 @@ class Plugins {
38
61
  plugin = this._plugins.get(name);
39
62
  }
40
63
  if (plugin && plugin.pluginFactory) {
41
- return plugin.pluginFactory(paramsData);
64
+ return plugin.pluginFactory(paramsData, configuration);
42
65
  }
43
66
  return plugin;
44
67
  }
@@ -115,8 +138,8 @@ class Plugins {
115
138
  /**
116
139
  * Register plugin
117
140
  *
118
- * @param {string|Plugins} name - plugin name or plugins object to include
119
- * @param {Plugin|Router} [plugin] - plugin - optional when including plugin object
141
+ * @param {string|Plugins<S,C>} name - plugin name or plugins object to include
142
+ * @param {Plugin<S,C>|Router<S,C>} [plugin] - plugin - optional when including plugin object
120
143
  */
121
144
  register (name, plugin) {
122
145
  if (typeof name === 'string') {
@@ -137,7 +160,7 @@ class Plugins {
137
160
  * Register plugin factory
138
161
  *
139
162
  * @param {string} name - plugin name or plugins object to include
140
- * @param {Function} pluginFactory - function, which returns a plugin
163
+ * @param {PluginFactory<S,C>} pluginFactory - function, which returns a plugin
141
164
  */
142
165
  registerFactory (name, pluginFactory) {
143
166
  if (typeof pluginFactory !== 'function') {
package/src/Processor.js CHANGED
@@ -499,6 +499,11 @@ class Processor extends EventEmitter {
499
499
 
500
500
  // prepare request and responder
501
501
  let { state } = stateObject;
502
+ let configuration = {};
503
+ if ('getConfiguration' in this.reducer) {
504
+ configuration = this.reducer.getConfiguration();
505
+ }
506
+
502
507
  // @ts-ignore
503
508
  req = new Request(
504
509
  message,
@@ -511,7 +516,8 @@ class Processor extends EventEmitter {
511
516
  secret: this.options.secret,
512
517
  fetch: this.options.fetch,
513
518
  appId: responderData.appId
514
- }
519
+ },
520
+ configuration
515
521
  );
516
522
 
517
523
  const features = [
@@ -549,11 +555,6 @@ class Processor extends EventEmitter {
549
555
  res.setBookmark(aByAi);
550
556
  }
551
557
 
552
- if ('getConfiguration' in this.reducer) {
553
- const configuration = this.reducer.getConfiguration(state._lastAction);
554
- req.configuration = Object.freeze(configuration);
555
- }
556
-
557
558
  // process setState
558
559
  const setState = req.getSetState(req.AI_SETSTATE.EXCLUDE_WITH_SET_ENTITIES);
559
560
  await Ai.ai.processSetStateEntities(req, setState);
package/src/Request.js CHANGED
@@ -116,18 +116,29 @@ function makeTimestamp () {
116
116
  /**
117
117
  * Instance of {Request} class is passed as first parameter of handler (req)
118
118
  *
119
- * @class
119
+ * @template {object} [S=object]
120
+ * @template {object} [C=object]
121
+ * @class Request
120
122
  */
121
123
  class Request {
122
124
 
123
125
  /**
124
126
  * @param {*} event
125
- * @param {*} state
127
+ * @param {S} state
126
128
  * @param {string} pageId
127
129
  * @param {Map} globalIntents
128
130
  * @param {RequestOrchestratorOptions} [orchestratorOptions]
131
+ * @param {C} [configuration]
129
132
  */
130
- constructor (event, state, pageId, globalIntents = new Map(), orchestratorOptions = {}) {
133
+ constructor (
134
+ event,
135
+ state,
136
+ pageId,
137
+ globalIntents = new Map(),
138
+ orchestratorOptions = {},
139
+ // @ts-ignore
140
+ configuration = {}
141
+ ) {
131
142
  this.campaign = event.campaign || null;
132
143
 
133
144
  this.taskId = event.taskId || null;
@@ -188,7 +199,7 @@ class Request {
188
199
  this.pageId = pageId;
189
200
 
190
201
  /**
191
- * @prop {object} state current state of the conversation
202
+ * @type {S} current state of the conversation
192
203
  */
193
204
  this.state = state;
194
205
 
@@ -265,8 +276,8 @@ class Request {
265
276
  */
266
277
  this.FEATURE_TRACKING = FEATURE_TRACKING;
267
278
 
268
- /** @type {object} */
269
- this.configuration = Object.freeze({});
279
+ /** @type {C} */
280
+ this.configuration = configuration;
270
281
  }
271
282
 
272
283
  get data () {
package/src/Router.js CHANGED
@@ -15,8 +15,11 @@ function defaultPathContext () {
15
15
  }
16
16
 
17
17
  /**
18
+ *
19
+ * @template {object} [S=object]
20
+ * @template {object} [C=object]
18
21
  * @callback Resolver processing function
19
- * @param {Request} [req]
22
+ * @param {Request<S,C>} [req]
20
23
  * @param {Responder} [res]
21
24
  * @param {Function} [postBack]
22
25
  */
@@ -32,12 +35,21 @@ function defaultPathContext () {
32
35
  */
33
36
 
34
37
  /**
35
- * @typedef {Resolver|string|RegExp|IRouter|BotPath} Middleware flow control statement or function
38
+ * @typedef {string|RegExp|BotPath} RouteExp
39
+ */
40
+
41
+ /**
42
+ *
43
+ * @template {object} [S=object]
44
+ * @template {object} [C=object]
45
+ * @typedef {Resolver<S,C>|RouteExp|IRouter} Middleware flow control statement or function
36
46
  */
37
47
 
38
48
  /**
39
49
  * Cascading router
40
50
  *
51
+ * @template {object} [S=object]
52
+ * @template {object} [C=object]
41
53
  * @class Router
42
54
  * @extends {ReducerWrapper}
43
55
  */
@@ -67,7 +79,7 @@ class Router extends ReducerWrapper {
67
79
  /**
68
80
  * Appends middleware, action handler or another router
69
81
  *
70
- * @param {...Middleware|Middleware[]} resolvers - list of resolvers
82
+ * @param {...Middleware<S,C>|Middleware<S,C>[]} resolvers - list of resolvers
71
83
  * @returns {this}
72
84
  *
73
85
  * @example
@@ -11,6 +11,8 @@ const ConversationTester = require('../ConversationTester');
11
11
  * @prop {Function} getTestCases
12
12
  */
13
13
 
14
+ /** @typedef {import('../ConversationTester').Logger} Logger */
15
+
14
16
  /**
15
17
  * Returns API for conversations testing
16
18
  *
@@ -26,6 +28,8 @@ const ConversationTester = require('../ConversationTester');
26
28
  * @param {number} [options.textCasesPerStep]
27
29
  * @param {number} [options.textCaseParallel]
28
30
  * @param {boolean} [options.allowEmptyResponse]
31
+ * @param {Logger} [options.log]
32
+ *
29
33
  * @param {string[]|Function} [acl] - limit api to array of groups or use auth function
30
34
  */
31
35
  function conversationTestApi (testsSource, botFactory, options, acl) {