botium-core 1.11.16 → 1.12.3

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.
Files changed (42) hide show
  1. package/dist/botium-cjs.js +493 -202
  2. package/dist/botium-cjs.js.map +1 -1
  3. package/dist/botium-es.js +484 -194
  4. package/dist/botium-es.js.map +1 -1
  5. package/index.js +1 -0
  6. package/package.json +29 -29
  7. package/samples/connectors/custom/botium-connector-myapi.js +3 -3
  8. package/samples/extensions/asserterHooks/DummyAsserter.js +3 -3
  9. package/src/BotDriver.js +3 -3
  10. package/src/Capabilities.js +9 -0
  11. package/src/Defaults.js +1 -1
  12. package/src/Enums.js +6 -0
  13. package/src/containers/BaseContainer.js +20 -10
  14. package/src/containers/PluginConnectorContainer.js +1 -0
  15. package/src/containers/plugins/SimpleRestContainer.js +98 -30
  16. package/src/scripting/BotiumError.js +21 -0
  17. package/src/scripting/CompilerCsv.js +1 -1
  18. package/src/scripting/CompilerObjectBase.js +4 -14
  19. package/src/scripting/CompilerTxt.js +4 -15
  20. package/src/scripting/CompilerXlsx.js +81 -25
  21. package/src/scripting/Convo.js +16 -4
  22. package/src/scripting/ScriptingProvider.js +6 -0
  23. package/src/scripting/helper.js +54 -1
  24. package/src/scripting/logichook/LogicHookUtils.js +2 -0
  25. package/src/scripting/logichook/asserter/JsonPathAsserter.js +1 -1
  26. package/src/scripting/logichook/logichooks/ClearQueueLogicHook.js +1 -1
  27. package/src/scripting/logichook/userinput/MediaInput.js +14 -2
  28. package/test/connectors/convos/hello.convo.txt +6 -0
  29. package/test/connectors/simplerest.spec.js +106 -2
  30. package/test/convo/convos/continuefailing.convo.txt +19 -0
  31. package/test/convo/transcript.spec.js +34 -0
  32. package/test/scripting/scriptingProvider.spec.js +4 -4
  33. package/test/scripting/scriptingmemory/convosMultiMemorySameCols/buy.convo.txt +6 -0
  34. package/test/scripting/scriptingmemory/convosMultiMemorySameCols/products1.scriptingmemory.txt +2 -0
  35. package/test/scripting/scriptingmemory/convosMultiMemorySameCols/products2.scriptingmemory.txt +2 -0
  36. package/test/scripting/scriptingmemory/convosSimpleCols/buy.convo.txt +8 -0
  37. package/test/scripting/scriptingmemory/convosSimpleCols/product.scriptingmemory.txt +3 -0
  38. package/test/scripting/scriptingmemory/convosTwoTablesCols/buy.convo.txt +6 -0
  39. package/test/scripting/scriptingmemory/convosTwoTablesCols/customer.xlsx +0 -0
  40. package/test/scripting/scriptingmemory/convosTwoTablesCols/product.xlsx +0 -0
  41. package/test/scripting/scriptingmemory/fillScriptingMemoryFromFile.spec.js +45 -0
  42. package/test/scripting/userinputs/mediaInputConvos.spec.js +53 -2
