@liflig/cdk 3.0.21 → 3.1.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,578 @@
1
+ import * as constructs from "constructs";
2
+ import * as cdk from "aws-cdk-lib";
3
+ import * as lambda from "aws-cdk-lib/aws-lambda";
4
+ import * as apigw from "aws-cdk-lib/aws-apigatewayv2";
5
+ import * as logs from "aws-cdk-lib/aws-logs";
6
+ import * as iam from "aws-cdk-lib/aws-iam";
7
+ import * as secretsmanager from "aws-cdk-lib/aws-secretsmanager";
8
+ import * as integrations from "aws-cdk-lib/aws-apigatewayv2-integrations";
9
+ import * as authorizers from "aws-cdk-lib/aws-apigatewayv2-authorizers";
10
+ import * as lambdaNodejs from "aws-cdk-lib/aws-lambda-nodejs";
11
+ import { createHash } from "crypto";
12
+ import * as route53 from "aws-cdk-lib/aws-route53";
13
+ import * as route53Targets from "aws-cdk-lib/aws-route53-targets";
14
+ import * as acm from "aws-cdk-lib/aws-certificatemanager";
15
+ import { tagResources } from "../tags";
16
+ import * as path from "path";
17
+ import { fileURLToPath } from "url";
18
+ const __filename = fileURLToPath(import.meta.url);
19
+ const __dirname = path.dirname(__filename);
20
+ /**
21
+ * This construct tries to simplify the creation of an API Gateway for a service, by collecting most
22
+ * of the common setup here.
23
+ *
24
+ * The approach followed in this construct is:
25
+ * 1. One API-GW per service
26
+ * 3. One subdomain per API-GW / service
27
+ * 4. Use HTTP API, not REST
28
+ * 5. Use a $default stage with autodeploy
29
+ * 6. Support multiple routes (with possible `/{proxy+}` to let all sub-paths through)
30
+ * 7. Allow custom integration/authorizer for each route, or defaults for the whole gateway
31
+ *
32
+ * The route integration is one of these:
33
+ * - ALB private integration with VPC Link using HTTPS to the ALB
34
+ * - Lambda integration
35
+ * - SQS integration
36
+ *
37
+ * ### Load Balancer Security Group
38
+ *
39
+ * Note that the load balancer used in an {@link AlbIntegrationProps} must allow outbound HTTPS
40
+ * traffic to its SecurityGroup. Otherwise, the VPC Link used by the API-GW can't get traffic from
41
+ * the ALB.
42
+ *
43
+ * ```
44
+ * const loadBalancerSecurityGroup = new ec2.SecurityGroup(..., {
45
+ * allowAllOutbound: false,
46
+ * })
47
+ *
48
+ * loadBalancerSecurityGroup.addEgressRule(
49
+ * loadBalancerSecurityGroup,
50
+ * ec2.Port.tcp(443),
51
+ * "Outbound to self for ALB to API-GW VPC-Link",
52
+ * )
53
+ *
54
+ * const loadBalancer = new lifligLoadBalancer.LoadBalancer(...,
55
+ * {
56
+ * overrideLoadBalancerProps: {
57
+ * securityGroup: loadBalancerSecurityGroup,
58
+ * },
59
+ * },
60
+ * )
61
+ * ```
62
+ *
63
+ * @template AuthScopesT This type parameter allows you to improve type safety on the
64
+ * `requiredScope` field on {@link CognitoUserPoolOrBasicAuthAuthorizerProps}, by narrowing the type
65
+ * to specific strings. You can then extend the `ApiGateway` with this type to enforce those scopes
66
+ * across the application. Remember that auth scopes must be on the format
67
+ * `{resource server identifier}/{scope name}`.
68
+ *
69
+ * Example:
70
+ * ```
71
+ * type AuthScopes = "external/read_users" | "internal/create_users"
72
+ *
73
+ * export class MyProjectApiGateway extends ApiGateway<AuthScopes> {}
74
+ * ```
75
+ * TypeScript will then enforce that `requiredScope` is one of `AuthScopes`, and provide
76
+ * auto-complete.
77
+ *
78
+ * @author Kristian Rekstad <kre@capraconsulting.no>
79
+ * @author Hermann Mørkrid <hem@liflig.no>
80
+ */
81
+ export class ApiGateway extends constructs.Construct {
82
+ /** The API Gateway HTTP API. This is the main construct for API-GW. */
83
+ httpApi;
84
+ /** The routes which connect the {@link httpApi} to the backend integration(s). */
85
+ routes = [];
86
+ /** The domain which consumers must use.*/
87
+ domain;
88
+ /** Access log group. */
89
+ logGroup;
90
+ props;
91
+ constructor(scope, id, props) {
92
+ super(scope, id);
93
+ this.props = props;
94
+ ApiGateway.validateProps(props, id, cdk.Stack.of(this));
95
+ const customDomain = new CustomDomain(this, "CustomDomain", props.dns);
96
+ this.domain = customDomain.customDomainName;
97
+ let corsOptions;
98
+ if (props.corsAllowAll) {
99
+ corsOptions = {
100
+ allowOrigins: ["*"],
101
+ // "The Authorization header can't be wildcarded and always needs to be listed explicitly"
102
+ // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Headers
103
+ // Content-Type may also require explicit whitelisting: https://stackoverflow.com/a/63567647
104
+ allowHeaders: ["Authorization", "Content-Type", "*"],
105
+ // Not using '*' here, because "In requests with credentials, it is treated as the literal
106
+ // method name '*' without special semantics"
107
+ // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Methods
108
+ allowMethods: [
109
+ apigw.CorsHttpMethod.GET,
110
+ apigw.CorsHttpMethod.POST,
111
+ apigw.CorsHttpMethod.PUT,
112
+ apigw.CorsHttpMethod.PATCH,
113
+ apigw.CorsHttpMethod.DELETE,
114
+ apigw.CorsHttpMethod.OPTIONS,
115
+ apigw.CorsHttpMethod.HEAD,
116
+ ],
117
+ maxAge: cdk.Duration.days(1),
118
+ };
119
+ }
120
+ // The actual API. This holds the routes, authorizers and integrations.
121
+ const api = new apigw.HttpApi(this, "HttpApi-" + props.dns.subdomain, {
122
+ // defaultIntegration: defaultIntegration, // This is for a catch-all $default route
123
+ description: `An HTTP API for ${props.dns.subdomain}.${props.dns.hostedZone.zoneName}.`,
124
+ disableExecuteApiEndpoint: true, // Force externals to go through custom domain. MUST be true when using Mutual TLS, for security reasons
125
+ createDefaultStage: true,
126
+ defaultDomainMapping: { domainName: customDomain.apiGwCustomDomain },
127
+ corsPreflight: corsOptions,
128
+ ...props?.propsOverride?.httpApi,
129
+ });
130
+ this.httpApi = api;
131
+ const stage = api.defaultStage?.node.defaultChild;
132
+ const logs = new ApiGatewayAccessLogs(this, "AccessLogs", stage, props.accessLogs, defaultAccessLogFormat);
133
+ this.logGroup = logs.logGroup;
134
+ stage.defaultRouteSettings = {
135
+ ...stage.defaultRouteSettings,
136
+ detailedMetricsEnabled: props.detailedMetrics ?? true,
137
+ throttlingBurstLimit: props.throttling?.burst, // Default for account is 5_000
138
+ throttlingRateLimit: props.throttling?.rate, // Default for account is 10_000
139
+ };
140
+ const defaultIntegration = props.defaultIntegration
141
+ ? this.createIntegration(props.defaultIntegration, props.dns)
142
+ : undefined;
143
+ const defaultAuthorizer = props.defaultAuthorization
144
+ ? this.createAuthorizer("DefaultAuthorizer", props.defaultAuthorization)
145
+ : undefined;
146
+ for (const route of props.routes) {
147
+ let integration;
148
+ if (route.integration) {
149
+ integration = this.createIntegration(route.integration, props.dns);
150
+ }
151
+ else {
152
+ integration = defaultIntegration; // Verified in validateProps
153
+ }
154
+ let authorizer;
155
+ if (route.authorization) {
156
+ authorizer = this.createAuthorizer(
157
+ // Using the route path here caused CloudFormation to fail due to resource names being too
158
+ // long. Therefore, we now hash the route path to get a shorter name.
159
+ `RouteAuthorizer${shortHash(`${route.method ?? ""}${route.path}`)}`, route.authorization);
160
+ }
161
+ else {
162
+ authorizer = defaultAuthorizer;
163
+ }
164
+ const routePaths = [route.path];
165
+ if (route.includeSubpaths === true) {
166
+ // If we include sub-paths, we add an additional route with /{proxy+} (the + means it will
167
+ // match all subroutes). We want both this route and the normal route without /{proxy+},
168
+ // since /{proxy+} will only match the base path with trailing slash.
169
+ routePaths.push(route.path + (route.path === "/" ? "" : "/") + "{proxy+}");
170
+ }
171
+ for (const routePath of routePaths) {
172
+ // The previous version of the API Gateway construct had a single route with the ID
173
+ // 'DefaultProxyRoute', and all gateways we used exposed the route /{proxy+}. When trying to
174
+ // change to this new version of the construct, deployment failed for gateways exposing
175
+ // /{proxy+}, because "a route with that key [i.e. path + method] already exists". We think
176
+ // this may be because we used the same route path, but with a new route ID, causing
177
+ // confusion for CloudFormation. So we keep the old route ID here for /{proxy+}.
178
+ const routeId = routePath === "/{proxy+}" ? "DefaultProxyRoute" : `Route-${routePath}`;
179
+ this.routes.push(new apigw.HttpRoute(this, routeId, {
180
+ httpApi: api,
181
+ integration: integration,
182
+ authorizer: authorizer,
183
+ routeKey: apigw.HttpRouteKey.with(routePath, route.method
184
+ ? apigw.HttpMethod[route.method]
185
+ : apigw.HttpMethod.ANY),
186
+ }));
187
+ }
188
+ }
189
+ tagResources(this, () => ({ service: props.dns.subdomain }));
190
+ }
191
+ /** @throws Error */
192
+ static validateProps(props, id, stack) {
193
+ for (const route of props.routes) {
194
+ if (!route.integration && !props.defaultIntegration) {
195
+ throw new Error(`No integration defined for route '${route.path}', and no default integration specified for the gateway`);
196
+ }
197
+ if (!route.authorization && !props.defaultAuthorization) {
198
+ throw new Error(`No authorization defined for route '${route.path}', and no default authorization specified for the gateway`);
199
+ }
200
+ if (!route.path.startsWith("/")) {
201
+ throw new Error(`Invalid path '${route.path}': paths must begin with '/' (use '/' for root path)`);
202
+ }
203
+ if (route.path !== "/" && route.path.endsWith("/")) {
204
+ throw new Error(`Invalid path '${route.path}': paths cannot end with '/' (except root path)`);
205
+ }
206
+ if (route.integration?.type === "ALB" &&
207
+ route.integration.hostName.startsWith("https:")) {
208
+ throw new Error(`The albIntegration.hostHttpsName should not include a protocol: ${route.integration.hostName}`);
209
+ }
210
+ }
211
+ if (props.dns.subdomain === "" || props.dns.subdomain.includes(" ")) {
212
+ throw new Error(`Subdomain must be set, and not contain spaces: ${props.dns.subdomain}`);
213
+ }
214
+ if (props.throttling?.burst && props.throttling.burst > 5_000) {
215
+ console.warn(`⚠️ Your throttling burst limit '${props.throttling.burst}' is higher than the AWS Account limit. Make sure your account has upgraded this quota! `, stack, id);
216
+ }
217
+ if (props.throttling?.rate && props.throttling.rate > 10_000) {
218
+ console.warn(`⚠️ Your throttling rate limit '${props.throttling.rate}' is higher than the AWS Account limit. Make sure your account has upgraded this quota! `, stack, id);
219
+ }
220
+ }
221
+ /**
222
+ * The authorizer only accepts requests from external users that are authorized.
223
+ * Unauthorized users are stopped in the API-GW, and not forwarded to the integration.
224
+ */
225
+ createAuthorizer(id, authorization) {
226
+ switch (authorization.type) {
227
+ case "NONE": {
228
+ return undefined;
229
+ }
230
+ case "IAM": {
231
+ // API Gateway invokes your API route only if the client has execute-api permission for the route.
232
+ // The client has to use AWS SigV4 to identify themselves in the request.
233
+ // Read this page for help with IAM and API-GW https://docs.aws.amazon.com/apigateway/latest/developerguide/security_iam_service-with-iam.html
234
+ // Note that [resource policies are not supported yet for HTTP](https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-access-control-iam.html)
235
+ return new authorizers.HttpIamAuthorizer();
236
+ }
237
+ case "COGNITO_USER_POOL": {
238
+ // We use a custom lambda authorizer here instead of the `HttpUserPoolAuthorizer` provided
239
+ // by CDK, in order to support `requiredScope` and setting of custom context variables.
240
+ const authorizer = new CognitoUserPoolAuthorizer(this, id + "Lambda", authorization);
241
+ return new authorizers.HttpLambdaAuthorizer(id, authorizer.lambda, {
242
+ responseTypes: authorizer.responseTypes,
243
+ // Max 1h, or disabled (0s). The value only matters when invalidating/dynamic credentials
244
+ resultsCacheTtl: cdk.Duration.hours(1),
245
+ });
246
+ }
247
+ case "BASIC_AUTH": {
248
+ const authorizer = new BasicAuthAuthorizer(this, id + "Lambda", authorization);
249
+ return new authorizers.HttpLambdaAuthorizer(id, authorizer.lambda, {
250
+ responseTypes: authorizer.responseTypes,
251
+ // Max 1h, or disabled (0s). The value only matters when invalidating/dynamic credentials
252
+ resultsCacheTtl: cdk.Duration.minutes(30),
253
+ });
254
+ }
255
+ case "COGNITO_USER_POOL_OR_BASIC_AUTH": {
256
+ const authorizer = new CognitoUserPoolOrBasicAuthAuthorizer(this, id + "Lambda", authorization);
257
+ return new authorizers.HttpLambdaAuthorizer(id, authorizer.lambda, {
258
+ responseTypes: authorizer.responseTypes,
259
+ // Max 1h, or disabled (0s). The value only matters when invalidating/dynamic credentials
260
+ resultsCacheTtl: cdk.Duration.hours(1),
261
+ });
262
+ }
263
+ }
264
+ }
265
+ createIntegration(integration, dns) {
266
+ switch (integration.type) {
267
+ case "ALB": {
268
+ // The VPCLink connects the integration into the ALB's VPC. Must be manually created when
269
+ // the VPC is imported, which is the case when using CorePlatformConsumer.
270
+ const vpcLink = new apigw.VpcLink(this, "AlbVpcLink", {
271
+ vpc: integration.vpc,
272
+ securityGroups: [integration.securityGroup],
273
+ subnets: integration.vpc.selectSubnets(), // This correctly selects the private subnets
274
+ });
275
+ const parameterMapping = new apigw.ParameterMapping()
276
+ /** See {@link AlbIntegrationProps.hostName} */
277
+ .overwriteHeader("Host",
278
+ // The Host header can NOT use dynamic mapping, like $context.domainName etc.
279
+ apigw.MappingValue.custom(integration.hostName));
280
+ if (integration.mapParameters !== undefined) {
281
+ integration.mapParameters(parameterMapping);
282
+ }
283
+ return new integrations.HttpAlbIntegration("AlbIntegration-" + dns.subdomain, integration.loadBalancerListener, {
284
+ secureServerName: integration.hostName,
285
+ vpcLink: vpcLink,
286
+ method: apigw.HttpMethod.ANY,
287
+ parameterMapping,
288
+ });
289
+ }
290
+ case "Lambda": {
291
+ return new integrations.HttpLambdaIntegration("LambdaIntegration", integration.lambda, {
292
+ payloadFormatVersion: apigw.PayloadFormatVersion.VERSION_2_0,
293
+ parameterMapping: undefined,
294
+ });
295
+ }
296
+ case "SQS": {
297
+ // API-GW does not have access to SQS by default
298
+ const role = new iam.Role(this, `ApiGwTo${integration.queue.node.id}ServiceRole`, {
299
+ description: "Allows API-GW to add messages to " + integration.queue.queueArn,
300
+ assumedBy: new iam.ServicePrincipal("apigateway.amazonaws.com"),
301
+ });
302
+ integration.queue.grantSendMessages(role);
303
+ let parameterMapping = new apigw.ParameterMapping()
304
+ // https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-develop-integrations-aws-services-reference.html#SQS-SendMessage
305
+ .custom("QueueUrl", integration.queue.queueUrl)
306
+ .custom("MessageBody", "$request.body")
307
+ .custom("Region", "eu-west-1"); // Change this if the SQS queue is in another region!
308
+ if (integration.messageAttributes) {
309
+ parameterMapping = parameterMapping.custom("MessageAttributes", JSON.stringify(integration.messageAttributes));
310
+ }
311
+ return new SqsRouteIntegration("SqsIntegration", {
312
+ type: apigw.HttpIntegrationType.AWS_PROXY,
313
+ subtype: apigw.HttpIntegrationSubtype.SQS_SEND_MESSAGE,
314
+ credentials: apigw.IntegrationCredentials.fromRole(role),
315
+ parameterMapping: parameterMapping,
316
+ payloadFormatVersion: apigw.PayloadFormatVersion.VERSION_1_0,
317
+ });
318
+ }
319
+ }
320
+ }
321
+ /**
322
+ * Allows a grantable target (role, user etc.) permission to invoke the API.
323
+ * Only works when using `IAM` as {@link ApiGatewayRoute.authorization}.
324
+ *
325
+ * @param target A grantable, like {@link iam.Role}
326
+ */
327
+ grantInvoke(target) {
328
+ for (const routeProps of this.props.routes) {
329
+ const authType = routeProps.authorization?.type ?? this.props.defaultAuthorization?.type;
330
+ if (authType !== "IAM") {
331
+ throw new Error(`Cannot grant invoke for an API Gateway when not using IAM auth (found auth '${authType}' on route '${routeProps.path}') [${cdk.Stack.of(this).stackName}]`);
332
+ }
333
+ }
334
+ for (const route of this.routes) {
335
+ route.grantInvoke(target);
336
+ }
337
+ }
338
+ }
339
+ /**
340
+ * Creates a custom domain for the API-Gateway, a Route53 record and an HTTPS cert.
341
+ * @author Kristian Rekstad <kre@capraconsulting.no>
342
+ */
343
+ class CustomDomain extends constructs.Construct {
344
+ apiGwCustomDomain;
345
+ /** The Fully Qualified Domain Name (FQDN) like `product.platform.example.no`. */
346
+ customDomainName;
347
+ constructor(scope, id, props) {
348
+ super(scope, id);
349
+ this.customDomainName = `${props.subdomain}.${props.hostedZone.zoneName}`;
350
+ // Can also use wildcard certs instead! Cheaper
351
+ /** Allows external users to connect with HTTPS. */
352
+ const customDomainCert = new acm.Certificate(this, "HttpsCertificate", {
353
+ domainName: this.customDomainName,
354
+ validation: acm.CertificateValidation.fromDns(props.hostedZone),
355
+ });
356
+ // Note that API-GW can also support wildcard domains! https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-custom-domain-names.html#http-wildcard-custom-domain-names
357
+ // But this will not work when AWS account X has CustomDomain `staging.platform.example.no` and account Y has CustomDomain `*.platform.example.no`.
358
+ // Not sure how sub-subdomains are affected: `myservice.staging.platform.example.no` and `*.platform.example.no`.
359
+ this.apiGwCustomDomain = new apigw.DomainName(this, "DomainName-" + props.subdomain, {
360
+ domainName: this.customDomainName,
361
+ certificate: customDomainCert,
362
+ endpointType: apigw.EndpointType.REGIONAL,
363
+ securityPolicy: apigw.SecurityPolicy.TLS_1_2,
364
+ });
365
+ // This makes the API-GW publicly available on the custom domain name.
366
+ new route53.ARecord(this, "Route53ARecordApigwAlias", {
367
+ recordName: props.subdomain,
368
+ zone: props.hostedZone,
369
+ target: route53.RecordTarget.fromAlias(new route53Targets.ApiGatewayv2DomainProperties(this.apiGwCustomDomain.regionalDomainName, this.apiGwCustomDomain.regionalHostedZoneId)),
370
+ ttl: props.ttl ?? cdk.Duration.minutes(5), // Low TTL makes it easier to do changes
371
+ });
372
+ }
373
+ }
374
+ /** Acts as glue (between the integration props and the HttpApi) when creating an SqsIntegration. */
375
+ class SqsRouteIntegration extends apigw.HttpRouteIntegration {
376
+ integrationProps;
377
+ /**
378
+ * @param id The id used in the {@link apigw.HttpIntegration} construct
379
+ * created internally by {@link apigw.HttpRouteIntegration._bindToRoute}.
380
+ * [Source code](https://github.com/aws/aws-cdk/blob/b5ae37782bc3cb637eeef9fbb1fbe2c5efdfc068/packages/%40aws-cdk/aws-apigatewayv2/lib/http/integration.ts#L321)
381
+ * @param integrationProps The props to pass to the {@link apigw.HttpIntegration} construct.
382
+ */
383
+ constructor(id, integrationProps) {
384
+ super(id);
385
+ this.integrationProps = integrationProps;
386
+ }
387
+ /**
388
+ * This sends the properties needed for creating a {@link apigw.HttpIntegration} to the
389
+ * {@link apigw.HttpRouteIntegration}.
390
+ */
391
+ bind() {
392
+ // This method is called by:
393
+ // https://github.com/aws/aws-cdk/blob/b5ae37782bc3cb637eeef9fbb1fbe2c5efdfc068/packages/%40aws-cdk/aws-apigatewayv2/lib/http/integration.ts#L319
394
+ return this.integrationProps;
395
+ }
396
+ }
397
+ /**
398
+ * Creates a custom authorizer lambda which reads `Authorization: Bearer <token>` header and
399
+ * verifies the token against a Cognito user pool.
400
+ */
401
+ class CognitoUserPoolAuthorizer extends constructs.Construct {
402
+ lambda;
403
+ /**
404
+ * Use simple response type (`{ isAuthorized: true/false }`), as opposed to returning an IAM
405
+ * Policy document.
406
+ */
407
+ responseTypes = [
408
+ authorizers.HttpLambdaResponseType.SIMPLE,
409
+ ];
410
+ constructor(scope, id, props) {
411
+ super(scope, id);
412
+ this.lambda = new lambdaNodejs.NodejsFunction(this, "AuthorizerFunction", {
413
+ entry: path.join(__dirname, "authorizer-lambdas/cognito-user-pool-authorizer.ts"),
414
+ runtime: lambda.Runtime.NODEJS_22_X,
415
+ timeout: cdk.Duration.seconds(5),
416
+ environment: {
417
+ ["USER_POOL_ID"]: props.userPool.userPoolId,
418
+ ["REQUIRED_SCOPE"]: props.requiredScope ?? "",
419
+ ["CREDENTIALS_FOR_INTERNAL_AUTHORIZATION"]: props.credentialsForInternalAuthorization
420
+ ? props.credentialsForInternalAuthorization
421
+ : "",
422
+ },
423
+ });
424
+ if (props.credentialsForInternalAuthorization) {
425
+ secretsmanager.Secret.fromSecretNameV2(scope, id + "BasicAuthSecret", props.credentialsForInternalAuthorization).grantRead(this.lambda);
426
+ }
427
+ }
428
+ }
429
+ /**
430
+ * Creates a custom authorizer lambda which reads `Authorization: Basic <base64-encoded credentials>`
431
+ * header and verifies the credentials against a given secret.
432
+ */
433
+ class BasicAuthAuthorizer extends constructs.Construct {
434
+ lambda;
435
+ // Simple is `{ isAuthorized: true/false }`, as opposed to returning an IAM Policy document
436
+ responseTypes = [
437
+ authorizers.HttpLambdaResponseType.SIMPLE,
438
+ ];
439
+ constructor(scope, id, props) {
440
+ super(scope, id);
441
+ this.lambda = new lambdaNodejs.NodejsFunction(this, "BasicAuthLambda", {
442
+ entry: path.join(__dirname, "authorizer-lambdas/basic-auth-authorizer.ts"),
443
+ description: "An authorizer for API-Gateway that checks Basic Auth credentials on requests",
444
+ runtime: lambda.Runtime.NODEJS_22_X,
445
+ environment: {
446
+ ["CREDENTIALS_SECRET_NAME"]: props.credentialsSecretName
447
+ ? props.credentialsSecretName
448
+ : "",
449
+ },
450
+ });
451
+ if (props.credentialsSecretName) {
452
+ secretsmanager.Secret.fromSecretNameV2(scope, id + "BasicAuthSecret", props.credentialsSecretName).grantRead(this.lambda);
453
+ }
454
+ }
455
+ }
456
+ /**
457
+ * Creates a custom authorizer lambda which allows both:
458
+ * - `Authorization: Bearer <token>` header, for which the token is checked against the given
459
+ * Cognito user pool
460
+ * - `Authorization: Basic <base64-encoded credentials>` header, for which the credentials are
461
+ * checked against the credentials from the given basic auth secret name
462
+ *
463
+ * If either of these are given and valid, the request is authenticated.
464
+ */
465
+ class CognitoUserPoolOrBasicAuthAuthorizer extends constructs.Construct {
466
+ lambda;
467
+ /**
468
+ * Use simple response type (`{ isAuthorized: true/false }`), as opposed to returning an IAM
469
+ * Policy document.
470
+ */
471
+ responseTypes = [
472
+ authorizers.HttpLambdaResponseType.SIMPLE,
473
+ ];
474
+ constructor(scope, id, props) {
475
+ super(scope, id);
476
+ this.lambda = new lambdaNodejs.NodejsFunction(this, "AuthorizerFunction", {
477
+ entry: path.join(__dirname, "authorizer-lambdas/cognito-user-pool-or-basic-auth-authorizer.ts"),
478
+ runtime: lambda.Runtime.NODEJS_22_X,
479
+ timeout: cdk.Duration.seconds(5),
480
+ environment: {
481
+ ["USER_POOL_ID"]: props.userPool ? props.userPool.userPoolId : "",
482
+ ["REQUIRED_SCOPE"]: props.requiredScope ?? "",
483
+ ["BASIC_AUTH_CREDENTIALS_SECRET_NAME"]: props.basicAuthCredentialsSecretName
484
+ ? props.basicAuthCredentialsSecretName
485
+ : "",
486
+ },
487
+ });
488
+ if (props.basicAuthCredentialsSecretName) {
489
+ secretsmanager.Secret.fromSecretNameV2(scope, id + "BasicAuthSecret", props.basicAuthCredentialsSecretName).grantRead(this.lambda);
490
+ }
491
+ }
492
+ }
493
+ /**
494
+ * A slightly extended version of the [default JSON format suggested by AWS](https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-logging.html#http-api-enable-logging.examples).
495
+ */
496
+ const defaultAccessLogFormat = {
497
+ requestId: "$context.requestId",
498
+ userAgent: "$context.identity.userAgent",
499
+ ip: "$context.identity.sourceIp",
500
+ /** CLF format: `dd/MMM/yyyy:HH:mm:ss +-hhmm` */
501
+ requestTime: "$context.requestTime",
502
+ requestTimeEpoch: "$context.requestTimeEpoch",
503
+ dataProcessed: "$context.dataProcessed",
504
+ httpMethod: "$context.httpMethod",
505
+ path: "$context.path",
506
+ routeKey: "$context.routeKey",
507
+ status: "$context.status",
508
+ protocol: "$context.protocol",
509
+ responseLength: "$context.responseLength",
510
+ responseLatency: "$context.responseLatency",
511
+ domainName: "$context.domainName",
512
+ // hostHeaderOverride: "$context.requestOverride.header.Host", //Mapping template overrides cannot be used with proxy integration endpoints, which lack data mappings https://docs.aws.amazon.com/apigateway/latest/developerguide/apigateway-override-request-response-parameters.html#:~:text=Mapping%20template%20overrides%20cannot%20be%20used%20with%20proxy%20integration%20endpoints%2C%20which%20lack%20data%20mappings
513
+ error: {
514
+ type: "$context.error.responseType",
515
+ gatewayError: "$context.error.message",
516
+ integrationError: "$context.integration.error",
517
+ authorizerError: "$context.authorizer.error",
518
+ },
519
+ integration: {
520
+ latency: "$context.integration.latency",
521
+ requestId: "$context.integration.requestId",
522
+ responseStatus: "$context.integration.status",
523
+ },
524
+ auth: {
525
+ iam: {
526
+ userArn: "$context.identity.userArn",
527
+ awsAccount: "$context.identity.accountId",
528
+ awsPrincipal: "$context.identity.caller",
529
+ awsPrincipalOrg: "$context.identity.principalOrgId",
530
+ },
531
+ basic: { user: "$context.authorizer.user" },
532
+ },
533
+ awsEndpointRequest: {
534
+ id: "$context.awsEndpointRequestId",
535
+ id2: "$context.awsEndpointRequestId2",
536
+ },
537
+ // For datadog
538
+ message: "$context.identity.sourceIp - $context.httpMethod $context.domainName $context.path ($context.routeKey) - $context.status [$context.responseLatency ms]",
539
+ };
540
+ /**
541
+ * Enables access logs on the API-Gateway.
542
+ *
543
+ * @author Kristian Rekstad <kre@capraconsulting.no>
544
+ */
545
+ class ApiGatewayAccessLogs extends constructs.Construct {
546
+ logGroup;
547
+ constructor(scope, id, stage, props, defaultAccessLogFormat) {
548
+ super(scope, id);
549
+ // logGroup is set up with help from: https://github.com/aws/aws-cdk/issues/11100#issuecomment-904627081
550
+ // Not sure if HTTP API actually needs the service role with managed policy: https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-logging.html
551
+ const accessLogs = new logs.LogGroup(this, "AccessLogGroup", {
552
+ retention: props?.retention,
553
+ removalPolicy: props?.removalPolicy ?? cdk.RemovalPolicy.RETAIN,
554
+ // Always use the default encryption key. Otherwise, the key needs special policies:
555
+ // https://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/encrypt-log-data-kms.html#cmk-permissions
556
+ encryptionKey: undefined,
557
+ });
558
+ this.logGroup = accessLogs;
559
+ stage.accessLogSettings = {
560
+ destinationArn: accessLogs.logGroupArn,
561
+ format: JSON.stringify(props?.accessLogFormat ?? defaultAccessLogFormat),
562
+ };
563
+ const apiGwLogsRole = new iam.Role(this, "ApiGatewayPushToCloudwatchLogsRole", {
564
+ assumedBy: new iam.ServicePrincipal("apigateway.amazonaws.com"),
565
+ managedPolicies: [
566
+ iam.ManagedPolicy.fromAwsManagedPolicyName("service-role/AmazonAPIGatewayPushToCloudWatchLogs"),
567
+ ],
568
+ });
569
+ accessLogs.grantWrite(apiGwLogsRole);
570
+ }
571
+ }
572
+ /** Returns a short semi-unique hash of the given string. */
573
+ function shortHash(str) {
574
+ // SHA-1 is no-no when we need cryptographic security, but here we just it for shortening a name,
575
+ // which is fine
576
+ return createHash("sha1").update(str).digest("hex").substring(0, 10);
577
+ }
578
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaHR0cC1hcGktZ2F0ZXdheS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9hcGktZ2F0ZXdheS9odHRwLWFwaS1nYXRld2F5LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sS0FBSyxVQUFVLE1BQU0sWUFBWSxDQUFBO0FBQ3hDLE9BQU8sS0FBSyxHQUFHLE1BQU0sYUFBYSxDQUFBO0FBR2xDLE9BQU8sS0FBSyxNQUFNLE1BQU0sd0JBQXdCLENBQUE7QUFFaEQsT0FBTyxLQUFLLEtBQUssTUFBTSw4QkFBOEIsQ0FBQTtBQUNyRCxPQUFPLEtBQUssSUFBSSxNQUFNLHNCQUFzQixDQUFBO0FBQzVDLE9BQU8sS0FBSyxHQUFHLE1BQU0scUJBQXFCLENBQUE7QUFDMUMsT0FBTyxLQUFLLGNBQWMsTUFBTSxnQ0FBZ0MsQ0FBQTtBQUNoRSxPQUFPLEtBQUssWUFBWSxNQUFNLDJDQUEyQyxDQUFBO0FBQ3pFLE9BQU8sS0FBSyxXQUFXLE1BQU0sMENBQTBDLENBQUE7QUFFdkUsT0FBTyxLQUFLLFlBQVksTUFBTSwrQkFBK0IsQ0FBQTtBQUM3RCxPQUFPLEVBQUUsVUFBVSxFQUFFLE1BQU0sUUFBUSxDQUFBO0FBQ25DLE9BQU8sS0FBSyxPQUFPLE1BQU0seUJBQXlCLENBQUE7QUFDbEQsT0FBTyxLQUFLLGNBQWMsTUFBTSxpQ0FBaUMsQ0FBQTtBQUNqRSxPQUFPLEtBQUssR0FBRyxNQUFNLG9DQUFvQyxDQUFBO0FBQ3pELE9BQU8sRUFBRSxZQUFZLEVBQUUsTUFBTSxTQUFTLENBQUE7QUFDdEMsT0FBTyxLQUFLLElBQUksTUFBTSxNQUFNLENBQUE7QUFDNUIsT0FBTyxFQUFFLGFBQWEsRUFBRSxNQUFNLEtBQUssQ0FBQTtBQUVuQyxNQUFNLFVBQVUsR0FBRyxhQUFhLENBQUMsTUFBTSxDQUFDLElBQUksQ0FBQyxHQUFHLENBQUMsQ0FBQTtBQUNqRCxNQUFNLFNBQVMsR0FBRyxJQUFJLENBQUMsT0FBTyxDQUFDLFVBQVUsQ0FB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LEVBQUUsYUFBYSxJQUFJLEdBQUcsQ0FBQyxhQUFhLENBQUMsTUFBTTtZQUMvRCxvRkFBb0Y7WUFDcEYscUdBQXFHO1lBQ3JHLGFBQWEsRUFBRSxTQUFTO1NBQ3pCLENBQUMsQ0FBQTtRQUNGLElBQUksQ0FBQyxRQUFRLEdBQUcsVUFBVSxDQUFBO1FBRTFCLEtBQUssQ0FBQyxpQkFBaUIsR0FBRztZQUN4QixjQUFjLEVBQUUsVUFBVSxDQUFDLFdBQVc7WUFDdEMsTUFBTSxFQUFFLElBQUksQ0FBQyxTQUFTLENBQUMsS0FBSyxFQUFFLGVBQWUsSUFBSSxzQkFBc0IsQ0FBQztTQUN6RSxDQUFBO1FBRUQsTUFBTSxhQUFhLEdBQUcsSUFBSSxHQUFHLENBQUMsSUFBSSxDQUNoQyxJQUFJLEVBQ0osb0NBQW9DLEVBQ3BDO1lBQ0UsU0FBUyxFQUFFLElBQUksR0FBRyxDQUFDLGdCQUFnQixDQUFDLDBCQUEwQixDQUFDO1lBQy9ELGVBQWUsRUFBRTtnQkFDZixHQUFHLENBQUMsYUFBYSxDQUFDLHdCQUF3QixDQUN4QyxtREFBbUQsQ0FDcEQ7YUFDRjtTQUNGLENBQ0YsQ0FBQTtRQUVELFVBQVUsQ0FBQyxVQUFVLENBQUMsYUFBYSxDQUFDLENBQUE7SUFDdEMsQ0FBQztDQUNGO0FBRUQsNERBQTREO0FBQzVELFNBQVMsU0FBUyxDQUFDLEdBQVc7SUFDNUIsaUdBQWlHO0lBQ2pHLGdCQUFnQjtJQUNoQixPQUFPLFVBQVUsQ0FBQyxNQUFNLENBQUMsQ0FBQyxNQUFNLENBQUMsR0FBRyxDQUFDLENBQUMsTUFBTSxDQUFDLEtBQUssQ0FBQyxDQUFDLFNBQVMsQ0FBQyxDQUFDLEVBQUUsRUFBRSxDQUFDLENBQUE7QUFDdEUsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCAqIGFzIGNvbnN0cnVjdHMgZnJvbSBcImNvbnN0cnVjdHNcIlxuaW1wb3J0ICogYXMgY2RrIGZyb20gXCJhd3MtY2RrLWxpYlwiXG5pbXBvcnQgdHlwZSAqIGFzIGVsYiBmcm9tIFwiYXdzLWNkay1saWIvYXdzLWVsYXN0aWNsb2FkYmFsYW5jaW5ndjJcIlxuaW1wb3J0IHR5cGUgKiBhcyBlYzIgZnJvbSBcImF3cy1jZGstbGliL2F3cy1lYzJcIlxuaW1wb3J0ICogYXMgbGFtYmRhIGZyb20gXCJhd3MtY2RrLWxpYi9hd3MtbGFtYmRhXCJcbmltcG9ydCB0eXBlICogYXMgc3FzIGZyb20gXCJhd3MtY2RrLWxpYi9hd3Mtc3FzXCJcbmltcG9ydCAqIGFzIGFwaWd3IGZyb20gXCJhd3MtY2RrLWxpYi9hd3MtYXBpZ2F0ZXdheXYyXCJcbmltcG9ydCAqIGFzIGxvZ3MgZnJvbSBcImF3cy1jZGstbGliL2F3cy1sb2dzXCJcbmltcG9ydCAqIGFzIGlhbSBmcm9tIFwiYXdzLWNkay1saWIvYXdzLWlhbVwiXG5pbXBvcnQgKiBhcyBzZWNyZXRzbWFuYWdlciBmcm9tIFwiYXdzLWNkay1saWIvYXdzLXNlY3JldHNtYW5hZ2VyXCJcbmltcG9ydCAqIGFzIGludGVncmF0aW9ucyBmcm9tIFwiYXdzLWNkay1saWIvYXdzLWFwaWdhdGV3YXl2Mi1pbnRlZ3JhdGlvbnNcIlxuaW1wb3J0ICogYXMgYXV0aG9yaXplcnMgZnJvbSBcImF3cy1jZGstbGliL2F3cy1hcGlnYXRld2F5djItYXV0aG9yaXplcnNcIlxuaW1wb3J0IHR5cGUgeyBJVXNlclBvb2wgfSBmcm9tIFwiYXdzLWNkay1saWIvYXdzLWNvZ25pdG9cIlxuaW1wb3J0ICogYXMgbGFtYmRhTm9kZWpzIGZyb20gXCJhd3MtY2RrLWxpYi9hd3MtbGFtYmRhLW5vZGVqc1wiXG5pbXBvcnQgeyBjcmVhdGVIYXNoIH0gZnJvbSBcImNyeXB0b1wiXG5pbXBvcnQgKiBhcyByb3V0ZTUzIGZyb20gXCJhd3MtY2RrLWxpYi9hd3Mtcm91dGU1M1wiXG5pbXBvcnQgKiBhcyByb3V0ZTUzVGFyZ2V0cyBmcm9tIFwiYXdzLWNkay1saWIvYXdzLXJvdXRlNTMtdGFyZ2V0c1wiXG5pbXBvcnQgKiBhcyBhY20gZnJvbSBcImF3cy1jZGstbGliL2F3cy1jZXJ0aWZpY2F0ZW1hbmFnZXJcIlxuaW1wb3J0IHsgdGFnUmVzb3VyY2VzIH0gZnJvbSBcIi4uL3RhZ3NcIlxuaW1wb3J0ICogYXMgcGF0aCBmcm9tIFwicGF0aFwiXG5pbXBvcnQgeyBmaWxlVVJMVG9QYXRoIH0gZnJvbSBcInVybFwiXG5cbmNvbnN0IF9fZmlsZW5hbWUgPSBmaWxlVVJMVG9QYXRoKGltcG9ydC5tZXRhLnVybClcbmNvbnN0IF9fZGlybmFtZSA9IHBhdGguZGlybmFtZShfX2ZpbGVuYW1lKVxuXG4vKipcbiAqIFByb3BzIGZvciB0aGUge0BsaW5rIEFwaUdhdGV3YXl9IGNvbnN0cnVjdC5cbiAqXG4gKiBAYXV0aG9yIEtyaXN0aWFuIFJla3N0YWQgPGtyZUBjYXByYWNvbnN1bHRpbmcubm8+XG4gKiBAYXV0aG9yIEhlcm1hbm4gTcO4cmtyaWQgPGhlbUBsaWZsaWcubm8+XG4gKi9cbmV4cG9ydCB0eXBlIEFwaUdhdGV3YXlQcm9wczxBdXRoU2NvcGVzVCBleHRlbmRzIHN0cmluZyA9IHN0cmluZz4gPSB7XG4gIC8qKiBTZXR0aW5ncyBmb3IgdGhlIGV4dGVybmFsLWZhY2luZyBwYXJ0IG9mIHRoZSBBUEktR1cuICovXG4gIGRuczogQXBpR2F0ZXdheURuc1Byb3BzXG5cbiAgLyoqXG4gICAqIElmIG5vIGludGVncmF0aW9uIGlzIHNwZWNpZmllZCBmb3IgYSByb3V0ZSwgdGhpcyBpbnRlZ3JhdGlvbiBpcyB1c2VkLlxuICAgKlxuICAgKiBTZWUge0BsaW5rIEludGVncmF0aW9uUHJvcHN9IGZvciB0aGUgYXZhaWxhYmxlIG9wdGlvbnMuXG4gICAqL1xuICBkZWZhdWx0SW50ZWdyYXRpb24/OiBJbnRlZ3JhdGlvblByb3BzXG5cbiAgLyoqXG4gICAqIElmIG5vIGF1dGhvcml6YXRpb24gaXMgc3BlY2lmaWVkIGZvciBhIHJvdXRlLCB0aGlzIGF1dGhvcml6YXRpb24gaXMgdXNlZC5cbiAgICpcbiAgICogU2VlIHtAbGluayBBdXRob3JpemF0aW9uUHJvcHN9IGZvciB0aGUgYXZhaWxhYmxlIG9wdGlvbnMuXG4gICAqL1xuICBkZWZhdWx0QXV0aG9yaXphdGlvbj86IEF1dGhvcml6YXRpb25Qcm9wczxBdXRoU2NvcGVzVD5cblxuICByb3V0ZXM6IEFwaUdhdGV3YXlSb3V0ZTxBdXRoU2NvcGVzVD5bXVxuXG4gIC8qKlxuICAgKiBUaGUgQVBJLUdXIGFjY2VzcyBsb2dzIGZvciB0aGUgYCRkZWZhdWx0YCBzdGFnZSBhcmUga2VwdC5cbiAgICogVGhpcyBoYXMgb3B0aW9ucyBmb3IgdGhlIGxvZ3MuXG4gICAqL1xuICBhY2Nlc3NMb2dzPzogQXBpR2F0ZXdheUFjY2Vzc0xvZ3NQcm9wc1xuXG4gIC8qKlxuICAgKiBTZXQgdG8gZmFsc2UgdG8gZGlzYWJsZSByb3V0ZS1sZXZlbCBtZXRyaWNzLlxuICAgKiBUaGlzIGNhbiBpbmNyZWFzZSBDbG91ZFdhdGNoIGNvc3RzIHdoZW4gbm90IGRpc2FibGVkLlxuICAgKlxuICAgKiBTZWUgW0FXUzogV29ya2luZyB3aXRoIG1ldHJpY3MgZm9yIEhUVFAgQVBJc10oaHR0cHM6Ly9kb2NzLmF3cy5hbWF6b24uY29tL2FwaWdhdGV3YXkvbGF0ZXN0L2RldmVsb3Blcmd1aWRlL2h0dHAtYXBpLW1ldHJpY3MuaHRtbD9pY21waWQ9YXBpZ2F0ZXdheV9jb25zb2xlX2hlbHAjOn46dGV4dD1hbmQlMjBzdGFnZSUyMElELi0sQXBpSWQlMkMlMjBTdGFnZSUyQyUyMFJvdXRlLC1GaWx0ZXJzJTIwQVBJJTIwR2F0ZXdheSlcbiAgICogZm9yIG1vcmUgaW5mby5cbiAgICpcbiAgICogQGRlZmF1bHQgdHJ1ZVxuICAgKi9cbiAgZGV0YWlsZWRNZXRyaWNzPzogYm9vbGVhblxuXG4gIC8qKlxuICAgKiBUaHJvdHRsaW5nIG9mIHJlcXVlc3RzLlxuICAgKiBJZiBub3Qgc2V0LCB0aGUgQVdTIGRlZmF1bHQgb2YgNTAwMCBidXJzdCBhbmQgMTAwMDAgcmF0ZSBpcyB1c2VkLlxuICAgKlxuICAgKiBUaGUgZGVmYXVsdCB0aHJvdHRsaW5nIGlzIHBlci1hY2NvdW50LCBhbmQgY291bnRzIGFsbCBBUElzIGluIHRoZSBhY2NvdW50IGFuZCByZWdpb24uXG4gICAqIElmIHlvdSBoYXZlIGFub3RoZXIgQVBJIGluIHRoaXMgcmVnaW9uIGdldHRpbmcgMTBfMDAwIHJlcXVlc3QgcmF0ZSwgaXQgbWF5IGltcGFjdCB0aGlzIEFQSSBhcyB3ZWxsLlxuICAgKi9cbiAgdGhyb3R0bGluZz86IHtcbiAgICAvKipcbiAgICAgKiBHb2luZyBvdmVyIGA1XzAwMGAgbWF5IHJlcXVpcmUgeW91IHRvIGNvbnRhY3QgQVdTIFN1cHBvcnQgLSB0aGV5IGFyZSBhY2NvdW50IGJvdW5kLlxuICAgICAqL1xuICAgIGJ1cnN0PzogbnVtYmVyXG4gICAgLyoqXG4gICAgICogR29pbmcgb3ZlciBgMTBfMDAwYCBtYXkgcmVxdWlyZSB5b3UgdG8gY29udGFjdCBBV1MgU3VwcG9ydCAtIHRoZXkgYXJlIGFjY291bnQgYm91bmQuXG4gICAgICovXG4gICAgcmF0ZT86IG51bWJlclxuICB9XG5cbiAgLyoqXG4gICAqIFNldHMgQ09SUyBoZWFkZXJzIG9uIHJlc3BvbnNlcyBmcm9tIHRoZSBBUEkgR2F0ZXdheSB0byBhbGxvdyBhbGwgb3JpZ2lucywgaGVhZGVycyBhbmQgbWV0aG9kcy5cbiAgICogVXNlZnVsIGlmIHlvdXIgQVBJIHNob3VsZCBiZSBhY2Nlc3NlZCBieSBhbnkgYnJvd3Nlci5cbiAgICovXG4gIGNvcnNBbGxvd0FsbD86IGJvb2xlYW5cblxuICAvKipcbiAgICogSWYgc29tZSBzZXR0aW5ncyBpbiB0aGlzIGNvbnN0cnVjdCBkbyBub3Qgd29yayBmb3IgeW91LCB0aGlzIGlzIGFuIGVzY2FwZSBoYXRjaCBtZWNoYW5pc20gdG9cbiAgICogb3ZlcnJpZGUgYW55dGhpbmcuXG4gICAqL1xuICBwcm9wc092ZXJyaWRlPzoge1xuICAgIC8qKlxuICAgICAqIE92ZXJyaWRlIHNldHRpbmdzIGZvciB0aGUge0BsaW5rIGFwaWd3Lkh0dHBBcGl9IChhY2Nlc3NpYmxlIGluIHtAbGluayBBcGlHYXRld2F5Lmh0dHBBcGl9KS5cbiAgICAgKlxuICAgICAqIEZvciBleGFtcGxlLCBpZiB5b3UgaGF2ZSBhIGZyb250ZW5kIGFjY2Vzc2luZyB0aGlzIEFQSSwgeW91IG1pZ2h0IHdhbnQgdG8gc2V0XG4gICAgICogW0NPUlMgcHJlZmxpZ2h0XShodHRwczovL2RvY3MuYXdzLmFtYXpvbi5jb20vY2RrL2FwaS92MS9kb2NzL2F3cy1hcGlnYXRld2F5djItcmVhZG1lLmh0bWwjY3Jvc3Mtb3JpZ2luLXJlc291cmNlLXNoYXJpbmctY29ycylcbiAgICAgKiBzZXR0aW5ncy5cbiAgICAgKi9cbiAgICBodHRwQXBpPzogUGFydGlhbDxhcGlndy5IdHRwQXBpUHJvcHM+XG4gIH1cbn1cblxuZXhwb3J0IHR5cGUgQXBpR2F0ZXdheURuc1Byb3BzID0ge1xuICAvKipcbiAgICogT25seSB0aGUgc3ViZG9tYWluIHByZWZpeCwgd2hpY2ggc2hvdWxkIGJlIHRoZSBuYW1lIG9mIHRoZSBzZXJ2aWNlLlxuICAgKiBFeGFtcGxlOiBTdWJkb21haW4gYHByb2R1Y3RgIHdvdWxkIGdpdmUgdGhlIGVuZCByZXN1bHQgb2YgYW4gQVBJLUdXIHdpdGhcbiAgICogYHByb2R1Y3QucGxhdGZvcm0uZXhhbXBsZS5ub2AuXG4gICAqL1xuICBzdWJkb21haW46IHN0cmluZ1xuXG4gIC8qKlxuICAgKiBIb3N0ZWQgWm9uZSBmb3IgdGhlIGV4dGVybmFsIGZhY2luZyBkb21haW4uXG4gICAqIFRoaXMgaXMgd2hlcmUgcm91dGVzIHdpbGwgYmUgY3JlYXRlZCwgdG8gcmVkaXJlY3QgY29uc3VtZXJzIHRvIHRoZSBBUEktR1cuXG4gICAqIEZvciBleGFtcGxlIGEgSFogZm9yIGBwbGF0Zm9ybS5leGFtcGxlLm5vYC5cbiAgICovXG4gIGhvc3RlZFpvbmU6IHJvdXRlNTMuSUhvc3RlZFpvbmVcblxuICAvKipcbiAgICogVGhlIFRpbWUgVG8gTGl2ZSAoVFRMKSBmb3IgdGhlIHB1YmxpYyBETlMgQSByZWNvcmQgdGhhdCB3aWxsIGV4cG9zZSB0aGUgQVBJLUdXLlxuICAgKiBUaGlzIGlzIGhvdyBsb25nIEROUyBzZXJ2ZXJzIHdpbGwgY2FjaGUgdGhlIHJlY29yZC5cbiAgICpcbiAgICogQSBsb25nIFRUTCAoaG91cnMpIGlzIGJlbmVmaWNpYWwgdG8gRE5TIHNlcnZlcnMsIGJ1dCBtYWtlcyBkZXZlbG9wZXJzICh5b3UpIHdhaXQgbG9uZ2VyIHdoZW5cbiAgICogZG9pbmcgY2hhbmdlcy5cbiAgICpcbiAgICogQGRlZmF1bHQgNSBtaW51dGVzXG4gICAqL1xuICB0dGw/OiBjZGsuRHVyYXRpb25cbn1cblxuZXhwb3J0IHR5cGUgQXBpR2F0ZXdheVJvdXRlPEF1dGhTY29wZXNUIGV4dGVuZHMgc3RyaW5nID0gc3RyaW5nPiA9IHtcbiAgLyoqIFRoZSBwYXRoIG9mIHRoZSByb3V0ZSB0byBleHBvc2UgdGhyb3VnaCB0aGUgQVBJIEdhdGV3YXkuIFVzZSBcIi9cIiBmb3IgdGhlIHJvb3Qgcm91dGUuICovXG4gIHBhdGg6IHN0cmluZ1xuXG4gIC8qKlxuICAgKiBCeSBkZWZhdWx0LCB3ZSBvbmx5IGZvcndhcmQgcmVxdWVzdHMgdGhhdCBtYXRjaCB0aGUgcm91dGUncyBwYXRoIGV4YWN0bHkuIFNvIGZvciBhIHJvdXRlIHdpdGhcbiAgICogcGF0aCBgL2FwaS91c2Vyc2AsIGEgcmVxdWVzdCB0byBgL2FwaS91c2Vyc2Agd2lsbCBiZSBmb3J3YXJkZWQsIGJ1dCBhIHJlcXVlc3QgdG9cbiAgICogYC9hcGkvdXNlcnMvYWRtaW5gIHdpbGwgbm90LiBJZiB5b3Ugd2FudCB0byBmb3J3YXJkIHJlcXVlc3RzIHRvIGFsbCBzdWItcGF0aHMgdW5kZXIgdGhlIHJvdXRlJ3NcbiAgICogcGF0aCwgeW91IGNhbiBzZXQgdGhpcyB0byB0cnVlLlxuICAgKlxuICAgKiBAZGVmYXVsdCBmYWxzZVxuICAgKi9cbiAgaW5jbHVkZVN1YnBhdGhzPzogYm9vbGVhblxuXG4gIC8qKlxuICAgKiBUaGUgSFRUUCBtZXRob2QgdG8gZXhwb3NlLiBgQU5ZYCBleHBvc2VzIGFsbCBIVFRQIG1ldGhvZHMgb24gdGhlIHBhdGguXG4gICAqXG4gICAqIEBkZWZhdWx0IFwiQU5ZXCJcbiAgICovXG4gIG1ldGhvZD86IEh0dHBNZXRob2RcblxuICAvKipcbiAgICogVGhlIGludGVncmF0aW9uIHRoYXQgdGhlIHJvdXRlIHdpbGwgZm9yd2FyZCB0by4gU2VlIHtAbGluayBJbnRlZ3JhdGlvblByb3BzfSBmb3IgdGhlIGF2YWlsYWJsZVxuICAgKiBvcHRpb25zLlxuICAgKlxuICAgKiBJZiB1bmRlZmluZWQsIHVzZXMgdGhlIHtAbGluayBBcGlHYXRld2F5UHJvcHMuZGVmYXVsdEludGVncmF0aW9ufS5cbiAgICovXG4gIGludGVncmF0aW9uPzogSW50ZWdyYXRpb25Qcm9wc1xuXG4gIC8qKlxuICAgKiBIb3cgcmVxdWVzdHMgb24gdGhlIHJvdXRlIGFyZSBhdXRoZW50aWNhdGVkLiBTZWUge0BsaW5rIEF1dGhvcml6YXRpb25Qcm9wc30gZm9yIHRoZSBhdmFpbGFibGVcbiAgICogb3B0aW9ucy5cbiAgICpcbiAgICogSWYgdW5kZWZpbmVkLCB1c2VzIHRoZSB7QGxpbmsgQXBpR2F0ZXdheVByb3BzLmRlZmF1bHRBdXRob3JpemF0aW9ufS5cbiAgICovXG4gIGF1dGhvcml6YXRpb24/OiBBdXRob3JpemF0aW9uUHJvcHM8QXV0aFNjb3Blc1Q+XG59XG5cbmV4cG9ydCB0eXBlIEh0dHBNZXRob2QgPVxuICB8IFwiQU5ZXCJcbiAgfCBcIkdFVFwiXG4gIHwgXCJQT1NUXCJcbiAgfCBcIlBVVFwiXG4gIHwgXCJQQVRDSFwiXG4gIHwgXCJERUxFVEVcIlxuICB8IFwiT1BUSU9OU1wiXG4gIHwgXCJIRUFEXCJcblxuZXhwb3J0IHR5cGUgSW50ZWdyYXRpb25Qcm9wcyA9XG4gIC8qKiBVc2UgdGhpcyB3aGVuIGNvbm5lY3RpbmcgdGhlIHJvdXRlIHRvIGFuIEFMQiAoQXBwbGljYXRpb24gTG9hZCBCYWxhbmNlcikuICovXG4gIHwgKHsgdHlwZTogXCJBTEJcIiB9ICYgQWxiSW50ZWdyYXRpb25Qcm9wcylcbiAgLyoqIFVzZSB0aGlzIHdoZW4gY29ubmVjdGluZyByb3V0ZSB0byBhIExhbWJkYS4gKi9cbiAgfCAoeyB0eXBlOiBcIkxhbWJkYVwiIH0gJiBMYW1iZGFJbnRlZ3JhdGlvblByb3BzKVxuICAvKiogVXNlIHRoaXMgd2hlbiBjb25uZWN0aW5nIGEgcm91dGUgdG8gc2VuZCB0byBhbiBTUVMgcXVldWUuICovXG4gIHwgKHsgdHlwZTogXCJTUVNcIiB9ICYgU3FzSW50ZWdyYXRpb25Qcm9wcylcblxuLyoqXG4gKiBQcm9wcyBmb3IgdGhlIEFQSS1HVyAtPiBBTEIgKEFwcGxpY2F0aW9uIExvYWQgQmFsYW5jZXIpIGludGVncmF0aW9uLlxuICpcbiAqIFNlZSB0aGUgbm90ZSBvbiB7QGxpbmsgQXBpR2F0ZXdheX0gYWJvdXQgdGhlIGxvYWQgYmFsYW5jZXIgc2VjdXJpdHkgZ3JvdXAuXG4gKi9cbmV4cG9ydCB0eXBlIEFsYkludGVncmF0aW9uUHJvcHMgPSB7XG4gIC8qKlxuICAgKiBBIGxpc3RlbmVyIG9uIGUuZy4gcG9ydCA0NDMgKEhUVFBTKS5cbiAgICpcbiAgICogU2VlIHRoZSBub3RlIG9uIHtAbGluayBBcGlHYXRld2F5fSBhYm91dCB0aGUgbG9hZCBiYWxhbmNlciBzZWN1cml0eSBncm91cC5cbiAgICovXG4gIGxvYWRCYWxhbmNlckxpc3RlbmVyOiBlbGIuSUFwcGxpY2F0aW9uTGlzdGVuZXJcblxuICAvKipcbiAgICogVGhlIGhvc3QgbmFtZSAoZG9tYWluIG5hbWUpIG9mIHRoZSBiYWNrZW5kIHNlcnZpY2UgdGhhdCB3ZSB3YW50IHRvIHJlYWNoIHRocm91Z2ggdGhlIEFMQi5cbiAgICpcbiAgICogVGhpcyBpcyB1c2VkIHRvOlxuICAgKiAtIFZlcmlmeSB0aGUgSFRUUFMgY2VydGlmaWNhdGUgb2YgdGhlIGJhY2tlbmQgc2VydmljZSwgc28gdGhhdCB0aGUgcmVxdWVzdCBmb3J3YXJkZWQgZnJvbVxuICAgKiAgIEFQSS1HVyBjYW4gdXNlIFRMU1xuICAgKiAtIFNldCB0aGUgYEhvc3RgIGhlYWRlciBvbiB0aGUgcmVxdWVzdCB3aGVuIGZvcndhcmRpbmcgdG8gdGhlIEFMQiwgc28gdGhhdCByZXF1ZXN0cyBjYW4gYmVcbiAgICogICByb3V0ZWQgdG8gdGhlIGNvcnJlY3QgVGFyZ2V0IEdyb3VwXG4gICAqXG4gICAqIEV4YW1wbGUgdmFsdWU6IGA8c2VydmljZT4uc3RhZ2luZy5teS1wcm9qZWN0LmxpZmxpZy5pb2AgKG5vdCBwcmVmaXhlZCBieSBgaHR0cHM6Ly9gKS5cbiAgICovXG4gIGhvc3ROYW1lOiBzdHJpbmdcblxuICAvKipcbiAgICogVGhlIFZQQyB1c2VkIGJ5IHRoZSBBTEIuIFRoZSBBUEktR1cgaW50ZWdyYXRpb24gd2lsbCBjb25uZWN0IHRvIHRoZSBBTEIgdXNpbmcgYSBWUEMgTGluayBmb3JcbiAgICogdGhpcyBWUEMuXG4gICAqXG4gICAqIFNlZSB0aGUgbm90ZSBvbiB7QGxpbmsgQXBpR2F0ZXdheX0gYWJvdXQgdGhlIGxvYWQgYmFsYW5jZXIgc2VjdXJpdHkgZ3JvdXAuXG4gICAqL1xuICB2cGM6IGVjMi5JVnBjXG5cbiAgLyoqXG4gICAqIEEgc2VjdXJpdHkgZ3JvdXAgKFNHKSB0aGF0IGFsbG93cyBpbmNvbWluZyB0cmFmZmljIHRvIHRoZSBBTEIuIFdpbGwgYmUgdXNlZCBieSB0aGUgVlBDIGxpbmssIHNvXG4gICAqIHRoZSBBUEktR1cgaW50ZWdyYXRpb24gY2FuIGNvbm5lY3QuXG4gICAqXG4gICAqIFRoaXMgaXMgdXN1YWxseSB0aGUgc2FtZSBTRyBhcyB0aGUgQUxCIHVzZXMsIGJlY2F1c2UgdGhleSBoYXZlIGEgcnVsZSB0aGF0IGFsbG93cyB0cmFmZmljIGZyb21cbiAgICogb3RoZXJzIGluIHRoZSBzYW1lIFNHLlxuICAgKlxuICAgKiBTZWUgdGhlIG5vdGUgb24ge0BsaW5rIEFwaUdhdGV3YXl9IGFib3V0IHRoZSBsb2FkIGJhbGFuY2VyIHNlY3VyaXR5IGdyb3VwLlxuICAgKi9cbiAgc2VjdXJpdHlHcm91cDogZWMyLklTZWN1cml0eUdyb3VwXG5cbiAgLyoqXG4gICAqIE1hcCByZXF1ZXN0IHBhcmFtZXRlcnMgKGFkZC9vdmVyd3JpdGUgcGF0aC9oZWFkZXJzL3F1ZXJ5IHBhcmFtcykgYmVmb3JlIGZvcndhcmRpbmcgdG8gdGhlXG4gICAqIGJhY2tlbmQuXG4gICAqXG4gICAqIFNlZSB7QGxpbmsgaHR0cHM6Ly9kb2NzLmF3cy5hbWF6b24uY29tL2FwaWdhdGV3YXkvbGF0ZXN0L2RldmVsb3Blcmd1aWRlL2h0dHAtYXBpLXBhcmFtZXRlci1tYXBwaW5nLmh0bWx9XG4gICAqIGZvciBtb3JlIG9uIHRoaXMuIFJlYWQgdGhlICdSZXNlcnZlZCBoZWFkZXJzJyBzZWN0aW9uIGZvciB3aGljaCBoZWFkZXJzIGNhbm5vdCBiZSBvdmVycmlkZGVuLlxuICAgKiBJbiBhZGRpdGlvbiB0byB0aGUgQVdTLXJlc2VydmVkIGhlYWRlcnMsIHlvdSBzaG91bGQgbm90IG92ZXJyaWRlIHRoZSAnSG9zdCcgaGVhZGVyIGVpdGhlciwgYXNcbiAgICogdGhhdCdzIHVzZWQgZm9yIHJvdXRpbmcgdGhlIHJlcXVlc3QgdG8gdGhlIGNvcnJlY3Qgc2VydmljZSBiZWhpbmQgdGhlIGxvYWQgYmFsYW5jZXIuXG4gICAqXG4gICAqICMjIyBFeGFtcGxlOlxuICAgKlxuICAgKiBBZGRpbmcgYSBoZWFkZXI6XG4gICAqIGBgYFxuICAgKiBtYXBQYXJhbWV0ZXJzOiAocGFyYW1ldGVycykgPT4gcGFyYW1ldGVycy5vdmVyd3JpdGVIZWFkZXIoXG4gICAqICAgIFwiWC1NeS1DdXN0b20tSGVhZGVyXCIsXG4gICAqICAgIGFwaWd3Lk1hcHBpbmdWYWx1ZS5jdXN0b20oXCJteS1jdXN0b20tdmFsdWVcIiksXG4gICAqIClcbiAgICogYGBgXG4gICAqXG4gICAqIE92ZXJ3cml0aW5nIHRoZSBwYXRoIChpZiwgZm9yIGV4YW1wbGUsIHlvdSBjb25maWd1cmUgYSBgL3VzZXJzYCByb3V0ZSBvbiB0aGUgQVBJIEdhdGV3YXkgdGhhdFxuICAgKiB5b3Ugd2FudCB0byBmb3J3YXJkIHRvIGAvYXBpL3VzZXJzYCBvbiB0aGUgYmFja2VuZCk6XG4gICAqIGBgYFxuICAgKiBtYXBQYXJhbWV0ZXJzOiAocGFyYW1ldGVycykgPT4gcGFyYW1ldGVycy5vdmVyd3JpdGVQYXRoKFwiL2FwaS91c2Vyc1wiKVxuICAgKiBgYGBcbiAgICovXG4gIG1hcFBhcmFtZXRlcnM/OiAocGFyYW1ldGVyczogYXBpZ3cuUGFyYW1ldGVyTWFwcGluZykgPT4gdm9pZFxufVxuXG5leHBvcnQgdHlwZSBMYW1iZGFJbnRlZ3JhdGlvblByb3BzID0ge1xuICAvKipcbiAgICogVGhlIExhbWJkYSBpbnRlZ3JhdGlvbiB1c2VzIHRoZSBWMiBwYXlsb2FkIGZvcm1hdDpcbiAgICoge0BsaW5rIGh0dHBzOi8vZG9jcy5hd3MuYW1hem9uLmNvbS9hcGlnYXRld2F5L2xhdGVzdC9kZXZlbG9wZXJndWlkZS9odHRwLWFwaS1kZXZlbG9wLWludGVncmF0aW9ucy1sYW1iZGEuaHRtbH1cbiAgICpcbiAgICogSWYgd3JpdGluZyB0aGUgTGFtYmRhIGluIFR5cGVTY3JpcHQsIHRoaXMgbWVhbnMgeW91IHNob3VsZCB1c2UgYEFQSUdhdGV3YXlQcm94eUV2ZW50VjJgIGFzIHRoZVxuICAgKiByZXF1ZXN0IHR5cGUsIGFuZCBgQVBJR2F0ZXdheVByb3h5UmVzdWx0VjJgIGFzIHRoZSByZXNwb25zZSB0eXBlLlxuICAgKi9cbiAgbGFtYmRhOiBsYW1iZGEuSUZ1bmN0aW9uXG59XG5cbmV4cG9ydCB0eXBlIFNxc0ludGVncmF0aW9uUHJvcHMgPSB7XG4gIHF1ZXVlOiBzcXMuSVF1ZXVlXG5cbiAgLyoqXG4gICAqIE1lc3NhZ2UgYXR0cmlidXRlcyB0byBwYXNzIG9uIHRvIFNRUy4gVGhlIGtleXMgaW4gdGhpcyBvYmplY3QgYXJlIHRoZSBuYW1lcyBvZiB0aGUgYXR0cmlidXRlcy5cbiAgICogRWFjaCBhdHRyaWJ1dGUgaGFzIGEgRGF0YVR5cGUgZmllbGQsIGFuZCBlaXRoZXIgYSBTdHJpbmdWYWx1ZSBvciBCaW5hcnlWYWx1ZSBmaWVsZCBkZXBlbmRpbmcgb25cbiAgICogaXRzIHR5cGUuIFNlZSBBV1MgZG9jczpcbiAgICogaHR0cHM6Ly9kb2NzLmF3cy5hbWF6b24uY29tL0FXU1NpbXBsZVF1ZXVlU2VydmljZS9sYXRlc3QvQVBJUmVmZXJlbmNlL0FQSV9NZXNzYWdlQXR0cmlidXRlVmFsdWUuaHRtbFxuICAgKlxuICAgKiBJbiB0aGUgU3RyaW5nVmFsdWUgZmllbGQsIHlvdSBjYW4gZG8gQVBJIEdhdGV3YXkgcGFyYW1ldGVyIG1hcHBpbmc6XG4gICAqIGh0dHBzOi8vZG9jcy5hd3MuYW1hem9uLmNvbS9hcGlnYXRld2F5L2xhdGVzdC9kZXZlbG9wZXJndWlkZS9odHRwLWFwaS1wYXJhbWV0ZXItbWFwcGluZy5odG1sXG4gICAqXG4gICAqIEV4YW1wbGU6XG4gICAqIGBgYFxuICAgKiBtZXNzYWdlQXR0cmlidXRlczoge1xuICAgKiAgIGNsaWVudElkOiB7XG4gICAqICAgICBEYXRhVHlwZTogXCJTdHJpbmdcIixcbiAgICogICAgIFN0cmluZ1ZhbHVlOiBcIiR7Y29udGV4dC5hdXRob3JpemVyLmNsaWVudElkfVwiLFxuICAgKiAgIH0sXG4gICAqIH0sXG4gICAqIGBgYFxuICAgKi9cbiAgbWVzc2FnZUF0dHJpYnV0ZXM/OiB7XG4gICAgW2F0dHJpYnV0ZU5hbWU6IHN0cmluZ106XG4gICAgICB8IHsgRGF0YVR5cGU6IFwiU3RyaW5nXCIgfCBcIk51bWJlclwiOyBTdHJpbmdWYWx1ZTogc3RyaW5nIH1cbiAgICAgIHwge1xuICAgICAgICAgIERhdGFUeXBlOiBcIkJpbmFyeVwiXG4gICAgICAgICAgLyoqIEJhc2U2NC1lbmNvZGVkIGJpbmFyeSBkYXRhIG9iamVjdC4gKi9cbiAgICAgICAgICBCaW5hcnlWYWx1ZTogc3RyaW5nXG4gICAgICAgIH1cbiAgfVxufVxuXG5leHBvcnQgdHlwZSBBdXRob3JpemF0aW9uUHJvcHM8QXV0aFNjb3Blc1QgZXh0ZW5kcyBzdHJpbmcgPSBzdHJpbmc+ID1cbiAgLyoqXG4gICAqIE5vIGF1dGhlbnRpY2F0aW9uLCBmb3Igd2hlbiB5b3Ugd2FudCBhIGZ1bGx5IHB1YmxpYyByb3V0ZSAob3IgaGFuZGxlIGF1dGhlbnRpY2F0aW9uIGluIHRoZVxuICAgKiBiYWNrZW5kIGludGVncmF0aW9uKS5cbiAgICovXG4gIHwgeyB0eXBlOiBcIk5PTkVcIiB9XG4gIC8qKlxuICAgKiBBV1MgSUFNIGF1dGhvcml6YXRpb24uXG4gICAqIGh0dHBzOi8vZG9jcy5hd3MuYW1hem9uLmNvbS9hcGlnYXRld2F5L2xhdGVzdC9kZXZlbG9wZXJndWlkZS9odHRwLWFwaS1hY2Nlc3MtY29udHJvbC1pYW0uaHRtbFxuICAgKi9cbiAgfCB7IHR5cGU6IFwiSUFNXCIgfVxuICAvKipcbiAgICogQ3JlYXRlcyBhIGN1c3RvbSBhdXRob3JpemVyIGxhbWJkYSB3aGljaCByZWFkcyBgQXV0aG9yaXphdGlvbjogQmVhcmVyIDx0b2tlbj5gIGhlYWRlciBhbmRcbiAgICogdmVyaWZpZXMgdGhlIHRva2VuIGFnYWluc3QgYSBDb2duaXRvIHVzZXIgcG9vbC5cbiAgICovXG4gIHwgKHtcbiAgICAgIHR5cGU6IFwiQ09HTklUT19VU0VSX1BPT0xcIlxuICAgIH0gJiBDb2duaXRvVXNlclBvb2xBdXRob3JpemVyUHJvcHM8QXV0aFNjb3Blc1Q+KVxuICAvKipcbiAgICogQ3JlYXRlcyBhIGN1c3RvbSBhdXRob3JpemVyIGxhbWJkYSB3aGljaCByZWFkcyBgQXV0aG9yaXphdGlvbjogQmFzaWMgPGJhc2U2NC1lbmNvZGVkIGNyZWRlbnRpYWxzPmBcbiAgICogaGVhZGVyIGFuZCB2ZXJpZmllcyB0aGUgY3JlZGVudGlhbHMgYWdhaW5zdCBhIGdpdmVuIHNlY3JldC5cbiAgICovXG4gIHwgKHsgdHlwZTogXCJCQVNJQ19BVVRIXCIgfSAmIEJhc2ljQXV0aEF1dGhvcml6ZXJQcm9wcylcbiAgLyoqXG4gICAqIENyZWF0ZXMgYSBjdXN0b20gYXV0aG9yaXplciBsYW1iZGEgd2hpY2ggYWxsb3dzIGJvdGg6XG4gICAqIC0gYEF1dGhvcml6YXRpb246IEJlYXJlciA8dG9rZW4+YCBoZWFkZXIsIGZvciB3aGljaCB0aGUgdG9rZW4gaXMgY2hlY2tlZCBhZ2FpbnN0IHRoZSBnaXZlblxuICAgKiAgIENvZ25pdG8gdXNlciBwb29sXG4gICAqIC0gYEF1dGhvcml6YXRpb246IEJhc2ljIDxiYXNlNjQtZW5jb2RlZCBjcmVkZW50aWFscz5gIGhlYWRlciwgZm9yIHdoaWNoIHRoZSBjcmVkZW50aWFscyBhcmVcbiAgICogICBjaGVja2VkIGFnYWluc3QgdGhlIGNyZWRlbnRpYWxzIGZyb20gdGhlIGdpdmVuIGJhc2ljIGF1dGggc2VjcmV0IG5hbWVcbiAgICpcbiAgICogSWYgZWl0aGVyIG9mIHRoZXNlIGFyZSBnaXZlbiBhbmQgdmFsaWQsIHRoZSByZXF1ZXN0IGlzIGF1dGhlbnRpY2F0ZWQuXG4gICAqL1xuICB8ICh7XG4gICAgICB0eXBlOiBcIkNPR05JVE9fVVNFUl9QT09MX09SX0JBU0lDX0FVVEhcIlxuICAgIH0gJiBDb2duaXRvVXNlclBvb2xPckJhc2ljQXV0aEF1dGhvcml6ZXJQcm9wczxBdXRoU2NvcGVzVD4pXG5cbmV4cG9ydCB0eXBlIENvZ25pdG9Vc2VyUG9vbEF1dGhvcml6ZXJQcm9wczxcbiAgQXV0aFNjb3Blc1QgZXh0ZW5kcyBzdHJpbmcgPSBzdHJpbmcsXG4+ID0ge1xuICB1c2VyUG9vbDogSVVzZXJQb29sXG5cbiAgLyoqXG4gICAqIFZlcmlmaWVzIHRoYXQgdG9rZW4gY2xhaW1zIGNvbnRhaW4gdGhlIGdpdmVuIHNjb3BlLlxuICAgKlxuICAgKiBXaGVuIGRlZmluZWQgYXMgcGFydCBvZiBhIHJlc291cmNlIHNlcnZlciwgc2NvcGVzIGFyZSBvbiB0aGUgZm9ybWF0OlxuICAgKiBge3Jlc291cmNlIHNlcnZlciBpZGVudGlmaWVyfS97c2NvcGUgbmFtZX1gLCBlLmcuIGBleHRlcm5hbC92aWV3X3VzZXJzYC5cbiAgICpcbiAgICogVG8gZ2V0IG1vcmUgdHlwZSBzYWZldHkgb24gdGhpcyBwYXJhbWV0ZXIsIHNlZSB0aGUgZG9jcyBmb3IgdGhlIGBBdXRoU2NvcGVzVGAgdHlwZSBwYXJhbWV0ZXIgb25cbiAgICoge0BsaW5rIEFwaUdhdGV3YXl9LlxuICAgKi9cbiAgcmVxdWlyZWRTY29wZT86IEF1dGhTY29wZXNUXG5cbiAgLyoqXG4gICAqIE5hbWUgb2Ygc2VjcmV0IGluIEFXUyBTZWNyZXRzIE1hbmFnZXIgdGhhdCBzdG9yZXMgYmFzaWMgYXV0aCBjcmVkZW50aWFscyBmb3IgdGhlIGJhY2tlbmRcbiAgICogc2VydmljZSwgdG8gYmUgZm9yd2FyZGVkIHRvIHRoZSBiYWNrZW5kIGlmIENvZ25pdG8gdXNlciBwb29sIGF1dGhlbnRpY2F0aW9uIHN1Y2NlZWRlZC5cbiAgICpcbiAgICogVGhlIHNlY3JldCB2YWx1ZSBtdXN0IGZvbGxvdyB0aGlzIGZvcm1hdDpcbiAgICogYGBganNvblxuICAgKiB7XCJ1c2VybmFtZVwiOlwiPHVzZXJuYW1lPlwiLFwicGFzc3dvcmRcIjpcIjxwYXNzd29yZD5cIn1cbiAgICogYGBgXG4gICAqXG4gICAqIFRoaXMgcHJvcCBzb2x2ZXMgdGhlIGZvbGxvd2luZyB1c2UtY2FzZTpcbiAgICogLSBZb3Ugd2FudCB0byBkbyBDb2duaXRvIHVzZXIgcG9vbCBhdXRoZW50aWNhdGlvbiBpbiB0aGUgQVBJIEdhdGV3YXlcbiAgICogLSBZb3Ugd2FudCBhbiBhZGRpdGlvbmFsIGF1dGggY2hlY2sgaW4gdGhlIGJhY2tlbmQsIGJ1dCB5b3UgZG9uJ3Qgd2FudCB0byBkZWFsIHdpdGggQ29nbml0b1xuICAgKiAgIHRoZXJlXG4gICAqIC0gVGhlIGJhY2tlbmQgdXNlcyBiYXNpYyBhdXRoXG4gICAqXG4gICAqIFRoaXMgcHJvcCBzb2x2ZXMgdGhpcyBieSBsZXR0aW5nIHlvdSBzcGVjaWZ5IGNyZWRlbnRpYWxzIHRvIHBhc3MgdG8gdGhlIGJhY2tlbmQgYWZ0ZXIgQVBJLUdXXG4gICAqIGF1dGhlbnRpY2F0aW9uIHN1Y2NlZWRzLiBZb3UgY2FuIHBhc3MgdGhlIGVuY29kZWQgY3JlZGVudGlhbHMgdGhyb3VnaFxuICAgKiB7QGxpbmsgQWxiSW50ZWdyYXRpb25Qcm9wcy5tYXBQYXJhbWV0ZXJzfSwgdXNpbmcgdGhlIGBhdXRob3JpemVyLmludGVybmFsQXV0aG9yaXphdGlvbkhlYWRlcmBcbiAgICogY29udGV4dCB2YXJpYWJsZSwgbGlrZSBzbzpcbiAgICogYGBgXG4gICAqIG1hcFBhcmFtZXRlcnM6IChwYXJhbWV0ZXJzKSA9PiBwYXJhbWV0ZXJzLm92ZXJ3cml0ZUhlYWRlcihcbiAgICogICAvLyAnQXV0aG9yaXphdGlvbicgaGVhZGVyIGNhbm5vdCBiZSBvdmVycmlkZGVuLCBzbyB3ZSB1c2UgYSBjdXN0b20gaGVhZGVyXG4gICAqICAgXCJYLUludGVybmFsLUF1dGhvcml6YXRpb25cIixcbiAgICogICBhcGlndy5NYXBwaW5nVmFsdWUuY29udGV4dFZhcmlhYmxlKFwiYXV0aG9yaXplci5pbnRlcm5hbEF1dGhvcml6YXRpb25IZWFkZXJcIiksXG4gICAqIClcbiAgICogYGBgXG4gICAqIFRoZSBiYWNrZW5kIGNhbiB0aGVuIGNoZWNrIHRoZSBgWC1JbnRlcm5hbC1BdXRob3JpemF0aW9uYCBoZWFkZXIuXG4gICAqL1xuICBjcmVkZW50aWFsc0ZvckludGVybmFsQXV0aG9yaXphdGlvbj86IHN0cmluZ1xufVxuXG5leHBvcnQgdHlwZSBCYXNpY0F1dGhBdXRob3JpemVyUHJvcHMgPSB7XG4gIC8qKlxuICAgKiBOYW1lIG9mIHNlY3JldCBpbiBBV1MgU2VjcmV0cyBNYW5hZ2VyIHRoYXQgc3RvcmVzIGJhc2ljIGF1dGggY3JlZGVudGlhbHMuIFRoZSBzZWNyZXQgdmFsdWUgbXVzdFxuICAgKiBmb2xsb3cgdGhpcyBmb3JtYXQ6XG4gICAqIGBgYGpzb25cbiAgICoge1widXNlcm5hbWVcIjpcIjx1c2VybmFtZT5cIixcInBhc3N3b3JkXCI6XCI8cGFzc3dvcmQ+XCJ9XG4gICAqIGBgYFxuICAgKi9cbiAgY3JlZGVudGlhbHNTZWNyZXROYW1lOiBzdHJpbmdcbn1cblxuZXhwb3J0IHR5cGUgQ29nbml0b1VzZXJQb29sT3JCYXNpY0F1dGhBdXRob3JpemVyUHJvcHM8XG4gIEF1dGhTY29wZXNUIGV4dGVuZHMgc3RyaW5nID0gc3RyaW5nLFxuPiA9IHtcbiAgdXNlclBvb2w6IElVc2VyUG9vbFxuXG4gIC8qKlxuICAgKiBOYW1lIG9mIHNlY3JldCBpbiBBV1MgU2VjcmV0cyBNYW5hZ2VyIHRoYXQgc3RvcmVzIGJhc2ljIGF1dGggY3JlZGVudGlhbHMuIFRoZSBzZWNyZXQgdmFsdWUgbXVzdFxuICAgKiBmb2xsb3cgdGhpcyBmb3JtYXQ6XG4gICAqIGBgYGpzb25cbiAgICoge1widXNlcm5hbWVcIjpcIjx1c2VybmFtZT5cIixcInBhc3N3b3JkXCI6XCI8cGFzc3dvcmQ+XCJ9XG4gICAqIGBgYFxuICAgKi9cbiAgYmFzaWNBdXRoQ3JlZGVudGlhbHNTZWNyZXROYW1lPzogc3RyaW5nXG5cbiAgLyoqXG4gICAqIFZlcmlmaWVzIHRoYXQgdG9rZW4gY2xhaW1zIGNvbnRhaW4gdGhlIGdpdmVuIHNjb3BlLiBPbmx5IGFwcGxpY2FibGUgZm9yIGBCZWFyZXJgIHRva2VuIHJlcXVlc3RzXG4gICAqIGNoZWNrZWQgYWdhaW5zdCB0aGUgQ29nbml0byBVc2VyIFBvb2wgKG5vdCBhcHBsaWNhYmxlIGZvciBiYXNpYyBhdXRoKS5cbiAgICpcbiAgICogV2hlbiBkZWZpbmVkIGFzIHBhcnQgb2YgYSByZXNvdXJjZSBzZXJ2ZXIsIHNjb3BlcyBhcmUgb24gdGhlIGZvcm1hdDpcbiAgICogYHtyZXNvdXJjZSBzZXJ2ZXIgaWRlbnRpZmllcn0ve3Njb3BlIG5hbWV9YCwgZS5nLiBgZXh0ZXJuYWwvdmlld191c2Vyc2AuXG4gICAqXG4gICAqIFRvIGdldCBtb3JlIHR5cGUgc2FmZXR5IG9uIHRoaXMgcGFyYW1ldGVyLCBzZWUgdGhlIGRvY3MgZm9yIHRoZSBgQXV0aFNjb3Blc1RgIHR5cGUgcGFyYW1ldGVyIG9uXG4gICAqIHtAbGluayBBcGlHYXRld2F5fS5cbiAgICovXG4gIHJlcXVpcmVkU2NvcGU/OiBBdXRoU2NvcGVzVFxufVxuXG5leHBvcnQgdHlwZSBBcGlHYXRld2F5QWNjZXNzTG9nc1Byb3BzID0ge1xuICAvKipcbiAgICogRGVsZXRlIHRoZSBhY2Nlc3MgbG9ncyBpZiB0aGlzIGNvbnN0cnVjdCBpcyBkZWxldGVkP1xuICAgKlxuICAgKiBNYXliZSB5b3Ugd2FudCB0byBERVNUUk9ZIGluc3RlYWQuIE9yIGZvciBsZWdhbCByZWFzb25zLCByZXRhaW4gZm9yIGF1ZGl0LlxuICAgKlxuICAgKiBAZGVmYXVsdCBSZW1vdmFsUG9saWN5LlJFVEFJTlxuICAgKi9cbiAgcmVtb3ZhbFBvbGljeT86IGNkay5SZW1vdmFsUG9saWN5XG5cbiAgLyoqXG4gICAqIEhvdyBsb25nIHRvIGtlZXAgdGhlIGxvZ3MuIElmIHVuZGVmaW5lZCwgdXNlcyB0aGUgc2FtZSBkZWZhdWx0IGFzIG5ldyBBV1MgbG9nIGdyb3Vwcy5cbiAgICpcbiAgICogQGRlZmF1bHQgUmV0ZW50aW9uRGF5cy5UV09fWUVBUlNcbiAgICovXG4gIHJldGVudGlvbj86IGxvZ3MuUmV0ZW50aW9uRGF5c1xuXG4gIC8qKlxuICAgKiBBIGN1c3RvbSBKU09OIGxvZyBmb3JtYXQsIHdoaWNoIHVzZXMgdmFyaWFibGVzIGZyb20gYFwiJGNvbnRleHRcImAuXG4gICAqXG4gICAqIFNlZSBbQVdTOiBDbG91ZFdhdGNoIGxvZyBmb3JtYXRzIGZvciBBUEkgR2F0ZXdheV0oaHR0cHM6Ly9kb2NzLmF3cy5hbWF6b24uY29tL2FwaWdhdGV3YXkvbGF0ZXN0L2RldmVsb3Blcmd1aWRlL3NldC11cC1sb2dnaW5nLmh0bWwjYXBpZ2F0ZXdheS1jbG91ZHdhdGNoLWxvZy1mb3JtYXRzKVxuICAgKiBmb3IgZm9ybWF0cyBhbmQgcnVsZXMuIEl0IGlzIHBvc3NpYmxlIHRvIHVzZSBvdGhlciBmb3JtYXRzIGxpa2UgQ0xGIGFuZCBYTUwsIGJ1dCB0aGlzIGNvbnN0cnVjdFxuICAgKiBvbmx5IHN1cHBvcnRzIEpTT04gZm9yIG5vdy5cbiAgICpcbiAgICogRm9yIGEgbGlzdCBvZiBhbGwgcG9zc2libGUgdmFyaWFibGVzIHRvIGxvZywgc2VlXG4gICAqIFtBV1M6ICRjb250ZXh0IFZhcmlhYmxlcyBmb3IgZGF0YSBtb2RlbHMsIGF1dGhvcml6ZXJzLCBtYXBwaW5nIHRlbXBsYXRlcywgYW5kIENsb3VkV2F0Y2ggYWNjZXNzIGxvZ2dpbmddKGh0dHBzOi8vZG9jcy5hd3MuYW1hem9uLmNvbS9hcGlnYXRld2F5L2xhdGVzdC9kZXZlbG9wZXJndWlkZS9hcGktZ2F0ZXdheS1tYXBwaW5nLXRlbXBsYXRlLXJlZmVyZW5jZS5odG1sI2NvbnRleHQtdmFyaWFibGUtcmVmZXJlbmNlKVxuICAgKiBhbmRcbiAgICogW0FXUzogJGNvbnRleHQgVmFyaWFibGVzIGZvciBhY2Nlc3MgbG9nZ2luZyBvbmx5XShodHRwczovL2RvY3MuYXdzLmFtYXpvbi5jb20vYXBpZ2F0ZXdheS9sYXRlc3QvZGV2ZWxvcGVyZ3VpZGUvYXBpLWdhdGV3YXktbWFwcGluZy10ZW1wbGF0ZS1yZWZlcmVuY2UuaHRtbCNjb250ZXh0LXZhcmlhYmxlLXJlZmVyZW5jZS1hY2Nlc3MtbG9nZ2luZy1vbmx5KSAuXG4gICAqXG4gICAqIEBkZWZhdWx0IHtAbGluayBkZWZhdWx0QWNjZXNzTG9nRm9ybWF0fVxuICAgKi9cbiAgYWNjZXNzTG9nRm9ybWF0PzogUmVjb3JkPHN0cmluZywgc3RyaW5nPlxufVxuXG4vKipcbiAqIFRoaXMgY29uc3RydWN0IHRyaWVzIHRvIHNpbXBsaWZ5IHRoZSBjcmVhdGlvbiBvZiBhbiBBUEkgR2F0ZXdheSBmb3IgYSBzZXJ2aWNlLCBieSBjb2xsZWN0aW5nIG1vc3RcbiAqIG9mIHRoZSBjb21tb24gc2V0dXAgaGVyZS5cbiAqXG4gKiBUaGUgYXBwcm9hY2ggZm9sbG93ZWQgaW4gdGhpcyBjb25zdHJ1Y3QgaXM6XG4gKiAxLiBPbmUgQVBJLUdXIHBlciBzZXJ2aWNlXG4gKiAzLiBPbmUgc3ViZG9tYWluIHBlciBBUEktR1cgLyBzZXJ2aWNlXG4gKiA0LiBVc2UgSFRUUCBBUEksIG5vdCBSRVNUXG4gKiA1LiBVc2UgYSAkZGVmYXVsdCBzdGFnZSB3aXRoIGF1dG9kZXBsb3lcbiAqIDYuIFN1cHBvcnQgbXVsdGlwbGUgcm91dGVzICh3aXRoIHBvc3NpYmxlIGAve3Byb3h5K31gIHRvIGxldCBhbGwgc3ViLXBhdGhzIHRocm91Z2gpXG4gKiA3LiBBbGxvdyBjdXN0b20gaW50ZWdyYXRpb24vYXV0aG9yaXplciBmb3IgZWFjaCByb3V0ZSwgb3IgZGVmYXVsdHMgZm9yIHRoZSB3aG9sZSBnYXRld2F5XG4gKlxuICogVGhlIHJvdXRlIGludGVncmF0aW9uIGlzIG9uZSBvZiB0aGVzZTpcbiAqIC0gQUxCIHByaXZhdGUgaW50ZWdyYXRpb24gd2l0aCBWUEMgTGluayB1c2luZyBIVFRQUyB0byB0aGUgQUxCXG4gKiAtIExhbWJkYSBpbnRlZ3JhdGlvblxuICogLSBTUVMgaW50ZWdyYXRpb25cbiAqXG4gKiAjIyMgTG9hZCBCYWxhbmNlciBTZWN1cml0eSBHcm91cFxuICpcbiAqIE5vdGUgdGhhdCB0aGUgbG9hZCBiYWxhbmNlciB1c2VkIGluIGFuIHtAbGluayBBbGJJbnRlZ3JhdGlvblByb3BzfSBtdXN0IGFsbG93IG91dGJvdW5kIEhUVFBTXG4gKiB0cmFmZmljIHRvIGl0cyBTZWN1cml0eUdyb3VwLiBPdGhlcndpc2UsIHRoZSBWUEMgTGluayB1c2VkIGJ5IHRoZSBBUEktR1cgY2FuJ3QgZ2V0IHRyYWZmaWMgZnJvbVxuICogdGhlIEFMQi5cbiAqXG4gKiBgYGBcbiAqIGNvbnN0IGxvYWRCYWxhbmNlclNlY3VyaXR5R3JvdXAgPSBuZXcgZWMyLlNlY3VyaXR5R3JvdXAoLi4uLCB7XG4gKiAgIGFsbG93QWxsT3V0Ym91bmQ6IGZhbHNlLFxuICogfSlcbiAqXG4gKiBsb2FkQmFsYW5jZXJTZWN1cml0eUdyb3VwLmFkZEVncmVzc1J1bGUoXG4gKiAgIGxvYWRCYWxhbmNlclNlY3VyaXR5R3JvdXAsXG4gKiAgIGVjMi5Qb3J0LnRjcCg0NDMpLFxuICogICBcIk91dGJvdW5kIHRvIHNlbGYgZm9yIEFMQiB0byBBUEktR1cgVlBDLUxpbmtcIixcbiAqIClcbiAqXG4gKiBjb25zdCBsb2FkQmFsYW5jZXIgPSBuZXcgbGlmbGlnTG9hZEJhbGFuY2VyLkxvYWRCYWxhbmNlciguLi4sXG4gKiAgIHtcbiAqICAgICBvdmVycmlkZUxvYWRCYWxhbmNlclByb3BzOiB7XG4gKiAgICAgICBzZWN1cml0eUdyb3VwOiBsb2FkQmFsYW5jZXJTZWN1cml0eUdyb3VwLFxuICogICAgIH0sXG4gKiAgIH0sXG4gKiApXG4gKiBgYGBcbiAqXG4gKiBAdGVtcGxhdGUgQXV0aFNjb3Blc1QgVGhpcyB0eXBlIHBhcmFtZXRlciBhbGxvd3MgeW91IHRvIGltcHJvdmUgdHlwZSBzYWZldHkgb24gdGhlXG4gKiBgcmVxdWlyZWRTY29wZWAgZmllbGQgb24ge0BsaW5rIENvZ25pdG9Vc2VyUG9vbE9yQmFzaWNBdXRoQXV0aG9yaXplclByb3BzfSwgYnkgbmFycm93aW5nIHRoZSB0eXBlXG4gKiB0byBzcGVjaWZpYyBzdHJpbmdzLiBZb3UgY2FuIHRoZW4gZXh0ZW5kIHRoZSBgQXBpR2F0ZXdheWAgd2l0aCB0aGlzIHR5cGUgdG8gZW5mb3JjZSB0aG9zZSBzY29wZXNcbiAqIGFjcm9zcyB0aGUgYXBwbGljYXRpb24uIFJlbWVtYmVyIHRoYXQgYXV0aCBzY29wZXMgbXVzdCBiZSBvbiB0aGUgZm9ybWF0XG4gKiBge3Jlc291cmNlIHNlcnZlciBpZGVudGlmaWVyfS97c2NvcGUgbmFtZX1gLlxuICpcbiAqIEV4YW1wbGU6XG4gKiBgYGBcbiAqIHR5cGUgQXV0aFNjb3BlcyA9IFwiZXh0ZXJuYWwvcmVhZF91c2Vyc1wiIHwgXCJpbnRlcm5hbC9jcmVhdGVfdXNlcnNcIlxuICpcbiAqIGV4cG9ydCBjbGFzcyBNeVByb2plY3RBcGlHYXRld2F5IGV4dGVuZHMgQXBpR2F0ZXdheTxBdXRoU2NvcGVzPiB7fVxuICogYGBgXG4gKiBUeXBlU2NyaXB0IHdpbGwgdGhlbiBlbmZvcmNlIHRoYXQgYHJlcXVpcmVkU2NvcGVgIGlzIG9uZSBvZiBgQXV0aFNjb3Blc2AsIGFuZCBwcm92aWRlXG4gKiBhdXRvLWNvbXBsZXRlLlxuICpcbiAqIEBhdXRob3IgS3Jpc3RpYW4gUmVrc3RhZCA8a3JlQGNhcHJhY29uc3VsdGluZy5ubz5cbiAqIEBhdXRob3IgSGVybWFubiBNw7hya3JpZCA8aGVtQGxpZmxpZy5ubz5cbiAqL1xuZXhwb3J0IGNsYXNzIEFwaUdhdGV3YXk8XG4gIEF1dGhTY29wZXNUIGV4dGVuZHMgc3RyaW5nID0gc3RyaW5nLFxuPiBleHRlbmRzIGNvbnN0cnVjdHMuQ29uc3RydWN0IHtcbiAgLyoqIFRoZSBBUEkgR2F0ZXdheSBIVFRQIEFQSS4gVGhpcyBpcyB0aGUgbWFpbiBjb25zdHJ1Y3QgZm9yIEFQSS1HVy4gKi9cbiAgcHVibGljIHJlYWRvbmx5IGh0dHBBcGk6IGFwaWd3Lkh0dHBBcGlcblxuICAvKiogVGhlIHJvdXRlcyB3aGljaCBjb25uZWN0IHRoZSB7QGxpbmsgaHR0cEFwaX0gdG8gdGhlIGJhY2tlbmQgaW50ZWdyYXRpb24ocykuICovXG4gIHB1YmxpYyByZWFkb25seSByb3V0ZXM6IGFwaWd3Lkh0dHBSb3V0ZVtdID0gW11cblxuICAvKiogVGhlIGRvbWFpbiB3aGljaCBjb25zdW1lcnMgbXVzdCB1c2UuKi9cbiAgcHVibGljIHJlYWRvbmx5IGRvbWFpbjogc3RyaW5nXG5cbiAgLyoqIEFjY2VzcyBsb2cgZ3JvdXAuICovXG4gIHB1YmxpYyByZWFkb25seSBsb2dHcm91cDogbG9ncy5Mb2dHcm91cFxuXG4gIHByaXZhdGUgcmVhZG9ubHkgcHJvcHM6IEFwaUdhdGV3YXlQcm9wczxBdXRoU2NvcGVzVD5cblxuICBjb25zdHJ1Y3RvcihcbiAgICBzY29wZTogY29uc3RydWN0cy5Db25zdHJ1Y3QsXG4gICAgaWQ6IHN0cmluZyxcbiAgICBwcm9wczogQXBpR2F0ZXdheVByb3BzPEF1dGhTY29wZXNUPixcbiAgKSB7XG4gICAgc3VwZXIoc2NvcGUsIGlkKVxuICAgIHRoaXMucHJvcHMgPSBwcm9wc1xuXG4gICAgQXBpR2F0ZXdheS52YWxpZGF0ZVByb3BzKHByb3BzLCBpZCwgY2RrLlN0YWNrLm9mKHRoaXMpKVxuXG4gICAgY29uc3QgY3VzdG9tRG9tYWluID0gbmV3IEN1c3RvbURvbWFpbih0aGlzLCBcIkN1c3RvbURvbWFpblwiLCBwcm9wcy5kbnMpXG4gICAgdGhpcy5kb21haW4gPSBjdXN0b21Eb21haW4uY3VzdG9tRG9tYWluTmFtZVxuXG4gICAgbGV0IGNvcnNPcHRpb25zOiBhcGlndy5Db3JzUHJlZmxpZ2h0T3B0aW9ucyB8IHVuZGVmaW5lZFxuICAgIGlmIChwcm9wcy5jb3JzQWxsb3dBbGwpIHtcbiAgICAgIGNvcnNPcHRpb25zID0ge1xuICAgICAgICBhbGxvd09yaWdpbnM6IFtcIipcIl0sXG4gICAgICAgIC8vIFwiVGhlIEF1dGhvcml6YXRpb24gaGVhZGVyIGNhbid0IGJlIHdpbGRjYXJkZWQgYW5kIGFsd2F5cyBuZWVkcyB0byBiZSBsaXN0ZWQgZXhwbGljaXRseVwiXG4gICAgICAgIC8vIGh0dHBzOi8vZGV2ZWxvcGVyLm1vemlsbGEub3JnL2VuLVVTL2RvY3MvV2ViL0hUVFAvSGVhZGVycy9BY2Nlc3MtQ29udHJvbC1BbGxvdy1IZWFkZXJzXG4gICAgICAgIC8vIENvbnRlbnQtVHlwZSBtYXkgYWxzbyByZXF1aXJlIGV4cGxpY2l0IHdoaXRlbGlzdGluZzogaHR0cHM6Ly9zdGFja292ZXJmbG93LmNvbS9hLzYzNTY3NjQ3XG4gICAgICAgIGFsbG93SGVhZGVyczogW1wiQXV0aG9yaXphdGlvblwiLCBcIkNvbnRlbnQtVHlwZVwiLCBcIipcIl0sXG4gICAgICAgIC8vIE5vdCB1c2luZyAnKicgaGVyZSwgYmVjYXVzZSBcIkluIHJlcXVlc3RzIHdpdGggY3JlZGVudGlhbHMsIGl0IGlzIHRyZWF0ZWQgYXMgdGhlIGxpdGVyYWxcbiAgICAgICAgLy8gbWV0aG9kIG5hbWUgJyonIHdpdGhvdXQgc3BlY2lhbCBzZW1hbnRpY3NcIlxuICAgICAgICAvLyBodHRwczovL2RldmVsb3Blci5tb3ppbGxhLm9yZy9lbi1VUy9kb2NzL1dlYi9IVFRQL0hlYWRlcnMvQWNjZXNzLUNvbnRyb2wtQWxsb3ctTWV0aG9kc1xuICAgICAgICBhbGxvd01ldGhvZHM6IFtcbiAgICAgICAgICBhcGlndy5Db3JzSHR0cE1ldGhvZC5HRVQsXG4gICAgICAgICAgYXBpZ3cuQ29yc0h0dHBNZXRob2QuUE9TVCxcbiAgICAgICAgICBhcGlndy5Db3JzSHR0cE1ldGhvZC5QVVQsXG4gICAgICAgICAgYXBpZ3cuQ29yc0h0dHBNZXRob2QuUEFUQ0gsXG4gICAgICAgICAgYXBpZ3cuQ29yc0h0dHBNZXRob2QuREVMRVRFLFxuICAgICAgICAgIGFwaWd3LkNvcnNIdHRwTWV0aG9kLk9QVElPTlMsXG4gICAgICAgICAgYXBpZ3cuQ29yc0h0dHBNZXRob2QuSEVBRCxcbiAgICAgICAgXSxcbiAgICAgICAgbWF4QWdlOiBjZGsuRHVyYXRpb24uZGF5cygxKSxcbiAgICAgIH1cbiAgICB9XG5cbiAgICAvLyBUaGUgYWN0dWFsIEFQSS4gVGhpcyBob2xkcyB0aGUgcm91dGVzLCBhdXRob3JpemVycyBhbmQgaW50ZWdyYXRpb25zLlxuICAgIGNvbnN0IGFwaSA9IG5ldyBhcGlndy5IdHRwQXBpKHRoaXMsIFwiSHR0cEFwaS1cIiArIHByb3BzLmRucy5zdWJkb21haW4sIHtcbiAgICAgIC8vIGRlZmF1bHRJbnRlZ3JhdGlvbjogZGVmYXVsdEludGVncmF0aW9uLCAvLyBUaGlzIGlzIGZvciBhIGNhdGNoLWFsbCAkZGVmYXVsdCByb3V0ZVxuICAgICAgZGVzY3JpcHRpb246IGBBbiBIVFRQIEFQSSBmb3IgJHtwcm9wcy5kbnMuc3ViZG9tYWlufS4ke3Byb3BzLmRucy5ob3N0ZWRab25lLnpvbmVOYW1lfS5gLFxuICAgICAgZGlzYWJsZUV4ZWN1dGVBcGlFbmRwb2ludDogdHJ1ZSwgLy8gRm9yY2UgZXh0ZXJuYWxzIHRvIGdvIHRocm91Z2ggY3VzdG9tIGRvbWFpbi4gTVVTVCBiZSB0cnVlIHdoZW4gdXNpbmcgTXV0dWFsIFRMUywgZm9yIHNlY3VyaXR5IHJlYXNvbnNcbiAgICAgIGNyZWF0ZURlZmF1bHRTdGFnZTogdHJ1ZSxcbiAgICAgIGRlZmF1bHREb21haW5NYXBwaW5nOiB7IGRvbWFpbk5hbWU6IGN1c3RvbURvbWFpbi5hcGlHd0N1c3RvbURvbWFpbiB9LFxuICAgICAgY29yc1ByZWZsaWdodDogY29yc09wdGlvbnMsXG4gICAgICAuLi5wcm9wcz8ucHJvcHNPdmVycmlkZT8uaHR0cEFwaSxcbiAgICB9KVxuICAgIHRoaXMuaHR0cEFwaSA9IGFwaVxuXG4gICAgY29uc3Qgc3RhZ2UgPSBhcGkuZGVmYXVsdFN0YWdlPy5ub2RlLmRlZmF1bHRDaGlsZCBhcyBhcGlndy5DZm5TdGFnZVxuXG4gICAgY29uc3QgbG9ncyA9IG5ldyBBcGlHYXRld2F5QWNjZXNzTG9ncyhcbiAgICAgIHRoaXMsXG4gICAgICBcIkFjY2Vzc0xvZ3NcIixcbiAgICAgIHN0YWdlLFxuICAgICAgcHJvcHMuYWNjZXNzTG9ncyxcbiAgICAgIGRlZmF1bHRBY2Nlc3NMb2dGb3JtYXQsXG4gICAgKVxuICAgIHRoaXMubG9nR3JvdXAgPSBsb2dzLmxvZ0dyb3VwXG5cbiAgICBzdGFnZS5kZWZhdWx0Um91dGVTZXR0aW5ncyA9IHtcbiAgICAgIC4uLnN0YWdlLmRlZmF1bHRSb3V0ZVNldHRpbmdzLFxuICAgICAgZGV0YWlsZWRNZXRyaWNzRW5hYmxlZDogcHJvcHMuZGV0YWlsZWRNZXRyaWNzID8/IHRydWUsXG4gICAgICB0aHJvdHRsaW5nQnVyc3RMaW1pdDogcHJvcHMudGhyb3R0bGluZz8uYnVyc3QsIC8vIERlZmF1bHQgZm9yIGFjY291bnQgaXMgNV8wMDBcbiAgICAgIHRocm90dGxpbmdSYXRlTGltaXQ6IHByb3BzLnRocm90dGxpbmc/LnJhdGUsIC8vIERlZmF1bHQgZm9yIGFjY291bnQgaXMgMTBfMDAwXG4gICAgfVxuXG4gICAgY29uc3QgZGVmYXVsdEludGVncmF0aW9uID0gcHJvcHMuZGVmYXVsdEludGVncmF0aW9uXG4gICAgICA/IHRoaXMuY3JlYXRlSW50ZWdyYXRpb24ocHJvcHMuZGVmYXVsdEludGVncmF0aW9uLCBwcm9wcy5kbnMpXG4gICAgICA6IHVuZGVmaW5lZFxuXG4gICAgY29uc3QgZGVmYXVsdEF1dGhvcml6ZXIgPSBwcm9wcy5kZWZhdWx0QXV0aG9yaXphdGlvblxuICAgICAgPyB0aGlzLmNyZWF0ZUF1dGhvcml6ZXIoXCJEZWZhdWx0QXV0aG9yaXplclwiLCBwcm9wcy5kZWZhdWx0QXV0aG9yaXphdGlvbilcbiAgICAgIDogdW5kZWZpbmVkXG5cbiAgICBmb3IgKGNvbnN0IHJvdXRlIG9mIHByb3BzLnJvdXRlcykge1xuICAgICAgbGV0IGludGVncmF0aW9uOiBhcGlndy5IdHRwUm91dGVJbnRlZ3JhdGlvblxuICAgICAgaWYgKHJvdXRlLmludGVncmF0aW9uKSB7XG4gICAgICAgIGludGVncmF0aW9uID0gdGhpcy5jcmVhdGVJbnRlZ3JhdGlvbihyb3V0ZS5pbnRlZ3JhdGlvbiwgcHJvcHMuZG5zKVxuICAgICAgfSBlbHNlIHtcbiAgICAgICAgaW50ZWdyYXRpb24gPSBkZWZhdWx0SW50ZWdyYXRpb24hIC8vIFZlcmlmaWVkIGluIHZhbGlkYXRlUHJvcHNcbiAgICAgIH1cblxuICAgICAgbGV0IGF1dGhvcml6ZXI6IGFwaWd3LklIdHRwUm91dGVBdXRob3JpemVyIHwgdW5kZWZpbmVkXG4gICAgICBpZiAocm91dGUuYXV0aG9yaXphdGlvbikge1xuICAgICAgICBhdXRob3JpemVyID0gdGhpcy5jcmVhdGVBdXRob3JpemVyKFxuICAgICAgICAgIC8vIFVzaW5nIHRoZSByb3V0ZSBwYXRoIGhlcmUgY2F1c2VkIENsb3VkRm9ybWF0aW9uIHRvIGZhaWwgZHVlIHRvIHJlc291cmNlIG5hbWVzIGJlaW5nIHRvb1xuICAgICAgICAgIC8vIGxvbmcuIFRoZXJlZm9yZSwgd2Ugbm93IGhhc2ggdGhlIHJvdXRlIHBhdGggdG8gZ2V0IGEgc2hvcnRlciBuYW1lLlxuICAgICAgICAgIGBSb3V0ZUF1dGhvcml6ZXIke3Nob3J0SGFzaChgJHtyb3V0ZS5tZXRob2QgPz8gXCJcIn0ke3JvdXRlLnBhdGh9YCl9YCxcbiAgICAgICAgICByb3V0ZS5hdXRob3JpemF0aW9uLFxuICAgICAgICApXG4gICAgICB9IGVsc2Uge1xuICAgICAgICBhdXRob3JpemVyID0gZGVmYXVsdEF1dGhvcml6ZXJcbiAgICAgIH1cblxuICAgICAgY29uc3Qgcm91dGVQYXRocyA9IFtyb3V0ZS5wYXRoXVxuICAgICAgaWYgKHJvdXRlLmluY2x1ZGVTdWJwYXRocyA9PT0gdHJ1ZSkge1xuICAgICAgICAvLyBJZiB3ZSBpbmNsdWRlIHN1Yi1wYXRocywgd2UgYWRkIGFuIGFkZGl0aW9uYWwgcm91dGUgd2l0aCAve3Byb3h5K30gKHRoZSArIG1lYW5zIGl0IHdpbGxcbiAgICAgICAgLy8gbWF0Y2ggYWxsIHN1YnJvdXRlcykuIFdlIHdhbnQgYm90aCB0aGlzIHJvdXRlIGFuZCB0aGUgbm9ybWFsIHJvdXRlIHdpdGhvdXQgL3twcm94eSt9LFxuICAgICAgICAvLyBzaW5jZSAve3Byb3h5K30gd2lsbCBvbmx5IG1hdGNoIHRoZSBiYXNlIHBhdGggd2l0aCB0cmFpbGluZyBzbGFzaC5cbiAgICAgICAgcm91dGVQYXRocy5wdXNoKFxuICAgICAgICAgIHJvdXRlLnBhdGggKyAocm91dGUucGF0aCA9PT0gXCIvXCIgPyBcIlwiIDogXCIvXCIpICsgXCJ7cHJveHkrfVwiLFxuICAgICAgICApXG4gICAgICB9XG5cbiAgICAgIGZvciAoY29uc3Qgcm91dGVQYXRoIG9mIHJvdXRlUGF0aHMpIHtcbiAgICAgICAgLy8gVGhlIHByZXZpb3VzIHZlcnNpb24gb2YgdGhlIEFQSSBHYXRld2F5IGNvbnN0cnVjdCBoYWQgYSBzaW5nbGUgcm91dGUgd2l0aCB0aGUgSURcbiAgICAgICAgLy8gJ0RlZmF1bHRQcm94eVJvdXRlJywgYW5kIGFsbCBnYXRld2F5cyB3ZSB1c2VkIGV4cG9zZWQgdGhlIHJvdXRlIC97cHJveHkrfS4gV2hlbiB0cnlpbmcgdG9cbiAgICAgICAgLy8gY2hhbmdlIHRvIHRoaXMgbmV3IHZlcnNpb24gb2YgdGhlIGNvbnN0cnVjdCwgZGVwbG95bWVudCBmYWlsZWQgZm9yIGdhdGV3YXlzIGV4cG9zaW5nXG4gICAgICAgIC8vIC97cHJveHkrfSwgYmVjYXVzZSBcImEgcm91dGUgd2l0aCB0aGF0IGtleSBbaS5lLiBwYXRoICsgbWV0aG9kXSBhbHJlYWR5IGV4aXN0c1wiLiBXZSB0aGlua1xuICAgICAgICAvLyB0aGlzIG1heSBiZSBiZWNhdXNlIHdlIHVzZWQgdGhlIHNhbWUgcm91dGUgcGF0aCwgYnV0IHdpdGggYSBuZXcgcm91dGUgSUQsIGNhdXNpbmdcbiAgICAgICAgLy8gY29uZnVzaW9uIGZvciBDbG91ZEZvcm1hdGlvbi4gU28gd2Uga2VlcCB0aGUgb2xkIHJvdXRlIElEIGhlcmUgZm9yIC97cHJveHkrfS5cbiAgICAgICAgY29uc3Qgcm91dGVJZCA9XG4gICAgICAgICAgcm91dGVQYXRoID09PSBcIi97cHJveHkrfVwiID8gXCJEZWZhdWx0UHJveHlSb3V0ZVwiIDogYFJvdXRlLSR7cm91dGVQYXRofWBcblxuICAgICAgICB0aGlzLnJvdXRlcy5wdXNoKFxuICAgICAgICAgIG5ldyBhcGlndy5IdHRwUm91dGUodGhpcywgcm91dGVJZCwge1xuICAgICAgICAgICAgaHR0cEFwaTogYXBpLFxuICAgICAgICAgICAgaW50ZWdyYXRpb246IGludGVncmF0aW9uLFxuICAgICAgICAgICAgYXV0aG9yaXplcjogYXV0aG9yaXplcixcbiAgICAgICAgICAgIHJvdXRlS2V5OiBhcGlndy5IdHRwUm91dGVLZXkud2l0aChcbiAgICAgICAgICAgICAgcm91dGVQYXRoLFxuICAgICAgICAgICAgICByb3V0ZS5tZXRob2RcbiAgICAgICAgICAgICAgICA/IGFwaWd3Lkh0dHBNZXRob2Rbcm91dGUubWV0aG9kXVxuICAgICAgICAgICAgICAgIDogYXBpZ3cuSHR0cE1ldGhvZC5BTlksXG4gICAgICAgICAgICApLFxuICAgICAgICAgIH0pLFxuICAgICAgICApXG4gICAgICB9XG4gICAgfVxuXG4gICAgdGFnUmVzb3VyY2VzKHRoaXMsICgpID0+ICh7IHNlcnZpY2U6IHByb3BzLmRucy5zdWJkb21haW4gfSkpXG4gIH1cblxuICAvKiogQHRocm93cyBFcnJvciAqL1xuICBwcml2YXRlIHN0YXRpYyB2YWxpZGF0ZVByb3BzKFxuICAgIHByb3BzOiBBcGlHYXRld2F5UHJvcHMsXG4gICAgaWQ6IHN0cmluZyxcbiAgICBzdGFjazogY2RrLlN0YWNrLFxuICApIHtcbiAgICBmb3IgKGNvbnN0IHJvdXRlIG9mIHByb3BzLnJvdXRlcykge1xuICAgICAgaWYgKCFyb3V0ZS5pbnRlZ3JhdGlvbiAmJiAhcHJvcHMuZGVmYXVsdEludGVncmF0aW9uKSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcihcbiAgICAgICAgICBgTm8gaW50ZWdyYXRpb24gZGVmaW5lZCBmb3Igcm91dGUgJyR7cm91dGUucGF0aH0nLCBhbmQgbm8gZGVmYXVsdCBpbnRlZ3JhdGlvbiBzcGVjaWZpZWQgZm9yIHRoZSBnYXRld2F5YCxcbiAgICAgICAgKVxuICAgICAgfVxuICAgICAgaWYgKCFyb3V0ZS5hdXRob3JpemF0aW9uICYmICFwcm9wcy5kZWZhdWx0QXV0aG9yaXphdGlvbikge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoXG4gICAgICAgICAgYE5vIGF1dGhvcml6YXRpb24gZGVmaW5lZCBmb3Igcm91dGUgJyR7cm91dGUucGF0aH0nLCBhbmQgbm8gZGVmYXVsdCBhdXRob3JpemF0aW9uIHNwZWNpZmllZCBmb3IgdGhlIGdhdGV3YXlgLFxuICAgICAgICApXG4gICAgICB9XG4gICAgICBpZiAoIXJvdXRlLnBhdGguc3RhcnRzV2l0aChcIi9cIikpIHtcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKFxuICAgICAgICAgIGBJbnZhbGlkIHBhdGggJyR7cm91dGUucGF0aH0nOiBwYXRocyBtdXN0IGJlZ2luIHdpdGggJy8nICh1c2UgJy8nIGZvciByb290IHBhdGgpYCxcbiAgICAgICAgKVxuICAgICAgfVxuICAgICAgaWYgKHJvdXRlLnBhdGggIT09IFwiL1wiICYmIHJvdXRlLnBhdGguZW5kc1dpdGgoXCIvXCIpKSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcihcbiAgICAgICAgICBgSW52YWxpZCBwYXRoICcke3JvdXRlLnBhdGh9JzogcGF0aHMgY2Fubm90IGVuZCB3aXRoICcvJyAoZXhjZXB0IHJvb3QgcGF0aClgLFxuICAgICAgICApXG4gICAgICB9XG4gICAgICBpZiAoXG4gICAgICAgIHJvdXRlLmludGVncmF0aW9uPy50eXBlID09PSBcIkFMQlwiICYmXG4gICAgICAgIHJvdXRlLmludGVncmF0aW9uLmhvc3ROYW1lLnN0YXJ0c1dpdGgoXCJodHRwczpcIilcbiAgICAgICkge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoXG4gICAgICAgICAgYFRoZSBhbGJJbnRlZ3JhdGlvbi5ob3N0SHR0cHNOYW1lIHNob3VsZCBub3QgaW5jbHVkZSBhIHByb3RvY29sOiAke3JvdXRlLmludGVncmF0aW9uLmhvc3ROYW1lfWAsXG4gICAgICAgIClcbiAgICAgIH1cbiAgICB9XG4gICAgaWYgKHByb3BzLmRucy5zdWJkb21haW4gPT09IFwiXCIgfHwgcHJvcHMuZG5zLnN1YmRvbWFpbi5pbmNsdWRlcyhcIiBcIikpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcihcbiAgICAgICAgYFN1YmRvbWFpbiBtdXN0IGJlIHNldCwgYW5kIG5vdCBjb250YWluIHNwYWNlczogJHtwcm9wcy5kbnMuc3ViZG9tYWlufWAsXG4gICAgICApXG4gICAgfVxuICAgIGlmIChwcm9wcy50aHJvdHRsaW5nPy5idXJzdCAmJiBwcm9wcy50aHJvdHRsaW5nLmJ1cnN0ID4gNV8wMDApIHtcbiAgICAgIGNvbnNvbGUud2FybihcbiAgICAgICAgYOKaoO+4jyBZb3VyIHRocm90dGxpbmcgYnVyc3QgbGltaXQgJyR7cHJvcHMudGhyb3R0bGluZy5idXJzdH0nIGlzIGhpZ2hlciB0aGFuIHRoZSBBV1MgQWNjb3VudCBsaW1pdC4gTWFrZSBzdXJlIHlvdXIgYWNjb3VudCBoYXMgdXBncmFkZWQgdGhpcyBxdW90YSEgYCxcbiAgICAgICAgc3RhY2ssXG4gICAgICAgIGlkLFxuICAgICAgKVxuICAgIH1cbiAgICBpZiAocHJvcHMudGhyb3R0bGluZz8ucmF0ZSAmJiBwcm9wcy50aHJvdHRsaW5nLnJhdGUgPiAxMF8wMDApIHtcbiAgICAgIGNvbnNvbGUud2FybihcbiAgICAgICAgYOKaoO+4jyBZb3VyIHRocm90dGxpbmcgcmF0ZSBsaW1pdCAnJHtwcm9wcy50aHJvdHRsaW5nLnJhdGV9JyBpcyBoaWdoZXIgdGhhbiB0aGUgQVdTIEFjY291bnQgbGltaXQuIE1ha2Ugc3VyZSB5b3VyIGFjY291bnQgaGFzIHVwZ3JhZGVkIHRoaXMgcXVvdGEhIGAsXG4gICAgICAgIHN0YWNrLFxuICAgICAgICBpZCxcbiAgICAgIClcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogVGhlIGF1dGhvcml6ZXIgb25seSBhY2NlcHRzIHJlcXVlc3RzIGZyb20gZXh0ZXJuYWwgdXNlcnMgdGhhdCBhcmUgYXV0aG9yaXplZC5cbiAgICogVW5hdXRob3JpemVkIHVzZXJzIGFyZSBzdG9wcGVkIGluIHRoZSBBUEktR1csIGFuZCBub3QgZm9yd2FyZGVkIHRvIHRoZSBpbnRlZ3JhdGlvbi5cbiAgICovXG4gIHByaXZhdGUgY3JlYXRlQXV0aG9yaXplcjxBdXRoU2NvcGVzVCBleHRlbmRzIHN0cmluZz4oXG4gICAgaWQ6IHN0cmluZyxcbiAgICBhdXRob3JpemF0aW9uOiBBdXRob3JpemF0aW9uUHJvcHM8QXV0aFNjb3Blc1Q+LFxuICApOiBhcGlndy5JSHR0cFJvdXRlQXV0aG9yaXplciB8IHVuZGVmaW5lZCB7XG4gICAgc3dpdGNoIChhdXRob3JpemF0aW9uLnR5cGUpIHtcbiAgICAgIGNhc2UgXCJOT05FXCI6IHtcbiAgICAgICAgcmV0dXJuIHVuZGVmaW5lZFxuICAgICAgfVxuICAgICAgY2FzZSBcIklBTVwiOiB7XG4gICAgICAgIC8vIEFQSSBHYXRld2F5IGludm9rZXMgeW91ciBBUEkgcm91dGUgb25seSBpZiB0aGUgY2xpZW50IGhhcyBleGVjdXRlLWFwaSBwZXJtaXNzaW9uIGZvciB0aGUgcm91dGUuXG4gICAgICAgIC8vIFRoZSBjbGllbnQgaGFzIHRvIHVzZSBBV1MgU2lnVjQgdG8gaWRlbnRpZnkgdGhlbXNlbHZlcyBpbiB0aGUgcmVxdWVzdC5cbiAgICAgICAgLy8gUmVhZCB0aGlzIHBhZ2UgZm9yIGhlbHAgd2l0aCBJQU0gYW5kIEFQSS1HVyBodHRwczovL2RvY3MuYXdzLmFtYXpvbi5jb20vYXBpZ2F0ZXdheS9sYXRlc3QvZGV2ZWxvcGVyZ3VpZGUvc2VjdXJpdHlfaWFtX3NlcnZpY2Utd2l0aC1pYW0uaHRtbFxuICAgICAgICAvLyBOb3RlIHRoYXQgW3Jlc291cmNlIHBvbGljaWVzIGFyZSBub3Qgc3VwcG9ydGVkIHlldCBmb3IgSFRUUF0oaHR0cHM6Ly9kb2NzLmF3cy5hbWF6b24uY29tL2FwaWdhdGV3YXkvbGF0ZXN0L2RldmVsb3Blcmd1aWRlL2h0dHAtYXBpLWFjY2Vzcy1jb250cm9sLWlhbS5odG1sKVxuICAgICAgICByZXR1cm4gbmV3IGF1dGhvcml6ZXJzLkh0dHBJYW1BdXRob3JpemVyKClcbiAgICAgIH1cbiAgICAgIGNhc2UgXCJDT0dOSVRPX1VTRVJfUE9PTFwiOiB7XG4gICAgICAgIC8vIFdlIHVzZSBhIGN1c3RvbSBsYW1iZGEgYXV0aG9yaXplciBoZXJlIGluc3RlYWQgb2YgdGhlIGBIdHRwVXNlclBvb2xBdXRob3JpemVyYCBwcm92aWRlZFxuICAgICAgICAvLyBieSBDREssIGluIG9yZGVyIHRvIHN1cHBvcnQgYHJlcXVpcmVkU2NvcGVgIGFuZCBzZXR0aW5nIG9mIGN1c3RvbSBjb250ZXh0IHZhcmlhYmxlcy5cbiAgICAgICAgY29uc3QgYXV0aG9yaXplciA9IG5ldyBDb2duaXRvVXNlclBvb2xBdXRob3JpemVyKFxuICAgICAgICAgIHRoaXMsXG4gICAgICAgICAgaWQgKyBcIkxhbWJkYVwiLFxuICAgICAgICAgIGF1dGhvcml6YXRpb24sXG4gICAgICAgIClcblxuICAgICAgICByZXR1cm4gbmV3IGF1dGhvcml6ZXJzLkh0dHBMYW1iZGFBdXRob3JpemVyKGlkLCBhdXRob3JpemVyLmxhbWJkYSwge1xuICAgICAgICAgIHJlc3BvbnNlVHlwZXM6IGF1dGhvcml6ZXIucmVzcG9uc2VUeXBlcyxcbiAgICAgICAgICAvLyBNYXggMWgsIG9yIGRpc2FibGVkICgwcykuIFRoZSB2YWx1ZSBvbmx5IG1hdHRlcnMgd2hlbiBpbnZhbGlkYXRpbmcvZHluYW1pYyBjcmVkZW50aWFsc1xuICAgICAgICAgIHJlc3VsdHNDYWNoZVR0bDogY2RrLkR1cmF0aW9uLmhvdXJzKDEpLFxuICAgICAgICB9KVxuICAgICAgfVxuICAgICAgY2FzZSBcIkJBU0lDX0FVVEhcIjoge1xuICAgICAgICBjb25zdCBhdXRob3JpemVyID0gbmV3IEJhc2ljQXV0aEF1dGhvcml6ZXIoXG4gICAgICAgICAgdGhpcyxcbiAgICAgICAgICBpZCArIFwiTGFtYmRhXCIsXG4gICAgICAgICAgYXV0aG9yaXphdGlvbixcbiAgICAgICAgKVxuXG4gICAgICAgIHJldHVybiBuZXcgYXV0aG9yaXplcnMuSHR0cExhbWJkYUF1dGhvcml6ZXIoaWQsIGF1dGhvcml6ZXIubGFtYmRhLCB7XG4gICAgICAgICAgcmVzcG9uc2VUeXBlczogYXV0aG9yaXplci5yZXNwb25zZVR5cGVzLFxuICAgICAgICAgIC8vIE1heCAxaCwgb3IgZGlzYWJsZWQgKDBzKS4gVGhlIHZhbHVlIG9ubHkgbWF0dGVycyB3aGVuIGludmFsaWRhdGluZy9keW5hbWljIGNyZWRlbnRpYWxzXG4gICAgICAgICAgcmVzdWx0c0NhY2hlVHRsOiBjZGsuRHVyYXRpb24ubWludXRlcygzMCksXG4gICAgICAgIH0pXG4gICAgICB9XG4gICAgICBjYXNlIFwiQ09HTklUT19VU0VSX1BPT0xfT1JfQkFTSUNfQVVUSFwiOiB7XG4gICAgICAgIGNvbnN0IGF1dGhvcml6ZXIgPSBuZXcgQ29nbml0b1VzZXJQb29sT3JCYXNpY0F1dGhBdXRob3JpemVyKFxuICAgICAgICAgIHRoaXMsXG4gICAgICAgICAgaWQgKyBcIkxhbWJkYVwiLFxuICAgICAgICAgIGF1dGhvcml6YXRpb24sXG4gICAgICAgIClcblxuICAgICAgICByZXR1cm4gbmV3IGF1dGhvcml6ZXJzLkh0dHBMYW1iZGFBdXRob3JpemVyKGlkLCBhdXRob3JpemVyLmxhbWJkYSwge1xuICAgICAgICAgIHJlc3BvbnNlVHlwZXM6IGF1dGhvcml6ZXIucmVzcG9uc2VUeXBlcyxcbiAgICAgICAgICAvLyBNYXggMWgsIG9yIGRpc2FibGVkICgwcykuIFRoZSB2YWx1ZSBvbmx5IG1hdHRlcnMgd2hlbiBpbnZhbGlkYXRpbmcvZHluYW1pYyBjcmVkZW50aWFsc1xuICAgICAgICAgIHJlc3VsdHNDYWNoZVR0bDogY2RrLkR1cmF0aW9uLmhvdXJzKDEpLFxuICAgICAgICB9KVxuICAgICAgfVxuICAgIH1cbiAgfVxuXG4gIHByaXZhdGUgY3JlYXRlSW50ZWdyYXRpb24oXG4gICAgaW50ZWdyYXRpb246IEludGVncmF0aW9uUHJvcHMsXG4gICAgZG5zOiBBcGlHYXRld2F5UHJvcHNbXCJkbnNcIl0sXG4gICk6IGFwaWd3Lkh0dHBSb3V0ZUludGVncmF0aW9uIHtcbiAgICBzd2l0Y2ggKGludGVncmF0aW9uLnR5cGUpIHtcbiAgICAgIGNhc2UgXCJBTEJcIjoge1xuICAgICAgICAvLyBUaGUgVlBDTGluayBjb25uZWN0cyB0aGUgaW50ZWdyYXRpb24gaW50byB0aGUgQUxCJ3MgVlBDLiBNdXN0IGJlIG1hbnVhbGx5IGNyZWF0ZWQgd2hlblxuICAgICAgICAvLyB0aGUgVlBDIGlzIGltcG9ydGVkLCB3aGljaCBpcyB0aGUgY2FzZSB3aGVuIHVzaW5nIENvcmVQbGF0Zm9ybUNvbnN1bWVyLlxuICAgICAgICBjb25zdCB2cGNMaW5rID0gbmV3IGFwaWd3LlZwY0xpbmsodGhpcywgXCJBbGJWcGNMaW5rXCIsIHtcbiAgICAgICAgICB2cGM6IGludGVncmF0aW9uLnZwYyxcbiAgICAgICAgICBzZWN1cml0eUdyb3VwczogW2ludGVncmF0aW9uLnNlY3VyaXR5R3JvdXBdLFxuICAgICAgICAgIHN1Ym5ldHM6IGludGVncmF0aW9uLnZwYy5zZWxlY3RTdWJuZXRzKCksIC8vIFRoaXMgY29ycmVjdGx5IHNlbGVjdHMgdGhlIHByaXZhdGUgc3VibmV0c1xuICAgICAgICB9KVxuXG4gICAgICAgIGNvbnN0IHBhcmFtZXRlck1hcHBpbmcgPSBuZXcgYXBpZ3cuUGFyYW1ldGVyTWFwcGluZygpXG4gICAgICAgICAgLyoqIFNlZSB7QGxpbmsgQWxiSW50ZWdyYXRpb25Qcm9wcy5ob3N0TmFtZX0gKi9cbiAgICAgICAgICAub3ZlcndyaXRlSGVhZGVyKFxuICAgICAgICAgICAgXCJIb3N0XCIsXG4gICAgICAgICAgICAvLyBUaGUgSG9zdCBoZWFkZXIgY2FuIE5PVCB1c2UgZHluYW1pYyBtYXBwaW5nLCBsaWtlICRjb250ZXh0LmRvbWFpbk5hbWUgZXRjLlxuICAgICAgICAgICAgYXBpZ3cuTWFwcGluZ1ZhbHVlLmN1c3RvbShpbnRlZ3JhdGlvbi5ob3N0TmFtZSksXG4gICAgICAgICAgKVxuICAgICAgICBpZiAoaW50ZWdyYXRpb24ubWFwUGFyYW1ldGVycyAhPT0gdW5kZWZpbmVkKSB7XG4gICAgICAgICAgaW50ZWdyYXRpb24ubWFwUGFyYW1ldGVycyhwYXJhbWV0ZXJNYXBwaW5nKVxuICAgICAgICB9XG5cbiAgICAgICAgcmV0dXJuIG5ldyBpbnRlZ3JhdGlvbnMuSHR0cEFsYkludGVncmF0aW9uKFxuICAgICAgICAgIFwiQWxiSW50ZWdyYXRpb24tXCIgKyBkbnMuc3ViZG9tYWluLFxuICAgICAgICAgIGludGVncmF0aW9uLmxvYWRCYWxhbmNlckxpc3RlbmVyLFxuICAgICAgICAgIHtcbiAgICAgICAgICAgIHNlY3VyZVNlcnZlck5hbWU6IGludGVncmF0aW9uLmhvc3ROYW1lLFxuICAgICAgICAgICAgdnBjTGluazogdnBjTGluayxcbiAgICAgICAgICAgIG1ldGhvZDogYXBpZ3cuSHR0cE1ldGhvZC5BTlksXG4gICAgICAgICAgICBwYXJhbWV0ZXJNYXBwaW5nLFxuICAgICAgICAgIH0sXG4gICAgICAgIClcbiAgICAgIH1cbiAgICAgIGNhc2UgXCJMYW1iZGFcIjoge1xuICAgICAgICByZXR1cm4gbmV3IGludGVncmF0aW9ucy5IdHRwTGFtYmRhSW50ZWdyYXRpb24oXG4gICAgICAgICAgXCJMYW1iZGFJbnRlZ3JhdGlvblwiLFxuICAgICAgICAgIGludGVncmF0aW9uLmxhbWJkYSxcbiAgICAgICAgICB7XG4gICAgICAgICAgICBwYXlsb2FkRm9ybWF0VmVyc2lvbjogYXBpZ3cuUGF5bG9hZEZvcm1hdFZlcnNpb24uVkVSU0lPTl8yXzAsXG4gICAgICAgICAgICBwYXJhbWV0ZXJNYXBwaW5nOiB1bmRlZmluZWQsXG4gICAgICAgICAgfSxcbiAgICAgICAgKVxuICAgICAgfVxuICAgICAgY2FzZSBcIlNRU1wiOiB7XG4gICAgICAgIC8vIEFQSS1HVyBkb2VzIG5vdCBoYXZlIGFjY2VzcyB0byBTUVMgYnkgZGVmYXVsdFxuICAgICAgICBjb25zdCByb2xlID0gbmV3IGlhbS5Sb2xlKFxuICAgICAgICAgIHRoaXMsXG4gICAgICAgICAgYEFwaUd3VG8ke2ludGVncmF0aW9uLnF1ZXVlLm5vZGUuaWR9U2VydmljZVJvbGVgLFxuICAgICAgICAgIHtcbiAgICAgICAgICAgIGRlc2NyaXB0aW9uOlxuICAgICAgICAgICAgICBcIkFsbG93cyBBUEktR1cgdG8gYWRkIG1lc3NhZ2VzIHRvIFwiICsgaW50ZWdyYXRpb24ucXVldWUucXVldWVBcm4sXG4gICAgICAgICAgICBhc3N1bWVkQnk6IG5ldyBpYW0uU2VydmljZVByaW5jaXBhbChcImFwaWdhdGV3YXkuYW1hem9uYXdzLmNvbVwiKSxcbiAgICAgICAgICB9LFxuICAgICAgICApXG4gICAgICAgIGludGVncmF0aW9uLnF1ZXVlLmdyYW50U2VuZE1lc3NhZ2VzKHJvbGUpXG5cbiAgICAgICAgbGV0IHBhcmFtZXRlck1hcHBpbmcgPSBuZXcgYXBpZ3cuUGFyYW1ldGVyTWFwcGluZygpXG4gICAgICAgICAgLy8gaHR0cHM6Ly9kb2NzLmF3cy5hbWF6b24uY29tL2FwaWdhdGV3YXkvbGF0ZXN0L2RldmVsb3Blcmd1aWRlL2h0dHAtYXBpLWRldmVsb3AtaW50ZWdyYXRpb25zLWF3cy1zZXJ2aWNlcy1yZWZlcmVuY2UuaHRtbCNTUVMtU2VuZE1lc3NhZ2VcbiAgICAgICAgICAuY3VzdG9tKFwiUXVldWVVcmxcIiwgaW50ZWdyYXRpb24ucXVldWUucXVldWVVcmwpXG4gICAgICAgICAgLmN1c3RvbShcIk1lc3NhZ2VCb2R5XCIsIFwiJHJlcXVlc3QuYm9keVwiKVxuICAgICAgICAgIC5jdXN0b20oXCJSZWdpb25cIiwgXCJldS13ZXN0LTFcIikgLy8gQ2hhbmdlIHRoaXMgaWYgdGhlIFNRUyBxdWV1ZSBpcyBpbiBhbm90aGVyIHJlZ2lvbiFcblxuICAgICAgICBpZiAoaW50ZWdyYXRpb24ubWVzc2FnZUF0dHJpYnV0ZXMpIHtcbiAgICAgICAgICBwYXJhbWV0ZXJNYXBwaW5nID0gcGFyYW1ldGVyTWFwcGluZy5jdXN0b20oXG4gICAgICAgICAgICBcIk1lc3NhZ2VBdHRyaWJ1dGVzXCIsXG4gICAgICAgICAgICBKU09OLnN0cmluZ2lmeShpbnRlZ3JhdGlvbi5tZXNzYWdlQXR0cmlidXRlcyksXG4gICAgICAgICAgKVxuICAgICAgICB9XG5cbiAgICAgICAgcmV0dXJuIG5ldyBTcXNSb3V0ZUludGVncmF0aW9uKFwiU3FzSW50ZWdyYXRpb25cIiwge1xuICAgICAgICAgIHR5cGU6IGFwaWd3Lkh0dHBJbnRlZ3JhdGlvblR5cGUuQVdTX1BST1hZLFxuICAgICAgICAgIHN1YnR5cGU6IGFwaWd3Lkh0dHBJbnRlZ3JhdGlvblN1YnR5cGUuU1FTX1NFTkRfTUVTU0FHRSxcbiAgICAgICAgICBjcmVkZW50aWFsczogYXBpZ3cuSW50ZWdyYXRpb25DcmVkZW50aWFscy5mcm9tUm9sZShyb2xlKSxcbiAgICAgICAgICBwYXJhbWV0ZXJNYXBwaW5nOiBwYXJhbWV0ZXJNYXBwaW5nLFxuICAgICAgICAgIHBheWxvYWRGb3JtYXRWZXJzaW9uOiBhcGlndy5QYXlsb2FkRm9ybWF0VmVyc2lvbi5WRVJTSU9OXzFfMCxcbiAgICAgICAgfSlcbiAgICAgIH1cbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogQWxsb3dzIGEgZ3JhbnRhYmxlIHRhcmdldCAocm9sZSwgdXNlciBldGMuKSBwZXJtaXNzaW9uIHRvIGludm9rZSB0aGUgQVBJLlxuICAgKiBPbmx5IHdvcmtzIHdoZW4gdXNpbmcgYElBTWAgYXMge0BsaW5rIEFwaUdhdGV3YXlSb3V0ZS5hdXRob3JpemF0aW9ufS5cbiAgICpcbiAgICogQHBhcmFtIHRhcmdldCBBIGdyYW50YWJsZSwgbGlrZSB7QGxpbmsgaWFtLlJvbGV9XG4gICAqL1xuICBwdWJsaWMgZ3JhbnRJbnZva2UodGFyZ2V0OiBpYW0uSUdyYW50YWJsZSkge1xuICAgIGZvciAoY29uc3Qgcm91dGVQcm9wcyBvZiB0aGlzLnByb3BzLnJvdXRlcykge1xuICAgICAgY29uc3QgYXV0aFR5cGUgPVxuICAgICAgICByb3V0ZVByb3BzLmF1dGhvcml6YXRpb24/LnR5cGUgPz8gdGhpcy5wcm9wcy5kZWZhdWx0QXV0aG9yaXphdGlvbj8udHlwZVxuXG4gICAgICBpZiAoYXV0aFR5cGUgIT09IFwiSUFNXCIpIHtcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKFxuICAgICAgICAgIGBDYW5ub3QgZ3JhbnQgaW52b2tlIGZvciBhbiBBUEkgR2F0ZXdheSB3aGVuIG5vdCB1c2luZyBJQU0gYXV0aCAoZm91bmQgYXV0aCAnJHthdXRoVHlwZX0nIG9uIHJvdXRlICcke3JvdXRlUHJvcHMucGF0aH0nKSBbJHtjZGsuU3RhY2sub2YodGhpcykuc3RhY2tOYW1lfV1gLFxuICAgICAgICApXG4gICAgICB9XG4gICAgfVxuXG4gICAgZm9yIChjb25zdCByb3V0ZSBvZiB0aGlzLnJvdXRlcykge1xuICAgICAgcm91dGUuZ3JhbnRJbnZva2UodGFyZ2V0KVxuICAgIH1cbiAgfVxufVxuXG4vKipcbiAqIENyZWF0ZXMgYSBjdXN0b20gZG9tYWluIGZvciB0aGUgQVBJLUdhdGV3YXksIGEgUm91dGU1MyByZWNvcmQgYW5kIGFuIEhUVFBTIGNlcnQuXG4gKiBAYXV0aG9yIEtyaXN0aWFuIFJla3N0YWQgPGtyZUBjYXByYWNvbnN1bHRpbmcubm8+XG4gKi9cbmNsYXNzIEN1c3RvbURvbWFpbiBleHRlbmRzIGNvbnN0cnVjdHMuQ29uc3RydWN0IHtcbiAgcHVibGljIHJlYWRvbmx5IGFwaUd3Q3VzdG9tRG9tYWluOiBhcGlndy5Eb21haW5OYW1lXG5cbiAgLyoqIFRoZSBGdWxseSBRdWFsaWZpZWQgRG9tYWluIE5hbWUgKEZRRE4pIGxpa2UgYHByb2R1Y3QucGxhdGZvcm0uZXhhbXBsZS5ub2AuICovXG4gIHB1YmxpYyByZWFkb25seSBjdXN0b21Eb21haW5OYW1lOiBzdHJpbmdcblxuICBjb25zdHJ1Y3RvcihcbiAgICBzY29wZTogY29uc3RydWN0cy5Db25zdHJ1Y3QsXG4gICAgaWQ6IHN0cmluZyxcbiAgICBwcm9wczogQXBpR2F0ZXdheURuc1Byb3BzLFxuICApIHtcbiAgICBzdXBlcihzY29wZSwgaWQpXG4gICAgdGhpcy5jdXN0b21Eb21haW5OYW1lID0gYCR7cHJvcHMuc3ViZG9tYWlufS4ke3Byb3BzLmhvc3RlZFpvbmUuem9uZU5hbWV9YFxuXG4gICAgLy8gQ2FuIGFsc28gdXNlIHdpbGRjYXJkIGNlcnRzIGluc3RlYWQhIENoZWFwZXJcbiAgICAvKiogQWxsb3dzIGV4dGVybmFsIHVzZXJzIHRvIGNvbm5lY3Qgd2l0aCBIVFRQUy4gKi9cbiAgICBjb25zdCBjdXN0b21Eb21haW5DZXJ0ID0gbmV3IGFjbS5DZXJ0aWZpY2F0ZSh0aGlzLCBcIkh0dHBzQ2VydGlmaWNhdGVcIiwge1xuICAgICAgZG9tYWluTmFtZTogdGhpcy5jdXN0b21Eb21haW5OYW1lLFxuICAgICAgdmFsaWRhdGlvbjogYWNtLkNlcnRpZmljYXRlVmFsaWRhdGlvbi5mcm9tRG5zKHByb3BzLmhvc3RlZFpvbmUpLFxuICAgIH0pXG5cbiAgICAvLyBOb3RlIHRoYXQgQVBJLUdXIGNhbiBhbHNvIHN1cHBvcnQgd2lsZGNhcmQgZG9tYWlucyEgaHR0cHM6Ly9kb2NzLmF3cy5hbWF6b24uY29tL2FwaWdhdGV3YXkvbGF0ZXN0L2RldmVsb3Blcmd1aWRlL2h0dHAtYXBpLWN1c3RvbS1kb21haW4tbmFtZXMuaHRtbCNodHRwLXdpbGRjYXJkLWN1c3RvbS1kb21haW4tbmFtZXNcbiAgICAvLyBCdXQgdGhpcyB3aWxsIG5vdCB3b3JrIHdoZW4gQVdTIGFjY291bnQgWCBoYXMgQ3VzdG9tRG9tYWluIGBzdGFnaW5nLnBsYXRmb3JtLmV4YW1wbGUubm9gIGFuZCBhY2NvdW50IFkgaGFzIEN1c3RvbURvbWFpbiBgKi5wbGF0Zm9ybS5leGFtcGxlLm5vYC5cbiAgICAvLyBOb3Qgc3VyZSBob3cgc3ViLXN1YmRvbWFpbnMgYXJlIGFmZmVjdGVkOiBgbXlzZXJ2aWNlLnN0YWdpbmcucGxhdGZvcm0uZXhhbXBsZS5ub2AgYW5kIGAqLnBsYXRmb3JtLmV4YW1wbGUubm9gLlxuICAgIHRoaXMuYXBpR3dDdXN0b21Eb21haW4gPSBuZXcgYXBpZ3cuRG9tYWluTmFtZShcbiAgICAgIHRoaXMsXG4gICAgICBcIkRvbWFpbk5hbWUtXCIgKyBwcm9wcy5zdWJkb21haW4sXG4gICAgICB7XG4gICAgICAgIGRvbWFpbk5hbWU6IHRoaXMuY3VzdG9tRG9tYWluTmFtZSxcbiAgICAgICAgY2VydGlmaWNhdGU6IGN1c3RvbURvbWFpbkNlcnQsXG4gICAgICAgIGVuZHBvaW50VHlwZTogYXBpZ3cuRW5kcG9pbnRUeXBlLlJFR0lPTkFMLFxuICAgICAgICBzZWN1cml0eVBvbGljeTogYXBpZ3cuU2VjdXJpdHlQb2xpY3kuVExTXzFfMixcbiAgICAgIH0sXG4gICAgKVxuXG4gICAgLy8gVGhpcyBtYWtlcyB0aGUgQVBJLUdXIHB1YmxpY2x5IGF2YWlsYWJsZSBvbiB0aGUgY3VzdG9tIGRvbWFpbiBuYW1lLlxuICAgIG5ldyByb3V0ZTUzLkFSZWNvcmQodGhpcywgXCJSb3V0ZTUzQVJlY29yZEFwaWd3QWxpYXNcIiwge1xuICAgICAgcmVjb3JkTmFtZTogcHJvcHMuc3ViZG9tYWluLFxuICAgICAgem9uZTogcHJvcHMuaG9zdGVkWm9uZSxcbiAgICAgIHRhcmdldDogcm91dGU1My5SZWNvcmRUYXJnZXQuZnJvbUFsaWFzKFxuICAgICAgICBuZXcgcm91dGU1M1RhcmdldHMuQXBpR2F0ZXdheXYyRG9tYWluUHJvcGVydGllcyhcbiAgICAgICAgICB0aGlzLmFwaUd3Q3VzdG9tRG9tYWluLnJlZ2lvbmFsRG9tYWluTmFtZSxcbiAgICAgICAgICB0aGlzLmFwaUd3Q3VzdG9tRG9tYWluLnJlZ2lvbmFsSG9zdGVkWm9uZUlkLFxuICAgICAgICApLFxuICAgICAgKSxcbiAgICAgIHR0bDogcHJvcHMudHRsID8/IGNkay5EdXJhdGlvbi5taW51dGVzKDUpLCAvLyBMb3cgVFRMIG1ha2VzIGl0IGVhc2llciB0byBkbyBjaGFuZ2VzXG4gICAgfSlcbiAgfVxufVxuXG4vKiogQWN0cyBhcyBnbHVlIChiZXR3ZWVuIHRoZSBpbnRlZ3JhdGlvbiBwcm9wcyBhbmQgdGhlIEh0dHBBcGkpIHdoZW4gY3JlYXRpbmcgYW4gU3FzSW50ZWdyYXRpb24uICovXG5jbGFzcyBTcXNSb3V0ZUludGVncmF0aW9uIGV4dGVuZHMgYXBpZ3cuSHR0cFJvdXRlSW50ZWdyYXRpb24ge1xuICAvKipcbiAgICogQHBhcmFtIGlkIFRoZSBpZCB1c2VkIGluIHRoZSB7QGxpbmsgYXBpZ3cuSHR0cEludGVncmF0aW9ufSBjb25zdHJ1Y3RcbiAgICogICAgY3JlYXRlZCBpbnRlcm5hbGx5IGJ5IHtAbGluayBhcGlndy5IdHRwUm91dGVJbnRlZ3JhdGlvbi5fYmluZFRvUm91dGV9LlxuICAgKiAgICBbU291cmNlIGNvZGVdKGh0dHBzOi8vZ2l0aHViLmNvbS9hd3MvYXdzLWNkay9ibG9iL2I1YWUzNzc4MmJjM2NiNjM3ZWVlZjlmYmIxZmJlMmM1ZWZkZmMwNjgvcGFja2FnZXMvJTQwYXdzLWNkay9hd3MtYXBpZ2F0ZXdheXYyL2xpYi9odHRwL2ludGVncmF0aW9uLnRzI0wzMjEpXG4gICAqIEBwYXJhbSBpbnRlZ3JhdGlvblByb3BzIFRoZSBwcm9wcyB0byBwYXNzIHRvIHRoZSB7QGxpbmsgYXBpZ3cuSHR0cEludGVncmF0aW9ufSBjb25zdHJ1Y3QuXG4gICAqL1xuICBjb25zdHJ1Y3RvcihcbiAgICBpZDogc3RyaW5nLFxuICAgIHByaXZhdGUgaW50ZWdyYXRpb25Qcm9wczogYXBpZ3cuSHR0cFJvdXRlSW50ZWdyYXRpb25Db25maWcsXG4gICkge1xuICAgIHN1cGVyKGlkKVxuICB9XG5cbiAgLyoqXG4gICAqIFRoaXMgc2VuZHMgdGhlIHByb3BlcnRpZXMgbmVlZGVkIGZvciBjcmVhdGluZyBhIHtAbGluayBhcGlndy5IdHRwSW50ZWdyYXRpb259IHRvIHRoZVxuICAgKiB7QGxpbmsgYXBpZ3cuSHR0cFJvdXRlSW50ZWdyYXRpb259LlxuICAgKi9cbiAgYmluZCgpOiBhcGlndy5IdHRwUm91dGVJbnRlZ3JhdGlvbkNvbmZpZyB7XG4gICAgLy8gVGhpcyBtZXRob2QgaXMgY2FsbGVkIGJ5OlxuICAgIC8vIGh0dHBzOi8vZ2l0aHViLmNvbS9hd3MvYXdzLWNkay9ibG9iL2I1YWUzNzc4MmJjM2NiNjM3ZWVlZjlmYmIxZmJlMmM1ZWZkZmMwNjgvcGFja2FnZXMvJTQwYXdzLWNkay9hd3MtYXBpZ2F0ZXdheXYyL2xpYi9odHRwL2ludGVncmF0aW9uLnRzI0wzMTlcbiAgICByZXR1cm4gdGhpcy5pbnRlZ3JhdGlvblByb3BzXG4gIH1cbn1cblxuLyoqXG4gKiBDcmVhdGVzIGEgY3VzdG9tIGF1dGhvcml6ZXIgbGFtYmRhIHdoaWNoIHJlYWRzIGBBdXRob3JpemF0aW9uOiBCZWFyZXIgPHRva2VuPmAgaGVhZGVyIGFuZFxuICogdmVyaWZpZXMgdGhlIHRva2VuIGFnYWluc3QgYSBDb2duaXRvIHVzZXIgcG9vbC5cbiAqL1xuY2xhc3MgQ29nbml0b1VzZXJQb29sQXV0aG9yaXplcjxcbiAgQXV0aFNjb3Blc1QgZXh0ZW5kcyBzdHJpbmcsXG4+IGV4dGVuZHMgY29uc3RydWN0cy5Db25zdHJ1Y3Qge1xuICBwdWJsaWMgcmVhZG9ubHkgbGFtYmRhOiBsYW1iZGEuSUZ1bmN0aW9uXG5cbiAgLyoqXG4gICAqIFVzZSBzaW1wbGUgcmVzcG9uc2UgdHlwZSAoYHsgaXNBdXRob3JpemVkOiB0cnVlL2ZhbHNlIH1gKSwgYXMgb3Bwb3NlZCB0byByZXR1cm5pbmcgYW4gSUFNXG4gICAqIFBvbGljeSBkb2N1bWVudC5cbiAgICovXG4gIHB1YmxpYyByZWFkb25seSByZXNwb25zZVR5cGVzOiBhdXRob3JpemVycy5IdHRwTGFtYmRhUmVzcG9uc2VUeXBlW10gPSBbXG4gICAgYXV0aG9yaXplcnMuSHR0cExhbWJkYVJlc3BvbnNlVHlwZS5TSU1QTEUsXG4gIF1cblxuICBjb25zdHJ1Y3RvcihcbiAgICBzY29wZTogY29uc3RydWN0cy5Db25zdHJ1Y3QsXG4gICAgaWQ6IHN0cmluZyxcbiAgICBwcm9wczogQ29nbml0b1VzZXJQb29sQXV0aG9yaXplclByb3BzPEF1dGhTY29wZXNUPixcbiAgKSB7XG4gICAgc3VwZXIoc2NvcGUsIGlkKVxuXG4gICAgdGhpcy5sYW1iZGEgPSBuZXcgbGFtYmRhTm9kZWpzLk5vZGVqc0Z1bmN0aW9uKHRoaXMsIFwiQXV0aG9yaXplckZ1bmN0aW9uXCIsIHtcbiAgICAgIGVudHJ5OiBwYXRoLmpvaW4oXG4gICAgICAgIF9fZGlybmFtZSxcbiAgICAgICAgXCJhdXRob3JpemVyLWxhbWJkYXMvY29nbml0by11c2VyLXBvb2wtYXV0aG9yaXplci50c1wiLFxuICAgICAgKSxcbiAgICAgIHJ1bnRpbWU6IGxhbWJkYS5SdW50aW1lLk5PREVKU18yMl9YLFxuICAgICAgdGltZW91dDogY2RrLkR1cmF0aW9uLnNlY29uZHMoNSksXG4gICAgICBlbnZpcm9ubWVudDoge1xuICAgICAgICBbXCJVU0VSX1BPT0xfSURcIl06IHByb3BzLnVzZXJQb29sLnVzZXJQb29sSWQsXG4gICAgICAgIFtcIlJFUVVJUkVEX1NDT1BFXCJdOiBwcm9wcy5yZXF1aXJlZFNjb3BlID8/IFwiXCIsXG4gICAgICAgIFtcIkNSRURFTlRJQUxTX0ZPUl9JTlRFUk5BTF9BVVRIT1JJWkFUSU9OXCJdOlxuICAgICAgICAgIHByb3BzLmNyZWRlbnRpYWxzRm9ySW50ZXJuYWxBdXRob3JpemF0aW9uXG4gICAgICAgICAgICA/IHByb3BzLmNyZWRlbnRpYWxzRm9ySW50ZXJuYWxBdXRob3JpemF0aW9uXG4gICAgICAgICAgICA6IFwiXCIsXG4gICAgICB9LFxuICAgIH0pXG5cbiAgICBpZiAocHJvcHMuY3JlZGVudGlhbHNGb3JJbnRlcm5hbEF1dGhvcml6YXRpb24pIHtcbiAgICAgIHNlY3JldHNtYW5hZ2VyLlNlY3JldC5mcm9tU2VjcmV0TmFtZVYyKFxuICAgICAgICBzY29wZSxcbiAgICAgICAgaWQgKyBcIkJhc2ljQXV0aFNlY3JldFwiLFxuICAgICAgICBwcm9wcy5jcmVkZW50aWFsc0ZvckludGVybmFsQXV0aG9yaXphdGlvbixcbiAgICAgICkuZ3JhbnRSZWFkKHRoaXMubGFtYmRhKVxuICAgIH1cbiAgfVxufVxuXG4vKipcbiAqIENyZWF0ZXMgYSBjdXN0b20gYXV0aG9yaXplciBsYW1iZGEgd2hpY2ggcmVhZHMgYEF1dGhvcml6YXRpb246IEJhc2ljIDxiYXNlNjQtZW5jb2RlZCBjcmVkZW50aWFscz5gXG4gKiBoZWFkZXIgYW5kIHZlcmlmaWVzIHRoZSBjcmVkZW50aWFscyBhZ2FpbnN0IGEgZ2l2ZW4gc2VjcmV0LlxuICovXG5jbGFzcyBCYXNpY0F1dGhBdXRob3JpemVyIGV4dGVuZHMgY29uc3RydWN0cy5Db25zdHJ1Y3Qge1xuICBwdWJsaWMgcmVhZG9ubHkgbGFtYmRhOiBsYW1iZGEuSUZ1bmN0aW9uXG5cbiAgLy8gU2ltcGxlIGlzIGB7IGlzQXV0aG9yaXplZDogdHJ1ZS9mYWxzZSB9YCwgYXMgb3Bwb3NlZCB0byByZXR1cm5pbmcgYW4gSUFNIFBvbGljeSBkb2N1bWVudFxuICBwdWJsaWMgcmVhZG9ubHkgcmVzcG9uc2VUeXBlczogYXV0aG9yaXplcnMuSHR0cExhbWJkYVJlc3BvbnNlVHlwZVtdID0gW1xuICAgIGF1dGhvcml6ZXJzLkh0dHBMYW1iZGFSZXNwb25zZVR5cGUuU0lNUExFLFxuICBdXG5cbiAgY29uc3RydWN0b3IoXG4gICAgc2NvcGU6IGNvbnN0cnVjdHMuQ29uc3RydWN0LFxuICAgIGlkOiBzdHJpbmcsXG4gICAgcHJvcHM6IEJhc2ljQXV0aEF1dGhvcml6ZXJQcm9wcyxcbiAgKSB7XG4gICAgc3VwZXIoc2NvcGUsIGlkKVxuXG4gICAgdGhpcy5sYW1iZGEgPSBuZXcgbGFtYmRhTm9kZWpzLk5vZGVqc0Z1bmN0aW9uKHRoaXMsIFwiQmFzaWNBdXRoTGFtYmRhXCIsIHtcbiAgICAgIGVudHJ5OiBwYXRoLmpvaW4oXG4gICAgICAgIF9fZGlybmFtZSxcbiAgICAgICAgXCJhdXRob3JpemVyLWxhbWJkYXMvYmFzaWMtYXV0aC1hdXRob3JpemVyLnRzXCIsXG4gICAgICApLFxuICAgICAgZGVzY3JpcHRpb246XG4gICAgICAgIFwiQW4gYXV0aG9yaXplciBmb3IgQVBJLUdhdGV3YXkgdGhhdCBjaGVja3MgQmFzaWMgQXV0aCBjcmVkZW50aWFscyBvbiByZXF1ZXN0c1wiLFxuICAgICAgcnVudGltZTogbGFtYmRhLlJ1bnRpbWUuTk9ERUpTXzIyX1gsXG4gICAgICBlbnZpcm9ubWVudDoge1xuICAgICAgICBbXCJDUkVERU5USUFMU19TRUNSRVRfTkFNRVwiXTogcHJvcHMuY3JlZGVudGlhbHNTZWNyZXROYW1lXG4gICAgICAgICAgPyBwcm9wcy5jcmVkZW50aWFsc1NlY3JldE5hbWVcbiAgICAgICAgICA6IFwiXCIsXG4gICAgICB9LFxuICAgIH0pXG5cbiAgICBpZiAocHJvcHMuY3JlZGVudGlhbHNTZWNyZXROYW1lKSB7XG4gICAgICBzZWNyZXRzbWFuYWdlci5TZWNyZXQuZnJvbVNlY3JldE5hbWVWMihcbiAgICAgICAgc2NvcGUsXG4gICAgICAgIGlkICsgXCJCYXNpY0F1dGhTZWNyZXRcIixcbiAgICAgICAgcHJvcHMuY3JlZGVudGlhbHNTZWNyZXROYW1lLFxuICAgICAgKS5ncmFudFJlYWQodGhpcy5sYW1iZGEpXG4gICAgfVxuICB9XG59XG5cbi8qKlxuICogQ3JlYXRlcyBhIGN1c3RvbSBhdXRob3JpemVyIGxhbWJkYSB3aGljaCBhbGxvd3MgYm90aDpcbiAqIC0gYEF1dGhvcml6YXRpb246IEJlYXJlciA8dG9rZW4+YCBoZWFkZXIsIGZvciB3aGljaCB0aGUgdG9rZW4gaXMgY2hlY2tlZCBhZ2FpbnN0IHRoZSBnaXZlblxuICogICBDb2duaXRvIHVzZXIgcG9vbFxuICogLSBgQXV0aG9yaXphdGlvbjogQmFzaWMgPGJhc2U2NC1lbmNvZGVkIGNyZWRlbnRpYWxzPmAgaGVhZGVyLCBmb3Igd2hpY2ggdGhlIGNyZWRlbnRpYWxzIGFyZVxuICogICBjaGVja2VkIGFnYWluc3QgdGhlIGNyZWRlbnRpYWxzIGZyb20gdGhlIGdpdmVuIGJhc2ljIGF1dGggc2VjcmV0IG5hbWVcbiAqXG4gKiBJZiBlaXRoZXIgb2YgdGhlc2UgYXJlIGdpdmVuIGFuZCB2YWxpZCwgdGhlIHJlcXVlc3QgaXMgYXV0aGVudGljYXRlZC5cbiAqL1xuY2xhc3MgQ29nbml0b1VzZXJQb29sT3JCYXNpY0F1dGhBdXRob3JpemVyPFxuICBBdXRoU2NvcGVzVCBleHRlbmRzIHN0cmluZyxcbj4gZXh0ZW5kcyBjb25zdHJ1Y3RzLkNvbnN0cnVjdCB7XG4gIHB1YmxpYyByZWFkb25seSBsYW1iZGE6IGxhbWJkYS5JRnVuY3Rpb25cblxuICAvKipcbiAgICogVXNlIHNpbXBsZSByZXNwb25zZSB0eXBlIChgeyBpc0F1dGhvcml6ZWQ6IHRydWUvZmFsc2UgfWApLCBhcyBvcHBvc2VkIHRvIHJldHVybmluZyBhbiBJQU1cbiAgICogUG9saWN5IGRvY3VtZW50LlxuICAgKi9cbiAgcHVibGljIHJlYWRvbmx5IHJlc3BvbnNlVHlwZXM6IGF1dGhvcml6ZXJzLkh0dHBMYW1iZGFSZXNwb25zZVR5cGVbXSA9IFtcbiAgICBhdXRob3JpemVycy5IdHRwTGFtYmRhUmVzcG9uc2VUeXBlLlNJTVBMRSxcbiAgXVxuXG4gIGNvbnN0cnVjdG9yKFxuICAgIHNjb3BlOiBjb25zdHJ1Y3RzLkNvbnN0cnVjdCxcbiAgICBpZDogc3RyaW5nLFxuICAgIHByb3BzOiBDb2duaXRvVXNlclBvb2xPckJhc2ljQXV0aEF1dGhvcml6ZXJQcm9wczxBdXRoU2NvcGVzVD4sXG4gICkge1xuICAgIHN1cGVyKHNjb3BlLCBpZClcblxuICAgIHRoaXMubGFtYmRhID0gbmV3IGxhbWJkYU5vZGVqcy5Ob2RlanNGdW5jdGlvbih0aGlzLCBcIkF1dGhvcml6ZXJGdW5jdGlvblwiLCB7XG4gICAgICBlbnRyeTogcGF0aC5qb2luKFxuICAgICAgICBfX2Rpcm5hbWUsXG4gICAgICAgIFwiYXV0aG9yaXplci1sYW1iZGFzL2NvZ25pdG8tdXNlci1wb29sLW9yLWJhc2ljLWF1dGgtYXV0aG9yaXplci50c1wiLFxuICAgICAgKSxcbiAgICAgIHJ1bnRpbWU6IGxhbWJkYS5SdW50aW1lLk5PREVKU18yMl9YLFxuICAgICAgdGltZW91dDogY2RrLkR1cmF0aW9uLnNlY29uZHMoNSksXG4gICAgICBlbnZpcm9ubWVudDoge1xuICAgICAgICBbXCJVU0VSX1BPT0xfSURcIl06IHByb3BzLnVzZXJQb29sID8gcHJvcHMudXNlclBvb2wudXNlclBvb2xJZCA6IFwiXCIsXG4gICAgICAgIFtcIlJFUVVJUkVEX1NDT1BFXCJdOiBwcm9wcy5yZXF1aXJlZFNjb3BlID8/IFwiXCIsXG4gICAgICAgIFtcIkJBU0lDX0FVVEhfQ1JFREVOVElBTFNfU0VDUkVUX05BTUVcIl06XG4gICAgICAgICAgcHJvcHMuYmFzaWNBdXRoQ3JlZGVudGlhbHNTZWNyZXROYW1lXG4gICAgICAgICAgICA/IHByb3BzLmJhc2ljQXV0aENyZWRlbnRpYWxzU2VjcmV0TmFtZVxuICAgICAgICAgICAgOiBcIlwiLFxuICAgICAgfSxcbiAgICB9KVxuXG4gICAgaWYgKHByb3BzLmJhc2ljQXV0aENyZWRlbnRpYWxzU2VjcmV0TmFtZSkge1xuICAgICAgc2VjcmV0c21hbmFnZXIuU2VjcmV0LmZyb21TZWNyZXROYW1lVjIoXG4gICAgICAgIHNjb3BlLFxuICAgICAgICBpZCArIFwiQmFzaWNBdXRoU2VjcmV0XCIsXG4gICAgICAgIHByb3BzLmJhc2ljQXV0aENyZWRlbnRpYWxzU2VjcmV0TmFtZSxcbiAgICAgICkuZ3JhbnRSZWFkKHRoaXMubGFtYmRhKVxuICAgIH1cbiAgfVxufVxuXG4vKipcbiAqIEEgc2xpZ2h0bHkgZXh0ZW5kZWQgdmVyc2lvbiBvZiB0aGUgW2RlZmF1bHQgSlNPTiBmb3JtYXQgc3VnZ2VzdGVkIGJ5IEFXU10oaHR0cHM6Ly9kb2NzLmF3cy5hbWF6b24uY29tL2FwaWdhdGV3YXkvbGF0ZXN0L2RldmVsb3Blcmd1aWRlL2h0dHAtYXBpLWxvZ2dpbmcuaHRtbCNodHRwLWFwaS1lbmFibGUtbG9nZ2luZy5leGFtcGxlcykuXG4gKi9cbmNvbnN0IGRlZmF1bHRBY2Nlc3NMb2dGb3JtYXQgPSB7XG4gIHJlcXVlc3RJZDogXCIkY29udGV4dC5yZXF1ZXN0SWRcIixcbiAgdXNlckFnZW50OiBcIiRjb250ZXh0LmlkZW50aXR5LnVzZXJBZ2VudFwiLFxuICBpcDogXCIkY29udGV4dC5pZGVudGl0eS5zb3VyY2VJcFwiLFxuICAvKiogQ0xGIGZvcm1hdDogYGRkL01NTS95eXl5OkhIOm1tOnNzICstaGhtbWAgKi9cbiAgcmVxdWVzdFRpbWU6IFwiJGNvbnRleHQucmVxdWVzdFRpbWVcIixcbiAgcmVxdWVzdFRpbWVFcG9jaDogXCIkY29udGV4dC5yZXF1ZXN0VGltZUVwb2NoXCIsXG4gIGRhdGFQcm9jZXNzZWQ6IFwiJGNvbnRleHQuZGF0YVByb2Nlc3NlZFwiLFxuICBodHRwTWV0aG9kOiBcIiRjb250ZXh0Lmh0dHBNZXRob2RcIixcbiAgcGF0aDogXCIkY29udGV4dC5wYXRoXCIsXG4gIHJvdXRlS2V5OiBcIiRjb250ZXh0LnJvdXRlS2V5XCIsXG4gIHN0YXR1czogXCIkY29udGV4dC5zdGF0dXNcIixcbiAgcHJvdG9jb2w6IFwiJGNvbnRleHQucHJvdG9jb2xcIixcbiAgcmVzcG9uc2VMZW5ndGg6IFwiJGNvbnRleHQucmVzcG9uc2VMZW5ndGhcIixcbiAgcmVzcG9uc2VMYXRlbmN5OiBcIiRjb250ZXh0LnJlc3BvbnNlTGF0ZW5jeVwiLFxuICBkb21haW5OYW1lOiBcIiRjb250ZXh0LmRvbWFpbk5hbWVcIixcbiAgLy8gIGhvc3RIZWFkZXJPdmVycmlkZTogXCIkY29udGV4dC5yZXF1ZXN0T3ZlcnJpZGUuaGVhZGVyLkhvc3RcIiwgLy9NYXBwaW5nIHRlbXBsYXRlIG92ZXJyaWRlcyBjYW5ub3QgYmUgdXNlZCB3aXRoIHByb3h5IGludGVncmF0aW9uIGVuZHBvaW50cywgd2hpY2ggbGFjayBkYXRhIG1hcHBpbmdzIGh0dHBzOi8vZG9jcy5hd3MuYW1hem9uLmNvbS9hcGlnYXRld2F5L2xhdGVzdC9kZXZlbG9wZXJndWlkZS9hcGlnYXRld2F5LW92ZXJyaWRlLXJlcXVlc3QtcmVzcG9uc2UtcGFyYW1ldGVycy5odG1sIzp+OnRleHQ9TWFwcGluZyUyMHRlbXBsYXRlJTIwb3ZlcnJpZGVzJTIwY2Fubm90JTIwYmUlMjB1c2VkJTIwd2l0aCUyMHByb3h5JTIwaW50ZWdyYXRpb24lMjBlbmRwb2ludHMlMkMlMjB3aGljaCUyMGxhY2slMjBkYXRhJTIwbWFwcGluZ3NcbiAgZXJyb3I6IHtcbiAgICB0eXBlOiBcIiRjb250ZXh0LmVycm9yLnJlc3BvbnNlVHlwZVwiLFxuICAgIGdhdGV3YXlFcnJvcjogXCIkY29udGV4dC5lcnJvci5tZXNzYWdlXCIsXG4gICAgaW50ZWdyYXRpb25FcnJvcjogXCIkY29udGV4dC5pbnRlZ3JhdGlvbi5lcnJvclwiLFxuICAgIGF1dGhvcml6ZXJFcnJvcjogXCIkY29udGV4dC5hdXRob3JpemVyLmVycm9yXCIsXG4gIH0sXG4gIGludGVncmF0aW9uOiB7XG4gICAgbGF0ZW5jeTogXCIkY29udGV4dC5pbnRlZ3JhdGlvbi5sYXRlbmN5XCIsXG4gICAgcmVxdWVzdElkOiBcIiRjb250ZXh0LmludGVncmF0aW9uLnJlcXVlc3RJZFwiLFxuICAgIHJlc3BvbnNlU3RhdHVzOiBcIiRjb250ZXh0LmludGVncmF0aW9uLnN0YXR1c1wiLFxuICB9LFxuICBhdXRoOiB7XG4gICAgaWFtOiB7XG4gICAgICB1c2VyQXJuOiBcIiRjb250ZXh0LmlkZW50aXR5LnVzZXJBcm5cIixcbiAgICAgIGF3c0FjY291bnQ6IFwiJGNvbnRleHQuaWRlbnRpdHkuYWNjb3VudElkXCIsXG4gICAgICBhd3NQcmluY2lwYWw6IFwiJGNvbnRleHQuaWRlbnRpdHkuY2FsbGVyXCIsXG4gICAgICBhd3NQcmluY2lwYWxPcmc6IFwiJGNvbnRleHQuaWRlbnRpdHkucHJpbmNpcGFsT3JnSWRcIixcbiAgICB9LFxuICAgIGJhc2ljOiB7IHVzZXI6IFwiJGNvbnRleHQuYXV0aG9yaXplci51c2VyXCIgfSxcbiAgfSxcbiAgYXdzRW5kcG9pbnRSZXF1ZXN0OiB7XG4gICAgaWQ6IFwiJGNvbnRleHQuYXdzRW5kcG9pbnRSZXF1ZXN0SWRcIixcbiAgICBpZDI6IFwiJGNvbnRleHQuYXdzRW5kcG9pbnRSZXF1ZXN0SWQyXCIsXG4gIH0sXG4gIC8vIEZvciBkYXRhZG9nXG4gIG1lc3NhZ2U6XG4gICAgXCIkY29udGV4dC5pZGVudGl0eS5zb3VyY2VJcCAtICRjb250ZXh0Lmh0dHBNZXRob2QgJGNvbnRleHQuZG9tYWluTmFtZSAkY29udGV4dC5wYXRoICgkY29udGV4dC5yb3V0ZUtleSkgLSAkY29udGV4dC5zdGF0dXMgWyRjb250ZXh0LnJlc3BvbnNlTGF0ZW5jeSBtc11cIixcbn1cblxuLyoqXG4gKiBFbmFibGVzIGFjY2VzcyBsb2dzIG9uIHRoZSBBUEktR2F0ZXdheS5cbiAqXG4gKiBAYXV0aG9yIEtyaXN0aWFuIFJla3N0YWQgPGtyZUBjYXByYWNvbnN1bHRpbmcubm8+XG4gKi9cbmNsYXNzIEFwaUdhdGV3YXlBY2Nlc3NMb2dzIGV4dGVuZHMgY29uc3RydWN0cy5Db25zdHJ1Y3Qge1xuICBwdWJsaWMgcmVhZG9ubHkgbG9nR3JvdXA6IGxvZ3MuTG9nR3JvdXBcblxuICBjb25zdHJ1Y3RvcihcbiAgICBzY29wZTogY29uc3RydWN0cy5Db25zdHJ1Y3QsXG4gICAgaWQ6IHN0cmluZyxcbiAgICBzdGFnZTogYXBpZ3cuQ2ZuU3RhZ2UsXG4gICAgcHJvcHM6IEFwaUdhdGV3YXlBY2Nlc3NMb2dzUHJvcHMgfCB1bmRlZmluZWQsXG4gICAgZGVmYXVsdEFjY2Vzc0xvZ0Zvcm1hdDogUmVjb3JkPHN0cmluZywgdW5rbm93bj4sXG4gICkge1xuICAgIHN1cGVyKHNjb3BlLCBpZClcblxuICAgIC8vIGxvZ0dyb3VwIGlzIHNldCB1cCB3aXRoIGhlbHAgZnJvbTogaHR0cHM6Ly9naXRodWIuY29tL2F3cy9hd3MtY2RrL2lzc3Vlcy8xMTEwMCNpc3N1ZWNvbW1lbnQtOTA0NjI3MDgxXG4gICAgLy8gTm90IHN1cmUgaWYgSFRUUCBBUEkgYWN0dWFsbHkgbmVlZHMgdGhlIHNlcnZpY2Ugcm9sZSB3aXRoIG1hbmFnZWQgcG9saWN5OiBodHRwczovL2RvY3MuYXdzLmFtYXpvbi5jb20vYXBpZ2F0ZXdheS9sYXRlc3QvZGV2ZWxvcGVyZ3VpZGUvaHR0cC1hcGktbG9nZ2luZy5odG1sXG4gICAgY29uc3QgYWNjZXNzTG9ncyA9IG5ldyBsb2dzLkxvZ0dyb3VwKHRoaXMsIFwiQWNjZXNzTG9nR3JvdXBcIiwge1xuICAgICAgcmV0ZW50aW9uOiBwcm9wcz8ucmV0ZW50aW9uLFxuICAgICAgcmVtb3ZhbFBvbGljeTogcHJvcHM/LnJlbW92YWxQb2xpY3kgPz8gY2RrLlJlbW92YWxQb2xpY3kuUkVUQUlOLFxuICAgICAgLy8gQWx3YXlzIHVzZSB0aGUgZGVmYXVsdCBlbmNyeXB0aW9uIGtleS4gT3RoZXJ3aXNlLCB0aGUga2V5IG5lZWRzIHNwZWNpYWwgcG9saWNpZXM6XG4gICAgICAvLyBodHRwczovL2RvY3MuYXdzLmFtYXpvbi5jb20vQW1hem9uQ2xvdWRXYXRjaC9sYXRlc3QvbG9ncy9lbmNyeXB0LWxvZy1kYXRhLWttcy5odG1sI2Ntay1wZXJtaXNzaW9uc1xuICAgICAgZW5jcnlwdGlvbktleTogdW5kZWZpbmVkLFxuICAgIH0pXG4gICAgdGhpcy5sb2dHcm91cCA9IGFjY2Vzc0xvZ3NcblxuICAgIHN0YWdlLmFjY2Vzc0xvZ1NldHRpbmdzID0ge1xuICAgICAgZGVzdGluYXRpb25Bcm46IGFjY2Vzc0xvZ3MubG9nR3JvdXBBcm4sXG4gICAgICBmb3JtYXQ6IEpTT04uc3RyaW5naWZ5KHByb3BzPy5hY2Nlc3NMb2dGb3JtYXQgPz8gZGVmYXVsdEFjY2Vzc0xvZ0Zvcm1hdCksXG4gICAgfVxuXG4gICAgY29uc3QgYXBpR3dMb2dzUm9sZSA9IG5ldyBpYW0uUm9sZShcbiAgICAgIHRoaXMsXG4gICAgICBcIkFwaUdhdGV3YXlQdXNoVG9DbG91ZHdhdGNoTG9nc1JvbGVcIixcbiAgICAgIHtcbiAgICAgICAgYXNzdW1lZEJ5OiBuZXcgaWFtLlNlcnZpY2VQcmluY2lwYWwoXCJhcGlnYXRld2F5LmFtYXpvbmF3cy5jb21cIiksXG4gICAgICAgIG1hbmFnZWRQb2xpY2llczogW1xuICAgICAgICAgIGlhbS5NYW5hZ2VkUG9saWN5LmZyb21Bd3NNYW5hZ2VkUG9saWN5TmFtZShcbiAgICAgICAgICAgIFwic2VydmljZS1yb2xlL0FtYXpvbkFQSUdhdGV3YXlQdXNoVG9DbG91ZFdhdGNoTG9nc1wiLFxuICAgICAgICAgICksXG4gICAgICAgIF0sXG4gICAgICB9LFxuICAgIClcblxuICAgIGFjY2Vzc0xvZ3MuZ3JhbnRXcml0ZShhcGlHd0xvZ3NSb2xlKVxuICB9XG59XG5cbi8qKiBSZXR1cm5zIGEgc2hvcnQgc2VtaS11bmlxdWUgaGFzaCBvZiB0aGUgZ2l2ZW4gc3RyaW5nLiAqL1xuZnVuY3Rpb24gc2hvcnRIYXNoKHN0cjogc3RyaW5nKTogc3RyaW5nIHtcbiAgLy8gU0hBLTEgaXMgbm8tbm8gd2hlbiB3ZSBuZWVkIGNyeXB0b2dyYXBoaWMgc2VjdXJpdHksIGJ1dCBoZXJlIHdlIGp1c3QgaXQgZm9yIHNob3J0ZW5pbmcgYSBuYW1lLFxuICAvLyB3aGljaCBpcyBmaW5lXG4gIHJldHVybiBjcmVhdGVIYXNoKFwic2hhMVwiKS51cGRhdGUoc3RyKS5kaWdlc3QoXCJoZXhcIikuc3Vic3RyaW5nKDAsIDEwKVxufVxuIl19