botium-core 1.11.13 → 1.12.0

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 (31) hide show
  1. package/dist/botium-cjs.js +692 -524
  2. package/dist/botium-cjs.js.map +1 -1
  3. package/dist/botium-es.js +323 -155
  4. package/dist/botium-es.js.map +1 -1
  5. package/package.json +30 -29
  6. package/samples/connectors/custom/botium-connector-myapi.js +3 -3
  7. package/samples/extensions/asserterHooks/DummyAsserter.js +3 -3
  8. package/src/Capabilities.js +7 -1
  9. package/src/Defaults.js +1 -0
  10. package/src/containers/plugins/SimpleRestContainer.js +129 -52
  11. package/src/scripting/CompilerCsv.js +1 -1
  12. package/src/scripting/MatchFunctions.js +21 -0
  13. package/src/scripting/ScriptingProvider.js +49 -40
  14. package/src/scripting/helper.js +3 -3
  15. package/src/scripting/logichook/LogicHookConsts.js +4 -0
  16. package/src/scripting/logichook/LogicHookUtils.js +2 -0
  17. package/src/scripting/logichook/asserter/JsonPathAsserter.js +1 -1
  18. package/src/scripting/logichook/asserter/TextWildcardExactAllAsserter.js +8 -0
  19. package/src/scripting/logichook/asserter/TextWildcardExactAllICAsserter.js +8 -0
  20. package/src/scripting/logichook/asserter/TextWildcardExactAnyAsserter.js +8 -0
  21. package/src/scripting/logichook/asserter/TextWildcardExactAnyICAsserter.js +8 -0
  22. package/src/scripting/logichook/logichooks/UpdateCustomLogicHook.js +3 -4
  23. package/test/connectors/convos/hello.convo.txt +6 -0
  24. package/test/connectors/simplerest.spec.js +129 -2
  25. package/test/scripting/asserters/convos/text_wildcardexact_all_nok.yml +7 -0
  26. package/test/scripting/asserters/convos/text_wildcardexact_all_ok.yml +7 -0
  27. package/test/scripting/asserters/convos/text_wildcardexact_any_nok.yml +7 -0
  28. package/test/scripting/asserters/convos/text_wildcardexact_any_ok.yml +7 -0
  29. package/test/scripting/asserters/textWildcardExactAllAsserter.spec.js +51 -0
  30. package/test/scripting/asserters/textWildcardExactAnyAsserter.spec.js +51 -0
  31. package/test/scripting/matching/matchingmode.spec.js +43 -0
package/package.json CHANGED
@@ -1,11 +1,11 @@
1
1
  {
2
2
  "name": "botium-core",
3
- "version": "1.11.13",
3
+ "version": "1.12.0",
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,65 +32,66 @@
32
32
  },
33
33
  "homepage": "https://www.botium.ai",
34
34
  "dependencies": {
35
- "@babel/runtime": "^7.15.4",
36
- "async": "^3.2.1",
37
- "body-parser": "^1.19.0",
35
+ "@babel/runtime": "^7.16.5",
36
+ "async": "^3.2.2",
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.3",
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.27.9",
45
+ "ioredis": "^4.28.2",
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.32",
50
+ "markdown-it": "^12.3.0",
51
+ "mime-types": "^2.1.34",
52
52
  "mkdirp": "^1.0.4",
53
53
  "moment": "^2.29.1",
54
54
  "mustache": "^4.2.0",
55
55
  "promise-retry": "^2.0.1",
56
- "promise.allsettled": "^1.0.4",
56
+ "promise.allsettled": "^1.0.5",
57
57
  "randomatic": "^3.1.1",
58
58
  "request": "^2.88.2",
59
59
  "rimraf": "^3.0.2",
60
60
  "sanitize-filename": "^1.6.3",
61
- "slugify": "^1.6.0",
62
- "socket.io": "^4.2.0",
63
- "socket.io-client": "^4.2.0",
61
+ "slugify": "^1.6.4",
62
+ "socket.io": "^4.4.0",
63
+ "socket.io-client": "^4.4.0",
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.3",
68
+ "vm2": "^3.9.5",
69
69
  "write-yaml": "^1.0.0",
70
- "xlsx": "^0.17.1",
70
+ "xlsx": "^0.17.4",
71
71
  "xregexp": "^5.1.0",
72
72
  "yaml": "^1.10.2"
73
73
  },
