botium-core 1.12.6 → 1.13.2

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "botium-core",
3
- "version": "1.12.6",
3
+ "version": "1.13.2",
4
4
  "description": "The Selenium for Chatbots",
5
5
  "main": "index.js",
6
6
  "module": "dist/botium-es.js",
@@ -32,25 +32,25 @@
32
32
  },
33
33
  "homepage": "https://www.botium.ai",
34
34
  "dependencies": {
35
- "@babel/runtime": "^7.17.9",
36
- "async": "^3.2.3",
35
+ "@babel/runtime": "^7.18.6",
36
+ "async": "^3.2.4",
37
37
  "body-parser": "^1.20.0",
38
38
  "boolean": "^3.2.0",
39
39
  "bottleneck": "^2.19.5",
40
- "csv-parse": "^5.0.4",
40
+ "csv-parse": "^5.3.0",
41
41
  "debug": "^4.3.4",
42
42
  "esprima": "^4.0.1",
43
- "express": "^4.17.3",
43
+ "express": "^4.18.1",
44
44
  "globby": "11.0.4",
45
- "ioredis": "^5.0.4",
45
+ "ioredis": "^5.1.0",
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.3.2",
50
+ "markdown-it": "^13.0.1",
51
51
  "mime-types": "^2.1.35",
52
52
  "mkdirp": "^1.0.4",
53
- "moment": "^2.29.3",
53
+ "moment": "^2.29.4",
54
54
  "mustache": "^4.2.0",
55
55
  "promise-retry": "^2.0.1",
56
56
  "promise.allsettled": "^1.0.5",
@@ -59,39 +59,39 @@
59
59
  "rimraf": "^3.0.2",
60
60
  "sanitize-filename": "^1.6.3",
61
61
  "slugify": "^1.6.5",
62
- "socket.io": "^4.4.1",
63
- "socket.io-client": "^4.4.1",
62
+ "socket.io": "^4.5.1",
63
+ "socket.io-client": "^4.5.1",
64
64
  "socketio-auth": "^0.1.1",
65
65
  "swagger-jsdoc": "^6.2.1",
66
- "swagger-ui-express": "^4.3.0",
66
+ "swagger-ui-express": "^4.4.0",
67
67
  "uuid": "^8.3.2",
68
- "vm2": "^3.9.9",
68
+ "vm2": "^3.9.10",
69
69
  "write-yaml": "^1.0.0",
70
70
  "xlsx": "^0.18.5",
71
- "xregexp": "^5.1.0",
72
- "yaml": "^2.0.1"
71
+ "xregexp": "^5.1.1",
72
+ "yaml": "^2.1.1"
73
73
  },