package/index.js CHANGED
@@ -4,6 +4,7 @@ module.exports = {
4
4
  ScriptingConstants: require('./src/scripting/Constants'),
5
5
  Capabilities: require('./src/Capabilities'),
6
6
  Defaults: require('./src/Defaults'),
7
+ Enums: require('./src/Enums'),
7
8
  Source: require('./src/Source'),
8
9
  Events: require('./src/Events'),
9
10
  Plugins: require('./src/Plugins'),
package/package.json CHANGED
@@ -1,11 +1,11 @@
1
1
  {
2
2
  "name": "botium-core",
3
- "version": "1.11.16",
3
+ "version": "1.12.3",
4
4
  "description": "The Selenium for Chatbots",
5
5
  "main": "index.js",
6
6
  "module": "dist/botium-es.js",
7
7
  "engines": {
8
- "node": ">=10.0.0"
8
+ "node": ">=14.0.0"
9
9
  },
10
10
  "scripts": {
11
11
  "postinstall": "node ./report.js",
@@ -32,23 +32,23 @@
32
32
  },
33
33
  "homepage": "https://www.botium.ai",
34
34
  "dependencies": {
35
- "@babel/runtime": "^7.16.0",
36
- "async": "^3.2.2",
37
- "body-parser": "^1.19.0",
35
+ "@babel/runtime": "^7.17.2",
36
+ "async": "^3.2.3",
37
+ "body-parser": "^1.19.1",
38
38
  "boolean": "^3.1.4",
39
39
  "bottleneck": "^2.19.5",
40
- "csv-parse": "^4.16.3",
41
- "debug": "^4.3.2",
40
+ "csv-parse": "^5.0.4",
41
+ "debug": "^4.3.3",
42
42
  "esprima": "^4.0.1",
43
- "express": "^4.17.1",
43
+ "express": "^4.17.2",
44
44
  "globby": "11.0.4",
45
- "ioredis": "^4.28.0",
45
+ "ioredis": "^4.28.5",
46
46
  "is-class": "^0.0.9",
47
47
  "is-json": "^2.0.1",
48
48
  "jsonpath": "^1.1.1",
49
49
  "lodash": "^4.17.21",
50
- "markdown-it": "^12.2.0",
51
- "mime-types": "^2.1.33",
50
+ "markdown-it": "^12.3.2",
51
+ "mime-types": "^2.1.34",
52
52
  "mkdirp": "^1.0.4",
53
53
  "moment": "^2.29.1",
54
54
  "mustache": "^4.2.0",
@@ -58,40 +58,40 @@
58
58
  "request": "^2.88.2",
59
59
  "rimraf": "^3.0.2",
60
60
  "sanitize-filename": "^1.6.3",
61
- "slugify": "^1.6.1",
62
- "socket.io": "^4.3.1",
63
- "socket.io-client": "^4.3.2",
61
+ "slugify": "^1.6.5",
62
+ "socket.io": "^4.4.1",
63
+ "socket.io-client": "^4.4.1",
64
64
  "socketio-auth": "^0.1.1",
65
65
  "swagger-jsdoc": "^6.1.0",
66
- "swagger-ui-express": "^4.1.6",
66
+ "swagger-ui-express": "^4.3.0",
67
67
  "uuid": "^8.3.2",
68
- "vm2": "^3.9.5",
68
+ "vm2": "^3.9.7",
69
69
  "write-yaml": "^1.0.0",
70
- "xlsx": "^0.17.3",
70
+ "xlsx": "^0.18.2",
71
71
  "xregexp": "^5.1.0",
72
72
  "yaml": "^1.10.2"
73
73
  },
74
74
  "devDependencies": {
75
- "@babel/core": "^7.16.0",
76
- "@babel/node": "^7.16.0",
77
- "@babel/plugin-transform-runtime": "^7.16.0",
78
- "@babel/preset-env": "^7.16.0",
79
- "chai": "^4.3.4",
75
+ "@babel/core": "^7.17.2",
76
+ "@babel/node": "^7.16.8",
77
+ "@babel/plugin-transform-runtime": "^7.17.0",
78
+ "@babel/preset-env": "^7.16.11",
79
+ "chai": "^4.3.6",
80
80
  "chai-as-promised": "^7.1.1",
81
81
  "cross-env": "^7.0.3",
82
- "eslint": "^8.1.0",
82
+ "eslint": "^8.9.0",
83
83
  "eslint-config-standard": "^16.0.3",
84
- "eslint-plugin-import": "^2.25.2",
84
+ "eslint-plugin-import": "^2.25.4",
85
85
  "eslint-plugin-node": "^11.1.0",
86
- "eslint-plugin-promise": "^5.1.1",
86
+ "eslint-plugin-promise": "^6.0.0",
87
87
  "eslint-plugin-standard": "^4.1.0",
88
88
  "license-checker": "^25.0.1",
89
89
  "license-compatibility-checker": "^0.3.5",
90
- "mocha": "^9.1.3",
91
- "nock": "^13.1.4",
92
- "npm-check-updates": "^12.0.0",
90
+ "mocha": "^9.2.0",
91
+ "nock": "^13.2.4",
92
+ "npm-check-updates": "^12.3.0",
93
93
  "nyc": "^15.1.0",
94
- "rollup": "^2.59.0",
94
+ "rollup": "^2.67.2",
95
95
  "rollup-plugin-babel": "^4.4.0",
96
96
  "rollup-plugin-commonjs": "^10.1.0",
97
97
  "rollup-plugin-json": "^4.0.0",
@@ -23,13 +23,13 @@ class BotiumConnectorMyApi {
23
23
  [CoreCapabilities.SIMPLEREST_URL]: this.caps[Capabilities.MYAPI_URL],
24
24
  [CoreCapabilities.SIMPLEREST_METHOD]: 'POST',
25
25
  [CoreCapabilities.SIMPLEREST_RESPONSE_JSONPATH]: '$.reply',
26
- [CoreCapabilities.SIMPLEREST_BODY_TEMPLATE]: JSON.stringify({
26
+ [CoreCapabilities.SIMPLEREST_BODY_TEMPLATE]: JSON.stringify({
27
27
  username: 'botium',
28
28
  message: '{{msg.messageText}}',
29
29
  session: '{{botium.conversationId}}',
30
30
  startsession: false,
31
31
  quickreply: null
32
- })
32
+ })
33
33
  }
34
34
  if (this.caps[Capabilities.MYAPI_TOKEN]) {
35
35
  this.delegateCaps[CoreCapabilities.SIMPLEREST_HEADERS_TEMPLATE] = `{ "Authorization": "Token ${this.caps[Capabilities.MYAPI_TOKEN]}"}`
@@ -104,4 +104,4 @@ module.exports = {
104
104
  }
105
105
  ]
106
106
  }
107
- }
107
+ }
@@ -6,17 +6,17 @@ module.exports = class DummyAsserter {
6
6
  this.caps = caps
7
7
  }
