@usebruno/filestore 0.10.0 → 0.11.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/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/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/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/stringifyItem.d.ts.map +1 -1
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/types.d.ts +6 -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/package.json +7 -5
- package/src/constants.ts +9 -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 +313 -0
- package/src/formats/bru/utils/redact-large-text-blocks.ts +102 -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/items/parseApp.ts +37 -0
- package/src/formats/yml/items/parseGraphQLRequest.ts +8 -0
- package/src/formats/yml/items/parseGrpcRequest.ts +24 -3
- package/src/formats/yml/items/parseHttpRequest.ts +13 -0
- 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 +3 -0
- package/src/formats/yml/items/stringifyGrpcRequest.ts +9 -11
- package/src/formats/yml/items/stringifyHttpRequest.ts +10 -0
- package/src/formats/yml/items/stringifyWebsocketRequest.ts +22 -9
- package/src/formats/yml/parseCollection.spec.ts +70 -0
- package/src/formats/yml/parseCollection.ts +8 -3
- package/src/formats/yml/parseEnvironment.spec.ts +99 -0
- package/src/formats/yml/parseEnvironment.ts +41 -19
- package/src/formats/yml/parseFolder.spec.ts +52 -0
- package/src/formats/yml/parseFolder.ts +14 -21
- package/src/formats/yml/parseItem.spec.ts +147 -0
- package/src/formats/yml/parseItem.ts +4 -0
- package/src/formats/yml/stringifyCollection.spec.ts +77 -0
- package/src/formats/yml/stringifyCollection.ts +4 -0
- package/src/formats/yml/stringifyEnvironment.spec.ts +91 -0
- package/src/formats/yml/stringifyEnvironment.ts +55 -20
- package/src/formats/yml/stringifyFolder.spec.ts +47 -0
- package/src/formats/yml/stringifyItem.spec.ts +53 -0
- package/src/formats/yml/stringifyItem.ts +4 -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 +204 -0
- package/src/formats/yml/tests/variables.spec.js +7 -7
- package/src/index.ts +5 -0
- package/src/types/bruno-lang.d.ts +1 -0
- package/src/types.ts +7 -0
- package/src/utils/secretsJsonToEnvironments.spec.ts +211 -0
- package/src/utils/secretsJsonToEnvironments.ts +129 -0
- 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
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import parseCollection from '../parseCollection';
|
|
4
|
+
|
|
5
|
+
const loadFixture = (name) =>
|
|
6
|
+
fs.readFileSync(path.join(__dirname, 'fixtures', 'presets', `${name}.yml`), 'utf8');
|
|
7
|
+
|
|
8
|
+
describe('yml parseCollection - presets', () => {
|
|
9
|
+
it('parses presets when request.type and request.url are present', () => {
|
|
10
|
+
const { brunoConfig } = parseCollection(loadFixture('with-type-and-url'));
|
|
11
|
+
|
|
12
|
+
expect(brunoConfig.presets).toEqual({
|
|
13
|
+
requestType: 'graphql',
|
|
14
|
+
requestUrl: 'https://example.com/graphql'
|
|
15
|
+
});
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
it('defaults requestType and requestUrl to empty strings (not arrays) when request fields are missing', () => {
|
|
19
|
+
const { brunoConfig } = parseCollection(loadFixture('empty-request'));
|
|
20
|
+
|
|
21
|
+
expect(brunoConfig.presets).toEqual({
|
|
22
|
+
requestType: '',
|
|
23
|
+
requestUrl: ''
|
|
24
|
+
});
|
|
25
|
+
expect(Array.isArray(brunoConfig.presets.requestType)).toBe(false);
|
|
26
|
+
expect(Array.isArray(brunoConfig.presets.requestUrl)).toBe(false);
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it('does not set presets when the extension has no presets block', () => {
|
|
30
|
+
const { brunoConfig } = parseCollection(loadFixture('no-presets'));
|
|
31
|
+
|
|
32
|
+
expect(brunoConfig.presets).toBeUndefined();
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it('does not set presets when presets exists but request key is absent', () => {
|
|
36
|
+
const { brunoConfig } = parseCollection(loadFixture('no-request-key'));
|
|
37
|
+
|
|
38
|
+
expect(brunoConfig.presets).toBeUndefined();
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it('parses a realistic collection with only request.type set (no url) — defaults url to empty string', () => {
|
|
42
|
+
const { brunoConfig } = parseCollection(loadFixture('type-only-realistic'));
|
|
43
|
+
|
|
44
|
+
expect(brunoConfig.presets).toEqual({
|
|
45
|
+
requestType: 'graphql',
|
|
46
|
+
requestUrl: ''
|
|
47
|
+
});
|
|
48
|
+
expect(Array.isArray(brunoConfig.presets.requestUrl)).toBe(false);
|
|
49
|
+
expect(brunoConfig.ignore).toEqual(['node_modules', '.git']);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it('parses a realistic collection with request.type http and request.url set', () => {
|
|
53
|
+
const { brunoConfig } = parseCollection(loadFixture('url-only-realistic'));
|
|
54
|
+
|
|
55
|
+
expect(brunoConfig.presets).toEqual({
|
|
56
|
+
requestType: 'http',
|
|
57
|
+
requestUrl: 'https://example.com/graphql'
|
|
58
|
+
});
|
|
59
|
+
expect(Array.isArray(brunoConfig.presets.requestType)).toBe(false);
|
|
60
|
+
expect(Array.isArray(brunoConfig.presets.requestUrl)).toBe(false);
|
|
61
|
+
expect(brunoConfig.ignore).toEqual(['node_modules', '.git']);
|
|
62
|
+
});
|
|
63
|
+
});
|
|
@@ -48,46 +48,46 @@ variables:
|
|
|
48
48
|
const byName = (env) => Object.fromEntries(env.variables.map((v) => [v.name, v]));
|
|
49
49
|
|
|
50
50
|
describe('yml parseEnvironment - typed values', () => {
|
|
51
|
-
it('
|
|
51
|
+
it('coerces the value to its native JS type and preserves the type via dataType', () => {
|
|
52
52
|
const variables = byName(parseEnvironment(ENV_YML));
|
|
53
53
|
|
|
54
|
-
expect(variables.env_num).toMatchObject({ value:
|
|
55
|
-
expect(typeof variables.env_num.value).toBe('
|
|
54
|
+
expect(variables.env_num).toMatchObject({ value: 300, type: 'text', dataType: 'number' });
|
|
55
|
+
expect(typeof variables.env_num.value).toBe('number');
|
|
56
56
|
|
|
57
|
-
expect(variables.env_bool).toMatchObject({ value:
|
|
58
|
-
expect(variables.falsy_num).toMatchObject({ value:
|
|
59
|
-
expect(variables.falsy_bool).toMatchObject({ value:
|
|
57
|
+
expect(variables.env_bool).toMatchObject({ value: true, dataType: 'boolean' });
|
|
58
|
+
expect(variables.falsy_num).toMatchObject({ value: 0, dataType: 'number' });
|
|
59
|
+
expect(variables.falsy_bool).toMatchObject({ value: false, dataType: 'boolean' });
|
|
60
60
|
|
|
61
|
-
expect(variables.env_obj.
|
|
62
|
-
expect(typeof variables.env_obj.value).toBe('
|
|
63
|
-
expect(variables.env_obj.value).
|
|
61
|
+
expect(variables.env_obj.dataType).toBe('object');
|
|
62
|
+
expect(typeof variables.env_obj.value).toBe('object');
|
|
63
|
+
expect(variables.env_obj.value).toMatchObject({ scope: 'env' });
|
|
64
64
|
|
|
65
|
-
expect(variables.env_array_obj).toMatchObject({ value:
|
|
65
|
+
expect(variables.env_array_obj).toMatchObject({ value: [1, 2, 3, 4], dataType: 'object' });
|
|
66
66
|
});
|
|
67
67
|
|
|
68
|
-
it('does not set
|
|
68
|
+
it('does not set dataType for plain string values', () => {
|
|
69
69
|
const variables = byName(parseEnvironment(ENV_YML));
|
|
70
70
|
|
|
71
71
|
expect(variables.env_str).toMatchObject({ value: 'env_string', type: 'text', secret: false });
|
|
72
|
-
expect(variables.env_str).not.toHaveProperty('
|
|
72
|
+
expect(variables.env_str).not.toHaveProperty('dataType');
|
|
73
73
|
});
|
|
74
74
|
|
|
75
|
-
it('parses secret variables with no value or
|
|
75
|
+
it('parses secret variables with no value or dataType', () => {
|
|
76
76
|
const variables = byName(parseEnvironment(ENV_YML));
|
|
77
77
|
|
|
78
78
|
expect(variables.env_secret_str).toMatchObject({ name: 'env_secret_str', value: '', secret: true });
|
|
79
|
-
expect(variables.env_secret_str).not.toHaveProperty('
|
|
79
|
+
expect(variables.env_secret_str).not.toHaveProperty('dataType');
|
|
80
80
|
});
|
|
81
81
|
|
|
82
|
-
it('parses secret variables with a type, keeping the value empty and the type in
|
|
82
|
+
it('parses secret variables with a type, keeping the value empty and the type in dataType', () => {
|
|
83
83
|
const variables = byName(parseEnvironment(ENV_YML));
|
|
84
84
|
|
|
85
|
-
expect(variables.env_secret_num).toMatchObject({ value: '', secret: true,
|
|
86
|
-
expect(variables.env_secret_bool).toMatchObject({ value: '', secret: true,
|
|
87
|
-
expect(variables.env_secret_obj).toMatchObject({ value: '', secret: true,
|
|
85
|
+
expect(variables.env_secret_num).toMatchObject({ value: '', secret: true, dataType: 'number' });
|
|
86
|
+
expect(variables.env_secret_bool).toMatchObject({ value: '', secret: true, dataType: 'boolean' });
|
|
87
|
+
expect(variables.env_secret_obj).toMatchObject({ value: '', secret: true, dataType: 'object' });
|
|
88
88
|
});
|
|
89
89
|
|
|
90
|
-
it('serializes secret variable
|
|
90
|
+
it('serializes secret variable dataType back to a type field, omitting it for plain secrets', () => {
|
|
91
91
|
const yml = stringifyEnvironment(parseEnvironment(ENV_YML));
|
|
92
92
|
|
|
93
93
|
expect(yml).toContain('- secret: true\n name: env_secret_num\n type: number');
|
|
@@ -97,19 +97,19 @@ describe('yml parseEnvironment - typed values', () => {
|
|
|
97
97
|
expect(yml).not.toContain('name: env_secret_str\n type:');
|
|
98
98
|
});
|
|
99
99
|
|
|
100
|
-
it('serializes
|
|
100
|
+
it('serializes dataType back to an OpenCollection { type, data } value', () => {
|
|
101
101
|
const yml = stringifyEnvironment(parseEnvironment(ENV_YML));
|
|
102
102
|
|
|
103
103
|
expect(yml).toContain('type: number');
|
|
104
104
|
expect(yml).toContain('data: "300"');
|
|
105
105
|
expect(yml).toContain('type: boolean');
|
|
106
106
|
expect(yml).toContain('type: object');
|
|
107
|
-
// plain strings stay plain, never wrapped as a string
|
|
107
|
+
// plain strings stay plain, never wrapped as a string dataType
|
|
108
108
|
expect(yml).toContain('value: env_string');
|
|
109
109
|
expect(yml).not.toContain('type: string');
|
|
110
110
|
});
|
|
111
111
|
|
|
112
|
-
it('round-trips value and
|
|
112
|
+
it('round-trips value and dataType through parse -> stringify -> parse', () => {
|
|
113
113
|
const env = parseEnvironment(ENV_YML);
|
|
114
114
|
const reparsed = parseEnvironment(stringifyEnvironment(env));
|
|
115
115
|
|
|
@@ -118,44 +118,5 @@ describe('yml parseEnvironment - typed values', () => {
|
|
|
118
118
|
});
|
|
119
119
|
});
|
|
120
120
|
|
|
121
|
-
|
|
122
|
-
variables
|
|
123
|
-
- name: env_str
|
|
124
|
-
value: env_string
|
|
125
|
-
externalSecrets:
|
|
126
|
-
type: my-vault
|
|
127
|
-
variables:
|
|
128
|
-
- name: by_path
|
|
129
|
-
path: secret/data/secret
|
|
130
|
-
- name: by_secret_name
|
|
131
|
-
secretName: secret
|
|
132
|
-
- name: by_vault_name
|
|
133
|
-
vaultName: secret
|
|
134
|
-
`;
|
|
135
|
-
|
|
136
|
-
describe('yml parseEnvironment - external secrets', () => {
|
|
137
|
-
it('parses externalSecrets, preserving the type and arbitrary variable keys', () => {
|
|
138
|
-
const env = parseEnvironment(EXTERNAL_SECRETS_YML);
|
|
139
|
-
|
|
140
|
-
expect(env.externalSecrets).toEqual({
|
|
141
|
-
type: 'my-vault',
|
|
142
|
-
variables: [
|
|
143
|
-
{ name: 'by_path', path: 'secret/data/secret' },
|
|
144
|
-
{ name: 'by_secret_name', secretName: 'secret' },
|
|
145
|
-
{ name: 'by_vault_name', vaultName: 'secret' }
|
|
146
|
-
]
|
|
147
|
-
});
|
|
148
|
-
});
|
|
149
|
-
|
|
150
|
-
it('does not set externalSecrets when the yml has none', () => {
|
|
151
|
-
const env = parseEnvironment('name: test_env\nvariables: []\n');
|
|
152
|
-
expect(env.externalSecrets).toBeUndefined();
|
|
153
|
-
});
|
|
154
|
-
|
|
155
|
-
it('round-trips externalSecrets through parse -> stringify -> parse', () => {
|
|
156
|
-
const env = parseEnvironment(EXTERNAL_SECRETS_YML);
|
|
157
|
-
const reparsed = parseEnvironment(stringifyEnvironment(env));
|
|
158
|
-
|
|
159
|
-
expect(reparsed.externalSecrets).toEqual(env.externalSecrets);
|
|
160
|
-
});
|
|
161
|
-
});
|
|
121
|
+
// External-secrets parsing/serialization is covered in parseEnvironment.spec.ts and
|
|
122
|
+
// stringifyEnvironment.spec.ts against the strict { type, variables } model.
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import parseEnvironment from '../parseEnvironment';
|
|
2
|
+
|
|
3
|
+
describe('yml env parser', () => {
|
|
4
|
+
it('should parse empty variables', () => {
|
|
5
|
+
const input = `name: test\n`;
|
|
6
|
+
|
|
7
|
+
const output = parseEnvironment(input);
|
|
8
|
+
|
|
9
|
+
expect(output.name).toBe('test');
|
|
10
|
+
expect(output.variables).toEqual([]);
|
|
11
|
+
expect(output.externalSecrets).toBeUndefined();
|
|
12
|
+
expect(output.color).toBeNull();
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
it('should parse multiple regular variables with disabled flag', () => {
|
|
16
|
+
const input = `name: test
|
|
17
|
+
variables:
|
|
18
|
+
- name: url
|
|
19
|
+
value: http://localhost:3000
|
|
20
|
+
- name: port
|
|
21
|
+
value: "3000"
|
|
22
|
+
disabled: true
|
|
23
|
+
`;
|
|
24
|
+
|
|
25
|
+
const output = parseEnvironment(input);
|
|
26
|
+
|
|
27
|
+
expect(output.variables).toHaveLength(2);
|
|
28
|
+
expect(output.variables[0]).toMatchObject({ name: 'url', value: 'http://localhost:3000', enabled: true });
|
|
29
|
+
expect(output.variables[1]).toMatchObject({ name: 'port', value: '3000', enabled: false });
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it('should parse a secret variable', () => {
|
|
33
|
+
const input = `name: test
|
|
34
|
+
variables:
|
|
35
|
+
- secret: true
|
|
36
|
+
name: token
|
|
37
|
+
`;
|
|
38
|
+
|
|
39
|
+
const output = parseEnvironment(input);
|
|
40
|
+
|
|
41
|
+
expect(output.variables).toHaveLength(1);
|
|
42
|
+
expect(output.variables[0]).toMatchObject({
|
|
43
|
+
name: 'token',
|
|
44
|
+
value: '',
|
|
45
|
+
enabled: true,
|
|
46
|
+
secret: true
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it('should parse a hashicorp-vault provider block with disabled entries', () => {
|
|
51
|
+
const input = `name: test
|
|
52
|
+
variables:
|
|
53
|
+
- name: url
|
|
54
|
+
value: http://localhost:3000
|
|
55
|
+
externalSecrets:
|
|
56
|
+
type: hashicorp-vault-cloud
|
|
57
|
+
variables:
|
|
58
|
+
- name: cloudSecret
|
|
59
|
+
path: secret/data/cloud
|
|
60
|
+
- name: dbPassword
|
|
61
|
+
path: secret/data/db-password
|
|
62
|
+
disabled: true
|
|
63
|
+
`;
|
|
64
|
+
|
|
65
|
+
const output = parseEnvironment(input);
|
|
66
|
+
|
|
67
|
+
expect(output.variables).toHaveLength(1);
|
|
68
|
+
expect(output.externalSecrets!.type).toBe('hashicorp-vault-cloud');
|
|
69
|
+
expect(output.externalSecrets!.variables).toMatchObject([
|
|
70
|
+
{ name: 'cloudSecret', path: 'secret/data/cloud', enabled: true },
|
|
71
|
+
{ name: 'dbPassword', path: 'secret/data/db-password', enabled: false }
|
|
72
|
+
]);
|
|
73
|
+
output.externalSecrets!.variables.forEach((variable: any) => {
|
|
74
|
+
expect(variable.uid).toMatch(/^[a-zA-Z0-9]{21}$/);
|
|
75
|
+
});
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
it('should parse an aws-secrets-manager provider block', () => {
|
|
79
|
+
const input = `name: test
|
|
80
|
+
externalSecrets:
|
|
81
|
+
type: aws-secrets-manager
|
|
82
|
+
variables:
|
|
83
|
+
- name: awsSecret
|
|
84
|
+
secretName: my-aws-secret
|
|
85
|
+
`;
|
|
86
|
+
|
|
87
|
+
const output = parseEnvironment(input);
|
|
88
|
+
|
|
89
|
+
expect(output.externalSecrets!.type).toBe('aws-secrets-manager');
|
|
90
|
+
expect(output.externalSecrets!.variables).toMatchObject([{ name: 'awsSecret', secretName: 'my-aws-secret', enabled: true }]);
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
it('should parse an azure-key-vault provider block', () => {
|
|
94
|
+
const input = `name: test
|
|
95
|
+
externalSecrets:
|
|
96
|
+
type: azure-key-vault
|
|
97
|
+
variables:
|
|
98
|
+
- name: azureSecret
|
|
99
|
+
vaultName: my-key-vault
|
|
100
|
+
`;
|
|
101
|
+
|
|
102
|
+
const output = parseEnvironment(input);
|
|
103
|
+
|
|
104
|
+
expect(output.externalSecrets!.type).toBe('azure-key-vault');
|
|
105
|
+
expect(output.externalSecrets!.variables).toMatchObject([{ name: 'azureSecret', vaultName: 'my-key-vault', enabled: true }]);
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
it('should keep a half-filled row (a name with an empty identifier)', () => {
|
|
109
|
+
const input = `name: test
|
|
110
|
+
externalSecrets:
|
|
111
|
+
type: hashicorp-vault-server
|
|
112
|
+
variables:
|
|
113
|
+
- name: good
|
|
114
|
+
path: secret/data/good
|
|
115
|
+
- name: bad
|
|
116
|
+
path: ''
|
|
117
|
+
`;
|
|
118
|
+
|
|
119
|
+
const output = parseEnvironment(input);
|
|
120
|
+
|
|
121
|
+
expect(output.externalSecrets!.type).toBe('hashicorp-vault-server');
|
|
122
|
+
expect(output.externalSecrets!.variables).toMatchObject([
|
|
123
|
+
{ name: 'good', path: 'secret/data/good', enabled: true },
|
|
124
|
+
{ name: 'bad', path: '', enabled: true }
|
|
125
|
+
]);
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
it('should parse a provider block with no variables', () => {
|
|
129
|
+
const input = `name: test
|
|
130
|
+
externalSecrets:
|
|
131
|
+
type: aws-secrets-manager
|
|
132
|
+
`;
|
|
133
|
+
|
|
134
|
+
const output = parseEnvironment(input);
|
|
135
|
+
|
|
136
|
+
expect(output.externalSecrets!.type).toBe('aws-secrets-manager');
|
|
137
|
+
expect(output.externalSecrets!.variables).toEqual([]);
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
it('should drop the block for an unknown provider type', () => {
|
|
141
|
+
const input = `name: test
|
|
142
|
+
externalSecrets:
|
|
143
|
+
type: made-up-provider
|
|
144
|
+
variables:
|
|
145
|
+
- name: x
|
|
146
|
+
path: p/x
|
|
147
|
+
`;
|
|
148
|
+
|
|
149
|
+
const output = parseEnvironment(input);
|
|
150
|
+
expect(output.externalSecrets).toBeUndefined();
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
it('should default name to Untitled Environment when missing', () => {
|
|
154
|
+
const input = `variables:\n - name: foo\n value: bar\n`;
|
|
155
|
+
|
|
156
|
+
const output = parseEnvironment(input);
|
|
157
|
+
|
|
158
|
+
expect(output.name).toBe('Untitled Environment');
|
|
159
|
+
});
|
|
160
|
+
});
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
import stringifyEnvironment from '../stringifyEnvironment';
|
|
2
|
+
|
|
3
|
+
describe('yml env stringifier', () => {
|
|
4
|
+
it('should stringify empty environment', () => {
|
|
5
|
+
const output = stringifyEnvironment({
|
|
6
|
+
uid: 'env1',
|
|
7
|
+
name: 'test',
|
|
8
|
+
variables: [],
|
|
9
|
+
color: null
|
|
10
|
+
} as any);
|
|
11
|
+
|
|
12
|
+
expect(output).toBe('name: test\n');
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
it('should stringify multiple regular variables with disabled flag', () => {
|
|
16
|
+
const output = stringifyEnvironment({
|
|
17
|
+
uid: 'env1',
|
|
18
|
+
name: 'test',
|
|
19
|
+
variables: [
|
|
20
|
+
{ uid: 'v1', name: 'url', value: 'http://localhost:3000', type: 'text', enabled: true },
|
|
21
|
+
{ uid: 'v2', name: 'port', value: '3000', type: 'text', enabled: false }
|
|
22
|
+
],
|
|
23
|
+
color: null
|
|
24
|
+
} as any);
|
|
25
|
+
|
|
26
|
+
expect(output).toBe(`name: test
|
|
27
|
+
variables:
|
|
28
|
+
- name: url
|
|
29
|
+
value: http://localhost:3000
|
|
30
|
+
- name: port
|
|
31
|
+
value: "3000"
|
|
32
|
+
disabled: true
|
|
33
|
+
`);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it('should stringify secret variables without their values', () => {
|
|
37
|
+
const output = stringifyEnvironment({
|
|
38
|
+
uid: 'env1',
|
|
39
|
+
name: 'test',
|
|
40
|
+
variables: [
|
|
41
|
+
{ uid: 'v1', name: 'token', value: 'secret-value-not-persisted', type: 'text', enabled: true, secret: true }
|
|
42
|
+
],
|
|
43
|
+
color: null
|
|
44
|
+
} as any);
|
|
45
|
+
|
|
46
|
+
expect(output).toBe(`name: test
|
|
47
|
+
variables:
|
|
48
|
+
- secret: true
|
|
49
|
+
name: token
|
|
50
|
+
`);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it('should stringify a hashicorp-vault provider block with disabled entries', () => {
|
|
54
|
+
const output = stringifyEnvironment({
|
|
55
|
+
uid: 'env1',
|
|
56
|
+
name: 'test',
|
|
57
|
+
variables: [
|
|
58
|
+
{ uid: 'v1', name: 'url', value: 'http://localhost:3000', type: 'text', enabled: true }
|
|
59
|
+
],
|
|
60
|
+
externalSecrets: {
|
|
61
|
+
type: 'hashicorp-vault-cloud',
|
|
62
|
+
variables: [
|
|
63
|
+
{ uid: 'e1', name: 'cloudSecret', path: 'secret/data/cloud', enabled: true },
|
|
64
|
+
{ uid: 'e2', name: 'dbPassword', path: 'secret/data/db-password', enabled: false }
|
|
65
|
+
]
|
|
66
|
+
},
|
|
67
|
+
color: null
|
|
68
|
+
} as any);
|
|
69
|
+
|
|
70
|
+
expect(output).toBe(`name: test
|
|
71
|
+
variables:
|
|
72
|
+
- name: url
|
|
73
|
+
value: http://localhost:3000
|
|
74
|
+
externalSecrets:
|
|
75
|
+
type: hashicorp-vault-cloud
|
|
76
|
+
variables:
|
|
77
|
+
- name: cloudSecret
|
|
78
|
+
path: secret/data/cloud
|
|
79
|
+
- name: dbPassword
|
|
80
|
+
path: secret/data/db-password
|
|
81
|
+
disabled: true
|
|
82
|
+
`);
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
it('should stringify an aws-secrets-manager provider block', () => {
|
|
86
|
+
const output = stringifyEnvironment({
|
|
87
|
+
uid: 'env1',
|
|
88
|
+
name: 'test',
|
|
89
|
+
variables: [],
|
|
90
|
+
externalSecrets: {
|
|
91
|
+
type: 'aws-secrets-manager',
|
|
92
|
+
variables: [
|
|
93
|
+
{ uid: 'e1', name: 'awsSecret', secretName: 'my-aws-secret', enabled: true }
|
|
94
|
+
]
|
|
95
|
+
},
|
|
96
|
+
color: null
|
|
97
|
+
} as any);
|
|
98
|
+
|
|
99
|
+
expect(output).toBe(`name: test
|
|
100
|
+
externalSecrets:
|
|
101
|
+
type: aws-secrets-manager
|
|
102
|
+
variables:
|
|
103
|
+
- name: awsSecret
|
|
104
|
+
secretName: my-aws-secret
|
|
105
|
+
`);
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
it('should stringify an azure-key-vault provider block', () => {
|
|
109
|
+
const output = stringifyEnvironment({
|
|
110
|
+
uid: 'env1',
|
|
111
|
+
name: 'test',
|
|
112
|
+
variables: [],
|
|
113
|
+
externalSecrets: {
|
|
114
|
+
type: 'azure-key-vault',
|
|
115
|
+
variables: [
|
|
116
|
+
{ uid: 'e1', name: 'azureSecret', vaultName: 'my-key-vault', enabled: true }
|
|
117
|
+
]
|
|
118
|
+
},
|
|
119
|
+
color: null
|
|
120
|
+
} as any);
|
|
121
|
+
|
|
122
|
+
expect(output).toBe(`name: test
|
|
123
|
+
externalSecrets:
|
|
124
|
+
type: azure-key-vault
|
|
125
|
+
variables:
|
|
126
|
+
- name: azureSecret
|
|
127
|
+
vaultName: my-key-vault
|
|
128
|
+
`);
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
it('should keep a half-filled row (a name with an empty identifier)', () => {
|
|
132
|
+
const output = stringifyEnvironment({
|
|
133
|
+
uid: 'env1',
|
|
134
|
+
name: 'test',
|
|
135
|
+
variables: [],
|
|
136
|
+
externalSecrets: {
|
|
137
|
+
type: 'hashicorp-vault-server',
|
|
138
|
+
variables: [
|
|
139
|
+
{ uid: 'e1', name: 'good', path: 'secret/data/good', enabled: true },
|
|
140
|
+
{ uid: 'e2', name: 'bad', path: '', enabled: true }
|
|
141
|
+
]
|
|
142
|
+
},
|
|
143
|
+
color: null
|
|
144
|
+
} as any);
|
|
145
|
+
|
|
146
|
+
// The half-filled 'bad' row is serialized with an explicit empty identifier, not dropped.
|
|
147
|
+
expect(output).toContain(` - name: good
|
|
148
|
+
path: secret/data/good`);
|
|
149
|
+
expect(output).toContain(` - name: bad
|
|
150
|
+
path: ""`);
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
it('coerces a non-string identifier to a string (matching the parser) instead of dropping it', () => {
|
|
154
|
+
const output = stringifyEnvironment({
|
|
155
|
+
uid: 'env1',
|
|
156
|
+
name: 'test',
|
|
157
|
+
variables: [],
|
|
158
|
+
externalSecrets: {
|
|
159
|
+
type: 'hashicorp-vault-server',
|
|
160
|
+
variables: [{ uid: 'e1', name: 'num', path: 123, enabled: true }]
|
|
161
|
+
},
|
|
162
|
+
color: null
|
|
163
|
+
} as any);
|
|
164
|
+
|
|
165
|
+
// 123 is coerced to '123', not dropped to an empty identifier.
|
|
166
|
+
expect(output).toContain('123');
|
|
167
|
+
expect(output).not.toContain(`- name: num
|
|
168
|
+
path: ""`);
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
it('should emit only the provider block when variables is empty', () => {
|
|
172
|
+
const output = stringifyEnvironment({
|
|
173
|
+
uid: 'env1',
|
|
174
|
+
name: 'test',
|
|
175
|
+
variables: [],
|
|
176
|
+
externalSecrets: {
|
|
177
|
+
type: 'aws-secrets-manager',
|
|
178
|
+
variables: []
|
|
179
|
+
},
|
|
180
|
+
color: null
|
|
181
|
+
} as any);
|
|
182
|
+
|
|
183
|
+
expect(output).toBe(`name: test
|
|
184
|
+
externalSecrets:
|
|
185
|
+
type: aws-secrets-manager
|
|
186
|
+
variables: []
|
|
187
|
+
`);
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
it('should not emit the external-secret section for an unknown provider type', () => {
|
|
191
|
+
const output = stringifyEnvironment({
|
|
192
|
+
uid: 'env1',
|
|
193
|
+
name: 'test',
|
|
194
|
+
variables: [],
|
|
195
|
+
externalSecrets: {
|
|
196
|
+
type: 'made-up-provider',
|
|
197
|
+
variables: [{ uid: 'e1', name: 'x', path: 'p/x', enabled: true }]
|
|
198
|
+
},
|
|
199
|
+
color: null
|
|
200
|
+
} as any);
|
|
201
|
+
|
|
202
|
+
expect(output).toBe('name: test\n');
|
|
203
|
+
});
|
|
204
|
+
});
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { toBrunoVariables, toOpenCollectionVariables } from '../common/variables';
|
|
2
2
|
|
|
3
3
|
describe('yml variables - typed values (collection / folder / request vars)', () => {
|
|
4
|
-
it('reads a typed value
|
|
4
|
+
it('reads a typed value coercing it to its native JS type and records the dataType', () => {
|
|
5
5
|
const ocVariables = [
|
|
6
6
|
{ name: 'var_str', value: 'plain' },
|
|
7
7
|
{ name: 'var_num', value: { type: 'number', data: '300' } },
|
|
@@ -12,16 +12,16 @@ describe('yml variables - typed values (collection / folder / request vars)', ()
|
|
|
12
12
|
const { req } = toBrunoVariables(ocVariables);
|
|
13
13
|
|
|
14
14
|
expect(req.find((v) => v.name === 'var_str')).toMatchObject({ value: 'plain' });
|
|
15
|
-
expect(req.find((v) => v.name === 'var_str')).not.toHaveProperty('
|
|
16
|
-
expect(req.find((v) => v.name === 'var_num')).toMatchObject({ value:
|
|
17
|
-
expect(req.find((v) => v.name === 'var_bool')).toMatchObject({ value:
|
|
18
|
-
expect(req.find((v) => v.name === 'var_obj')).toMatchObject({ value:
|
|
15
|
+
expect(req.find((v) => v.name === 'var_str')).not.toHaveProperty('dataType');
|
|
16
|
+
expect(req.find((v) => v.name === 'var_num')).toMatchObject({ value: 300, dataType: 'number' });
|
|
17
|
+
expect(req.find((v) => v.name === 'var_bool')).toMatchObject({ value: false, dataType: 'boolean' });
|
|
18
|
+
expect(req.find((v) => v.name === 'var_obj')).toMatchObject({ value: { scope: 'folder' }, dataType: 'object' });
|
|
19
19
|
});
|
|
20
20
|
|
|
21
|
-
it('writes
|
|
21
|
+
it('writes dataType back as a { type, data } value, leaving plain strings untouched', () => {
|
|
22
22
|
const brunoVariables = [
|
|
23
23
|
{ uid: 'u1', name: 'var_str', value: 'plain', enabled: true, local: false },
|
|
24
|
-
{ uid: 'u2', name: 'var_num', value: '300',
|
|
24
|
+
{ uid: 'u2', name: 'var_num', value: '300', dataType: 'number', enabled: true, local: false }
|
|
25
25
|
];
|
|
26
26
|
|
|
27
27
|
const ocVariables = toOpenCollectionVariables(brunoVariables);
|
package/src/index.ts
CHANGED
|
@@ -27,6 +27,7 @@ import {
|
|
|
27
27
|
} from './types';
|
|
28
28
|
import { DEFAULT_COLLECTION_FORMAT } from './constants';
|
|
29
29
|
import { bruRequestParseAndRedactBodyData } from './formats/bru/utils/request-parse-and-redact-body-data';
|
|
30
|
+
import { redactLargeBruTextBlocks, restoreRedactedBlocks } from './formats/bru/utils/redact-large-text-blocks';
|
|
30
31
|
|
|
31
32
|
// request
|
|
32
33
|
export const parseRequest = (content: string, options: ParseOptions = { format: DEFAULT_COLLECTION_FORMAT }): any => {
|
|
@@ -135,6 +136,10 @@ export const parseDotEnv = (content: string): Record<string, string> => {
|
|
|
135
136
|
return dotenvToJson(content);
|
|
136
137
|
};
|
|
137
138
|
|
|
139
|
+
export { redactLargeBruTextBlocks, restoreRedactedBlocks };
|
|
140
|
+
export type { RedactedBlock, RedactionResult } from './formats/bru/utils/redact-large-text-blocks';
|
|
141
|
+
export { migrateSecretsJsonToEnvironments } from './utils/secretsJsonToEnvironments';
|
|
142
|
+
|
|
138
143
|
export { BruParserWorker };
|
|
139
144
|
export * from './types';
|
|
140
145
|
export * from './constants';
|
|
@@ -6,4 +6,5 @@ declare module '@usebruno/lang' {
|
|
|
6
6
|
export function collectionBruToJson(bruContent: string): any;
|
|
7
7
|
export function jsonToCollectionBru(jsonData: any): string;
|
|
8
8
|
export function dotenvToJson(envContent: string): Record<string, string>;
|
|
9
|
+
export function outdentString(str: string, spaces?: number): string;
|
|
9
10
|
}
|