@usebruno/js 0.15.0 → 0.17.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.15.0",
3
+ "version": "0.17.0",
4
4
  "license": "MIT",
5
5
  "main": "src/index.js",
6
6
  "files": [
package/src/bru.js CHANGED
@@ -5,20 +5,13 @@ const { interpolate } = require('@usebruno/common');
5
5
  const variableNameRegex = /^[\w-.]*$/;
6
6
 
7
7
  class Bru {
8
- constructor(
9
- envVariables,
10
- runtimeVariables,
11
- processEnvVars,
12
- collectionPath,
13
- historyLogger,
14
- setVisualizations,
15
- secretVariables,
16
- requestVariables
17
- ) {
8
+ constructor(envVariables, runtimeVariables, processEnvVars, collectionPath, historyLogger, setVisualizations, secretVariables, collectionVariables, folderVariables, requestVariables) {
18
9
  this.envVariables = envVariables || {};
19
10
  this.runtimeVariables = runtimeVariables || {};
20
11
  this.processEnvVars = cloneDeep(processEnvVars || {});
21
12
  this.secretVariables = cloneDeep(secretVariables || {});
13
+ this.collectionVariables = collectionVariables || {};
14
+ this.folderVariables = folderVariables || {};
22
15
  this.requestVariables = requestVariables || {};
23
16
  this.collectionPath = collectionPath;
24
17
  this.historyLogger = historyLogger;
@@ -31,7 +24,9 @@ class Bru {
31
24
  }
32
25
 
33
26
  const combinedVars = {
27
+ ...this.collectionVariables,
34
28
  ...this.envVariables,
29
+ ...this.folderVariables,
35
30
  ...this.requestVariables,
36
31
  ...this.runtimeVariables,
37
32
  process: {
@@ -93,7 +88,7 @@ class Bru {
93
88
  if (variableNameRegex.test(key) === false) {
94
89
  throw new Error(
95
90
  `Variable name: "${key}" contains invalid characters!` +
96
- ' Names must only contain alpha-numeric characters, "-", "_", "."'
91
+ ' Names must only contain alpha-numeric characters, "-", "_", "."'
97
92
  );
98
93
  }
99
94
 
@@ -113,7 +108,7 @@ class Bru {
113
108
  if (variableNameRegex.test(key) === false) {
114
109
  throw new Error(
115
110
  `Variable name: "${key}" contains invalid characters!` +
116
- ' Names must only contain alpha-numeric characters, "-", "_", "."'
111
+ ' Names must only contain alpha-numeric characters, "-", "_", "."'
117
112
  );
118
113
  }
119
114
 
@@ -124,6 +119,14 @@ class Bru {
124
119
  delete this.runtimeVariables[key];
125
120
  }
126
121
 
122
+ getCollectionVar(key) {
123
+ return this._interpolate(this.collectionVariables[key]);
124
+ }
125
+
126
+ getFolderVar(key) {
127
+ return this._interpolate(this.folderVariables[key]);
128
+ }
129
+
127
130
  getRequestVar(key) {
128
131
  return this._interpolate(this.requestVariables[key]);
129
132
  }
@@ -158,7 +161,7 @@ class Bru {
158
161
  this.setVisualizations({ uid: uuid(), type, data });
159
162
  }
160
163
  }
161
-
164
+
162
165
  sleep(ms) {
163
166
  return new Promise((resolve) => setTimeout(resolve, ms));
164
167
  }
@@ -1,14 +1,37 @@
1
1
  const { uuid } = require('./utils');
2
2
 
3
3
  class BrunoRequest {
4
+ /**
5
+ * The following properties are available as shorthand:
6
+ * - req.url
7
+ * - req.method
8
+ * - req.headers
9
+ * - req.timeout
10
+ * - req.body
11
+ *
12
+ * Above shorthands are useful for accessing the request properties directly in the scripts
13
+ * It must be noted that the user cannot set these properties directly.
14
+ * They should use the respective setter methods to set these properties.
15
+ */
4
16
  constructor(req, historyLogger) {
5
17
  this.req = req;
6
18
  this.url = req.url;
7
19
  this.method = req.method;
8
20
  this.headers = req.headers;
9
- this.body = req.data;
10
21
  this.timeout = req.timeout;
11
22
  this.historyLogger = historyLogger;
23
+
24
+ /**
25
+ * We automatically parse the JSON body if the content type is JSON
26
+ * This is to make it easier for the user to access the body directly
27
+ *
28
+ * It must be noted that the request data is always a string and is what gets sent over the network
29
+ * If the user wants to access the raw data, they can use getBody({raw: true}) method
30
+ */
31
+ const isJson = this.hasJSONContentType(this.req.headers);
32
+ if (isJson) {
33
+ this.body = this.__safeParseJSON(req.data);
34
+ }
12
35
  }
13
36
 
14
37
  getUrl() {
@@ -16,6 +39,7 @@ class BrunoRequest {
16
39
  }
17
40
 
18
41
  setUrl(url) {
42
+ this.url = url;
19
43
  this.req.url = url;
20
44
  }
21
45
 
@@ -40,6 +64,7 @@ class BrunoRequest {
40
64
  }
41
65
 
42
66
  setMethod(method) {
67
+ this.method = method;
43
68
  this.req.method = method;
44
69
  }
45
70
 
@@ -48,6 +73,7 @@ class BrunoRequest {
48
73
  }
49
74
 
50
75
  setHeaders(headers) {
76
+ this.headers = headers;
51
77
  this.req.headers = headers;
52
78
  }
53
79
 
@@ -64,14 +90,45 @@ class BrunoRequest {
64
90
  createdAt: new Date().toISOString()
65
91
  });
66
92
  }
93
+ this.headers[name] = value;
67
94
  this.req.headers[name] = value;
68
95
  }
69
96
 
70
- getBody() {
97
+ hasJSONContentType(headers) {
98
+ const contentType = headers?.['Content-Type'] || headers?.['content-type'] || '';
99
+ return contentType.includes('json');
100
+ }
101
+
102
+ /**
103
+ * Get the body of the request
104
+ *
105
+ * We automatically parse and return the JSON body if the content type is JSON
106
+ * If the user wants the raw body, they can pass the raw option as true
107
+ */
108
+ getBody(options = {}) {
109
+ if (options.raw) {
110
+ return this.req.data;
111
+ }
112
+
113
+ const isJson = this.hasJSONContentType(this.req.headers);
114
+ if (isJson) {
115
+ return this.__safeParseJSON(this.req.data);
116
+ }
117
+
71
118
  return this.req.data;
72
119
  }
73
120
 
74
- setBody(data) {
121
+ /**
122
+ * If the content type is JSON and if the data is an object
123
+ * - We set the body property as the object itself
124
+ * - We set the request data as the stringified JSON as it is what gets sent over the network
125
+ * Otherwise
126
+ * - We set the request data as the data itself
127
+ * - We set the body property as the data itself
128
+ *
129
+ * If the user wants to override this behavior, they can pass the raw option as true
130
+ */
131
+ setBody(data, options = {}) {
75
132
  if (this.historyLogger) {
76
133
  this.historyLogger({
77
134
  uid: uuid(),
@@ -80,7 +137,22 @@ class BrunoRequest {
80
137
  createdAt: new Date().toISOString()
81
138
  });
82
139
  }
140
+
141
+ if (options.raw) {
142
+ this.req.data = data;
143
+ this.body = data;
144
+ return;
145
+ }
146
+
147
+ const isJson = this.hasJSONContentType(this.req.headers);
148
+ if (isJson && this.__isObject(data)) {
149
+ this.body = data;
150
+ this.req.data = this.__safeStringifyJSON(data);
151
+ return;
152
+ }
153
+
83
154
  this.req.data = data;
155
+ this.body = data;
84
156
  }
85
157
 
86
158
  setMaxRedirects(maxRedirects) {
@@ -92,8 +164,34 @@ class BrunoRequest {
92
164
  }
93
165
 
94
166
  setTimeout(timeout) {
167
+ this.timeout = timeout;
95
168
  this.req.timeout = timeout;
96
169
  }
170
+
171
+ __safeParseJSON(str) {
172
+ try {
173
+ return JSON.parse(str);
174
+ } catch (e) {
175
+ return str;
176
+ }
177
+ }
178
+
179
+ __safeStringifyJSON(obj) {
180
+ try {
181
+ return JSON.stringify(obj);
182
+ } catch (e) {
183
+ return obj;
184
+ }
185
+ }
186
+
187
+ __isObject(obj) {
188
+ return obj !== null && typeof obj === 'object';
189
+ }
190
+
191
+
192
+ disableParsingResponseJson() {
193
+ this.req.__brunoDisableParsingResponseJson = true;
194
+ }
97
195
  }
98
196
 
99
197
  module.exports = BrunoRequest;
@@ -27,6 +27,15 @@ class BrunoResponse {
27
27
  getResponseTime() {
28
28
  return this.res ? this.res.responseTime : null;
29
29
  }
30
+
31
+ setBody(data) {
32
+ if (!this.res) {
33
+ return;
34
+ }
35
+
36
+ this.body = data;
37
+ this.res.data = data;
38
+ }
30
39
  }
31
40
 
32
41
  module.exports = BrunoResponse;
@@ -2,14 +2,16 @@ const { interpolate } = require('@usebruno/common');
2
2
 
3
3
  const interpolateString = (
4
4
  str,
5
- { envVariables = {}, runtimeVariables = {}, processEnvVars = {}, requestVariables = {} }
5
+ { envVariables = {}, runtimeVariables = {}, processEnvVars = {}, collectionVariables = {}, folderVariables = {}, requestVariables = {} }
6
6
  ) => {
7
7
  if (!str || !str.length || typeof str !== 'string') {
8
8
  return str;
9
9
  }
10
10
 
11
11
  const combinedVars = {
12
+ ...collectionVariables,
12
13
  ...envVariables,
14
+ ...folderVariables,
13
15
  ...requestVariables,
14
16
  ...runtimeVariables,
15
17
  process: {
@@ -192,6 +192,8 @@ const evaluateRhsOperand = (rhsOperand, operator, context, runtime) => {
192
192
  }
193
193
 
194
194
  const interpolationContext = {
195
+ collectionVariables: context.bru.collectionVariables,
196
+ folderVariables: context.bru.folderVariables,
195
197
  requestVariables: context.bru.requestVariables,
196
198
  runtimeVariables: context.bru.runtimeVariables,
197
199
  envVariables: context.bru.envVariables,
@@ -238,6 +240,8 @@ class AssertRuntime {
238
240
  }
239
241
 
240
242
  runAssertions(assertions, request, response, envVariables, runtimeVariables, processEnvVars, historyLogger, secretVariables) {
243
+ const collectionVariables = request?.collectionVariables || {};
244
+ const folderVariables = request?.folderVariables || {};
241
245
  const requestVariables = request?.requestVariables || {};
242
246
  const enabledAssertions = _.filter(assertions, (a) => a.enabled);
243
247
  if (!enabledAssertions.length) {
@@ -252,6 +256,8 @@ class AssertRuntime {
252
256
  undefined, // historyLogger,
253
257
  undefined, // setVisualizations,
254
258
  secretVariables,
259
+ collectionVariables,
260
+ folderVariables,
255
261
  requestVariables
256
262
  );
257
263
  const req = new BrunoRequest(request, historyLogger);
@@ -264,7 +270,9 @@ class AssertRuntime {
264
270
  };
265
271
 
266
272
  const context = {
273
+ ...collectionVariables,
267
274
  ...envVariables,
275
+ ...folderVariables,
268
276
  ...requestVariables,
269
277
  ...runtimeVariables,
270
278
  ...processEnvVars,
@@ -53,17 +53,10 @@ class ScriptRuntime {
53
53
  let setVisualizations = (data) => {
54
54
  visualizations.push(data);
55
55
  }
56
+ const collectionVariables = request?.collectionVariables || {};
57
+ const folderVariables = request?.folderVariables || {};
56
58
  const requestVariables = request?.requestVariables || {};
57
- const bru = new Bru(
58
- envVariables,
59
- runtimeVariables,
60
- processEnvVars,
61
- collectionPath,
62
- historyLogger,
63
- setVisualizations,
64
- secretVariables,
65
- requestVariables
66
- );
59
+ const bru = new Bru(envVariables, runtimeVariables, processEnvVars, collectionPath, historyLogger, setVisualizations, secretVariables, collectionVariables, folderVariables, requestVariables);
67
60
  const req = new BrunoRequest(request, historyLogger);
68
61
  const allowScriptFilesystemAccess = get(scriptingConfig, 'filesystemAccess.allow', false);
69
62
  const moduleWhitelist = get(scriptingConfig, 'moduleWhitelist', []);
@@ -185,17 +178,10 @@ class ScriptRuntime {
185
178
  let setVisualizations = (data) => {
186
179
  visualizations.push(data);
187
180
  }
181
+ const collectionVariables = request?.collectionVariables || {};
182
+ const folderVariables = request?.folderVariables || {};
188
183
  const requestVariables = request?.requestVariables || {};
189
- const bru = new Bru(
190
- envVariables,
191
- runtimeVariables,
192
- processEnvVars,
193
- collectionPath,
194
- historyLogger,
195
- setVisualizations,
196
- secretVariables,
197
- requestVariables
198
- );
184
+ const bru = new Bru(envVariables, runtimeVariables, processEnvVars, collectionPath, historyLogger, setVisualizations, secretVariables, collectionVariables, folderVariables, requestVariables);
199
185
  const req = new BrunoRequest(request, historyLogger);
200
186
  const res = new BrunoResponse(response);
201
187
  const allowScriptFilesystemAccess = get(scriptingConfig, 'filesystemAccess.allow', false);
@@ -50,17 +50,10 @@ class TestRuntime {
50
50
  historyLogger,
51
51
  secretVariables
52
52
  ) {
53
+ const collectionVariables = request?.collectionVariables || {};
54
+ const folderVariables = request?.folderVariables || {};
53
55
  const requestVariables = request?.requestVariables || {};
54
- const bru = new Bru(
55
- envVariables,
56
- runtimeVariables,
57
- processEnvVars,
58
- collectionPath,
59
- historyLogger,
60
- undefined, // setVisualizations
61
- secretVariables,
62
- requestVariables
63
- );
56
+ const bru = new Bru(envVariables, runtimeVariables, processEnvVars, collectionPath, historyLogger, undefined, secretVariables, collectionVariables, folderVariables, requestVariables);
64
57
  const req = new BrunoRequest(request, historyLogger);
65
58
  const res = new BrunoResponse(response);
66
59
  const allowScriptFilesystemAccess = get(scriptingConfig, 'filesystemAccess.allow', false);
@@ -95,8 +88,6 @@ class TestRuntime {
95
88
  };
96
89
  }
97
90
 
98
- // add 'await' prefix to the test function calls
99
- testsFile = appendAwaitToTestFunc(testsFile);
100
91
 
101
92
  const context = {
102
93
  test,
@@ -1,22 +1,10 @@
1
1
  const _ = require('lodash');
2
2
  const Bru = require('../bru');
3
3
  const BrunoRequest = require('../bruno-request');
4
- const { evaluateJsTemplateLiteral, evaluateJsExpression, createResponseParser } = require('../utils');
4
+ const { evaluateJsExpression, createResponseParser } = require('../utils');
5
5
 
6
6
  const { executeQuickJsVm } = require('../sandbox/quickjs');
7
7
 
8
- const evaluateJsTemplateLiteralBasedOnRuntime = (literal, context, runtime) => {
9
- if (runtime === 'quickjs') {
10
- return executeQuickJsVm({
11
- script: literal,
12
- context,
13
- scriptType: 'template-literal'
14
- });
15
- }
16
-
17
- return evaluateJsTemplateLiteral(literal, context);
18
- };
19
-
20
8
  const evaluateJsExpressionBasedOnRuntime = (expr, context, runtime, mode) => {
21
9
  if (runtime === 'quickjs') {
22
10
  return executeQuickJsVm({
@@ -35,56 +23,7 @@ class VarsRuntime {
35
23
  this.mode = props?.mode || 'developer';
36
24
  }
37
25
 
38
- runPreRequestVars(vars, request, envVariables, runtimeVariables, collectionPath, processEnvVars, historyLogger, secretVars = {}) {
39
- if (!request?.requestVariables) {
40
- request.requestVariables = {};
41
- }
42
- const enabledVars = _.filter(vars, (v) => v.enabled);
43
- if (!enabledVars.length) {
44
- return;
45
- }
46
-
47
- const bru = new Bru(
48
- envVariables,
49
- runtimeVariables,
50
- processEnvVars,
51
- null,
52
- historyLogger,
53
- undefined, // setVisualizations
54
- secretVars,
55
- undefined, // requestVariables
56
- );
57
- const req = new BrunoRequest(request, historyLogger);
58
-
59
- const bruContext = {
60
- bru,
61
- req
62
- };
63
-
64
- const context = {
65
- ...envVariables,
66
- ...runtimeVariables,
67
- ...secretVars,
68
- ...bruContext
69
- };
70
-
71
- _.each(enabledVars, (v) => {
72
- const value = evaluateJsTemplateLiteralBasedOnRuntime(v.value, context, this.runtime);
73
- request?.requestVariables && (request.requestVariables[v.name] = value);
74
- });
75
- }
76
-
77
- runPostResponseVars(
78
- vars,
79
- request,
80
- response,
81
- envVariables,
82
- runtimeVariables,
83
- collectionPath,
84
- processEnvVars,
85
- historyLogger,
86
- secretVars = {}
87
- ) {
26
+ runPostResponseVars(vars, request, response, envVariables, runtimeVariables, collectionPath, processEnvVars, historyLogger, secretVars = {}) {
88
27
  const requestVariables = request?.requestVariables || {};
89
28
  const enabledVars = _.filter(vars, (v) => v.enabled);
90
29
  if (!enabledVars.length) {
@@ -69,6 +69,18 @@ const addBruShimToContext = (vm, bru) => {
69
69
  vm.setProp(bruObject, 'getRequestVar', getRequestVar);
70
70
  getRequestVar.dispose();
71
71
 
72
+ let getFolderVar = vm.newFunction('getFolderVar', function (key) {
73
+ return marshallToVm(bru.getFolderVar(vm.dump(key)), vm);
74
+ });
75
+ vm.setProp(bruObject, 'getFolderVar', getFolderVar);
76
+ getFolderVar.dispose();
77
+
78
+ let getCollectionVar = vm.newFunction('getCollectionVar', function (key) {
79
+ return marshallToVm(bru.getCollectionVar(vm.dump(key)), vm);
80
+ });
81
+ vm.setProp(bruObject, 'getCollectionVar', getCollectionVar);
82
+ getCollectionVar.dispose();
83
+
72
84
  const sleep = vm.newFunction('sleep', (timer) => {
73
85
  const t = vm.getString(timer);
74
86
  const promise = vm.newPromise();
@@ -105,6 +105,12 @@ const addBrunoRequestShimToContext = (vm, req) => {
105
105
  vm.setProp(reqObject, 'setTimeout', setTimeout);
106
106
  setTimeout.dispose();
107
107
 
108
+ let disableParsingResponseJson = vm.newFunction('disableParsingResponseJson', function () {
109
+ req.disableParsingResponseJson();
110
+ });
111
+ vm.setProp(reqObject, 'disableParsingResponseJson', disableParsingResponseJson);
112
+ disableParsingResponseJson.dispose();
113
+
108
114
  vm.setProp(vm.global, 'req', reqObject);
109
115
  reqObject.dispose();
110
116
  };
@@ -25,6 +25,8 @@ const marshallToVm = (value, vm) => {
25
25
  }
26
26
  return obj;
27
27
  }
28
+ } else if (typeof value === 'function') {
29
+ return vm.newString('[Function (anonymous)]');
28
30
  }
29
31
  };
30
32