8
8
 
9
- assertConvoBegin ({convo, container, args}) {
9
+ assertConvoBegin ({ convo, container, args }) {
10
10
  console.log(`Dummy asserter Begin started with those args: ${utils.inspect(args)}`)
11
11
  return Promise.resolve()
12
12
  }
13
13
 
14
- assertConvoStep ({convo, convoStep, args, botMsg}) {
14
+ assertConvoStep ({ convo, convoStep, args, botMsg }) {
15
15
  console.log(`ConvoStep dummy assertion with those args: ${utils.inspect(args)}, botMessage: ${utils.inspect(botMsg)} ...`)
16
16
  return Promise.resolve()
17
17
  }
18
18
 
19
- assertConvoEnd ({convo, container, transcript, args}) {
19
+ assertConvoEnd ({ convo, container, transcript, args }) {
20
20
  console.log(`ConvoEnd dummy assertion with those args: ${utils.inspect(args)}, transcript: ${utils.inspect(transcript)} ...`)
21
21
  return Promise.resolve()
22
22
  }
package/src/BotDriver.js CHANGED
@@ -109,9 +109,9 @@ module.exports = class BotDriver {
109
109
 
110
110
  Build () {
111
111
  debug(`Build - Botium Core Version: ${version}`)
112
- debug(`Build - Capabilites: ${util.inspect(this.caps)}`)
113
- debug(`Build - Sources : ${util.inspect(this.sources)}`)
114
- debug(`Build - Envs : ${util.inspect(this.envs)}`)
112
+ debug(`Build - Capabilites: ${JSON.stringify(_.pickBy(this.caps, (value, key) => Defaults.Capabilities[key] !== value), null, 2)}`)
113
+ debug(`Build - Sources: ${JSON.stringify(_.pickBy(this.sources, (value, key) => Defaults.Sources[key] !== value), null, 2)}`)
114
+ debug(`Build - Envs: ${JSON.stringify(_.pickBy(this.envs, (value, key) => Defaults.Envs[key] !== value), null, 2)}`)
115
115
  this.eventEmitter.emit(Events.CONTAINER_BUILDING)
116
116
 
117
117
  return new Promise((resolve, reject) => {
@@ -73,6 +73,11 @@ module.exports = {
73
73
  SIMPLEREST_RESPONSE_HOOK: 'SIMPLEREST_RESPONSE_HOOK',
74
74
  SIMPLEREST_MEDIA_JSONPATH: 'SIMPLEREST_MEDIA_JSONPATH',
75
75
  SIMPLEREST_BUTTONS_JSONPATH: 'SIMPLEREST_BUTTONS_JSONPATH',
76
+ SIMPLEREST_CARDS_JSONPATH: 'SIMPLEREST_CARDS_JSONPATH',
77
+ SIMPLEREST_CARD_TEXT_JSONPATH: 'SIMPLEREST_CARD_TEXT_JSONPATH',
78
+ SIMPLEREST_CARD_SUBTEXT_JSONPATH: 'SIMPLEREST_CARD_SUBTEXT_JSONPATH',
79
+ SIMPLEREST_CARD_BUTTONS_JSONPATH: 'SIMPLEREST_CARD_BUTTONS_JSONPATH',
80
+ SIMPLEREST_CARD_ATTACHMENTS_JSONPATH: 'SIMPLEREST_CARD_ATTACHMENTS_JSONPATH',
76
81
  SIMPLEREST_CONTEXT_JSONPATH: 'SIMPLEREST_CONTEXT_JSONPATH',
77
82
  SIMPLEREST_CONTEXT_MERGE_OR_REPLACE: 'SIMPLEREST_CONTEXT_MERGE_OR_REPLACE',
78
83
  SIMPLEREST_CONVERSATION_ID_TEMPLATE: 'SIMPLEREST_CONVERSATION_ID_TEMPLATE',
@@ -111,6 +116,7 @@ module.exports = {
111
116
  SCRIPTING_NORMALIZE_TEXT: 'SCRIPTING_NORMALIZE_TEXT',
112
117
  SCRIPTING_ENABLE_MEMORY: 'SCRIPTING_ENABLE_MEMORY',
113
118
  SCRIPTING_ENABLE_MULTIPLE_ASSERT_ERRORS: 'SCRIPTING_ENABLE_MULTIPLE_ASSERT_ERRORS',
119
+ SCRIPTING_ENABLE_SKIP_ASSERT_ERRORS: 'SCRIPTING_ENABLE_SKIP_ASSERT_ERRORS',
114
120
  SCRIPTING_FORCE_BOT_CONSUMED: 'SCRIPTING_FORCE_BOT_CONSUMED',
115
121
  // regexp, regexpIgnoreCase, wildcard, wildcardIgnoreCase, wildcardExact, wildcardExactIgnoreCase, include, includeIgnoreCase, equals, equalsIgnoreCase
116
122
  SCRIPTING_MATCHING_MODE: 'SCRIPTING_MATCHING_MODE',
@@ -126,6 +132,8 @@ module.exports = {
126
132
  SCRIPTING_MEMORYEXPANSION_KEEP_ORIG: 'SCRIPTING_MEMORYEXPANSION_KEEP_ORIG',
127
133
  // word, non_whitespace, joker
128
134
  SCRIPTING_MEMORY_MATCHING_MODE: 'SCRIPTING_MEMORY_MATCHING_MODE',
135
+ // varnames, testcasenames
136
+ SCRIPTING_MEMORY_COLUMN_MODE: 'SCRIPTING_MEMORY_COLUMN_MODE',
129
137
  // Botium Lifecycle Hooks
130
138
  CUSTOMHOOK_ONBUILD: 'CUSTOMHOOK_ONBUILD',
131
139
  CUSTOMHOOK_ONSTART: 'CUSTOMHOOK_ONSTART',
@@ -140,6 +148,7 @@ module.exports = {
140
148
  // API Calls Rate Limiting
141
149
  RATELIMIT_USERSAYS_MAXCONCURRENT: 'RATELIMIT_USERSAYS_MAXCONCURRENT',
142
150
  RATELIMIT_USERSAYS_MINTIME: 'RATELIMIT_USERSAYS_MINTIME',
151
+ RATELIMIT_BOTTLENECK_FN: 'RATELIMIT_BOTTLENECK_FN',
143
152
  SECURITY_ALLOW_UNSAFE: 'SECURITY_ALLOW_UNSAFE',
144
153
  PRECOMPILERS: 'PRECOMPILERS'
145
154
  }
package/src/Defaults.js CHANGED
@@ -31,7 +31,6 @@ module.exports = {
31
31
  [Capabilities.SIMPLEREST_METHOD]: 'GET',
32
32
  [Capabilities.SIMPLEREST_IGNORE_EMPTY]: true,
33
33
  [Capabilities.SIMPLEREST_TIMEOUT]: 10000,
34
- [Capabilities.SIMPLEREST_EXTRA_OPTIONS]: {},
35
34
  [Capabilities.SIMPLEREST_STRICT_SSL]: true,
36
35
  [Capabilities.SIMPLEREST_INBOUND_UPDATE_CONTEXT]: true,
37
36
  [Capabilities.SIMPLEREST_CONTEXT_MERGE_OR_REPLACE]: 'MERGE',
@@ -50,6 +49,7 @@ module.exports = {
50
49
  [Capabilities.SCRIPTING_UTTEXPANSION_NAMING_MODE]: 'justLineTag',
51
50
  [Capabilities.SCRIPTING_UTTEXPANSION_NAMING_UTTERANCE_MAX]: '16',
52
51
  [Capabilities.SCRIPTING_MEMORYEXPANSION_KEEP_ORIG]: false,
52
+ [Capabilities.SCRIPTING_ENABLE_SKIP_ASSERT_ERRORS]: false,
53
53
  [Capabilities.SCRIPTING_FORCE_BOT_CONSUMED]: false,
54
54
  [Capabilities.ASSERTERS]: [],
55
55
  [Capabilities.LOGIC_HOOKS]: [],
package/src/Enums.js ADDED
@@ -0,0 +1,6 @@
1
+ module.exports = {
2
+ E_SCRIPTING_MEMORY_COLUMN_MODE: {
3
+ VARNAMES: 'varnames',
4
+ TESTCASENAMES: 'testcasenames'
5
+ }
6
+ }
@@ -21,7 +21,7 @@ module.exports = class BaseContainer {
21
21
  this.tempDirectory = tempDirectory
22
22
  this.cleanupTasks = []
23
23
  this.queues = {}
24
- this.userSaysLimiter = null
24
+ this.bottleneck = null
25
25
  }
26
26
 
27
27
  Validate () {
@@ -32,18 +32,28 @@ module.exports = class BaseContainer {
32
32
  this.onStopHook = getHook(this.caps, this.caps[Capabilities.CUSTOMHOOK_ONSTOP])
33
33
  this.onCleanHook = getHook(this.caps, this.caps[Capabilities.CUSTOMHOOK_ONCLEAN])
34
34
 
35
- return Promise.resolve()
36
- }
37
-
38
- Build () {
39
- if (this.caps[Capabilities.RATELIMIT_USERSAYS_MAXCONCURRENT] || this.caps[Capabilities.RATELIMIT_USERSAYS_MINTIME]) {
35
+ if (this.caps[Capabilities.RATELIMIT_BOTTLENECK_FN]) {
36
+ if (_.isFunction(this.caps[Capabilities.RATELIMIT_BOTTLENECK_FN])) {
37
+ this.bottleneck = this.caps[Capabilities.RATELIMIT_BOTTLENECK_FN]
38
+ debug('Validate: Applying userSays rate limits from capability')
39
+ } else {
40
+ const limiter = new Bottleneck(this.caps[Capabilities.RATELIMIT_BOTTLENECK_FN])
41
+ this.bottleneck = (fn) => limiter.schedule(fn)
42
+ debug(`Validate: Applying userSays rate limits ${util.inspect(this.caps[Capabilities.RATELIMIT_BOTTLENECK_FN])}`)
43
+ }
44
+ } else if (this.caps[Capabilities.RATELIMIT_USERSAYS_MAXCONCURRENT] || this.caps[Capabilities.RATELIMIT_USERSAYS_MINTIME]) {
40
45
  const opts = {}
41
46
  if (this.caps[Capabilities.RATELIMIT_USERSAYS_MAXCONCURRENT]) opts.maxConcurrent = this.caps[Capabilities.RATELIMIT_USERSAYS_MAXCONCURRENT]
42
47
  if (this.caps[Capabilities.RATELIMIT_USERSAYS_MINTIME]) opts.minTime = this.caps[Capabilities.RATELIMIT_USERSAYS_MINTIME]
43
- this.userSaysLimiter = new Bottleneck(opts)
44
- debug(`Build: Applying userSays rate limits ${util.inspect(opts)}`)
48
+ const limiter = new Bottleneck(opts)
49
+ this.bottleneck = (fn) => limiter.schedule(fn)
50
+ debug(`Validate: Applying userSays rate limits ${util.inspect(opts)}`)
45
51
  }
46
52
 
53
+ return Promise.resolve()
54
+ }
55
+
56
+ Build () {
47
57
  return new Promise((resolve, reject) => {
48
58
  this._RunCustomHook('onBuild', this.onBuildHook)
49
59
  .then(() => resolve(this))
@@ -69,8 +79,8 @@ module.exports = class BaseContainer {
69
79
  const run = () => this._RunCustomHook('onUserSays', this.onUserSaysHook, { meMsg })
70
80
  .then(() => this.UserSaysImpl(meMsg))
71
81
 
72
- if (this.userSaysLimiter) {
73
- return this.userSaysLimiter.schedule(run)
82
+ if (this.bottleneck) {
83
+ return this.bottleneck(run)
74
84
  } else {
75
85
  return run()
76
86
  }
@@ -17,6 +17,7 @@ module.exports = class PluginConnectorContainer extends BaseContainer {
17
17
  {
18
18
  container: this,
19
19
  queueBotSays: (msg) => this._QueueBotSays(msg),
20
+ bottleneck: this.bottleneck,
20
21
  eventEmitter: this.eventEmitter,
21
22
  caps: this.caps,
22
23
  sources: this.sources,
@@ -17,13 +17,15 @@ const Defaults = require('../../Defaults').Capabilities
17
17
  const { SCRIPTING_FUNCTIONS } = require('../../scripting/ScriptingMemory')
18
18
  const { getHook, executeHook } = require('../../helpers/HookUtils')
19
19
  const { escapeJSONString } = require('../../helpers/Utils')
20
+ const { BotiumError } = require('../../scripting/BotiumError')
20
21
 
21
22
  Mustache.escape = s => s
22
23
 
23
24
  module.exports = class SimpleRestContainer {
24
- constructor ({ queueBotSays, caps }) {
25
+ constructor ({ queueBotSays, caps, bottleneck }) {
25
26
  this.queueBotSays = queueBotSays
26
27
  this.caps = Object.assign({}, Defaults, caps)
28
+ this.bottleneck = bottleneck || ((fn) => fn())
27
29
  this.processInbound = false
28
30
  this.redisTopic = this.caps[Capabilities.SIMPLEREST_REDIS_TOPIC] || 'SIMPLEREST_INBOUND_SUBSCRIPTION'
29
31
 
@@ -307,34 +309,82 @@ module.exports = class SimpleRestContainer {
307
309
  }
308
310
 
309
311
  for (const jsonPathRoot of jsonPathRoots) {
310
- const media = []
311
- const buttons = []
312
-
313
- const jsonPathsMedia = getAllCapValues(Capabilities.SIMPLEREST_MEDIA_JSONPATH, this.caps)
314
- jsonPathsMedia.forEach(jsonPath => {
315
- const responseMedia = jp.query(jsonPathRoot, jsonPath)
316
- if (responseMedia) {
317
- (_.isArray(responseMedia) ? _.flattenDeep(responseMedia) : [responseMedia]).forEach(m =>
318
- media.push({
319
- mediaUri: m,
320
- mimeType: mime.lookup(m) || 'application/unknown'
321
- })
322
- )
323
- debug(`found response media: ${util.inspect(media)}`)
312
+ const _retrieveMedia = (jsonPathMediaRoot, jsonPathsMedia) => {
313
+ const retrievedMedia = []
314
+ jsonPathsMedia.forEach(jsonPath => {
315
+ const responseMedia = jp.query(jsonPathMediaRoot, jsonPath)
316
+ if (responseMedia) {
317
+ (_.isArray(responseMedia) ? _.flattenDeep(responseMedia) : [responseMedia]).forEach(m =>
318
+ retrievedMedia.push({
319
+ mediaUri: m,
320
+ mimeType: mime.lookup(m) || 'application/unknown'
321
+ })
322
+ )
323
+ }
324
+ })
325
+ return retrievedMedia
326
+ }
327
+
328
+ const _retrieveButtons = (jsonPathButtonRoot, jsonPathsButtons) => {
329
+ const retrievedButtons = []
330
+ jsonPathsButtons.forEach(jsonPath => {
331
+ const responseButtons = jp.query(jsonPathButtonRoot, jsonPath)
332
+ if (responseButtons) {
333
+ (_.isArray(responseButtons) ? _.flattenDeep(responseButtons) : [responseButtons]).forEach(b =>
334
+ retrievedButtons.push({
335
+ text: b
336
+ })
337
+ )
338
+ }
339
+ })
340
+ return retrievedButtons
341
+ }
342
+
343
+ const _getCardText = (responseCardText) => {
344
+ if (responseCardText) {
345
+ const texts = _.isArray(responseCardText) ? _.flattenDeep(responseCardText) : [responseCardText]
346
+ if (texts.length > 1) {
347
+ debug(`more than one text found for card: ${util.inspect(texts)}`)
348
+ }
349
+ if (texts.length > 0) {
350
+ return texts[0]
351
+ }
324
352
  }
325
- })
326
- const jsonPathsButtons = getAllCapValues(Capabilities.SIMPLEREST_BUTTONS_JSONPATH, this.caps)
327
- jsonPathsButtons.forEach(jsonPath => {
328
- const responseButtons = jp.query(jsonPathRoot, jsonPath)
329
- if (responseButtons) {
330
- (_.isArray(responseButtons) ? _.flattenDeep(responseButtons) : [responseButtons]).forEach(b =>
331
- buttons.push({
332
- text: b
353
+ }
354
+
355
+ const media = _retrieveMedia(jsonPathRoot, getAllCapValues(Capabilities.SIMPLEREST_MEDIA_JSONPATH, this.caps))
356
+ debug(`found response media: ${util.inspect(media)}`)
357
+ const buttons = _retrieveButtons(jsonPathRoot, getAllCapValues(Capabilities.SIMPLEREST_BUTTONS_JSONPATH, this.caps))
358
+ debug(`found response buttons: ${util.inspect(buttons)}`)
359
+ const cards = []
360
+
361
+ const jsonPathsCards = getAllCapValues(Capabilities.SIMPLEREST_CARDS_JSONPATH, this.caps)
362
+ jsonPathsCards.forEach(jsonPath => {
363
+ const responseCards = jp.query(jsonPathRoot, jsonPath)
364
+ if (responseCards) {
365
+ (_.isArray(responseCards) ? _.flattenDeep(responseCards) : [responseCards]).forEach(c => {
366
+ const card = {}
367
+
368
+ const jsonPathsCardText = getAllCapValues(Capabilities.SIMPLEREST_CARD_TEXT_JSONPATH, this.caps)
369
+ jsonPathsCardText.forEach(jsonPath => {
370
+ card.text = _getCardText(jp.query(c, jsonPath))
333
371
  })
334
- )
335
- debug(`found response buttons: ${util.inspect(buttons)}`)
372
+
373
+ const jsonPathsCardSubText = getAllCapValues(Capabilities.SIMPLEREST_CARD_SUBTEXT_JSONPATH, this.caps)
374
+ jsonPathsCardSubText.forEach(jsonPath => {
375
+ card.subtext = _getCardText(jp.query(c, jsonPath))
376
+ })
377
+
378
+ card.buttons = _retrieveButtons(c, getAllCapValues(Capabilities.SIMPLEREST_CARD_BUTTONS_JSONPATH, this.caps))
379
+ card.media = _retrieveMedia(c, getAllCapValues(Capabilities.SIMPLEREST_CARD_ATTACHMENTS_JSONPATH, this.caps))
380
+
381
+ if (_.keys(card).length > 0) {
382
+ cards.push(card)
383
+ }
384
+ })
336
385
  }
337
386
  })
387
+ debug(`found response cards: ${util.inspect(cards)}`)
338
388
 
339
389
  let hasMessageText = false
340
390
  const jsonPathsTexts = getAllCapValues(Capabilities.SIMPLEREST_RESPONSE_JSONPATH, this.caps)
@@ -349,18 +399,18 @@ module.exports = class SimpleRestContainer {
349
399
  if (!messageText) continue
350
400
 
351
401
  hasMessageText = true
352
- const botMsg = { sourceData: body, messageText, media, buttons }
402
+ const botMsg = { sourceData: body, messageText, media, buttons, cards }
353
403
  await executeHook(this.caps, this.responseHook, Object.assign({ botMsg, botMsgRoot: jsonPathRoot, messageTextIndex }, this.view))
354
404
  result.push(botMsg)
355
405
  }
356
406
  }
357
407
 
358
408
  if (!hasMessageText) {
359
- const botMsg = { messageText: '', sourceData: body, media, buttons }
409
+ const botMsg = { messageText: '', sourceData: body, media, buttons, cards }
360
410
  const beforeHookKeys = Object.keys(botMsg)
361
411
  await executeHook(this.caps, this.responseHook, Object.assign({ botMsg, botMsgRoot: jsonPathRoot }, this.view))
362
412
  const afterHookKeys = Object.keys(botMsg)
363
- if (beforeHookKeys.length !== afterHookKeys.length || !!(botMsg.messageText && botMsg.messageText.length > 0) || botMsg.media.length > 0 || botMsg.buttons.length > 0 || !this.caps[Capabilities.SIMPLEREST_IGNORE_EMPTY]) {
413
+ if (beforeHookKeys.length !== afterHookKeys.length || !!(botMsg.messageText && botMsg.messageText.length > 0) || botMsg.media.length > 0 || botMsg.buttons.length > 0 || botMsg.cards.length > 0 || !this.caps[Capabilities.SIMPLEREST_IGNORE_EMPTY]) {
364
414
  result.push(botMsg)
365
415
  }
366
416
  }
@@ -387,6 +437,15 @@ module.exports = class SimpleRestContainer {
387
437
  if (debug.enabled && body) {
388
438
  debug(botiumUtils.shortenJsonString(body))
389
439
  }
440
+ if (body) {
441
+ const jsonBody = botiumUtils.toJsonWeak(body)
442
+ const errKey = Object.keys(jsonBody).find(k => k.startsWith('err') || k.startsWith('fail'))
443
+ if (errKey) {
444
+ return reject(new BotiumError(`got error response: ${response.statusCode}/${response.statusMessage} - ${jsonBody[errKey]}`, {
445
+ message: botiumUtils.shortenJsonString(body)
446
+ }))
447
+ }
448
+ }
390
449
  return reject(new Error(`got error response: ${response.statusCode}/${response.statusMessage}`))
391
450
  }
392
451
 
@@ -478,6 +537,15 @@ module.exports = class SimpleRestContainer {
478
537
  requestOptions.uri = `${requestOptions.uri}?${appendToUri}`
479
538
  }
480
539
  }
540
+ if (msg.ADD_FORM_PARAM && Object.keys(msg.ADD_FORM_PARAM).length > 0) {
541
+ requestOptions.form = {}
542
+ for (const formKey of Object.keys(msg.ADD_FORM_PARAM)) {
543
+ const formValue = this._getMustachedVal(
544
+ _.isString(msg.ADD_FORM_PARAM[formKey]) ? msg.ADD_FORM_PARAM[formKey] : JSON.stringify(msg.ADD_FORM_PARAM[formKey]),
545
+ false)
546
+ requestOptions.form[formKey] = formValue
547
+ }
548
+ }
481
549
  if (msg.ADD_HEADER && Object.keys(msg.ADD_HEADER).length > 0) {
482
550
  requestOptions.headers = requestOptions.headers || {}
483
551
 
@@ -509,11 +577,11 @@ module.exports = class SimpleRestContainer {
509
577
  throw new Error(`Failed to ping bot after ${retries} retries`)
510
578
  }
511
579
  tries++
512
- const { err, response, body } = await new Promise((resolve) => {
580
+ const { err, response, body } = await this.bottleneck(() => new Promise((resolve) => {
513
581
  request(pingConfig, (err, response, body) => {
514
582
  resolve({ err, response, body })
515
583
  })
516
- })
584
+ }))
517
585
  if (err) {
518
586
  debug(`_waitForUrlResponse error on url check ${pingConfig.uri}: ${err}`)
519
587
  await timeout(pingConfig.timeout)
@@ -26,6 +26,27 @@ const BotiumError = class BotiumError extends Error {
26
26
  this.context.message = message.message || message
27
27
  }
28
28
 
29
+ isAsserterError () {
30
+ if (this.context) {
31
+ const errArr = _.isArray(this.context) ? this.context : [this.context]
32
+ const hasNotAsserterError = errArr.findIndex(errDetail => {
33
+ if (errDetail.type === 'list') {
34
+ if (errDetail.errors) {
35
+ return errDetail.errors.findIndex(e => e.type !== 'asserter') >= 0
36
+ } else {
37
+ return true
38
+ }
39
+ } else {
40
+ return errDetail.type !== 'asserter'
41
+ }
42
+ }) >= 0
43
+ if (hasNotAsserterError) return false
44
+ return true
45
+ } else {
46
+ return false
47
+ }
48
+ }
49
+
29
50
  prettify (includeJson) {
30
51
  const lines = []
31
52
  if (this.context) {
@@ -1,4 +1,4 @@
1
- const parse = require('csv-parse/lib/sync')
1
+ const { parse } = require('csv-parse/sync')
2
2
  const _ = require('lodash')
3
3
  const debug = require('debug')('botium-core-CompilerCsv')
4
4
 
@@ -6,7 +6,7 @@ const CompilerBase = require('./CompilerBase')
6
6
  const Constants = require('./Constants')
7
7
  const Utterance = require('./Utterance')
8
8
  const { Convo } = require('./Convo')
9
- const { linesToConvoStep, validSenders } = require('./helper')
9
+ const { linesToConvoStep, validSenders, linesToScriptingMemories } = require('./helper')
10
10
 
11
11
  module.exports = class CompilerObjectBase extends CompilerBase {
12
12
  constructor (context, caps = {}) {
@@ -109,20 +109,10 @@ module.exports = class CompilerObjectBase extends CompilerBase {
109
109
  if (lines && lines.length > 0) {
110
110
  if (_.isString(lines[0])) {
111
111
  if (lines.length > 1) {
112
- const names = lines[0].split('|').map((name) => name.trim()).slice(1)
113
- const scriptingMemories = []
114
- for (let row = 1; row < lines.length; row++) {
115
- const rawRow = lines[row].split('|').map((name) => name.trim())
116
- const caseName = rawRow[0]
117
- const values = rawRow.slice(1)
118
- const json = {}
119
- for (let col = 0; col < names.length; col++) {
120
- json[names[col]] = values[col]
121
- }
122
- const scriptingMemory = { header: { name: caseName }, values: json }
123
- scriptingMemories.push(scriptingMemory)
112
+ const scriptingMemories = linesToScriptingMemories(lines, this.caps[Capabilities.SCRIPTING_MEMORY_COLUMN_MODE])
113
+ if (scriptingMemories && scriptingMemories.length > 0) {
114
+ this.context.AddScriptingMemories(scriptingMemories)
124
115
  }
125
- this.context.AddScriptingMemories(scriptingMemories)
126
116
  return scriptingMemories
127
117
  }
128
118
  } else {
@@ -5,7 +5,7 @@ const Constants = require('./Constants')
5
5
  const CompilerBase = require('./CompilerBase')
6
6
  const Utterance = require('./Utterance')
7
7
  const { ConvoHeader, Convo } = require('./Convo')
8
- const { linesToConvoStep, convoStepToLines, validateConvo, validSenders } = require('./helper')
8
+ const { linesToConvoStep, convoStepToLines, validateConvo, validSenders, linesToScriptingMemories } = require('./helper')
9
9
 
10
10
  module.exports = class CompilerTxt extends CompilerBase {
11
11
  constructor (context, caps = {}) {
@@ -131,21 +131,10 @@ module.exports = class CompilerTxt extends CompilerBase {
131
131
 
132
132
  _compileScriptingMemory (lines) {
133
133
  if (lines && lines.length > 1) {
134
- const names = lines[0].split('|').map((name) => name.trim()).slice(1)
135
- const scriptingMemories = []
136
- for (let row = 1; row < lines.length; row++) {
137
- if (!lines[row] || lines[row].length === 0) continue
138
- const rawRow = lines[row].split('|').map((name) => name.trim())
139
- const caseName = rawRow[0]
140
- const values = rawRow.slice(1)
141
- const json = {}
142
- for (let col = 0; col < names.length; col++) {
143
- json[names[col]] = values[col]
144
- }
145
- const scriptingMemory = { header: { name: caseName }, values: json }
146
- scriptingMemories.push(scriptingMemory)
134
+ const scriptingMemories = linesToScriptingMemories(lines, this.caps[Capabilities.SCRIPTING_MEMORY_COLUMN_MODE])
135
+ if (scriptingMemories && scriptingMemories.length > 0) {
136
+ this.context.AddScriptingMemories(scriptingMemories)
147
137
  }
148
- this.context.AddScriptingMemories(scriptingMemories)
149
138
  return scriptingMemories
150
139
  }
151
140
  }