74
74
  "devDependencies": {
75
- "@babel/core": "^7.15.5",
76
- "@babel/node": "^7.15.4",
77
- "@babel/plugin-transform-runtime": "^7.15.0",
78
- "@babel/preset-env": "^7.15.6",
75
+ "@babel/core": "^7.16.5",
76
+ "@babel/node": "^7.16.5",
77
+ "@babel/plugin-transform-runtime": "^7.16.5",
78
+ "@babel/preset-env": "^7.16.5",
79
79
  "chai": "^4.3.4",
80
80
  "chai-as-promised": "^7.1.1",
81
81
  "cross-env": "^7.0.3",
82
- "eslint": "^7.32.0",
82
+ "eslint": "^8.4.1",
83
83
  "eslint-config-standard": "^16.0.3",
84
- "eslint-plugin-import": "^2.24.2",
84
+ "eslint-plugin-import": "^2.25.3",
85
85
  "eslint-plugin-node": "^11.1.0",
86
- "eslint-plugin-promise": "^5.1.0",
86
+ "eslint-plugin-promise": "^5.2.0",
87
87
  "eslint-plugin-standard": "^4.1.0",
88
88
  "license-checker": "^25.0.1",
89
- "mocha": "^9.1.1",
90
- "nock": "^13.1.3",
91
- "npm-check-updates": "^11.8.5",
89
+ "license-compatibility-checker": "^0.3.5",
90
+ "mocha": "^9.1.3",
91
+ "nock": "^13.2.1",
92
+ "npm-check-updates": "^12.0.5",
92
93
  "nyc": "^15.1.0",
93
- "rollup": "^2.56.3",
94
+ "rollup": "^2.61.1",
94
95
  "rollup-plugin-babel": "^4.4.0",
95
96
  "rollup-plugin-commonjs": "^10.1.0",
96
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
  }
