@usebruno/filestore 0.10.0 → 0.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.
- package/dist/cjs/index.js +1 -1
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/workers/worker-script.js +1 -1
- package/dist/cjs/workers/worker-script.js.map +1 -1
- package/dist/constants.d.ts +2 -0
- package/dist/constants.d.ts.map +1 -1
- package/dist/esm/index.js +1 -1
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/workers/worker-script.js +1 -1
- package/dist/esm/workers/worker-script.js.map +1 -1
- package/dist/formats/bru/index.d.ts.map +1 -1
- package/dist/formats/bru/utils/redact-large-text-blocks.d.ts +11 -0
- package/dist/formats/bru/utils/redact-large-text-blocks.d.ts.map +1 -0
- package/dist/formats/yml/common/app.d.ts +8 -0
- package/dist/formats/yml/common/app.d.ts.map +1 -0
- package/dist/formats/yml/common/auth.d.ts.map +1 -1
- package/dist/formats/yml/common/body.d.ts.map +1 -1
- package/dist/formats/yml/common/datatype.d.ts +14 -0
- package/dist/formats/yml/common/datatype.d.ts.map +1 -0
- package/dist/formats/yml/common/headers.d.ts.map +1 -1
- package/dist/formats/yml/common/variables.d.ts +1 -2
- package/dist/formats/yml/common/variables.d.ts.map +1 -1
- package/dist/formats/yml/index.d.ts +3 -1
- package/dist/formats/yml/index.d.ts.map +1 -1
- package/dist/formats/yml/items/parseApp.d.ts +13 -0
- package/dist/formats/yml/items/parseApp.d.ts.map +1 -0
- package/dist/formats/yml/items/parseGraphQLRequest.d.ts.map +1 -1
- package/dist/formats/yml/items/parseGrpcRequest.d.ts.map +1 -1
- package/dist/formats/yml/items/parseHttpRequest.d.ts.map +1 -1
- package/dist/formats/yml/items/parseWebsocketRequest.d.ts.map +1 -1
- package/dist/formats/yml/items/stringifyApp.d.ts +4 -0
- package/dist/formats/yml/items/stringifyApp.d.ts.map +1 -0
- package/dist/formats/yml/items/stringifyGraphQLRequest.d.ts.map +1 -1
- package/dist/formats/yml/items/stringifyGrpcRequest.d.ts.map +1 -1
- package/dist/formats/yml/items/stringifyHttpRequest.d.ts.map +1 -1
- package/dist/formats/yml/items/stringifyWebsocketRequest.d.ts.map +1 -1
- package/dist/formats/yml/mockServerTypes.d.ts +95 -0
- package/dist/formats/yml/mockServerTypes.d.ts.map +1 -0
- package/dist/formats/yml/parseCollection.d.ts.map +1 -1
- package/dist/formats/yml/parseEnvironment.d.ts +3 -1
- package/dist/formats/yml/parseEnvironment.d.ts.map +1 -1
- package/dist/formats/yml/parseFolder.d.ts.map +1 -1
- package/dist/formats/yml/parseItem.d.ts.map +1 -1
- package/dist/formats/yml/parseMockServer.d.ts +4 -0
- package/dist/formats/yml/parseMockServer.d.ts.map +1 -0
- package/dist/formats/yml/stringifyCollection.d.ts.map +1 -1
- package/dist/formats/yml/stringifyEnvironment.d.ts +3 -1
- package/dist/formats/yml/stringifyEnvironment.d.ts.map +1 -1
- package/dist/formats/yml/stringifyFolder.d.ts.map +1 -1
- package/dist/formats/yml/stringifyItem.d.ts.map +1 -1
- package/dist/formats/yml/stringifyMockServer.d.ts +4 -0
- package/dist/formats/yml/stringifyMockServer.d.ts.map +1 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/types.d.ts +7 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/utils/secretsJsonToEnvironments.d.ts +38 -0
- package/dist/utils/secretsJsonToEnvironments.d.ts.map +1 -0
- package/dist/workers/WorkerQueue/index.d.ts +3 -2
- package/dist/workers/WorkerQueue/index.d.ts.map +1 -1
- package/dist/workers/index.d.ts +4 -0
- package/dist/workers/index.d.ts.map +1 -1
- package/package.json +7 -5
- package/src/constants.ts +13 -0
- package/src/formats/bru/index.ts +52 -0
- package/src/formats/bru/tests/parseBruEnvironment.spec.js +63 -0
- package/src/formats/bru/tests/redact-large-text-blocks.spec.js +472 -0
- package/src/formats/bru/utils/redact-large-text-blocks.ts +143 -0
- package/src/formats/yml/common/actions.ts +1 -1
- package/src/formats/yml/common/app.ts +27 -0
- package/src/formats/yml/common/auth.ts +53 -1
- package/src/formats/yml/common/body.spec.ts +62 -0
- package/src/formats/yml/common/body.ts +27 -9
- package/src/formats/yml/common/datatype.spec.ts +110 -0
- package/src/formats/yml/common/datatype.ts +57 -0
- package/src/formats/yml/common/headers.ts +4 -0
- package/src/formats/yml/common/variables.spec.ts +127 -0
- package/src/formats/yml/common/variables.ts +14 -22
- package/src/formats/yml/index.ts +5 -1
- package/src/formats/yml/items/parseApp.ts +37 -0
- package/src/formats/yml/items/parseGraphQLRequest.ts +14 -16
- package/src/formats/yml/items/parseGrpcRequest.ts +24 -3
- package/src/formats/yml/items/parseHttpRequest.ts +19 -16
- package/src/formats/yml/items/parseWebsocketRequest.ts +29 -9
- package/src/formats/yml/items/stringifyApp.ts +45 -0
- package/src/formats/yml/items/stringifyGraphQLRequest.ts +7 -6
- package/src/formats/yml/items/stringifyGrpcRequest.ts +9 -11
- package/src/formats/yml/items/stringifyHttpRequest.ts +14 -6
- package/src/formats/yml/items/stringifyWebsocketRequest.ts +22 -9
- package/src/formats/yml/mockServerTypes.ts +95 -0
- package/src/formats/yml/parseCollection.spec.ts +193 -0
- package/src/formats/yml/parseCollection.ts +24 -6
- package/src/formats/yml/parseEnvironment.spec.ts +99 -0
- package/src/formats/yml/parseEnvironment.ts +41 -19
- package/src/formats/yml/parseFolder.spec.ts +70 -0
- package/src/formats/yml/parseFolder.ts +19 -22
- package/src/formats/yml/parseItem.spec.ts +147 -0
- package/src/formats/yml/parseItem.ts +4 -0
- package/src/formats/yml/parseMockServer.spec.ts +180 -0
- package/src/formats/yml/parseMockServer.ts +113 -0
- package/src/formats/yml/stringifyCollection.spec.ts +223 -0
- package/src/formats/yml/stringifyCollection.ts +26 -9
- package/src/formats/yml/stringifyEnvironment.spec.ts +91 -0
- package/src/formats/yml/stringifyEnvironment.ts +55 -20
- package/src/formats/yml/stringifyFolder.spec.ts +70 -0
- package/src/formats/yml/stringifyFolder.ts +7 -2
- package/src/formats/yml/stringifyItem.spec.ts +53 -0
- package/src/formats/yml/stringifyItem.ts +4 -0
- package/src/formats/yml/stringifyMockServer.spec.ts +177 -0
- package/src/formats/yml/stringifyMockServer.ts +129 -0
- package/src/formats/yml/tests/defaultEnvironment.spec.js +50 -0
- package/src/formats/yml/tests/fixtures/presets/empty-request.yml +7 -0
- package/src/formats/yml/tests/fixtures/presets/no-presets.yml +3 -0
- package/src/formats/yml/tests/fixtures/presets/no-request-key.yml +6 -0
- package/src/formats/yml/tests/fixtures/presets/type-only-realistic.yml +24 -0
- package/src/formats/yml/tests/fixtures/presets/url-only-realistic.yml +25 -0
- package/src/formats/yml/tests/fixtures/presets/with-type-and-url.yml +9 -0
- package/src/formats/yml/tests/parseCollection.spec.js +63 -0
- package/src/formats/yml/tests/parseEnvironment.spec.js +24 -63
- package/src/formats/yml/tests/parseEnvironment.spec.ts +160 -0
- package/src/formats/yml/tests/stringifyEnvironment.spec.ts +230 -0
- package/src/formats/yml/tests/variables.spec.js +7 -7
- package/src/formats/yml/utils.ts +1 -1
- package/src/index.ts +39 -1
- package/src/types/bruno-lang.d.ts +1 -0
- package/src/types.ts +8 -0
- package/src/utils/secretsJsonToEnvironments.spec.ts +211 -0
- package/src/utils/secretsJsonToEnvironments.ts +129 -0
- package/src/workers/WorkerQueue/index.ts +10 -2
- package/src/workers/index.ts +25 -1
- package/src/workers/worker-script.ts +45 -21
- package/dist/formats/bru/tests/example-status-swap.spec.d.ts +0 -2
- package/dist/formats/bru/tests/example-status-swap.spec.d.ts.map +0 -1
- package/dist/formats/bru/tests/fixtures/oauth2-additional-params.d.ts +0 -57
- package/dist/formats/bru/tests/fixtures/oauth2-additional-params.d.ts.map +0 -1
- package/dist/formats/bru/tests/oauth2-additional-params.spec.d.ts +0 -2
- package/dist/formats/bru/tests/oauth2-additional-params.spec.d.ts.map +0 -1
- package/dist/formats/bru/tests/request-parse-and-redact-body-data.spec.d.ts +0 -2
- package/dist/formats/bru/tests/request-parse-and-redact-body-data.spec.d.ts.map +0 -1
- package/dist/formats/yml/tests/parseEnvironment.spec.d.ts +0 -2
- package/dist/formats/yml/tests/parseEnvironment.spec.d.ts.map +0 -1
- package/dist/formats/yml/tests/variables.spec.d.ts +0 -2
- package/dist/formats/yml/tests/variables.spec.d.ts.map +0 -1
package/src/formats/bru/index.ts
CHANGED
|
@@ -8,11 +8,26 @@ import {
|
|
|
8
8
|
jsonToCollectionBru as _jsonToCollectionBru
|
|
9
9
|
} from '@usebruno/lang';
|
|
10
10
|
import { getOauth2AdditionalParameters } from './utils/oauth2-additional-params';
|
|
11
|
+
import { uuid } from '../../utils';
|
|
11
12
|
|
|
12
13
|
export const parseBruRequest = (data: string | any, parsed: boolean = false): any => {
|
|
13
14
|
try {
|
|
14
15
|
const json = parsed ? data : bruToJsonV2(data);
|
|
15
16
|
|
|
17
|
+
if (_.get(json, 'meta.type') === 'app') {
|
|
18
|
+
const seq = _.get(json, 'meta.seq');
|
|
19
|
+
const tags = _.get(json, 'meta.tags', []);
|
|
20
|
+
return {
|
|
21
|
+
type: 'app',
|
|
22
|
+
name: _.get(json, 'meta.name'),
|
|
23
|
+
seq: !_.isNaN(seq) ? Number(seq) : 1,
|
|
24
|
+
tags: Array.isArray(tags) ? tags : [],
|
|
25
|
+
settings: _.get(json, 'settings', {}),
|
|
26
|
+
app: { code: _.get(json, 'app.code', null) },
|
|
27
|
+
request: null
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
|
|
16
31
|
let requestType = _.get(json, 'meta.type');
|
|
17
32
|
switch (requestType) {
|
|
18
33
|
case 'http':
|
|
@@ -38,11 +53,18 @@ export const parseBruRequest = (data: string | any, parsed: boolean = false): an
|
|
|
38
53
|
'ws-request': 'ws.url',
|
|
39
54
|
'default': 'http.url'
|
|
40
55
|
};
|
|
56
|
+
|
|
57
|
+
const appData = _.get(json, 'app');
|
|
58
|
+
const app = appData
|
|
59
|
+
? { code: _.get(appData, 'code', null), enabled: _.get(appData, 'enabled', false) === true }
|
|
60
|
+
: null;
|
|
61
|
+
|
|
41
62
|
const transformedJson = {
|
|
42
63
|
type: requestType,
|
|
43
64
|
name: _.get(json, 'meta.name'),
|
|
44
65
|
seq: !_.isNaN(sequence) ? Number(sequence) : 1,
|
|
45
66
|
settings: _.get(json, 'settings', {}),
|
|
67
|
+
app,
|
|
46
68
|
tags: Array.isArray(tags) ? tags : [],
|
|
47
69
|
request: {
|
|
48
70
|
// Preserving special characters in custom methods. Using _.upperCase strips special characters.
|
|
@@ -117,6 +139,22 @@ export const parseBruRequest = (data: string | any, parsed: boolean = false): an
|
|
|
117
139
|
|
|
118
140
|
export const stringifyBruRequest = (json: any): string => {
|
|
119
141
|
try {
|
|
142
|
+
// Standalone app item — emit only meta + the app code block.
|
|
143
|
+
if (_.get(json, 'type') === 'app') {
|
|
144
|
+
const seq = _.get(json, 'seq');
|
|
145
|
+
const bruJson: any = {
|
|
146
|
+
meta: {
|
|
147
|
+
name: _.get(json, 'name'),
|
|
148
|
+
type: 'app',
|
|
149
|
+
seq: !_.isNaN(seq) ? Number(seq) : 1,
|
|
150
|
+
tags: _.get(json, 'tags', [])
|
|
151
|
+
},
|
|
152
|
+
settings: _.get(json, 'settings', {}),
|
|
153
|
+
app: { code: _.get(json, 'app.code', '') }
|
|
154
|
+
};
|
|
155
|
+
return jsonToBruV2(bruJson);
|
|
156
|
+
}
|
|
157
|
+
|
|
120
158
|
let type = _.get(json, 'type');
|
|
121
159
|
switch (type) {
|
|
122
160
|
case 'http-request':
|
|
@@ -221,6 +259,11 @@ export const stringifyBruRequest = (json: any): string => {
|
|
|
221
259
|
bruJson.docs = _.get(json, 'request.docs', '');
|
|
222
260
|
bruJson.examples = _.get(json, 'examples', []).map((e: any) => jsonExampleToBru(e));
|
|
223
261
|
|
|
262
|
+
const app = _.get(json, 'app');
|
|
263
|
+
if (app && (app.enabled === true || (app.code && app.code.length))) {
|
|
264
|
+
bruJson.app = { code: app.code || null, enabled: app.enabled === true };
|
|
265
|
+
}
|
|
266
|
+
|
|
224
267
|
const bru = jsonToBruV2(bruJson);
|
|
225
268
|
return bru;
|
|
226
269
|
} catch (error) {
|
|
@@ -326,6 +369,15 @@ export const parseBruEnvironment = (bru: string): any => {
|
|
|
326
369
|
_.each(json.variables, (v: any) => (v.type = 'text'));
|
|
327
370
|
}
|
|
328
371
|
|
|
372
|
+
// bruToEnvJsonV2 emits the nested `externalSecrets: { type, variables: [{ name,
|
|
373
|
+
// <identifier>, enabled }] }`. The app/schema additionally expect a `uid` per variable.
|
|
374
|
+
if (json?.externalSecrets && Array.isArray(json.externalSecrets.variables)) {
|
|
375
|
+
json.externalSecrets.variables = json.externalSecrets.variables.map((v: any) => ({
|
|
376
|
+
uid: uuid(),
|
|
377
|
+
...v
|
|
378
|
+
}));
|
|
379
|
+
}
|
|
380
|
+
|
|
329
381
|
return json;
|
|
330
382
|
} catch (error) {
|
|
331
383
|
return Promise.reject(error);
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
const { parseBruEnvironment } = require('../index');
|
|
2
|
+
|
|
3
|
+
describe('parseBruEnvironment external secrets normalization', () => {
|
|
4
|
+
it('should normalize the external secrets block to the app shape with uid and enabled', () => {
|
|
5
|
+
const bru = `vars {
|
|
6
|
+
}
|
|
7
|
+
vars:externalsecrets:hashicorp-vault-server {
|
|
8
|
+
secret: secret/data/secret
|
|
9
|
+
password: secret/data/password
|
|
10
|
+
}
|
|
11
|
+
`;
|
|
12
|
+
|
|
13
|
+
const output = parseBruEnvironment(bru);
|
|
14
|
+
|
|
15
|
+
expect(output.externalSecrets.type).toBe('hashicorp-vault-server');
|
|
16
|
+
expect(output.externalSecrets.variables).toHaveLength(2);
|
|
17
|
+
|
|
18
|
+
output.externalSecrets.variables.forEach((variable) => {
|
|
19
|
+
expect(typeof variable.uid).toBe('string');
|
|
20
|
+
expect(variable.uid).toMatch(/^[a-zA-Z0-9]{21}$/);
|
|
21
|
+
expect(variable.enabled).toBe(true);
|
|
22
|
+
expect(variable).not.toHaveProperty('disabled');
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
expect(output.externalSecrets.variables.map(({ name, path, enabled }) => ({ name, path, enabled }))).toEqual([
|
|
26
|
+
{ name: 'secret', path: 'secret/data/secret', enabled: true },
|
|
27
|
+
{ name: 'password', path: 'secret/data/password', enabled: true }
|
|
28
|
+
]);
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it('should map a disabled external secret entry to enabled: false', () => {
|
|
32
|
+
const bru = `vars:externalsecrets:aws-secrets-manager {
|
|
33
|
+
active: my-active-secret
|
|
34
|
+
~inactive: my-inactive-secret
|
|
35
|
+
}
|
|
36
|
+
`;
|
|
37
|
+
|
|
38
|
+
const output = parseBruEnvironment(bru);
|
|
39
|
+
|
|
40
|
+
expect(output.externalSecrets.type).toBe('aws-secrets-manager');
|
|
41
|
+
const variables = output.externalSecrets.variables;
|
|
42
|
+
expect(variables.map(({ name, secretName, enabled }) => ({ name, secretName, enabled }))).toEqual([
|
|
43
|
+
{ name: 'active', secretName: 'my-active-secret', enabled: true },
|
|
44
|
+
{ name: 'inactive', secretName: 'my-inactive-secret', enabled: false }
|
|
45
|
+
]);
|
|
46
|
+
variables.forEach((variable) => {
|
|
47
|
+
expect(variable).not.toHaveProperty('disabled');
|
|
48
|
+
expect(variable.uid).toMatch(/^[a-zA-Z0-9]{21}$/);
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it('should leave environments without external secrets unchanged', () => {
|
|
53
|
+
const bru = `vars {
|
|
54
|
+
host: localhost
|
|
55
|
+
}
|
|
56
|
+
`;
|
|
57
|
+
|
|
58
|
+
const output = parseBruEnvironment(bru);
|
|
59
|
+
|
|
60
|
+
expect(output.externalSecrets).toBeUndefined();
|
|
61
|
+
expect(output.variables).toEqual([{ name: 'host', value: 'localhost', enabled: true, secret: false, type: 'text' }]);
|
|
62
|
+
});
|
|
63
|
+
});
|
|
@@ -0,0 +1,472 @@
|
|
|
1
|
+
const { redactLargeBruTextBlocks, restoreRedactedBlocks } = require('../utils/redact-large-text-blocks');
|
|
2
|
+
const { parseBruRequest, parseBruCollection } = require('../index');
|
|
3
|
+
|
|
4
|
+
const parseViaRedaction = (content, parser) => {
|
|
5
|
+
const { skeleton, blocks } = redactLargeBruTextBlocks(content);
|
|
6
|
+
return restoreRedactedBlocks(parser(skeleton), blocks);
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
const toCRLF = (content) => content.replace(/\n/g, '\r\n');
|
|
10
|
+
|
|
11
|
+
const requestBru = `meta {
|
|
12
|
+
name: Redaction Test
|
|
13
|
+
type: http
|
|
14
|
+
seq: 1
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
get {
|
|
18
|
+
url: https://example.com/api
|
|
19
|
+
body: json
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
headers {
|
|
23
|
+
content-type: application/json
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
body:json {
|
|
27
|
+
{
|
|
28
|
+
"hello": "world",
|
|
29
|
+
"nested": {
|
|
30
|
+
"a": 1,
|
|
31
|
+
"b": [1, 2, 3]
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
body:text {
|
|
37
|
+
This is a text body
|
|
38
|
+
spanning multiple lines
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
script:pre-request {
|
|
42
|
+
const x = 1;
|
|
43
|
+
if (x) {
|
|
44
|
+
console.log("hi");
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
script:post-response {
|
|
49
|
+
bru.setVar("y", 2);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
tests {
|
|
53
|
+
test("ok", function() {
|
|
54
|
+
expect(1).to.equal(1);
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
docs {
|
|
59
|
+
# Title
|
|
60
|
+
|
|
61
|
+
Some **markdown** docs with a fenced block:
|
|
62
|
+
|
|
63
|
+
code line stays indented
|
|
64
|
+
}
|
|
65
|
+
`;
|
|
66
|
+
|
|
67
|
+
const graphqlBru = `meta {
|
|
68
|
+
name: GraphQL Test
|
|
69
|
+
type: graphql
|
|
70
|
+
seq: 1
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
post {
|
|
74
|
+
url: https://example.com/graphql
|
|
75
|
+
body: graphql
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
body:graphql {
|
|
79
|
+
{
|
|
80
|
+
launchesPast {
|
|
81
|
+
launch_site {
|
|
82
|
+
site_name
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
body:graphql:vars {
|
|
89
|
+
{
|
|
90
|
+
"limit": 10
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
`;
|
|
94
|
+
|
|
95
|
+
const xmlSparqlBru = `meta {
|
|
96
|
+
name: Others
|
|
97
|
+
type: http
|
|
98
|
+
seq: 1
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
post {
|
|
102
|
+
url: https://example.com
|
|
103
|
+
body: xml
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
body:xml {
|
|
107
|
+
<xml>
|
|
108
|
+
<name>John</name>
|
|
109
|
+
</xml>
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
body:sparql {
|
|
113
|
+
SELECT * WHERE {
|
|
114
|
+
?s ?p ?o .
|
|
115
|
+
}
|
|
116
|
+
LIMIT 10
|
|
117
|
+
}
|
|
118
|
+
`;
|
|
119
|
+
|
|
120
|
+
const collectionBru = `headers {
|
|
121
|
+
x-trace: enabled
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
script:pre-request {
|
|
125
|
+
bru.setEnvVar("base", "https://example.com");
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
tests {
|
|
129
|
+
test("collection level", function() {
|
|
130
|
+
expect(true).to.equal(true);
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
docs {
|
|
135
|
+
# Collection docs
|
|
136
|
+
|
|
137
|
+
Notes for the whole collection.
|
|
138
|
+
}
|
|
139
|
+
`;
|
|
140
|
+
|
|
141
|
+
const folderBru = `meta {
|
|
142
|
+
name: My Folder
|
|
143
|
+
seq: 2
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
docs {
|
|
147
|
+
# Folder docs
|
|
148
|
+
}
|
|
149
|
+
`;
|
|
150
|
+
|
|
151
|
+
const blankLinesBru = `meta {
|
|
152
|
+
name: Blanks
|
|
153
|
+
type: http
|
|
154
|
+
seq: 1
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
post {
|
|
158
|
+
url: https://example.com
|
|
159
|
+
body: json
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
body:json {
|
|
163
|
+
|
|
164
|
+
{
|
|
165
|
+
"a": 1
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
}
|
|
170
|
+
`;
|
|
171
|
+
|
|
172
|
+
const appBru = `meta {
|
|
173
|
+
name: App Request
|
|
174
|
+
type: http
|
|
175
|
+
seq: 1
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
get {
|
|
179
|
+
url: https://example.com
|
|
180
|
+
body: none
|
|
181
|
+
auth: none
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
app {
|
|
185
|
+
enabled: true
|
|
186
|
+
code: '''
|
|
187
|
+
<div id="x">hi</div>
|
|
188
|
+
<style>
|
|
189
|
+
body {
|
|
190
|
+
color: red;
|
|
191
|
+
}
|
|
192
|
+
</style>
|
|
193
|
+
<script>
|
|
194
|
+
function go() {
|
|
195
|
+
return { ok: 1 };
|
|
196
|
+
}
|
|
197
|
+
</script>
|
|
198
|
+
'''
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
docs {
|
|
202
|
+
# Docs after the app block
|
|
203
|
+
}
|
|
204
|
+
`;
|
|
205
|
+
|
|
206
|
+
const standaloneAppBru = `meta {
|
|
207
|
+
name: Dashboard
|
|
208
|
+
type: app
|
|
209
|
+
seq: 2
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
app {
|
|
213
|
+
code: '''
|
|
214
|
+
<div id="app">standalone</div>
|
|
215
|
+
'''
|
|
216
|
+
}
|
|
217
|
+
`;
|
|
218
|
+
|
|
219
|
+
const appWithoutCodeBru = `meta {
|
|
220
|
+
name: No Code
|
|
221
|
+
type: http
|
|
222
|
+
seq: 1
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
get {
|
|
226
|
+
url: https://example.com
|
|
227
|
+
body: none
|
|
228
|
+
auth: none
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
app {
|
|
232
|
+
enabled: true
|
|
233
|
+
}
|
|
234
|
+
`;
|
|
235
|
+
|
|
236
|
+
const appEmptyCodeBru = `meta {
|
|
237
|
+
name: Empty Code
|
|
238
|
+
type: http
|
|
239
|
+
seq: 1
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
app {
|
|
243
|
+
enabled: true
|
|
244
|
+
code: '''
|
|
245
|
+
'''
|
|
246
|
+
}
|
|
247
|
+
`;
|
|
248
|
+
|
|
249
|
+
const docsAboutAppCodeBru = `meta {
|
|
250
|
+
name: Docs About App Code
|
|
251
|
+
type: http
|
|
252
|
+
seq: 1
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
get {
|
|
256
|
+
url: https://example.com
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
docs {
|
|
260
|
+
# How the app block works
|
|
261
|
+
|
|
262
|
+
app {
|
|
263
|
+
code: '''
|
|
264
|
+
<div>hi</div>
|
|
265
|
+
'''
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
`;
|
|
269
|
+
|
|
270
|
+
const appWithoutCodeThenDocsBru = `meta {
|
|
271
|
+
name: No Code Then Docs
|
|
272
|
+
type: http
|
|
273
|
+
seq: 1
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
get {
|
|
277
|
+
url: https://example.com
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
app {
|
|
281
|
+
enabled: true
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
docs {
|
|
285
|
+
Set the code like this:
|
|
286
|
+
|
|
287
|
+
code: '''
|
|
288
|
+
<div>hi</div>
|
|
289
|
+
'''
|
|
290
|
+
}
|
|
291
|
+
`;
|
|
292
|
+
|
|
293
|
+
describe('redactLargeBruTextBlocks', () => {
|
|
294
|
+
describe('matches a normal parse (ohm oracle)', () => {
|
|
295
|
+
const requestCases = [
|
|
296
|
+
['request with bodies, scripts, tests, docs', requestBru],
|
|
297
|
+
['graphql query + variables', graphqlBru],
|
|
298
|
+
['xml + sparql bodies', xmlSparqlBru],
|
|
299
|
+
['CRLF request', toCRLF(requestBru)],
|
|
300
|
+
['leading + trailing blank lines in body', blankLinesBru],
|
|
301
|
+
['CRLF leading + trailing blank lines', toCRLF(blankLinesBru)],
|
|
302
|
+
['request-level app code', appBru],
|
|
303
|
+
['CRLF request-level app code', toCRLF(appBru)],
|
|
304
|
+
['standalone app item', standaloneAppBru],
|
|
305
|
+
['app block without code', appWithoutCodeBru],
|
|
306
|
+
['app block with an empty code value', appEmptyCodeBru],
|
|
307
|
+
['docs documenting an app code block', docsAboutAppCodeBru],
|
|
308
|
+
['app block without code followed by docs holding a code pair', appWithoutCodeThenDocsBru]
|
|
309
|
+
];
|
|
310
|
+
|
|
311
|
+
it.each(requestCases)('%s', (_name, content) => {
|
|
312
|
+
expect(parseViaRedaction(content, parseBruRequest)).toEqual(parseBruRequest(content));
|
|
313
|
+
});
|
|
314
|
+
|
|
315
|
+
const collectionCases = [
|
|
316
|
+
['collection', collectionBru],
|
|
317
|
+
['folder', folderBru],
|
|
318
|
+
['CRLF collection', toCRLF(collectionBru)]
|
|
319
|
+
];
|
|
320
|
+
|
|
321
|
+
it.each(collectionCases)('%s', (_name, content) => {
|
|
322
|
+
expect(parseViaRedaction(content, parseBruCollection)).toEqual(parseBruCollection(content));
|
|
323
|
+
});
|
|
324
|
+
});
|
|
325
|
+
|
|
326
|
+
it('extracts each large text block and shrinks the skeleton', () => {
|
|
327
|
+
const { skeleton, blocks } = redactLargeBruTextBlocks(requestBru);
|
|
328
|
+
expect(blocks.length).toBe(6);
|
|
329
|
+
expect(skeleton.length).toBeLessThan(requestBru.length);
|
|
330
|
+
blocks.forEach((block) => {
|
|
331
|
+
expect(skeleton).toContain(block.token);
|
|
332
|
+
expect(skeleton).not.toContain(block.value);
|
|
333
|
+
});
|
|
334
|
+
});
|
|
335
|
+
|
|
336
|
+
it('keeps the skeleton bounded regardless of block size', () => {
|
|
337
|
+
const blob = 'x'.repeat(4 * 1024 * 1024);
|
|
338
|
+
const bru = `meta {
|
|
339
|
+
name: Huge
|
|
340
|
+
type: http
|
|
341
|
+
seq: 1
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
post {
|
|
345
|
+
url: https://example.com
|
|
346
|
+
body: json
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
body:json {
|
|
350
|
+
{ "blob": "${blob}" }
|
|
351
|
+
}
|
|
352
|
+
`;
|
|
353
|
+
const { skeleton, blocks } = redactLargeBruTextBlocks(bru);
|
|
354
|
+
expect(bru.length).toBeGreaterThan(4 * 1024 * 1024);
|
|
355
|
+
expect(skeleton.length).toBeLessThan(1024);
|
|
356
|
+
expect(blocks[0].value).toContain(blob);
|
|
357
|
+
});
|
|
358
|
+
|
|
359
|
+
it('keeps huge app code out of the skeleton, pair and delimiters intact', () => {
|
|
360
|
+
const blob = 'x'.repeat(4 * 1024 * 1024);
|
|
361
|
+
const bru = appBru.replace('<div id="x">hi</div>', `<div>${blob}</div>`);
|
|
362
|
+
|
|
363
|
+
const { skeleton, blocks } = redactLargeBruTextBlocks(bru);
|
|
364
|
+
expect(skeleton.length).toBeLessThan(1024);
|
|
365
|
+
expect(skeleton).toMatch(/code: '''\n\s+__BRU_REDACTED_TEXT_BLOCK_\w+__\n\s+'''/);
|
|
366
|
+
expect(skeleton).toContain('enabled: true');
|
|
367
|
+
expect(blocks.some((block) => block.value.includes(blob))).toBe(true);
|
|
368
|
+
});
|
|
369
|
+
|
|
370
|
+
it('leaves an app block whose code has no value untouched', () => {
|
|
371
|
+
const { blocks } = redactLargeBruTextBlocks(appEmptyCodeBru);
|
|
372
|
+
expect(blocks).toEqual([]);
|
|
373
|
+
});
|
|
374
|
+
|
|
375
|
+
it('leaves a code pair outside the app block untouched', () => {
|
|
376
|
+
const { blocks } = redactLargeBruTextBlocks(docsAboutAppCodeBru);
|
|
377
|
+
expect(blocks.length).toBe(1);
|
|
378
|
+
expect(blocks[0].value).toContain("code: '''");
|
|
379
|
+
expect(blocks[0].value).toContain('<div>hi</div>');
|
|
380
|
+
expect(blocks[0].value).not.toContain('__BRU_REDACTED_TEXT_BLOCK_');
|
|
381
|
+
});
|
|
382
|
+
|
|
383
|
+
it('leaves a code pair after an app block that has no code untouched', () => {
|
|
384
|
+
const { blocks } = redactLargeBruTextBlocks(appWithoutCodeThenDocsBru);
|
|
385
|
+
expect(blocks.length).toBe(1);
|
|
386
|
+
expect(blocks[0].value).toContain("code: '''");
|
|
387
|
+
expect(blocks[0].value).not.toContain('__BRU_REDACTED_TEXT_BLOCK_');
|
|
388
|
+
});
|
|
389
|
+
|
|
390
|
+
it('leaves dictionary body blocks untouched', () => {
|
|
391
|
+
const multipart = `meta {
|
|
392
|
+
name: Multipart
|
|
393
|
+
type: http
|
|
394
|
+
seq: 1
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
post {
|
|
398
|
+
url: https://example.com
|
|
399
|
+
body: multipartForm
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
body:multipart-form {
|
|
403
|
+
field: value
|
|
404
|
+
}
|
|
405
|
+
`;
|
|
406
|
+
const { blocks } = redactLargeBruTextBlocks(multipart);
|
|
407
|
+
expect(blocks.length).toBe(0);
|
|
408
|
+
expect(parseViaRedaction(multipart, parseBruRequest)).toEqual(parseBruRequest(multipart));
|
|
409
|
+
});
|
|
410
|
+
|
|
411
|
+
it('captures nested braces in content without ending the block early', () => {
|
|
412
|
+
const nested = `body:json {
|
|
413
|
+
{
|
|
414
|
+
"a": {
|
|
415
|
+
"b": [ { "c": 1 } ],
|
|
416
|
+
"d": {}
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
script:pre-request {
|
|
422
|
+
function outer() {
|
|
423
|
+
if (true) {
|
|
424
|
+
return { ok: 1 };
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
`;
|
|
429
|
+
const { blocks } = redactLargeBruTextBlocks(nested);
|
|
430
|
+
expect(blocks.length).toBe(2);
|
|
431
|
+
expect(blocks[0].value).toContain('"c": 1');
|
|
432
|
+
expect(blocks[0].value).toContain('"d": {}');
|
|
433
|
+
expect(blocks[1].value).toContain('return { ok: 1 };');
|
|
434
|
+
expect(parseViaRedaction(nested, parseBruRequest)).toEqual(parseBruRequest(nested));
|
|
435
|
+
});
|
|
436
|
+
|
|
437
|
+
it('only redacts blocks at column 0, never indented tags', () => {
|
|
438
|
+
const indented = `docs {
|
|
439
|
+
Nested pseudo-block should be treated as content:
|
|
440
|
+
body:json {
|
|
441
|
+
{ "a": 1 }
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
`;
|
|
445
|
+
const { blocks } = redactLargeBruTextBlocks(indented);
|
|
446
|
+
expect(blocks.length).toBe(1);
|
|
447
|
+
expect(blocks[0].value).toContain('body:json {');
|
|
448
|
+
});
|
|
449
|
+
|
|
450
|
+
it('returns content unchanged when no large text blocks are present', () => {
|
|
451
|
+
const minimal = `meta {
|
|
452
|
+
name: Bare
|
|
453
|
+
type: http
|
|
454
|
+
seq: 1
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
get {
|
|
458
|
+
url: https://example.com
|
|
459
|
+
}
|
|
460
|
+
`;
|
|
461
|
+
const { skeleton, blocks } = redactLargeBruTextBlocks(minimal);
|
|
462
|
+
expect(blocks).toEqual([]);
|
|
463
|
+
expect(skeleton).toBe(minimal);
|
|
464
|
+
});
|
|
465
|
+
});
|
|
466
|
+
|
|
467
|
+
describe('restoreRedactedBlocks', () => {
|
|
468
|
+
it('returns the parsed object unchanged when there are no blocks', () => {
|
|
469
|
+
const parsed = { request: { body: { json: 'x' } } };
|
|
470
|
+
expect(restoreRedactedBlocks(parsed, [])).toBe(parsed);
|
|
471
|
+
});
|
|
472
|
+
});
|