@aws-mdaa/dataops-job-l3-construct 1.5.0 → 1.6.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/.jsii +38 -28
- package/README.md +5 -0
- package/lib/dataops-job-l3-construct.d.ts +8 -1
- package/lib/dataops-job-l3-construct.js +40 -13
- package/node_modules/@aws-mdaa/config/README.md +3 -0
- package/node_modules/@aws-mdaa/config/lib/blueprint-value-transformer.d.ts +20 -0
- package/node_modules/@aws-mdaa/config/lib/blueprint-value-transformer.js +70 -0
- package/node_modules/@aws-mdaa/config/lib/blueprint-value-transformer.ts +88 -0
- package/node_modules/@aws-mdaa/config/lib/config.d.ts +87 -0
- package/node_modules/@aws-mdaa/config/lib/config.js +7 -0
- package/node_modules/@aws-mdaa/config/lib/config.ts +92 -0
- package/node_modules/@aws-mdaa/config/lib/index.d.ts +11 -0
- package/node_modules/@aws-mdaa/config/lib/index.js +28 -0
- package/node_modules/@aws-mdaa/config/lib/index.ts +12 -0
- package/node_modules/@aws-mdaa/config/lib/param-transformer.d.ts +49 -0
- package/node_modules/@aws-mdaa/config/lib/param-transformer.js +160 -0
- package/node_modules/@aws-mdaa/config/lib/param-transformer.ts +159 -0
- package/node_modules/@aws-mdaa/config/lib/path-value-transformer.d.ts +10 -0
- package/node_modules/@aws-mdaa/config/lib/path-value-transformer.js +30 -0
- package/node_modules/@aws-mdaa/config/lib/path-value-transformer.ts +27 -0
- package/node_modules/@aws-mdaa/config/lib/ref-value-transformer.d.ts +44 -0
- package/node_modules/@aws-mdaa/config/lib/ref-value-transformer.js +243 -0
- package/node_modules/@aws-mdaa/config/lib/ref-value-transformer.ts +302 -0
- package/node_modules/@aws-mdaa/config/lib/ssm-ref-transformer.d.ts +8 -0
- package/node_modules/@aws-mdaa/config/lib/ssm-ref-transformer.js +22 -0
- package/node_modules/@aws-mdaa/config/lib/ssm-ref-transformer.ts +21 -0
- package/node_modules/@aws-mdaa/config/lib/transformer.d.ts +35 -0
- package/node_modules/@aws-mdaa/config/lib/transformer.js +66 -0
- package/node_modules/@aws-mdaa/config/lib/transformer.ts +74 -0
- package/node_modules/@aws-mdaa/config/package.json +42 -0
- package/node_modules/@aws-mdaa/config/test/blueprint-value-transformer.test.js +224 -0
- package/node_modules/@aws-mdaa/config/test/blueprint-value-transformer.test.ts +259 -0
- package/node_modules/@aws-mdaa/config/test/config-nt.test.d.ts +5 -0
- package/node_modules/@aws-mdaa/config/test/config-nt.test.js +129 -0
- package/node_modules/@aws-mdaa/config/test/config-nt.test.ts +163 -0
- package/node_modules/@aws-mdaa/config/test/config.test.d.ts +5 -0
- package/node_modules/@aws-mdaa/config/test/config.test.js +409 -0
- package/node_modules/@aws-mdaa/config/test/config.test.ts +517 -0
- package/node_modules/@aws-mdaa/config/test/param-transformer.test.d.ts +5 -0
- package/node_modules/@aws-mdaa/config/test/param-transformer.test.js +216 -0
- package/node_modules/@aws-mdaa/config/test/param-transformer.test.ts +234 -0
- package/node_modules/@aws-mdaa/config/test/path-value-transformer.test.d.ts +5 -0
- package/node_modules/@aws-mdaa/config/test/path-value-transformer.test.js +59 -0
- package/node_modules/@aws-mdaa/config/test/path-value-transformer.test.ts +68 -0
- package/node_modules/@aws-mdaa/config/test/ref-value-transformer.test.d.ts +5 -0
- package/node_modules/@aws-mdaa/config/test/ref-value-transformer.test.js +254 -0
- package/node_modules/@aws-mdaa/config/test/ref-value-transformer.test.ts +304 -0
- package/node_modules/@aws-mdaa/config/test/ssm-ref-transformer.test.d.ts +5 -0
- package/node_modules/@aws-mdaa/config/test/ssm-ref-transformer.test.js +66 -0
- package/node_modules/@aws-mdaa/config/test/ssm-ref-transformer.test.ts +79 -0
- package/node_modules/@aws-mdaa/config/tsconfig.tsbuildinfo +1 -0
- package/node_modules/lodash/README.md +2 -2
- package/node_modules/lodash/_baseOrderBy.js +1 -1
- package/node_modules/lodash/_baseUnset.js +7 -20
- package/node_modules/lodash/_setCacheHas.js +1 -1
- package/node_modules/lodash/compact.js +1 -1
- package/node_modules/lodash/core.js +3 -3
- package/node_modules/lodash/core.min.js +26 -25
- package/node_modules/lodash/fromPairs.js +3 -1
- package/node_modules/lodash/lodash.js +38 -27
- package/node_modules/lodash/lodash.min.js +125 -129
- package/node_modules/lodash/package.json +4 -2
- package/node_modules/lodash/random.js +9 -0
- package/node_modules/lodash/template.js +16 -4
- package/node_modules/lodash/templateSettings.js +4 -0
- package/package.json +27 -32
- package/node_modules/@aws-mdaa/s3-bucketpolicy-helper/README.md +0 -185
- package/node_modules/@aws-mdaa/s3-bucketpolicy-helper/lib/index.d.ts +0 -57
- package/node_modules/@aws-mdaa/s3-bucketpolicy-helper/lib/index.js +0 -198
- package/node_modules/@aws-mdaa/s3-bucketpolicy-helper/lib/index.ts +0 -241
- package/node_modules/@aws-mdaa/s3-bucketpolicy-helper/package.json +0 -44
- package/node_modules/@aws-mdaa/s3-bucketpolicy-helper/test/bucketpolicy-helper.test.js +0 -200
- package/node_modules/@aws-mdaa/s3-bucketpolicy-helper/test/bucketpolicy-helper.test.ts +0 -215
- package/node_modules/@aws-mdaa/s3-bucketpolicy-helper/tsconfig.tsbuildinfo +0 -1
- package/node_modules/@aws-mdaa/s3-inventory-helper/.npmignore +0 -34
- package/node_modules/@aws-mdaa/s3-inventory-helper/README.md +0 -3
- package/node_modules/@aws-mdaa/s3-inventory-helper/jest.config.js +0 -5
- package/node_modules/@aws-mdaa/s3-inventory-helper/lib/index.d.ts +0 -48
- package/node_modules/@aws-mdaa/s3-inventory-helper/lib/index.js +0 -213
- package/node_modules/@aws-mdaa/s3-inventory-helper/lib/index.ts +0 -241
- package/node_modules/@aws-mdaa/s3-inventory-helper/package.json +0 -44
- package/node_modules/@aws-mdaa/s3-inventory-helper/test/TODO +0 -0
- package/node_modules/@aws-mdaa/s3-inventory-helper/tsconfig.json +0 -40
- package/node_modules/@aws-mdaa/s3-inventory-helper/tsconfig.tsbuildinfo +0 -1
- package/node_modules/@aws-mdaa/s3-inventory-helper/typedoc.json +0 -7
- /package/node_modules/@aws-mdaa/{s3-bucketpolicy-helper → config}/.npmignore +0 -0
- /package/node_modules/@aws-mdaa/{s3-bucketpolicy-helper → config}/jest.config.js +0 -0
- /package/node_modules/@aws-mdaa/{s3-bucketpolicy-helper/test/bucketpolicy-helper.test.d.ts → config/test/blueprint-value-transformer.test.d.ts} +0 -0
- /package/node_modules/@aws-mdaa/{s3-bucketpolicy-helper → config}/tsconfig.json +0 -0
- /package/node_modules/@aws-mdaa/{s3-bucketpolicy-helper → config}/typedoc.json +0 -0
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/*!
|
|
3
|
+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
4
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
const aws_cdk_lib_1 = require("aws-cdk-lib");
|
|
8
|
+
const lib_1 = require("../lib");
|
|
9
|
+
describe('MdaaConfigBlueprintRefValueTransformer', () => {
|
|
10
|
+
let mockNaming;
|
|
11
|
+
let stack;
|
|
12
|
+
beforeEach(() => {
|
|
13
|
+
stack = new aws_cdk_lib_1.Stack();
|
|
14
|
+
mockNaming = {
|
|
15
|
+
props: {
|
|
16
|
+
org: 'test-org',
|
|
17
|
+
env: 'test-env',
|
|
18
|
+
domain: 'test-domain',
|
|
19
|
+
moduleName: 'test-module',
|
|
20
|
+
},
|
|
21
|
+
withModuleName: jest.fn().mockReturnThis(),
|
|
22
|
+
withDomain: jest.fn().mockReturnThis(),
|
|
23
|
+
withOrg: jest.fn().mockReturnThis(),
|
|
24
|
+
withEnv: jest.fn().mockReturnThis(),
|
|
25
|
+
withSuffix: jest.fn().mockReturnThis(),
|
|
26
|
+
ssmDomainPath: jest.fn().mockReturnValue('/test/domain/path'),
|
|
27
|
+
ssmEnvPath: jest.fn().mockReturnValue('/test/env/path'),
|
|
28
|
+
ssmOrgPath: jest.fn().mockReturnValue('/test-org/'),
|
|
29
|
+
resourceName: jest
|
|
30
|
+
.fn()
|
|
31
|
+
.mockImplementation((suffix) => (suffix ? `test-resource-${suffix}` : 'test-resource')),
|
|
32
|
+
ssmPath: jest.fn().mockImplementation((path) => `/test/${path}`),
|
|
33
|
+
stackName: jest.fn().mockImplementation((name) => (name ? `test-stack-${name}` : 'test-stack')),
|
|
34
|
+
exportName: jest.fn().mockImplementation((path) => `test-export-${path}`),
|
|
35
|
+
};
|
|
36
|
+
});
|
|
37
|
+
test('returns unchanged value without refs', () => {
|
|
38
|
+
const transformer = new lib_1.MdaaConfigBlueprintRefValueTransformer({
|
|
39
|
+
naming: mockNaming,
|
|
40
|
+
scope: stack,
|
|
41
|
+
});
|
|
42
|
+
expect(transformer.transformValue('plain-value')).toBe('plain-value');
|
|
43
|
+
});
|
|
44
|
+
test('returns unchanged value with empty string', () => {
|
|
45
|
+
const transformer = new lib_1.MdaaConfigBlueprintRefValueTransformer({
|
|
46
|
+
naming: mockNaming,
|
|
47
|
+
scope: stack,
|
|
48
|
+
});
|
|
49
|
+
expect(transformer.transformValue('')).toBe('');
|
|
50
|
+
});
|
|
51
|
+
test('transforms blueprint ref', () => {
|
|
52
|
+
const transformer = new lib_1.MdaaConfigBlueprintRefValueTransformer({
|
|
53
|
+
naming: mockNaming,
|
|
54
|
+
scope: stack,
|
|
55
|
+
});
|
|
56
|
+
const result = transformer.transformValue('{{blueprint:/some/path}}');
|
|
57
|
+
expect(result).toContain('{{resolve:ssm:/test-org/');
|
|
58
|
+
expect(result).toContain('/some/path}}');
|
|
59
|
+
expect(mockNaming.ssmOrgPath).toHaveBeenCalledWith('', false);
|
|
60
|
+
});
|
|
61
|
+
test('transforms blueprint ref with prefix and suffix', () => {
|
|
62
|
+
const transformer = new lib_1.MdaaConfigBlueprintRefValueTransformer({
|
|
63
|
+
naming: mockNaming,
|
|
64
|
+
scope: stack,
|
|
65
|
+
});
|
|
66
|
+
const result = transformer.transformValue('prefix-{{blueprint:/path}}-suffix');
|
|
67
|
+
expect(result).toContain('prefix-');
|
|
68
|
+
expect(result).toContain('-suffix');
|
|
69
|
+
expect(result).toContain('{{resolve:ssm:');
|
|
70
|
+
});
|
|
71
|
+
test('handles multiple blueprint refs', () => {
|
|
72
|
+
const transformer = new lib_1.MdaaConfigBlueprintRefValueTransformer({
|
|
73
|
+
naming: mockNaming,
|
|
74
|
+
scope: stack,
|
|
75
|
+
});
|
|
76
|
+
const result = transformer.transformValue('{{blueprint:/path1}}-{{blueprint:/path2}}');
|
|
77
|
+
expect(result).toContain('/path1}}');
|
|
78
|
+
expect(result).toContain('/path2}}');
|
|
79
|
+
});
|
|
80
|
+
test('reuses existing CfnParameter for datazoneEnvironmentProjectId', () => {
|
|
81
|
+
const transformer = new lib_1.MdaaConfigBlueprintRefValueTransformer({
|
|
82
|
+
naming: mockNaming,
|
|
83
|
+
scope: stack,
|
|
84
|
+
});
|
|
85
|
+
// First call creates the parameter
|
|
86
|
+
transformer.transformValue('{{blueprint:/path1}}');
|
|
87
|
+
// Second call should reuse the same parameter
|
|
88
|
+
transformer.transformValue('{{blueprint:/path2}}');
|
|
89
|
+
// Should only have one datazoneEnvironmentProjectId parameter
|
|
90
|
+
const params = stack.node.children.filter(child => child.node.id === 'datazoneEnvironmentProjectId');
|
|
91
|
+
expect(params.length).toBe(1);
|
|
92
|
+
});
|
|
93
|
+
test('ignores non-blueprint refs', () => {
|
|
94
|
+
const transformer = new lib_1.MdaaConfigBlueprintRefValueTransformer({
|
|
95
|
+
naming: mockNaming,
|
|
96
|
+
scope: stack,
|
|
97
|
+
});
|
|
98
|
+
const result = transformer.transformValue('{{other:value}}');
|
|
99
|
+
// Non-blueprint refs should remain unchanged
|
|
100
|
+
expect(result).toBe('{{other:value}}');
|
|
101
|
+
});
|
|
102
|
+
test('handles nested refs in blueprint path', () => {
|
|
103
|
+
const transformer = new lib_1.MdaaConfigBlueprintRefValueTransformer({
|
|
104
|
+
naming: mockNaming,
|
|
105
|
+
scope: stack,
|
|
106
|
+
});
|
|
107
|
+
// Nested refs should be processed recursively
|
|
108
|
+
const result = transformer.transformValue('{{blueprint:/{{other}}/path}}');
|
|
109
|
+
// The inner ref is not a blueprint ref, so it stays as-is in the path
|
|
110
|
+
expect(result).toContain('{{resolve:ssm:');
|
|
111
|
+
});
|
|
112
|
+
test('handles unbalanced braces gracefully', () => {
|
|
113
|
+
const transformer = new lib_1.MdaaConfigBlueprintRefValueTransformer({
|
|
114
|
+
naming: mockNaming,
|
|
115
|
+
scope: stack,
|
|
116
|
+
});
|
|
117
|
+
// Unbalanced braces should be skipped
|
|
118
|
+
expect(transformer.transformValue('{{blueprint:/path')).toBe('{{blueprint:/path');
|
|
119
|
+
expect(transformer.transformValue('blueprint:/path}}')).toBe('blueprint:/path}}');
|
|
120
|
+
});
|
|
121
|
+
test('logs resolved blueprint path', () => {
|
|
122
|
+
const consoleSpy = jest.spyOn(console, 'log').mockImplementation();
|
|
123
|
+
const transformer = new lib_1.MdaaConfigBlueprintRefValueTransformer({
|
|
124
|
+
naming: mockNaming,
|
|
125
|
+
scope: stack,
|
|
126
|
+
});
|
|
127
|
+
transformer.transformValue('{{blueprint:/test/path}}');
|
|
128
|
+
expect(consoleSpy).toHaveBeenCalledWith(expect.stringContaining('Resolving blueprint path:'));
|
|
129
|
+
consoleSpy.mockRestore();
|
|
130
|
+
});
|
|
131
|
+
test('handles blueprint ref with special characters in path', () => {
|
|
132
|
+
const transformer = new lib_1.MdaaConfigBlueprintRefValueTransformer({
|
|
133
|
+
naming: mockNaming,
|
|
134
|
+
scope: stack,
|
|
135
|
+
});
|
|
136
|
+
const result = transformer.transformValue('{{blueprint:/path/with-dashes_and_underscores/123}}');
|
|
137
|
+
expect(result).toContain('{{resolve:ssm:');
|
|
138
|
+
expect(result).toContain('/path/with-dashes_and_underscores/123}}');
|
|
139
|
+
});
|
|
140
|
+
test('handles blueprint ref at start of string', () => {
|
|
141
|
+
const transformer = new lib_1.MdaaConfigBlueprintRefValueTransformer({
|
|
142
|
+
naming: mockNaming,
|
|
143
|
+
scope: stack,
|
|
144
|
+
});
|
|
145
|
+
const result = transformer.transformValue('{{blueprint:/path}}-suffix');
|
|
146
|
+
expect(result).toMatch(/^\{\{resolve:ssm:/);
|
|
147
|
+
expect(result).toContain('-suffix');
|
|
148
|
+
});
|
|
149
|
+
test('handles blueprint ref at end of string', () => {
|
|
150
|
+
const transformer = new lib_1.MdaaConfigBlueprintRefValueTransformer({
|
|
151
|
+
naming: mockNaming,
|
|
152
|
+
scope: stack,
|
|
153
|
+
});
|
|
154
|
+
const result = transformer.transformValue('prefix-{{blueprint:/path}}');
|
|
155
|
+
expect(result).toContain('prefix-');
|
|
156
|
+
expect(result).toMatch(/\}\}$/);
|
|
157
|
+
});
|
|
158
|
+
test('handles multiple different ref types mixed', () => {
|
|
159
|
+
const transformer = new lib_1.MdaaConfigBlueprintRefValueTransformer({
|
|
160
|
+
naming: mockNaming,
|
|
161
|
+
scope: stack,
|
|
162
|
+
});
|
|
163
|
+
const result = transformer.transformValue('{{blueprint:/path1}}-{{other:ref}}-{{blueprint:/path2}}');
|
|
164
|
+
expect(result).toContain('/path1}}');
|
|
165
|
+
expect(result).toContain('{{other:ref}}');
|
|
166
|
+
expect(result).toContain('/path2}}');
|
|
167
|
+
});
|
|
168
|
+
test('handles empty blueprint path', () => {
|
|
169
|
+
const transformer = new lib_1.MdaaConfigBlueprintRefValueTransformer({
|
|
170
|
+
naming: mockNaming,
|
|
171
|
+
scope: stack,
|
|
172
|
+
});
|
|
173
|
+
const result = transformer.transformValue('{{blueprint:}}');
|
|
174
|
+
expect(result).toContain('{{resolve:ssm:');
|
|
175
|
+
});
|
|
176
|
+
test('handles blueprint ref with only slashes', () => {
|
|
177
|
+
const transformer = new lib_1.MdaaConfigBlueprintRefValueTransformer({
|
|
178
|
+
naming: mockNaming,
|
|
179
|
+
scope: stack,
|
|
180
|
+
});
|
|
181
|
+
const result = transformer.transformValue('{{blueprint:/}}');
|
|
182
|
+
expect(result).toContain('{{resolve:ssm:');
|
|
183
|
+
expect(result).toContain('/}}');
|
|
184
|
+
});
|
|
185
|
+
test('creates parameter on first blueprint ref', () => {
|
|
186
|
+
const transformer = new lib_1.MdaaConfigBlueprintRefValueTransformer({
|
|
187
|
+
naming: mockNaming,
|
|
188
|
+
scope: stack,
|
|
189
|
+
});
|
|
190
|
+
const childrenBefore = stack.node.children.length;
|
|
191
|
+
transformer.transformValue('{{blueprint:/first}}');
|
|
192
|
+
const childrenAfter = stack.node.children.length;
|
|
193
|
+
expect(childrenAfter).toBeGreaterThan(childrenBefore);
|
|
194
|
+
});
|
|
195
|
+
test('does not create duplicate parameters', () => {
|
|
196
|
+
const transformer = new lib_1.MdaaConfigBlueprintRefValueTransformer({
|
|
197
|
+
naming: mockNaming,
|
|
198
|
+
scope: stack,
|
|
199
|
+
});
|
|
200
|
+
transformer.transformValue('{{blueprint:/first}}');
|
|
201
|
+
const childrenAfterFirst = stack.node.children.length;
|
|
202
|
+
transformer.transformValue('{{blueprint:/second}}');
|
|
203
|
+
const childrenAfterSecond = stack.node.children.length;
|
|
204
|
+
// Should not create additional parameters
|
|
205
|
+
expect(childrenAfterSecond).toBe(childrenAfterFirst);
|
|
206
|
+
});
|
|
207
|
+
test('throws when blueprint ref without scope', () => {
|
|
208
|
+
const transformer = new lib_1.MdaaConfigBlueprintRefValueTransformer({
|
|
209
|
+
naming: mockNaming,
|
|
210
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
211
|
+
scope: undefined,
|
|
212
|
+
});
|
|
213
|
+
expect(() => transformer.transformValue('{{blueprint:/path}}')).toThrow('Unable to resolve blueprint params outside of a Construct');
|
|
214
|
+
});
|
|
215
|
+
test('throws when blueprint ref without naming', () => {
|
|
216
|
+
const transformer = new lib_1.MdaaConfigBlueprintRefValueTransformer({
|
|
217
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
218
|
+
naming: undefined,
|
|
219
|
+
scope: stack,
|
|
220
|
+
});
|
|
221
|
+
expect(() => transformer.transformValue('{{blueprint:/path}}')).toThrow('Unable to resolve blueprint params without a naming implementation');
|
|
222
|
+
});
|
|
223
|
+
});
|
|
224
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYmx1ZXByaW50LXZhbHVlLXRyYW5zZm9ybWVyLnRlc3QuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJibHVlcHJpbnQtdmFsdWUtdHJhbnNmb3JtZXIudGVzdC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiO0FBQUE7OztHQUdHOztBQUVILDZDQUFvQztBQUNwQyxnQ0FBZ0U7QUFHaEUsUUFBUSxDQUFDLHdDQUF3QyxFQUFFLEdBQUcsRUFBRTtJQUN0RCxJQUFJLFVBQStCLENBQUM7SUFDcEMsSUFBSSxLQUFZLENBQUM7SUFFakIsVUFBVSxDQUFDLEdBQUcsRUFBRTtRQUNkLEtBQUssR0FBRyxJQUFJLG1CQUFLLEVBQUUsQ0FBQztRQUNwQixVQUFVLEdBQUc7WUFDWCxLQUFLLEVBQUU7Z0JBQ0wsR0FBRyxFQUFFLFVBQVU7Z0JBQ2YsR0FBRyxFQUFFLFVBQVU7Z0JBQ2YsTUFBTSxFQUFFLGFBQWE7Z0JBQ3JCLFVBQVUsRUFBRSxhQUFhO2FBQzFCO1lBQ0QsY0FBYyxFQUFFLElBQUksQ0FBQyxFQUFFLEVBQUUsQ0FBQyxjQUFjLEVBQUU7WUFDMUMsVUFBVSxFQUFFLElBQUksQ0FBQyxFQUFFLEVBQUUsQ0FBQyxjQUFjLEVBQUU7WUFDdEMsT0FBTyxFQUFFLElBQUksQ0FBQyxFQUFFLEVBQUUsQ0FBQyxjQUFjLEVBQUU7WUFDbkMsT0FBTyxFQUFFLElBQUksQ0FBQyxFQUFFLEVBQUUsQ0FBQyxjQUFjLEVBQUU7WUFDbkMsVUFBVSxFQUFFLElBQUksQ0FBQyxFQUFFLEVBQUUsQ0FBQyxjQUFjLEVBQUU7WUFDdEMsYUFBYSxFQUFFLElBQUksQ0FBQyxFQUFFLEVBQUUsQ0FBQyxlQUFlLENBQUMsbUJBQW1CLENBQUM7WUFDN0QsVUFBVSxFQUFFLElBQUksQ0FBQyxFQUFFLEVBQUUsQ0FBQyxlQUFlLENBQUMsZ0JBQWdCLENBQUM7WUFDdkQsVUFBVSxFQUFFLElBQUksQ0FBQyxFQUFFLEVBQUUsQ0FBQyxlQUFlLENBQUMsWUFBWSxDQUFDO1lBQ25ELFlB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KiBDb3B5cmlnaHQgQW1hem9uLmNvbSwgSW5jLiBvciBpdHMgYWZmaWxpYXRlcy4gQWxsIFJpZ2h0cyBSZXNlcnZlZC5cbiAqIFNQRFgtTGljZW5zZS1JZGVudGlmaWVyOiBBcGFjaGUtMi4wXG4gKi9cblxuaW1wb3J0IHsgU3RhY2sgfSBmcm9tICdhd3MtY2RrLWxpYic7XG5pbXBvcnQgeyBNZGFhQ29uZmlnQmx1ZXByaW50UmVmVmFsdWVUcmFuc2Zvcm1lciB9IGZyb20gJy4uL2xpYic7XG5pbXBvcnQgeyBJTWRhYVJlc291cmNlTmFtaW5nIH0gZnJvbSAnQGF3cy1tZGFhL25hbWluZyc7XG5cbmRlc2NyaWJlKCdNZGFhQ29uZmlnQmx1ZXByaW50UmVmVmFsdWVUcmFuc2Zvcm1lcicsICgpID0+IHtcbiAgbGV0IG1vY2tOYW1pbmc6IElNZGFhUmVzb3VyY2VOYW1pbmc7XG4gIGxldCBzdGFjazogU3RhY2s7XG5cbiAgYmVmb3JlRWFjaCgoKSA9PiB7XG4gICAgc3RhY2sgPSBuZXcgU3RhY2soKTtcbiAgICBtb2NrTmFtaW5nID0ge1xuICAgICAgcHJvcHM6IHtcbiAgICAgICAgb3JnOiAndGVzdC1vcmcnLFxuICAgICAgICBlbnY6ICd0ZXN0LWVudicsXG4gICAgICAgIGRvbWFpbjogJ3Rlc3QtZG9tYWluJyxcbiAgICAgICAgbW9kdWxlTmFtZTogJ3Rlc3QtbW9kdWxlJyxcbiAgICAgIH0sXG4gICAgICB3aXRoTW9kdWxlTmFtZTogamVzdC5mbigpLm1vY2tSZXR1cm5UaGlzKCksXG4gICAgICB3aXRoRG9tYWluOiBqZXN0LmZuKCkubW9ja1JldHVyblRoaXMoKSxcbiAgICAgIHdpdGhPcmc6IGplc3QuZm4oKS5tb2NrUmV0dXJuVGhpcygpLFxuICAgICAgd2l0aEVudjogamVzdC5mbigpLm1vY2tSZXR1cm5UaGlzKCksXG4gICAgICB3aXRoU3VmZml4OiBqZXN0LmZuKCkubW9ja1JldHVyblRoaXMoKSxcbiAgICAgIHNzbURvbWFpblBhdGg6IGplc3QuZm4oKS5tb2NrUmV0dXJuVmFsdWUoJy90ZXN0L2RvbWFpbi9wYXRoJyksXG4gICAgICBzc21FbnZQYXRoOiBqZXN0LmZuKCkubW9ja1JldHVyblZhbHVlKCcvdGVzdC9lbnYvcGF0aCcpLFxuICAgICAgc3NtT3JnUGF0aDogamVzdC5mbigpLm1vY2tSZXR1cm5WYWx1ZSgnL3Rlc3Qtb3JnLycpLFxuICAgICAgcmVzb3VyY2VOYW1lOiBqZXN0XG4gICAgICAgIC5mbigpXG4gICAgICAgIC5tb2NrSW1wbGVtZW50YXRpb24oKHN1ZmZpeD86IHN0cmluZykgPT4gKHN1ZmZpeCA/IGB0ZXN0LXJlc291cmNlLSR7c3VmZml4fWAgOiAndGVzdC1yZXNvdXJjZScpKSxcbiAgICAgIHNzbVBhdGg6IGplc3QuZm4oKS5tb2NrSW1wbGVtZW50YXRpb24oKHBhdGg6IHN0cmluZykgPT4gYC90ZXN0LyR7cGF0aH1gKSxcbiAgICAgIHN0YWNrTmFtZTogamVzdC5mbigpLm1vY2tJbXBsZW1lbnRhdGlvbigobmFtZT86IHN0cmluZykgPT4gKG5hbWUgPyBgdGVzdC1zdGFjay0ke25hbWV9YCA6ICd0ZXN0LXN0YWNrJykpLFxuICAgICAgZXhwb3J0TmFtZTogamVzdC5mbigpLm1vY2tJbXBsZW1lbnRhdGlvbigocGF0aDogc3RyaW5nKSA9PiBgdGVzdC1leHBvcnQtJHtwYXRofWApLFxuICAgIH0gYXMgdW5rbm93biBhcyBJTWRhYVJlc291cmNlTmFtaW5nO1xuICB9KTtcblxuICB0ZXN0KCdyZXR1cm5zIHVuY2hhbmdlZCB2YWx1ZSB3aXRob3V0IHJlZnMnLCAoKSA9PiB7XG4gICAgY29uc3QgdHJhbnNmb3JtZXIgPSBuZXcgTWRhYUNvbmZpZ0JsdWVwcmludFJlZlZhbHVlVHJhbnNmb3JtZXIoe1xuICAgICAgbmFtaW5nOiBtb2NrTmFtaW5nLFxuICAgICAgc2NvcGU6IHN0YWNrLFxuICAgIH0pO1xuICAgIGV4cGVjdCh0cmFuc2Zvcm1lci50cmFuc2Zvcm1WYWx1ZSgncGxhaW4tdmFsdWUnKSkudG9CZSgncGxhaW4tdmFsdWUnKTtcbiAgfSk7XG5cbiAgdGVzdCgncmV0dXJucyB1bmNoYW5nZWQgdmFsdWUgd2l0aCBlbXB0eSBzdHJpbmcnLCAoKSA9PiB7XG4gICAgY29uc3QgdHJhbnNmb3JtZXIgPSBuZXcgTWRhYUNvbmZpZ0JsdWVwcmludFJlZlZhbHVlVHJhbnNmb3JtZXIoe1xuICAgICAgbmFtaW5nOiBtb2NrTmFtaW5nLFxuICAgICAgc2NvcGU6IHN0YWNrLFxuICAgIH0pO1xuICAgIGV4cGVjdCh0cmFuc2Zvcm1lci50cmFuc2Zvcm1WYWx1ZSgnJykpLnRvQmUoJycpO1xuICB9KTtcblxuICB0ZXN0KCd0cmFuc2Zvcm1zIGJsdWVwcmludCByZWYnLCAoKSA9PiB7XG4gICAgY29uc3QgdHJhbnNmb3JtZXIgPSBuZXcgTWRhYUNvbmZpZ0JsdWVwcmludFJlZlZhbHVlVHJhbnNmb3JtZXIoe1xuICAgICAgbmFtaW5nOiBtb2NrTmFtaW5nLFxuICAgICAgc2NvcGU6IHN0YWNrLFxuICAgIH0pO1xuICAgIGNvbnN0IHJlc3VsdCA9IHRyYW5zZm9ybWVyLnRyYW5zZm9ybVZhbHVlKCd7e2JsdWVwcmludDovc29tZS9wYXRofX0nKTtcbiAgICBleHBlY3QocmVzdWx0KS50b0NvbnRhaW4oJ3t7cmVzb2x2ZTpzc206L3Rlc3Qtb3JnLycpO1xuICAgIGV4cGVjdChyZXN1bHQpLnRvQ29udGFpbignL3NvbWUvcGF0aH19Jyk7XG4gICAgZXhwZWN0KG1vY2tOYW1pbmcuc3NtT3JnUGF0aCkudG9IYXZlQmVlbkNhbGxlZFdpdGgoJycsIGZhbHNlKTtcbiAgfSk7XG5cbiAgdGVzdCgndHJhbnNmb3JtcyBibHVlcHJpbnQgcmVmIHdpdGggcHJlZml4IGFuZCBzdWZmaXgnLCAoKSA9PiB7XG4gICAgY29uc3QgdHJhbnNmb3JtZXIgPSBuZXcgTWRhYUNvbmZpZ0JsdWVwcmludFJlZlZhbHVlVHJhbnNmb3JtZXIoe1xuICAgICAgbmFtaW5nOiBtb2NrTmFtaW5nLFxuICAgICAgc2NvcGU6IHN0YWNrLFxuICAgIH0pO1xuICAgIGNvbnN0IHJlc3VsdCA9IHRyYW5zZm9ybWVyLnRyYW5zZm9ybVZhbHVlKCdwcmVmaXgte3tibHVlcHJpbnQ6L3BhdGh9fS1zdWZmaXgnKTtcbiAgICBleHBlY3QocmVzdWx0KS50b0NvbnRhaW4oJ3ByZWZpeC0nKTtcbiAgICBleHBlY3QocmVzdWx0KS50b0NvbnRhaW4oJy1zdWZmaXgnKTtcbiAgICBleHBlY3QocmVzdWx0KS50b0NvbnRhaW4oJ3t7cmVzb2x2ZTpzc206Jyk7XG4gIH0pO1xuXG4gIHRlc3QoJ2hhbmRsZXMgbXVsdGlwbGUgYmx1ZXByaW50IHJlZnMnLCAoKSA9PiB7XG4gICAgY29uc3QgdHJhbnNmb3JtZXIgPSBuZXcgTWRhYUNvbmZpZ0JsdWVwcmludFJlZlZhbHVlVHJhbnNmb3JtZXIoe1xuICAgICAgbmFtaW5nOiBtb2NrTmFtaW5nLFxuICAgICAgc2NvcGU6IHN0YWNrLFxuICAgIH0pO1xuICAgIGNvbnN0IHJlc3VsdCA9IHRyYW5zZm9ybWVyLnRyYW5zZm9ybVZhbHVlKCd7e2JsdWVwcmludDovcGF0aDF9fS17e2JsdWVwcmludDovcGF0aDJ9fScpO1xuICAgIGV4cGVjdChyZXN1bHQpLnRvQ29udGFpbignL3BhdGgxfX0nKTtcbiAgICBleHBlY3QocmVzdWx0KS50b0NvbnRhaW4oJy9wYXRoMn19Jyk7XG4gIH0pO1xuXG4gIHRlc3QoJ3JldXNlcyBleGlzdGluZyBDZm5QYXJhbWV0ZXIgZm9yIGRhdGF6b25lRW52aXJvbm1lbnRQcm9qZWN0SWQnLCAoKSA9PiB7XG4gICAgY29uc3QgdHJhbnNmb3JtZXIgPSBuZXcgTWRhYUNvbmZpZ0JsdWVwcmludFJlZlZhbHVlVHJhbnNmb3JtZXIoe1xuICAgICAgbmFtaW5nOiBtb2NrTmFtaW5nLFxuICAgICAgc2NvcGU6IHN0YWNrLFxuICAgIH0pO1xuXG4gICAgLy8gRmlyc3QgY2FsbCBjcmVhdGVzIHRoZSBwYXJhbWV0ZXJcbiAgICB0cmFuc2Zvcm1lci50cmFuc2Zvcm1WYWx1ZSgne3tibHVlcHJpbnQ6L3BhdGgxfX0nKTtcblxuICAgIC8vIFNlY29uZCBjYWxsIHNob3VsZCByZXVzZSB0aGUgc2FtZSBwYXJhbWV0ZXJcbiAgICB0cmFuc2Zvcm1lci50cmFuc2Zvcm1WYWx1ZSgne3tibHVlcHJpbnQ6L3BhdGgyfX0nKTtcblxuICAgIC8vIFNob3VsZCBvbmx5IGhhdmUgb25lIGRhdGF6b25lRW52aXJvbm1lbnRQcm9qZWN0SWQgcGFyYW1ldGVyXG4gICAgY29uc3QgcGFyYW1zID0gc3RhY2subm9kZS5jaGlsZHJlbi5maWx0ZXIoY2hpbGQgPT4gY2hpbGQubm9kZS5pZCA9PT0gJ2RhdGF6b25lRW52aXJvbm1lbnRQcm9qZWN0SWQnKTtcbiAgICBleHBlY3QocGFyYW1zLmxlbmd0aCkudG9CZSgxKTtcbiAgfSk7XG5cbiAgdGVzdCgnaWdub3JlcyBub24tYmx1ZXByaW50IHJlZnMnLCAoKSA9PiB7XG4gICAgY29uc3QgdHJhbnNmb3JtZXIgPSBuZXcgTWRhYUNvbmZpZ0JsdWVwcmludFJlZlZhbHVlVHJhbnNmb3JtZXIoe1xuICAgICAgbmFtaW5nOiBtb2NrTmFtaW5nLFxuICAgICAgc2NvcGU6IHN0YWNrLFxuICAgIH0pO1xuICAgIGNvbnN0IHJlc3VsdCA9IHRyYW5zZm9ybWVyLnRyYW5zZm9ybVZhbHVlKCd7e290aGVyOnZhbHVlfX0nKTtcbiAgICAvLyBOb24tYmx1ZXByaW50IHJlZnMgc2hvdWxkIHJlbWFpbiB1bmNoYW5nZWRcbiAgICBleHBlY3QocmVzdWx0KS50b0JlKCd7e290aGVyOnZhbHVlfX0nKTtcbiAgfSk7XG5cbiAgdGVzdCgnaGFuZGxlcyBuZXN0ZWQgcmVmcyBpbiBibHVlcHJpbnQgcGF0aCcsICgpID0+IHtcbiAgICBjb25zdCB0cmFuc2Zvcm1lciA9IG5ldyBNZGFhQ29uZmlnQmx1ZXByaW50UmVmVmFsdWVUcmFuc2Zvcm1lcih7XG4gICAgICBuYW1pbmc6IG1vY2tOYW1pbmcsXG4gICAgICBzY29wZTogc3RhY2ssXG4gICAgfSk7XG4gICAgLy8gTmVzdGVkIHJlZnMgc2hvdWxkIGJlIHByb2Nlc3NlZCByZWN1cnNpdmVseVxuICAgIGNvbnN0IHJlc3VsdCA9IHRyYW5zZm9ybWVyLnRyYW5zZm9ybVZhbHVlKCd7e2JsdWVwcmludDove3tvdGhlcn19L3BhdGh9fScpO1xuICAgIC8vIFRoZSBpbm5lciByZWYgaXMgbm90IGEgYmx1ZXByaW50IHJlZiwgc28gaXQgc3RheXMgYXMtaXMgaW4gdGhlIHBhdGhcbiAgICBleHBlY3QocmVzdWx0KS50b0NvbnRhaW4oJ3t7cmVzb2x2ZTpzc206Jyk7XG4gIH0pO1xuXG4gIHRlc3QoJ2hhbmRsZXMgdW5iYWxhbmNlZCBicmFjZXMgZ3JhY2VmdWxseScsICgpID0+IHtcbiAgICBjb25zdCB0cmFuc2Zvcm1lciA9IG5ldyBNZGFhQ29uZmlnQmx1ZXByaW50UmVmVmFsdWVUcmFuc2Zvcm1lcih7XG4gICAgICBuYW1pbmc6IG1vY2tOYW1pbmcsXG4gICAgICBzY29wZTogc3RhY2ssXG4gICAgfSk7XG4gICAgLy8gVW5iYWxhbmNlZCBicmFjZXMgc2hvdWxkIGJlIHNraXBwZWRcbiAgICBleHBlY3QodHJhbnNmb3JtZXIudHJhbnNmb3JtVmFsdWUoJ3t7Ymx1ZXByaW50Oi9wYXRoJykpLnRvQmUoJ3t7Ymx1ZXByaW50Oi9wYXRoJyk7XG4gICAgZXhwZWN0KHRyYW5zZm9ybWVyLnRyYW5zZm9ybVZhbHVlKCdibHVlcHJpbnQ6L3BhdGh9fScpKS50b0JlKCdibHVlcHJpbnQ6L3BhdGh9fScpO1xuICB9KTtcblxuICB0ZXN0KCdsb2dzIHJlc29sdmVkIGJsdWVwcmludCBwYXRoJywgKCkgPT4ge1xuICAgIGNvbnN0IGNvbnNvbGVTcHkgPSBqZXN0LnNweU9uKGNvbnNvbGUsICdsb2cnKS5tb2NrSW1wbGVtZW50YXRpb24oKTtcbiAgICBjb25zdCB0cmFuc2Zvcm1lciA9IG5ldyBNZGFhQ29uZmlnQmx1ZXByaW50UmVmVmFsdWVUcmFuc2Zvcm1lcih7XG4gICAgICBuYW1pbmc6IG1vY2tOYW1pbmcsXG4gICAgICBzY29wZTogc3RhY2ssXG4gICAgfSk7XG5cbiAgICB0cmFuc2Zvcm1lci50cmFuc2Zvcm1WYWx1ZSgne3tibHVlcHJpbnQ6L3Rlc3QvcGF0aH19Jyk7XG5cbiAgICBleHBlY3QoY29uc29sZVNweSkudG9IYXZlQmVlbkNhbGxlZFdpdGgoZXhwZWN0LnN0cmluZ0NvbnRhaW5pbmcoJ1Jlc29sdmluZyBibHVlcHJpbnQgcGF0aDonKSk7XG4gICAgY29uc29sZVNweS5tb2NrUmVzdG9yZSgpO1xuICB9KTtcblxuICB0ZXN0KCdoYW5kbGVzIGJsdWVwcmludCByZWYgd2l0aCBzcGVjaWFsIGNoYXJhY3RlcnMgaW4gcGF0aCcsICgpID0+IHtcbiAgICBjb25zdCB0cmFuc2Zvcm1lciA9IG5ldyBNZGFhQ29uZmlnQmx1ZXByaW50UmVmVmFsdWVUcmFuc2Zvcm1lcih7XG4gICAgICBuYW1pbmc6IG1vY2tOYW1pbmcsXG4gICAgICBzY29wZTogc3RhY2ssXG4gICAgfSk7XG4gICAgY29uc3QgcmVzdWx0ID0gdHJhbnNmb3JtZXIudHJhbnNmb3JtVmFsdWUoJ3t7Ymx1ZXByaW50Oi9wYXRoL3dpdGgtZGFzaGVzX2FuZF91bmRlcnNjb3Jlcy8xMjN9fScpO1xuICAgIGV4cGVjdChyZXN1bHQpLnRvQ29udGFpbigne3tyZXNvbHZlOnNzbTonKTtcbiAgICBleHBlY3QocmVzdWx0KS50b0NvbnRhaW4oJy9wYXRoL3dpdGgtZGFzaGVzX2FuZF91bmRlcnNjb3Jlcy8xMjN9fScpO1xuICB9KTtcblxuICB0ZXN0KCdoYW5kbGVzIGJsdWVwcmludCByZWYgYXQgc3RhcnQgb2Ygc3RyaW5nJywgKCkgPT4ge1xuICAgIGNvbnN0IHRyYW5zZm9ybWVyID0gbmV3IE1kYWFDb25maWdCbHVlcHJpbnRSZWZWYWx1ZVRyYW5zZm9ybWVyKHtcbiAgICAgIG5hbWluZzogbW9ja05hbWluZyxcbiAgICAgIHNjb3BlOiBzdGFjayxcbiAgICB9KTtcbiAgICBjb25zdCByZXN1bHQgPSB0cmFuc2Zvcm1lci50cmFuc2Zvcm1WYWx1ZSgne3tibHVlcHJpbnQ6L3BhdGh9fS1zdWZmaXgnKTtcbiAgICBleHBlY3QocmVzdWx0KS50b01hdGNoKC9eXFx7XFx7cmVzb2x2ZTpzc206Lyk7XG4gICAgZXhwZWN0KHJlc3VsdCkudG9Db250YWluKCctc3VmZml4Jyk7XG4gIH0pO1xuXG4gIHRlc3QoJ2hhbmRsZXMgYmx1ZXByaW50IHJlZiBhdCBlbmQgb2Ygc3RyaW5nJywgKCkgPT4ge1xuICAgIGNvbnN0IHRyYW5zZm9ybWVyID0gbmV3IE1kYWFDb25maWdCbHVlcHJpbnRSZWZWYWx1ZVRyYW5zZm9ybWVyKHtcbiAgICAgIG5hbWluZzogbW9ja05hbWluZyxcbiAgICAgIHNjb3BlOiBzdGFjayxcbiAgICB9KTtcbiAgICBjb25zdCByZXN1bHQgPSB0cmFuc2Zvcm1lci50cmFuc2Zvcm1WYWx1ZSgncHJlZml4LXt7Ymx1ZXByaW50Oi9wYXRofX0nKTtcbiAgICBleHBlY3QocmVzdWx0KS50b0NvbnRhaW4oJ3ByZWZpeC0nKTtcbiAgICBleHBlY3QocmVzdWx0KS50b01hdGNoKC9cXH1cXH0kLyk7XG4gIH0pO1xuXG4gIHRlc3QoJ2hhbmRsZXMgbXVsdGlwbGUgZGlmZmVyZW50IHJlZiB0eXBlcyBtaXhlZCcsICgpID0+IHtcbiAgICBjb25zdCB0cmFuc2Zvcm1lciA9IG5ldyBNZGFhQ29uZmlnQmx1ZXByaW50UmVmVmFsdWVUcmFuc2Zvcm1lcih7XG4gICAgICBuYW1pbmc6IG1vY2tOYW1pbmcsXG4gICAgICBzY29wZTogc3RhY2ssXG4gICAgfSk7XG4gICAgY29uc3QgcmVzdWx0ID0gdHJhbnNmb3JtZXIudHJhbnNmb3JtVmFsdWUoJ3t7Ymx1ZXByaW50Oi9wYXRoMX19LXt7b3RoZXI6cmVmfX0te3tibHVlcHJpbnQ6L3BhdGgyfX0nKTtcbiAgICBleHBlY3QocmVzdWx0KS50b0NvbnRhaW4oJy9wYXRoMX19Jyk7XG4gICAgZXhwZWN0KHJlc3VsdCkudG9Db250YWluKCd7e290aGVyOnJlZn19Jyk7XG4gICAgZXhwZWN0KHJlc3VsdCkudG9Db250YWluKCcvcGF0aDJ9fScpO1xuICB9KTtcblxuICB0ZXN0KCdoYW5kbGVzIGVtcHR5IGJsdWVwcmludCBwYXRoJywgKCkgPT4ge1xuICAgIGNvbnN0IHRyYW5zZm9ybWVyID0gbmV3IE1kYWFDb25maWdCbHVlcHJpbnRSZWZWYWx1ZVRyYW5zZm9ybWVyKHtcbiAgICAgIG5hbWluZzogbW9ja05hbWluZyxcbiAgICAgIHNjb3BlOiBzdGFjayxcbiAgICB9KTtcbiAgICBjb25zdCByZXN1bHQgPSB0cmFuc2Zvcm1lci50cmFuc2Zvcm1WYWx1ZSgne3tibHVlcHJpbnQ6fX0nKTtcbiAgICBleHBlY3QocmVzdWx0KS50b0NvbnRhaW4oJ3t7cmVzb2x2ZTpzc206Jyk7XG4gIH0pO1xuXG4gIHRlc3QoJ2hhbmRsZXMgYmx1ZXByaW50IHJlZiB3aXRoIG9ubHkgc2xhc2hlcycsICgpID0+IHtcbiAgICBjb25zdCB0cmFuc2Zvcm1lciA9IG5ldyBNZGFhQ29uZmlnQmx1ZXByaW50UmVmVmFsdWVUcmFuc2Zvcm1lcih7XG4gICAgICBuYW1pbmc6IG1vY2tOYW1pbmcsXG4gICAgICBzY29wZTogc3RhY2ssXG4gICAgfSk7XG4gICAgY29uc3QgcmVzdWx0ID0gdHJhbnNmb3JtZXIudHJhbnNmb3JtVmFsdWUoJ3t7Ymx1ZXByaW50Oi99fScpO1xuICAgIGV4cGVjdChyZXN1bHQpLnRvQ29udGFpbigne3tyZXNvbHZlOnNzbTonKTtcbiAgICBleHBlY3QocmVzdWx0KS50b0NvbnRhaW4oJy99fScpO1xuICB9KTtcblxuICB0ZXN0KCdjcmVhdGVzIHBhcmFtZXRlciBvbiBmaXJzdCBibHVlcHJpbnQgcmVmJywgKCkgPT4ge1xuICAgIGNvbnN0IHRyYW5zZm9ybWVyID0gbmV3IE1kYWFDb25maWdCbHVlcHJpbnRSZWZWYWx1ZVRyYW5zZm9ybWVyKHtcbiAgICAgIG5hbWluZzogbW9ja05hbWluZyxcbiAgICAgIHNjb3BlOiBzdGFjayxcbiAgICB9KTtcblxuICAgIGNvbnN0IGNoaWxkcmVuQmVmb3JlID0gc3RhY2subm9kZS5jaGlsZHJlbi5sZW5ndGg7XG4gICAgdHJhbnNmb3JtZXIudHJhbnNmb3JtVmFsdWUoJ3t7Ymx1ZXByaW50Oi9maXJzdH19Jyk7XG4gICAgY29uc3QgY2hpbGRyZW5BZnRlciA9IHN0YWNrLm5vZGUuY2hpbGRyZW4ubGVuZ3RoO1xuXG4gICAgZXhwZWN0KGNoaWxkcmVuQWZ0ZXIpLnRvQmVHcmVhdGVyVGhhbihjaGlsZHJlbkJlZm9yZSk7XG4gIH0pO1xuXG4gIHRlc3QoJ2RvZXMgbm90IGNyZWF0ZSBkdXBsaWNhdGUgcGFyYW1ldGVycycsICgpID0+IHtcbiAgICBjb25zdCB0cmFuc2Zvcm1lciA9IG5ldyBNZGFhQ29uZmlnQmx1ZXByaW50UmVmVmFsdWVUcmFuc2Zvcm1lcih7XG4gICAgICBuYW1pbmc6IG1vY2tOYW1pbmcsXG4gICAgICBzY29wZTogc3RhY2ssXG4gICAgfSk7XG5cbiAgICB0cmFuc2Zvcm1lci50cmFuc2Zvcm1WYWx1ZSgne3tibHVlcHJpbnQ6L2ZpcnN0fX0nKTtcbiAgICBjb25zdCBjaGlsZHJlbkFmdGVyRmlyc3QgPSBzdGFjay5ub2RlLmNoaWxkcmVuLmxlbmd0aDtcblxuICAgIHRyYW5zZm9ybWVyLnRyYW5zZm9ybVZhbHVlKCd7e2JsdWVwcmludDovc2Vjb25kfX0nKTtcbiAgICBjb25zdCBjaGlsZHJlbkFmdGVyU2Vjb25kID0gc3RhY2subm9kZS5jaGlsZHJlbi5sZW5ndGg7XG5cbiAgICAvLyBTaG91bGQgbm90IGNyZWF0ZSBhZGRpdGlvbmFsIHBhcmFtZXRlcnNcbiAgICBleHBlY3QoY2hpbGRyZW5BZnRlclNlY29uZCkudG9CZShjaGlsZHJlbkFmdGVyRmlyc3QpO1xuICB9KTtcblxuICB0ZXN0KCd0aHJvd3Mgd2hlbiBibHVlcHJpbnQgcmVmIHdpdGhvdXQgc2NvcGUnLCAoKSA9PiB7XG4gICAgY29uc3QgdHJhbnNmb3JtZXIgPSBuZXcgTWRhYUNvbmZpZ0JsdWVwcmludFJlZlZhbHVlVHJhbnNmb3JtZXIoe1xuICAgICAgbmFtaW5nOiBtb2NrTmFtaW5nLFxuICAgICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIEB0eXBlc2NyaXB0LWVzbGludC9uby1leHBsaWNpdC1hbnlcbiAgICAgIHNjb3BlOiB1bmRlZmluZWQgYXMgYW55LFxuICAgIH0pO1xuICAgIGV4cGVjdCgoKSA9PiB0cmFuc2Zvcm1lci50cmFuc2Zvcm1WYWx1ZSgne3tibHVlcHJpbnQ6L3BhdGh9fScpKS50b1Rocm93KFxuICAgICAgJ1VuYWJsZSB0byByZXNvbHZlIGJsdWVwcmludCBwYXJhbXMgb3V0c2lkZSBvZiBhIENvbnN0cnVjdCcsXG4gICAgKTtcbiAgfSk7XG5cbiAgdGVzdCgndGhyb3dzIHdoZW4gYmx1ZXByaW50IHJlZiB3aXRob3V0IG5hbWluZycsICgpID0+IHtcbiAgICBjb25zdCB0cmFuc2Zvcm1lciA9IG5ldyBNZGFhQ29uZmlnQmx1ZXByaW50UmVmVmFsdWVUcmFuc2Zvcm1lcih7XG4gICAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgQHR5cGVzY3JpcHQtZXNsaW50L25vLWV4cGxpY2l0LWFueVxuICAgICAgbmFtaW5nOiB1bmRlZmluZWQgYXMgYW55LFxuICAgICAgc2NvcGU6IHN0YWNrLFxuICAgIH0pO1xuICAgIGV4cGVjdCgoKSA9PiB0cmFuc2Zvcm1lci50cmFuc2Zvcm1WYWx1ZSgne3tibHVlcHJpbnQ6L3BhdGh9fScpKS50b1Rocm93KFxuICAgICAgJ1VuYWJsZSB0byByZXNvbHZlIGJsdWVwcmludCBwYXJhbXMgd2l0aG91dCBhIG5hbWluZyBpbXBsZW1lbnRhdGlvbicsXG4gICAgKTtcbiAgfSk7XG59KTtcbiJdfQ==
|
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
3
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { Stack } from 'aws-cdk-lib';
|
|
7
|
+
import { MdaaConfigBlueprintRefValueTransformer } from '../lib';
|
|
8
|
+
import { IMdaaResourceNaming } from '@aws-mdaa/naming';
|
|
9
|
+
|
|
10
|
+
describe('MdaaConfigBlueprintRefValueTransformer', () => {
|
|
11
|
+
let mockNaming: IMdaaResourceNaming;
|
|
12
|
+
let stack: Stack;
|
|
13
|
+
|
|
14
|
+
beforeEach(() => {
|
|
15
|
+
stack = new Stack();
|
|
16
|
+
mockNaming = {
|
|
17
|
+
props: {
|
|
18
|
+
org: 'test-org',
|
|
19
|
+
env: 'test-env',
|
|
20
|
+
domain: 'test-domain',
|
|
21
|
+
moduleName: 'test-module',
|
|
22
|
+
},
|
|
23
|
+
withModuleName: jest.fn().mockReturnThis(),
|
|
24
|
+
withDomain: jest.fn().mockReturnThis(),
|
|
25
|
+
withOrg: jest.fn().mockReturnThis(),
|
|
26
|
+
withEnv: jest.fn().mockReturnThis(),
|
|
27
|
+
withSuffix: jest.fn().mockReturnThis(),
|
|
28
|
+
ssmDomainPath: jest.fn().mockReturnValue('/test/domain/path'),
|
|
29
|
+
ssmEnvPath: jest.fn().mockReturnValue('/test/env/path'),
|
|
30
|
+
ssmOrgPath: jest.fn().mockReturnValue('/test-org/'),
|
|
31
|
+
resourceName: jest
|
|
32
|
+
.fn()
|
|
33
|
+
.mockImplementation((suffix?: string) => (suffix ? `test-resource-${suffix}` : 'test-resource')),
|
|
34
|
+
ssmPath: jest.fn().mockImplementation((path: string) => `/test/${path}`),
|
|
35
|
+
stackName: jest.fn().mockImplementation((name?: string) => (name ? `test-stack-${name}` : 'test-stack')),
|
|
36
|
+
exportName: jest.fn().mockImplementation((path: string) => `test-export-${path}`),
|
|
37
|
+
} as unknown as IMdaaResourceNaming;
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
test('returns unchanged value without refs', () => {
|
|
41
|
+
const transformer = new MdaaConfigBlueprintRefValueTransformer({
|
|
42
|
+
naming: mockNaming,
|
|
43
|
+
scope: stack,
|
|
44
|
+
});
|
|
45
|
+
expect(transformer.transformValue('plain-value')).toBe('plain-value');
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
test('returns unchanged value with empty string', () => {
|
|
49
|
+
const transformer = new MdaaConfigBlueprintRefValueTransformer({
|
|
50
|
+
naming: mockNaming,
|
|
51
|
+
scope: stack,
|
|
52
|
+
});
|
|
53
|
+
expect(transformer.transformValue('')).toBe('');
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
test('transforms blueprint ref', () => {
|
|
57
|
+
const transformer = new MdaaConfigBlueprintRefValueTransformer({
|
|
58
|
+
naming: mockNaming,
|
|
59
|
+
scope: stack,
|
|
60
|
+
});
|
|
61
|
+
const result = transformer.transformValue('{{blueprint:/some/path}}');
|
|
62
|
+
expect(result).toContain('{{resolve:ssm:/test-org/');
|
|
63
|
+
expect(result).toContain('/some/path}}');
|
|
64
|
+
expect(mockNaming.ssmOrgPath).toHaveBeenCalledWith('', false);
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
test('transforms blueprint ref with prefix and suffix', () => {
|
|
68
|
+
const transformer = new MdaaConfigBlueprintRefValueTransformer({
|
|
69
|
+
naming: mockNaming,
|
|
70
|
+
scope: stack,
|
|
71
|
+
});
|
|
72
|
+
const result = transformer.transformValue('prefix-{{blueprint:/path}}-suffix');
|
|
73
|
+
expect(result).toContain('prefix-');
|
|
74
|
+
expect(result).toContain('-suffix');
|
|
75
|
+
expect(result).toContain('{{resolve:ssm:');
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
test('handles multiple blueprint refs', () => {
|
|
79
|
+
const transformer = new MdaaConfigBlueprintRefValueTransformer({
|
|
80
|
+
naming: mockNaming,
|
|
81
|
+
scope: stack,
|
|
82
|
+
});
|
|
83
|
+
const result = transformer.transformValue('{{blueprint:/path1}}-{{blueprint:/path2}}');
|
|
84
|
+
expect(result).toContain('/path1}}');
|
|
85
|
+
expect(result).toContain('/path2}}');
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
test('reuses existing CfnParameter for datazoneEnvironmentProjectId', () => {
|
|
89
|
+
const transformer = new MdaaConfigBlueprintRefValueTransformer({
|
|
90
|
+
naming: mockNaming,
|
|
91
|
+
scope: stack,
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
// First call creates the parameter
|
|
95
|
+
transformer.transformValue('{{blueprint:/path1}}');
|
|
96
|
+
|
|
97
|
+
// Second call should reuse the same parameter
|
|
98
|
+
transformer.transformValue('{{blueprint:/path2}}');
|
|
99
|
+
|
|
100
|
+
// Should only have one datazoneEnvironmentProjectId parameter
|
|
101
|
+
const params = stack.node.children.filter(child => child.node.id === 'datazoneEnvironmentProjectId');
|
|
102
|
+
expect(params.length).toBe(1);
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
test('ignores non-blueprint refs', () => {
|
|
106
|
+
const transformer = new MdaaConfigBlueprintRefValueTransformer({
|
|
107
|
+
naming: mockNaming,
|
|
108
|
+
scope: stack,
|
|
109
|
+
});
|
|
110
|
+
const result = transformer.transformValue('{{other:value}}');
|
|
111
|
+
// Non-blueprint refs should remain unchanged
|
|
112
|
+
expect(result).toBe('{{other:value}}');
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
test('handles nested refs in blueprint path', () => {
|
|
116
|
+
const transformer = new MdaaConfigBlueprintRefValueTransformer({
|
|
117
|
+
naming: mockNaming,
|
|
118
|
+
scope: stack,
|
|
119
|
+
});
|
|
120
|
+
// Nested refs should be processed recursively
|
|
121
|
+
const result = transformer.transformValue('{{blueprint:/{{other}}/path}}');
|
|
122
|
+
// The inner ref is not a blueprint ref, so it stays as-is in the path
|
|
123
|
+
expect(result).toContain('{{resolve:ssm:');
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
test('handles unbalanced braces gracefully', () => {
|
|
127
|
+
const transformer = new MdaaConfigBlueprintRefValueTransformer({
|
|
128
|
+
naming: mockNaming,
|
|
129
|
+
scope: stack,
|
|
130
|
+
});
|
|
131
|
+
// Unbalanced braces should be skipped
|
|
132
|
+
expect(transformer.transformValue('{{blueprint:/path')).toBe('{{blueprint:/path');
|
|
133
|
+
expect(transformer.transformValue('blueprint:/path}}')).toBe('blueprint:/path}}');
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
test('logs resolved blueprint path', () => {
|
|
137
|
+
const consoleSpy = jest.spyOn(console, 'log').mockImplementation();
|
|
138
|
+
const transformer = new MdaaConfigBlueprintRefValueTransformer({
|
|
139
|
+
naming: mockNaming,
|
|
140
|
+
scope: stack,
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
transformer.transformValue('{{blueprint:/test/path}}');
|
|
144
|
+
|
|
145
|
+
expect(consoleSpy).toHaveBeenCalledWith(expect.stringContaining('Resolving blueprint path:'));
|
|
146
|
+
consoleSpy.mockRestore();
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
test('handles blueprint ref with special characters in path', () => {
|
|
150
|
+
const transformer = new MdaaConfigBlueprintRefValueTransformer({
|
|
151
|
+
naming: mockNaming,
|
|
152
|
+
scope: stack,
|
|
153
|
+
});
|
|
154
|
+
const result = transformer.transformValue('{{blueprint:/path/with-dashes_and_underscores/123}}');
|
|
155
|
+
expect(result).toContain('{{resolve:ssm:');
|
|
156
|
+
expect(result).toContain('/path/with-dashes_and_underscores/123}}');
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
test('handles blueprint ref at start of string', () => {
|
|
160
|
+
const transformer = new MdaaConfigBlueprintRefValueTransformer({
|
|
161
|
+
naming: mockNaming,
|
|
162
|
+
scope: stack,
|
|
163
|
+
});
|
|
164
|
+
const result = transformer.transformValue('{{blueprint:/path}}-suffix');
|
|
165
|
+
expect(result).toMatch(/^\{\{resolve:ssm:/);
|
|
166
|
+
expect(result).toContain('-suffix');
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
test('handles blueprint ref at end of string', () => {
|
|
170
|
+
const transformer = new MdaaConfigBlueprintRefValueTransformer({
|
|
171
|
+
naming: mockNaming,
|
|
172
|
+
scope: stack,
|
|
173
|
+
});
|
|
174
|
+
const result = transformer.transformValue('prefix-{{blueprint:/path}}');
|
|
175
|
+
expect(result).toContain('prefix-');
|
|
176
|
+
expect(result).toMatch(/\}\}$/);
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
test('handles multiple different ref types mixed', () => {
|
|
180
|
+
const transformer = new MdaaConfigBlueprintRefValueTransformer({
|
|
181
|
+
naming: mockNaming,
|
|
182
|
+
scope: stack,
|
|
183
|
+
});
|
|
184
|
+
const result = transformer.transformValue('{{blueprint:/path1}}-{{other:ref}}-{{blueprint:/path2}}');
|
|
185
|
+
expect(result).toContain('/path1}}');
|
|
186
|
+
expect(result).toContain('{{other:ref}}');
|
|
187
|
+
expect(result).toContain('/path2}}');
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
test('handles empty blueprint path', () => {
|
|
191
|
+
const transformer = new MdaaConfigBlueprintRefValueTransformer({
|
|
192
|
+
naming: mockNaming,
|
|
193
|
+
scope: stack,
|
|
194
|
+
});
|
|
195
|
+
const result = transformer.transformValue('{{blueprint:}}');
|
|
196
|
+
expect(result).toContain('{{resolve:ssm:');
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
test('handles blueprint ref with only slashes', () => {
|
|
200
|
+
const transformer = new MdaaConfigBlueprintRefValueTransformer({
|
|
201
|
+
naming: mockNaming,
|
|
202
|
+
scope: stack,
|
|
203
|
+
});
|
|
204
|
+
const result = transformer.transformValue('{{blueprint:/}}');
|
|
205
|
+
expect(result).toContain('{{resolve:ssm:');
|
|
206
|
+
expect(result).toContain('/}}');
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
test('creates parameter on first blueprint ref', () => {
|
|
210
|
+
const transformer = new MdaaConfigBlueprintRefValueTransformer({
|
|
211
|
+
naming: mockNaming,
|
|
212
|
+
scope: stack,
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
const childrenBefore = stack.node.children.length;
|
|
216
|
+
transformer.transformValue('{{blueprint:/first}}');
|
|
217
|
+
const childrenAfter = stack.node.children.length;
|
|
218
|
+
|
|
219
|
+
expect(childrenAfter).toBeGreaterThan(childrenBefore);
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
test('does not create duplicate parameters', () => {
|
|
223
|
+
const transformer = new MdaaConfigBlueprintRefValueTransformer({
|
|
224
|
+
naming: mockNaming,
|
|
225
|
+
scope: stack,
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
transformer.transformValue('{{blueprint:/first}}');
|
|
229
|
+
const childrenAfterFirst = stack.node.children.length;
|
|
230
|
+
|
|
231
|
+
transformer.transformValue('{{blueprint:/second}}');
|
|
232
|
+
const childrenAfterSecond = stack.node.children.length;
|
|
233
|
+
|
|
234
|
+
// Should not create additional parameters
|
|
235
|
+
expect(childrenAfterSecond).toBe(childrenAfterFirst);
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
test('throws when blueprint ref without scope', () => {
|
|
239
|
+
const transformer = new MdaaConfigBlueprintRefValueTransformer({
|
|
240
|
+
naming: mockNaming,
|
|
241
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
242
|
+
scope: undefined as any,
|
|
243
|
+
});
|
|
244
|
+
expect(() => transformer.transformValue('{{blueprint:/path}}')).toThrow(
|
|
245
|
+
'Unable to resolve blueprint params outside of a Construct',
|
|
246
|
+
);
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
test('throws when blueprint ref without naming', () => {
|
|
250
|
+
const transformer = new MdaaConfigBlueprintRefValueTransformer({
|
|
251
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
252
|
+
naming: undefined as any,
|
|
253
|
+
scope: stack,
|
|
254
|
+
});
|
|
255
|
+
expect(() => transformer.transformValue('{{blueprint:/path}}')).toThrow(
|
|
256
|
+
'Unable to resolve blueprint params without a naming implementation',
|
|
257
|
+
);
|
|
258
|
+
});
|
|
259
|
+
});
|