@aws-solutions-constructs/aws-cloudfront-s3 2.98.0 → 2.100.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.
|
@@ -0,0 +1,859 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance
|
|
6
|
+
* with the License. A copy of the License is located at
|
|
7
|
+
*
|
|
8
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
*
|
|
10
|
+
* or in the 'license' file accompanying this file. This file is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES
|
|
11
|
+
* OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific language governing permissions
|
|
12
|
+
* and limitations under the License.
|
|
13
|
+
*/
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
const assertions_1 = require("aws-cdk-lib/assertions");
|
|
16
|
+
const s3 = require("aws-cdk-lib/aws-s3");
|
|
17
|
+
const cdk = require("aws-cdk-lib");
|
|
18
|
+
const aws_cdk_lib_1 = require("aws-cdk-lib");
|
|
19
|
+
const lib_1 = require("../lib");
|
|
20
|
+
const acm = require("aws-cdk-lib/aws-certificatemanager");
|
|
21
|
+
const defaults = require("@aws-solutions-constructs/core");
|
|
22
|
+
const aws_kms_1 = require("aws-cdk-lib/aws-kms");
|
|
23
|
+
const origins = require("aws-cdk-lib/aws-cloudfront-origins");
|
|
24
|
+
const aws_cloudfront_1 = require("aws-cdk-lib/aws-cloudfront");
|
|
25
|
+
function deploy(stack, props) {
|
|
26
|
+
return new lib_1.CloudFrontToS3(stack, 'test-cloudfront-s3', {
|
|
27
|
+
bucketProps: {
|
|
28
|
+
removalPolicy: cdk.RemovalPolicy.DESTROY,
|
|
29
|
+
},
|
|
30
|
+
...props
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
test('construct defaults set properties correctly', () => {
|
|
34
|
+
const stack = new cdk.Stack();
|
|
35
|
+
const construct = new lib_1.CloudFrontToS3(stack, 'test-cloudfront-s3', {});
|
|
36
|
+
expect(construct.cloudFrontWebDistribution).toBeDefined();
|
|
37
|
+
expect(construct.cloudFrontFunction).toBeDefined();
|
|
38
|
+
expect(construct.cloudFrontLoggingBucket).toBeDefined();
|
|
39
|
+
expect(construct.s3Bucket).toBeDefined();
|
|
40
|
+
expect(construct.s3LoggingBucket).toBeDefined();
|
|
41
|
+
expect(construct.s3BucketInterface).toBeDefined();
|
|
42
|
+
expect(construct.cloudFrontLoggingBucketAccessLogBucket).toBeDefined();
|
|
43
|
+
expect(construct.originAccessControl).toBeDefined();
|
|
44
|
+
});
|
|
45
|
+
test('check s3Bucket default encryption', () => {
|
|
46
|
+
const stack = new cdk.Stack();
|
|
47
|
+
deploy(stack);
|
|
48
|
+
const template = assertions_1.Template.fromStack(stack);
|
|
49
|
+
template.hasResourceProperties('AWS::S3::Bucket', {
|
|
50
|
+
BucketEncryption: {
|
|
51
|
+
ServerSideEncryptionConfiguration: [{
|
|
52
|
+
ServerSideEncryptionByDefault: {
|
|
53
|
+
SSEAlgorithm: "AES256"
|
|
54
|
+
}
|
|
55
|
+
}]
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
});
|
|
59
|
+
test('check s3Bucket public access block configuration', () => {
|
|
60
|
+
const stack = new cdk.Stack();
|
|
61
|
+
deploy(stack);
|
|
62
|
+
const template = assertions_1.Template.fromStack(stack);
|
|
63
|
+
template.hasResourceProperties('AWS::S3::Bucket', {
|
|
64
|
+
PublicAccessBlockConfiguration: {
|
|
65
|
+
BlockPublicAcls: true,
|
|
66
|
+
BlockPublicPolicy: true,
|
|
67
|
+
IgnorePublicAcls: true,
|
|
68
|
+
RestrictPublicBuckets: true
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
});
|
|
72
|
+
test('test s3Bucket override publicAccessBlockConfiguration', () => {
|
|
73
|
+
const stack = new cdk.Stack();
|
|
74
|
+
const props = {
|
|
75
|
+
bucketProps: {
|
|
76
|
+
blockPublicAccess: {
|
|
77
|
+
blockPublicAcls: false,
|
|
78
|
+
blockPublicPolicy: true,
|
|
79
|
+
ignorePublicAcls: false,
|
|
80
|
+
restrictPublicBuckets: true
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
new lib_1.CloudFrontToS3(stack, 'test-cloudfront-s3', props);
|
|
85
|
+
const template = assertions_1.Template.fromStack(stack);
|
|
86
|
+
template.hasResourceProperties("AWS::S3::Bucket", {
|
|
87
|
+
PublicAccessBlockConfiguration: {
|
|
88
|
+
BlockPublicAcls: false,
|
|
89
|
+
BlockPublicPolicy: true,
|
|
90
|
+
IgnorePublicAcls: false,
|
|
91
|
+
RestrictPublicBuckets: true
|
|
92
|
+
},
|
|
93
|
+
});
|
|
94
|
+
});
|
|
95
|
+
test('check existing bucket', () => {
|
|
96
|
+
const stack = new cdk.Stack();
|
|
97
|
+
const existingBucket = new s3.Bucket(stack, 'my-bucket', {
|
|
98
|
+
bucketName: 'my-bucket'
|
|
99
|
+
});
|
|
100
|
+
const props = {
|
|
101
|
+
existingBucketObj: existingBucket
|
|
102
|
+
};
|
|
103
|
+
new lib_1.CloudFrontToS3(stack, 'test-cloudfront-s3', props);
|
|
104
|
+
const template = assertions_1.Template.fromStack(stack);
|
|
105
|
+
template.hasResourceProperties("AWS::S3::Bucket", {
|
|
106
|
+
BucketName: "my-bucket"
|
|
107
|
+
});
|
|
108
|
+
});
|
|
109
|
+
test('check exception for Missing existingObj from props for deploy = false', () => {
|
|
110
|
+
const stack = new cdk.Stack();
|
|
111
|
+
try {
|
|
112
|
+
new lib_1.CloudFrontToS3(stack, 'test-cloudfront-s3', {});
|
|
113
|
+
}
|
|
114
|
+
catch (e) {
|
|
115
|
+
expect(e).toBeInstanceOf(Error);
|
|
116
|
+
}
|
|
117
|
+
});
|
|
118
|
+
test('check properties', () => {
|
|
119
|
+
const stack = new cdk.Stack();
|
|
120
|
+
const construct = deploy(stack);
|
|
121
|
+
expect(construct.cloudFrontWebDistribution).toBeDefined();
|
|
122
|
+
expect(construct.s3Bucket).toBeDefined();
|
|
123
|
+
});
|
|
124
|
+
test("Confirm CheckS3Props is called", () => {
|
|
125
|
+
// Stack
|
|
126
|
+
const stack = new cdk.Stack();
|
|
127
|
+
const testBucket = new s3.Bucket(stack, 'test-bucket', {});
|
|
128
|
+
const app = () => {
|
|
129
|
+
// Helper declaration
|
|
130
|
+
new lib_1.CloudFrontToS3(stack, "bad-s3-args", {
|
|
131
|
+
existingBucketObj: testBucket,
|
|
132
|
+
bucketProps: {
|
|
133
|
+
removalPolicy: aws_cdk_lib_1.RemovalPolicy.DESTROY
|
|
134
|
+
},
|
|
135
|
+
});
|
|
136
|
+
};
|
|
137
|
+
// Assertion
|
|
138
|
+
expect(app).toThrow('Error - Either provide bucketProps or existingBucketObj, but not both.\n');
|
|
139
|
+
});
|
|
140
|
+
test("Test existingBucketObj", () => {
|
|
141
|
+
// Stack
|
|
142
|
+
const stack = new cdk.Stack();
|
|
143
|
+
const construct = new lib_1.CloudFrontToS3(stack, "existingIBucket", {
|
|
144
|
+
existingBucketObj: s3.Bucket.fromBucketName(stack, 'mybucket', 'mybucket')
|
|
145
|
+
});
|
|
146
|
+
// Assertion
|
|
147
|
+
expect(construct.cloudFrontWebDistribution).toBeDefined();
|
|
148
|
+
const template = assertions_1.Template.fromStack(stack);
|
|
149
|
+
template.hasResourceProperties("AWS::CloudFront::Distribution", {
|
|
150
|
+
DistributionConfig: {
|
|
151
|
+
Origins: [
|
|
152
|
+
{
|
|
153
|
+
DomainName: {
|
|
154
|
+
"Fn::Join": [
|
|
155
|
+
"",
|
|
156
|
+
[
|
|
157
|
+
"mybucket.s3.",
|
|
158
|
+
{
|
|
159
|
+
Ref: "AWS::Region"
|
|
160
|
+
},
|
|
161
|
+
".",
|
|
162
|
+
{
|
|
163
|
+
Ref: "AWS::URLSuffix"
|
|
164
|
+
}
|
|
165
|
+
]
|
|
166
|
+
]
|
|
167
|
+
},
|
|
168
|
+
Id: "existingIBucketCloudFrontDistributionOrigin1D5849125",
|
|
169
|
+
OriginAccessControlId: { "Fn::GetAtt": ["existingIBucketCloudFrontOacEB42E98F", "Id"] },
|
|
170
|
+
S3OriginConfig: {}
|
|
171
|
+
}
|
|
172
|
+
]
|
|
173
|
+
}
|
|
174
|
+
});
|
|
175
|
+
});
|
|
176
|
+
test('test cloudfront with custom domain names', () => {
|
|
177
|
+
const stack = new cdk.Stack();
|
|
178
|
+
const certificate = acm.Certificate.fromCertificateArn(stack, 'Cert', 'arn:${Aws.PARTITION}:acm:us-east-1:123456789012:certificate/11112222-3333-1234-1234-123456789012');
|
|
179
|
+
const props = {
|
|
180
|
+
cloudFrontDistributionProps: {
|
|
181
|
+
domainNames: ['mydomains'],
|
|
182
|
+
certificate
|
|
183
|
+
}
|
|
184
|
+
};
|
|
185
|
+
new lib_1.CloudFrontToS3(stack, 'test-cloudfront-s3', props);
|
|
186
|
+
const template = assertions_1.Template.fromStack(stack);
|
|
187
|
+
template.hasResourceProperties("AWS::CloudFront::Distribution", {
|
|
188
|
+
DistributionConfig: {
|
|
189
|
+
Aliases: [
|
|
190
|
+
"mydomains"
|
|
191
|
+
]
|
|
192
|
+
}
|
|
193
|
+
});
|
|
194
|
+
});
|
|
195
|
+
test('Cloudfront logging bucket with destroy removal policy and auto delete objects', () => {
|
|
196
|
+
const stack = new cdk.Stack();
|
|
197
|
+
const cloudfrontLogBucketName = 'cf-log-bucket';
|
|
198
|
+
new lib_1.CloudFrontToS3(stack, 'cloudfront-s3', {
|
|
199
|
+
cloudFrontLoggingBucketProps: {
|
|
200
|
+
removalPolicy: cdk.RemovalPolicy.DESTROY,
|
|
201
|
+
autoDeleteObjects: true,
|
|
202
|
+
bucketName: cloudfrontLogBucketName
|
|
203
|
+
}
|
|
204
|
+
});
|
|
205
|
+
const template = assertions_1.Template.fromStack(stack);
|
|
206
|
+
template.hasResourceProperties("AWS::S3::Bucket", {
|
|
207
|
+
OwnershipControls: { Rules: [{ ObjectOwnership: "ObjectWriter" }] },
|
|
208
|
+
BucketName: cloudfrontLogBucketName,
|
|
209
|
+
});
|
|
210
|
+
template.hasResourceProperties("Custom::S3AutoDeleteObjects", {
|
|
211
|
+
ServiceToken: {
|
|
212
|
+
"Fn::GetAtt": [
|
|
213
|
+
"CustomS3AutoDeleteObjectsCustomResourceProviderHandler9D90184F",
|
|
214
|
+
"Arn"
|
|
215
|
+
]
|
|
216
|
+
},
|
|
217
|
+
BucketName: {
|
|
218
|
+
Ref: "cloudfronts3CloudfrontLoggingBucket5B845143"
|
|
219
|
+
}
|
|
220
|
+
});
|
|
221
|
+
});
|
|
222
|
+
test('s3 bucket with one content bucket and no access logging of CONTENT bucket', () => {
|
|
223
|
+
const stack = new cdk.Stack();
|
|
224
|
+
const construct = new lib_1.CloudFrontToS3(stack, 'cloudfront-s3', {
|
|
225
|
+
bucketProps: {
|
|
226
|
+
removalPolicy: cdk.RemovalPolicy.DESTROY,
|
|
227
|
+
},
|
|
228
|
+
logS3AccessLogs: false
|
|
229
|
+
});
|
|
230
|
+
const template = assertions_1.Template.fromStack(stack);
|
|
231
|
+
// Content bucket+Cloudfront Logs bucket+
|
|
232
|
+
// Access Log bucket for Cloudfront Logs bucket = 3 buckets
|
|
233
|
+
template.resourceCountIs("AWS::S3::Bucket", 3);
|
|
234
|
+
expect(construct.s3LoggingBucket).toEqual(undefined);
|
|
235
|
+
});
|
|
236
|
+
test('CloudFront origin path present when provided', () => {
|
|
237
|
+
const stack = new cdk.Stack();
|
|
238
|
+
new lib_1.CloudFrontToS3(stack, 'cloudfront-s3', {
|
|
239
|
+
originPath: '/testPath'
|
|
240
|
+
});
|
|
241
|
+
const template = assertions_1.Template.fromStack(stack);
|
|
242
|
+
template.hasResourceProperties("AWS::CloudFront::Distribution", {
|
|
243
|
+
DistributionConfig: {
|
|
244
|
+
Origins: [
|
|
245
|
+
{
|
|
246
|
+
OriginPath: "/testPath",
|
|
247
|
+
}
|
|
248
|
+
]
|
|
249
|
+
}
|
|
250
|
+
});
|
|
251
|
+
});
|
|
252
|
+
test('CloudFront origin path should not be present if not provided', () => {
|
|
253
|
+
const stack = new cdk.Stack();
|
|
254
|
+
new lib_1.CloudFrontToS3(stack, 'cloudfront-s3', {});
|
|
255
|
+
defaults.expectNonexistence(stack, "AWS::CloudFront::Distribution", {
|
|
256
|
+
DistributionConfig: {
|
|
257
|
+
Origins: [
|
|
258
|
+
{
|
|
259
|
+
OriginPath: "/testPath",
|
|
260
|
+
}
|
|
261
|
+
]
|
|
262
|
+
}
|
|
263
|
+
});
|
|
264
|
+
});
|
|
265
|
+
test('Test the deployment with securityHeadersBehavior instead of HTTP security headers', () => {
|
|
266
|
+
// Initial setup
|
|
267
|
+
const stack = new aws_cdk_lib_1.Stack();
|
|
268
|
+
const cloudFrontToS3 = new lib_1.CloudFrontToS3(stack, 'test-cloudfront-s3', {
|
|
269
|
+
insertHttpSecurityHeaders: false,
|
|
270
|
+
responseHeadersPolicyProps: {
|
|
271
|
+
securityHeadersBehavior: {
|
|
272
|
+
strictTransportSecurity: {
|
|
273
|
+
accessControlMaxAge: aws_cdk_lib_1.Duration.seconds(63072),
|
|
274
|
+
includeSubdomains: true,
|
|
275
|
+
override: true,
|
|
276
|
+
preload: true
|
|
277
|
+
},
|
|
278
|
+
contentSecurityPolicy: {
|
|
279
|
+
contentSecurityPolicy: "upgrade-insecure-requests; default-src 'none';",
|
|
280
|
+
override: true
|
|
281
|
+
},
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
});
|
|
285
|
+
// Assertion
|
|
286
|
+
const template = assertions_1.Template.fromStack(stack);
|
|
287
|
+
template.hasResourceProperties("AWS::CloudFront::ResponseHeadersPolicy", {
|
|
288
|
+
ResponseHeadersPolicyConfig: {
|
|
289
|
+
SecurityHeadersConfig: {
|
|
290
|
+
ContentSecurityPolicy: {
|
|
291
|
+
ContentSecurityPolicy: "upgrade-insecure-requests; default-src 'none';",
|
|
292
|
+
Override: true
|
|
293
|
+
},
|
|
294
|
+
StrictTransportSecurity: {
|
|
295
|
+
AccessControlMaxAgeSec: 63072,
|
|
296
|
+
IncludeSubdomains: true,
|
|
297
|
+
Override: true,
|
|
298
|
+
Preload: true
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
});
|
|
303
|
+
expect(cloudFrontToS3.cloudFrontFunction).toEqual(undefined);
|
|
304
|
+
});
|
|
305
|
+
test("throw exception if insertHttpSecurityHeaders and responseHeadersPolicyProps are provided", () => {
|
|
306
|
+
const stack = new cdk.Stack();
|
|
307
|
+
expect(() => {
|
|
308
|
+
new lib_1.CloudFrontToS3(stack, "test-cloudfront-s3", {
|
|
309
|
+
insertHttpSecurityHeaders: true,
|
|
310
|
+
responseHeadersPolicyProps: {
|
|
311
|
+
securityHeadersBehavior: {
|
|
312
|
+
strictTransportSecurity: {
|
|
313
|
+
accessControlMaxAge: aws_cdk_lib_1.Duration.seconds(63072),
|
|
314
|
+
includeSubdomains: true,
|
|
315
|
+
override: false,
|
|
316
|
+
preload: true
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
});
|
|
321
|
+
}).toThrow();
|
|
322
|
+
});
|
|
323
|
+
test("Confirm CheckCloudFrontProps is being called", () => {
|
|
324
|
+
const stack = new cdk.Stack();
|
|
325
|
+
expect(() => {
|
|
326
|
+
new lib_1.CloudFrontToS3(stack, "test-cloudfront-apigateway", {
|
|
327
|
+
insertHttpSecurityHeaders: true,
|
|
328
|
+
responseHeadersPolicyProps: {
|
|
329
|
+
securityHeadersBehavior: {
|
|
330
|
+
strictTransportSecurity: {
|
|
331
|
+
accessControlMaxAge: aws_cdk_lib_1.Duration.seconds(63072),
|
|
332
|
+
includeSubdomains: true,
|
|
333
|
+
override: false,
|
|
334
|
+
preload: true
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
});
|
|
339
|
+
}).toThrow('responseHeadersPolicyProps.securityHeadersBehavior can only be passed if httpSecurityHeaders is set to `false`.');
|
|
340
|
+
});
|
|
341
|
+
test("Custom resource is provisioned if encryption key is provided as bucketProp", () => {
|
|
342
|
+
const stack = new cdk.Stack();
|
|
343
|
+
const encryptionKey = new aws_kms_1.Key(stack, 'cmkKey', {
|
|
344
|
+
enableKeyRotation: true,
|
|
345
|
+
removalPolicy: aws_cdk_lib_1.RemovalPolicy.DESTROY
|
|
346
|
+
});
|
|
347
|
+
deploy(stack, {
|
|
348
|
+
bucketProps: {
|
|
349
|
+
encryptionKey,
|
|
350
|
+
encryption: s3.BucketEncryption.KMS
|
|
351
|
+
}
|
|
352
|
+
});
|
|
353
|
+
const template = assertions_1.Template.fromStack(stack);
|
|
354
|
+
// 2 Functions - our custom resource and a function created by the CDK
|
|
355
|
+
template.resourceCountIs('AWS::Lambda::Function', 2);
|
|
356
|
+
template.hasResourceProperties('AWS::Lambda::Function', {
|
|
357
|
+
Description: "Custom resource function that updates a provided key policy to allow CloudFront access.",
|
|
358
|
+
Role: {
|
|
359
|
+
"Fn::GetAtt": ["testcloudfronts3LambdaFunctionServiceRole2A43EA92", "Arn"]
|
|
360
|
+
}
|
|
361
|
+
});
|
|
362
|
+
});
|
|
363
|
+
test("Custom resource is provisioned if CMK was used to encrypt an existing bucket", () => {
|
|
364
|
+
const stack = new cdk.Stack();
|
|
365
|
+
const encryptionKey = new aws_kms_1.Key(stack, 'cmkKey', {
|
|
366
|
+
enableKeyRotation: true,
|
|
367
|
+
removalPolicy: aws_cdk_lib_1.RemovalPolicy.DESTROY
|
|
368
|
+
});
|
|
369
|
+
const existingBucketObj = defaults.buildS3Bucket(stack, {
|
|
370
|
+
bucketProps: {
|
|
371
|
+
encryption: s3.BucketEncryption.KMS,
|
|
372
|
+
encryptionKey
|
|
373
|
+
}
|
|
374
|
+
}, 'existing-s3-bucket-encrypted-with-cmk').bucket;
|
|
375
|
+
new lib_1.CloudFrontToS3(stack, 'test-cloudfront-s3', {
|
|
376
|
+
existingBucketObj
|
|
377
|
+
});
|
|
378
|
+
const template = assertions_1.Template.fromStack(stack);
|
|
379
|
+
// 2 Functions - our custom resource and a function created by the CDK
|
|
380
|
+
template.resourceCountIs('AWS::Lambda::Function', 2);
|
|
381
|
+
// ensure that our Function has the correct role attached
|
|
382
|
+
template.hasResourceProperties('AWS::Lambda::Function', {
|
|
383
|
+
Description: "Custom resource function that updates a provided key policy to allow CloudFront access.",
|
|
384
|
+
Role: {
|
|
385
|
+
"Fn::GetAtt": ["testcloudfronts3LambdaFunctionServiceRole2A43EA92", "Arn"]
|
|
386
|
+
}
|
|
387
|
+
});
|
|
388
|
+
});
|
|
389
|
+
test("Custom resource is not provisioned if encryption key is not provided as bucketProp", () => {
|
|
390
|
+
const stack = new cdk.Stack();
|
|
391
|
+
deploy(stack);
|
|
392
|
+
const template = assertions_1.Template.fromStack(stack);
|
|
393
|
+
template.resourceCountIs('AWS::Lambda::Function', 0);
|
|
394
|
+
});
|
|
395
|
+
test("Custom resource is not provisioned if CMK was not used to encrypt an existing bucket", () => {
|
|
396
|
+
const stack = new cdk.Stack();
|
|
397
|
+
const existingBucketObj = defaults.buildS3Bucket(stack, {}, 'existing-s3-bucket-encrypted-with-cmk').bucket;
|
|
398
|
+
new lib_1.CloudFrontToS3(stack, 'test-cloudfront-s3', {
|
|
399
|
+
existingBucketObj
|
|
400
|
+
});
|
|
401
|
+
const template = assertions_1.Template.fromStack(stack);
|
|
402
|
+
template.resourceCountIs('AWS::Lambda::Function', 0);
|
|
403
|
+
});
|
|
404
|
+
test("HttpOrigin is provisioned if a static website bucket is used", () => {
|
|
405
|
+
const stack = new cdk.Stack();
|
|
406
|
+
const blockPublicAccess = false;
|
|
407
|
+
const props = {
|
|
408
|
+
bucketProps: {
|
|
409
|
+
enforceSSL: false,
|
|
410
|
+
publicReadAccess: true, // <-- required for isWebsite
|
|
411
|
+
blockPublicAccess: {
|
|
412
|
+
blockPublicAcls: blockPublicAccess,
|
|
413
|
+
restrictPublicBuckets: blockPublicAccess,
|
|
414
|
+
blockPublicPolicy: blockPublicAccess,
|
|
415
|
+
ignorePublicAcls: blockPublicAccess
|
|
416
|
+
},
|
|
417
|
+
websiteIndexDocument: "index.html" // <-- required for isWebsite
|
|
418
|
+
},
|
|
419
|
+
insertHttpSecurityHeaders: false
|
|
420
|
+
};
|
|
421
|
+
const construct = new lib_1.CloudFrontToS3(stack, 'test-cloudfront-s3', props);
|
|
422
|
+
const template = assertions_1.Template.fromStack(stack);
|
|
423
|
+
// Assert resources
|
|
424
|
+
template.resourceCountIs('AWS::CloudFront::OriginAccessControl', 0);
|
|
425
|
+
template.hasResourceProperties('AWS::CloudFront::Distribution', {
|
|
426
|
+
DistributionConfig: {
|
|
427
|
+
Origins: [
|
|
428
|
+
{
|
|
429
|
+
CustomOriginConfig: {
|
|
430
|
+
OriginProtocolPolicy: "http-only"
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
]
|
|
434
|
+
}
|
|
435
|
+
});
|
|
436
|
+
template.resourceCountIs('AWS::CloudFront::OriginAccessIdentity', 0);
|
|
437
|
+
// Assert pattern properties (output props)
|
|
438
|
+
expect(construct.originAccessControl).toBe(undefined);
|
|
439
|
+
});
|
|
440
|
+
test("OAC is provisioned in all other cases", () => {
|
|
441
|
+
const stack = new cdk.Stack();
|
|
442
|
+
const construct = new lib_1.CloudFrontToS3(stack, 'test-cloudfront-s3', {});
|
|
443
|
+
const template = assertions_1.Template.fromStack(stack);
|
|
444
|
+
// Assert resources
|
|
445
|
+
template.resourceCountIs('AWS::CloudFront::OriginAccessControl', 1);
|
|
446
|
+
template.resourceCountIs('AWS::CloudFront::OriginAccessIdentity', 0);
|
|
447
|
+
// Assert pattern properties (output props)
|
|
448
|
+
expect(construct.originAccessControl).not.toBe(undefined);
|
|
449
|
+
});
|
|
450
|
+
test("If a customer provides their own httpOrigin, or other origin type, use that one", () => {
|
|
451
|
+
const stack = new cdk.Stack();
|
|
452
|
+
const blockPublicAccess = false;
|
|
453
|
+
const props = {
|
|
454
|
+
bucketProps: {
|
|
455
|
+
enforceSSL: false,
|
|
456
|
+
publicReadAccess: true, // <-- required for isWebsite
|
|
457
|
+
blockPublicAccess: {
|
|
458
|
+
blockPublicAcls: blockPublicAccess,
|
|
459
|
+
restrictPublicBuckets: blockPublicAccess,
|
|
460
|
+
blockPublicPolicy: blockPublicAccess,
|
|
461
|
+
ignorePublicAcls: blockPublicAccess
|
|
462
|
+
},
|
|
463
|
+
websiteIndexDocument: "index.html" // <-- required for isWebsite
|
|
464
|
+
},
|
|
465
|
+
insertHttpSecurityHeaders: false,
|
|
466
|
+
cloudFrontDistributionProps: {
|
|
467
|
+
defaultBehavior: {
|
|
468
|
+
origin: new origins.HttpOrigin('example.com', {
|
|
469
|
+
originId: 'custom-http-origin-for-testing'
|
|
470
|
+
})
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
};
|
|
474
|
+
new lib_1.CloudFrontToS3(stack, 'test-cloudfront-s3', props);
|
|
475
|
+
const template = assertions_1.Template.fromStack(stack);
|
|
476
|
+
// Assert resources
|
|
477
|
+
template.hasResourceProperties('AWS::CloudFront::Distribution', {
|
|
478
|
+
DistributionConfig: {
|
|
479
|
+
Origins: [
|
|
480
|
+
{
|
|
481
|
+
DomainName: "example.com",
|
|
482
|
+
Id: "custom-http-origin-for-testing"
|
|
483
|
+
}
|
|
484
|
+
]
|
|
485
|
+
}
|
|
486
|
+
});
|
|
487
|
+
});
|
|
488
|
+
test('Test that we do not create an Access Log bucket for CF logs if one is provided', () => {
|
|
489
|
+
const stack = new cdk.Stack();
|
|
490
|
+
const cfS3AccessLogBucket = new s3.Bucket(stack, 'cf-s3-access-logs');
|
|
491
|
+
new lib_1.CloudFrontToS3(stack, 'test-cloudfront-s3', {
|
|
492
|
+
cloudFrontLoggingBucketProps: {
|
|
493
|
+
serverAccessLogsBucket: cfS3AccessLogBucket
|
|
494
|
+
}
|
|
495
|
+
});
|
|
496
|
+
const template = assertions_1.Template.fromStack(stack);
|
|
497
|
+
template.resourceCountIs("AWS::S3::Bucket", 4);
|
|
498
|
+
});
|
|
499
|
+
// =====================
|
|
500
|
+
// S3 Content Bucket Access Logs Bucket
|
|
501
|
+
// =====================
|
|
502
|
+
test('Providing loggingBucketProps and existingLoggingBucket is an error', () => {
|
|
503
|
+
const stack = new cdk.Stack();
|
|
504
|
+
const logBucket = new s3.Bucket(stack, 'log-bucket', {});
|
|
505
|
+
const app = () => {
|
|
506
|
+
new lib_1.CloudFrontToS3(stack, 'cloudfront-s3', {
|
|
507
|
+
bucketProps: {
|
|
508
|
+
serverAccessLogsBucket: logBucket,
|
|
509
|
+
},
|
|
510
|
+
loggingBucketProps: {
|
|
511
|
+
bucketName: 'anything'
|
|
512
|
+
}
|
|
513
|
+
});
|
|
514
|
+
};
|
|
515
|
+
expect(app).toThrow(/Error - bothlog bucket props and an existing log bucket were provided.\n/);
|
|
516
|
+
});
|
|
517
|
+
test('Providing existingLoggingBucket and logS3AccessLogs=false is an error', () => {
|
|
518
|
+
const stack = new cdk.Stack();
|
|
519
|
+
const logBucket = new s3.Bucket(stack, 'cloudfront-log-bucket', {});
|
|
520
|
+
const app = () => {
|
|
521
|
+
new lib_1.CloudFrontToS3(stack, 'cloudfront-s3', {
|
|
522
|
+
bucketProps: {
|
|
523
|
+
serverAccessLogsBucket: logBucket,
|
|
524
|
+
},
|
|
525
|
+
logS3AccessLogs: false
|
|
526
|
+
});
|
|
527
|
+
};
|
|
528
|
+
expect(app).toThrow(/Error - logS3AccessLogs is false, but a log bucket was provided in bucketProps.\n/);
|
|
529
|
+
});
|
|
530
|
+
test('Providing loggingBucketProps and logS3AccessLogs=false is an error', () => {
|
|
531
|
+
const stack = new cdk.Stack();
|
|
532
|
+
const app = () => {
|
|
533
|
+
new lib_1.CloudFrontToS3(stack, 'cloudfront-s3', {
|
|
534
|
+
loggingBucketProps: {
|
|
535
|
+
bucketName: 'anything'
|
|
536
|
+
},
|
|
537
|
+
logS3AccessLogs: false
|
|
538
|
+
});
|
|
539
|
+
};
|
|
540
|
+
// NOTE: This error is thrown by CheckS3Props(), not CheckConstructSpecificProps()
|
|
541
|
+
expect(app).toThrow(/Error - If logS3AccessLogs is false, supplying loggingBucketProps or existingLoggingBucketObj is invalid.\n/);
|
|
542
|
+
});
|
|
543
|
+
// test('No new loggingBucket is created if existingLoggingBucket is supplied', () => {
|
|
544
|
+
test('loggingBucketProps is supplied is integrated into architecture correctly', () => {
|
|
545
|
+
const stack = new cdk.Stack();
|
|
546
|
+
const testName = "test-name";
|
|
547
|
+
const construct = new lib_1.CloudFrontToS3(stack, 'cloudfront-s3', {
|
|
548
|
+
bucketProps: {
|
|
549
|
+
removalPolicy: cdk.RemovalPolicy.DESTROY,
|
|
550
|
+
},
|
|
551
|
+
loggingBucketProps: {
|
|
552
|
+
removalPolicy: cdk.RemovalPolicy.DESTROY,
|
|
553
|
+
autoDeleteObjects: true,
|
|
554
|
+
bucketName: testName
|
|
555
|
+
}
|
|
556
|
+
});
|
|
557
|
+
expect(construct.s3LoggingBucket).toBeDefined();
|
|
558
|
+
const template = assertions_1.Template.fromStack(stack);
|
|
559
|
+
template.resourceCountIs("AWS::S3::Bucket", 4);
|
|
560
|
+
template.hasResourceProperties("AWS::S3::Bucket", {
|
|
561
|
+
BucketName: testName
|
|
562
|
+
});
|
|
563
|
+
template.hasResourceProperties("AWS::S3::Bucket", {
|
|
564
|
+
LoggingConfiguration: {
|
|
565
|
+
DestinationBucketName: {
|
|
566
|
+
Ref: "cloudfronts3S3LoggingBucket52EEB708"
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
});
|
|
570
|
+
});
|
|
571
|
+
test('bucketProps:serverAccessLogsBucket is supplied is integrated into architecture correctly', () => {
|
|
572
|
+
const testName = 'some-name';
|
|
573
|
+
const stack = new cdk.Stack();
|
|
574
|
+
const logBucket = new s3.Bucket(stack, 'test-log', {
|
|
575
|
+
bucketName: testName,
|
|
576
|
+
});
|
|
577
|
+
const construct = new lib_1.CloudFrontToS3(stack, 'cloudfront-s3', {
|
|
578
|
+
bucketProps: {
|
|
579
|
+
serverAccessLogsBucket: logBucket,
|
|
580
|
+
},
|
|
581
|
+
});
|
|
582
|
+
expect(construct.s3LoggingBucket).toBeDefined();
|
|
583
|
+
const template = assertions_1.Template.fromStack(stack);
|
|
584
|
+
template.resourceCountIs("AWS::S3::Bucket", 4);
|
|
585
|
+
template.hasResourceProperties("AWS::S3::Bucket", {
|
|
586
|
+
LoggingConfiguration: {
|
|
587
|
+
DestinationBucketName: {
|
|
588
|
+
Ref: "testlogE88B4C6B"
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
});
|
|
592
|
+
});
|
|
593
|
+
// =====================
|
|
594
|
+
// CloudFront Log Bucket
|
|
595
|
+
// =====================
|
|
596
|
+
test('Providing cloudFrontLoggingBucketProps and a log bucket in cloudFrontDistrbutionProps is an error', () => {
|
|
597
|
+
const stack = new cdk.Stack();
|
|
598
|
+
const logBucket = new s3.Bucket(stack, 'cloudfront-log-bucket', {});
|
|
599
|
+
const app = () => {
|
|
600
|
+
new lib_1.CloudFrontToS3(stack, 'cloudfront-s3', {
|
|
601
|
+
cloudFrontDistributionProps: {
|
|
602
|
+
logBucket
|
|
603
|
+
},
|
|
604
|
+
cloudFrontLoggingBucketProps: {
|
|
605
|
+
removalPolicy: cdk.RemovalPolicy.DESTROY,
|
|
606
|
+
autoDeleteObjects: true
|
|
607
|
+
}
|
|
608
|
+
});
|
|
609
|
+
};
|
|
610
|
+
expect(app).toThrow();
|
|
611
|
+
});
|
|
612
|
+
test('cloudFrontLoggingBucketProps are used correctly', () => {
|
|
613
|
+
const stack = new cdk.Stack();
|
|
614
|
+
const testName = "test-name";
|
|
615
|
+
const construct = new lib_1.CloudFrontToS3(stack, 'cloudfront-s3', {
|
|
616
|
+
cloudFrontLoggingBucketProps: {
|
|
617
|
+
removalPolicy: cdk.RemovalPolicy.DESTROY,
|
|
618
|
+
autoDeleteObjects: true,
|
|
619
|
+
bucketName: testName
|
|
620
|
+
}
|
|
621
|
+
});
|
|
622
|
+
expect(construct.cloudFrontLoggingBucket).toBeDefined();
|
|
623
|
+
const template = assertions_1.Template.fromStack(stack);
|
|
624
|
+
template.resourceCountIs("AWS::S3::Bucket", 4);
|
|
625
|
+
template.hasResourceProperties("AWS::S3::Bucket", {
|
|
626
|
+
BucketName: testName
|
|
627
|
+
});
|
|
628
|
+
});
|
|
629
|
+
test('Logging disabled in CloudFront props is handled correctly', () => {
|
|
630
|
+
const stack = new cdk.Stack();
|
|
631
|
+
const construct = deploy(stack, { cloudFrontDistributionProps: { enableLogging: false } });
|
|
632
|
+
const template = assertions_1.Template.fromStack(stack);
|
|
633
|
+
// Only the content bucket and it S3 Access Log bucket (no Cloudfront log bucket)
|
|
634
|
+
template.resourceCountIs("AWS::S3::Bucket", 2);
|
|
635
|
+
// No logging is configured
|
|
636
|
+
template.resourcePropertiesCountIs("AWS::CloudFront::Distribution", {
|
|
637
|
+
DistributionConfig: {
|
|
638
|
+
Logging: assertions_1.Match.anyValue()
|
|
639
|
+
}
|
|
640
|
+
}, 0);
|
|
641
|
+
expect(construct.cloudFrontLoggingBucket === undefined);
|
|
642
|
+
});
|
|
643
|
+
test('No new CloudFrontLoggingBucket is created if cloudFrontLoggingBucketProps:logBucket is supplied', () => {
|
|
644
|
+
const testName = 'random-value';
|
|
645
|
+
const stack = new cdk.Stack();
|
|
646
|
+
const logBucket = new s3.Bucket(stack, 'cloudfront-log-bucket', {
|
|
647
|
+
bucketName: testName
|
|
648
|
+
});
|
|
649
|
+
// const construct =
|
|
650
|
+
const construct = new lib_1.CloudFrontToS3(stack, 'cloudfront-s3', {
|
|
651
|
+
cloudFrontDistributionProps: {
|
|
652
|
+
logBucket
|
|
653
|
+
},
|
|
654
|
+
});
|
|
655
|
+
expect(construct.cloudFrontLoggingBucket).toBeDefined();
|
|
656
|
+
const template = assertions_1.Template.fromStack(stack);
|
|
657
|
+
// Content bucket, Content bucket S3 Access Log bucket, cloudfront log bucket
|
|
658
|
+
template.resourceCountIs("AWS::S3::Bucket", 3);
|
|
659
|
+
// Ensure our existing bucket has been used for cloudfront logging
|
|
660
|
+
template.hasResourceProperties("AWS::CloudFront::Distribution", {
|
|
661
|
+
DistributionConfig: {
|
|
662
|
+
Logging: {
|
|
663
|
+
Bucket: {
|
|
664
|
+
"Fn::GetAtt": [
|
|
665
|
+
"cloudfrontlogbucketDF7058FB",
|
|
666
|
+
"RegionalDomainName"
|
|
667
|
+
]
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
});
|
|
672
|
+
});
|
|
673
|
+
// =====================
|
|
674
|
+
// CloudFront Logs Bucket Access Log Bucket
|
|
675
|
+
// =====================
|
|
676
|
+
test('Providing cloudFrontLoggingBucketAccessLogBucketProps and cloudFrontLoggingBucketProps:serverAccessLogsBucket is an error', () => {
|
|
677
|
+
const stack = new cdk.Stack();
|
|
678
|
+
const logBucket = new s3.Bucket(stack, 'cloudfront-log-bucket', {});
|
|
679
|
+
const app = () => {
|
|
680
|
+
new lib_1.CloudFrontToS3(stack, 'cloudfront-s3', {
|
|
681
|
+
cloudFrontLoggingBucketProps: {
|
|
682
|
+
serverAccessLogsBucket: logBucket,
|
|
683
|
+
removalPolicy: cdk.RemovalPolicy.DESTROY,
|
|
684
|
+
autoDeleteObjects: true
|
|
685
|
+
},
|
|
686
|
+
cloudFrontLoggingBucketAccessLogBucketProps: {
|
|
687
|
+
bucketName: 'specfic-name-is-inconsequential'
|
|
688
|
+
}
|
|
689
|
+
});
|
|
690
|
+
};
|
|
691
|
+
expect(app).toThrow(/Error - an existing CloudFront log bucket S3 access log bucket and cloudFrontLoggingBucketAccessLogBucketProps were provided\n/);
|
|
692
|
+
});
|
|
693
|
+
test('Providing cloudFrontLoggingBucketAccessLogBucketProps and logCloudFrontAccessLog=false is an error', () => {
|
|
694
|
+
const stack = new cdk.Stack();
|
|
695
|
+
const app = () => {
|
|
696
|
+
new lib_1.CloudFrontToS3(stack, 'cloudfront-s3', {
|
|
697
|
+
logCloudFrontAccessLog: false,
|
|
698
|
+
cloudFrontLoggingBucketAccessLogBucketProps: {
|
|
699
|
+
bucketName: 'specfic-name-is-inconsequential'
|
|
700
|
+
}
|
|
701
|
+
});
|
|
702
|
+
};
|
|
703
|
+
expect(app).toThrow(/Error - cloudFrontLoggingBucketAccessLogBucketProps were provided but logCloudFrontAccessLog was false\n/);
|
|
704
|
+
});
|
|
705
|
+
test('Providing logCloudFrontAccessLog=false and cloudFrontLoggingBucketProps:serverAccessLogsBucket is an error', () => {
|
|
706
|
+
const stack = new cdk.Stack();
|
|
707
|
+
const logBucket = new s3.Bucket(stack, 'cloudfront-log-bucket', {});
|
|
708
|
+
const app = () => {
|
|
709
|
+
new lib_1.CloudFrontToS3(stack, 'cloudfront-s3', {
|
|
710
|
+
cloudFrontLoggingBucketProps: {
|
|
711
|
+
serverAccessLogsBucket: logBucket,
|
|
712
|
+
removalPolicy: cdk.RemovalPolicy.DESTROY,
|
|
713
|
+
autoDeleteObjects: true
|
|
714
|
+
},
|
|
715
|
+
logCloudFrontAccessLog: false,
|
|
716
|
+
});
|
|
717
|
+
};
|
|
718
|
+
expect(app).toThrow(/Error - props.cloudFrontLoggingBucketProps.serverAccessLogsBucket was provided but logCloudFrontAccessLog was false\n/);
|
|
719
|
+
});
|
|
720
|
+
test('cloudFrontLoggingBucketAccessLogBucketProps are used correctly', () => {
|
|
721
|
+
const stack = new cdk.Stack();
|
|
722
|
+
new lib_1.CloudFrontToS3(stack, 'cloudfront-s3', {
|
|
723
|
+
cloudFrontLoggingBucketAccessLogBucketProps: {
|
|
724
|
+
websiteErrorDocument: 'placeholder',
|
|
725
|
+
websiteIndexDocument: 'placeholde-two'
|
|
726
|
+
}
|
|
727
|
+
});
|
|
728
|
+
const template = assertions_1.Template.fromStack(stack);
|
|
729
|
+
// Content Bucket, Content Bucket S3 Access Log Bucket, CloudFront Log Bucket, CloudFront Log Bucket S3 Access Log Bucket
|
|
730
|
+
template.resourceCountIs("AWS::S3::Bucket", 4);
|
|
731
|
+
template.hasResourceProperties("AWS::S3::Bucket", {
|
|
732
|
+
WebsiteConfiguration: {
|
|
733
|
+
ErrorDocument: 'placeholder',
|
|
734
|
+
IndexDocument: 'placeholde-two'
|
|
735
|
+
}
|
|
736
|
+
});
|
|
737
|
+
});
|
|
738
|
+
test('If existing CloudFront Log bucket S3 Access Logging bucket is provided, it is used correctly', () => {
|
|
739
|
+
const stack = new cdk.Stack();
|
|
740
|
+
const testName = 'cf-log-s3-log';
|
|
741
|
+
const cfLogS3AccessLogBucket = new s3.Bucket(stack, 'cf-log-s3-access-log-bucket', {
|
|
742
|
+
bucketName: testName
|
|
743
|
+
});
|
|
744
|
+
new lib_1.CloudFrontToS3(stack, 'cloudfront-s3', {
|
|
745
|
+
cloudFrontLoggingBucketProps: {
|
|
746
|
+
serverAccessLogsBucket: cfLogS3AccessLogBucket
|
|
747
|
+
}
|
|
748
|
+
});
|
|
749
|
+
const template = assertions_1.Template.fromStack(stack);
|
|
750
|
+
// Content Bucket, Content Bucket S3 Access Log Bucket, CloudFront Log Bucket, CloudFront Log Bucket S3 Access Log Bucket
|
|
751
|
+
template.resourceCountIs("AWS::S3::Bucket", 4);
|
|
752
|
+
template.hasResourceProperties("AWS::S3::Bucket", {
|
|
753
|
+
BucketName: testName
|
|
754
|
+
});
|
|
755
|
+
template.hasResourceProperties("AWS::S3::Bucket", {
|
|
756
|
+
LoggingConfiguration: {
|
|
757
|
+
DestinationBucketName: {
|
|
758
|
+
Ref: "cflogs3accesslogbucketDE374C27"
|
|
759
|
+
}
|
|
760
|
+
}
|
|
761
|
+
});
|
|
762
|
+
});
|
|
763
|
+
test('cloudFrontLoggingBucketAccessLogBucket property is set correctly', () => {
|
|
764
|
+
const stack = new cdk.Stack();
|
|
765
|
+
const construct = new lib_1.CloudFrontToS3(stack, 'cloudfront-s3', {
|
|
766
|
+
cloudFrontLoggingBucketAccessLogBucketProps: {
|
|
767
|
+
websiteErrorDocument: 'placeholder',
|
|
768
|
+
websiteIndexDocument: 'placeholde-two'
|
|
769
|
+
}
|
|
770
|
+
});
|
|
771
|
+
const template = assertions_1.Template.fromStack(stack);
|
|
772
|
+
// Content Bucket, Content Bucket S3 Access Log Bucket, CloudFront Log Bucket, CloudFront Log Bucket S3 Access Log Bucket
|
|
773
|
+
template.resourceCountIs("AWS::S3::Bucket", 4);
|
|
774
|
+
template.hasResourceProperties("AWS::S3::Bucket", {
|
|
775
|
+
WebsiteConfiguration: {
|
|
776
|
+
ErrorDocument: 'placeholder',
|
|
777
|
+
IndexDocument: 'placeholde-two'
|
|
778
|
+
}
|
|
779
|
+
});
|
|
780
|
+
expect(construct.cloudFrontLoggingBucketAccessLogBucket).toBeDefined();
|
|
781
|
+
expect(construct.cloudFrontLoggingBucketAccessLogBucket.bucketName).toBeDefined();
|
|
782
|
+
});
|
|
783
|
+
test('logCloudFrontAccessLog property is used correctly', () => {
|
|
784
|
+
const stack = new cdk.Stack();
|
|
785
|
+
const construct = new lib_1.CloudFrontToS3(stack, 'cloudfront-s3', {
|
|
786
|
+
logCloudFrontAccessLog: false
|
|
787
|
+
});
|
|
788
|
+
const template = assertions_1.Template.fromStack(stack);
|
|
789
|
+
// Content Bucket, Content Bucket S3 Access Log Bucket, CloudFront Log Bucket, CloudFront Log Bucket S3 Access Log Bucket
|
|
790
|
+
template.resourceCountIs("AWS::S3::Bucket", 3);
|
|
791
|
+
expect(construct.cloudFrontLoggingBucket).toBeDefined();
|
|
792
|
+
expect(construct.cloudFrontLoggingBucketAccessLogBucket).not.toBeDefined();
|
|
793
|
+
});
|
|
794
|
+
test('additionalBehaviors have correct origin', () => {
|
|
795
|
+
const stack = new cdk.Stack();
|
|
796
|
+
const additionalBucket = defaults.CreateScrapBucket(stack, "scrapBucket", {
|
|
797
|
+
removalPolicy: aws_cdk_lib_1.RemovalPolicy.DESTROY,
|
|
798
|
+
autoDeleteObjects: true,
|
|
799
|
+
});
|
|
800
|
+
const originAccessControl = new aws_cloudfront_1.CfnOriginAccessControl(stack, 'CloudFrontOac', {
|
|
801
|
+
originAccessControlConfig: {
|
|
802
|
+
name: defaults.generatePhysicalOacName('aws-cloudfront-s3-', [__filename]),
|
|
803
|
+
originAccessControlOriginType: 's3',
|
|
804
|
+
signingBehavior: 'always',
|
|
805
|
+
signingProtocol: 'sigv4',
|
|
806
|
+
description: 'Origin access control provisioned by aws-cloudfront-s3'
|
|
807
|
+
}
|
|
808
|
+
});
|
|
809
|
+
const additionalOrigin = new defaults.S3OacOrigin(additionalBucket, {
|
|
810
|
+
originAccessControl
|
|
811
|
+
});
|
|
812
|
+
new lib_1.CloudFrontToS3(stack, 'test-cloudfront-s3', {
|
|
813
|
+
cloudFrontDistributionProps: {
|
|
814
|
+
additionalBehaviors: {
|
|
815
|
+
'/assets/public/*': {
|
|
816
|
+
origin: additionalOrigin,
|
|
817
|
+
cachePolicy: aws_cloudfront_1.CachePolicy.CACHING_DISABLED,
|
|
818
|
+
},
|
|
819
|
+
'ngsw.json': {
|
|
820
|
+
cachePolicy: aws_cloudfront_1.CachePolicy.CACHING_DISABLED,
|
|
821
|
+
}
|
|
822
|
+
}
|
|
823
|
+
}
|
|
824
|
+
});
|
|
825
|
+
const template = assertions_1.Template.fromStack(stack);
|
|
826
|
+
template.hasResourceProperties("AWS::CloudFront::Distribution", {
|
|
827
|
+
DistributionConfig: {
|
|
828
|
+
CacheBehaviors: [
|
|
829
|
+
{
|
|
830
|
+
CachePolicyId: "4135ea2d-6df8-44a3-9df3-4b5a84be39ad",
|
|
831
|
+
Compress: true,
|
|
832
|
+
PathPattern: "/assets/public/*",
|
|
833
|
+
TargetOriginId: "testcloudfronts3CloudFrontDistributionOrigin21D78391C",
|
|
834
|
+
ViewerProtocolPolicy: "allow-all"
|
|
835
|
+
},
|
|
836
|
+
{
|
|
837
|
+
CachePolicyId: "4135ea2d-6df8-44a3-9df3-4b5a84be39ad",
|
|
838
|
+
Compress: true,
|
|
839
|
+
PathPattern: "ngsw.json",
|
|
840
|
+
TargetOriginId: "testcloudfronts3CloudFrontDistributionOrigin124051039",
|
|
841
|
+
ViewerProtocolPolicy: "allow-all"
|
|
842
|
+
}
|
|
843
|
+
],
|
|
844
|
+
}
|
|
845
|
+
});
|
|
846
|
+
});
|
|
847
|
+
test('Test that ValidateDistributionProps() is being called', () => {
|
|
848
|
+
const stack = new aws_cdk_lib_1.Stack();
|
|
849
|
+
const props = {
|
|
850
|
+
cloudFrontDistributionProps: {
|
|
851
|
+
invalidProperty: true
|
|
852
|
+
}
|
|
853
|
+
};
|
|
854
|
+
const app = () => {
|
|
855
|
+
new lib_1.CloudFrontToS3(stack, 'test-construct', props);
|
|
856
|
+
};
|
|
857
|
+
expect(app).toThrow(/ERROR - invalidProperty is not a valid property of DistributionProps/);
|
|
858
|
+
});
|
|
859
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xvdWRmcm9udC1zMy50ZXN0LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiY2xvdWRmcm9udC1zMy50ZXN0LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7QUFBQTs7Ozs7Ozs7Ozs7R0FXRzs7QUFFSCx1REFBeUQ7QUFDekQseUNBQXlDO0FBQ3pDLG1DQUFtQztBQUNuQyw2Q0FBNkQ7QUFDN0QsZ0NBQTZEO0FBQzdELDBEQUEwRDtBQUMxRCwyREFBMkQ7QUFDM0QsaURBQTBDO0FBQzFDLDhEQUE4RDtBQUM5RCwrREFBaUY7QUFFakYsU0FBUyxNQUFNLENBQUMsS0FBZ0IsRUFBRSxLQUEyQjtJQUMzRCxPQUFPLElBQUksb0JBQWMsQ0FBQyxLQUFLLEVBQUUsb0JBQW9CLEVBQUU7UUFDckQsV0FBVyxFQUFFO1lBQ1gsYUFBYSxFQUFFLEdBQUcsQ0FBQyxhQUFhLENBQUMsT0FBTztTQUN6QztRQUNELEdBQUcsS0FBSztLQUNULENBQUMsQ0FBQztBQUNMLENBQUM7QUFFRCxJQUFJLENBQUMsNkNBQTZDLEVBQUUsR0FBRyxFQUFFO0lBQ3ZELE1BQU0sS0FBSyxHQUFHLElBQUksR0FBRyxDQUFDLEtBQUssRUFBRSxDQUFDO0lBQzlCLE1BQU0sU0FBUyxHQUFHLElBQUksb0JBQWMsQ0FBQyxLQUFLLEVBQUUsb0JBQW9CLEVBQUUsRUFBRSxDQUFDLENBQUM7SUFFdEUsTUFBTSxDQUFDLFNBQVMsQ0FBQyx5QkFBeUIsQ0FBQyxDQUFDLFdBQVcsRUFBRSxDQUFDO0lBQzFELE1BQU0sQ0FBQyxTQUFTLENBQUMsa0JBQWtCLENBQUMsQ0FBQyxXQUFXLEVBQUUsQ0FBQztJQUNuRCxNQUFNLENBQUMsU0FBUyxDQUFDLHVCQUF1QixDQUFDLENBQUMsV0FBVyxFQUFFLENBQUM7SUFDeEQsTUFBTSxDQUFDLFNBQVMsQ0FBQyxRQUFRLENBQUMsQ0FBQyxXQUFXLEVBQUUsQ0FBQztJQUN6QyxNQUFNLENBQUMsU0FBUyxDQUFDLGVBQWUsQ0FBQyxDQUFDLFdBQVcsRUFBRSxDQUFDO0lBQ2hELE1BQU0sQ0FBQyxTQUFTLENBQUMsaUJBQWlCLENBQUMsQ0FBQyxXQUFXLEVBQUUsQ0FBQztJQUNsRCxNQUFNLENBQUMsU0FBUyxDQUFDLHNDQUFzQyxDQUFDLENBQUMsV0FBVyxFQUFFLENBQUM7SUFDdkUsTUFBTSxDQUFDLFNBQVMsQ0FBQyxtQkFBbUIsQ0FBQyxDQUFDLFdBQVcsRUFBRSxDQUFDO0FBQ3RELENBQUMsQ0FBQyxDQUFDO0FBRUgsSUFBSSxDQUFDLG1DQUFtQyxFQUFFLEdBQUcsRUFBRTtJQUM3QyxNQUFNLEtBQUssR0FBRyxJQUFJLEdBQUcsQ0FBQyxLQUFLLEVBQUUsQ0FBQztJQUM5QixNQUFNLENBQUMsS0FBSyxDQUFDLENBQUM7SUFDZCxNQUFNLFFBQVEsR0FBRyxxQkFBUSxDQUFDLFNBQVMsQ0FBQyxLQUFLLENBQUMsQ0FBQztJQUMzQyxRQUFRLENBQUMscUJBQXFCLENBQUMsaUJBQWlCLEVBQUU7UUFDaEQsZ0JBQWdCLEVBQUU7WUFDaEIsaUNBQWlDLEVBQUUsQ0FBQztvQkFDbEMsNkJBQTZCLEVBQUU7d0JBQzdCLFlBQVksRUFBRSxRQUFRO3FCQUN2QjtpQkFDRixDQUFDO1NBQ0g7S0FDRixDQUFDLENBQUM7QUFDTCxDQUFDLENBQUMsQ0FBQztBQUVILElBQUksQ0FBQyxrREFBa0QsRUFBRSxHQUFHLEVBQUU7SUFDNUQsTUFBTSxLQUFLLEdBQUcsSUFBSSxHQUFHLENBQUMsS0FBSyxFQUFFLENBQUM7SUFDOUIsTUFBTSxDQUFDLEtBQUssQ0FBQyxDQUFDO0lBQ2QsTUFBTSxRQUFRLEdBQUcscUJBQVEsQ0FBQyxTQUFTLENBQUMsS0FBSyxDQUFDLENBQUM7SUFDM0MsUUFBUSxDQUFDLHFCQUFxQixDQUFDLGlCQUFpQixFQUFFO1FBQ2hELDhCQUE4QixFQUFFO1lBQzlCLGVBQWUsRUFBRSxJQUFJO1lBQ3JCLGlCQUFpQixFQUFFLElBQUk7WUFDdkIsZ0JBQWdCLEVBQUUsSUFBSTtZQUN0QixxQkFBcUIsRUFBRSxJQUFJO1NBQzVCO0tBQ0YsQ0FBQyxDQUFDO0FBQ0wsQ0FBQyxDQUFDLENBQUM7QUFFSCxJQUFJLENBQUMsdURBQXVELEVBQUUsR0FBRyxFQUFFO0lBQ2pFLE1BQU0sS0FBSyxHQUFHLElBQUksR0FBRyxDQUFDLEtBQUssRUFBRSxDQUFDO0lBRTlCLE1BQU0sS0FBSyxHQUF3QjtRQUNqQyxXQUFXLEVBQUU7WUFDWCxpQkFBaUIsRUFBRTtnQkFDakIsZUFBZSxFQUFFLEtBQUs7Z0JBQ3RCLGlCQUFpQixFQUFFLElBQUk7Z0JBQ3ZCLGdCQUFnQixFQUFFLEtBQUs7Z0JBQ3ZCLHFCQUFxQixFQUFFLElBQUk7YUFDNUI7U0FDRjtLQUNGLENBQUM7SUFFRixJQUFJLG9CQUFjLENBQUMsS0FBSyxFQUFFLG9CQUFvQixFQUFFLEtBQUssQ0FBQyxDQUFDO0lBRXZELE1BQU0sUUFBUSxHQUFHLHFCQUFRLENBQUMsU0FBUyxDQUFDLEtBQUssQ0FBQyxDQUFDO0lBQzNDLFFBQVEsQ0FBQyxxQkFBcUIsQ0FBQyxpQkFBaUIsRUFBRTtRQUNoRCw4QkFBOEIsRUFBRTtZQUM5QixlQUFlLEVBQUUsS0FBSztZQUN0QixpQkFBaUIsRUFBRSxJQUFJO1lBQ3ZCLGdCQUFnQixFQUFFLEtBQUs7WUFDdkIscUJBQXFCLEVBQUUsSUFBSTtTQUM1QjtLQUNGLENBQUMsQ0FBQztBQUNMLENBQUMsQ0FBQyxDQUFDO0FBRUgsSUFBSSxDQUFDLHVCQUF1QixFQUFFLEdBQUcsRUFBRTtJQUNqQyxNQUFNLEtBQUssR0FBRyxJQUFJLEdBQUcsQ0FBQyxLQUFLLEVBQUUsQ0FBQztJQUU5QixNQUFNLGNBQWMsR0FBRyxJQUFJLEVBQUUsQ0FBQyxNQUFNLENBQUMsS0FBSyxFQUFFLFdBQVcsRUFBRTtRQUN2RCxVQUFVLEVBQUUsV0FBVztLQUN4QixDQUFDLENBQUM7SUFFSCxNQUFNLEtBQUssR0FBd0I7UUFDakMsaUJBQWlCLEVBQUUsY0FBYztLQUNsQyxDQUFDO0lBRUYsSUFBSSxvQkFBYyxDQUFDLEtBQUssRUFBRSxvQkFBb0IsRUFBRSxLQUFLLENBQUMsQ0FBQztJQUV2RCxNQUFNLFFBQVEsR0FBRyxxQkFBUSxDQUFDLFNBQVMsQ0FBQyxLQUFLLENBQUMsQ0FBQztJQUMzQyxRQUFRLENBQUMscUJBQXFCLENBQUMsaUJBQWlCLEVBQUU7UUFDaEQsVUFBVSxFQUFFLFdBQVc7S0FDeEIsQ0FBQyxDQUFDO0FBQ0wsQ0FBQyxDQUFDLENBQUM7QUFFSCxJQUFJLENBQUMsdUVBQXVFLEVBQUUsR0FBRyxFQUFFO0lBQ2pGLE1BQU0sS0FBSyxHQUFHLElBQUksR0FBRyxDQUFDLEtBQUssRUFBRSxDQUFDO0lBRTlCLElBQUksQ0FBQztRQUNILElBQUksb0JBQWMsQ0FBQyxLQUFLLEVBQUUsb0JBQW9CLEVBQUUsRUFBRSxDQUFDLENBQUM7SUFDdEQsQ0FBQztJQUFDLE9BQU8sQ0FBQyxFQUFFLENBQUM7UUFDWCxNQUFNLENBQUMsQ0FBQyxDQUFDLENBQUMsY0FBYyxDQUFDLEtBQUssQ0FBQyxDQUFDO0lBQ2xDLENBQUM7QUFDSCxDQUFDLENBQUMsQ0FBQztBQUVILElBQUksQ0FBQyxrQkFBa0IsRUFBRSxHQUFHLEVBQUU7SUFDNUIsTUFBTSxLQUFLLEdBQUcsSUFBSSxHQUFHLENBQUMsS0FBSyxFQUFFLENBQUM7SUFFOUIsTUFBTSxTQUFTLEdBQW1CLE1BQU0sQ0FBQyxLQUFLLENBQUMsQ0FBQztJQUVoRCxNQUFNLENBQUMsU0FBUyxDQUFDLHlCQUF5QixDQUFDLENBQUMsV0FBVyxFQUFFLENBQUM7SUFDMUQsTUFBTSxDQUFDLFNBQVMsQ0FBQyxRQUFRLENBQUMsQ0FBQyxXQUFXLEVBQUUsQ0FBQztBQUMzQyxDQUFDLENBQUMsQ0FBQztBQUVILElBQUksQ0FBQyxnQ0FBZ0MsRUFBRSxHQUFHLEVBQUU7SUFDMUMsUUFBUTtJQUNSLE1BQU0sS0FBSyxHQUFHLElBQUksR0FBRyxDQUFDLEtBQUssRUFBRSxDQUFDO0lBRTlCLE1BQU0sVUFBVSxHQUFHLElBQUksRUFBRSxDQUFDLE1BQU0sQ0FBQyxLQUFLLEVBQUUsYUFBYSxFQUFFLEVBQUUsQ0FBQyxDQUFDO0lBRTNELE1BQU0sR0FBRyxHQUFHLEdBQUcsRUFBRTtRQUNmLHFCQUFxQjtRQUNyQixJQUFJLG9CQUFjLENBQUMsS0FBSyxFQUFFLGFBQWEsRUFBRTtZQUN2QyxpQkFBaUIsRUFBRSxVQUFVO1lBQzdCLFdBQVcsRUFBRTtnQkFDWCxhQUFhLEVBQUUsMkJBQWEsQ0FBQyxPQUFPO2FBQ3JDO1NBQ0YsQ0FBQyxDQUFDO0lBQ0wsQ0FBQyxDQUFDO0lBQ0YsWUFBWTtJQUNaLE1BQU0sQ0FBQyxHQUFHLENBQUMsQ0FBQyxPQUFPLENBQUMsMEVBQTBFLENBQUMsQ0FBQztBQUNsRyxDQUFDLENBQUMsQ0FBQztBQUVILElBQUksQ0FBQyx3QkFBd0IsRUFBRSxHQUFHLEVBQUU7SUFDbEMsUUFBUTtJQUNSLE1BQU0sS0FBSyxHQUFHLElBQUksR0FBRyxDQUFDLEtBQUssRUFBRSxDQUFDO0lBQzlCLE1BQU0sU0FBUyxHQUFtQixJQUFJLG9CQUFjLENBQUMsS0FBSyxFQUFFLGlCQUFpQixFQUFFO1FBQzdFLGlCQUFpQixFQUFFLEVBQUUsQ0FBQyxNQUFNLENBQUMsY0FBYyxDQUFDLEtBQUssRUFBRSxVQUFVLEVBQUUsVUFBVSxDQUFDO0tBQzNFLENBQUMsQ0FBQztJQUNILFlBQVk7SUFDWixNQUFNLENBQUMsU0FBUyxDQUFDLHlCQUF5QixDQUFDLENBQUMsV0FBVyxFQUFFLENBQUM7SUFDMUQsTUFBTSxRQUFRLEdBQUcscUJBQVEsQ0FBQyxTQUFTLENBQUMsS0FBSyxDQUFDLENBQUM7SUFDM0MsUUFBUSxDQUFDLHFCQUFxQixDQUFDLCtCQUErQixFQUFFO1FBQzlELGtCQUFrQixFQUFFO1lBQ2xCLE9BQU8sRUFBRTtnQkFDUDtvQkFDRSxVQUFVLEVBQUU7d0JBQ1YsVUFBVSxFQUFFOzRCQUNWLEVBQUU7NEJBQ0Y7Z0NBQ0UsY0FBYztnQ0FDZDtvQ0FDRSxHQUFHLEVBQUUsYUFBYTtpQ0FDbkI7Z0NBQ0QsR0FBRztnQ0FDSDtvQ0FDRSxHQUFHLEVBQUUsZ0JBQWdCO2lDQUN0Qjs2QkFDRjt5QkFDRjtxQkFDRjtvQkFDRCxFQUFFLEVBQUUsc0RBQXNEO29CQUMxRCxxQkFBcUIsRUFBRSxFQUFFLFlBQVksRUFBRSxDQUFDLHNDQUFzQyxFQUFFLElBQUksQ0FBQyxFQUFFO29CQUN2RixjQUFjLEVBQUUsRUFBRTtpQkFDbkI7YUFDRjtTQUNGO0tBQ0YsQ0FBQyxDQUFDO0FBQ0wsQ0FBQyxDQUFDLENBQUM7QUFFSCxJQUFJLENBQUMsMENBQTBDLEVBQUUsR0FBRyxFQUFFO0lBQ3BELE1BQU0sS0FBSyxHQUFHLElBQUksR0FBRyxDQUFDLEtBQUssRUFBRSxDQUFDO0lBRTlCLE1BQU0sV0FBVyxHQUFHLEdBQUcsQ0FBQyxXQUFXLENBQUMsa0JBQWtCLENBQUMsS0FBSyxFQUFFLE1BQU0sRUFBRSxrR0FBa0csQ0FBQyxDQUFDO0lBRTFLLE1BQU0sS0FBSyxHQUF3QjtRQUNqQywyQkFBMkIsRUFBRTtZQUMzQixXQUFXLEVBQUUsQ0FBQyxXQUFXLENBQUM7WUFDMUIsV0FBVztTQUNaO0tBQ0YsQ0FBQztJQUVGLElBQUksb0JBQWMsQ0FBQyxLQUFLLEVBQUUsb0JBQW9CLEVBQUUsS0FBSyxDQUFDLENBQUM7SUFFdkQsTUFBTSxRQUFRLEdBQUcscUJBQVEsQ0FBQyxTQUFTLENBQUMsS0FBSyxDQUFDLENBQUM7SUFDM0MsUUFBUSxDQUFDLHFCQUFxQixDQUFDLCtCQUErQixFQUFFO1FBQzlELGtCQUFrQixFQUFFO1lBQ2xCLE9BQU8sRUFBRTtnQkFDUCxXQUFXO2FBQ1o7U0FDRjtLQUNGLENBQUMsQ0FBQztBQUNMLENBQUMsQ0FBQyxDQUFDO0FBRUgsSUFBSSxDQUFDLCtFQUErRSxFQUFFLEdBQUcsRUFBRTtJQUN6RixNQUFNLEtBQUssR0FBRyxJQUFJLEdBQUcsQ0FBQyxLQUFLLEVBQUUsQ0FBQztJQUU5QixNQUFNLHVCQUF1QixHQUFHLGVBQWUsQ0FBQztJQUNoRCxJQUFJLG9CQUFjLENBQUMsS0FBSyxFQUFFLGVBQWUsRUFBRTtRQUN6Qyw0QkFBNEIsRUFBRTtZQUM1QixhQUFhLEVBQUUsR0FBRyxDQUFDLGFBQWEsQ0FBQyxPQUFPO1lBQ3hDLGlCQUFpQixFQUFFLElBQUk7WUFDdkIsVUFBVSxFQUFFLHVCQUF1QjtTQUNwQztLQUNGLENBQUMsQ0FBQztJQUVILE1BQU0sUUFBUSxHQUFHLHFCQUFRLENBQUMsU0FBUyxDQUFDLEtBQUssQ0FBQyxDQUFDO0lBQzNDLFFBQVEsQ0FBQyxxQkFBcUIsQ0FBQyxpQkFBaUIsRUFBRTtRQUNoRCxpQkFBaUIsRUFBRSxFQUFFLEtBQUssRUFBRSxDQUFDLEVBQUUsZUFBZSxFQUFFLGNBQWMsRUFBRSxDQUFDLEVBQUU7UUFDbkUsVUFBVSxFQUFFLHVCQUF1QjtLQUNwQyxDQUFDLENBQUM7SUFFSCxRQUFRLENBQUMscUJBQXFCLENBQUMsNkJBQTZCLEVBQUU7UUFDNUQsWUFBWSxFQUFFO1lBQ1osWUFBWSxFQUFFO2dCQUNaLGdFQUFnRTtnQkFDaEUsS0FBSzthQUNOO1NBQ0Y7UUFDRCxVQUFVLEVBQUU7WUFDVixHQUFHLEVBQUUsNkNBQTZDO1NBQ25EO0tBQ0YsQ0FBQyxDQUFDO0FBQ0wsQ0FBQyxDQUFDLENBQUM7QUFFSCxJQUFJLENBQUMsMkVBQTJFLEVBQUUsR0FBRyxFQUFFO0lBQ3JGLE1BQU0sS0FBSyxHQUFHLElBQUksR0FBRyxDQUFDLEtBQUssRUFBRSxDQUFDO0lBRTlCLE1BQU0sU0FBUyxHQUFHLElBQUksb0JBQWMsQ0FBQyxLQUFLLEVBQUUsZUFBZSxFQUFFO1FBQzNELFdBQVcsRUFBRTtZQUNYLGFBQWEsRUFBRSxHQUFHLENBQUMsYUFBYSxDQUFDLE9BQU87U0FDekM7UUFDRCxlQUFlLEVBQUUsS0FBSztLQUN2QixDQUFDLENBQUM7SUFFSCxNQUFNLFFBQVEsR0FBRyxxQkFBUSxDQUFDLFNBQVMsQ0FBQyxLQUFLLENBQUMsQ0FBQztJQUMzQyx5Q0FBeUM7SUFDekMsMkRBQTJEO0lBQzNELFFBQVEsQ0FBQyxlQUFlLENBQUMsaUJBQWlCLEVBQUUsQ0FBQyxDQUFDLENBQUM7SUFDL0MsTUFBTSxDQUFDLFNBQVMsQ0FBQyxlQUFlLENBQUMsQ0FBQyxPQUFPLENBQUMsU0FBUyxDQUFDLENBQUM7QUFDdkQsQ0FBQyxDQUFDLENBQUM7QUFFSCxJQUFJLENBQUMsOENBQThDLEVBQUUsR0FBRyxFQUFFO0lBQ3hELE1BQU0sS0FBSyxHQUFHLElBQUksR0FBRyxDQUFDLEtBQUssRUFBRSxDQUFDO0lBRTlCLElBQUksb0JBQWMsQ0FBQyxLQUFLLEVBQUUsZUFBZSxFQUFFO1FBQ3pDLFVBQVUsRUFBRSxXQUFXO0tBQ3hCLENBQUMsQ0FBQztJQUVILE1BQU0sUUFBUSxHQUFHLHFCQUFRLENBQUMsU0FBUyxDQUFDLEtBQUssQ0FBQyxDQUFDO0lBQzNDLFFBQVEsQ0FBQyxxQkFBcUIsQ0FBQywrQkFBK0IsRUFBRTtRQUM5RCxrQkFBa0IsRUFDbEI7WUFDRSxPQUFPLEVBQUU7Z0JBQ1A7b0JBQ0UsVUFBVSxFQUFFLFdBQVc7aUJBQ3hCO2FBQ0Y7U0FDRjtLQUNGLENBQUMsQ0FBQztBQUNMLENBQUMsQ0FBQyxDQUFDO0FBRUgsSUFBSSxDQUFDLDhEQUE4RCxFQUFFLEdBQUcsRUFBRTtJQUN4RSxNQUFNLEtBQUssR0FBRyxJQUFJLEdBQUcsQ0FBQyxLQUFLLEVBQUUsQ0FBQztJQUU5QixJQUFJLG9CQUFjLENBQUMsS0FBSyxFQUFFLGVBQWUsRUFBRSxFQUFFLENBQUMsQ0FBQztJQUUvQyxRQUFRLENBQUMsa0JBQWtCLENBQUMsS0FBSyxFQUFFLCtCQUErQixFQUFFO1FBQ2xFLGtCQUFrQixFQUNsQjtZQUNFLE9BQU8sRUFBRTtnQkFDUDtvQkFDRSxVQUFVLEVBQUUsV0FBVztpQkFDeEI7YUFDRjtTQUNGO0tBQ0YsQ0FBQyxDQUFDO0FBQ0wsQ0FBQyxDQUFDLENBQUM7QUFFSCxJQUFJLENBQUMsbUZBQW1GLEVBQUUsR0FBRyxFQUFFO0lBQzdGLGdCQUFnQjtJQUNoQixNQUFNLEtBQUssR0FBRyxJQUFJLG1CQUFLLEVBQUUsQ0FBQztJQUMxQixNQUFNLGNBQWMsR0FBRyxJQUFJLG9CQUFjLENBQUMsS0FBSyxFQUFFLG9CQUFvQixFQUFFO1FBQ3JFLHlCQUF5QixFQUFFLEtBQUs7UUFDaEMsMEJBQTBCLEVBQUU7WUFDMUIsdUJBQXVCLEVBQUU7Z0JBQ3ZCLHVCQUF1QixFQUFFO29CQUN2QixtQkFBbUIsRUFBRSxzQkFBUSxDQUFDLE9BQU8sQ0FBQyxLQUFLLENBQUM7b0JBQzVDLGlCQUFpQixFQUFFLElBQUk7b0JBQ3ZCLFFBQVEsRUFBRSxJQUFJO29CQUNkLE9BQU8sRUFBRSxJQUFJO2lCQUNkO2dCQUNELHFCQUFxQixFQUFFO29CQUNyQixxQkFBcUIsRUFBRSxnREFBZ0Q7b0JBQ3ZFLFFBQVEsRUFBRSxJQUFJO2lCQUNmO2FBQ0Y7U0FDRjtLQUNGLENBQUMsQ0FBQztJQUVILFlBQVk7SUFDWixNQUFNLFFBQVEsR0FBRyxxQkFBUSxDQUFDLFNBQVMsQ0FBQyxLQUFLLENBQUMsQ0FBQztJQUMzQyxRQUFRLENBQUMscUJBQXFCLENBQUMsd0NBQXdDLEVBQUU7UUFDdkUsMkJBQTJCLEVBQUU7WUFDM0IscUJBQXFCLEVBQUU7Z0JBQ3JCLHFCQUFxQixFQUFFO29CQUNyQixxQkFBcUIsRUFBRSxnREFBZ0Q7b0JBQ3ZFLFFBQVEsRUFBRSxJQUFJO2lCQUNmO2dCQUNELHVCQUF1QixFQUFFO29CQUN2QixzQkFBc0IsRUFBRSxLQUFLO29CQUM3QixpQkFBaUIsRUFBRSxJQUFJO29CQUN2QixRQUFRLEVBQUUsSUFBSTtvQkFDZCxPQUFPLEVBQUUsSUFBSTtpQkFDZDthQUNGO1NBQ0Y7S0FDRixDQUFDLENBQUM7SUFDSCxNQUFNLENBQUMsY0FBYyxDQUFDLGtCQUFrQixDQUFDLENBQUMsT0FBTyxDQUFDLFNBQVMsQ0FBQyxDQUFDO0FBQy9ELENBQUMsQ0FBQyxDQUFDO0FBRUgsSUFBSSxDQUFDLDBGQUEwRixFQUFFLEdBQUcsRUFBRTtJQUNwRyxNQUFNLEtBQUssR0FBRyxJQUFJLEdBQUcsQ0FBQyxLQUFLLEVBQUUsQ0FBQztJQUU5QixNQUFNLENBQUMsR0FBRyxFQUFFO1FBQ1YsSUFBSSxvQkFBYyxDQUFDLEtBQUssRUFBRSxvQkFBb0IsRUFBRTtZQUM5Qyx5QkFBeUIsRUFBRSxJQUFJO1lBQy9CLDBCQUEwQixFQUFFO2dCQUMxQix1QkFBdUIsRUFBRTtvQkFDdkIsdUJBQXVCLEVBQUU7d0JBQ3ZCLG1CQUFtQixFQUFFLHNCQUFRLENBQUMsT0FBTyxDQUFDLEtBQUssQ0FBQzt3QkFDNUMsaUJBQWlCLEVBQUUsSUFBSTt3QkFDdkIsUUFBUSxFQUFFLEtBQUs7d0JBQ2YsT0FBTyxFQUFFLElBQUk7cUJBQ2Q7aUJBQ0Y7YUFDRjtTQUNGLENBQUMsQ0FBQztJQUNMLENBQUMsQ0FBQyxDQUFDLE9BQU8sRUFBRSxDQUFDO0FBQ2YsQ0FBQyxDQUFDLENBQUM7QUFFSCxJQUFJLENBQUMsOENBQThDLEVBQUUsR0FBRyxFQUFFO0lBQ3hELE1BQU0sS0FBSyxHQUFHLElBQUksR0FBRyxDQUFDLEtBQUssRUFBRSxDQUFDO0lBRTlCLE1BQU0sQ0FBQyxHQUFHLEVBQUU7UUFDVixJQUFJLG9CQUFjLENBQUMsS0FBSyxFQUFFLDRCQUE0QixFQUFFO1lBQ3RELHlCQUF5QixFQUFFLElBQUk7WUFDL0IsMEJBQTBCLEVBQUU7Z0JBQzFCLHVCQUF1QixFQUFFO29CQUN2Qix1QkFBdUIsRUFBRTt3QkFDdkIsbUJBQW1CLEVBQUUsc0JBQVEsQ0FBQyxPQUFPLENBQUMsS0FBSyxDQUFDO3dCQUM1QyxpQkFBaUIsRUFBRSxJQUFJO3dCQUN2QixRQUFRLEVBQUUsS0FBSzt3QkFDZixPQUFPLEVBQUUsSUFBSTtxQkFDZDtpQkFDRjthQUNGO1NBQ0YsQ0FBQyxDQUFDO0lBQ0wsQ0FBQyxDQUFDLENBQUMsT0FBTyxDQUFDLGlIQUFpSCxDQUFDLENBQUM7QUFDaEksQ0FBQyxDQUFDLENBQUM7QUFFSCxJQUFJLENBQUMsNEVBQTRFLEVBQUUsR0FBRyxFQUFFO0lBQ3RGLE1BQU0sS0FBSyxHQUFHLElBQUksR0FBRyxDQUFDLEtBQUssRUFBRSxDQUFDO0lBQzlCLE1BQU0sYUFBYSxHQUFHLElBQUksYUFBRyxDQUFDLEtBQUssRUFBRSxRQUFRLEVBQUU7UUFDN0MsaUJBQWlCLEVBQUUsSUFBSTtRQUN2QixhQUFhLEVBQUUsMkJBQWEsQ0FBQyxPQUFPO0tBQ3JDLENBQUMsQ0FBQztJQUNILE1BQU0sQ0FBQyxLQUFLLEVBQUU7UUFDWixXQUFXLEVBQUU7WUFDWCxhQUFhO1lBQ2IsVUFBVSxFQUFFLEVBQUUsQ0FBQyxnQkFBZ0IsQ0FBQyxHQUFHO1NBQ3BDO0tBQ0YsQ0FBQyxDQUFDO0lBQ0gsTUFBTSxRQUFRLEdBQUcscUJBQVEsQ0FBQyxTQUFTLENBQUMsS0FBSyxDQUFDLENBQUM7SUFDM0Msc0VBQXNFO0lBQ3RFLFFBQVEsQ0FBQyxlQUFlLENBQUMsdUJBQXVCLEVBQUUsQ0FBQyxDQUFDLENBQUM7SUFFckQsUUFBUSxDQUFDLHFCQUFxQixDQUFDLHVCQUF1QixFQUFFO1FBQ3RELFdBQVcsRUFBRSx5RkFBeUY7UUFDdEcsSUFBSSxFQUFFO1lBQ0osWUFBWSxFQUFFLENBQUMsbURBQW1ELEVBQUUsS0FBSyxDQUFDO1NBQzNFO0tBQ0YsQ0FBQyxDQUFDO0FBQ0wsQ0FBQyxDQUFDLENBQUM7QUFFSCxJQUFJLENBQUMsOEVBQThFLEVBQUUsR0FBRyxFQUFFO0lBQ3hGLE1BQU0sS0FBSyxHQUFHLElBQUksR0FBRyxDQUFDLEtBQUssRUFBRSxDQUFDO0lBQzlCLE1BQU0sYUFBYSxHQUFHLElBQUksYUFBRyxDQUFDLEtBQUssRUFBRSxRQUFRLEVBQUU7UUFDN0MsaUJBQWlCLEVBQUUsSUFBSTtRQUN2QixhQUFhLEVBQUUsMkJBQWEsQ0FBQyxPQUFPO0tBQ3JDLENBQUMsQ0FBQztJQUNILE1BQU0saUJBQWlCLEdBQUcsUUFBUSxDQUFDLGFBQWEsQ0FBQyxLQUFLLEVBQUU7UUFDdEQsV0FBVyxFQUFFO1lBQ1gsVUFBVSxFQUFFLEVBQUUsQ0FBQyxnQkFBZ0IsQ0FBQyxHQUFHO1lBQ25DLGFBQWE7U0FDZDtLQUNGLEVBQUUsdUNBQXVDLENBQUMsQ0FBQyxNQUFNLENBQUM7SUFDbkQsSUFBSSxvQkFBYyxDQUFDLEtBQUssRUFBRSxvQkFBb0IsRUFBRTtRQUM5QyxpQkFBaUI7S0FDbEIsQ0FBQyxDQUFDO0lBQ0gsTUFBTSxRQUFRLEdBQUcscUJBQVEsQ0FBQyxTQUFTLENBQUMsS0FBSyxDQUFDLENBQUM7SUFDM0Msc0VBQXNFO0lBQ3RFLFFBQVEsQ0FBQyxlQUFlLENBQUMsdUJBQXVCLEVBQUUsQ0FBQyxDQUFDLENBQUM7SUFFckQseURBQXlEO0lBQ3pELFFBQVEsQ0FBQyxxQkFBcUIsQ0FBQyx1QkFBdUIsRUFBRTtRQUN0RCxXQUFXLEVBQUUseUZBQXlGO1FBQ3RHLElBQUksRUFBRTtZQUNKLFlBQVksRUFBRSxDQUFDLG1EQUFtRCxFQUFFLEtBQUssQ0FBQztTQUMzRTtLQUNGLENBQUMsQ0FBQztBQUNMLENBQUMsQ0FBQyxDQUFDO0FBRUgsSUFBSSxDQUFDLG9GQUFvRixFQUFFLEdBQUcsRUFBRTtJQUM5RixNQUFNLEtBQUssR0FBRyxJQUFJLEdBQUcsQ0FBQyxLQUFLLEVBQUUsQ0FBQztJQUM5QixNQUFNLENBQUMsS0FBSyxDQUFDLENBQUM7SUFDZCxNQUFNLFFBQVEsR0FBRyxxQkFBUSxDQUFDLFNBQVMsQ0FBQyxLQUFLLENBQUMsQ0FBQztJQUMzQyxRQUFRLENBQUMsZUFBZSxDQUFDLHVCQUF1QixFQUFFLENBQUMsQ0FBQyxDQUFDO0FBQ3ZELENBQUMsQ0FBQyxDQUFDO0FBRUgsSUFBSSxDQUFDLHNGQUFzRixFQUFFLEdBQUcsRUFBRTtJQUNoRyxNQUFNLEtBQUssR0FBRyxJQUFJLEdBQUcsQ0FBQyxLQUFLLEVBQUUsQ0FBQztJQUM5QixNQUFNLGlCQUFpQixHQUFHLFFBQVEsQ0FBQyxhQUFhLENBQUMsS0FBSyxFQUFFLEVBQUUsRUFBRSx1Q0FBdUMsQ0FBQyxDQUFDLE1BQU0sQ0FBQztJQUM1RyxJQUFJLG9CQUFjLENBQUMsS0FBSyxFQUFFLG9CQUFvQixFQUFFO1FBQzlDLGlCQUFpQjtLQUNsQixDQUFDLENBQUM7SUFDSCxNQUFNLFFBQVEsR0FBRyxxQkFBUSxDQUFDLFNBQVMsQ0FBQyxLQUFLLENBQUMsQ0FBQztJQUMzQyxRQUFRLENBQUMsZUFBZSxDQUFDLHVCQUF1QixFQUFFLENBQUMsQ0FBQyxDQUFDO0FBQ3ZELENBQUMsQ0FBQyxDQUFDO0FBRUgsSUFBSSxDQUFDLDhEQUE4RCxFQUFFLEdBQUcsRUFBRTtJQUN4RSxNQUFNLEtBQUssR0FBRyxJQUFJLEdBQUcsQ0FBQyxLQUFLLEVBQUUsQ0FBQztJQUM5QixNQUFNLGlCQUFpQixHQUFHLEtBQUssQ0FBQztJQUNoQyxNQUFNLEtBQUssR0FBd0I7UUFDakMsV0FBVyxFQUFFO1lBQ1gsVUFBVSxFQUFFLEtBQUs7WUFDakIsZ0JBQWdCLEVBQUUsSUFBSSxFQUFFLDZCQUE2QjtZQUNyRCxpQkFBaUIsRUFBRTtnQkFDakIsZUFBZSxFQUFFLGlCQUFpQjtnQkFDbEMscUJBQXFCLEVBQUUsaUJBQWlCO2dCQUN4QyxpQkFBaUIsRUFBRSxpQkFBaUI7Z0JBQ3BDLGdCQUFnQixFQUFFLGlCQUFpQjthQUNwQztZQUNELG9CQUFvQixFQUFFLFlBQVksQ0FBQyw2QkFBNkI7U0FDakU7UUFDRCx5QkFBeUIsRUFBRSxLQUFLO0tBQ2pDLENBQUM7SUFDRixNQUFNLFNBQVMsR0FBRyxJQUFJLG9CQUFjLENBQUMsS0FBSyxFQUFFLG9CQUFvQixFQUFFLEtBQUssQ0FBQyxDQUFDO0lBQ3pFLE1BQU0sUUFBUSxHQUFHLHFCQUFRLENBQUMsU0FBUyxDQUFDLEtBQUssQ0FBQyxDQUFDO0lBQzNDLG1CQUFtQjtJQUNuQixRQUFRLENBQUMsZUFBZSxDQUFDLHNDQUFzQyxFQUFFLENBQUMsQ0FBQyxDQUFDO0lBQ3BFLFFBQVEsQ0FBQyxxQkFBcUIsQ0FBQywrQkFBK0IsRUFBRTtRQUM5RCxrQkFBa0IsRUFBRTtZQUNsQixPQUFPLEVBQUU7Z0JBQ1A7b0JBQ0Usa0JBQWtCLEVBQUU7d0JBQ2xCLG9CQUFvQixFQUFFLFdBQVc7cUJBQ2xDO2lCQUNGO2FBQ0Y7U0FDRjtLQUNGLENBQUMsQ0FBQztJQUNILFFBQVEsQ0FBQyxlQUFlLENBQUMsdUNBQXVDLEVBQUUsQ0FBQyxDQUFDLENBQUM7SUFDckUsMkNBQTJDO0lBQzNDLE1BQU0sQ0FBQyxTQUFTLENBQUMsbUJBQW1CLENBQUMsQ0FBQyxJQUFJLENBQUMsU0FBUyxDQUFDLENBQUM7QUFDeEQsQ0FBQyxDQUFDLENBQUM7QUFFSCxJQUFJLENBQUMsdUNBQXVDLEVBQUUsR0FBRyxFQUFFO0lBQ2pELE1BQU0sS0FBSyxHQUFHLElBQUksR0FBRyxDQUFDLEtBQUssRUFBRSxDQUFDO0lBQzlCLE1BQU0sU0FBUyxHQUFHLElBQUksb0JBQWMsQ0FBQyxLQUFLLEVBQUUsb0JBQW9CLEVBQUUsRUFBRSxDQUFDLENBQUM7SUFDdEUsTUFBTSxRQUFRLEdBQUcscUJBQVEsQ0FBQyxTQUFTLENBQUMsS0FBSyxDQUFDLENBQUM7SUFDM0MsbUJBQW1CO0lBQ25CLFFBQVEsQ0FBQyxlQUFlLENBQUMsc0NBQXNDLEVBQUUsQ0FBQyxDQUFDLENBQUM7SUFDcEUsUUFBUSxDQUFDLGVBQWUsQ0FBQyx1Q0FBdUMsRUFBRSxDQUFDLENBQUMsQ0FBQztJQUNyRSwyQ0FBMkM7SUFDM0MsTUFBTSxDQUFDLFNBQVMsQ0FBQyxtQkFBbUIsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxJQUFJLENBQUMsU0FBUyxDQUFDLENBQUM7QUFDNUQsQ0FBQyxDQUFDLENBQUM7QUFFSCxJQUFJLENBQUMsaUZBQWlGLEVBQUUsR0FBRyxFQUFFO0lBQzNGLE1BQU0sS0FBSyxHQUFHLElBQUksR0FBRyxDQUFDLEtBQUssRUFBRSxDQUFDO0lBQzlCLE1BQU0saUJBQWlCLEdBQUcsS0FBSyxDQUFDO0lBQ2hDLE1BQU0sS0FBSyxHQUF3QjtRQUNqQyxXQUFXLEVBQUU7WUFDWCxVQUFVLEVBQUUsS0FBSztZQUNqQixnQkFBZ0IsRUFBRSxJQUFJLEVBQUUsNkJBQTZCO1lBQ3JELGlCQUFpQixFQUFFO2dCQUNqQixlQUFlLEVBQUUsaUJBQWlCO2dCQUNsQyxxQkFBcUIsRUFBRSxpQkFBaUI7Z0JBQ3hDLGlCQUFpQixFQUFFLGlCQUFpQjtnQkFDcEMsZ0JBQWdCLEVBQUUsaUJBQWlCO2FBQ3BDO1lBQ0Qsb0JBQW9CLEVBQUUsWUFBWSxDQUFDLDZCQUE2QjtTQUNqRTtRQUNELHlCQUF5QixFQUFFLEtBQUs7UUFDaEMsMkJBQTJCLEVBQUU7WUFDM0IsZUFBZSxFQUFFO2dCQUNmLE1BQU0sRUFBRSxJQUFJLE9BQU8sQ0FBQyxVQUFVLENBQUMsYUFBYSxFQUFFO29CQUM1QyxRQUFRLEVBQUUsZ0NBQWdDO2lCQUMzQyxDQUFDO2FBQ0g7U0FDRjtLQUNGLENBQUM7SUFDRixJQUFJLG9CQUFjLENBQUMsS0FBSyxFQUFFLG9CQUFvQixFQUFFLEtBQUssQ0FBQyxDQUFDO0lBQ3ZELE1BQU0sUUFBUSxHQUFHLHFCQUFRLENBQUMsU0FBUyxDQUFDLEtBQUssQ0FBQyxDQUFDO0lBQzNDLG1CQUFtQjtJQUNuQixRQUFRLENBQUMscUJBQXFCLENBQUMsK0JBQStCLEVBQUU7UUFDOUQsa0JBQWtCLEVBQUU7WUFDbEIsT0FBTyxFQUFFO2dCQUNQO29CQUNFLFVBQVUsRUFBRSxhQUFhO29CQUN6QixFQUFFLEVBQUUsZ0NBQWdDO2lCQUNyQzthQUNGO1NBQ0Y7S0FDRixDQUFDLENBQUM7QUFDTCxDQUFDLENBQUMsQ0FBQztBQUVILElBQUksQ0FBQyxnRkFBZ0YsRUFBRSxHQUFHLEVBQUU7SUFDMUYsTUFBTSxLQUFLLEdBQUcsSUFBSSxHQUFHLENBQUMsS0FBSyxFQUFFLENBQUM7SUFDOUIsTUFBTSxtQkFBbUIsR0FBRyxJQUFJLEVBQUUsQ0FBQyxNQUFNLENBQUMsS0FBSyxFQUFFLG1CQUFtQixDQUFDLENBQUM7SUFDdEUsSUFBSSxvQkFBYyxDQUFDLEtBQUssRUFBRSxvQkFBb0IsRUFBRTtRQUM5Qyw0QkFBNEIsRUFBRTtZQUM1QixzQkFBc0IsRUFBRSxtQkFBbUI7U0FDNUM7S0FDRixDQUFDLENBQUM7SUFFSCxNQUFNLFFBQVEsR0FBRyxxQkFBUSxDQUFDLFNBQVMsQ0FBQyxLQUFLLENBQUMsQ0FBQztJQUMzQyxRQUFRLENBQUMsZUFBZSxDQUFDLGlCQUFpQixFQUFFLENBQUMsQ0FBQyxDQUFDO0FBRWpELENBQUMsQ0FBQyxDQUFDO0FBRUgsd0JBQXdCO0FBQ3hCLHVDQUF1QztBQUN2Qyx3QkFBd0I7QUFDeEIsSUFBSSxDQUFDLG9FQUFvRSxFQUFFLEdBQUcsRUFBRTtJQUM5RSxNQUFNLEtBQUssR0FBRyxJQUFJLEdBQUcsQ0FBQyxLQUFLLEVBQUUsQ0FBQztJQUM5QixNQUFNLFNBQVMsR0FBRyxJQUFJLEVBQUUsQ0FBQyxNQUFNLENBQUMsS0FBSyxFQUFFLFlBQVksRUFBRSxFQUFFLENBQUMsQ0FBQztJQUV6RCxNQUFNLEdBQUcsR0FBRyxHQUFHLEVBQUU7UUFDZixJQUFJLG9CQUFjLENBQUMsS0FBSyxFQUFFLGVBQWUsRUFBRTtZQUN6QyxXQUFXLEVBQUU7Z0JBQ1gsc0JBQXNCLEVBQUUsU0FBUzthQUNsQztZQUNELGtCQUFrQixFQUFFO2dCQUNsQixVQUFVLEVBQUUsVUFBVTthQUN2QjtTQUNGLENBQUMsQ0FBQztJQUNMLENBQUMsQ0FBQztJQUNGLE1BQU0sQ0FBQyxHQUFHLENBQUMsQ0FBQyxPQUFPLENBQUMsMEVBQTBFLENBQUMsQ0FBQztBQUNsRyxDQUFDLENBQUMsQ0FBQztBQUVILElBQUksQ0FBQyx1RUFBdUUsRUFBRSxHQUFHLEVBQUU7SUFDakYsTUFBTSxLQUFLLEdBQUcsSUFBSSxHQUFHLENBQUMsS0FBSyxFQUFFLENBQUM7SUFDOUIsTUFBTSxTQUFTLEdBQUcsSUFBSSxFQUFFLENBQUMsTUFBTSxDQUFDLEtBQUssRUFBRSx1QkFBdUIsRUFBRSxFQUFFLENBQUMsQ0FBQztJQUVwRSxNQUFNLEdBQUcsR0FBRyxHQUFHLEVBQUU7UUFDZixJQUFJLG9CQUFjLENBQUMsS0FBSyxFQUFFLGVBQWUsRUFBRTtZQUN6QyxXQUFXLEVBQUU7Z0JBQ1gsc0JBQXNCLEVBQUUsU0FBUzthQUNsQztZQUNELGVBQWUsRUFBRSxLQUFLO1NBQ3ZCLENBQUMsQ0FBQztJQUNMLENBQUMsQ0FBQztJQUNGLE1BQU0sQ0FBQyxHQUFHLENBQUMsQ0FBQyxPQUFPLENBQUMsbUZBQW1GLENBQUMsQ0FBQztBQUMzRyxDQUFDLENBQUMsQ0FBQztBQUVILElBQUksQ0FBQyxvRUFBb0UsRUFBRSxHQUFHLEVBQUU7SUFDOUUsTUFBTSxLQUFLLEdBQUcsSUFBSSxHQUFHLENBQUMsS0FBSyxFQUFFLENBQUM7SUFFOUIsTUFBTSxHQUFHLEdBQUcsR0FBRyxFQUFFO1FBQ2YsSUFBSSxvQkFBYyxDQUFDLEtBQUssRUFBRSxlQUFlLEVBQUU7WUFDekMsa0JBQWtCLEVBQUU7Z0JBQ2xCLFVBQVUsRUFBRSxVQUFVO2FBQ3ZCO1lBQ0QsZUFBZSxFQUFFLEtBQUs7U0FDdkIsQ0FBQyxDQUFDO0lBQ0wsQ0FBQyxDQUFDO0lBQ0Ysa0ZBQWtGO0lBQ2xGLE1BQU0sQ0FBQyxHQUFHLENBQUMsQ0FBQyxPQUFPLENBQUMsNkdBQTZHLENBQUMsQ0FBQztBQUNySSxDQUFDLENBQUMsQ0FBQztBQUVILHVGQUF1RjtBQUN2RixJQUFJLENBQUMsMEVBQTBFLEVBQUUsR0FBRyxFQUFFO0lBQ3BGLE1BQU0sS0FBSyxHQUFHLElBQUksR0FBRyxDQUFDLEtBQUssRUFBRSxDQUFDO0lBRTlCLE1BQU0sUUFBUSxHQUFHLFdBQVcsQ0FBQztJQUM3QixNQUFNLFNBQVMsR0FBRyxJQUFJLG9CQUFjLENBQUMsS0FBSyxFQUFFLGVBQWUsRUFBRTtRQUMzRCxXQUFXLEVBQUU7WUFDWCxhQUFhLEVBQUUsR0FBRyxDQUFDLGFBQWEsQ0FBQyxPQUFPO1NBQ3pDO1FBQ0Qsa0JBQWtCLEVBQUU7WUFDbEIsYUFBYSxFQUFFLEdBQUcsQ0FBQyxhQUFhLENBQUMsT0FBTztZQUN4QyxpQkFBaUIsRUFBRSxJQUFJO1lBQ3ZCLFVBQVUsRUFBRSxRQUFRO1NBQ3JCO0tBQ0YsQ0FBQyxDQUFDO0lBRUgsTUFBTSxDQUFDLFNBQVMsQ0FBQyxlQUFlLENBQUMsQ0FBQyxXQUFXLEVBQUUsQ0FBQztJQUVoRCxNQUFNLFFBQVEsR0FBRyxxQkFBUSxDQUFDLFNBQVMsQ0FBQyxLQUFLLENBQUMsQ0FBQztJQUMzQyxRQUFRLENBQUMsZUFBZSxDQUFDLGlCQUFpQixFQUFFLENBQUMsQ0FBQyxDQUFDO0lBRS9DLFFBQVEsQ0FBQyxxQkFBcUIsQ0FBQyxpQkFBaUIsRUFBRTtRQUNoRCxVQUFVLEVBQUUsUUFBUTtLQUNyQixDQUFDLENBQUM7SUFFSCxRQUFRLENBQUMscUJBQXFCLENBQUMsaUJBQWlCLEVBQUU7UUFDaEQsb0JBQW9CLEVBQUU7WUFDcEIscUJBQXFCLEVBQUU7Z0JBQ3JCLEdBQUcsRUFBRSxxQ0FBcUM7YUFDM0M7U0FDRjtLQUNGLENBQUMsQ0FBQztBQUNMLENBQUMsQ0FBQyxDQUFDO0FBQ0gsSUFBSSxDQUFDLDBGQUEwRixFQUFFLEdBQUcsRUFBRTtJQUNwRyxNQUFNLFFBQVEsR0FBRyxXQUFXLENBQUM7SUFDN0IsTUFBTSxLQUFLLEdBQUcsSUFBSSxHQUFHLENBQUMsS0FBSyxFQUFFLENBQUM7SUFDOUIsTUFBTSxTQUFTLEdBQUcsSUFBSSxFQUFFLENBQUMsTUFBTSxDQUFDLEtBQUssRUFBRSxVQUFVLEVBQUU7UUFDakQsVUFBVSxFQUFFLFFBQVE7S0FDckIsQ0FBQyxDQUFDO0lBRUgsTUFBTSxTQUFTLEdBQUcsSUFBSSxvQkFBYyxDQUFDLEtBQUssRUFBRSxlQUFlLEVBQUU7UUFDM0QsV0FBVyxFQUFFO1lBQ1gsc0JBQXNCLEVBQUUsU0FBUztTQUNsQztLQUNGLENBQUMsQ0FBQztJQUVILE1BQU0sQ0FBQyxTQUFTLENBQUMsZUFBZSxDQUFDLENBQUMsV0FBVyxFQUFFLENBQUM7SUFDaEQsTUFBTSxRQUFRLEdBQUcscUJBQVEsQ0FBQyxTQUFTLENBQUMsS0FBSyxDQUFDLENBQUM7SUFFM0MsUUFBUSxDQUFDLGVBQWUsQ0FBQyxpQkFBaUIsRUFBRSxDQUFDLENBQUMsQ0FBQztJQUMvQyxRQUFRLENBQUMscUJBQXFCLENBQUMsaUJBQWlCLEVBQUU7UUFDaEQsb0JBQW9CLEVBQUU7WUFDcEIscUJBQXFCLEVBQUU7Z0JBQ3JCLEdBQUcsRUFBRSxpQkFBaUI7YUFDdkI7U0FDRjtLQUNGLENBQUMsQ0FBQztBQUNMLENBQUMsQ0FBQyxDQUFDO0FBRUgsd0JBQXdCO0FBQ3hCLHdCQUF3QjtBQUN4Qix3QkFBd0I7QUFDeEIsSUFBSSxDQUFDLG1HQUFtRyxFQUFFLEdBQUcsRUFBRTtJQUM3RyxNQUFNLEtBQUssR0FBRyxJQUFJLEdBQUcsQ0FBQyxLQUFLLEVBQUUsQ0FBQztJQUM5QixNQUFNLFNBQVMsR0FBRyxJQUFJLEVBQUUsQ0FBQyxNQUFNLENBQUMsS0FBSyxFQUFFLHVCQUF1QixFQUFFLEVBQUUsQ0FBQyxDQUFDO0lBRXBFLE1BQU0sR0FBRyxHQUFHLEdBQUcsRUFBRTtRQUNmLElBQUksb0JBQWMsQ0FBQyxLQUFLLEVBQUUsZUFBZSxFQUFFO1lBQ3pDLDJCQUEyQixFQUFFO2dCQUMzQixTQUFTO2FBQ1Y7WUFDRCw0QkFBNEIsRUFBRTtnQkFDNUIsYUFBYSxFQUFFLEdBQUcsQ0FBQyxhQUFhLENBQUMsT0FBTztnQkFDeEMsaUJBQWlCLEVBQUUsSUFBSTthQUN4QjtTQUNGLENBQUMsQ0FBQztJQUNMLENBQUMsQ0FBQztJQUVGLE1BQU0sQ0FBQyxHQUFHLENBQUMsQ0FBQyxPQUFPLEVBQUUsQ0FBQztBQUN4QixDQUFDLENBQUMsQ0FBQztBQUNILElBQUksQ0FBQyxpREFBaUQsRUFBRSxHQUFHLEVBQUU7SUFDM0QsTUFBTSxLQUFLLEdBQUcsSUFBSSxHQUFHLENBQUMsS0FBSyxFQUFFLENBQUM7SUFFOUIsTUFBTSxRQUFRLEdBQUcsV0FBVyxDQUFDO0lBQzdCLE1BQU0sU0FBUyxHQUFHLElBQUksb0JBQWMsQ0FBQyxLQUFLLEVBQUUsZUFBZSxFQUFFO1FBQzNELDRCQUE0QixFQUFFO1lBQzVCLGFBQWEsRUFBRSxHQUFHLENBQUMsYUFBYSxDQUFDLE9BQU87WUFDeEMsaUJBQWlCLEVBQUUsSUFBSTtZQUN2QixVQUFVLEVBQUUsUUFBUTtTQUNyQjtLQUNGLENBQUMsQ0FBQztJQUVILE1BQU0sQ0FBQyxTQUFTLENBQUMsdUJBQXVCLENBQUMsQ0FBQyxXQUFXLEVBQUUsQ0FBQztJQUV4RCxNQUFNLFFBQVEsR0FBRyxxQkFBUSxDQUFDLFNBQVMsQ0FBQyxLQUFLLENBQUMsQ0FBQztJQUMzQyxRQUFRLENBQUMsZUFBZSxDQUFDLGlCQUFpQixFQUFFLENBQUMsQ0FBQyxDQUFDO0lBRS9DLFFBQVEsQ0FBQyxxQkFBcUIsQ0FBQyxpQkFBaUIsRUFBRTtRQUNoRCxVQUFVLEVBQUUsUUFBUTtLQUNyQixDQUFDLENBQUM7QUFFTCxDQUFDLENBQUMsQ0FBQztBQUNILElBQUksQ0FBQywyREFBMkQsRUFBRSxHQUFHLEVBQUU7SUFDckUsTUFBTSxLQUFLLEdBQUcsSUFBSSxHQUFHLENBQUMsS0FBSyxFQUFFLENBQUM7SUFFOUIsTUFBTSxTQUFTLEdBQUcsTUFBTSxDQUFDLEtBQUssRUFBRSxFQUFFLDJCQUEyQixFQUFFLEVBQUUsYUFBYSxFQUFFLEtBQUssRUFBRSxFQUFFLENBQUMsQ0FBQztJQUMzRixNQUFNLFFBQVEsR0FBRyxxQkFBUSxDQUFDLFNBQVMsQ0FBQyxLQUFLLENBQUMsQ0FBQztJQUUzQyxpRkFBaUY7SUFDakYsUUFBUSxDQUFDLGVBQWUsQ0FBQyxpQkFBaUIsRUFBRSxDQUFDLENBQUMsQ0FBQztJQUUvQywyQkFBMkI7SUFDM0IsUUFBUSxDQUFDLHlCQUF5QixDQUFDLCtCQUErQixFQUFFO1FBQ2xFLGtCQUFrQixFQUFFO1lBQ2xCLE9BQU8sRUFBRSxrQkFBSyxDQUFDLFFBQVEsRUFBRTtTQUMxQjtLQUNGLEVBQUUsQ0FBQyxDQUFDLENBQUM7SUFDTixNQUFNLENBQUMsU0FBUyxDQUFDLHVCQUF1QixLQUFLLFNBQVMsQ0FBQyxDQUFDO0FBQzFELENBQUMsQ0FBQyxDQUFDO0FBQ0gsSUFBSSxDQUFDLGlHQUFpRyxFQUFFLEdBQUcsRUFBRTtJQUMzRyxNQUFNLFFBQVEsR0FBRyxjQUFjLENBQUM7SUFDaEMsTUFBTSxLQUFLLEdBQUcsSUFBSSxHQUFHLENBQUMsS0FBSyxFQUFFLENBQUM7SUFDOUIsTUFBTSxTQUFTLEdBQUcsSUFBSSxFQUFFLENBQUMsTUFBTSxDQUFDLEtBQUssRUFBRSx1QkFBdUIsRUFBRTtRQUM5RCxVQUFVLEVBQUUsUUFBUTtLQUNyQixDQUFDLENBQUM7SUFFSCxvQkFBb0I7SUFDcEIsTUFBTSxTQUFTLEdBQUcsSUFBSSxvQkFBYyxDQUFDLEtBQUssRUFBRSxlQUFlLEVBQUU7UUFDM0QsMkJBQTJCLEVBQUU7WUFDM0IsU0FBUztTQUNWO0tBQ0YsQ0FBQyxDQUFDO0lBRUgsTUFBTSxDQUFDLFNBQVMsQ0FBQyx1QkFBdUIsQ0FBQyxDQUFDLFdBQVcsRUFBRSxDQUFDO0lBQ3hELE1BQU0sUUFBUSxHQUFHLHFCQUFRLENBQUMsU0FBUyxDQUFDLEtBQUssQ0FBQyxDQUFDO0lBRTNDLDZFQUE2RTtJQUM3RSxRQUFRLENBQUMsZUFBZSxDQUFDLGlCQUFpQixFQUFFLENBQUMsQ0FBQyxDQUFDO0lBRS9DLGtFQUFrRTtJQUNsRSxRQUFRLENBQUMscUJBQXFCLENBQUMsK0JBQStCLEVBQUU7UUFDOUQsa0JBQWtCLEVBQUU7WUFDbEIsT0FBTyxFQUFFO2dCQUNQLE1BQU0sRUFBRTtvQkFDTixZQUFZLEVBQUU7d0JBQ1osNkJBQTZCO3dCQUM3QixvQkFBb0I7cUJBQ3JCO2lCQUNGO2FBQ0Y7U0FDRjtLQUNGLENBQUMsQ0FBQztBQUVMLENBQUMsQ0FBQyxDQUFDO0FBRUgsd0JBQXdCO0FBQ3hCLDJDQUEyQztBQUMzQyx3QkFBd0I7QUFDeEIsSUFBSSxDQUFDLDJIQUEySCxFQUFFLEdBQUcsRUFBRTtJQUNySSxNQUFNLEtBQUssR0FBRyxJQUFJLEdBQUcsQ0FBQyxLQUFLLEVBQUUsQ0FBQztJQUM5QixNQUFNLFNBQVMsR0FBRyxJQUFJLEVBQUUsQ0FBQyxNQUFNLENBQUMsS0FBSyxFQUFFLHVCQUF1QixFQUFFLEVBQUUsQ0FBQyxDQUFDO0lBRXBFLE1BQU0sR0FBRyxHQUFHLEdBQUcsRUFBRTtRQUNmLElBQUksb0JBQWMsQ0FBQyxLQUFLLEVBQUUsZUFBZSxFQUFFO1lBQ3pDLDRCQUE0QixFQUFFO2dCQUM1QixzQkFBc0IsRUFBRSxTQUFTO2dCQUNqQyxhQUFhLEVBQUUsR0FBRyxDQUFDLGFBQWEsQ0FBQyxPQUFPO2dCQUN4QyxpQkFBaUIsRUFBRSxJQUFJO2FBQ3hCO1lBQ0QsMkNBQTJDLEVBQUU7Z0JBQzNDLFVBQVUsRUFBRSxpQ0FBaUM7YUFDOUM7U0FDRixDQUFDLENBQUM7SUFDTCxDQUFDLENBQUM7SUFFRixNQUFNLENBQUMsR0FBRyxDQUFDLENBQUMsT0FBTyxDQUNqQixnSUFBZ0ksQ0FBQyxDQUFDO0FBQ3RJLENBQUMsQ0FBQyxDQUFDO0FBQ0gsSUFBSSxDQUFDLG9HQUFvRyxFQUFFLEdBQUcsRUFBRTtJQUM5RyxNQUFNLEtBQUssR0FBRyxJQUFJLEdBQUcsQ0FBQyxLQUFLLEVBQUUsQ0FBQztJQUU5QixNQUFNLEdBQUcsR0FBRyxHQUFHLEVBQUU7UUFDZixJQUFJLG9CQUFjLENBQUMsS0FBSyxFQUFFLGVBQWUsRUFBRTtZQUN6QyxzQkFBc0IsRUFBRSxLQUFLO1lBQzdCLDJDQUEyQyxFQUFFO2dCQUMzQyxVQUFVLEVBQUUsaUNBQWlDO2FBQzlDO1NBQ0YsQ0FBQyxDQUFDO0lBQ0wsQ0FBQyxDQUFDO0lBRUYsTUFBTSxDQUFDLEdBQUcsQ0FBQyxDQUFDLE9BQU8sQ0FBQywwR0FBMEcsQ0FBQyxDQUFDO0FBQ2xJLENBQUMsQ0FBQyxDQUFDO0FBQ0gsSUFBSSxDQUFDLDRHQUE0RyxFQUFFLEdBQUcsRUFBRTtJQUN0SCxNQUFNLEtBQUssR0FBRyxJQUFJLEdBQUcsQ0FBQyxLQUFLLEVBQUUsQ0FBQztJQUM5QixNQUFNLFNBQVMsR0FBRyxJQUFJLEVBQUUsQ0FBQyxNQUFNLENBQUMsS0FBSyxFQUFFLHVCQUF1QixFQUFFLEVBQUUsQ0FBQyxDQUFDO0lBRXBFLE1BQU0sR0FBRyxHQUFHLEdBQUcsRUFBRTtRQUNmLElBQUksb0JBQWMsQ0FBQyxLQUFLLEVBQUUsZUFBZSxFQUFFO1lBQ3pDLDRCQUE0QixFQUFFO2dCQUM1QixzQkFBc0IsRUFBRSxTQUFTO2dCQUNqQyxhQUFhLEVBQUUsR0FBRyxDQUFDLGFBQWEsQ0FBQyxPQUFPO2dCQUN4QyxpQkFBaUIsRUFBRSxJQUFJO2FBQ3hCO1lBQ0Qsc0JBQXNCLEVBQUUsS0FBSztTQUM5QixDQUFDLENBQUM7SUFDTCxDQUFDLENBQUM7SUFFRixNQUFNLENBQUMsR0FBRyxDQUFDLENBQUMsT0FBTyxDQUFDLHVIQUF1SCxDQUFDLENBQUM7QUFDL0ksQ0FBQyxDQUFDLENBQUM7QUFDSCxJQUFJLENBQUMsZ0VBQWdFLEVBQUUsR0FBRyxFQUFFO0lBQzFFLE1BQU0sS0FBSyxHQUFHLElBQUksR0FBRyxDQUFDLEtBQUssRUFBRSxDQUFDO0lBRTlCLElBQUksb0JBQWMsQ0FBQyxLQUFLLEVBQUUsZUFBZSxFQUFFO1FBQ3pDLDJDQUEyQyxFQUFFO1lBQzNDLG9CQUFvQixFQUFFLGFBQWE7WUFDbkMsb0JBQW9CLEVBQUUsZ0JBQWdCO1NBQ3ZDO0tBQ0YsQ0FBQyxDQUFDO0lBRUgsTUFBTSxRQUFRLEdBQUcscUJBQVEsQ0FBQyxTQUFTLENBQUMsS0FBSyxDQUFDLENBQUM7SUFFM0MseUhBQXlIO0lBQ3pILFFBQVEsQ0FBQyxlQUFlLENBQUMsaUJBQWlCLEVBQUUsQ0FBQyxDQUFDLENBQUM7SUFDL0MsUUFBUSxDQUFDLHFCQUFxQixDQUFDLGlCQUFpQixFQUFFO1FBQ2hELG9CQUFvQixFQUFFO1lBQ3BCLGFBQWEsRUFBRSxhQUFhO1lBQzVCLGFBQWEsRUFBRSxnQkFBZ0I7U0FDaEM7S0FDRixDQUFDLENBQUM7QUFFTCxDQUFDLENBQUMsQ0FBQztBQUNILElBQUksQ0FBQyw4RkFBOEYsRUFBRSxHQUFHLEVBQUU7SUFDeEcsTUFBTSxLQUFLLEdBQUcsSUFBSSxHQUFHLENBQUMsS0FBSyxFQUFFLENBQUM7SUFDOUIsTUFBTSxRQUFRLEdBQUcsZUFBZSxDQUFDO0lBQ2pDLE1BQU0sc0JBQXNCLEdBQUcsSUFBSSxFQUFFLENBQUMsTUFBTSxDQUFDLEtBQUssRUFBRSw2QkFBNkIsRUFBRTtRQUNqRixVQUFVLEVBQUUsUUFBUTtLQUNyQixDQUFDLENBQUM7SUFFSCxJQUFJLG9CQUFjLENBQUMsS0FBSyxFQUFFLGVBQWUsRUFBRTtRQUN6Qyw0QkFBNEIsRUFBRTtZQUM1QixzQkFBc0IsRUFBRSxzQkFBc0I7U0FDL0M7S0FDRixDQUFDLENBQUM7SUFFSCxNQUFNLFFBQVEsR0FBRyxxQkFBUSxDQUFDLFNBQVMsQ0FBQyxLQUFLLENBQUMsQ0FBQztJQUUzQyx5SEFBeUg7SUFDekgsUUFBUSxDQUFDLGVBQWUsQ0FBQyxpQkFBaUIsRUFBRSxDQUFDLENBQUMsQ0FBQztJQUUvQyxRQUFRLENBQUMscUJBQXFCLENBQUMsaUJBQWlCLEVBQUU7UUFDaEQsVUFBVSxFQUFFLFFBQVE7S0FDckIsQ0FBQyxDQUFDO0lBRUgsUUFBUSxDQUFDLHFCQUFxQixDQUFDLGlCQUFpQixFQUFFO1FBQ2hELG9CQUFvQixFQUFFO1lBQ3BCLHFCQUFxQixFQUFFO2dCQUNyQixHQUFHLEVBQUUsZ0NBQWdDO2FBQ3RDO1NBQ0Y7S0FDRixDQUFDLENBQUM7QUFFTCxDQUFDLENBQUMsQ0FBQztBQUVILElBQUksQ0FBQyxrRUFBa0UsRUFBRSxHQUFHLEVBQUU7SUFDNUUsTUFBTSxLQUFLLEdBQUcsSUFBSSxHQUFHLENBQUMsS0FBSyxFQUFFLENBQUM7SUFFOUIsTUFBTSxTQUFTLEdBQUcsSUFBSSxvQkFBYyxDQUFDLEtBQUssRUFBRSxlQUFlLEVBQUU7UUFDM0QsMkNBQTJDLEVBQUU7WUFDM0Msb0JBQW9CLEVBQUUsYUFBYTtZQUNuQyxvQkFBb0IsRUFBRSxnQkFBZ0I7U0FDdkM7S0FDRixDQUFDLENBQUM7SUFFSCxNQUFNLFFBQVEsR0FBRyxxQkFBUSxDQUFDLFNBQVMsQ0FBQyxLQUFLLENBQUMsQ0FBQztJQUUzQyx5SEFBeUg7SUFDekgsUUFBUSxDQUFDLGVBQWUsQ0FBQyxpQkFBaUIsRUFBRSxDQUFDLENBQUMsQ0FBQztJQUMvQyxRQUFRLENBQUMscUJBQXFCLENBQUMsaUJBQWlCLEVBQUU7UUFDaEQsb0JBQW9CLEVBQUU7WUFDcEIsYUFBYSxFQUFFLGFBQWE7WUFDNUIsYUFBYSxFQUFFLGdCQUFnQjtTQUNoQztLQUNGLENBQUMsQ0FBQztJQUNILE1BQU0sQ0FBQyxTQUFTLENBQUMsc0NBQXNDLENBQUMsQ0FBQyxXQUFXLEVBQUUsQ0FBQztJQUN2RSxNQUFNLENBQUMsU0FBUyxDQUFDLHNDQUF1QyxDQUFDLFVBQVUsQ0FBQyxDQUFDLFdBQVcsRUFBRSxDQUFDO0FBQ3JGLENBQUMsQ0FBQyxDQUFDO0FBRUgsSUFBSSxDQUFDLG1EQUFtRCxFQUFFLEdBQUcsRUFBRTtJQUM3RCxNQUFNLEtBQUssR0FBRyxJQUFJLEdBQUcsQ0FBQyxLQUFLLEVBQUUsQ0FBQztJQUU5QixNQUFNLFNBQVMsR0FBRyxJQUFJLG9CQUFjLENBQUMsS0FBSyxFQUFFLGVBQWUsRUFBRTtRQUMzRCxzQkFBc0IsRUFBRSxLQUFLO0tBQzlCLENBQUMsQ0FBQztJQUVILE1BQU0sUUFBUSxHQUFHLHFCQUFRLENBQUMsU0FBUyxDQUFDLEtBQUssQ0FBQyxDQUFDO0lBRTNDLHlIQUF5SDtJQUN6SCxRQUFRLENBQUMsZUFBZSxDQUFDLGlCQUFpQixFQUFFLENBQUMsQ0FBQyxDQUFDO0lBQy9DLE1BQU0sQ0FBQyxTQUFTLENBQUMsdUJBQXVCLENBQUMsQ0FBQyxXQUFXLEVBQUUsQ0FBQztJQUN4RCxNQUFNLENBQUMsU0FBUyxDQUFDLHNDQUFzQyxDQUFDLENBQUMsR0FBRyxDQUFDLFdBQVcsRUFBRSxDQUFDO0FBQzdFLENBQUMsQ0FBQyxDQUFDO0FBRUgsSUFBSSxDQUFDLHlDQUF5QyxFQUFFLEdBQUcsRUFBRTtJQUNuRCxNQUFNLEtBQUssR0FBRyxJQUFJLEdBQUcsQ0FBQyxLQUFLLEVBQUUsQ0FBQztJQUU5QixNQUFNLGdCQUFnQixHQUFHLFFBQVEsQ0FBQyxpQkFBaUIsQ0FBQyxLQUFLLEVBQUUsYUFBYSxFQUFFO1FBQ3hFLGFBQWEsRUFBRSwyQkFBYSxDQUFDLE9BQU87UUFDcEMsaUJBQWlCLEVBQUUsSUFBSTtLQUN4QixDQUFDLENBQUM7SUFFSCxNQUFNLG1CQUFtQixHQUFHLElBQUksdUNBQXNCLENBQUMsS0FBSyxFQUFFLGVBQWUsRUFBRTtRQUM3RSx5QkFBeUIsRUFBRTtZQUN6QixJQUFJLEVBQUUsUUFBUSxDQUFDLHVCQUF1QixDQUFDLG9CQUFvQixFQUFFLENBQUMsVUFBVSxDQUFDLENBQUM7WUFDMUUsNkJBQTZCLEVBQUUsSUFBSTtZQUNuQyxlQUFlLEVBQUUsUUFBUTtZQUN6QixlQUFlLEVBQUUsT0FBTztZQUN4QixXQUFXLEVBQUUsd0RBQXdEO1NBQ3RFO0tBQ0YsQ0FBQyxDQUFDO0lBRUgsTUFBTSxnQkFBZ0IsR0FBRyxJQUFJLFFBQVEsQ0FBQyxXQUFXLENBQUMsZ0JBQWdCLEVBQUU7UUFDbEUsbUJBQW1CO0tBQ3BCLENBQUMsQ0FBQztJQUVILElBQUksb0JBQWMsQ0FBQyxLQUFLLEVBQUUsb0JBQW9CLEVBQUU7UUFDOUMsMkJBQTJCLEVBQUU7WUFDM0IsbUJBQW1CLEVBQUU7Z0JBQ25CLGtCQUFrQixFQUFFO29CQUNsQixNQUFNLEVBQUUsZ0JBQWdCO29CQUN4QixXQUFXLEVBQUUsNEJBQVcsQ0FBQyxnQkFBZ0I7aUJBQzFDO2dCQUNELFdBQVcsRUFBRTtvQkFDWCxXQUFXLEVBQUUsNEJBQVcsQ0FBQyxnQkFBZ0I7aUJBQzFDO2FBQ0Y7U0FDRjtLQUNGLENBQUMsQ0FBQztJQUVILE1BQU0sUUFBUSxHQUFHLHFCQUFRLENBQUMsU0FBUyxDQUFDLEtBQUssQ0FBQyxDQUFDO0lBQzNDLFFBQVEsQ0FBQyxxQkFBcUIsQ0FBQywrQkFBK0IsRUFBRTtRQUM5RCxrQkFBa0IsRUFBRTtZQUNsQixjQUFjLEVBQUU7Z0JBQ2Q7b0JBQ0UsYUFBYSxFQUFFLHNDQUFzQztvQkFDckQsUUFBUSxFQUFFLElBQUk7b0JBQ2QsV0FBVyxFQUFFLGtCQUFrQjtvQkFDL0IsY0FBYyxFQUFFLHVEQUF1RDtvQkFDdkUsb0JBQW9CLEVBQUUsV0FBVztpQkFDbEM7Z0JBQ0Q7b0JBQ0UsYUFBYSxFQUFFLHNDQUFzQztvQkFDckQsUUFBUSxFQUFFLElBQUk7b0JBQ2QsV0FBVyxFQUFFLFdBQVc7b0JBQ3hCLGNBQWMsRUFBRSx1REFBdUQ7b0JBQ3ZFLG9CQUFvQixFQUFFLFdBQVc7aUJBQ2xDO2FBQ0Y7U0FDRjtLQUNGLENBQUMsQ0FBQztBQUNMLENBQUMsQ0FBQyxDQUFDO0FBRUgsSUFBSSxDQUFDLHVEQUF1RCxFQUFFLEdBQUcsRUFBRTtJQUNqRSxNQUFNLEtBQUssR0FBRyxJQUFJLG1CQUFLLEVBQUUsQ0FBQztJQUMxQixNQUFNLEtBQUssR0FBd0I7UUFDakMsMkJBQTJCLEVBQUU7WUFDM0IsZUFBZSxFQUFFLElBQUk7U0FDdEI7S0FDRixDQUFDO0lBRUYsTUFBTSxHQUFHLEdBQUcsR0FBRyxFQUFFO1FBQ2YsSUFBSSxvQkFBYyxDQUFDLEtBQUssRUFBRSxnQkFBZ0IsRUFBRSxLQUFLLENBQUMsQ0FBQztJQUNyRCxDQUFDLENBQUM7SUFFRixNQUFNLENBQUMsR0FBRyxDQUFDLENBQUMsT0FBTyxDQUFDLHNFQUFzRSxDQUFDLENBQUM7QUFDOUYsQ0FBQyxDQUFDLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyIvKipcbiAqICBDb3B5cmlnaHQgQW1hem9uLmNvbSwgSW5jLiBvciBpdHMgYWZmaWxpYXRlcy4gQWxsIFJpZ2h0cyBSZXNlcnZlZC5cbiAqXG4gKiAgTGljZW5zZWQgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlLCBWZXJzaW9uIDIuMCAodGhlIFwiTGljZW5zZVwiKS4gWW91IG1heSBub3QgdXNlIHRoaXMgZmlsZSBleGNlcHQgaW4gY29tcGxpYW5jZVxuICogIHdpdGggdGhlIExpY2Vuc2UuIEEgY29weSBvZiB0aGUgTGljZW5zZSBpcyBsb2NhdGVkIGF0XG4gKlxuICogICAgICBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjBcbiAqXG4gKiAgb3IgaW4gdGhlICdsaWNlbnNlJyBmaWxlIGFjY29tcGFueWluZyB0aGlzIGZpbGUuIFRoaXMgZmlsZSBpcyBkaXN0cmlidXRlZCBvbiBhbiAnQVMgSVMnIEJBU0lTLCBXSVRIT1VUIFdBUlJBTlRJRVNcbiAqICBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBleHByZXNzIG9yIGltcGxpZWQuIFNlZSB0aGUgTGljZW5zZSBmb3IgdGhlIHNwZWNpZmljIGxhbmd1YWdlIGdvdmVybmluZyBwZXJtaXNzaW9uc1xuICogIGFuZCBsaW1pdGF0aW9ucyB1bmRlciB0aGUgTGljZW5zZS5cbiAqL1xuXG5pbXBvcnQgeyBNYXRjaCwgVGVtcGxhdGUgfSBmcm9tIFwiYXdzLWNkay1saWIvYXNzZXJ0aW9uc1wiO1xuaW1wb3J0ICogYXMgczMgZnJvbSAnYXdzLWNkay1saWIvYXdzLXMzJztcbmltcG9ydCAqIGFzIGNkayBmcm9tIFwiYXdzLWNkay1saWJcIjtcbmltcG9ydCB7IER1cmF0aW9uLCBSZW1vdmFsUG9saWN5LCBTdGFjayB9IGZyb20gXCJhd3MtY2RrLWxpYlwiO1xuaW1wb3J0IHsgQ2xvdWRGcm9udFRvUzMsIENsb3VkRnJvbnRUb1MzUHJvcHMgfSBmcm9tIFwiLi4vbGliXCI7XG5pbXBvcnQgKiBhcyBhY20gZnJvbSAnYXdzLWNkay1saWIvYXdzLWNlcnRpZmljYXRlbWFuYWdlcic7XG5pbXBvcnQgKiBhcyBkZWZhdWx0cyBmcm9tICdAYXdzLXNvbHV0aW9ucy1jb25zdHJ1Y3RzL2NvcmUnO1xuaW1wb3J0IHsgS2V5IH0gZnJvbSBcImF3cy1jZGstbGliL2F3cy1rbXNcIjtcbmltcG9ydCAqIGFzIG9yaWdpbnMgZnJvbSAnYXdzLWNkay1saWIvYXdzLWNsb3VkZnJvbnQtb3JpZ2lucyc7XG5pbXBvcnQgeyBDYWNoZVBvbGljeSwgQ2ZuT3JpZ2luQWNjZXNzQ29udHJvbCB9IGZyb20gXCJhd3MtY2RrLWxpYi9hd3MtY2xvdWRmcm9udFwiO1xuXG5mdW5jdGlvbiBkZXBsb3koc3RhY2s6IGNkay5TdGFjaywgcHJvcHM/OiBDbG91ZEZyb250VG9TM1Byb3BzKSB7XG4gIHJldHVybiBuZXcgQ2xvdWRGcm9udFRvUzMoc3RhY2ssICd0ZXN0LWNsb3VkZnJvbnQtczMnLCB7XG4gICAgYnVja2V0UHJvcHM6IHtcbiAgICAgIHJlbW92YWxQb2xpY3k6IGNkay5SZW1vdmFsUG9saWN5LkRFU1RST1ksXG4gICAgfSxcbiAgICAuLi5wcm9wc1xuICB9KTtcbn1cblxudGVzdCgnY29uc3RydWN0IGRlZmF1bHRzIHNldCBwcm9wZXJ0aWVzIGNvcnJlY3RseScsICgpID0+IHtcbiAgY29uc3Qgc3RhY2sgPSBuZXcgY2RrLlN0YWNrKCk7XG4gIGNvbnN0IGNvbnN0cnVjdCA9IG5ldyBDbG91ZEZyb250VG9TMyhzdGFjaywgJ3Rlc3QtY2xvdWRmcm9udC1zMycsIHt9KTtcblxuICBleHBlY3QoY29uc3RydWN0LmNsb3VkRnJvbnRXZWJEaXN0cmlidXRpb24pLnRvQmVEZWZpbmVkKCk7XG4gIGV4cGVjdChjb25zdHJ1Y3QuY2xvdWRGcm9udEZ1bmN0aW9uKS50b0JlRGVmaW5lZCgpO1xuICBleHBlY3QoY29uc3RydWN0LmNsb3VkRnJvbnRMb2dnaW5nQnVja2V0KS50b0JlRGVmaW5lZCgpO1xuICBleHBlY3QoY29uc3RydWN0LnMzQnVja2V0KS50b0JlRGVmaW5lZCgpO1xuICBleHBlY3QoY29uc3RydWN0LnMzTG9nZ2luZ0J1Y2tldCkudG9CZURlZmluZWQoKTtcbiAgZXhwZWN0KGNvbnN0cnVjdC5zM0J1Y2tldEludGVyZmFjZSkudG9CZURlZmluZWQoKTtcbiAgZXhwZWN0KGNvbnN0cnVjdC5jbG91ZEZyb250TG9nZ2luZ0J1Y2tldEFjY2Vzc0xvZ0J1Y2tldCkudG9CZURlZmluZWQoKTtcbiAgZXhwZWN0KGNvbnN0cnVjdC5vcmlnaW5BY2Nlc3NDb250cm9sKS50b0JlRGVmaW5lZCgpO1xufSk7XG5cbnRlc3QoJ2NoZWNrIHMzQnVja2V0IGRlZmF1bHQgZW5jcnlwdGlvbicsICgpID0+IHtcbiAgY29uc3Qgc3RhY2sgPSBuZXcgY2RrLlN0YWNrKCk7XG4gIGRlcGxveShzdGFjayk7XG4gIGNvbnN0IHRlbXBsYXRlID0gVGVtcGxhdGUuZnJvbVN0YWNrKHN0YWNrKTtcbiAgdGVtcGxhdGUuaGFzUmVzb3VyY2VQcm9wZXJ0aWVzKCdBV1M6OlMzOjpCdWNrZXQnLCB7XG4gICAgQnVja2V0RW5jcnlwdGlvbjoge1xuICAgICAgU2VydmVyU2lkZUVuY3J5cHRpb25Db25maWd1cmF0aW9uOiBbe1xuICAgICAgICBTZXJ2ZXJTaWRlRW5jcnlwdGlvbkJ5RGVmYXVsdDoge1xuICAgICAgICAgIFNTRUFsZ29yaXRobTogXCJBRVMyNTZcIlxuICAgICAgICB9XG4gICAgICB9XVxuICAgIH1cbiAgfSk7XG59KTtcblxudGVzdCgnY2hlY2sgczNCdWNrZXQgcHVibGljIGFjY2VzcyBibG9jayBjb25maWd1cmF0aW9uJywgKCkgPT4ge1xuICBjb25zdCBzdGFjayA9IG5ldyBjZGsuU3RhY2soKTtcbiAgZGVwbG95KHN0YWNrKTtcbiAgY29uc3QgdGVtcGxhdGUgPSBUZW1wbGF0ZS5mcm9tU3RhY2soc3RhY2spO1xuICB0ZW1wbGF0ZS5oYXNSZXNvdXJjZVByb3BlcnRpZXMoJ0FXUzo6UzM6OkJ1Y2tldCcsIHtcbiAgICBQdWJsaWNBY2Nlc3NCbG9ja0NvbmZpZ3VyYXRpb246IHtcbiAgICAgIEJsb2NrUHVibGljQWNsczogdHJ1ZSxcbiAgICAgIEJsb2NrUHVibGljUG9saWN5OiB0cnVlLFxuICAgICAgSWdub3JlUHVibGljQWNsczogdHJ1ZSxcbiAgICAgIFJlc3RyaWN0UHVibGljQnVja2V0czogdHJ1ZVxuICAgIH1cbiAgfSk7XG59KTtcblxudGVzdCgndGVzdCBzM0J1Y2tldCBvdmVycmlkZSBwdWJsaWNBY2Nlc3NCbG9ja0NvbmZpZ3VyYXRpb24nLCAoKSA9PiB7XG4gIGNvbnN0IHN0YWNrID0gbmV3IGNkay5TdGFjaygpO1xuXG4gIGNvbnN0IHByb3BzOiBDbG91ZEZyb250VG9TM1Byb3BzID0ge1xuICAgIGJ1Y2tldFByb3BzOiB7XG4gICAgICBibG9ja1B1YmxpY0FjY2Vzczoge1xuICAgICAgICBibG9ja1B1YmxpY0FjbHM6IGZhbHNlLFxuICAgICAgICBibG9ja1B1YmxpY1BvbGljeTogdHJ1ZSxcbiAgICAgICAgaWdub3JlUHVibGljQWNsczogZmFsc2UsXG4gICAgICAgIHJlc3RyaWN0UHVibGljQnVja2V0czogdHJ1ZVxuICAgICAgfVxuICAgIH1cbiAgfTtcblxuICBuZXcgQ2xvdWRGcm9udFRvUzMoc3RhY2ssICd0ZXN0LWNsb3VkZnJvbnQtczMnLCBwcm9wcyk7XG5cbiAgY29uc3QgdGVtcGxhdGUgPSBUZW1wbGF0ZS5mcm9tU3RhY2soc3RhY2spO1xuICB0ZW1wbGF0ZS5oYXNSZXNvdXJjZVByb3BlcnRpZXMoXCJBV1M6OlMzOjpCdWNrZXRcIiwge1xuICAgIFB1YmxpY0FjY2Vzc0Jsb2NrQ29uZmlndXJhdGlvbjoge1xuICAgICAgQmxvY2tQdWJsaWNBY2xzOiBmYWxzZSxcbiAgICAgIEJsb2NrUHVibGljUG9saWN5OiB0cnVlLFxuICAgICAgSWdub3JlUHVibGljQWNsczogZmFsc2UsXG4gICAgICBSZXN0cmljdFB1YmxpY0J1Y2tldHM6IHRydWVcbiAgICB9LFxuICB9KTtcbn0pO1xuXG50ZXN0KCdjaGVjayBleGlzdGluZyBidWNrZXQnLCAoKSA9PiB7XG4gIGNvbnN0IHN0YWNrID0gbmV3IGNkay5TdGFjaygpO1xuXG4gIGNvbnN0IGV4aXN0aW5nQnVja2V0ID0gbmV3IHMzLkJ1Y2tldChzdGFjaywgJ215LWJ1Y2tldCcsIHtcbiAgICBidWNrZXROYW1lOiAnbXktYnVja2V0J1xuICB9KTtcblxuICBjb25zdCBwcm9wczogQ2xvdWRGcm9udFRvUzNQcm9wcyA9IHtcbiAgICBleGlzdGluZ0J1Y2tldE9iajogZXhpc3RpbmdCdWNrZXRcbiAgfTtcblxuICBuZXcgQ2xvdWRGcm9udFRvUzMoc3RhY2ssICd0ZXN0LWNsb3VkZnJvbnQtczMnLCBwcm9wcyk7XG5cbiAgY29uc3QgdGVtcGxhdGUgPSBUZW1wbGF0ZS5mcm9tU3RhY2soc3RhY2spO1xuICB0ZW1wbGF0ZS5oYXNSZXNvdXJjZVByb3BlcnRpZXMoXCJBV1M6OlMzOjpCdWNrZXRcIiwge1xuICAgIEJ1Y2tldE5hbWU6IFwibXktYnVja2V0XCJcbiAgfSk7XG59KTtcblxudGVzdCgnY2hlY2sgZXhjZXB0aW9uIGZvciBNaXNzaW5nIGV4aXN0aW5nT2JqIGZyb20gcHJvcHMgZm9yIGRlcGxveSA9IGZhbHNlJywgKCkgPT4ge1xuICBjb25zdCBzdGFjayA9IG5ldyBjZGsuU3RhY2soKTtcblxuICB0cnkge1xuICAgIG5ldyBDbG91ZEZyb250VG9TMyhzdGFjaywgJ3Rlc3QtY2xvdWRmcm9udC1zMycsIHt9KTtcbiAgfSBjYXRjaCAoZSkge1xuICAgIGV4cGVjdChlKS50b0JlSW5zdGFuY2VPZihFcnJvcik7XG4gIH1cbn0pO1xuXG50ZXN0KCdjaGVjayBwcm9wZXJ0aWVzJywgKCkgPT4ge1xuICBjb25zdCBzdGFjayA9IG5ldyBjZGsuU3RhY2soKTtcblxuICBjb25zdCBjb25zdHJ1Y3Q6IENsb3VkRnJvbnRUb1MzID0gZGVwbG95KHN0YWNrKTtcblxuICBleHBlY3QoY29uc3RydWN0LmNsb3VkRnJvbnRXZWJEaXN0cmlidXRpb24pLnRvQmVEZWZpbmVkKCk7XG4gIGV4cGVjdChjb25zdHJ1Y3QuczNCdWNrZXQpLnRvQmVEZWZpbmVkKCk7XG59KTtcblxudGVzdChcIkNvbmZpcm0gQ2hlY2tTM1Byb3BzIGlzIGNhbGxlZFwiLCAoKSA9PiB7XG4gIC8vIFN0YWNrXG4gIGNvbnN0IHN0YWNrID0gbmV3IGNkay5TdGFjaygpO1xuXG4gIGNvbnN0IHRlc3RCdWNrZXQgPSBuZXcgczMuQnVja2V0KHN0YWNrLCAndGVzdC1idWNrZXQnLCB7fSk7XG5cbiAgY29uc3QgYXBwID0gKCkgPT4ge1xuICAgIC8vIEhlbHBlciBkZWNsYXJhdGlvblxuICAgIG5ldyBDbG91ZEZyb250VG9TMyhzdGFjaywgXCJiYWQtczMtYXJnc1wiLCB7XG4gICAgICBleGlzdGluZ0J1Y2tldE9iajogdGVzdEJ1Y2tldCxcbiAgICAgIGJ1Y2tldFByb3BzOiB7XG4gICAgICAgIHJlbW92YWxQb2xpY3k6IFJlbW92YWxQb2xpY3kuREVTVFJPWVxuICAgICAgfSxcbiAgICB9KTtcbiAgfTtcbiAgLy8gQXNzZXJ0aW9uXG4gIGV4cGVjdChhcHApLnRvVGhyb3coJ0Vycm9yIC0gRWl0aGVyIHByb3ZpZGUgYnVja2V0UHJvcHMgb3IgZXhpc3RpbmdCdWNrZXRPYmosIGJ1dCBub3QgYm90aC5cXG4nKTtcbn0pO1xuXG50ZXN0KFwiVGVzdCBleGlzdGluZ0J1Y2tldE9ialwiLCAoKSA9PiB7XG4gIC8vIFN0YWNrXG4gIGNvbnN0IHN0YWNrID0gbmV3IGNkay5TdGFjaygpO1xuICBjb25zdCBjb25zdHJ1Y3Q6IENsb3VkRnJvbnRUb1MzID0gbmV3IENsb3VkRnJvbnRUb1MzKHN0YWNrLCBcImV4aXN0aW5nSUJ1Y2tldFwiLCB7XG4gICAgZXhpc3RpbmdCdWNrZXRPYmo6IHMzLkJ1Y2tldC5mcm9tQnVja2V0TmFtZShzdGFjaywgJ215YnVja2V0JywgJ215YnVja2V0JylcbiAgfSk7XG4gIC8vIEFzc2VydGlvblxuICBleHBlY3QoY29uc3RydWN0LmNsb3VkRnJvbnRXZWJEaXN0cmlidXRpb24pLnRvQmVEZWZpbmVkKCk7XG4gIGNvbnN0IHRlbXBsYXRlID0gVGVtcGxhdGUuZnJvbVN0YWNrKHN0YWNrKTtcbiAgdGVtcGxhdGUuaGFzUmVzb3VyY2VQcm9wZXJ0aWVzKFwiQVdTOjpDbG91ZEZyb250OjpEaXN0cmlidXRpb25cIiwge1xuICAgIERpc3RyaWJ1dGlvbkNvbmZpZzoge1xuICAgICAgT3JpZ2luczogW1xuICAgICAgICB7XG4gICAgICAgICAgRG9tYWluTmFtZToge1xuICAgICAgICAgICAgXCJGbjo6Sm9pblwiOiBbXG4gICAgICAgICAgICAgIFwiXCIsXG4gICAgICAgICAgICAgIFtcbiAgICAgICAgICAgICAgICBcIm15YnVja2V0LnMzLlwiLFxuICAgICAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgICAgIFJlZjogXCJBV1M6OlJlZ2lvblwiXG4gICAgICAgICAgICAgICAgfSxcbiAgICAgICAgICAgICAgICBcIi5cIixcbiAgICAgICAgICAgICAgICB7XG4gICAgICAgICAgICAgICAgICBSZWY6IFwiQVdTOjpVUkxTdWZmaXhcIlxuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgXVxuICAgICAgICAgICAgXVxuICAgICAgICAgIH0sXG4gICAgICAgICAgSWQ6IFwiZXhpc3RpbmdJQnVja2V0Q2xvdWRGcm9udERpc3RyaWJ1dGlvbk9yaWdpbjFENTg0OTEyNVwiLFxuICAgICAgICAgIE9yaWdpbkFjY2Vzc0NvbnRyb2xJZDogeyBcIkZuOjpHZXRBdHRcIjogW1wiZXhpc3RpbmdJQnVja2V0Q2xvdWRGcm9udE9hY0VCNDJFOThGXCIsIFwiSWRcIl0gfSxcbiAgICAgICAgICBTM09yaWdpbkNvbmZpZzoge31cbiAgICAgICAgfVxuICAgICAgXVxuICAgIH1cbiAgfSk7XG59KTtcblxudGVzdCgndGVzdCBjbG91ZGZyb250IHdpdGggY3VzdG9tIGRvbWFpbiBuYW1lcycsICgpID0+IHtcbiAgY29uc3Qgc3RhY2sgPSBuZXcgY2RrLlN0YWNrKCk7XG5cbiAgY29uc3QgY2VydGlmaWNhdGUgPSBhY20uQ2VydGlmaWNhdGUuZnJvbUNlcnRpZmljYXRlQXJuKHN0YWNrLCAnQ2VydCcsICdhcm46JHtBd3MuUEFSVElUSU9OfTphY206dXMtZWFzdC0xOjEyMzQ1Njc4OTAxMjpjZXJ0aWZpY2F0ZS8xMTExMjIyMi0zMzMzLTEyMzQtMTIzNC0xMjM0NTY3ODkwMTInKTtcblxuICBjb25zdCBwcm9wczogQ2xvdWRGcm9udFRvUzNQcm9wcyA9IHtcbiAgICBjbG91ZEZyb250RGlzdHJpYnV0aW9uUHJvcHM6IHtcbiAgICAgIGRvbWFpbk5hbWVzOiBbJ215ZG9tYWlucyddLFxuICAgICAgY2VydGlmaWNhdGVcbiAgICB9XG4gIH07XG5cbiAgbmV3IENsb3VkRnJvbnRUb1MzKHN0YWNrLCAndGVzdC1jbG91ZGZyb250LXMzJywgcHJvcHMpO1xuXG4gIGNvbnN0IHRlbXBsYXRlID0gVGVtcGxhdGUuZnJvbVN0YWNrKHN0YWNrKTtcbiAgdGVtcGxhdGUuaGFzUmVzb3VyY2VQcm9wZXJ0aWVzKFwiQVdTOjpDbG91ZEZyb250OjpEaXN0cmlidXRpb25cIiwge1xuICAgIERpc3RyaWJ1dGlvbkNvbmZpZzoge1xuICAgICAgQWxpYXNlczogW1xuICAgICAgICBcIm15ZG9tYWluc1wiXG4gICAgICBdXG4gICAgfVxuICB9KTtcbn0pO1xuXG50ZXN0KCdDbG91ZGZyb250IGxvZ2dpbmcgYnVja2V0IHdpdGggZGVzdHJveSByZW1vdmFsIHBvbGljeSBhbmQgYXV0byBkZWxldGUgb2JqZWN0cycsICgpID0+IHtcbiAgY29uc3Qgc3RhY2sgPSBuZXcgY2RrLlN0YWNrKCk7XG5cbiAgY29uc3QgY2xvdWRmcm9udExvZ0J1Y2tldE5hbWUgPSAnY2YtbG9nLWJ1Y2tldCc7XG4gIG5ldyBDbG91ZEZyb250VG9TMyhzdGFjaywgJ2Nsb3VkZnJvbnQtczMnLCB7XG4gICAgY2xvdWRGcm9udExvZ2dpbmdCdWNrZXRQcm9wczoge1xuICAgICAgcmVtb3ZhbFBvbGljeTogY2RrLlJlbW92YWxQb2xpY3kuREVTVFJPWSxcbiAgICAgIGF1dG9EZWxldGVPYmplY3RzOiB0cnVlLFxuICAgICAgYnVja2V0TmFtZTogY2xvdWRmcm9udExvZ0J1Y2tldE5hbWVcbiAgICB9XG4gIH0pO1xuXG4gIGNvbnN0IHRlbXBsYXRlID0gVGVtcGxhdGUuZnJvbVN0YWNrKHN0YWNrKTtcbiAgdGVtcGxhdGUuaGFzUmVzb3VyY2VQcm9wZXJ0aWVzKFwiQVdTOjpTMzo6QnVja2V0XCIsIHtcbiAgICBPd25lcnNoaXBDb250cm9sczogeyBSdWxlczogW3sgT2JqZWN0T3duZXJzaGlwOiBcIk9iamVjdFdyaXRlclwiIH1dIH0sXG4gICAgQnVja2V0TmFtZTogY2xvdWRmcm9udExvZ0J1Y2tldE5hbWUsXG4gIH0pO1xuXG4gIHRlbXBsYXRlLmhhc1Jlc291cmNlUHJvcGVydGllcyhcIkN1c3RvbTo6UzNBdXRvRGVsZXRlT2JqZWN0c1wiLCB7XG4gICAgU2VydmljZVRva2VuOiB7XG4gICAgICBcIkZuOjpHZXRBdHRcIjogW1xuICAgICAgICBcIkN1c3RvbVMzQXV0b0RlbGV0ZU9iamVjdHNDdXN0b21SZXNvdXJjZVByb3ZpZGVySGFuZGxlcjlEOTAxODRGXCIsXG4gICAgICAgIFwiQXJuXCJcbiAgICAgIF1cbiAgICB9LFxuICAgIEJ1Y2tldE5hbWU6IHtcbiAgICAgIFJlZjogXCJjbG91ZGZyb250czNDbG91ZGZyb250TG9nZ2luZ0J1Y2tldDVCODQ1MTQzXCJcbiAgICB9XG4gIH0pO1xufSk7XG5cbnRlc3QoJ3MzIGJ1Y2tldCB3aXRoIG9uZSBjb250ZW50IGJ1Y2tldCBhbmQgbm8gYWNjZXNzIGxvZ2dpbmcgb2YgQ09OVEVOVCBidWNrZXQnLCAoKSA9PiB7XG4gIGNvbnN0IHN0YWNrID0gbmV3IGNkay5TdGFjaygpO1xuXG4gIGNvbnN0IGNvbnN0cnVjdCA9IG5ldyBDbG91ZEZyb250VG9TMyhzdGFjaywgJ2Nsb3VkZnJvbnQtczMnLCB7XG4gICAgYnVja2V0UHJvcHM6IHtcbiAgICAgIHJlbW92YWxQb2xpY3k6IGNkay5SZW1vdmFsUG9saWN5LkRFU1RST1ksXG4gICAgfSxcbiAgICBsb2dTM0FjY2Vzc0xvZ3M6IGZhbHNlXG4gIH0pO1xuXG4gIGNvbnN0IHRlbXBsYXRlID0gVGVtcGxhdGUuZnJvbVN0YWNrKHN0YWNrKTtcbiAgLy8gQ29udGVudCBidWNrZXQrQ2xvdWRmcm9udCBMb2dzIGJ1Y2tldCtcbiAgLy8gQWNjZXNzIExvZyBidWNrZXQgZm9yIENsb3VkZnJvbnQgTG9ncyBidWNrZXQgPSAzIGJ1Y2tldHNcbiAgdGVtcGxhdGUucmVzb3VyY2VDb3VudElzKFwiQVdTOjpTMzo6QnVja2V0XCIsIDMpO1xuICBleHBlY3QoY29uc3RydWN0LnMzTG9nZ2luZ0J1Y2tldCkudG9FcXVhbCh1bmRlZmluZWQpO1xufSk7XG5cbnRlc3QoJ0Nsb3VkRnJvbnQgb3JpZ2luIHBhdGggcHJlc2VudCB3aGVuIHByb3ZpZGVkJywgKCkgPT4ge1xuICBjb25zdCBzdGFjayA9IG5ldyBjZGsuU3RhY2soKTtcblxuICBuZXcgQ2xvdWRGcm9udFRvUzMoc3RhY2ssICdjbG91ZGZyb250LXMzJywge1xuICAgIG9yaWdpblBhdGg6ICcvdGVzdFBhdGgnXG4gIH0pO1xuXG4gIGNvbnN0IHRlbXBsYXRlID0gVGVtcGxhdGUuZnJvbVN0YWNrKHN0YWNrKTtcbiAgdGVtcGxhdGUuaGFzUmVzb3VyY2VQcm9wZXJ0aWVzKFwiQVdTOjpDbG91ZEZyb250OjpEaXN0cmlidXRpb25cIiwge1xuICAgIERpc3RyaWJ1dGlvbkNvbmZpZzpcbiAgICB7XG4gICAgICBPcmlnaW5zOiBbXG4gICAgICAgIHtcbiAgICAgICAgICBPcmlnaW5QYXRoOiBcIi90ZXN0UGF0aFwiLFxuICAgICAgICB9XG4gICAgICBdXG4gICAgfVxuICB9KTtcbn0pO1xuXG50ZXN0KCdDbG91ZEZyb250IG9yaWdpbiBwYXRoIHNob3VsZCBub3QgYmUgcHJlc2VudCBpZiBub3QgcHJvdmlkZWQnLCAoKSA9PiB7XG4gIGNvbnN0IHN0YWNrID0gbmV3IGNkay5TdGFjaygpO1xuXG4gIG5ldyBDbG91ZEZyb250VG9TMyhzdGFjaywgJ2Nsb3VkZnJvbnQtczMnLCB7fSk7XG5cbiAgZGVmYXVsdHMuZXhwZWN0Tm9uZXhpc3RlbmNlKHN0YWNrLCBcIkFXUzo6Q2xvdWRGcm9udDo6RGlzdHJpYnV0aW9uXCIsIHtcbiAgICBEaXN0cmlidXRpb25Db25maWc6XG4gICAge1xuICAgICAgT3JpZ2luczogW1xuICAgICAgICB7XG4gICAgICAgICAgT3JpZ2luUGF0aDogXCIvdGVzdFBhdGhcIixcbiAgICAgICAgfVxuICAgICAgXVxuICAgIH1cbiAgfSk7XG59KTtcblxudGVzdCgnVGVzdCB0aGUgZGVwbG95bWVudCB3aXRoIHNlY3VyaXR5SGVhZGVyc0JlaGF2aW9yIGluc3RlYWQgb2YgSFRUUCBzZWN1cml0eSBoZWFkZXJzJywgKCkgPT4ge1xuICAvLyBJbml0aWFsIHNldHVwXG4gIGNvbnN0IHN0YWNrID0gbmV3IFN0YWNrKCk7XG4gIGNvbnN0IGNsb3VkRnJvbnRUb1MzID0gbmV3IENsb3VkRnJvbnRUb1MzKHN0YWNrLCAndGVzdC1jbG91ZGZyb250LXMzJywge1xuICAgIGluc2VydEh0dHBTZWN1cml0eUhlYWRlcnM6IGZhbHNlLFxuICAgIHJlc3BvbnNlSGVhZGVyc1BvbGljeVByb3BzOiB7XG4gICAgICBzZWN1cml0eUhlYWRlcnNCZWhhdmlvcjoge1xuICAgICAgICBzdHJpY3RUcmFuc3BvcnRTZWN1cml0eToge1xuICAgICAgICAgIGFjY2Vzc0NvbnRyb2xNYXhBZ2U6IER1cmF0aW9uLnNlY29uZHMoNjMwNzIpLFxuICAgICAgICAgIGluY2x1ZGVTdWJkb21haW5zOiB0cnVlLFxuICAgICAgICAgIG92ZXJyaWRlOiB0cnVlLFxuICAgICAgICAgIHByZWxvYWQ6IHRydWVcbiAgICAgICAgfSxcbiAgICAgICAgY29udGVudFNlY3VyaXR5UG9saWN5OiB7XG4gICAgICAgICAgY29udGVudFNlY3VyaXR5UG9saWN5OiBcInVwZ3JhZGUtaW5zZWN1cmUtcmVxdWVzdHM7IGRlZmF1bHQtc3JjICdub25lJztcIixcbiAgICAgICAgICBvdmVycmlkZTogdHJ1ZVxuICAgICAgICB9LFxuICAgICAgfVxuICAgIH1cbiAgfSk7XG5cbiAgLy8gQXNzZXJ0aW9uXG4gIGNvbnN0IHRlbXBsYXRlID0gVGVtcGxhdGUuZnJvbVN0YWNrKHN0YWNrKTtcbiAgdGVtcGxhdGUuaGFzUmVzb3VyY2VQcm9wZXJ0aWVzKFwiQVdTOjpDbG91ZEZyb250OjpSZXNwb25zZUhlYWRlcnNQb2xpY3lcIiwge1xuICAgIFJlc3BvbnNlSGVhZGVyc1BvbGljeUNvbmZpZzoge1xuICAgICAgU2VjdXJpdHlIZWFkZXJzQ29uZmlnOiB7XG4gICAgICAgIENvbnRlbnRTZWN1cml0eVBvbGljeToge1xuICAgICAgICAgIENvbnRlbnRTZWN1cml0eVBvbGljeTogXCJ1cGdyYWRlLWluc2VjdXJlLXJlcXVlc3RzOyBkZWZhdWx0LXNyYyAnbm9uZSc7XCIsXG4gICAgICAgICAgT3ZlcnJpZGU6IHRydWVcbiAgICAgICAgfSxcbiAgICAgICAgU3RyaWN0VHJhbnNwb3J0U2VjdXJpdHk6IHtcbiAgICAgICAgICBBY2Nlc3NDb250cm9sTWF4QWdlU2VjOiA2MzA3MixcbiAgICAgICAgICBJbmNsdWRlU3ViZG9tYWluczogdHJ1ZSxcbiAgICAgICAgICBPdmVycmlkZTogdHJ1ZSxcbiAgICAgICAgICBQcmVsb2FkOiB0cnVlXG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9XG4gIH0pO1xuICBleHBlY3QoY2xvdWRGcm9udFRvUzMuY2xvdWRGcm9udEZ1bmN0aW9uKS50b0VxdWFsKHVuZGVmaW5lZCk7XG59KTtcblxudGVzdChcInRocm93IGV4Y2VwdGlvbiBpZiBpbnNlcnRIdHRwU2VjdXJpdHlIZWFkZXJzIGFuZCByZXNwb25zZUhlYWRlcnNQb2xpY3lQcm9wcyBhcmUgcHJvdmlkZWRcIiwgKCkgPT4ge1xuICBjb25zdCBzdGFjayA9IG5ldyBjZGsuU3RhY2soKTtcblxuICBleHBlY3QoKCkgPT4ge1xuICAgIG5ldyBDbG91ZEZyb250VG9TMyhzdGFjaywgXCJ0ZXN0LWNsb3VkZnJvbnQtczNcIiwge1xuICAgICAgaW5zZXJ0SHR0cFNlY3VyaXR5SGVhZGVyczogdHJ1ZSxcbiAgICAgIHJlc3BvbnNlSGVhZGVyc1BvbGljeVByb3BzOiB7XG4gICAgICAgIHNlY3VyaXR5SGVhZGVyc0JlaGF2aW9yOiB7XG4gICAgICAgICAgc3RyaWN0VHJhbnNwb3J0U2VjdXJpdHk6IHtcbiAgICAgICAgICAgIGFjY2Vzc0NvbnRyb2xNYXhBZ2U6IER1cmF0aW9uLnNlY29uZHMoNjMwNzIpLFxuICAgICAgICAgICAgaW5jbHVkZVN1YmRvbWFpbnM6IHRydWUsXG4gICAgICAgICAgICBvdmVycmlkZTogZmFsc2UsXG4gICAgICAgICAgICBwcmVsb2FkOiB0cnVlXG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICB9XG4gICAgfSk7XG4gIH0pLnRvVGhyb3coKTtcbn0pO1xuXG50ZXN0KFwiQ29uZmlybSBDaGVja0Nsb3VkRnJvbnRQcm9wcyBpcyBiZWluZyBjYWxsZWRcIiwgKCkgPT4ge1xuICBjb25zdCBzdGFjayA9IG5ldyBjZGsuU3RhY2soKTtcblxuICBleHBlY3QoKCkgPT4ge1xuICAgIG5ldyBDbG91ZEZyb250VG9TMyhzdGFjaywgXCJ0ZXN0LWNsb3VkZnJvbnQtYXBpZ2F0ZXdheVwiLCB7XG4gICAgICBpbnNlcnRIdHRwU2VjdXJpdHlIZWFkZXJzOiB0cnVlLFxuICAgICAgcmVzcG9uc2VIZWFkZXJzUG9saWN5UHJvcHM6IHtcbiAgICAgICAgc2VjdXJpdHlIZWFkZXJzQmVoYXZpb3I6IHtcbiAgICAgICAgICBzdHJpY3RUcmFuc3BvcnRTZWN1cml0eToge1xuICAgICAgICAgICAgYWNjZXNzQ29udHJvbE1heEFnZTogRHVyYXRpb24uc2Vjb25kcyg2MzA3MiksXG4gICAgICAgICAgICBpbmNsdWRlU3ViZG9tYWluczogdHJ1ZSxcbiAgICAgICAgICAgIG92ZXJyaWRlOiBmYWxzZSxcbiAgICAgICAgICAgIHByZWxvYWQ6IHRydWVcbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9KTtcbiAgfSkudG9UaHJvdygncmVzcG9uc2VIZWFkZXJzUG9saWN5UHJvcHMuc2VjdXJpdHlIZWFkZXJzQmVoYXZpb3IgY2FuIG9ubHkgYmUgcGFzc2VkIGlmIGh0dHBTZWN1cml0eUhlYWRlcnMgaXMgc2V0IHRvIGBmYWxzZWAuJyk7XG59KTtcblxudGVzdChcIkN1c3RvbSByZXNvdXJjZSBpcyBwcm92aXNpb25lZCBpZiBlbmNyeXB0aW9uIGtleSBpcyBwcm92aWRlZCBhcyBidWNrZXRQcm9wXCIsICgpID0+IHtcbiAgY29uc3Qgc3RhY2sgPSBuZXcgY2RrLlN0YWNrKCk7XG4gIGNvbnN0IGVuY3J5cHRpb25LZXkgPSBuZXcgS2V5KHN0YWNrLCAnY21rS2V5Jywge1xuICAgIGVuYWJsZUtleVJvdGF0aW9uOiB0cnVlLFxuICAgIHJlbW92YWxQb2xpY3k6IFJlbW92YWxQb2xpY3kuREVTVFJPWVxuICB9KTtcbiAgZGVwbG95KHN0YWNrLCB7XG4gICAgYnVja2V0UHJvcHM6IHtcbiAgICAgIGVuY3J5cHRpb25LZXksXG4gICAgICBlbmNyeXB0aW9uOiBzMy5CdWNrZXRFbmNyeXB0aW9uLktNU1xuICAgIH1cbiAgfSk7XG4gIGNvbnN0IHRlbXBsYXRlID0gVGVtcGxhdGUuZnJvbVN0YWNrKHN0YWNrKTtcbiAgLy8gMiBGdW5jdGlvbnMgLSBvdXIgY3VzdG9tIHJlc291cmNlIGFuZCBhIGZ1bmN0aW9uIGNyZWF0ZWQgYnkgdGhlIENES1xuICB0ZW1wbGF0ZS5yZXNvdXJjZUNvdW50SXMoJ0FXUzo6TGFtYmRhOjpGdW5jdGlvbicsIDIpO1xuXG4gIHRlbXBsYXRlLmhhc1Jlc291cmNlUHJvcGVydGllcygnQVdTOjpMYW1iZGE6OkZ1bmN0aW9uJywge1xuICAgIERlc2NyaXB0aW9uOiBcIkN1c3RvbSByZXNvdXJjZSBmdW5jdGlvbiB0aGF0IHVwZGF0ZXMgYSBwcm92aWRlZCBrZXkgcG9saWN5IHRvIGFsbG93IENsb3VkRnJvbnQgYWNjZXNzLlwiLFxuICAgIFJvbGU6IHtcbiAgICAgIFwiRm46OkdldEF0dFwiOiBbXCJ0ZXN0Y2xvdWRmcm9udHMzTGFtYmRhRnVuY3Rpb25TZXJ2aWNlUm9sZTJBNDNFQTkyXCIsIFwiQXJuXCJdXG4gICAgfVxuICB9KTtcbn0pO1xuXG50ZXN0KFwiQ3VzdG9tIHJlc291cmNlIGlzIHByb3Zpc2lvbmVkIGlmIENNSyB3YXMgdXNlZCB0byBlbmNyeXB0IGFuIGV4aXN0aW5nIGJ1Y2tldFwiLCAoKSA9PiB7XG4gIGNvbnN0IHN0YWNrID0gbmV3IGNkay5TdGFjaygpO1xuICBjb25zdCBlbmNyeXB0aW9uS2V5ID0gbmV3IEtleShzdGFjaywgJ2Nta0tleScsIHtcbiAgICBlbmFibGVLZXlSb3RhdGlvbjogdHJ1ZSxcbiAgICByZW1vdmFsUG9saWN5OiBSZW1vdmFsUG9saWN5LkRFU1RST1lcbiAgfSk7XG4gIGNvbnN0IGV4aXN0aW5nQnVja2V0T2JqID0gZGVmYXVsdHMuYnVpbGRTM0J1Y2tldChzdGFjaywge1xuICAgIGJ1Y2tldFByb3BzOiB7XG4gICAgICBlbmNyeXB0aW9uOiBzMy5CdWNrZXRFbmNyeXB0aW9uLktNUyxcbiAgICAgIGVuY3J5cHRpb25LZXlcbiAgICB9XG4gIH0sICdleGlzdGluZy1zMy1idWNrZXQtZW5jcnlwdGVkLXdpdGgtY21rJykuYnVja2V0O1xuICBuZXcgQ2xvdWRGcm9udFRvUzMoc3RhY2ssICd0ZXN0LWNsb3VkZnJvbnQtczMnLCB7XG4gICAgZXhpc3RpbmdCdWNrZXRPYmpcbiAgfSk7XG4gIGNvbnN0IHRlbXBsYXRlID0gVGVtcGxhdGUuZnJvbVN0YWNrKHN0YWNrKTtcbiAgLy8gMiBGdW5jdGlvbnMgLSBvdXIgY3VzdG9tIHJlc291cmNlIGFuZCBhIGZ1bmN0aW9uIGNyZWF0ZWQgYnkgdGhlIENES1xuICB0ZW1wbGF0ZS5yZXNvdXJjZUNvdW50SXMoJ0FXUzo6TGFtYmRhOjpGdW5jdGlvbicsIDIpO1xuXG4gIC8vIGVuc3VyZSB0aGF0IG91ciBGdW5jdGlvbiBoYXMgdGhlIGNvcnJlY3Qgcm9sZSBhdHRhY2hlZFxuICB0ZW1wbGF0ZS5oYXNSZXNvdXJjZVByb3BlcnRpZXMoJ0FXUzo6TGFtYmRhOjpGdW5jdGlvbicsIHtcbiAgICBEZXNjcmlwdGlvbjogXCJDdXN0b20gcmVzb3VyY2UgZnVuY3Rpb24gdGhhdCB1cGRhdGVzIGEgcHJvdmlkZWQga2V5IHBvbGljeSB0byBhbGxvdyBDbG91ZEZyb250IGFjY2Vzcy5cIixcbiAgICBSb2xlOiB7XG4gICAgICBcIkZuOjpHZXRBdHRcIjogW1widGVzdGNsb3VkZnJvbnRzM0xhbWJkYUZ1bmN0aW9uU2VydmljZVJvbGUyQTQzRUE5MlwiLCBcIkFyblwiXVxuICAgIH1cbiAgfSk7XG59KTtcblxudGVzdChcIkN1c3RvbSByZXNvdXJjZSBpcyBub3QgcHJvdmlzaW9uZWQgaWYgZW5jcnlwdGlvbiBrZXkgaXMgbm90IHByb3ZpZGVkIGFzIGJ1Y2tldFByb3BcIiwgKCkgPT4ge1xuICBjb25zdCBzdGFjayA9IG5ldyBjZGsuU3RhY2soKTtcbiAgZGVwbG95KHN0YWNrKTtcbiAgY29uc3QgdGVtcGxhdGUgPSBUZW1wbGF0ZS5mcm9tU3RhY2soc3RhY2spO1xuICB0ZW1wbGF0ZS5yZXNvdXJjZUNvdW50SXMoJ0FXUzo6TGFtYmRhOjpGdW5jdGlvbicsIDApO1xufSk7XG5cbnRlc3QoXCJDdXN0b20gcmVzb3VyY2UgaXMgbm90IHByb3Zpc2lvbmVkIGlmIENNSyB3YXMgbm90IHVzZWQgdG8gZW5jcnlwdCBhbiBleGlzdGluZyBidWNrZXRcIiwgKCkgPT4ge1xuICBjb25zdCBzdGFjayA9IG5ldyBjZGsuU3RhY2soKTtcbiAgY29uc3QgZXhpc3RpbmdCdWNrZXRPYmogPSBkZWZhdWx0cy5idWlsZFMzQnVja2V0KHN0YWNrLCB7fSwgJ2V4aXN0aW5nLXMzLWJ1Y2tldC1lbmNyeXB0ZWQtd2l0aC1jbWsnKS5idWNrZXQ7XG4gIG5ldyBDbG91ZEZyb250VG9TMyhzdGFjaywgJ3Rlc3QtY2xvdWRmcm9udC1zMycsIHtcbiAgICBleGlzdGluZ0J1Y2tldE9ialxuICB9KTtcbiAgY29uc3QgdGVtcGxhdGUgPSBUZW1wbGF0ZS5mcm9tU3RhY2soc3RhY2spO1xuICB0ZW1wbGF0ZS5yZXNvdXJjZUNvdW50SXMoJ0FXUzo6TGFtYmRhOjpGdW5jdGlvbicsIDApO1xufSk7XG5cbnRlc3QoXCJIdHRwT3JpZ2luIGlzIHByb3Zpc2lvbmVkIGlmIGEgc3RhdGljIHdlYnNpdGUgYnVja2V0IGlzIHVzZWRcIiwgKCkgPT4ge1xuICBjb25zdCBzdGFjayA9IG5ldyBjZGsuU3RhY2soKTtcbiAgY29uc3QgYmxvY2tQdWJsaWNBY2Nlc3MgPSBmYWxzZTtcbiAgY29uc3QgcHJvcHM6IENsb3VkRnJvbnRUb1MzUHJvcHMgPSB7XG4gICAgYnVja2V0UHJvcHM6IHtcbiAgICAgIGVuZm9yY2VTU0w6IGZhbHNlLFxuICAgICAgcHVibGljUmVhZEFjY2VzczogdHJ1ZSwgLy8gPC0tIHJlcXVpcmVkIGZvciBpc1dlYnNpdGVcbiAgICAgIGJsb2NrUHVibGljQWNjZXNzOiB7XG4gICAgICAgIGJsb2NrUHVibGljQWNsczogYmxvY2tQdWJsaWNBY2Nlc3MsXG4gICAgICAgIHJlc3RyaWN0UHVibGljQnVja2V0czogYmxvY2tQdWJsaWNBY2Nlc3MsXG4gICAgICAgIGJsb2NrUHVibGljUG9saWN5OiBibG9ja1B1YmxpY0FjY2VzcyxcbiAgICAgICAgaWdub3JlUHVibGljQWNsczogYmxvY2tQdWJsaWNBY2Nlc3NcbiAgICAgIH0sXG4gICAgICB3ZWJzaXRlSW5kZXhEb2N1bWVudDogXCJpbmRleC5odG1sXCIgLy8gPC0tIHJlcXVpcmVkIGZvciBpc1dlYnNpdGVcbiAgICB9LFxuICAgIGluc2VydEh0dHBTZWN1cml0eUhlYWRlcnM6IGZhbHNlXG4gIH07XG4gIGNvbnN0IGNvbnN0cnVjdCA9IG5ldyBDbG91ZEZyb250VG9TMyhzdGFjaywgJ3Rlc3QtY2xvdWRmcm9udC1zMycsIHByb3BzKTtcbiAgY29uc3QgdGVtcGxhdGUgPSBUZW1wbGF0ZS5mcm9tU3RhY2soc3RhY2spO1xuICAvLyBBc3NlcnQgcmVzb3VyY2VzXG4gIHRlbXBsYXRlLnJlc291cmNlQ291bnRJcygnQVdTOjpDbG91ZEZyb250OjpPcmlnaW5BY2Nlc3NDb250cm9sJywgMCk7XG4gIHRlbXBsYXRlLmhhc1Jlc291cmNlUHJvcGVydGllcygnQVdTOjpDbG91ZEZyb250OjpEaXN0cmlidXRpb24nLCB7XG4gICAgRGlzdHJpYnV0aW9uQ29uZmlnOiB7XG4gICAgICBPcmlnaW5zOiBbXG4gICAgICAgIHtcbiAgICAgICAgICBDdXN0b21PcmlnaW5Db25maWc6IHtcbiAgICAgICAgICAgIE9yaWdpblByb3RvY29sUG9saWN5OiBcImh0dHAtb25seVwiXG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICBdXG4gICAgfVxuICB9KTtcbiAgdGVtcGxhdGUucmVzb3VyY2VDb3VudElzKCdBV1M6OkNsb3VkRnJvbnQ6Ok9yaWdpbkFjY2Vzc0lkZW50aXR5JywgMCk7XG4gIC8vIEFzc2VydCBwYXR0ZXJuIHByb3BlcnRpZXMgKG91dHB1dCBwcm9wcylcbiAgZXhwZWN0KGNvbnN0cnVjdC5vcmlnaW5BY2Nlc3NDb250cm9sKS50b0JlKHVuZGVmaW5lZCk7XG59KTtcblxudGVzdChcIk9BQyBpcyBwcm92aXNpb25lZCBpbiBhbGwgb3RoZXIgY2FzZXNcIiwgKCkgPT4ge1xuICBjb25zdCBzdGFjayA9IG5ldyBjZGsuU3RhY2soKTtcbiAgY29uc3QgY29uc3RydWN0ID0gbmV3IENsb3VkRnJvbnRUb1MzKHN0YWNrLCAndGVzdC1jbG91ZGZyb250LXMzJywge30pO1xuICBjb25zdCB0ZW1wbGF0ZSA9IFRlbXBsYXRlLmZyb21TdGFjayhzdGFjayk7XG4gIC8vIEFzc2VydCByZXNvdXJjZXNcbiAgdGVtcGxhdGUucmVzb3VyY2VDb3VudElzKCdBV1M6OkNsb3VkRnJvbnQ6Ok9yaWdpbkFjY2Vzc0NvbnRyb2wnLCAxKTtcbiAgdGVtcGxhdGUucmVzb3VyY2VDb3VudElzKCdBV1M6OkNsb3VkRnJvbnQ6Ok9yaWdpbkFjY2Vzc0lkZW50aXR5JywgMCk7XG4gIC8vIEFzc2VydCBwYXR0ZXJuIHByb3BlcnRpZXMgKG91dHB1dCBwcm9wcylcbiAgZXhwZWN0KGNvbnN0cnVjdC5vcmlnaW5BY2Nlc3NDb250cm9sKS5ub3QudG9CZSh1bmRlZmluZWQpO1xufSk7XG5cbnRlc3QoXCJJZiBhIGN1c3RvbWVyIHByb3ZpZGVzIHRoZWlyIG93biBodHRwT3JpZ2luLCBvciBvdGhlciBvcmlnaW4gdHlwZSwgdXNlIHRoYXQgb25lXCIsICgpID0+IHtcbiAgY29uc3Qgc3RhY2sgPSBuZXcgY2RrLlN0YWNrKCk7XG4gIGNvbnN0IGJsb2NrUHVibGljQWNjZXNzID0gZmFsc2U7XG4gIGNvbnN0IHByb3BzOiBDbG91ZEZyb250VG9TM1Byb3BzID0ge1xuICAgIGJ1Y2tldFByb3BzOiB7XG4gICAgICBlbmZvcmNlU1NMOiBmYWxzZSxcbiAgICAgIHB1YmxpY1JlYWRBY2Nlc3M6IHRydWUsIC8vIDwtLSByZXF1aXJlZCBmb3IgaXNXZWJzaXRlXG4gICAgICBibG9ja1B1YmxpY0FjY2Vzczoge1xuICAgICAgICBibG9ja1B1YmxpY0FjbHM6IGJsb2NrUHVibGljQWNjZXNzLFxuICAgICAgICByZXN0cmljdFB1YmxpY0J1Y2tldHM6IGJsb2NrUHVibGljQWNjZXNzLFxuICAgICAgICBibG9ja1B1YmxpY1BvbGljeTogYmxvY2tQdWJsaWNBY2Nlc3MsXG4gICAgICAgIGlnbm9yZVB1YmxpY0FjbHM6IGJsb2NrUHVibGljQWNjZXNzXG4gICAgICB9LFxuICAgICAgd2Vic2l0ZUluZGV4RG9jdW1lbnQ6IFwiaW5kZXguaHRtbFwiIC8vIDwtLSByZXF1aXJlZCBmb3IgaXNXZWJzaXRlXG4gICAgfSxcbiAgICBpbnNlcnRIdHRwU2VjdXJpdHlIZWFkZXJzOiBmYWxzZSxcbiAgICBjbG91ZEZyb250RGlzdHJpYnV0aW9uUHJvcHM6IHtcbiAgICAgIGRlZmF1bHRCZWhhdmlvcjoge1xuICAgICAgICBvcmlnaW46IG5ldyBvcmlnaW5zLkh0dHBPcmlnaW4oJ2V4YW1wbGUuY29tJywge1xuICAgICAgICAgIG9yaWdpbklkOiAnY3VzdG9tLWh0dHAtb3JpZ2luLWZvci10ZXN0aW5nJ1xuICAgICAgICB9KVxuICAgICAgfVxuICAgIH1cbiAgfTtcbiAgbmV3IENsb3VkRnJvbnRUb1MzKHN0YWNrLCAndGVzdC1jbG91ZGZyb250LXMzJywgcHJvcHMpO1xuICBjb25zdCB0ZW1wbGF0ZSA9IFRlbXBsYXRlLmZyb21TdGFjayhzdGFjayk7XG4gIC8vIEFzc2VydCByZXNvdXJjZXNcbiAgdGVtcGxhdGUuaGFzUmVzb3VyY2VQcm9wZXJ0aWVzKCdBV1M6OkNsb3VkRnJvbnQ6OkRpc3RyaWJ1dGlvbicsIHtcbiAgICBEaXN0cmlidXRpb25Db25maWc6IHtcbiAgICAgIE9yaWdpbnM6IFtcbiAgICAgICAge1xuICAgICAgICAgIERvbWFpbk5hbWU6IFwiZXhhbXBsZS5jb21cIixcbiAgICAgICAgICBJZDogXCJjdXN0b20taHR0cC1vcmlnaW4tZm9yLXRlc3RpbmdcIlxuICAgICAgICB9XG4gICAgICBdXG4gICAgfVxuICB9KTtcbn0pO1xuXG50ZXN0KCdUZXN0IHRoYXQgd2UgZG8gbm90IGNyZWF0ZSBhbiBBY2Nlc3MgTG9nIGJ1Y2tldCBmb3IgQ0YgbG9ncyBpZiBvbmUgaXMgcHJvdmlkZWQnLCAoKSA9PiB7XG4gIGNvbnN0IHN0YWNrID0gbmV3IGNkay5TdGFjaygpO1xuICBjb25zdCBjZlMzQWNjZXNzTG9nQnVja2V0ID0gbmV3IHMzLkJ1Y2tldChzdGFjaywgJ2NmLXMzLWFjY2Vzcy1sb2dzJyk7XG4gIG5ldyBDbG91ZEZyb250VG9TMyhzdGFjaywgJ3Rlc3QtY2xvdWRmcm9udC1zMycsIHtcbiAgICBjbG91ZEZyb250TG9nZ2luZ0J1Y2tldFByb3BzOiB7XG4gICAgICBzZXJ2ZXJBY2Nlc3NMb2dzQnVja2V0OiBjZlMzQWNjZXNzTG9nQnVja2V0XG4gICAgfVxuICB9KTtcblxuICBjb25zdCB0ZW1wbGF0ZSA9IFRlbXBsYXRlLmZyb21TdGFjayhzdGFjayk7XG4gIHRlbXBsYXRlLnJlc291cmNlQ291bnRJcyhcIkFXUzo6UzM6OkJ1Y2tldFwiLCA0KTtcblxufSk7XG5cbi8vID09PT09PT09PT09PT09PT09PT09PVxuLy8gUzMgQ29udGVudCBCdWNrZXQgQWNjZXNzIExvZ3MgQnVja2V0XG4vLyA9PT09PT09PT09PT09PT09PT09PT1cbnRlc3QoJ1Byb3ZpZGluZyBsb2dnaW5nQnVja2V0UHJvcHMgYW5kIGV4aXN0aW5nTG9nZ2luZ0J1Y2tldCBpcyBhbiBlcnJvcicsICgpID0+IHtcbiAgY29uc3Qgc3RhY2sgPSBuZXcgY2RrLlN0YWNrKCk7XG4gIGNvbnN0IGxvZ0J1Y2tldCA9IG5ldyBzMy5CdWNrZXQoc3RhY2ssICdsb2ctYnVja2V0Jywge30pO1xuXG4gIGNvbnN0IGFwcCA9ICgpID0+IHtcbiAgICBuZXcgQ2xvdWRGcm9udFRvUzMoc3RhY2ssICdjbG91ZGZyb250LXMzJywge1xuICAgICAgYnVja2V0UHJvcHM6IHtcbiAgICAgICAgc2VydmVyQWNjZXNzTG9nc0J1Y2tldDogbG9nQnVja2V0LFxuICAgICAgfSxcbiAgICAgIGxvZ2dpbmdCdWNrZXRQcm9wczoge1xuICAgICAgICBidWNrZXROYW1lOiAnYW55dGhpbmcnXG4gICAgICB9XG4gICAgfSk7XG4gIH07XG4gIGV4cGVjdChhcHApLnRvVGhyb3coL0Vycm9yIC0gYm90aGxvZyBidWNrZXQgcHJvcHMgYW5kIGFuIGV4aXN0aW5nIGxvZyBidWNrZXQgd2VyZSBwcm92aWRlZC5cXG4vKTtcbn0pO1xuXG50ZXN0KCdQcm92aWRpbmcgZXhpc3RpbmdMb2dnaW5nQnVja2V0IGFuZCBsb2dTM0FjY2Vzc0xvZ3M9ZmFsc2UgaXMgYW4gZXJyb3InLCAoKSA9PiB7XG4gIGNvbnN0IHN0YWNrID0gbmV3IGNkay5TdGFjaygpO1xuICBjb25zdCBsb2dCdWNrZXQgPSBuZXcgczMuQnVja2V0KHN0YWNrLCAnY2xvdWRmcm9udC1sb2ctYnVja2V0Jywge30pO1xuXG4gIGNvbnN0IGFwcCA9ICgpID0+IHtcbiAgICBuZXcgQ2xvdWRGcm9udFRvUzMoc3RhY2ssICdjbG91ZGZyb250LXMzJywge1xuICAgICAgYnVja2V0UHJvcHM6IHtcbiAgICAgICAgc2VydmVyQWNjZXNzTG9nc0J1Y2tldDogbG9nQnVja2V0LFxuICAgICAgfSxcbiAgICAgIGxvZ1MzQWNjZXNzTG9nczogZmFsc2VcbiAgICB9KTtcbiAgfTtcbiAgZXhwZWN0KGFwcCkudG9UaHJvdygvRXJyb3IgLSBsb2dTM0FjY2Vzc0xvZ3MgaXMgZmFsc2UsIGJ1dCBhIGxvZyBidWNrZXQgd2FzIHByb3ZpZGVkIGluIGJ1Y2tldFByb3BzLlxcbi8pO1xufSk7XG5cbnRlc3QoJ1Byb3ZpZGluZyBsb2dnaW5nQnVja2V0UHJvcHMgYW5kIGxvZ1MzQWNjZXNzTG9ncz1mYWxzZSBpcyBhbiBlcnJvcicsICgpID0+IHtcbiAgY29uc3Qgc3RhY2sgPSBuZXcgY2RrLlN0YWNrKCk7XG5cbiAgY29uc3QgYXBwID0gKCkgPT4ge1xuICAgIG5ldyBDbG91ZEZyb250VG9TMyhzdGFjaywgJ2Nsb3VkZnJvbnQtczMnLCB7XG4gICAgICBsb2dnaW5nQnVja2V0UHJvcHM6IHtcbiAgICAgICAgYnVja2V0TmFtZTogJ2FueXRoaW5nJ1xuICAgICAgfSxcbiAgICAgIGxvZ1MzQWNjZXNzTG9nczogZmFsc2VcbiAgICB9KTtcbiAgfTtcbiAgLy8gTk9URTogVGhpcyBlcnJvciBpcyB0aHJvd24gYnkgQ2hlY2tTM1Byb3BzKCksIG5vdCBDaGVja0NvbnN0cnVjdFNwZWNpZmljUHJvcHMoKVxuICBleHBlY3QoYXBwKS50b1Rocm93KC9FcnJvciAtIElmIGxvZ1MzQWNjZXNzTG9ncyBpcyBmYWxzZSwgc3VwcGx5aW5nIGxvZ2dpbmdCdWNrZXRQcm9wcyBvciBleGlzdGluZ0xvZ2dpbmdCdWNrZXRPYmogaXMgaW52YWxpZC5cXG4vKTtcbn0pO1xuXG4vLyB0ZXN0KCdObyBuZXcgbG9nZ2luZ0J1Y2tldCBpcyBjcmVhdGVkIGlmIGV4aXN0aW5nTG9nZ2luZ0J1Y2tldCBpcyBzdXBwbGllZCcsICgpID0+IHtcbnRlc3QoJ2xvZ2dpbmdCdWNrZXRQcm9wcyBpcyBzdXBwbGllZCBpcyBpbnRlZ3JhdGVkIGludG8gYXJjaGl0ZWN0dXJlIGNvcnJlY3RseScsICgpID0+IHtcbiAgY29uc3Qgc3RhY2sgPSBuZXcgY2RrLlN0YWNrKCk7XG5cbiAgY29uc3QgdGVzdE5hbWUgPSBcInRlc3QtbmFtZVwiO1xuICBjb25zdCBjb25zdHJ1Y3QgPSBuZXcgQ2xvdWRGcm9udFRvUzMoc3RhY2ssICdjbG91ZGZyb250LXMzJywge1xuICAgIGJ1Y2tldFByb3BzOiB7XG4gICAgICByZW1vdmFsUG9saWN5OiBjZGsuUmVtb3ZhbFBvbGljeS5ERVNUUk9ZLFxuICAgIH0sXG4gICAgbG9nZ2luZ0J1Y2tldFByb3BzOiB7XG4gICAgICByZW1vdmFsUG9saWN5OiBjZGsuUmVtb3ZhbFBvbGljeS5ERVNUUk9ZLFxuICAgICAgYXV0b0RlbGV0ZU9iamVjdHM6IHRydWUsXG4gICAgICBidWNrZXROYW1lOiB0ZXN0TmFtZVxuICAgIH1cbiAgfSk7XG5cbiAgZXhwZWN0KGNvbnN0cnVjdC5zM0xvZ2dpbmdCdWNrZXQpLnRvQmVEZWZpbmVkKCk7XG5cbiAgY29uc3QgdGVtcGxhdGUgPSBUZW1wbGF0ZS5mcm9tU3RhY2soc3RhY2spO1xuICB0ZW1wbGF0ZS5yZXNvdXJjZUNvdW50SXMoXCJBV1M6OlMzOjpCdWNrZXRcIiwgNCk7XG5cbiAgdGVtcGxhdGUuaGFzUmVzb3VyY2VQcm9wZXJ0aWVzKFwiQVdTOjpTMzo6QnVja2V0XCIsIHtcbiAgICBCdWNrZXROYW1lOiB0ZXN0TmFtZVxuICB9KTtcblxuICB0ZW1wbGF0ZS5oYXNSZXNvdXJjZVByb3BlcnRpZXMoXCJBV1M6OlMzOjpCdWNrZXRcIiwge1xuICAgIExvZ2dpbmdDb25maWd1cmF0aW9uOiB7XG4gICAgICBEZXN0aW5hdGlvbkJ1Y2tldE5hbWU6IHtcbiAgICAgICAgUmVmOiBcImNsb3VkZnJvbnRzM1MzTG9nZ2luZ0J1Y2tldDUyRUVCNzA4XCJcbiAgICAgIH1cbiAgICB9XG4gIH0pO1xufSk7XG50ZXN0KCdidWNrZXRQcm9wczpzZXJ2ZXJBY2Nlc3NMb2dzQnVja2V0IGlzIHN1cHBsaWVkIGlzIGludGVncmF0ZWQgaW50byBhcmNoaXRlY3R1cmUgY29ycmVjdGx5JywgKCkgPT4ge1xuICBjb25zdCB0ZXN0TmFtZSA9ICdzb21lLW5hbWUnO1xuICBjb25zdCBzdGFjayA9IG5ldyBjZGsuU3RhY2soKTtcbiAgY29uc3QgbG9nQnVja2V0ID0gbmV3IHMzLkJ1Y2tldChzdGFjaywgJ3Rlc3QtbG9nJywge1xuICAgIGJ1Y2tldE5hbWU6IHRlc3ROYW1lLFxuICB9KTtcblxuICBjb25zdCBjb25zdHJ1Y3QgPSBuZXcgQ2xvdWRGcm9udFRvUzMoc3RhY2ssICdjbG91ZGZyb250LXMzJywge1xuICAgIGJ1Y2tldFByb3BzOiB7XG4gICAgICBzZXJ2ZXJBY2Nlc3NMb2dzQnVja2V0OiBsb2dCdWNrZXQsXG4gICAgfSxcbiAgfSk7XG5cbiAgZXhwZWN0KGNvbnN0cnVjdC5zM0xvZ2dpbmdCdWNrZXQpLnRvQmVEZWZpbmVkKCk7XG4gIGNvbnN0IHRlbXBsYXRlID0gVGVtcGxhdGUuZnJvbVN0YWNrKHN0YWNrKTtcblxuICB0ZW1wbGF0ZS5yZXNvdXJjZUNvdW50SXMoXCJBV1M6OlMzOjpCdWNrZXRcIiwgNCk7XG4gIHRlbXBsYXRlLmhhc1Jlc291cmNlUHJvcGVydGllcyhcIkFXUzo6UzM6OkJ1Y2tldFwiLCB7XG4gICAgTG9nZ2luZ0NvbmZpZ3VyYXRpb246IHtcbiAgICAgIERlc3RpbmF0aW9uQnVja2V0TmFtZToge1xuICAgICAgICBSZWY6IFwidGVzdGxvZ0U4OEI0QzZCXCJcbiAgICAgIH1cbiAgICB9XG4gIH0pO1xufSk7XG5cbi8vID09PT09PT09PT09PT09PT09PT09PVxuLy8gQ2xvdWRGcm9udCBMb2cgQnVja2V0XG4vLyA9PT09PT09PT09PT09PT09PT09PT1cbnRlc3QoJ1Byb3ZpZGluZyBjbG91ZEZyb250TG9nZ2luZ0J1Y2tldFByb3BzIGFuZCBhIGxvZyBidWNrZXQgaW4gY2xvdWRGcm9udERpc3RyYnV0aW9uUHJvcHMgaXMgYW4gZXJyb3InLCAoKSA9PiB7XG4gIGNvbnN0IHN0YWNrID0gbmV3IGNkay5TdGFjaygpO1xuICBjb25zdCBsb2dCdWNrZXQgPSBuZXcgczMuQnVja2V0KHN0YWNrLCAnY2xvdWRmcm9udC1sb2ctYnVja2V0Jywge30pO1xuXG4gIGNvbnN0IGFwcCA9ICgpID0+IHtcbiAgICBuZXcgQ2xvdWRGcm9udFRvUzMoc3RhY2ssICdjbG91ZGZyb250LXMzJywge1xuICAgICAgY2xvdWRGcm9udERpc3RyaWJ1dGlvblByb3BzOiB7XG4gICAgICAgIGxvZ0J1Y2tldFxuICAgICAgfSxcbiAgICAgIGNsb3VkRnJvbnRMb2dnaW5nQnVja2V0UHJvcHM6IHtcbiAgICAgICAgcmVtb3ZhbFBvbGljeTogY2RrLlJlbW92YWxQb2xpY3kuREVTVFJPWSxcbiAgICAgICAgYXV0b0RlbGV0ZU9iamVjdHM6IHRydWVcbiAgICAgIH1cbiAgICB9KTtcbiAgfTtcblxuICBleHBlY3QoYXBwKS50b1Rocm93KCk7XG59KTtcbnRlc3QoJ2Nsb3VkRnJvbnRMb2dnaW5nQnVja2V0UHJvcHMgYXJlIHVzZWQgY29ycmVjdGx5JywgKCkgPT4ge1xuICBjb25zdCBzdGFjayA9IG5ldyBjZGsuU3RhY2soKTtcblxuICBjb25zdCB0ZXN0TmFtZSA9IFwidGVzdC1uYW1lXCI7XG4gIGNvbnN0IGNvbnN0cnVjdCA9IG5ldyBDbG91ZEZyb250VG9TMyhzdGFjaywgJ2Nsb3VkZnJvbnQtczMnLCB7XG4gICAgY2xvdWRGcm9udExvZ2dpbmdCdWNrZXRQcm9wczoge1xuICAgICAgcmVtb3ZhbFBvbGljeTogY2RrLlJlbW92YWxQb2xpY3kuREVTVFJPWSxcbiAgICAgIGF1dG9EZWxldGVPYmplY3RzOiB0cnVlLFxuICAgICAgYnVja2V0TmFtZTogdGVzdE5hbWVcbiAgICB9XG4gIH0pO1xuXG4gIGV4cGVjdChjb25zdHJ1Y3QuY2xvdWRGcm9udExvZ2dpbmdCdWNrZXQpLnRvQmVEZWZpbmVkKCk7XG5cbiAgY29uc3QgdGVtcGxhdGUgPSBUZW1wbGF0ZS5mcm9tU3RhY2soc3RhY2spO1xuICB0ZW1wbGF0ZS5yZXNvdXJjZUNvdW50SXMoXCJBV1M6OlMzOjpCdWNrZXRcIiwgNCk7XG5cbiAgdGVtcGxhdGUuaGFzUmVzb3VyY2VQcm9wZXJ0aWVzKFwiQVdTOjpTMzo6QnVja2V0XCIsIHtcbiAgICBCdWNrZXROYW1lOiB0ZXN0TmFtZVxuICB9KTtcblxufSk7XG50ZXN0KCdMb2dnaW5nIGRpc2FibGVkIGluIENsb3VkRnJvbnQgcHJvcHMgaXMgaGFuZGxlZCBjb3JyZWN0bHknLCAoKSA9PiB7XG4gIGNvbnN0IHN0YWNrID0gbmV3IGNkay5TdGFjaygpO1xuXG4gIGNvbnN0IGNvbnN0cnVjdCA9IGRlcGxveShzdGFjaywgeyBjbG91ZEZyb250RGlzdHJpYnV0aW9uUHJvcHM6IHsgZW5hYmxlTG9nZ2luZzogZmFsc2UgfSB9KTtcbiAgY29uc3QgdGVtcGxhdGUgPSBUZW1wbGF0ZS5mcm9tU3RhY2soc3RhY2spO1xuXG4gIC8vIE9ubHkgdGhlIGNvbnRlbnQgYnVja2V0IGFuZCBpdCBTMyBBY2Nlc3MgTG9nIGJ1Y2tldCAobm8gQ2xvdWRmcm9udCBsb2cgYnVja2V0KVxuICB0ZW1wbGF0ZS5yZXNvdXJjZUNvdW50SXMoXCJBV1M6OlMzOjpCdWNrZXRcIiwgMik7XG5cbiAgLy8gTm8gbG9nZ2luZyBpcyBjb25maWd1cmVkXG4gIHRlbXBsYXRlLnJlc291cmNlUHJvcGVydGllc0NvdW50SXMoXCJBV1M6OkNsb3VkRnJvbnQ6OkRpc3RyaWJ1dGlvblwiLCB7XG4gICAgRGlzdHJpYnV0aW9uQ29uZmlnOiB7XG4gICAgICBMb2dnaW5nOiBNYXRjaC5hbnlWYWx1ZSgpXG4gICAgfVxuICB9LCAwKTtcbiAgZXhwZWN0KGNvbnN0cnVjdC5jbG91ZEZyb250TG9nZ2luZ0J1Y2tldCA9PT0gdW5kZWZpbmVkKTtcbn0pO1xudGVzdCgnTm8gbmV3IENsb3VkRnJvbnRMb2dnaW5nQnVja2V0IGlzIGNyZWF0ZWQgaWYgY2xvdWRGcm9udExvZ2dpbmdCdWNrZXRQcm9wczpsb2dCdWNrZXQgaXMgc3VwcGxpZWQnLCAoKSA9PiB7XG4gIGNvbnN0IHRlc3ROYW1lID0gJ3JhbmRvbS12YWx1ZSc7XG4gIGNvbnN0IHN0YWNrID0gbmV3IGNkay5TdGFjaygpO1xuICBjb25zdCBsb2dCdWNrZXQgPSBuZXcgczMuQnVja2V0KHN0YWNrLCAnY2xvdWRmcm9udC1sb2ctYnVja2V0Jywge1xuICAgIGJ1Y2tldE5hbWU6IHRlc3ROYW1lXG4gIH0pO1xuXG4gIC8vIGNvbnN0IGNvbnN0cnVjdCA9XG4gIGNvbnN0IGNvbnN0cnVjdCA9IG5ldyBDbG91ZEZyb250VG9TMyhzdGFjaywgJ2Nsb3VkZnJvbnQtczMnLCB7XG4gICAgY2xvdWRGcm9udERpc3RyaWJ1dGlvblByb3BzOiB7XG4gICAgICBsb2dCdWNrZXRcbiAgICB9LFxuICB9KTtcblxuICBleHBlY3QoY29uc3RydWN0LmNsb3VkRnJvbnRMb2dnaW5nQnVja2V0KS50b0JlRGVmaW5lZCgpO1xuICBjb25zdCB0ZW1wbGF0ZSA9IFRlbXBsYXRlLmZyb21TdGFjayhzdGFjayk7XG5cbiAgLy8gQ29udGVudCBidWNrZXQsIENvbnRlbnQgYnVja2V0IFMzIEFjY2VzcyBMb2cgYnVja2V0LCBjbG91ZGZyb250IGxvZyBidWNrZXRcbiAgdGVtcGxhdGUucmVzb3VyY2VDb3VudElzKFwiQVdTOjpTMzo6QnVja2V0XCIsIDMpO1xuXG4gIC8vIEVuc3VyZSBvdXIgZXhpc3RpbmcgYnVja2V0IGhhcyBiZWVuIHVzZWQgZm9yIGNsb3VkZnJvbnQgbG9nZ2luZ1xuICB0ZW1wbGF0ZS5oYXNSZXNvdXJjZVByb3BlcnRpZXMoXCJBV1M6OkNsb3VkRnJvbnQ6OkRpc3RyaWJ1dGlvblwiLCB7XG4gICAgRGlzdHJpYnV0aW9uQ29uZmlnOiB7XG4gICAgICBMb2dnaW5nOiB7XG4gICAgICAgIEJ1Y2tldDoge1xuICAgICAgICAgIFwiRm46OkdldEF0dFwiOiBbXG4gICAgICAgICAgICBcImNsb3VkZnJvbnRsb2didWNrZXRERjcwNThGQlwiLFxuICAgICAgICAgICAgXCJSZWdpb25hbERvbWFpbk5hbWVcIlxuICAgICAgICAgIF1cbiAgICAgICAgfVxuICAgICAgfVxuICAgIH1cbiAgfSk7XG5cbn0pO1xuXG4vLyA9PT09PT09PT09PT09PT09PT09PT1cbi8vIENsb3VkRnJvbnQgTG9ncyBCdWNrZXQgQWNjZXNzIExvZyBCdWNrZXRcbi8vID09PT09PT09PT09PT09PT09PT09PVxudGVzdCgnUHJvdmlkaW5nIGNsb3VkRnJvbnRMb2dnaW5nQnVja2V0QWNjZXNzTG9nQnVja2V0UHJvcHMgYW5kIGNsb3VkRnJvbnRMb2dnaW5nQnVja2V0UHJvcHM6c2VydmVyQWNjZXNzTG9nc0J1Y2tldCBpcyBhbiBlcnJvcicsICgpID0+IHtcbiAgY29uc3Qgc3RhY2sgPSBuZXcgY2RrLlN0YWNrKCk7XG4gIGNvbnN0IGxvZ0J1Y2tldCA9IG5ldyBzMy5CdWNrZXQoc3RhY2ssICdjbG91ZGZyb250LWxvZy1idWNrZXQnLCB7fSk7XG5cbiAgY29uc3QgYXBwID0gKCkgPT4ge1xuICAgIG5ldyBDbG91ZEZyb250VG9TMyhzdGFjaywgJ2Nsb3VkZnJvbnQtczMnLCB7XG4gICAgICBjbG91ZEZyb250TG9nZ2luZ0J1Y2tldFByb3BzOiB7XG4gICAgICAgIHNlcnZlckFjY2Vzc0xvZ3NCdWNrZXQ6IGxvZ0J1Y2tldCxcbiAgICAgICAgcmVtb3ZhbFBvbGljeTogY2RrLlJlbW92YWxQb2xpY3kuREVTVFJPWSxcbiAgICAgICAgYXV0b0RlbGV0ZU9iamVjdHM6IHRydWVcbiAgICAgIH0sXG4gICAgICBjbG91ZEZyb250TG9nZ2luZ0J1Y2tldEFjY2Vzc0xvZ0J1Y2tldFByb3BzOiB7XG4gICAgICAgIGJ1Y2tldE5hbWU6ICdzcGVjZmljLW5hbWUtaXMtaW5jb25zZXF1ZW50aWFsJ1xuICAgICAgfVxuICAgIH0pO1xuICB9O1xuXG4gIGV4cGVjdChhcHApLnRvVGhyb3coXG4gICAgL0Vycm9yIC0gYW4gZXhpc3RpbmcgQ2xvdWRGcm9udCBsb2cgYnVja2V0IFMzIGFjY2VzcyBsb2cgYnVja2V0IGFuZCBjbG91ZEZyb250TG9nZ2luZ0J1Y2tldEFjY2Vzc0xvZ0J1Y2tldFByb3BzIHdlcmUgcHJvdmlkZWRcXG4vKTtcbn0pO1xudGVzdCgnUHJvdmlkaW5nIGNsb3VkRnJvbnRMb2dnaW5nQnVja2V0QWNjZXNzTG9nQnVja2V0UHJvcHMgYW5kIGxvZ0Nsb3VkRnJvbnRBY2Nlc3NMb2c9ZmFsc2UgaXMgYW4gZXJyb3InLCAoKSA9PiB7XG4gIGNvbnN0IHN0YWNrID0gbmV3IGNkay5TdGFjaygpO1xuXG4gIGNvbnN0IGFwcCA9ICgpID0+IHtcbiAgICBuZXcgQ2xvdWRGcm9udFRvUzMoc3RhY2ssICdjbG91ZGZyb250LXMzJywge1xuICAgICAgbG9nQ2xvdWRGcm9udEFjY2Vzc0xvZzogZmFsc2UsXG4gICAgICBjbG91ZEZyb250TG9nZ2luZ0J1Y2tldEFjY2Vzc0xvZ0J1Y2tldFByb3BzOiB7XG4gICAgICAgIGJ1Y2tldE5hbWU6ICdzcGVjZmljLW5hbWUtaXMtaW5jb25zZXF1ZW50aWFsJ1xuICAgICAgfVxuICAgIH0pO1xuICB9O1xuXG4gIGV4cGVjdChhcHApLnRvVGhyb3coL0Vycm9yIC0gY2xvdWRGcm9udExvZ2dpbmdCdWNrZXRBY2Nlc3NMb2dCdWNrZXRQcm9wcyB3ZXJlIHByb3ZpZGVkIGJ1dCBsb2dDbG91ZEZyb250QWNjZXNzTG9nIHdhcyBmYWxzZVxcbi8pO1xufSk7XG50ZXN0KCdQcm92aWRpbmcgbG9nQ2xvdWRGcm9udEFjY2Vzc0xvZz1mYWxzZSBhbmQgY2xvdWRGcm9udExvZ2dpbmdCdWNrZXRQcm9wczpzZXJ2ZXJBY2Nlc3NMb2dzQnVja2V0IGlzIGFuIGVycm9yJywgKCkgPT4ge1xuICBjb25zdCBzdGFjayA9IG5ldyBjZGsuU3RhY2soKTtcbiAgY29uc3QgbG9nQnVja2V0ID0gbmV3IHMzLkJ1Y2tldChzdGFjaywgJ2Nsb3VkZnJvbnQtbG9nLWJ1Y2tldCcsIHt9KTtcblxuICBjb25zdCBhcHAgPSAoKSA9PiB7XG4gICAgbmV3IENsb3VkRnJvbnRUb1MzKHN0YWNrLCAnY2xvdWRmcm9udC1zMycsIHtcbiAgICAgIGNsb3VkRnJvbnRMb2dnaW5nQnVja2V0UHJvcHM6IHtcbiAgICAgICAgc2VydmVyQWNjZXNzTG9nc0J1Y2tldDogbG9nQnVja2V0LFxuICAgICAgICByZW1vdmFsUG9saWN5OiBjZGsuUmVtb3ZhbFBvbGljeS5ERVNUUk9ZLFxuICAgICAgICBhdXRvRGVsZXRlT2JqZWN0czogdHJ1ZVxuICAgICAgfSxcbiAgICAgIGxvZ0Nsb3VkRnJvbnRBY2Nlc3NMb2c6IGZhbHNlLFxuICAgIH0pO1xuICB9O1xuXG4gIGV4cGVjdChhcHApLnRvVGhyb3coL0Vycm9yIC0gcHJvcHMuY2xvdWRGcm9udExvZ2dpbmdCdWNrZXRQcm9wcy5zZXJ2ZXJBY2Nlc3NMb2dzQnVja2V0IHdhcyBwcm92aWRlZCBidXQgbG9nQ2xvdWRGcm9udEFjY2Vzc0xvZyB3YXMgZmFsc2VcXG4vKTtcbn0pO1xudGVzdCgnY2xvdWRGcm9udExvZ2dpbmdCdWNrZXRBY2Nlc3NMb2dCdWNrZXRQcm9wcyBhcmUgdXNlZCBjb3JyZWN0bHknLCAoKSA9PiB7XG4gIGNvbnN0IHN0YWNrID0gbmV3IGNkay5TdGFjaygpO1xuXG4gIG5ldyBDbG91ZEZyb250VG9TMyhzdGFjaywgJ2Nsb3VkZnJvbnQtczMnLCB7XG4gICAgY2xvdWRGcm9udExvZ2dpbmdCdWNrZXRBY2Nlc3NMb2dCdWNrZXRQcm9wczoge1xuICAgICAgd2Vic2l0ZUVycm9yRG9jdW1lbnQ6ICdwbGFjZWhvbGRlcicsXG4gICAgICB3ZWJzaXRlSW5kZXhEb2N1bWVudDogJ3BsYWNlaG9sZGUtdHdvJ1xuICAgIH1cbiAgfSk7XG5cbiAgY29uc3QgdGVtcGxhdGUgPSBUZW1wbGF0ZS5mcm9tU3RhY2soc3RhY2spO1xuXG4gIC8vIENvbnRlbnQgQnVja2V0LCBDb250ZW50IEJ1Y2tldCBTMyBBY2Nlc3MgTG9nIEJ1Y2tldCwgQ2xvdWRGcm9udCBMb2cgQnVja2V0LCBDbG91ZEZyb250IExvZyBCdWNrZXQgUzMgQWNjZXNzIExvZyBCdWNrZXRcbiAgdGVtcGxhdGUucmVzb3VyY2VDb3VudElzKFwiQVdTOjpTMzo6QnVja2V0XCIsIDQpO1xuICB0ZW1wbGF0ZS5oYXNSZXNvdXJjZVByb3BlcnRpZXMoXCJBV1M6OlMzOjpCdWNrZXRcIiwge1xuICAgIFdlYnNpdGVDb25maWd1cmF0aW9uOiB7XG4gICAgICBFcnJvckRvY3VtZW50OiAncGxhY2Vob2xkZXInLFxuICAgICAgSW5kZXhEb2N1bWVudDogJ3BsYWNlaG9sZGUtdHdvJ1xuICAgIH1cbiAgfSk7XG5cbn0pO1xudGVzdCgnSWYgZXhpc3RpbmcgQ2xvdWRGcm9udCBMb2cgYnVja2V0IFMzIEFjY2VzcyBMb2dnaW5nIGJ1Y2tldCBpcyBwcm92aWRlZCwgaXQgaXMgdXNlZCBjb3JyZWN0bHknLCAoKSA9PiB7XG4gIGNvbnN0IHN0YWNrID0gbmV3IGNkay5TdGFjaygpO1xuICBjb25zdCB0ZXN0TmFtZSA9ICdjZi1sb2ctczMtbG9nJztcbiAgY29uc3QgY2ZMb2dTM0FjY2Vzc0xvZ0J1Y2tldCA9IG5ldyBzMy5CdWNrZXQoc3RhY2ssICdjZi1sb2ctczMtYWNjZXNzLWxvZy1idWNrZXQnLCB7XG4gICAgYnVja2V0TmFtZTogdGVzdE5hbWVcbiAgfSk7XG5cbiAgbmV3IENsb3VkRnJvbnRUb1MzKHN0YWNrLCAnY2xvdWRmcm9udC1zMycsIHtcbiAgICBjbG91ZEZyb250TG9nZ2luZ0J1Y2tldFByb3BzOiB7XG4gICAgICBzZXJ2ZXJBY2Nlc3NMb2dzQnVja2V0OiBjZkxvZ1MzQWNjZXNzTG9nQnVja2V0XG4gICAgfVxuICB9KTtcblxuICBjb25zdCB0ZW1wbGF0ZSA9IFRlbXBsYXRlLmZyb21TdGFjayhzdGFjayk7XG5cbiAgLy8gQ29udGVudCBCdWNrZXQsIENvbnRlbnQgQnVja2V0IFMzIEFjY2VzcyBMb2cgQnVja2V0LCBDbG91ZEZyb250IExvZyBCdWNrZXQsIENsb3VkRnJvbnQgTG9nIEJ1Y2tldCBTMyBBY2Nlc3MgTG9nIEJ1Y2tldFxuICB0ZW1wbGF0ZS5yZXNvdXJjZUNvdW50SXMoXCJBV1M6OlMzOjpCdWNrZXRcIiwgNCk7XG5cbiAgdGVtcGxhdGUuaGFzUmVzb3VyY2VQcm9wZXJ0aWVzKFwiQVdTOjpTMzo6QnVja2V0XCIsIHtcbiAgICBCdWNrZXROYW1lOiB0ZXN0TmFtZVxuICB9KTtcblxuICB0ZW1wbGF0ZS5oYXNSZXNvdXJjZVByb3BlcnRpZXMoXCJBV1M6OlMzOjpCdWNrZXRcIiwge1xuICAgIExvZ2dpbmdDb25maWd1cmF0aW9uOiB7XG4gICAgICBEZXN0aW5hdGlvbkJ1Y2tldE5hbWU6IHtcbiAgICAgICAgUmVmOiBcImNmbG9nczNhY2Nlc3Nsb2didWNrZXRERTM3NEMyN1wiXG4gICAgICB9XG4gICAgfVxuICB9KTtcblxufSk7XG5cbnRlc3QoJ2Nsb3VkRnJvbnRMb2dnaW5nQnVja2V0QWNjZXNzTG9nQnVja2V0IHByb3BlcnR5IGlzIHNldCBjb3JyZWN0bHknLCAoKSA9PiB7XG4gIGNvbnN0IHN0YWNrID0gbmV3IGNkay5TdGFjaygpO1xuXG4gIGNvbnN0IGNvbnN0cnVjdCA9IG5ldyBDbG91ZEZyb250VG9TMyhzdGFjaywgJ2Nsb3VkZnJvbnQtczMnLCB7XG4gICAgY2xvdWRGcm9udExvZ2dpbmdCdWNrZXRBY2Nlc3NMb2dCdWNrZXRQcm9wczoge1xuICAgICAgd2Vic2l0ZUVycm9yRG9jdW1lbnQ6ICdwbGFjZWhvbGRlcicsXG4gICAgICB3ZWJzaXRlSW5kZXhEb2N1bWVudDogJ3BsYWNlaG9sZGUtdHdvJ1xuICAgIH1cbiAgfSk7XG5cbiAgY29uc3QgdGVtcGxhdGUgPSBUZW1wbGF0ZS5mcm9tU3RhY2soc3RhY2spO1xuXG4gIC8vIENvbnRlbnQgQnVja2V0LCBDb250ZW50IEJ1Y2tldCBTMyBBY2Nlc3MgTG9nIEJ1Y2tldCwgQ2xvdWRGcm9udCBMb2cgQnVja2V0LCBDbG91ZEZyb250IExvZyBCdWNrZXQgUzMgQWNjZXNzIExvZyBCdWNrZXRcbiAgdGVtcGxhdGUucmVzb3VyY2VDb3VudElzKFwiQVdTOjpTMzo6QnVja2V0XCIsIDQpO1xuICB0ZW1wbGF0ZS5oYXNSZXNvdXJjZVByb3BlcnRpZXMoXCJBV1M6OlMzOjpCdWNrZXRcIiwge1xuICAgIFdlYnNpdGVDb25maWd1cmF0aW9uOiB7XG4gICAgICBFcnJvckRvY3VtZW50OiAncGxhY2Vob2xkZXInLFxuICAgICAgSW5kZXhEb2N1bWVudDogJ3BsYWNlaG9sZGUtdHdvJ1xuICAgIH1cbiAgfSk7XG4gIGV4cGVjdChjb25zdHJ1Y3QuY2xvdWRGcm9udExvZ2dpbmdCdWNrZXRBY2Nlc3NMb2dCdWNrZXQpLnRvQmVEZWZpbmVkKCk7XG4gIGV4cGVjdChjb25zdHJ1Y3QuY2xvdWRGcm9udExvZ2dpbmdCdWNrZXRBY2Nlc3NMb2dCdWNrZXQhLmJ1Y2tldE5hbWUpLnRvQmVEZWZpbmVkKCk7XG59KTtcblxudGVzdCgnbG9nQ2xvdWRGcm9udEFjY2Vzc0xvZyBwcm9wZXJ0eSBpcyB1c2VkIGNvcnJlY3RseScsICgpID0+IHtcbiAgY29uc3Qgc3RhY2sgPSBuZXcgY2RrLlN0YWNrKCk7XG5cbiAgY29uc3QgY29uc3RydWN0ID0gbmV3IENsb3VkRnJvbnRUb1MzKHN0YWNrLCAnY2xvdWRmcm9udC1zMycsIHtcbiAgICBsb2dDbG91ZEZyb250QWNjZXNzTG9nOiBmYWxzZVxuICB9KTtcblxuICBjb25zdCB0ZW1wbGF0ZSA9IFRlbXBsYXRlLmZyb21TdGFjayhzdGFjayk7XG5cbiAgLy8gQ29udGVudCBCdWNrZXQsIENvbnRlbnQgQnVja2V0IFMzIEFjY2VzcyBMb2cgQnVja2V0LCBDbG91ZEZyb250IExvZyBCdWNrZXQsIENsb3VkRnJvbnQgTG9nIEJ1Y2tldCBTMyBBY2Nlc3MgTG9nIEJ1Y2tldFxuICB0ZW1wbGF0ZS5yZXNvdXJjZUNvdW50SXMoXCJBV1M6OlMzOjpCdWNrZXRcIiwgMyk7XG4gIGV4cGVjdChjb25zdHJ1Y3QuY2xvdWRGcm9udExvZ2dpbmdCdWNrZXQpLnRvQmVEZWZpbmVkKCk7XG4gIGV4cGVjdChjb25zdHJ1Y3QuY2xvdWRGcm9udExvZ2dpbmdCdWNrZXRBY2Nlc3NMb2dCdWNrZXQpLm5vdC50b0JlRGVmaW5lZCgpO1xufSk7XG5cbnRlc3QoJ2FkZGl0aW9uYWxCZWhhdmlvcnMgaGF2ZSBjb3JyZWN0IG9yaWdpbicsICgpID0+IHtcbiAgY29uc3Qgc3RhY2sgPSBuZXcgY2RrLlN0YWNrKCk7XG5cbiAgY29uc3QgYWRkaXRpb25hbEJ1Y2tldCA9IGRlZmF1bHRzLkNyZWF0ZVNjcmFwQnVja2V0KHN0YWNrLCBcInNjcmFwQnVja2V0XCIsIHtcbiAgICByZW1vdmFsUG9saWN5OiBSZW1vdmFsUG9saWN5LkRFU1RST1ksXG4gICAgYXV0b0RlbGV0ZU9iamVjdHM6IHRydWUsXG4gIH0pO1xuXG4gIGNvbnN0IG9yaWdpbkFjY2Vzc0NvbnRyb2wgPSBuZXcgQ2ZuT3JpZ2luQWNjZXNzQ29udHJvbChzdGFjaywgJ0Nsb3VkRnJvbnRPYWMnLCB7XG4gICAgb3JpZ2luQWNjZXNzQ29udHJvbENvbmZpZzoge1xuICAgICAgbmFtZTogZGVmYXVsdHMuZ2VuZXJhdGVQaHlzaWNhbE9hY05hbWUoJ2F3cy1jbG91ZGZyb250LXMzLScsIFtfX2ZpbGVuYW1lXSksXG4gICAgICBvcmlnaW5BY2Nlc3NDb250cm9sT3JpZ2luVHlwZTogJ3MzJyxcbiAgICAgIHNpZ25pbmdCZWhhdmlvcjogJ2Fsd2F5cycsXG4gICAgICBzaWduaW5nUHJvdG9jb2w6ICdzaWd2NCcsXG4gICAgICBkZXNjcmlwdGlvbjogJ09yaWdpbiBhY2Nlc3MgY29udHJvbCBwcm92aXNpb25lZCBieSBhd3MtY2xvdWRmcm9udC1zMydcbiAgICB9XG4gIH0pO1xuXG4gIGNvbnN0IGFkZGl0aW9uYWxPcmlnaW4gPSBuZXcgZGVmYXVsdHMuUzNPYWNPcmlnaW4oYWRkaXRpb25hbEJ1Y2tldCwge1xuICAgIG9yaWdpbkFjY2Vzc0NvbnRyb2xcbiAgfSk7XG5cbiAgbmV3IENsb3VkRnJvbnRUb1MzKHN0YWNrLCAndGVzdC1jbG91ZGZyb250LXMzJywge1xuICAgIGNsb3VkRnJvbnREaXN0cmlidXRpb25Qcm9wczoge1xuICAgICAgYWRkaXRpb25hbEJlaGF2aW9yczoge1xuICAgICAgICAnL2Fzc2V0cy9wdWJsaWMvKic6IHtcbiAgICAgICAgICBvcmlnaW46IGFkZGl0aW9uYWxPcmlnaW4sXG4gICAgICAgICAgY2FjaGVQb2xpY3k6IENhY2hlUG9saWN5LkNBQ0hJTkdfRElTQUJMRUQsXG4gICAgICAgIH0sXG4gICAgICAgICduZ3N3Lmpzb24nOiB7XG4gICAgICAgICAgY2FjaGVQb2xpY3k6IENhY2hlUG9saWN5LkNBQ0hJTkdfRElTQUJMRUQsXG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9XG4gIH0pO1xuXG4gIGNvbnN0IHRlbXBsYXRlID0gVGVtcGxhdGUuZnJvbVN0YWNrKHN0YWNrKTtcbiAgdGVtcGxhdGUuaGFzUmVzb3VyY2VQcm9wZXJ0aWVzKFwiQVdTOjpDbG91ZEZyb250OjpEaXN0cmlidXRpb25cIiwge1xuICAgIERpc3RyaWJ1dGlvbkNvbmZpZzoge1xuICAgICAgQ2FjaGVCZWhhdmlvcnM6IFtcbiAgICAgICAge1xuICAgICAgICAgIENhY2hlUG9saWN5SWQ6IFwiNDEzNWVhMmQtNmRmOC00NGEzLTlkZjMtNGI1YTg0YmUzOWFkXCIsXG4gICAgICAgICAgQ29tcHJlc3M6IHRydWUsXG4gICAgICAgICAgUGF0aFBhdHRlcm46IFwiL2Fzc2V0cy9wdWJsaWMvKlwiLFxuICAgICAgICAgIFRhcmdldE9yaWdpbklkOiBcInRlc3RjbG91ZGZyb250czNDbG91ZEZyb250RGlzdHJpYnV0aW9uT3JpZ2luMjFENzgzOTFDXCIsXG4gICAgICAgICAgVmlld2VyUHJvdG9jb2xQb2xpY3k6IFwiYWxsb3ctYWxsXCJcbiAgICAgICAgfSxcbiAgICAgICAge1xuICAgICAgICAgIENhY2hlUG9saWN5SWQ6IFwiNDEzNWVhMmQtNmRmOC00NGEzLTlkZjMtNGI1YTg0YmUzOWFkXCIsXG4gICAgICAgICAgQ29tcHJlc3M6IHRydWUsXG4gICAgICAgICAgUGF0aFBhdHRlcm46IFwibmdzdy5qc29uXCIsXG4gICAgICAgICAgVGFyZ2V0T3JpZ2luSWQ6IFwidGVzdGNsb3VkZnJvbnRzM0Nsb3VkRnJvbnREaXN0cmlidXRpb25PcmlnaW4xMjQwNTEwMzlcIixcbiAgICAgICAgICBWaWV3ZXJQcm90b2NvbFBvbGljeTogXCJhbGxvdy1hbGxcIlxuICAgICAgICB9XG4gICAgICBdLFxuICAgIH1cbiAgfSk7XG59KTtcblxudGVzdCgnVGVzdCB0aGF0IFZhbGlkYXRlRGlzdHJpYnV0aW9uUHJvcHMoKSBpcyBiZWluZyBjYWxsZWQnLCAoKSA9PiB7XG4gIGNvbnN0IHN0YWNrID0gbmV3IFN0YWNrKCk7XG4gIGNvbnN0IHByb3BzOiBDbG91ZEZyb250VG9TM1Byb3BzID0ge1xuICAgIGNsb3VkRnJvbnREaXN0cmlidXRpb25Qcm9wczoge1xuICAgICAgaW52YWxpZFByb3BlcnR5OiB0cnVlXG4gICAgfVxuICB9O1xuXG4gIGNvbnN0IGFwcCA9ICgpID0+IHtcbiAgICBuZXcgQ2xvdWRGcm9udFRvUzMoc3RhY2ssICd0ZXN0LWNvbnN0cnVjdCcsIHByb3BzKTtcbiAgfTtcblxuICBleHBlY3QoYXBwKS50b1Rocm93KC9FUlJPUiAtIGludmFsaWRQcm9wZXJ0eSBpcyBub3QgYSB2YWxpZCBwcm9wZXJ0eSBvZiBEaXN0cmlidXRpb25Qcm9wcy8pO1xufSk7XG4iXX0=
|