@usebruno/js 0.13.0 → 0.14.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@usebruno/js",
3
- "version": "0.13.0",
3
+ "version": "0.14.0",
4
4
  "license": "MIT",
5
5
  "main": "src/index.js",
6
6
  "files": [
@@ -8,12 +8,13 @@
8
8
  "package.json"
9
9
  ],
10
10
  "peerDependencies": {
11
- "@n8n/vm2": "^3.9.23"
11
+ "@usebruno/vm2": "^3.9.13"
12
12
  },
13
13
  "scripts": {
14
14
  "test": "jest --testPathIgnorePatterns test.js"
15
15
  },
16
16
  "dependencies": {
17
+ "@usebruno/common": "0.2.0",
17
18
  "@usebruno/query": "0.1.0",
18
19
  "ajv": "^8.12.0",
19
20
  "ajv-formats": "^2.1.1",
@@ -23,7 +24,6 @@
23
24
  "chai": "^4.3.7",
24
25
  "chai-string": "^1.5.0",
25
26
  "crypto-js": "^4.1.1",
26
- "handlebars": "^4.7.8",
27
27
  "json-query": "^2.2.2",
28
28
  "lodash": "^4.17.21",
29
29
  "moment": "^2.29.4",
package/src/bru.js CHANGED
@@ -1,6 +1,6 @@
1
- const Handlebars = require('handlebars');
2
1
  const { cloneDeep } = require('lodash');
3
2
  const { uuid } = require('./utils');
3
+ const { interpolate } = require('@usebruno/common');
4
4
 
5
5
  const variableNameRegex = /^[\w-.]*$/;
6
6
 
@@ -12,31 +12,36 @@ class Bru {
12
12
  collectionPath,
13
13
  historyLogger,
14
14
  setHtmlVisualizationString,
15
- secretVariables
15
+ secretVariables,
16
+ requestVariables
16
17
  ) {
17
- this.envVariables = envVariables;
18
- this.collectionVariables = collectionVariables;
18
+ this.envVariables = envVariables || {};
19
+ this.collectionVariables = collectionVariables || {};
19
20
  this.processEnvVars = cloneDeep(processEnvVars || {});
20
21
  this.secretVariables = cloneDeep(secretVariables || {});
22
+ this.requestVariables = requestVariables || {};
21
23
  this.collectionPath = collectionPath;
22
24
  this.historyLogger = historyLogger;
23
25
  this.setHtmlVisualizationString = setHtmlVisualizationString;
24
26
  }
25
27
 
26
- _interpolateEnvVar = (str) => {
28
+ _interpolate = (str) => {
27
29
  if (!str || !str.length || typeof str !== 'string') {
28
30
  return str;
29
31
  }
30
32
 
31
- const template = Handlebars.compile(str, { noEscape: true });
32
-
33
- return template({
33
+ const combinedVars = {
34
+ ...this.envVariables,
35
+ ...this.requestVariables,
36
+ ...this.collectionVariables,
34
37
  process: {
35
38
  env: {
36
39
  ...this.processEnvVars
37
40
  }
38
41
  }
39
- });
42
+ };
43
+
44
+ return interpolate(str, combinedVars);
40
45
  };
41
46
 
42
47
  cwd() {
@@ -51,8 +56,12 @@ class Bru {
51
56
  return this.processEnvVars[key];
52
57
  }
53
58
 
59
+ hasEnvVar(key) {
60
+ return Object.hasOwn(this.envVariables, key);
61
+ }
62
+
54
63
  getEnvVar(key) {
55
- return this._interpolateEnvVar(this.envVariables[key]);
64
+ return this._interpolate(this.envVariables[key]);
56
65
  }
57
66
 
58
67
  setEnvVar(key, value) {
@@ -72,6 +81,10 @@ class Bru {
72
81
  this.envVariables[key] = value;
73
82
  }
74
83
 
84
+ hasVar(key) {
85
+ return Object.hasOwn(this.collectionVariables, key);
86
+ }
87
+
75
88
  setVar(key, value) {
76
89
  if (!key) {
77
90
  throw new Error('Creating a variable without specifying a name is not allowed.');
@@ -104,7 +117,15 @@ class Bru {
104
117
  );
105
118
  }
106
119
 
107
- return this.collectionVariables[key];
120
+ return this._interpolate(this.collectionVariables[key]);
121
+ }
122
+
123
+ deleteVar(key) {
124
+ delete this.collectionVariables[key];
125
+ }
126
+
127
+ getRequestVar(key) {
128
+ return this._interpolate(this.requestVariables[key]);
108
129
  }
109
130
 
110
131
  setNextRequest(nextRequest) {
@@ -1,44 +1,16 @@
1
- const Handlebars = require('handlebars');
2
- const { forOwn, cloneDeep } = require('lodash');
1
+ const { interpolate } = require('@usebruno/common');
3
2
 
4
- const interpolateEnvVars = (str, processEnvVars) => {
3
+ const interpolateString = (
4
+ str,
5
+ { envVariables = {}, collectionVariables = {}, processEnvVars = {}, requestVariables = {} }
6
+ ) => {
5
7
  if (!str || !str.length || typeof str !== 'string') {
6
8
  return str;
7
9
  }
8
10
 
9
- const template = Handlebars.compile(str, { noEscape: true });
10
-
11
- return template({
12
- process: {
13
- env: {
14
- ...processEnvVars
15
- }
16
- }
17
- });
18
- };
19
-
20
- const interpolateString = (str, { envVariables, collectionVariables, processEnvVars }) => {
21
- if (!str || !str.length || typeof str !== 'string') {
22
- return str;
23
- }
24
-
25
- processEnvVars = processEnvVars || {};
26
- collectionVariables = collectionVariables || {};
27
-
28
- // we clone envVariables because we don't want to modify the original object
29
- envVariables = envVariables ? cloneDeep(envVariables) : {};
30
-
31
- // envVariables can inturn have values as {{process.env.VAR_NAME}}
32
- // so we need to interpolate envVariables first with processEnvVars
33
- forOwn(envVariables, (value, key) => {
34
- envVariables[key] = interpolateEnvVars(value, processEnvVars);
35
- });
36
-
37
- const template = Handlebars.compile(str, { noEscape: true });
38
-
39
- // collectionVariables take precedence over envVariables
40
11
  const combinedVars = {
41
12
  ...envVariables,
13
+ ...requestVariables,
42
14
  ...collectionVariables,
43
15
  process: {
44
16
  env: {
@@ -47,7 +19,7 @@ const interpolateString = (str, { envVariables, collectionVariables, processEnvV
47
19
  }
48
20
  };
49
21
 
50
- return template(combinedVars);
22
+ return interpolate(str, combinedVars);
51
23
  };
52
24
 
53
25
  module.exports = {
@@ -66,6 +66,7 @@ chai.use(function (chai, utils) {
66
66
  * isNumber : is number
67
67
  * isString : is string
68
68
  * isBoolean : is boolean
69
+ * isArray : is array
69
70
  */
70
71
  const parseAssertionOperator = (str = '') => {
71
72
  if (!str || typeof str !== 'string' || !str.length) {
@@ -101,7 +102,8 @@ const parseAssertionOperator = (str = '') => {
101
102
  'isJson',
102
103
  'isNumber',
103
104
  'isString',
104
- 'isBoolean'
105
+ 'isBoolean',
106
+ 'isArray'
105
107
  ];
106
108
 
107
109
  const unaryOperators = [
@@ -114,7 +116,8 @@ const parseAssertionOperator = (str = '') => {
114
116
  'isJson',
115
117
  'isNumber',
116
118
  'isString',
117
- 'isBoolean'
119
+ 'isBoolean',
120
+ 'isArray'
118
121
  ];
119
122
 
120
123
  const [operator, ...rest] = str.trim().split(' ');
@@ -151,7 +154,8 @@ const isUnaryOperator = (operator) => {
151
154
  'isJson',
152
155
  'isNumber',
153
156
  'isString',
154
- 'isBoolean'
157
+ 'isBoolean',
158
+ 'isArray'
155
159
  ];
156
160
 
157
161
  return unaryOperators.includes(operator);
@@ -163,6 +167,7 @@ const evaluateRhsOperand = (rhsOperand, operator, context) => {
163
167
  }
164
168
 
165
169
  const interpolationContext = {
170
+ requestVariables: context.bru.requestVariables,
166
171
  collectionVariables: context.bru.collectionVariables,
167
172
  envVariables: context.bru.envVariables,
168
173
  processEnvVars: context.bru.processEnvVars
@@ -205,10 +210,11 @@ class AssertRuntime {
205
210
  response,
206
211
  envVariables,
207
212
  collectionVariables,
208
- collectionPath,
213
+ processEnvVars,
209
214
  historyLogger,
210
215
  secretVariables
211
216
  ) {
217
+ const requestVariables = request?.requestVariables || {};
212
218
  const enabledAssertions = _.filter(assertions, (a) => a.enabled);
213
219
  if (!enabledAssertions.length) {
214
220
  return [];
@@ -217,11 +223,12 @@ class AssertRuntime {
217
223
  const bru = new Bru(
218
224
  envVariables,
219
225
  collectionVariables,
220
- undefined, // processEnvVars,
226
+ processEnvVars,
221
227
  undefined, // collectionPath,
222
228
  undefined, // historyLogger,
223
229
  undefined, // setHtmlVisualizationString,
224
- secretVariables
230
+ secretVariables,
231
+ requestVariables
225
232
  );
226
233
  const req = new BrunoRequest(request, historyLogger);
227
234
  const res = createResponseParser(response);
@@ -234,7 +241,9 @@ class AssertRuntime {
234
241
 
235
242
  const context = {
236
243
  ...envVariables,
244
+ ...requestVariables,
237
245
  ...collectionVariables,
246
+ ...processEnvVars,
238
247
  ...bruContext
239
248
  };
240
249
 
@@ -330,6 +339,9 @@ class AssertRuntime {
330
339
  case 'isBoolean':
331
340
  expect(lhs).to.be.a('boolean');
332
341
  break;
342
+ case 'isArray':
343
+ expect(lhs).to.be.a('array');
344
+ break;
333
345
  default:
334
346
  expect(lhs).to.equal(rhs);
335
347
  break;
@@ -1,4 +1,4 @@
1
- const { NodeVM } = require('vm2');
1
+ const { NodeVM } = require('@usebruno/vm2');
2
2
  const path = require('path');
3
3
  const http = require('http');
4
4
  const https = require('https');
@@ -50,6 +50,7 @@ class ScriptRuntime {
50
50
  let setHtmlVisualizationString = (htmlString) => {
51
51
  htmlVisualizationString = htmlString;
52
52
  };
53
+ const requestVariables = request?.requestVariables || {};
53
54
  const bru = new Bru(
54
55
  envVariables,
55
56
  collectionVariables,
@@ -57,7 +58,8 @@ class ScriptRuntime {
57
58
  collectionPath,
58
59
  historyLogger,
59
60
  setHtmlVisualizationString,
60
- secretVariables
61
+ secretVariables,
62
+ requestVariables
61
63
  );
62
64
  const req = new BrunoRequest(request, historyLogger);
63
65
  const allowScriptFilesystemAccess = get(scriptingConfig, 'filesystemAccess.allow', false);
@@ -162,6 +164,7 @@ class ScriptRuntime {
162
164
  let setHtmlVisualizationString = (htmlString) => {
163
165
  htmlVisualizationString = htmlString;
164
166
  };
167
+ const requestVariables = request?.requestVariables || {};
165
168
  const bru = new Bru(
166
169
  envVariables,
167
170
  collectionVariables,
@@ -169,7 +172,8 @@ class ScriptRuntime {
169
172
  collectionPath,
170
173
  historyLogger,
171
174
  setHtmlVisualizationString,
172
- secretVariables
175
+ secretVariables,
176
+ requestVariables
173
177
  );
174
178
  const req = new BrunoRequest(request, historyLogger);
175
179
  const res = new BrunoResponse(response);
@@ -1,4 +1,4 @@
1
- const { NodeVM } = require('vm2');
1
+ const { NodeVM } = require('@usebruno/vm2');
2
2
  const chai = require('chai');
3
3
  const path = require('path');
4
4
  const http = require('http');
@@ -47,6 +47,7 @@ class TestRuntime {
47
47
  historyLogger,
48
48
  secretVariables
49
49
  ) {
50
+ const requestVariables = request?.requestVariables || {};
50
51
  const bru = new Bru(
51
52
  envVariables,
52
53
  collectionVariables,
@@ -54,7 +55,8 @@ class TestRuntime {
54
55
  collectionPath,
55
56
  historyLogger,
56
57
  undefined, // setHtmlVisualizationString
57
- secretVariables
58
+ secretVariables,
59
+ requestVariables
58
60
  );
59
61
  const req = new BrunoRequest(request, historyLogger);
60
62
  const res = new BrunoResponse(response);
@@ -14,6 +14,9 @@ class VarsRuntime {
14
14
  historyLogger,
15
15
  secretVars = {}
16
16
  ) {
17
+ if (!request?.requestVariables) {
18
+ request.requestVariables = {};
19
+ }
17
20
  const enabledVars = _.filter(vars, (v) => v.enabled);
18
21
  if (!enabledVars.length) {
19
22
  return;
@@ -44,12 +47,8 @@ class VarsRuntime {
44
47
 
45
48
  _.each(enabledVars, (v) => {
46
49
  const value = evaluateJsTemplateLiteral(v.value, context);
47
- bru.setVar(v.name, value);
50
+ request?.requestVariables && (request.requestVariables[v.name] = value);
48
51
  });
49
-
50
- return {
51
- collectionVariables
52
- };
53
52
  }
54
53
 
55
54
  runPostResponseVars(
@@ -63,6 +62,7 @@ class VarsRuntime {
63
62
  historyLogger,
64
63
  secretVars = {}
65
64
  ) {
65
+ const requestVariables = request?.requestVariables || {};
66
66
  const enabledVars = _.filter(vars, (v) => v.enabled);
67
67
  if (!enabledVars.length) {
68
68
  return;
@@ -75,7 +75,8 @@ class VarsRuntime {
75
75
  null,
76
76
  historyLogger,
77
77
  undefined, // setHtmlVisualizationString
78
- secretVars
78
+ secretVars,
79
+ requestVariables
79
80
  );
80
81
  const req = new BrunoRequest(request, historyLogger);
81
82
  const res = createResponseParser(response);
@@ -93,14 +94,27 @@ class VarsRuntime {
93
94
  ...bruContext
94
95
  };
95
96
 
97
+ const errors = new Map();
96
98
  _.each(enabledVars, (v) => {
97
- const value = evaluateJsExpression(v.value, context);
98
- bru.setVar(v.name, value);
99
+ try {
100
+ const value = evaluateJsExpression(v.value, context);
101
+ bru.setVar(v.name, value);
102
+ } catch (error) {
103
+ errors.set(v.name, error);
104
+ }
99
105
  });
100
106
 
107
+ let error = null;
108
+ if (errors.size > 0) {
109
+ // Format all errors as a single string to be displayed in a toast
110
+ const errorMessage = [...errors.entries()].map(([name, err]) => `${name}: ${err.message ?? err}`).join('\n');
111
+ error = `${errors.size} error${errors.size === 1 ? '' : 's'} in post response variables: \n${errorMessage}`;
112
+ }
113
+
101
114
  return {
102
115
  envVariables,
103
- collectionVariables
116
+ collectionVariables,
117
+ error
104
118
  };
105
119
  }
106
120
  }