@macedon-technologies/batman 1.29.0 → 1.30.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 +17 -95
- package/API.md +0 -268
- package/README.md +46 -0
- package/build-tools/scan-generated-workflows.ts +0 -1
- package/lib/ApiConstructFile.js +2 -20
- package/lib/Application.d.ts +0 -1
- package/lib/Application.js +4 -9
- package/lib/BaseApplicationStackFile.js +13 -9
- package/lib/BatmanProject.d.ts +0 -4
- package/lib/BatmanProject.js +2 -3
- package/lib/BootcampApplication.d.ts +0 -6
- package/lib/BootcampApplication.js +1 -8
- package/lib/BootcampBaseApplicationStackFile.js +1 -9
- package/lib/BootcampProject.js +2 -3
- package/lib/CodeOwnersFile.js +1 -1
- package/lib/LocalDevAppFile.js +1 -2
- package/lib/MainFile.d.ts +0 -1
- package/lib/MainFile.js +1 -4
- package/lib/ProductionDeployWorkflow.js +1 -1
- package/lib/SoftwareFactoryFiles.js +1 -1
- package/lib/StagingDeployWorkflow.js +1 -1
- package/lib/index.d.ts +0 -1
- package/lib/index.js +1 -2
- package/lib/utils.js +3 -3
- package/package.json +1 -1
- package/lib/WorkflowAutoDiscover.d.ts +0 -6
- package/lib/WorkflowAutoDiscover.js +0 -104
- package/lib/WorkflowProvider.d.ts +0 -5
- package/lib/WorkflowProvider.js +0 -308
package/lib/WorkflowProvider.js
DELETED
|
@@ -1,308 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.WorkflowProviderFile = void 0;
|
|
4
|
-
const projen_1 = require("projen");
|
|
5
|
-
class WorkflowProviderFile extends projen_1.TextFile {
|
|
6
|
-
constructor(project) {
|
|
7
|
-
super(project, 'src/constructs/WorkflowProvider.ts', {
|
|
8
|
-
marker: true,
|
|
9
|
-
readonly: true,
|
|
10
|
-
lines: [
|
|
11
|
-
"import { readFileSync } from 'fs';",
|
|
12
|
-
"import { join } from 'path';",
|
|
13
|
-
"import { Arn, CfnOutput, CustomResource, Duration, Reference, Stack } from 'aws-cdk-lib';",
|
|
14
|
-
"import { Effect, PolicyStatement } from 'aws-cdk-lib/aws-iam';",
|
|
15
|
-
"import { Code, Function, Runtime } from 'aws-cdk-lib/aws-lambda';",
|
|
16
|
-
"import { ISecret } from 'aws-cdk-lib/aws-secretsmanager';",
|
|
17
|
-
"import { Provider } from 'aws-cdk-lib/custom-resources';",
|
|
18
|
-
"import { Construct } from 'constructs';",
|
|
19
|
-
"import { JSONPath } from 'jsonpath-plus';",
|
|
20
|
-
'',
|
|
21
|
-
'export interface Replacement {',
|
|
22
|
-
' /**',
|
|
23
|
-
" * The 'name' from the 'node' that you'd like to make a replacement on.",
|
|
24
|
-
' */',
|
|
25
|
-
' readonly nodeName: string;',
|
|
26
|
-
'',
|
|
27
|
-
' /**',
|
|
28
|
-
' * A dotpath to the field on the node to change.',
|
|
29
|
-
' */',
|
|
30
|
-
' readonly path: string;',
|
|
31
|
-
'',
|
|
32
|
-
' /**',
|
|
33
|
-
' * The value to change the field to.',
|
|
34
|
-
' */',
|
|
35
|
-
' readonly value: string;',
|
|
36
|
-
'}',
|
|
37
|
-
'',
|
|
38
|
-
'export interface WorkflowProviderProps {',
|
|
39
|
-
' /**',
|
|
40
|
-
' * The full path to the workflow .JSON file you want to add.',
|
|
41
|
-
' */',
|
|
42
|
-
' readonly workflowFilePath: string;',
|
|
43
|
-
'',
|
|
44
|
-
' /**',
|
|
45
|
-
' * An AWS Secret that has the following JSON structure:',
|
|
46
|
-
' * {',
|
|
47
|
-
' * "url": "https://your-n8n-instance.com",',
|
|
48
|
-
' * "apiKey": "your-n8n-api-key"',
|
|
49
|
-
' * }',
|
|
50
|
-
' */',
|
|
51
|
-
' readonly secret: ISecret;',
|
|
52
|
-
'',
|
|
53
|
-
' /**',
|
|
54
|
-
' * An array of Replacements to make against the workflow. Use this to update Lambda function ARNs and other',
|
|
55
|
-
' * AWS-controlled resources in your workflow.',
|
|
56
|
-
' */',
|
|
57
|
-
' readonly replacements?: Replacement[];',
|
|
58
|
-
'}',
|
|
59
|
-
'',
|
|
60
|
-
'export class WorkflowProvider extends Construct {',
|
|
61
|
-
' public handler: Function;',
|
|
62
|
-
' public webhookPath: Reference;',
|
|
63
|
-
'',
|
|
64
|
-
' constructor(scope: Construct, id: string, props: WorkflowProviderProps) {',
|
|
65
|
-
' super(scope, id);',
|
|
66
|
-
'',
|
|
67
|
-
" const workflowContents = readFileSync(join(__dirname, '../workflows/alfred.json'));",
|
|
68
|
-
" const workflow = JSON.parse(workflowContents.toString('utf8'));",
|
|
69
|
-
' let newContents: string = workflow;',
|
|
70
|
-
' if (props.replacements) {',
|
|
71
|
-
' newContents = JSON.stringify(props.replacements',
|
|
72
|
-
' .reduce((p: any, c: Replacement) => {',
|
|
73
|
-
' replaceFieldByNodeName(p, c.nodeName, c.path, c.value);',
|
|
74
|
-
' return p;',
|
|
75
|
-
' }, workflow),',
|
|
76
|
-
' null,',
|
|
77
|
-
' 2);',
|
|
78
|
-
' }',
|
|
79
|
-
'',
|
|
80
|
-
'',
|
|
81
|
-
" const lambda = this.handler = new Function(this, 'Handler', {",
|
|
82
|
-
' runtime: Runtime.NODEJS_24_X,',
|
|
83
|
-
" handler: 'index.handler',",
|
|
84
|
-
' timeout: Duration.minutes(5),',
|
|
85
|
-
' code: Code.fromInline(`',
|
|
86
|
-
"const { SecretsManagerClient, GetSecretValueCommand } = require('@aws-sdk/client-secrets-manager');",
|
|
87
|
-
'',
|
|
88
|
-
'exports.handler = async (event) => {',
|
|
89
|
-
" console.log('Event:', JSON.stringify(event, null, 2));",
|
|
90
|
-
' ',
|
|
91
|
-
" if (event.RequestType === 'Delete') {",
|
|
92
|
-
" return { PhysicalResourceId: event.PhysicalResourceId || 'workflow-uploader' };",
|
|
93
|
-
' }',
|
|
94
|
-
'',
|
|
95
|
-
' try {',
|
|
96
|
-
' // Get secret',
|
|
97
|
-
' const secretsClient = new SecretsManagerClient({});',
|
|
98
|
-
' const secretCommand = new GetSecretValueCommand({ SecretId: process.env.SECRET_ARN });',
|
|
99
|
-
' const secretResponse = await secretsClient.send(secretCommand);',
|
|
100
|
-
' ',
|
|
101
|
-
' const secret = JSON.parse(secretResponse.SecretString);',
|
|
102
|
-
' const { url, apiKey } = secret;',
|
|
103
|
-
' ',
|
|
104
|
-
' const workflowContent = event.ResourceProperties.Content;',
|
|
105
|
-
' const { name, nodes, connections, settings } = JSON.parse(workflowContent);',
|
|
106
|
-
' const workflowData = {',
|
|
107
|
-
' name,',
|
|
108
|
-
' nodes,',
|
|
109
|
-
' connections,',
|
|
110
|
-
' settings,',
|
|
111
|
-
' };',
|
|
112
|
-
' delete workflowData.settings.binaryMode;',
|
|
113
|
-
'',
|
|
114
|
-
' const headers = {',
|
|
115
|
-
" 'Content-Type': 'application/json',",
|
|
116
|
-
" 'X-N8N-API-KEY': apiKey,",
|
|
117
|
-
' };',
|
|
118
|
-
' ',
|
|
119
|
-
' let workflowId = event.PhysicalResourceId;',
|
|
120
|
-
' let isUpdate = false;',
|
|
121
|
-
' ',
|
|
122
|
-
' // Try to get existing workflow if we have a physicalResourceId',
|
|
123
|
-
" if (workflowId && workflowId !== 'workflow-uploader') {",
|
|
124
|
-
' try {',
|
|
125
|
-
' const getResponse = await fetch(\\`\\${url}/api/v1/workflows/\\${workflowId}\\`, {',
|
|
126
|
-
" method: 'GET',",
|
|
127
|
-
' headers,',
|
|
128
|
-
' });',
|
|
129
|
-
' ',
|
|
130
|
-
' if (getResponse.ok) {',
|
|
131
|
-
' isUpdate = true;',
|
|
132
|
-
" console.log('Found existing workflow, will update');",
|
|
133
|
-
' }',
|
|
134
|
-
' } catch (error) {',
|
|
135
|
-
" console.log('Workflow not found, will create new one');",
|
|
136
|
-
' }',
|
|
137
|
-
' }',
|
|
138
|
-
' ',
|
|
139
|
-
' let result;',
|
|
140
|
-
' if (isUpdate) {',
|
|
141
|
-
' // Update existing workflow',
|
|
142
|
-
' const updateResponse = await fetch(\\`\\${url}/api/v1/workflows/\\${workflowId}\\`, {',
|
|
143
|
-
" method: 'PUT',",
|
|
144
|
-
' headers,',
|
|
145
|
-
' body: JSON.stringify(workflowData),',
|
|
146
|
-
' });',
|
|
147
|
-
' ',
|
|
148
|
-
' if (!updateResponse.ok) {',
|
|
149
|
-
' throw new Error(\\`Update failed: \\${updateResponse.status} \\${updateResponse.statusText}\\`);',
|
|
150
|
-
' }',
|
|
151
|
-
' ',
|
|
152
|
-
' result = await updateResponse.json();',
|
|
153
|
-
" console.log('Workflow updated successfully');",
|
|
154
|
-
' } else {',
|
|
155
|
-
' // Create new workflow',
|
|
156
|
-
' const createResponse = await fetch(\\`\\${url}/api/v1/workflows\\`, {',
|
|
157
|
-
" method: 'POST',",
|
|
158
|
-
' headers,',
|
|
159
|
-
' body: JSON.stringify(workflowData),',
|
|
160
|
-
' });',
|
|
161
|
-
' ',
|
|
162
|
-
' if (!createResponse.ok) {',
|
|
163
|
-
' let result = {};',
|
|
164
|
-
' if (createResponse.status === 400) {',
|
|
165
|
-
' result = await createResponse.json(); ',
|
|
166
|
-
' }',
|
|
167
|
-
' throw new Error(\\`Create failed: \\${createResponse.status} \\${createResponse.statusText} \\${JSON.stringify(result)} \\`);',
|
|
168
|
-
' }',
|
|
169
|
-
' ',
|
|
170
|
-
' result = await createResponse.json();',
|
|
171
|
-
' ',
|
|
172
|
-
' ',
|
|
173
|
-
' workflowId = result.id;',
|
|
174
|
-
" console.log('Workflow created successfully');",
|
|
175
|
-
' }',
|
|
176
|
-
' ',
|
|
177
|
-
' // Activate the workflow',
|
|
178
|
-
' const activateResponse = await fetch(\\`\\${url}/api/v1/workflows/\\${workflowId}/activate\\`, {',
|
|
179
|
-
" method: 'POST',",
|
|
180
|
-
' headers,',
|
|
181
|
-
' });',
|
|
182
|
-
' ',
|
|
183
|
-
' if (!activateResponse.ok) {',
|
|
184
|
-
' let result = {};',
|
|
185
|
-
' if (activateResponse.status === 400) {',
|
|
186
|
-
' result = await activateResponse.json();',
|
|
187
|
-
' }',
|
|
188
|
-
' console.warn(\\`Activation failed: \\${activateResponse.status} \\${activateResponse.statusText}\\\\ \\${JSON.stringify(result)} \\`);',
|
|
189
|
-
' } else {',
|
|
190
|
-
" console.log('Workflow activated successfully');",
|
|
191
|
-
' }',
|
|
192
|
-
'',
|
|
193
|
-
' // Fetch the workflow to extract webhook path',
|
|
194
|
-
" let webhookPath = \\'\\';",
|
|
195
|
-
' const getWorkflowResponse = await fetch(\\`\\${url}/api/v1/workflows/\\${workflowId}\\`, {',
|
|
196
|
-
" method: 'GET',",
|
|
197
|
-
' headers,',
|
|
198
|
-
' });',
|
|
199
|
-
'',
|
|
200
|
-
' if (getWorkflowResponse.ok) {',
|
|
201
|
-
' const workflow = await getWorkflowResponse.json();',
|
|
202
|
-
" const webhookNode = (workflow.nodes || []).find(n => n.type === 'n8n-nodes-base.webhook');",
|
|
203
|
-
' if (webhookNode && webhookNode.parameters && webhookNode.parameters.path) {',
|
|
204
|
-
' webhookPath = webhookNode.parameters.path;',
|
|
205
|
-
' console.log(\\`Found webhook path: \\${webhookPath}\\`);',
|
|
206
|
-
' }',
|
|
207
|
-
' }',
|
|
208
|
-
'',
|
|
209
|
-
' return {',
|
|
210
|
-
' PhysicalResourceId: workflowId,',
|
|
211
|
-
' Data: { WorkflowId: workflowId, WebhookPath: webhookPath }',
|
|
212
|
-
' };',
|
|
213
|
-
' } catch (error) {',
|
|
214
|
-
" console.error('Error:', error);",
|
|
215
|
-
' return {',
|
|
216
|
-
" PhysicalResourceId: event.PhysicalResourceId || 'workflow-uploader',",
|
|
217
|
-
" Data: { Error: String(error), WebhookPath: 'unavailable' }",
|
|
218
|
-
' };',
|
|
219
|
-
' }',
|
|
220
|
-
'};',
|
|
221
|
-
' `),',
|
|
222
|
-
' environment: {',
|
|
223
|
-
' SECRET_ARN: props.secret.secretArn,',
|
|
224
|
-
' },',
|
|
225
|
-
' });',
|
|
226
|
-
'',
|
|
227
|
-
' props.secret.grantRead(lambda);',
|
|
228
|
-
'',
|
|
229
|
-
'',
|
|
230
|
-
' // Grant KMS decrypt permissions for the secret',
|
|
231
|
-
' if (props.secret.encryptionKey) {',
|
|
232
|
-
" console.log('encryption key found, granting decrypt permissions');",
|
|
233
|
-
' props.secret.encryptionKey.grantDecrypt(lambda);',
|
|
234
|
-
'',
|
|
235
|
-
' } else {',
|
|
236
|
-
' lambda.role?.addToPrincipalPolicy(new PolicyStatement({',
|
|
237
|
-
' effect: Effect.ALLOW,',
|
|
238
|
-
' actions: [',
|
|
239
|
-
" 'kms:Decrypt',",
|
|
240
|
-
" 'kms:DescribeKey',",
|
|
241
|
-
' ],',
|
|
242
|
-
' resources: [Arn.format({',
|
|
243
|
-
" service: 'kms',",
|
|
244
|
-
" resource: 'key',",
|
|
245
|
-
" resourceName: 'a699f853-0475-4d4d-b8ab-be881d66624f',",
|
|
246
|
-
" partition: 'aws',",
|
|
247
|
-
' region: Stack.of(this).region,',
|
|
248
|
-
" account: '581514672367',",
|
|
249
|
-
'',
|
|
250
|
-
' })],',
|
|
251
|
-
' }));',
|
|
252
|
-
' }',
|
|
253
|
-
'',
|
|
254
|
-
" const provider = new Provider(this, 'Provider', {",
|
|
255
|
-
' onEventHandler: lambda,',
|
|
256
|
-
' });',
|
|
257
|
-
'',
|
|
258
|
-
" const customResource = new CustomResource(this, 'Resource', {",
|
|
259
|
-
' serviceToken: provider.serviceToken,',
|
|
260
|
-
' properties: {',
|
|
261
|
-
' Content: newContents,',
|
|
262
|
-
' },',
|
|
263
|
-
' });',
|
|
264
|
-
'',
|
|
265
|
-
" this.webhookPath = customResource.getAtt('WebhookPath');",
|
|
266
|
-
'',
|
|
267
|
-
' // Output the Lambda execution role ARN for cross-account KMS permissions',
|
|
268
|
-
" new CfnOutput(this, 'LambdaExecutionRoleArn', {",
|
|
269
|
-
' value: lambda.role!.roleArn,',
|
|
270
|
-
" description: 'Lambda execution role ARN - add this to KMS key policy in other account',",
|
|
271
|
-
' });',
|
|
272
|
-
' }',
|
|
273
|
-
'',
|
|
274
|
-
'',
|
|
275
|
-
'}',
|
|
276
|
-
'',
|
|
277
|
-
'export function replaceFieldByNodeName(',
|
|
278
|
-
' obj: Record<string, unknown>,',
|
|
279
|
-
' nodeName: string,',
|
|
280
|
-
' fieldPath: string, // dot-notation relative to the node, e.g. "parameters.url"',
|
|
281
|
-
' newValue: unknown,',
|
|
282
|
-
'): void {',
|
|
283
|
-
' const nodes = JSONPath({',
|
|
284
|
-
" path: `$.nodes[?(@.name=='${nodeName}')]`,",
|
|
285
|
-
' json: obj,',
|
|
286
|
-
" resultType: 'value',",
|
|
287
|
-
' });',
|
|
288
|
-
'',
|
|
289
|
-
' for (const node of nodes) {',
|
|
290
|
-
' setNestedValue(node, fieldPath, newValue);',
|
|
291
|
-
' }',
|
|
292
|
-
'}',
|
|
293
|
-
'',
|
|
294
|
-
'function setNestedValue(obj: any, dotPath: string, value: unknown): void {',
|
|
295
|
-
" const keys = dotPath.split('.');",
|
|
296
|
-
' let current = obj;',
|
|
297
|
-
' for (let i = 0; i < keys.length - 1; i++) {',
|
|
298
|
-
' current = current[keys[i]];',
|
|
299
|
-
' }',
|
|
300
|
-
' current[keys[keys.length - 1]] = value;',
|
|
301
|
-
'}',
|
|
302
|
-
'',
|
|
303
|
-
],
|
|
304
|
-
});
|
|
305
|
-
}
|
|
306
|
-
}
|
|
307
|
-
exports.WorkflowProviderFile = WorkflowProviderFile;
|
|
308
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiV29ya2Zsb3dQcm92aWRlci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3NyYy9Xb3JrZmxvd1Byb3ZpZGVyLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7OztBQUFBLG1DQUFrQztBQUdsQyxNQUFhLG9CQUFxQixTQUFRLGlCQUFRO0lBQ2hELFlBQVksT0FBNEI7UUFDdEMsS0FBSyxDQUFDLE9BQU8sRUFBRSxvQ0FBb0MsRUFBRTtZQUNuRCxNQUFNLEVBQUUsSUFBSTtZQUNaLFFBQVEsRUFBRSxJQUFJO1lBQ2QsS0FBSyxFQUFFO2dCQUNMLG9DQUFvQztnQkFDcEMsOEJBQThCO2dCQUM5QiwyRkFBMkY7Z0JBQzNGLGdFQUFnRTtnQkFDaEUsbUVBQW1FO2dCQUNuRSwyREFBMkQ7Z0JBQzNELDBEQUEwRDtnQkFDMUQseUNBQXlDO2dCQUN6QywyQ0FBMkM7Z0JBQzNDLEVBQUU7Z0JBQ0YsZ0NBQWdDO2dCQUNoQyxPQUFPO2dCQUNQLDJFQUEyRTtnQkFDM0UsT0FBTztnQkFDUCw4QkFBOEI7Z0JBQzlCLEVBQUU7Z0JBQ0YsT0FBTztnQkFDUCxvREFBb0Q7Z0JBQ3BELE9BQU87Z0JBQ1AsMEJBQTBCO2dCQUMxQixFQUFFO2dCQUNGLE9BQU87Z0JBQ1Asd0NBQXdDO2dCQUN4QyxPQUFPO2dCQUNQLDJCQUEyQjtnQkFDM0IsR0FBRztnQkFDSCxFQUFFO2dCQUNGLDBDQUEwQztnQkFDMUMsT0FBTztnQkFDUCxnRUFBZ0U7Z0JBQ2hFLE9BQU87Z0JBQ1Asc0NBQXNDO2dCQUN0QyxFQUFFO2dCQUNGLE9BQU87Z0JBQ1AsMkRBQTJEO2dCQUMzRCxRQUFRO2dCQUNSLGdEQUFnRDtnQkFDaEQscUNBQXFDO2dCQUNyQyxRQUFRO2dCQUNSLE9BQU87Z0JBQ1AsNkJBQTZCO2dCQUM3QixFQUFFO2dCQUNGLE9BQU87Z0JBQ1AsK0dBQStHO2dCQUMvRyxpREFBaUQ7Z0JBQ2pELE9BQU87Z0JBQ1AsMENBQTBDO2dCQUMxQyxHQUFHO2dCQUNILEVBQUU7Z0JBQ0Ys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cEMsK0ZBQStGO2dCQUMvRixTQUFTO2dCQUNULEtBQUs7Z0JBQ0wsRUFBRTtnQkFDRixFQUFFO2dCQUNGLEdBQUc7Z0JBQ0gsRUFBRTtnQkFDRix5Q0FBeUM7Z0JBQ3pDLGlDQUFpQztnQkFDakMscUJBQXFCO2dCQUNyQixrRkFBa0Y7Z0JBQ2xGLHNCQUFzQjtnQkFDdEIsV0FBVztnQkFDWCw0QkFBNEI7Z0JBQzVCLGdEQUFnRDtnQkFDaEQsZ0JBQWdCO2dCQUNoQiwwQkFBMEI7Z0JBQzFCLE9BQU87Z0JBQ1AsRUFBRTtnQkFDRiwrQkFBK0I7Z0JBQy9CLGdEQUFnRDtnQkFDaEQsS0FBSztnQkFDTCxHQUFHO2dCQUNILEVBQUU7Z0JBQ0YsNEVBQTRFO2dCQUM1RSxvQ0FBb0M7Z0JBQ3BDLHNCQUFzQjtnQkFDdEIsK0NBQStDO2dCQUMvQyxpQ0FBaUM7Z0JBQ2pDLEtBQUs7Z0JBQ0wsMkNBQTJDO2dCQUMzQyxHQUFHO2dCQUNILEVBQUU7YUFDSDtTQUNGLENBQUMsQ0FBQztJQUNMLENBQUM7Q0FDRjtBQTdTRCxvREE2U0MiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBUZXh0RmlsZSB9IGZyb20gJ3Byb2plbic7XG5pbXBvcnQgeyBBd3NDZGtUeXBlU2NyaXB0QXBwIH0gZnJvbSAncHJvamVuL2xpYi9hd3NjZGsnO1xuXG5leHBvcnQgY2xhc3MgV29ya2Zsb3dQcm92aWRlckZpbGUgZXh0ZW5kcyBUZXh0RmlsZSB7XG4gIGNvbnN0cnVjdG9yKHByb2plY3Q6IEF3c0Nka1R5cGVTY3JpcHRBcHApIHtcbiAgICBzdXBlcihwcm9qZWN0LCAnc3JjL2NvbnN0cnVjdHMvV29ya2Zsb3dQcm92aWRlci50cycsIHtcbiAgICAgIG1hcmtlcjogdHJ1ZSxcbiAgICAgIHJlYWRvbmx5OiB0cnVlLFxuICAgICAgbGluZXM6IFtcbiAgICAgICAgXCJpbXBvcnQgeyByZWFkRmlsZVN5bmMgfSBmcm9tICdmcyc7XCIsXG4gICAgICAgIFwiaW1wb3J0IHsgam9pbiB9IGZyb20gJ3BhdGgnO1wiLFxuICAgICAgICBcImltcG9ydCB7IEFybiwgQ2ZuT3V0cHV0LCBDdXN0b21SZXNvdXJjZSwgRHVyYXRpb24sIFJlZmVyZW5jZSwgU3RhY2sgfSBmcm9tICdhd3MtY2RrLWxpYic7XCIsXG4gICAgICAgIFwiaW1wb3J0IHsgRWZmZWN0LCBQb2xpY3lTdGF0ZW1lbnQgfSBmcm9tICdhd3MtY2RrLWxpYi9hd3MtaWFtJztcIixcbiAgICAgICAgXCJpbXBvcnQgeyBDb2RlLCBGdW5jdGlvbiwgUnVudGltZSB9IGZyb20gJ2F3cy1jZGstbGliL2F3cy1sYW1iZGEnO1wiLFxuICAgICAgICBcImltcG9ydCB7IElTZWNyZXQgfSBmcm9tICdhd3MtY2RrLWxpYi9hd3Mtc2VjcmV0c21hbmFnZXInO1wiLFxuICAgICAgICBcImltcG9ydCB7IFByb3ZpZGVyIH0gZnJvbSAnYXdzLWNkay1saWIvY3VzdG9tLXJlc291cmNlcyc7XCIsXG4gICAgICAgIFwiaW1wb3J0IHsgQ29uc3RydWN0IH0gZnJvbSAnY29uc3RydWN0cyc7XCIsXG4gICAgICAgIFwiaW1wb3J0IHsgSlNPTlBhdGggfSBmcm9tICdqc29ucGF0aC1wbHVzJztcIixcbiAgICAgICAgJycsXG4gICAgICAgICdleHBvcnQgaW50ZXJmYWNlIFJlcGxhY2VtZW50IHsnLFxuICAgICAgICAnICAvKionLFxuICAgICAgICBcIiAgICogVGhlICduYW1lJyBmcm9tIHRoZSAnbm9kZScgdGhhdCB5b3UnZCBsaWtlIHRvIG1ha2UgYSByZXBsYWNlbWVudCBvbi5cIixcbiAgICAgICAgJyAgICovJyxcbiAgICAgICAgJyAgcmVhZG9ubHkgbm9kZU5hbWU6IHN0cmluZzsnLFxuICAgICAgICAnJyxcbiAgICAgICAgJyAgLyoqJyxcbiAgICAgICAgJyAgICogQSBkb3RwYXRoIHRvIHRoZSBmaWVsZCBvbiB0aGUgbm9kZSB0byBjaGFuZ2UuJyxcbiAgICAgICAgJyAgICovJyxcbiAgICAgICAgJyAgcmVhZG9ubHkgcGF0aDogc3RyaW5nOycsXG4gICAgICAgICcnLFxuICAgICAgICAnICAvKionLFxuICAgICAgICAnICAgKiBUaGUgdmFsdWUgdG8gY2hhbmdlIHRoZSBmaWVsZCB0by4nLFxuICAgICAgICAnICAgKi8nLFxuICAgICAgICAnICByZWFkb25seSB2YWx1ZTogc3RyaW5nOycsXG4gICAgICAgICd9JyxcbiAgICAgICAgJycsXG4gICAgICAgICdleHBvcnQgaW50ZXJmYWNlIFdvcmtmbG93UHJvdmlkZXJQcm9wcyB7JyxcbiAgICAgICAgJyAgLyoqJyxcbiAgICAgICAgJyAgICogVGhlIGZ1bGwgcGF0aCB0byB0aGUgd29ya2Zsb3cgLkpTT04gZmlsZSB5b3Ugd2FudCB0byBhZGQuJyxcbiAgICAgICAgJyAgICovJyxcbiAgICAgICAgJyAgcmVhZG9ubHkgd29ya2Zsb3dGaWxlUGF0aDogc3RyaW5nOycsXG4gICAgICAgICcnLFxuICAgICAgICAnICAvKionLFxuICAgICAgICAnICAgKiBBbiBBV1MgU2VjcmV0IHRoYXQgaGFzIHRoZSBmb2xsb3dpbmcgSlNPTiBzdHJ1Y3R1cmU6JyxcbiAgICAgICAgJyAgICogeycsXG4gICAgICAgICcgICAqICAgXCJ1cmxcIjogXCJodHRwczovL3lvdXItbjhuLWluc3RhbmNlLmNvbVwiLCcsXG4gICAgICAgICcgICAqICAgXCJhcGlLZXlcIjogXCJ5b3VyLW44bi1hcGkta2V5XCInLFxuICAgICAgICAnICAgKiB9JyxcbiAgICAgICAgJyAgICovJyxcbiAgICAgICAgJyAgcmVhZG9ubHkgc2VjcmV0OiBJU2VjcmV0OycsXG4gICAgICAgICcnLFxuICAgICAgICAnICAvKionLFxuICAgICAgICAnICAgKiBBbiBhcnJheSBvZiBSZXBsYWNlbWVudHMgdG8gbWFrZSBhZ2FpbnN0IHRoZSB3b3JrZmxvdy4gVXNlIHRoaXMgdG8gdXBkYXRlIExhbWJkYSBmdW5jdGlvbiBBUk5zIGFuZCBvdGhlcicsXG4gICAgICAgICcgICAqIEFXUy1jb250cm9sbGVkIHJlc291cmNlcyBpbiB5b3VyIHdvcmtmbG93LicsXG4gICAgICAgICcgICAqLycsXG4gICAgICAgICcgIHJlYWRvbmx5IHJlcGxhY2VtZW50cz86IFJlcGxhY2VtZW50W107JyxcbiAgICAgICAgJ30nLFxuICAgICAgICAnJyxcbiAgICAgICAgJ2V4cG9ydCBjbGFzcyBXb3JrZmxvd1Byb3ZpZGVyIGV4dGVuZHMgQ29uc3RydWN0IHsnLFxuICAgICAgICAnICBwdWJsaWMgaGFuZGxlcjogRnVuY3Rpb247JyxcbiAgICAgICAgJyAgcHVibGljIHdlYmhvb2tQYXRoOiBSZWZlcmVuY2U7JyxcbiAgICAgICAgJycsXG4gICAgICAgICcgIGNvbnN0cnVjdG9yKHNjb3BlOiBDb25zdHJ1Y3QsIGlkOiBzdHJpbmcsIHByb3BzOiBXb3JrZmxvd1Byb3ZpZGVyUHJvcHMpIHsnLFxuICAgICAgICAnICAgIHN1cGVyKHNjb3BlLCBpZCk7JyxcbiAgICAgICAgJycsXG4gICAgICAgIFwiICAgIGNvbnN0IHdvcmtmbG93Q29udGVudHMgPSByZWFkRmlsZVN5bmMoam9pbihfX2Rpcm5hbWUsICcuLi93b3JrZmxvd3MvYWxmcmVkLmpzb24nKSk7XCIsXG4gICAgICAgIFwiICAgIGNvbnN0IHdvcmtmbG93ID0gSlNPTi5wYXJzZSh3b3JrZmxvd0NvbnRlbnRzLnRvU3RyaW5nKCd1dGY4JykpO1wiLFxuICAgICAgICAnICAgIGxldCBuZXdDb250ZW50czogc3RyaW5nID0gd29ya2Zsb3c7JyxcbiAgICAgICAgJyAgICBpZiAocHJvcHMucmVwbGFjZW1lbnRzKSB7JyxcbiAgICAgICAgJyAgICAgIG5ld0NvbnRlbnRzID0gSlNPTi5zdHJpbmdpZnkocHJvcHMucmVwbGFjZW1lbnRzJyxcbiAgICAgICAgJyAgICAgICAgLnJlZHVjZSgocDogYW55LCBjOiBSZXBsYWNlbWVudCkgPT4geycsXG4gICAgICAgICcgICAgICAgICAgcmVwbGFjZUZpZWxkQnlOb2RlTmFtZShwLCBjLm5vZGVOYW1lLCBjLnBhdGgsIGMudmFsdWUpOycsXG4gICAgICAgICcgICAgICAgICAgcmV0dXJuIHA7JyxcbiAgICAgICAgJyAgICAgICAgfSwgd29ya2Zsb3cpLCcsXG4gICAgICAgICcgICAgICBudWxsLCcsXG4gICAgICAgICcgICAgICAyKTsnLFxuICAgICAgICAnICAgIH0nLFxuICAgICAgICAnJyxcbiAgICAgICAgJycsXG4gICAgICAgIFwiICAgIGNvbnN0IGxhbWJkYSA9IHRoaXMuaGFuZGxlciA9IG5ldyBGdW5jdGlvbih0aGlzLCAnSGFuZGxlcicsIHtcIixcbiAgICAgICAgJyAgICAgIHJ1bnRpbWU6IFJ1bnRpbWUuTk9ERUpTXzI0X1gsJyxcbiAgICAgICAgXCIgICAgICBoYW5kbGVyOiAnaW5kZXguaGFuZGxlcicsXCIsXG4gICAgICAgICcgICAgICB0aW1lb3V0OiBEdXJhdGlvbi5taW51dGVzKDUpLCcsXG4gICAgICAgICcgICAgICBjb2RlOiBDb2RlLmZyb21JbmxpbmUoYCcsXG4gICAgICAgIFwiY29uc3QgeyBTZWNyZXRzTWFuYWdlckNsaWVudCwgR2V0U2VjcmV0VmFsdWVDb21tYW5kIH0gPSByZXF1aXJlKCdAYXdzLXNkay9jbGllbnQtc2VjcmV0cy1tYW5hZ2VyJyk7XCIsXG4gICAgICAgICcnLFxuICAgICAgICAnZXhwb3J0cy5oYW5kbGVyID0gYXN5bmMgKGV2ZW50KSA9PiB7JyxcbiAgICAgICAgXCIgIGNvbnNvbGUubG9nKCdFdmVudDonLCBKU09OLnN0cmluZ2lmeShldmVudCwgbnVsbCwgMikpO1wiLFxuICAgICAgICAnICAnLFxuICAgICAgICBcIiAgaWYgKGV2ZW50LlJlcXVlc3RUeXBlID09PSAnRGVsZXRlJykge1wiLFxuICAgICAgICBcIiAgICByZXR1cm4geyBQaHlzaWNhbFJlc291cmNlSWQ6IGV2ZW50LlBoeXNpY2FsUmVzb3VyY2VJZCB8fCAnd29ya2Zsb3ctdXBsb2FkZXInIH07XCIsXG4gICAgICAgICcgIH0nLFxuICAgICAgICAnJyxcbiAgICAgICAgJyAgdHJ5IHsnLFxuICAgICAgICAnICAgIC8vIEdldCBzZWNyZXQnLFxuICAgICAgICAnICAgIGNvbnN0IHNlY3JldHNDbGllbnQgPSBuZXcgU2VjcmV0c01hbmFnZXJDbGllbnQoe30pOycsXG4gICAgICAgICcgICAgY29uc3Qgc2VjcmV0Q29tbWFuZCA9IG5ldyBHZXRTZWNyZXRWYWx1ZUNvbW1hbmQoeyBTZWNyZXRJZDogcHJvY2Vzcy5lbnYuU0VDUkVUX0FSTiB9KTsnLFxuICAgICAgICAnICAgIGNvbnN0IHNlY3JldFJlc3BvbnNlID0gYXdhaXQgc2VjcmV0c0NsaWVudC5zZW5kKHNlY3JldENvbW1hbmQpOycsXG4gICAgICAgICcgICAgJyxcbiAgICAgICAgJyAgICBjb25zdCBzZWNyZXQgPSBKU09OLnBhcnNlKHNlY3JldFJlc3BvbnNlLlNlY3JldFN0cmluZyk7JyxcbiAgICAgICAgJyAgICBjb25zdCB7IHVybCwgYXBpS2V5IH0gPSBzZWNyZXQ7JyxcbiAgICAgICAgJyAgICAnLFxuICAgICAgICAnICAgIGNvbnN0IHdvcmtmbG93Q29udGVudCA9IGV2ZW50LlJlc291cmNlUHJvcGVydGllcy5Db250ZW50OycsXG4gICAgICAgICcgICAgY29uc3QgeyBuYW1lLCBub2RlcywgY29ubmVjdGlvbnMsIHNldHRpbmdzIH0gPSBKU09OLnBhcnNlKHdvcmtmbG93Q29udGVudCk7JyxcbiAgICAgICAgJyAgICBjb25zdCB3b3JrZmxvd0RhdGEgPSB7JyxcbiAgICAgICAgJyAgICAgIG5hbWUsJyxcbiAgICAgICAgJyAgICAgIG5vZGVzLCcsXG4gICAgICAgICcgICAgICBjb25uZWN0aW9ucywnLFxuICAgICAgICAnICAgICAgc2V0dGluZ3MsJyxcbiAgICAgICAgJyAgICB9OycsXG4gICAgICAgICcgICAgZGVsZXRlIHdvcmtmbG93RGF0YS5zZXR0aW5ncy5iaW5hcnlNb2RlOycsXG4gICAgICAgICcnLFxuICAgICAgICAnICAgIGNvbnN0IGhlYWRlcnMgPSB7JyxcbiAgICAgICAgXCIgICAgICAnQ29udGVudC1UeXBlJzogJ2FwcGxpY2F0aW9uL2pzb24nLFwiLFxuICAgICAgICBcIiAgICAgICdYLU44Ti1BUEktS0VZJzogYXBpS2V5LFwiLFxuICAgICAgICAnICAgIH07JyxcbiAgICAgICAgJyAgICAnLFxuICAgICAgICAnICAgIGxldCB3b3JrZmxvd0lkID0gZXZlbnQuUGh5c2ljYWxSZXNvdXJjZUlkOycsXG4gICAgICAgICcgICAgbGV0IGlzVXBkYXRlID0gZmFsc2U7JyxcbiAgICAgICAgJyAgICAnLFxuICAgICAgICAnICAgIC8vIFRyeSB0byBnZXQgZXhpc3Rpbmcgd29ya2Zsb3cgaWYgd2UgaGF2ZSBhIHBoeXNpY2FsUmVzb3VyY2VJZCcsXG4gICAgICAgIFwiICAgIGlmICh3b3JrZmxvd0lkICYmIHdvcmtmbG93SWQgIT09ICd3b3JrZmxvdy11cGxvYWRlcicpIHtcIixcbiAgICAgICAgJyAgICAgIHRyeSB7JyxcbiAgICAgICAgJyAgICAgICAgY29uc3QgZ2V0UmVzcG9uc2UgPSBhd2FpdCBmZXRjaChcXFxcYFxcXFwke3VybH0vYXBpL3YxL3dvcmtmbG93cy9cXFxcJHt3b3JrZmxvd0lkfVxcXFxgLCB7JyxcbiAgICAgICAgXCIgICAgICAgICAgbWV0aG9kOiAnR0VUJyxcIixcbiAgICAgICAgJyAgICAgICAgICBoZWFkZXJzLCcsXG4gICAgICAgICcgICAgICAgIH0pOycsXG4gICAgICAgICcgICAgICAgICcsXG4gICAgICAgICcgICAgICAgIGlmIChnZXRSZXNwb25zZS5vaykgeycsXG4gICAgICAgICcgICAgICAgICAgaXNVcGRhdGUgPSB0cnVlOycsXG4gICAgICAgIFwiICAgICAgICAgIGNvbnNvbGUubG9nKCdGb3VuZCBleGlzdGluZyB3b3JrZmxvdywgd2lsbCB1cGRhdGUnKTtcIixcbiAgICAgICAgJyAgICAgICAgfScsXG4gICAgICAgICcgICAgICB9IGNhdGNoIChlcnJvcikgeycsXG4gICAgICAgIFwiICAgICAgICBjb25zb2xlLmxvZygnV29ya2Zsb3cgbm90IGZvdW5kLCB3aWxsIGNyZWF0ZSBuZXcgb25lJyk7XCIsXG4gICAgICAgICcgICAgICB9JyxcbiAgICAgICAgJyAgICB9JyxcbiAgICAgICAgJyAgICAnLFxuICAgICAgICAnICAgIGxldCByZXN1bHQ7JyxcbiAgICAgICAgJyAgICBpZiAoaXNVcGRhdGUpIHsnLFxuICAgICAgICAnICAgICAgLy8gVXBkYXRlIGV4aXN0aW5nIHdvcmtmbG93JyxcbiAgICAgICAgJyAgICAgIGNvbnN0IHVwZGF0ZVJlc3BvbnNlID0gYXdhaXQgZmV0Y2goXFxcXGBcXFxcJHt1cmx9L2FwaS92MS93b3JrZmxvd3MvXFxcXCR7d29ya2Zsb3dJZH1cXFxcYCwgeycsXG4gICAgICAgIFwiICAgICAgICBtZXRob2Q6ICdQVVQnLFwiLFxuICAgICAgICAnICAgICAgICBoZWFkZXJzLCcsXG4gICAgICAgICcgICAgICAgIGJvZHk6IEpTT04uc3RyaW5naWZ5KHdvcmtmbG93RGF0YSksJyxcbiAgICAgICAgJyAgICAgIH0pOycsXG4gICAgICAgICcgICAgICAnLFxuICAgICAgICAnICAgICAgaWYgKCF1cGRhdGVSZXNwb25zZS5vaykgeycsXG4gICAgICAgICcgICAgICAgIHRocm93IG5ldyBFcnJvcihcXFxcYFVwZGF0ZSBmYWlsZWQ6IFxcXFwke3VwZGF0ZVJlc3BvbnNlLnN0YXR1c30gXFxcXCR7dXBkYXRlUmVzcG9uc2Uuc3RhdHVzVGV4dH1cXFxcYCk7JyxcbiAgICAgICAgJyAgICAgIH0nLFxuICAgICAgICAnICAgICAgJyxcbiAgICAgICAgJyAgICAgIHJlc3VsdCA9IGF3YWl0IHVwZGF0ZVJlc3BvbnNlLmpzb24oKTsnLFxuICAgICAgICBcIiAgICAgIGNvbnNvbGUubG9nKCdXb3JrZmxvdyB1cGRhdGVkIHN1Y2Nlc3NmdWxseScpO1wiLFxuICAgICAgICAnICAgIH0gZWxzZSB7JyxcbiAgICAgICAgJyAgICAgIC8vIENyZWF0ZSBuZXcgd29ya2Zsb3cnLFxuICAgICAgICAnICAgICAgY29uc3QgY3JlYXRlUmVzcG9uc2UgPSBhd2FpdCBmZXRjaChcXFxcYFxcXFwke3VybH0vYXBpL3YxL3dvcmtmbG93c1xcXFxgLCB7JyxcbiAgICAgICAgXCIgICAgICAgIG1ldGhvZDogJ1BPU1QnLFwiLFxuICAgICAgICAnICAgICAgICBoZWFkZXJzLCcsXG4gICAgICAgICcgICAgICAgIGJvZHk6IEpTT04uc3RyaW5naWZ5KHdvcmtmbG93RGF0YSksJyxcbiAgICAgICAgJyAgICAgIH0pOycsXG4gICAgICAgICcgICAgICAnLFxuICAgICAgICAnICAgICAgaWYgKCFjcmVhdGVSZXNwb25zZS5vaykgeycsXG4gICAgICAgICcgICAgICAgIGxldCByZXN1bHQgPSB7fTsnLFxuICAgICAgICAnICAgICAgICBpZiAoY3JlYXRlUmVzcG9uc2Uuc3RhdHVzID09PSA0MDApIHsnLFxuICAgICAgICAnICAgICAgICAgIHJlc3VsdCA9IGF3YWl0IGNyZWF0ZVJlc3BvbnNlLmpzb24oKTsgICcsXG4gICAgICAgICcgICAgICAgIH0nLFxuICAgICAgICAnICAgICAgICB0aHJvdyBuZXcgRXJyb3IoXFxcXGBDcmVhdGUgZmFpbGVkOiBcXFxcJHtjcmVhdGVSZXNwb25zZS5zdGF0dXN9IFxcXFwke2NyZWF0ZVJlc3BvbnNlLnN0YXR1c1RleHR9IFxcXFwke0pTT04uc3RyaW5naWZ5KHJlc3VsdCl9IFxcXFxgKTsnLFxuICAgICAgICAnICAgICAgfScsXG4gICAgICAgICcgICAgICAnLFxuICAgICAgICAnICAgICAgcmVzdWx0ID0gYXdhaXQgY3JlYXRlUmVzcG9uc2UuanNvbigpOycsXG4gICAgICAgICcgICAgICAnLFxuICAgICAgICAnICAgICAgJyxcbiAgICAgICAgJyAgICAgIHdvcmtmbG93SWQgPSByZXN1bHQuaWQ7JyxcbiAgICAgICAgXCIgICAgICBjb25zb2xlLmxvZygnV29ya2Zsb3cgY3JlYXRlZCBzdWNjZXNzZnVsbHknKTtcIixcbiAgICAgICAgJyAgICB9JyxcbiAgICAgICAgJyAgICAnLFxuICAgICAgICAnICAgIC8vIEFjdGl2YXRlIHRoZSB3b3JrZmxvdycsXG4gICAgICAgICcgICAgY29uc3QgYWN0aXZhdGVSZXNwb25zZSA9IGF3YWl0IGZldGNoKFxcXFxgXFxcXCR7dXJsfS9hcGkvdjEvd29ya2Zsb3dzL1xcXFwke3dvcmtmbG93SWR9L2FjdGl2YXRlXFxcXGAsIHsnLFxuICAgICAgICBcIiAgICAgIG1ldGhvZDogJ1BPU1QnLFwiLFxuICAgICAgICAnICAgICAgaGVhZGVycywnLFxuICAgICAgICAnICAgIH0pOycsXG4gICAgICAgICcgICAgJyxcbiAgICAgICAgJyAgICBpZiAoIWFjdGl2YXRlUmVzcG9uc2Uub2spIHsnLFxuICAgICAgICAnICAgIGxldCByZXN1bHQgPSB7fTsnLFxuICAgICAgICAnICAgICAgaWYgKGFjdGl2YXRlUmVzcG9uc2Uuc3RhdHVzID09PSA0MDApIHsnLFxuICAgICAgICAnICAgICAgICByZXN1bHQgPSBhd2FpdCBhY3RpdmF0ZVJlc3BvbnNlLmpzb24oKTsnLFxuICAgICAgICAnICAgICAgfScsXG4gICAgICAgICcgICAgICBjb25zb2xlLndhcm4oXFxcXGBBY3RpdmF0aW9uIGZhaWxlZDogXFxcXCR7YWN0aXZhdGVSZXNwb25zZS5zdGF0dXN9IFxcXFwke2FjdGl2YXRlUmVzcG9uc2Uuc3RhdHVzVGV4dH1cXFxcXFxcXCBcXFxcJHtKU09OLnN0cmluZ2lmeShyZXN1bHQpfSBcXFxcYCk7JyxcbiAgICAgICAgJyAgICB9IGVsc2UgeycsXG4gICAgICAgIFwiICAgICAgY29uc29sZS5sb2coJ1dvcmtmbG93IGFjdGl2YXRlZCBzdWNjZXNzZnVsbHknKTtcIixcbiAgICAgICAgJyAgICB9JyxcbiAgICAgICAgJycsXG4gICAgICAgICcgICAgLy8gRmV0Y2ggdGhlIHdvcmtmbG93IHRvIGV4dHJhY3Qgd2ViaG9vayBwYXRoJyxcbiAgICAgICAgXCIgICAgbGV0IHdlYmhvb2tQYXRoID0gXFxcXCdcXFxcJztcIixcbiAgICAgICAgJyAgICBjb25zdCBnZXRXb3JrZmxvd1Jlc3BvbnNlID0gYXdhaXQgZmV0Y2goXFxcXGBcXFxcJHt1cmx9L2FwaS92MS93b3JrZmxvd3MvXFxcXCR7d29ya2Zsb3dJZH1cXFxcYCwgeycsXG4gICAgICAgIFwiICAgICAgbWV0aG9kOiAnR0VUJyxcIixcbiAgICAgICAgJyAgICAgIGhlYWRlcnMsJyxcbiAgICAgICAgJyAgICB9KTsnLFxuICAgICAgICAnJyxcbiAgICAgICAgJyAgICBpZiAoZ2V0V29ya2Zsb3dSZXNwb25zZS5vaykgeycsXG4gICAgICAgICcgICAgICBjb25zdCB3b3JrZmxvdyA9IGF3YWl0IGdldFdvcmtmbG93UmVzcG9uc2UuanNvbigpOycsXG4gICAgICAgIFwiICAgICAgY29uc3Qgd2ViaG9va05vZGUgPSAod29ya2Zsb3cubm9kZXMgfHwgW10pLmZpbmQobiA9PiBuLnR5cGUgPT09ICduOG4tbm9kZXMtYmFzZS53ZWJob29rJyk7XCIsXG4gICAgICAgICcgICAgICBpZiAod2ViaG9va05vZGUgJiYgd2ViaG9va05vZGUucGFyYW1ldGVycyAmJiB3ZWJob29rTm9kZS5wYXJhbWV0ZXJzLnBhdGgpIHsnLFxuICAgICAgICAnICAgICAgICB3ZWJob29rUGF0aCA9IHdlYmhvb2tOb2RlLnBhcmFtZXRlcnMucGF0aDsnLFxuICAgICAgICAnICAgICAgICBjb25zb2xlLmxvZyhcXFxcYEZvdW5kIHdlYmhvb2sgcGF0aDogXFxcXCR7d2ViaG9va1BhdGh9XFxcXGApOycsXG4gICAgICAgICcgICAgICB9JyxcbiAgICAgICAgJyAgICB9JyxcbiAgICAgICAgJycsXG4gICAgICAgICcgICAgcmV0dXJuIHsnLFxuICAgICAgICAnICAgICAgUGh5c2ljYWxSZXNvdXJjZUlkOiB3b3JrZmxvd0lkLCcsXG4gICAgICAgICcgICAgICBEYXRhOiB7IFdvcmtmbG93SWQ6IHdvcmtmbG93SWQsIFdlYmhvb2tQYXRoOiB3ZWJob29rUGF0aCB9JyxcbiAgICAgICAgJyAgICB9OycsXG4gICAgICAgICcgIH0gY2F0Y2ggKGVycm9yKSB7JyxcbiAgICAgICAgXCIgICAgY29uc29sZS5lcnJvcignRXJyb3I6JywgZXJyb3IpO1wiLFxuICAgICAgICAnICAgIHJldHVybiB7JyxcbiAgICAgICAgXCIgICAgICAgUGh5c2ljYWxSZXNvdXJjZUlkOiBldmVudC5QaHlzaWNhbFJlc291cmNlSWQgfHwgJ3dvcmtmbG93LXVwbG9hZGVyJyxcIixcbiAgICAgICAgXCIgICAgICAgRGF0YTogeyBFcnJvcjogU3RyaW5nKGVycm9yKSwgV2ViaG9va1BhdGg6ICd1bmF2YWlsYWJsZScgfVwiLFxuICAgICAgICAnICAgIH07JyxcbiAgICAgICAgJyAgfScsXG4gICAgICAgICd9OycsXG4gICAgICAgICcgICAgICBgKSwnLFxuICAgICAgICAnICAgICAgZW52aXJvbm1lbnQ6IHsnLFxuICAgICAgICAnICAgICAgICBTRUNSRVRfQVJOOiBwcm9wcy5zZWNyZXQuc2VjcmV0QXJuLCcsXG4gICAgICAgICcgICAgICB9LCcsXG4gICAgICAgICcgICAgfSk7JyxcbiAgICAgICAgJycsXG4gICAgICAgICcgICAgcHJvcHMuc2VjcmV0LmdyYW50UmVhZChsYW1iZGEpOycsXG4gICAgICAgICcnLFxuICAgICAgICAnJyxcbiAgICAgICAgJyAgICAvLyBHcmFudCBLTVMgZGVjcnlwdCBwZXJtaXNzaW9ucyBmb3IgdGhlIHNlY3JldCcsXG4gICAgICAgICcgICAgaWYgKHByb3BzLnNlY3JldC5lbmNyeXB0aW9uS2V5KSB7JyxcbiAgICAgICAgXCIgICAgICBjb25zb2xlLmxvZygnZW5jcnlwdGlvbiBrZXkgZm91bmQsIGdyYW50aW5nIGRlY3J5cHQgcGVybWlzc2lvbnMnKTtcIixcbiAgICAgICAgJyAgICAgIHByb3BzLnNlY3JldC5lbmNyeXB0aW9uS2V5LmdyYW50RGVjcnlwdChsYW1iZGEpOycsXG4gICAgICAgICcnLFxuICAgICAgICAnICAgIH0gZWxzZSB7JyxcbiAgICAgICAgJyAgICAgIGxhbWJkYS5yb2xlPy5hZGRUb1ByaW5jaXBhbFBvbGljeShuZXcgUG9saWN5U3RhdGVtZW50KHsnLFxuICAgICAgICAnICAgICAgICBlZmZlY3Q6IEVmZmVjdC5BTExPVywnLFxuICAgICAgICAnICAgICAgICBhY3Rpb25zOiBbJyxcbiAgICAgICAgXCIgICAgICAgICAgJ2ttczpEZWNyeXB0JyxcIixcbiAgICAgICAgXCIgICAgICAgICAgJ2ttczpEZXNjcmliZUtleScsXCIsXG4gICAgICAgICcgICAgICAgIF0sJyxcbiAgICAgICAgJyAgICAgICAgcmVzb3VyY2VzOiBbQXJuLmZvcm1hdCh7JyxcbiAgICAgICAgXCIgICAgICAgICAgc2VydmljZTogJ2ttcycsXCIsXG4gICAgICAgIFwiICAgICAgICAgIHJlc291cmNlOiAna2V5JyxcIixcbiAgICAgICAgXCIgICAgICAgICAgcmVzb3VyY2VOYW1lOiAnYTY5OWY4NTMtMDQ3NS00ZDRkLWI4YWItYmU4ODFkNjY2MjRmJyxcIixcbiAgICAgICAgXCIgICAgICAgICAgcGFydGl0aW9uOiAnYXdzJyxcIixcbiAgICAgICAgJyAgICAgICAgICByZWdpb246IFN0YWNrLm9mKHRoaXMpLnJlZ2lvbiwnLFxuICAgICAgICBcIiAgICAgICAgICBhY2NvdW50OiAnNTgxNTE0NjcyMzY3JyxcIixcbiAgICAgICAgJycsXG4gICAgICAgICcgICAgICAgIH0pXSwnLFxuICAgICAgICAnICAgICAgfSkpOycsXG4gICAgICAgICcgICAgfScsXG4gICAgICAgICcnLFxuICAgICAgICBcIiAgICBjb25zdCBwcm92aWRlciA9IG5ldyBQcm92aWRlcih0aGlzLCAnUHJvdmlkZXInLCB7XCIsXG4gICAgICAgICcgICAgICBvbkV2ZW50SGFuZGxlcjogbGFtYmRhLCcsXG4gICAgICAgICcgICAgfSk7JyxcbiAgICAgICAgJycsXG4gICAgICAgIFwiICAgIGNvbnN0IGN1c3RvbVJlc291cmNlID0gbmV3IEN1c3RvbVJlc291cmNlKHRoaXMsICdSZXNvdXJjZScsIHtcIixcbiAgICAgICAgJyAgICAgIHNlcnZpY2VUb2tlbjogcHJvdmlkZXIuc2VydmljZVRva2VuLCcsXG4gICAgICAgICcgICAgICBwcm9wZXJ0aWVzOiB7JyxcbiAgICAgICAgJyAgICAgICAgQ29udGVudDogbmV3Q29udGVudHMsJyxcbiAgICAgICAgJyAgICAgIH0sJyxcbiAgICAgICAgJyAgICB9KTsnLFxuICAgICAgICAnJyxcbiAgICAgICAgXCIgICAgdGhpcy53ZWJob29rUGF0aCA9IGN1c3RvbVJlc291cmNlLmdldEF0dCgnV2ViaG9va1BhdGgnKTtcIixcbiAgICAgICAgJycsXG4gICAgICAgICcgICAgLy8gT3V0cHV0IHRoZSBMYW1iZGEgZXhlY3V0aW9uIHJvbGUgQVJOIGZvciBjcm9zcy1hY2NvdW50IEtNUyBwZXJtaXNzaW9ucycsXG4gICAgICAgIFwiICAgIG5ldyBDZm5PdXRwdXQodGhpcywgJ0xhbWJkYUV4ZWN1dGlvblJvbGVBcm4nLCB7XCIsXG4gICAgICAgICcgICAgICB2YWx1ZTogbGFtYmRhLnJvbGUhLnJvbGVBcm4sJyxcbiAgICAgICAgXCIgICAgICBkZXNjcmlwdGlvbjogJ0xhbWJkYSBleGVjdXRpb24gcm9sZSBBUk4gLSBhZGQgdGhpcyB0byBLTVMga2V5IHBvbGljeSBpbiBvdGhlciBhY2NvdW50JyxcIixcbiAgICAgICAgJyAgICB9KTsnLFxuICAgICAgICAnICB9JyxcbiAgICAgICAgJycsXG4gICAgICAgICcnLFxuICAgICAgICAnfScsXG4gICAgICAgICcnLFxuICAgICAgICAnZXhwb3J0IGZ1bmN0aW9uIHJlcGxhY2VGaWVsZEJ5Tm9kZU5hbWUoJyxcbiAgICAgICAgJyAgb2JqOiBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPiwnLFxuICAgICAgICAnICBub2RlTmFtZTogc3RyaW5nLCcsXG4gICAgICAgICcgIGZpZWxkUGF0aDogc3RyaW5nLCAvLyBkb3Qtbm90YXRpb24gcmVsYXRpdmUgdG8gdGhlIG5vZGUsIGUuZy4gXCJwYXJhbWV0ZXJzLnVybFwiJyxcbiAgICAgICAgJyAgbmV3VmFsdWU6IHVua25vd24sJyxcbiAgICAgICAgJyk6IHZvaWQgeycsXG4gICAgICAgICcgIGNvbnN0IG5vZGVzID0gSlNPTlBhdGgoeycsXG4gICAgICAgIFwiICAgIHBhdGg6IGAkLm5vZGVzWz8oQC5uYW1lPT0nJHtub2RlTmFtZX0nKV1gLFwiLFxuICAgICAgICAnICAgIGpzb246IG9iaiwnLFxuICAgICAgICBcIiAgICByZXN1bHRUeXBlOiAndmFsdWUnLFwiLFxuICAgICAgICAnICB9KTsnLFxuICAgICAgICAnJyxcbiAgICAgICAgJyAgZm9yIChjb25zdCBub2RlIG9mIG5vZGVzKSB7JyxcbiAgICAgICAgJyAgICBzZXROZXN0ZWRWYWx1ZShub2RlLCBmaWVsZFBhdGgsIG5ld1ZhbHVlKTsnLFxuICAgICAgICAnICB9JyxcbiAgICAgICAgJ30nLFxuICAgICAgICAnJyxcbiAgICAgICAgJ2Z1bmN0aW9uIHNldE5lc3RlZFZhbHVlKG9iajogYW55LCBkb3RQYXRoOiBzdHJpbmcsIHZhbHVlOiB1bmtub3duKTogdm9pZCB7JyxcbiAgICAgICAgXCIgIGNvbnN0IGtleXMgPSBkb3RQYXRoLnNwbGl0KCcuJyk7XCIsXG4gICAgICAgICcgIGxldCBjdXJyZW50ID0gb2JqOycsXG4gICAgICAgICcgIGZvciAobGV0IGkgPSAwOyBpIDwga2V5cy5sZW5ndGggLSAxOyBpKyspIHsnLFxuICAgICAgICAnICAgIGN1cnJlbnQgPSBjdXJyZW50W2tleXNbaV1dOycsXG4gICAgICAgICcgIH0nLFxuICAgICAgICAnICBjdXJyZW50W2tleXNba2V5cy5sZW5ndGggLSAxXV0gPSB2YWx1ZTsnLFxuICAgICAgICAnfScsXG4gICAgICAgICcnLFxuICAgICAgXSxcbiAgICB9KTtcbiAgfVxufVxuIl19
|