@@ -18,6 +18,7 @@ module.exports = {
18
18
  SIMPLEREST_PING_BODY: 'SIMPLEREST_PING_BODY',
19
19
  SIMPLEREST_PING_BODY_RAW: 'SIMPLEREST_PING_BODY_RAW',
20
20
  SIMPLEREST_PING_HEADERS: 'SIMPLEREST_PING_HEADERS',
21
+ SIMPLEREST_PING_REQUEST_HOOK: 'SIMPLEREST_PING_REQUEST_HOOK',
21
22
  SIMPLEREST_PING_RETRIES: 'SIMPLEREST_PING_RETRIES',
22
23
  SIMPLEREST_PING_TIMEOUT: 'SIMPLEREST_PING_TIMEOUT',
23
24
  SIMPLEREST_PING_UPDATE_CONTEXT: 'SIMPLEREST_PING_UPDATE_CONTEXT',
@@ -27,6 +28,7 @@ module.exports = {
27
28
  SIMPLEREST_START_BODY: 'SIMPLEREST_START_BODY',
28
29
  SIMPLEREST_START_BODY_RAW: 'SIMPLEREST_START_BODY_RAW',
29
30
  SIMPLEREST_START_HEADERS: 'SIMPLEREST_START_HEADERS',
31
+ SIMPLEREST_START_REQUEST_HOOK: 'SIMPLEREST_START_REQUEST_HOOK',
30
32
  SIMPLEREST_START_RETRIES: 'SIMPLEREST_START_RETRIES',
31
33
  SIMPLEREST_START_TIMEOUT: 'SIMPLEREST_START_TIMEOUT',
32
34
  SIMPLEREST_START_UPDATE_CONTEXT: 'SIMPLEREST_START_UPDATE_CONTEXT',
@@ -36,10 +38,12 @@ module.exports = {
36
38
  SIMPLEREST_STOP_BODY: 'SIMPLEREST_STOP_BODY',
37
39
  SIMPLEREST_STOP_BODY_RAW: 'SIMPLEREST_STOP_BODY_RAW',
38
40
  SIMPLEREST_STOP_HEADERS: 'SIMPLEREST_STOP_HEADERS',
41
+ SIMPLEREST_STOP_REQUEST_HOOK: 'SIMPLEREST_STOP_REQUEST_HOOK',
39
42
  SIMPLEREST_STOP_RETRIES: 'SIMPLEREST_STOP_RETRIES',
40
43
  SIMPLEREST_STOP_TIMEOUT: 'SIMPLEREST_STOP_TIMEOUT',
41
44
  SIMPLEREST_INIT_CONTEXT: 'SIMPLEREST_INIT_CONTEXT',
42
45
  SIMPLEREST_INIT_TEXT: 'SIMPLEREST_INIT_TEXT',
46
+ SIMPLEREST_INIT_PROCESS_RESPONSE: 'SIMPLEREST_INIT_PROCESS_RESPONSE',
43
47
  SIMPLEREST_PROXY_URL: 'SIMPLEREST_PROXY_URL',
44
48
  SIMPLEREST_STRICT_SSL: 'SIMPLEREST_STRICT_SSL',
45
49
  SIMPLEREST_URL: 'SIMPLEREST_URL',
@@ -54,11 +58,13 @@ module.exports = {
54
58
  SIMPLEREST_START_HOOK: 'SIMPLEREST_START_HOOK',
55
59
  SIMPLEREST_STOP_HOOK: 'SIMPLEREST_STOP_HOOK',
56
60
  SIMPLEREST_REQUEST_HOOK: 'SIMPLEREST_REQUEST_HOOK',
61
+ SIMPLEREST_PARSER_HOOK: 'SIMPLEREST_PARSER_HOOK',
57
62
  SIMPLEREST_POLL_URL: 'SIMPLEREST_POLL_URL',
58
63
  SIMPLEREST_POLL_VERB: 'SIMPLEREST_POLL_VERB',
59
64
  SIMPLEREST_POLL_BODY: 'SIMPLEREST_POLL_BODY',
60
65
  SIMPLEREST_POLL_BODY_RAW: 'SIMPLEREST_POLL_BODY_RAW',
61
66
  SIMPLEREST_POLL_HEADERS: 'SIMPLEREST_POLL_HEADERS',
67
+ SIMPLEREST_POLL_REQUEST_HOOK: 'SIMPLEREST_POLL_REQUEST_HOOK',
62
68
  SIMPLEREST_POLL_INTERVAL: 'SIMPLEREST_POLL_INTERVAL',
63
69
  SIMPLEREST_POLL_TIMEOUT: 'SIMPLEREST_PING_TIMEOUT',
64
70
  SIMPLEREST_POLL_UPDATE_CONTEXT: 'SIMPLEREST_POLL_UPDATE_CONTEXT',
@@ -106,7 +112,7 @@ module.exports = {
106
112
  SCRIPTING_ENABLE_MEMORY: 'SCRIPTING_ENABLE_MEMORY',
107
113
  SCRIPTING_ENABLE_MULTIPLE_ASSERT_ERRORS: 'SCRIPTING_ENABLE_MULTIPLE_ASSERT_ERRORS',
108
114
  SCRIPTING_FORCE_BOT_CONSUMED: 'SCRIPTING_FORCE_BOT_CONSUMED',
109
- // regexp, regexpIgnoreCase, wildcard, wildcardIgnoreCase, include, includeIgnoreCase, equals, equalsIgnoreCase
115
+ // regexp, regexpIgnoreCase, wildcard, wildcardIgnoreCase, wildcardExact, wildcardExactIgnoreCase, include, includeIgnoreCase, equals, equalsIgnoreCase
110
116
  SCRIPTING_MATCHING_MODE: 'SCRIPTING_MATCHING_MODE',
111
117
  // all, first, random
112
118
  SCRIPTING_UTTEXPANSION_MODE: 'SCRIPTING_UTTEXPANSION_MODE',
package/src/Defaults.js CHANGED
@@ -15,6 +15,7 @@ module.exports = {
15
15
  [Capabilities.SIMPLEREST_PING_VERB]: 'GET',
16
16
  [Capabilities.SIMPLEREST_PING_UPDATE_CONTEXT]: true,
17
17
  [Capabilities.SIMPLEREST_PING_PROCESS_RESPONSE]: false,
18
+ [Capabilities.SIMPLEREST_INIT_PROCESS_RESPONSE]: false,
18
19
  [Capabilities.SIMPLEREST_STOP_RETRIES]: 6,
19
20
  [Capabilities.SIMPLEREST_STOP_TIMEOUT]: 10000,
20
21
  [Capabilities.SIMPLEREST_STOP_VERB]: 'GET',
@@ -17,6 +17,7 @@ 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
 
@@ -57,7 +58,20 @@ module.exports = class SimpleRestContainer {
57
58
  this.startHook = getHook(this.caps, this.caps[Capabilities.SIMPLEREST_START_HOOK])
58
59
  this.stopHook = getHook(this.caps, this.caps[Capabilities.SIMPLEREST_STOP_HOOK])
59
60
  this.requestHook = getHook(this.caps, this.caps[Capabilities.SIMPLEREST_REQUEST_HOOK])
61
+ this.parserHook = getHook(this.caps, this.caps[Capabilities.SIMPLEREST_PARSER_HOOK])
60
62
  this.responseHook = getHook(this.caps, this.caps[Capabilities.SIMPLEREST_RESPONSE_HOOK])
63
+ this.pollRequestHook = getHook(this.caps, this.caps[Capabilities.SIMPLEREST_POLL_REQUEST_HOOK])
64
+
65
+ this.requestHooks = {}
66
+ if (this.caps[Capabilities.SIMPLEREST_PING_REQUEST_HOOK]) {
67
+ this.requestHooks.SIMPLEREST_PING = getHook(this.caps, this.caps[Capabilities.SIMPLEREST_PING_REQUEST_HOOK])
68
+ }
69
+ if (this.caps[Capabilities.SIMPLEREST_START_REQUEST_HOOK]) {
70
+ this.requestHooks.SIMPLEREST_START = getHook(this.caps, this.caps[Capabilities.SIMPLEREST_START_REQUEST_HOOK])
71
+ }
72
+ if (this.caps[Capabilities.SIMPLEREST_STOP_REQUEST_HOOK]) {
73
+ this.requestHooks.SIMPLEREST_STOP = getHook(this.caps, this.caps[Capabilities.SIMPLEREST_STOP_REQUEST_HOOK])
74
+ }
61
75
  }
62
76
 
63
77
  Build () {
@@ -111,18 +125,34 @@ module.exports = class SimpleRestContainer {
111
125
  executeHook(this.caps, this.startHook, this.view).then(() => startHookComplete()).catch(startHookComplete)
112
126
  },
113
127
 
128
+ (inboundListenerComplete) => {
129
+ this._subscribeInbound()
130
+ .then(() => inboundListenerComplete())
131
+ .catch(inboundListenerComplete)
132
+ },
133
+
134
+ (startPollingComplete) => {
135
+ this._startPolling()
136
+ .then(() => startPollingComplete())
137
+ .catch(startPollingComplete)
138
+ },
139
+
114
140
  (pingComplete) => {
115
141
  if (this.caps[Capabilities.SIMPLEREST_PING_URL]) {
116
142
  this._makeCall('SIMPLEREST_PING')
117
- .then(response => {
143
+ .then(body => {
118
144
  if (this.caps[Capabilities.SIMPLEREST_PING_UPDATE_CONTEXT] || this.caps[Capabilities.SIMPLEREST_PING_PROCESS_RESPONSE]) {
119
- if (_.isObject(response) || botiumUtils.isStringJson(response)) {
120
- debug(`Ping Uri ${this.caps[Capabilities.SIMPLEREST_PING_URL]} returned JSON response: ${botiumUtils.shortenJsonString(response)}`)
121
- const body = _.isObject(response) ? response : JSON.parse(response)
122
- return this._processBodyAsync(body, !!this.caps[Capabilities.SIMPLEREST_PING_PROCESS_RESPONSE], !!this.caps[Capabilities.SIMPLEREST_PING_UPDATE_CONTEXT])
123
- } else {
124
- debug(`Ping Uri ${this.caps[Capabilities.SIMPLEREST_PING_URL]} didn't return JSON response, ignoring it.`)
125
- }
145
+ return this._parseResponseBody(body)
146
+ .then(body => {
147
+ if (body) {
148
+ debug(`Ping Uri ${this.caps[Capabilities.SIMPLEREST_PING_URL]} returned JSON response: ${botiumUtils.shortenJsonString(body)}`)
149
+ return this._processBodyAsync(body, !!this.caps[Capabilities.SIMPLEREST_PING_PROCESS_RESPONSE], !!this.caps[Capabilities.SIMPLEREST_PING_UPDATE_CONTEXT])
150
+ } else {
151
+ debug(`Ping Uri ${this.caps[Capabilities.SIMPLEREST_PING_URL]} didn't return JSON response, ignoring it.`)
152
+ }
153
+ }).catch(err => {
154
+ debug(`Ping Uri ${this.caps[Capabilities.SIMPLEREST_PING_URL]} didn't return JSON response, ignoring it (${err.message})`)
155
+ })
126
156
  }
127
157
  }).then(() => {
128
158
  pingComplete()
@@ -136,37 +166,29 @@ module.exports = class SimpleRestContainer {
136
166
 
137
167
  (initComplete) => {
138
168
  if (_.isString(this.caps[Capabilities.SIMPLEREST_INIT_TEXT])) {
139
- this._doRequest({ messageText: this.caps[Capabilities.SIMPLEREST_INIT_TEXT] }, false, true).then(() => initComplete()).catch(initComplete)
169
+ this._doRequest({ messageText: this.caps[Capabilities.SIMPLEREST_INIT_TEXT] }, !!this.caps[Capabilities.SIMPLEREST_INIT_PROCESS_RESPONSE], true).then(() => initComplete()).catch(initComplete)
140
170
  } else {
141
171
  initComplete()
142
172
  }
143
173
  },
144
174
 
145
- (inboundListenerComplete) => {
146
- this._subscribeInbound()
147
- .then(() => inboundListenerComplete())
148
- .catch(inboundListenerComplete)
149
- },
150
-
151
- (startPollingComplete) => {
152
- this._startPolling()
153
- .then(() => startPollingComplete())
154
- .catch(startPollingComplete)
155
- },
156
-
157
175
  (startCallComplete) => {
158
176
  this.processInbound = true
159
177
  if (this.caps[Capabilities.SIMPLEREST_START_URL]) {
160
178
  this._makeCall('SIMPLEREST_START')
161
- .then(response => {
179
+ .then(body => {
162
180
  if (this.caps[Capabilities.SIMPLEREST_START_UPDATE_CONTEXT] || this.caps[Capabilities.SIMPLEREST_START_PROCESS_RESPONSE]) {
163
- if (_.isObject(response) || botiumUtils.isStringJson(response)) {
164
- debug(`Start Uri ${this.caps[Capabilities.SIMPLEREST_START_URL]} returned JSON response: ${botiumUtils.shortenJsonString(response)}`)
165
- const body = _.isObject(response) ? response : JSON.parse(response)
166
- return this._processBodyAsync(body, !!this.caps[Capabilities.SIMPLEREST_START_PROCESS_RESPONSE], !!this.caps[Capabilities.SIMPLEREST_START_UPDATE_CONTEXT])
167
- } else {
168
- debug(`Start Uri ${this.caps[Capabilities.SIMPLEREST_START_URL]} didn't return JSON response, ignoring it.`)
169
- }
181
+ return this._parseResponseBody(body)
182
+ .then(body => {
183
+ if (body) {
184
+ debug(`Start Uri ${this.caps[Capabilities.SIMPLEREST_START_URL]} returned JSON response: ${botiumUtils.shortenJsonString(body)}`)
185
+ return this._processBodyAsync(body, !!this.caps[Capabilities.SIMPLEREST_START_PROCESS_RESPONSE], !!this.caps[Capabilities.SIMPLEREST_START_UPDATE_CONTEXT])
186
+ } else {
187
+ debug(`Start Uri ${this.caps[Capabilities.SIMPLEREST_START_URL]} didn't return JSON response, ignoring it.`)
188
+ }
189
+ }).catch(err => {
190
+ debug(`Start Uri ${this.caps[Capabilities.SIMPLEREST_START_URL]} didn't return JSON response, ignoring it (${err.message})`)
191
+ })
170
192
  }
171
193
  }).then(() => {
172
194
  startCallComplete()
@@ -357,27 +379,40 @@ module.exports = class SimpleRestContainer {
357
379
 
358
380
  this.waitProcessQueue = []
359
381
 
360
- request(requestOptions, (err, response, body) => {
382
+ request(requestOptions, async (err, response, body) => {
361
383
  if (err) {
362
- reject(new Error(`rest request failed: ${err.message}`))
384
+ return reject(new Error(`rest request failed: ${err.message}`))
363
385
  } else {
364
386
  if (response.statusCode >= 400) {
365
387
  debug(`got error response: ${response.statusCode}/${response.statusMessage}`)
388
+ if (debug.enabled && body) {
389
+ debug(botiumUtils.shortenJsonString(body))
390
+ }
391
+ if (body) {
392
+ const jsonBody = botiumUtils.toJsonWeak(body)
393
+ const errKey = Object.keys(jsonBody).find(k => k.startsWith('err') || k.startsWith('fail'))
394
+ if (errKey) {
395
+ return reject(new BotiumError(`got error response: ${response.statusCode}/${response.statusMessage} - ${jsonBody[errKey]}`, {
396
+ message: botiumUtils.shortenJsonString(body)
397
+ }))
398
+ }
399
+ }
366
400
  return reject(new Error(`got error response: ${response.statusCode}/${response.statusMessage}`))
367
401
  }
368
402
 
369
403
  if (body) {
370
404
  debug(`got response code: ${response.statusCode}, body: ${botiumUtils.shortenJsonString(body)}`)
371
- if (_.isString(body)) {
372
- try {
373
- body = JSON.parse(body.trim())
374
- this._processBodyAsync(body, isFromUser, updateContext).then(() => resolve(this)).then(() => this._emptyWaitProcessQueue())
375
- } catch (err) {
376
- debug(`ignoring not JSON formatted response body (${err.message})`)
377
- resolve(this)
378
- this._emptyWaitProcessQueue()
379
- }
380
- } else if (_.isObject(body)) {
405
+
406
+ try {
407
+ body = await this._parseResponseBody(body)
408
+ } catch (err) {
409
+ debug(`ignoring not JSON formatted response body: ${err.message}`)
410
+ resolve(this)
411
+ this._emptyWaitProcessQueue()
412
+ return
413
+ }
414
+
415
+ if (body) {
381
416
  this._processBodyAsync(body, isFromUser, updateContext).then(() => resolve(this)).then(() => this._emptyWaitProcessQueue())
382
417
  } else {
383
418
  debug('ignoring response body (no string and no JSON object)')
@@ -453,6 +488,15 @@ module.exports = class SimpleRestContainer {
453
488
  requestOptions.uri = `${requestOptions.uri}?${appendToUri}`
454
489
  }
455
490
  }
491
+ if (msg.ADD_FORM_PARAM && Object.keys(msg.ADD_FORM_PARAM).length > 0) {
492
+ requestOptions.form = {}
493
+ for (const formKey of Object.keys(msg.ADD_FORM_PARAM)) {
494
+ const formValue = this._getMustachedVal(
495
+ _.isString(msg.ADD_FORM_PARAM[formKey]) ? msg.ADD_FORM_PARAM[formKey] : JSON.stringify(msg.ADD_FORM_PARAM[formKey]),
496
+ false)
497
+ requestOptions.form[formKey] = formValue
498
+ }
499
+ }
456
500
  if (msg.ADD_HEADER && Object.keys(msg.ADD_HEADER).length > 0) {
457
501
  requestOptions.headers = requestOptions.headers || {}
458
502
 
@@ -494,14 +538,36 @@ module.exports = class SimpleRestContainer {
494
538
  await timeout(pingConfig.timeout)
495
539
  } else if (response.statusCode >= 400) {
496
540
  debug(`_waitForUrlResponse on url check ${pingConfig.uri} got error response: ${response.statusCode}/${response.statusMessage}`)
541
+ if (debug.enabled && body) {
542
+ debug(botiumUtils.shortenJsonString(body))
543
+ }
497
544
  await timeout(pingConfig.timeout)
498
545
  } else {
499
- debug(`_waitForUrlResponse success on url check ${pingConfig.uri}`)
546
+ debug(`_waitForUrlResponse success on url check ${pingConfig.uri}: ${response.statusCode}/${response.statusMessage}`)
547
+ if (debug.enabled && body) {
548
+ debug(botiumUtils.shortenJsonString(body))
549
+ }
500
550
  return body
501
551
  }
502
552
  }
503
553
  }
504
554
 
555
+ async _parseResponseBody (body) {
556
+ if (!_.isObject(body) && _.isString(body)) {
557
+ try {
558
+ body = JSON.parse(body)
559
+ } catch (err) {
560
+ if (!this.parserHook) throw err
561
+ }
562
+ }
563
+ if (this.parserHook) {
564
+ await executeHook(this.caps, this.parserHook, Object.assign({ body, changeBody: (b) => { body = b } }, this.view))
565
+ }
566
+ if (_.isObject(body)) return body
567
+ else if (_.isString(body)) return JSON.parse(body)
568
+ else return null
569
+ }
570
+
505
571
  _getCapValue (capName) {
506
572
  return _.isFunction(this.caps[capName]) ? (this.caps[capName])() : this.caps[capName]
507
573
  }
@@ -629,7 +695,7 @@ module.exports = class SimpleRestContainer {
629
695
  }
630
696
  }
631
697
 
632
- _runPolling () {
698
+ async _runPolling () {
633
699
  if (!this.processInbound) return
634
700
 
635
701
  if (this.caps[Capabilities.SIMPLEREST_POLL_URL]) {
@@ -662,22 +728,31 @@ module.exports = class SimpleRestContainer {
662
728
  }
663
729
  this._addRequestOptions(pollConfig)
664
730
 
665
- request(pollConfig, (err, response, body) => {
731
+ try {
732
+ await executeHook(this.caps, this.pollRequestHook, Object.assign({ requestOptions: pollConfig }, this.view))
733
+ } catch (err) {
734
+ debug(`_runPolling: exeucting request hook failed - (${err.message})`)
735
+ return
736
+ }
737
+ request(pollConfig, async (err, response, body) => {
666
738
  if (err) {
667
739
  debug(`_runPolling: rest request failed: ${err.message}, request: ${JSON.stringify(pollConfig)}`)
668
740
  } else {
669
741
  if (response.statusCode >= 400) {
670
742
  debug(`_runPolling: got error response: ${response.statusCode}/${response.statusMessage}, request: ${JSON.stringify(pollConfig)}`)
743
+ if (debug.enabled && body) {
744
+ debug(botiumUtils.shortenJsonString(body))
745
+ }
671
746
  } else if (body) {
672
747
  debug(`_runPolling: got response code: ${response.statusCode}, body: ${botiumUtils.shortenJsonString(body)}`)
673
- if (_.isString(body)) {
674
- try {
675
- body = JSON.parse(body)
676
- setTimeout(() => this._processBodyAsync(body, true, !!this.caps[Capabilities.SIMPLEREST_POLL_UPDATE_CONTEXT]), 0)
677
- } catch (err) {
678
- debug(`_runPolling: ignoring not JSON formatted response body (${err.message})`)
679
- }
680
- } else if (_.isObject(body)) {
748
+
749
+ try {
750
+ body = await this._parseResponseBody(body)
751
+ } catch (err) {
752
+ debug(`_runPolling: ignoring not JSON formatted response body: ${err.message}`)
753
+ return
754
+ }
755
+ if (body) {
681
756
  setTimeout(() => this._processBodyAsync(body, true, !!this.caps[Capabilities.SIMPLEREST_POLL_UPDATE_CONTEXT]), 0)
682
757
  } else {
683
758
  debug('_runPolling: ignoring response body (no string and no JSON object)')
@@ -733,6 +808,8 @@ module.exports = class SimpleRestContainer {
733
808
  }
734
809
  this._addRequestOptions(httpConfig)
735
810
 
811
+ await executeHook(this.caps, this.requestHooks[capPrefix], Object.assign({ requestOptions: httpConfig }, this.view))
812
+
736
813
  const retries = this._getCapValue(`${capPrefix}_RETRIES`)
737
814
  debug(`_makeCall(${capPrefix}): rest request: ${JSON.stringify(httpConfig)}`)
738
815
  const response = await this._waitForUrlResponse(httpConfig, retries)
@@ -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
 
@@ -37,6 +37,24 @@ const wildcard = (ignoreCase) => (botresponse, utterance) => {
37
37
  return regexp.test(botresponse)
38
38
  }
39
39
 
40
+ const wildcardExact = (ignoreCase) => (botresponse, utterance) => {
41
+ if (_.isUndefined(botresponse)) {
42
+ if (utterance.trim() === '*') return true
43
+ else return false
44
+ }
45
+ utterance = toString(utterance)
46
+ botresponse = _normalize(botresponse)
47
+
48
+ const numWildcards = utterance.split('*').length - 1
49
+ if (numWildcards > 10) {
50
+ throw new Error('Maximum number of 10 wildcards supported.')
51
+ }
52
+ const utteranceRe = '^' + quoteRegexpString(utterance).replace(/\\\*/g, '(.*)') + '$'
53
+
54
+ const regexp = ignoreCase ? (new RegExp(utteranceRe, 'i')) : (new RegExp(utteranceRe, ''))
55
+ return regexp.test(botresponse)
56
+ }
57
+
40
58
  const include = (ignoreCase) => (botresponse, utterance) => {
41
59
  if (_.isUndefined(botresponse)) return false
42
60
  utterance = toString(utterance)
@@ -66,6 +84,8 @@ const getMatchFunction = (matchingMode) => {
66
84
  return regexp(matchingMode === 'regexpIgnoreCase')
67
85
  } else if (matchingMode === 'wildcard' || matchingMode === 'wildcardIgnoreCase' || matchingMode === 'wildcardLowerCase') {
68
86
  return wildcard(matchingMode === 'wildcardIgnoreCase' || matchingMode === 'wildcardLowerCase')
87
+ } else if (matchingMode === 'wildcardExact' || matchingMode === 'wildcardExactIgnoreCase') {
88
+ return wildcardExact(matchingMode === 'wildcardExactIgnoreCase')
69
89
  } else if (matchingMode === 'include' || matchingMode === 'includeIgnoreCase' || matchingMode === 'includeLowerCase') {
70
90
  return include(matchingMode === 'includeIgnoreCase' || matchingMode === 'includeLowerCase')
71
91
  } else if (matchingMode === 'equals' || matchingMode === 'equalsIgnoreCase') {
@@ -78,6 +98,7 @@ const getMatchFunction = (matchingMode) => {
78
98
  module.exports = {
79
99
  regexp,
80
100
  wildcard,
101
+ wildcardExact,
81
102
  include,
82
103
  equals,
83
104
  getMatchFunction