74
74
  "devDependencies": {
75
- "@babel/core": "^7.17.9",
76
- "@babel/node": "^7.16.8",
77
- "@babel/plugin-transform-runtime": "^7.17.0",
78
- "@babel/preset-env": "^7.16.11",
75
+ "@babel/core": "^7.18.6",
76
+ "@babel/node": "^7.18.6",
77
+ "@babel/plugin-transform-runtime": "^7.18.6",
78
+ "@babel/preset-env": "^7.18.6",
79
79
  "chai": "^4.3.6",
80
80
  "chai-as-promised": "^7.1.1",
81
81
  "cross-env": "^7.0.3",
82
- "eslint": "^8.13.0",
82
+ "eslint": "^8.19.0",
83
83
  "eslint-config-standard": "^17.0.0",
84
84
  "eslint-plugin-import": "^2.26.0",
85
- "eslint-plugin-n": "^15.1.0",
85
+ "eslint-plugin-n": "^15.2.4",
86
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.2.2",
91
- "nock": "^13.2.4",
92
- "npm-check-updates": "^12.5.9",
90
+ "mocha": "^10.0.0",
91
+ "nock": "^13.2.8",
92
+ "npm-check-updates": "^15.2.6",
93
93
  "nyc": "^15.1.0",
94
- "rollup": "^2.70.2",
94
+ "rollup": "^2.76.0",
95
95
  "rollup-plugin-babel": "^4.4.0",
96
96
  "rollup-plugin-commonjs": "^10.1.0",
97
97
  "rollup-plugin-json": "^4.0.0",
@@ -105,6 +105,10 @@ module.exports = {
105
105
  SCRIPTING_XLSX_SHEETNAMES_PCONVOS: 'SCRIPTING_XLSX_SHEETNAMES_PCONVOS',
106
106
  SCRIPTING_XLSX_SHEETNAMES_UTTERANCES: 'SCRIPTING_XLSX_SHEETNAMES_UTTERANCES',
107
107
  SCRIPTING_XLSX_SHEETNAMES_SCRIPTING_MEMORY: 'SCRIPTING_XLSX_SHEETNAMES_SCRIPTING_MEMORY',
108
+ // hidden capability. All newly in Box created testsets will have this as true. CsvCompiler
109
+ // - throws less error (Box reads csv files as utterances, and convo. Compiler cant throw exception if a file is correct, but box tries to load it with incorrect script type)
110
+ // 4 or more colums are compiled just as utterances.
111
+ SCRIPTING_CSV_LEGACY_MODE_OFF: 'SCRIPTING_CSV_LEGACY_MODE_OFF',
108
112
  SCRIPTING_CSV_DELIMITER: 'SCRIPTING_CSV_DELIMITER',
109
113
  SCRIPTING_CSV_SKIP_HEADER: 'SCRIPTING_CSV_SKIP_HEADER',
110
114
  SCRIPTING_CSV_QUOTE: 'SCRIPTING_CSV_QUOTE',
@@ -114,6 +118,9 @@ module.exports = {
114
118
  SCRIPTING_CSV_MULTIROW_COLUMN_TEXT: 'SCRIPTING_CSV_MULTIROW_COLUMN_TEXT',
115
119
  SCRIPTING_CSV_QA_COLUMN_QUESTION: 'SCRIPTING_CSV_QA_COLUMN_QUESTION',
116
120
  SCRIPTING_CSV_QA_COLUMN_ANSWER: 'SCRIPTING_CSV_QA_COLUMN_ANSWER',
121
+ SCRIPTING_CSV_UTTERANCE_STARTROW: 'SCRIPTING_CSV_UTTERANCE_STARTROW',
122
+ SCRIPTING_CSV_UTTERANCE_STARTROW_HEADER: 'SCRIPTING_CSV_UTTERANCE_STARTROW_HEADER',
123
+ SCRIPTING_CSV_UTTERANCE_STOP_ON_EMPTY: 'SCRIPTING_CSV_UTTERANCE_STOP_ON_EMPTY',
117
124
  SCRIPTING_NORMALIZE_TEXT: 'SCRIPTING_NORMALIZE_TEXT',
118
125
  SCRIPTING_ENABLE_MEMORY: 'SCRIPTING_ENABLE_MEMORY',
119
126
  SCRIPTING_ENABLE_MULTIPLE_ASSERT_ERRORS: 'SCRIPTING_ENABLE_MULTIPLE_ASSERT_ERRORS',
@@ -520,6 +520,9 @@ module.exports = class SimpleRestContainer {
520
520
  try {
521
521
  requestOptions.body = this._getMustachedCap(Capabilities.SIMPLEREST_BODY_TEMPLATE, !bodyRaw)
522
522
  requestOptions.json = !bodyRaw
523
+ if (requestOptions.json && (!requestOptions.body || Object.keys(requestOptions.body).length === 0)) {
524
+ debug(`warning: requestOptions.body content seems to be empty - ${requestOptions.body} - capability: "${this.caps[Capabilities.SIMPLEREST_BODY_TEMPLATE]}"`)
525
+ }
523
526
  } catch (err) {
524
527
  throw new Error(`composing body from SIMPLEREST_BODY_TEMPLATE failed (${err.message})`)
525
528
  }
@@ -1,13 +1,9 @@
1
1
  const util = require('util')
2
2
  const _ = require('lodash')
3
+ const debug = require('debug')('botium-core-RetryHelper')
3
4
 
4
5
  module.exports = class RetryHelper {
5
- constructor (caps, section) {
6
- this.retrySettings = {
7
- retries: caps[`RETRY_${section.toUpperCase()}_NUMRETRIES`] || 1,
8
- factor: caps[`RETRY_${section.toUpperCase()}_FACTOR`] || 1,
9
- minTimeout: caps[`RETRY_${section.toUpperCase()}_MINTIMEOUT`] || 1000
10
- }
6
+ constructor (caps, section, options = {}) {
11
7
  this.retryErrorPatterns = []
12
8
  const onErrorRegexp = caps[`RETRY_${section.toUpperCase()}_ONERROR_REGEXP`] || []
13
9
  if (onErrorRegexp) {
@@ -22,10 +18,20 @@ module.exports = class RetryHelper {
22
18
  this.retryErrorPatterns.push(onErrorRegexp)
23
19
  }
24
20
  }
21
+
22
+ // to turn on retries, NUMRETRIES or ONERROR_REGEXP has to be set
23
+ this.retrySettings = {
24
+ retries: caps[`RETRY_${section.toUpperCase()}_NUMRETRIES`] || (!_.isNil(options.numRetries) ? options.numRetries : (this.retryErrorPatterns.length === 0) ? 0 : 1),
25
+ factor: caps[`RETRY_${section.toUpperCase()}_FACTOR`] || (_.isNil(options.factor) ? 1 : options.factor),
26
+ minTimeout: caps[`RETRY_${section.toUpperCase()}_MINTIMEOUT`] || (_.isNil(options.minTimeout) ? 1000 : options.minTimeout)
27
+ }
28
+
29
+ debug(`Retry for ${section} is ${this.retrySettings.retries > 0 ? 'enabled' : 'disabled'}. Settings: ${JSON.stringify(this.retrySettings)} Patterns: ${JSON.stringify(this.retryErrorPatterns.map(r => r.toString()))}`)
25
30
  }
26
31
 
27
32
  shouldRetry (err) {
28
- if (!err || this.retryErrorPatterns.length === 0) return false
33
+ if (!err) return false
34
+ if (this.retryErrorPatterns.length === 0) return true
29
35
  const errString = util.inspect(err)
30
36
  for (const re of this.retryErrorPatterns) {
31
37
  if (errString.match(re)) return true
@@ -47,6 +47,7 @@ module.exports = class CompilerCsv extends CompilerBase {
47
47
  if (scriptData.length === 0) {
48
48
  return []
49
49
  }
50
+ const legacyModeOn = !this._GetOptionalCapability(Capabilities.SCRIPTING_CSV_LEGACY_MODE_OFF, false)
50
51
 
51
52
  let delimiter = this._GetOptionalCapability(Capabilities.SCRIPTING_CSV_DELIMITER)
52
53
  if (!delimiter) {
@@ -71,7 +72,8 @@ module.exports = class CompilerCsv extends CompilerBase {
71
72
  delimiter,
72
73
  escape: this.caps[Capabilities.SCRIPTING_CSV_ESCAPE],
73
74
  quote: this.caps[Capabilities.SCRIPTING_CSV_QUOTE],
74
- columns: false
75
+ columns: false,
76
+ relax_column_count: true
75
77
  })
76
78
  } catch (err) {
77
79
  throw new Error(`Invalid CSV: ${err.message || err}`)
@@ -79,123 +81,169 @@ module.exports = class CompilerCsv extends CompilerBase {
79
81
  if (rows.length === 0) {
80
82
  return []
81
83
  }
82
- if (rows[0].length === 1) {
83
- debug('Found 1-column CSV file, treating it as utterance file')
84
- if (scriptType === Constants.SCRIPTING_TYPE_UTTERANCES) {
85
- const result = [{ name: rows[0][0], utterances: rows.slice(1).map(r => r[0]) }]
86
- this.context.AddUtterances(result)
87
- return result
88
- } else {
84
+ const columnCount = rows[0].length
85
+ debug(`Legacy mode ${legacyModeOn ? 'on' : 'off'} rows ${rows.length} columns ${columnCount}`)
86
+
87
+ if ((scriptType === Constants.SCRIPTING_TYPE_CONVO || scriptType === Constants.SCRIPTING_TYPE_PCONVO)) {
88
+ if (columnCount === 1 || (!legacyModeOn && columnCount > 3)) {
89
+ debug(`Invalid column count '${columnCount}' in convo mode`)
90
+ return []
91
+ }
92
+ let header = null
93
+ if (rows.length > 0 && this.caps[Capabilities.SCRIPTING_CSV_SKIP_HEADER]) {
94
+ header = rows[0]
95
+ rows = rows.slice(1)
96
+ }
97
+ if (rows.length === 0) {
98
+ debug('Datarows not found in convo mode')
89
99
  return []
90
100
  }
91
- }
92
-
93
- if (scriptType !== Constants.SCRIPTING_TYPE_CONVO && scriptType !== Constants.SCRIPTING_TYPE_PCONVO) {
94
- return []
95
- }
96
- let header = null
97
- if (rows.length > 0 && this.caps[Capabilities.SCRIPTING_CSV_SKIP_HEADER]) {
98
- header = rows[0]
99
- rows = rows.slice(1)
100
- }
101
- if (rows.length === 0) {
102
- return []
103
- }
104
-
105
- const lineNumberBase = this.caps[Capabilities.SCRIPTING_CSV_SKIP_HEADER] ? 2 : 1
106
- if (rows[0].length === 2) {
107
- debug('Found 2-column CSV file, treating it as question/answer file')
108
101
 
109
- let colQuestion = DEFAULT_QA_COLUMN_QUESTION
110
- let colAnswer = DEFAULT_QA_COLUMN_ANSWER
102
+ const lineNumberBase = this.caps[Capabilities.SCRIPTING_CSV_SKIP_HEADER] ? 2 : 1
103
+ if (columnCount === 2) {
104
+ let colQuestion = DEFAULT_QA_COLUMN_QUESTION
105
+ let colAnswer = DEFAULT_QA_COLUMN_ANSWER
111
106
 
112
- if (header) {
113
- if (this.caps[Capabilities.SCRIPTING_CSV_QA_COLUMN_QUESTION] !== undefined) {
114
- colQuestion = _findColIndex(header, this.caps[Capabilities.SCRIPTING_CSV_QA_COLUMN_QUESTION])
107
+ if (header) {
108
+ if (this.caps[Capabilities.SCRIPTING_CSV_QA_COLUMN_QUESTION] !== undefined) {
109
+ colQuestion = _findColIndex(header, this.caps[Capabilities.SCRIPTING_CSV_QA_COLUMN_QUESTION])
110
+ }
111
+ if (this.caps[Capabilities.SCRIPTING_CSV_QA_COLUMN_ANSWER] !== undefined) {
112
+ colAnswer = _findColIndex(header, this.caps[Capabilities.SCRIPTING_CSV_QA_COLUMN_ANSWER])
113
+ }
115
114
  }
116
- if (this.caps[Capabilities.SCRIPTING_CSV_QA_COLUMN_ANSWER] !== undefined) {
117
- colAnswer = _findColIndex(header, this.caps[Capabilities.SCRIPTING_CSV_QA_COLUMN_ANSWER])
115
+
116
+ const convos = rows.map((row, i) => new Convo(this.context, {
117
+ header: {
118
+ name: `L${i + lineNumberBase}`
119
+ },
120
+ conversation: [
121
+ Object.assign({},
122
+ linesToConvoStep(
123
+ [row[colQuestion]],
124
+ 'me',
125
+ this.context,
126
+ undefined,
127
+ true
128
+ ), { stepTag: `L${i + lineNumberBase}-Question` }),
129
+ Object.assign({},
130
+ linesToConvoStep(
131
+ [row[colAnswer]],
132
+ 'bot',
133
+ this.context,
134
+ undefined,
135
+ true
136
+ ), { stepTag: `L${i + lineNumberBase}-Answer` })
137
+ ]
138
+ }))
139
+ if (scriptType === Constants.SCRIPTING_TYPE_CONVO) {
140
+ this.context.AddConvos(convos)
141
+ } else if (scriptType === Constants.SCRIPTING_TYPE_PCONVO) {
142
+ this.context.AddPartialConvos(convos)
118
143
  }
144
+ debug(`Found 2-column CSV file, treating it as question/answer file, extracted ${convos.length} convos`)
145
+ return convos
119
146
  }
120
147
 
121
- const convos = rows.map((row, i) => new Convo(this.context, {
122
- header: {
123
- name: `L${i + lineNumberBase}`
124
- },
125
- conversation: [
126
- Object.assign({},
127
- linesToConvoStep(
128
- [row[colQuestion]],
129
- 'me',
130
- this.context,
131
- undefined,
132
- true
133
- ), { stepTag: `L${i + lineNumberBase}-Question` }),
134
- Object.assign({},
135
- linesToConvoStep(
136
- [row[colAnswer]],
137
- 'bot',
138
- this.context,
139
- undefined,
140
- true
141
- ), { stepTag: `L${i + lineNumberBase}-Answer` })
142
- ]
143
- }))
144
- if (scriptType === Constants.SCRIPTING_TYPE_CONVO) {
145
- this.context.AddConvos(convos)
146
- } else if (scriptType === Constants.SCRIPTING_TYPE_PCONVO) {
147
- this.context.AddPartialConvos(convos)
148
- }
149
- return convos
150
- }
148
+ if (columnCount >= 3) {
149
+ let colConversationId = DEFAULT_MULTIROW_COLUMN_CONVERSATION
150
+ let colSender = DEFAULT_MULTIROW_COLUMN_SENDER
151
+ let colText = DEFAULT_MULTIROW_COLUMN_TEXT
151
152
 
152
- if (rows[0].length >= 3) {
153
- debug('Found 3-column CSV file, treating it as multi-row conversation file')
153
+ if (header) {
154
+ if (this.caps[Capabilities.SCRIPTING_CSV_MULTIROW_COLUMN_CONVERSATION_ID] !== undefined) {
155
+ colConversationId = _findColIndex(header, this.caps[Capabilities.SCRIPTING_CSV_MULTIROW_COLUMN_CONVERSATION_ID])
156
+ }
157
+ if (this.caps[Capabilities.SCRIPTING_CSV_MULTIROW_COLUMN_SENDER] !== undefined) {
158
+ colSender = _findColIndex(header, this.caps[Capabilities.SCRIPTING_CSV_MULTIROW_COLUMN_SENDER])
159
+ }
160
+ if (this.caps[Capabilities.SCRIPTING_CSV_MULTIROW_COLUMN_TEXT] !== undefined) {
161
+ colText = _findColIndex(header, this.caps[Capabilities.SCRIPTING_CSV_MULTIROW_COLUMN_TEXT])
162
+ }
163
+ }
154
164
 
155
- let colConversationId = DEFAULT_MULTIROW_COLUMN_CONVERSATION
156
- let colSender = DEFAULT_MULTIROW_COLUMN_SENDER
157
- let colText = DEFAULT_MULTIROW_COLUMN_TEXT
165
+ const conversationIds = _.uniq(rows.map(r => r[colConversationId]))
166
+ const convos = conversationIds.map(conversationId => {
167
+ const convoRows = rows.map((row, i) => {
168
+ if (row[colConversationId] === conversationId) {
169
+ return Object.assign({},
170
+ linesToConvoStep(
171
+ [row[colText]],
172
+ row[colSender],
173
+ this.context,
174
+ undefined,
175
+ true
176
+ ), { stepTag: `L${i + lineNumberBase}` })
177
+ }
178
+ return null
179
+ }).filter(c => c)
180
+ return new Convo(this.context, {
181
+ header: {
182
+ name: conversationId
183
+ },
184
+ conversation: convoRows
185
+ })
186
+ })
187
+ if (scriptType === Constants.SCRIPTING_TYPE_CONVO) {
188
+ this.context.AddConvos(convos)
189
+ } else if (scriptType === Constants.SCRIPTING_TYPE_PCONVO) {
190
+ this.context.AddPartialConvos(convos)
191
+ }
192
+ debug(`Found 3-column CSV file, treating it as multi-row conversation file, extracted ${convos.length} convos`)
193
+ return convos
194
+ }
195
+ } else if (scriptType === Constants.SCRIPTING_TYPE_UTTERANCES) {
196
+ if (columnCount === 2 || columnCount === 3 || (legacyModeOn && columnCount > 4)) {
197
+ debug(`Invalid column count '${columnCount}' in utterances mode`)
198
+ return []
199
+ }
200
+ const result = []
201
+ const startRow = this._GetOptionalCapability(Capabilities.SCRIPTING_CSV_UTTERANCE_STARTROW, 2) - 1
202
+ const startRowHeader = this._GetOptionalCapability(Capabilities.SCRIPTING_CSV_UTTERANCE_STARTROW_HEADER)
203
+ const stopOnEmpty = this._GetOptionalCapability(Capabilities.SCRIPTING_CSV_UTTERANCE_STOP_ON_EMPTY)
204
+
205
+ for (let col = 0; col < columnCount; col++) {
206
+ const name = rows[0][col]
207
+ if (!name || name.trim().length === 0) {
208
+ debug(`Column ${col + 1} has no header, skipping`)
209
+ continue
210
+ }
158
211
 
159
- if (header) {
160
- if (this.caps[Capabilities.SCRIPTING_CSV_MULTIROW_COLUMN_CONVERSATION_ID] !== undefined) {
161
- colConversationId = _findColIndex(header, this.caps[Capabilities.SCRIPTING_CSV_MULTIROW_COLUMN_CONVERSATION_ID])
212
+ const uttStruct = {
213
+ name,
214
+ utterances: []
215
+ }
216
+ let skip = !!startRowHeader
217
+ const getData = (row) => {
218
+ return rows[row][col] ? rows[row][col].trim() : false
162
219
  }
163
- if (this.caps[Capabilities.SCRIPTING_CSV_MULTIROW_COLUMN_SENDER] !== undefined) {
164
- colSender = _findColIndex(header, this.caps[Capabilities.SCRIPTING_CSV_MULTIROW_COLUMN_SENDER])
220
+ //
221
+ for (let row = startRow; row < rows.length && (skip || !stopOnEmpty || !!getData(row)); row++) { // eslint-disable-line no-unmodified-loop-condition
222
+ const data = getData(row)
223
+ if (!data) {
224
+ continue
225
+ }
226
+ if (!skip) {
227
+ uttStruct.utterances.push(data)
228
+ } else {
229
+ if (startRowHeader === rows[row][col]) {
230
+ skip = false
231
+ }
232
+ }
165
233
  }
166
- if (this.caps[Capabilities.SCRIPTING_CSV_MULTIROW_COLUMN_TEXT] !== undefined) {
167
- colText = _findColIndex(header, this.caps[Capabilities.SCRIPTING_CSV_MULTIROW_COLUMN_TEXT])
234
+ if (uttStruct.utterances.length === 0) {
235
+ // liveperson, skipping meta intents
236
+ debug(`Column ${col + 1} has no utterances, skipping`)
237
+ continue
168
238
  }
239
+ result.push(uttStruct)
169
240
  }
170
241
 
171
- const conversationIds = _.uniq(rows.map(r => r[colConversationId]))
172
- const convos = conversationIds.map(conversationId => {
173
- const convoRows = rows.map((row, i) => {
174
- if (row[colConversationId] === conversationId) {
175
- return Object.assign({},
176
- linesToConvoStep(
177
- [row[colText]],
178
- row[colSender],
179
- this.context,
180
- undefined,
181
- true
182
- ), { stepTag: `L${i + lineNumberBase}` })
183
- }
184
- return null
185
- }).filter(c => c)
186
- return new Convo(this.context, {
187
- header: {
188
- name: conversationId
189
- },
190
- conversation: convoRows
191
- })
192
- })
193
- if (scriptType === Constants.SCRIPTING_TYPE_CONVO) {
194
- this.context.AddConvos(convos)
195
- } else if (scriptType === Constants.SCRIPTING_TYPE_PCONVO) {
196
- this.context.AddPartialConvos(convos)
197
- }
198
- return convos
242
+ debug(`Multi-column utterance file, extracted ${result.length} utterances`)
243
+ this.context.AddUtterances(result)
244
+ return result
245
+ } else {
246
+ return []
199
247
  }
200
248
  }
201
249
  }
@@ -1,6 +1,7 @@
1
1
  const util = require('util')
2
2
  const _ = require('lodash')
3
3
  const debug = require('debug')('botium-core-Convo')
4
+ const promiseRetry = require('promise-retry')
4
5
 
5
6
  const BotiumMockMessage = require('../mocks/BotiumMockMessage')
6
7
  const Capabilities = require('../Capabilities')
@@ -8,6 +9,7 @@ const Events = require('../Events')
8
9
  const ScriptingMemory = require('./ScriptingMemory')
9
10
  const { BotiumError, botiumErrorFromErr, botiumErrorFromList } = require('./BotiumError')
10
11
  const { normalizeText, toString, removeBuffers, splitStringInNonEmptyLines } = require('./helper')
12
+ const RetryHelper = require('../helpers/RetryHelper')
11
13
 
12
14
  const { LOGIC_HOOK_INCLUDE } = require('./logichook/LogicHookConsts')
13
15
 
@@ -210,6 +212,24 @@ class Convo {
210
212
  }
211
213
 
212
214
  async Run (container) {
215
+ const retryHelper = new RetryHelper(container.caps, 'CONVO')
216
+ return promiseRetry(async (retry, number) => {
217
+ return this.RunImpl(container).catch(err => {
218
+ const retryRemaining = retryHelper.retrySettings.retries - number + 1
219
+ if (retryHelper.shouldRetry(err)) {
220
+ debug(`Convo failed with error "${err.message || JSON.stringify(err)}". Retry ${retryRemaining > 0 ? 'enabled' : 'disabled'} (remaining #${retryRemaining}/${retryHelper.retrySettings.retries}, criterion matches)`)
221
+ retry(err)
222
+ } else {
223
+ if (retryHelper.retryErrorPatterns.length > 0) {
224
+ debug(`Convo failed with error "${err.message || JSON.stringify(err)}". Retry 'disabled' (remaining (#${retryRemaining}/${retryHelper.retrySettings.retries}), criterion does not match)`)
225
+ }
226
+ throw err
227
+ }
228
+ })
229
+ }, retryHelper.retrySettings)
230
+ }
231
+
232
+ async RunImpl (container) {
213
233
  const transcript = new Transcript({
214
234
  steps: [],
215
235
  attachments: [],
@@ -20,7 +20,7 @@ const RetryHelper = require('../helpers/RetryHelper')
20
20
  const { getMatchFunction } = require('./MatchFunctions')
21
21
  const precompilers = require('./precompilers')
22
22
 
23
- const globPattern = '**/+(*.convo.txt|*.utterances.txt|*.pconvo.txt|*.scriptingmemory.txt|*.xlsx|*.xlsm|*.convo.csv|*.pconvo.csv|*.yaml|*.yml|*.json|*.md|*.markdown)'
23
+ const globPattern = '**/+(*.convo.txt|*.utterances.txt|*.pconvo.txt|*.scriptingmemory.txt|*.xlsx|*.xlsm|*.convo.csv|*.pconvo.csv|*.utterances.csv|*.yaml|*.yml|*.json|*.md|*.markdown)'
24
24
  const skipPattern = /^skip[.\-_]/i
25
25
 
26
26
  const p = (retryHelper, fn) => {
@@ -522,6 +522,10 @@ module.exports = class ScriptingProvider {
522
522
  result = this.ReadScriptFromBuffer(scriptBuffer, Constants.SCRIPTING_FORMAT_CSV, Constants.SCRIPTING_TYPE_CONVO)
523
523
  } else if (filename.endsWith('.pconvo.csv')) {
524
524
  result = this.ReadScriptFromBuffer(scriptBuffer, Constants.SCRIPTING_FORMAT_CSV, Constants.SCRIPTING_TYPE_PCONVO)
525
+ } else if (filename.endsWith('.pconvo.csv')) {
526
+ result = this.ReadScriptFromBuffer(scriptBuffer, Constants.SCRIPTING_FORMAT_CSV, Constants.SCRIPTING_TYPE_PCONVO)
527
+ } else if (filename.endsWith('.utterance.csv')) {
528
+ result = this.ReadScriptFromBuffer(scriptBuffer, Constants.SCRIPTING_FORMAT_CSV, Constants.SCRIPTING_TYPE_UTTERANCES)
525
529
  } else if (filename.endsWith('.yaml') || filename.endsWith('.yml')) {
526
530
  result = this.ReadScriptFromBuffer(scriptBuffer, Constants.SCRIPTING_FORMAT_YAML, [Constants.SCRIPTING_TYPE_UTTERANCES, Constants.SCRIPTING_TYPE_PCONVO, Constants.SCRIPTING_TYPE_CONVO, Constants.SCRIPTING_TYPE_SCRIPTING_MEMORY])
527
531
  } else if (filename.endsWith('.json')) {
@@ -192,7 +192,7 @@ module.exports = class LogicHookUtils {
192
192
  })
193
193
  return vm.run(script)
194
194
  } catch (err) {
195
- throw new Error(`${err.message || err}`)
195
+ throw new Error(`Script ${key} is not valid - ${err.message || err}`)
196
196
  }
197
197
  } else {
198
198
  throw new Error(`Script "${key}" is not valid - only functions and javascript code accepted`)