@liflig/cdk 3.0.22 → 3.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/api-gateway/authorizer-lambdas/basic-auth-authorizer.d.ts +15 -0
- package/lib/api-gateway/authorizer-lambdas/basic-auth-authorizer.js +61 -0
- package/lib/api-gateway/authorizer-lambdas/cognito-user-pool-authorizer.d.ts +48 -0
- package/lib/api-gateway/authorizer-lambdas/cognito-user-pool-authorizer.js +129 -0
- package/lib/api-gateway/authorizer-lambdas/cognito-user-pool-or-basic-auth-authorizer.d.ts +47 -0
- package/lib/api-gateway/authorizer-lambdas/cognito-user-pool-or-basic-auth-authorizer.js +136 -0
- package/lib/api-gateway/http-api-gateway.d.ts +500 -0
- package/lib/api-gateway/http-api-gateway.js +589 -0
- package/lib/api-gateway/index.d.ts +1 -0
- package/lib/api-gateway/index.js +2 -0
- package/lib/index.d.ts +2 -1
- package/lib/index.js +3 -2
- package/package.json +5 -4
|
@@ -0,0 +1,589 @@
|
|
|
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
|
+
* 2. One subdomain per API-GW / service
|
|
27
|
+
* 3. Use HTTP API, not REST
|
|
28
|
+
* 4. Use a $default stage with autodeploy
|
|
29
|
+
* 5. Support multiple routes (with possible `/{proxy+}` to let all sub-paths through)
|
|
30
|
+
* 6. 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
|
+
* When using `NodejsFunction` with `entry`, we point CDK to a file with our lambda code. This can
|
|
399
|
+
* either be a TypeScript or JavaScript file. When creating constructs in tests inside this library,
|
|
400
|
+
* the entry will point to the TypeScript file in the source code. But when a library consumer uses
|
|
401
|
+
* this, it will instead point to the transpiled JavaScript file. So to set the correct file
|
|
402
|
+
* extension, we must check if we're being run in the context of the TypeScript source code
|
|
403
|
+
* (src/api-gateway), otherwise we're being run by a consumer as transpiled JavaScript.
|
|
404
|
+
*/
|
|
405
|
+
const authorizerFileExtension = __dirname.endsWith("src/api-gateway")
|
|
406
|
+
? "ts"
|
|
407
|
+
: "js";
|
|
408
|
+
/**
|
|
409
|
+
* Creates a custom authorizer lambda which reads `Authorization: Bearer <token>` header and
|
|
410
|
+
* verifies the token against a Cognito user pool.
|
|
411
|
+
*/
|
|
412
|
+
class CognitoUserPoolAuthorizer extends constructs.Construct {
|
|
413
|
+
lambda;
|
|
414
|
+
/**
|
|
415
|
+
* Use simple response type (`{ isAuthorized: true/false }`), as opposed to returning an IAM
|
|
416
|
+
* Policy document.
|
|
417
|
+
*/
|
|
418
|
+
responseTypes = [
|
|
419
|
+
authorizers.HttpLambdaResponseType.SIMPLE,
|
|
420
|
+
];
|
|
421
|
+
constructor(scope, id, props) {
|
|
422
|
+
super(scope, id);
|
|
423
|
+
this.lambda = new lambdaNodejs.NodejsFunction(this, "AuthorizerFunction", {
|
|
424
|
+
entry: path.join(__dirname, `authorizer-lambdas/cognito-user-pool-authorizer.${authorizerFileExtension}`),
|
|
425
|
+
runtime: lambda.Runtime.NODEJS_22_X,
|
|
426
|
+
timeout: cdk.Duration.seconds(5),
|
|
427
|
+
environment: {
|
|
428
|
+
["USER_POOL_ID"]: props.userPool.userPoolId,
|
|
429
|
+
["REQUIRED_SCOPE"]: props.requiredScope ?? "",
|
|
430
|
+
["CREDENTIALS_FOR_INTERNAL_AUTHORIZATION"]: props.credentialsForInternalAuthorization
|
|
431
|
+
? props.credentialsForInternalAuthorization
|
|
432
|
+
: "",
|
|
433
|
+
},
|
|
434
|
+
});
|
|
435
|
+
if (props.credentialsForInternalAuthorization) {
|
|
436
|
+
secretsmanager.Secret.fromSecretNameV2(scope, id + "BasicAuthSecret", props.credentialsForInternalAuthorization).grantRead(this.lambda);
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
/**
|
|
441
|
+
* Creates a custom authorizer lambda which reads `Authorization: Basic <base64-encoded credentials>`
|
|
442
|
+
* header and verifies the credentials against a given secret.
|
|
443
|
+
*/
|
|
444
|
+
class BasicAuthAuthorizer extends constructs.Construct {
|
|
445
|
+
lambda;
|
|
446
|
+
// Simple is `{ isAuthorized: true/false }`, as opposed to returning an IAM Policy document
|
|
447
|
+
responseTypes = [
|
|
448
|
+
authorizers.HttpLambdaResponseType.SIMPLE,
|
|
449
|
+
];
|
|
450
|
+
constructor(scope, id, props) {
|
|
451
|
+
super(scope, id);
|
|
452
|
+
this.lambda = new lambdaNodejs.NodejsFunction(this, "BasicAuthLambda", {
|
|
453
|
+
entry: path.join(__dirname, `authorizer-lambdas/basic-auth-authorizer.${authorizerFileExtension}`),
|
|
454
|
+
description: "An authorizer for API-Gateway that checks Basic Auth credentials on requests",
|
|
455
|
+
runtime: lambda.Runtime.NODEJS_22_X,
|
|
456
|
+
environment: {
|
|
457
|
+
["CREDENTIALS_SECRET_NAME"]: props.credentialsSecretName
|
|
458
|
+
? props.credentialsSecretName
|
|
459
|
+
: "",
|
|
460
|
+
},
|
|
461
|
+
});
|
|
462
|
+
if (props.credentialsSecretName) {
|
|
463
|
+
secretsmanager.Secret.fromSecretNameV2(scope, id + "BasicAuthSecret", props.credentialsSecretName).grantRead(this.lambda);
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
/**
|
|
468
|
+
* Creates a custom authorizer lambda which allows both:
|
|
469
|
+
* - `Authorization: Bearer <token>` header, for which the token is checked against the given
|
|
470
|
+
* Cognito user pool
|
|
471
|
+
* - `Authorization: Basic <base64-encoded credentials>` header, for which the credentials are
|
|
472
|
+
* checked against the credentials from the given basic auth secret name
|
|
473
|
+
*
|
|
474
|
+
* If either of these are given and valid, the request is authenticated.
|
|
475
|
+
*/
|
|
476
|
+
class CognitoUserPoolOrBasicAuthAuthorizer extends constructs.Construct {
|
|
477
|
+
lambda;
|
|
478
|
+
/**
|
|
479
|
+
* Use simple response type (`{ isAuthorized: true/false }`), as opposed to returning an IAM
|
|
480
|
+
* Policy document.
|
|
481
|
+
*/
|
|
482
|
+
responseTypes = [
|
|
483
|
+
authorizers.HttpLambdaResponseType.SIMPLE,
|
|
484
|
+
];
|
|
485
|
+
constructor(scope, id, props) {
|
|
486
|
+
super(scope, id);
|
|
487
|
+
this.lambda = new lambdaNodejs.NodejsFunction(this, "AuthorizerFunction", {
|
|
488
|
+
entry: path.join(__dirname, `authorizer-lambdas/cognito-user-pool-or-basic-auth-authorizer.${authorizerFileExtension}`),
|
|
489
|
+
runtime: lambda.Runtime.NODEJS_22_X,
|
|
490
|
+
timeout: cdk.Duration.seconds(5),
|
|
491
|
+
environment: {
|
|
492
|
+
["USER_POOL_ID"]: props.userPool ? props.userPool.userPoolId : "",
|
|
493
|
+
["REQUIRED_SCOPE"]: props.requiredScope ?? "",
|
|
494
|
+
["BASIC_AUTH_CREDENTIALS_SECRET_NAME"]: props.basicAuthCredentialsSecretName
|
|
495
|
+
? props.basicAuthCredentialsSecretName
|
|
496
|
+
: "",
|
|
497
|
+
},
|
|
498
|
+
});
|
|
499
|
+
if (props.basicAuthCredentialsSecretName) {
|
|
500
|
+
secretsmanager.Secret.fromSecretNameV2(scope, id + "BasicAuthSecret", props.basicAuthCredentialsSecretName).grantRead(this.lambda);
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
/**
|
|
505
|
+
* 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).
|
|
506
|
+
*/
|
|
507
|
+
const defaultAccessLogFormat = {
|
|
508
|
+
requestId: "$context.requestId",
|
|
509
|
+
userAgent: "$context.identity.userAgent",
|
|
510
|
+
ip: "$context.identity.sourceIp",
|
|
511
|
+
/** CLF format: `dd/MMM/yyyy:HH:mm:ss +-hhmm` */
|
|
512
|
+
requestTime: "$context.requestTime",
|
|
513
|
+
requestTimeEpoch: "$context.requestTimeEpoch",
|
|
514
|
+
dataProcessed: "$context.dataProcessed",
|
|
515
|
+
httpMethod: "$context.httpMethod",
|
|
516
|
+
path: "$context.path",
|
|
517
|
+
routeKey: "$context.routeKey",
|
|
518
|
+
status: "$context.status",
|
|
519
|
+
protocol: "$context.protocol",
|
|
520
|
+
responseLength: "$context.responseLength",
|
|
521
|
+
responseLatency: "$context.responseLatency",
|
|
522
|
+
domainName: "$context.domainName",
|
|
523
|
+
// 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
|
|
524
|
+
error: {
|
|
525
|
+
type: "$context.error.responseType",
|
|
526
|
+
gatewayError: "$context.error.message",
|
|
527
|
+
integrationError: "$context.integration.error",
|
|
528
|
+
authorizerError: "$context.authorizer.error",
|
|
529
|
+
},
|
|
530
|
+
integration: {
|
|
531
|
+
latency: "$context.integration.latency",
|
|
532
|
+
requestId: "$context.integration.requestId",
|
|
533
|
+
responseStatus: "$context.integration.status",
|
|
534
|
+
},
|
|
535
|
+
auth: {
|
|
536
|
+
iam: {
|
|
537
|
+
userArn: "$context.identity.userArn",
|
|
538
|
+
awsAccount: "$context.identity.accountId",
|
|
539
|
+
awsPrincipal: "$context.identity.caller",
|
|
540
|
+
awsPrincipalOrg: "$context.identity.principalOrgId",
|
|
541
|
+
},
|
|
542
|
+
basic: { user: "$context.authorizer.user" },
|
|
543
|
+
},
|
|
544
|
+
awsEndpointRequest: {
|
|
545
|
+
id: "$context.awsEndpointRequestId",
|
|
546
|
+
id2: "$context.awsEndpointRequestId2",
|
|
547
|
+
},
|
|
548
|
+
// For datadog
|
|
549
|
+
message: "$context.identity.sourceIp - $context.httpMethod $context.domainName $context.path ($context.routeKey) - $context.status [$context.responseLatency ms]",
|
|
550
|
+
};
|
|
551
|
+
/**
|
|
552
|
+
* Enables access logs on the API-Gateway.
|
|
553
|
+
*
|
|
554
|
+
* @author Kristian Rekstad <kre@capraconsulting.no>
|
|
555
|
+
*/
|
|
556
|
+
class ApiGatewayAccessLogs extends constructs.Construct {
|
|
557
|
+
logGroup;
|
|
558
|
+
constructor(scope, id, stage, props, defaultAccessLogFormat) {
|
|
559
|
+
super(scope, id);
|
|
560
|
+
// logGroup is set up with help from: https://github.com/aws/aws-cdk/issues/11100#issuecomment-904627081
|
|
561
|
+
// 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
|
|
562
|
+
const accessLogs = new logs.LogGroup(this, "AccessLogGroup", {
|
|
563
|
+
retention: props?.retention,
|
|
564
|
+
removalPolicy: props?.removalPolicy ?? cdk.RemovalPolicy.RETAIN,
|
|
565
|
+
// Always use the default encryption key. Otherwise, the key needs special policies:
|
|
566
|
+
// https://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/encrypt-log-data-kms.html#cmk-permissions
|
|
567
|
+
encryptionKey: undefined,
|
|
568
|
+
});
|
|
569
|
+
this.logGroup = accessLogs;
|
|
570
|
+
stage.accessLogSettings = {
|
|
571
|
+
destinationArn: accessLogs.logGroupArn,
|
|
572
|
+
format: JSON.stringify(props?.accessLogFormat ?? defaultAccessLogFormat),
|
|
573
|
+
};
|
|
574
|
+
const apiGwLogsRole = new iam.Role(this, "ApiGatewayPushToCloudwatchLogsRole", {
|
|
575
|
+
assumedBy: new iam.ServicePrincipal("apigateway.amazonaws.com"),
|
|
576
|
+
managedPolicies: [
|
|
577
|
+
iam.ManagedPolicy.fromAwsManagedPolicyName("service-role/AmazonAPIGatewayPushToCloudWatchLogs"),
|
|
578
|
+
],
|
|
579
|
+
});
|
|
580
|
+
accessLogs.grantWrite(apiGwLogsRole);
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
/** Returns a short semi-unique hash of the given string. */
|
|
584
|
+
function shortHash(str) {
|
|
585
|
+
// SHA-1 is no-no when we need cryptographic security, but here we just it for shortening a name,
|
|
586
|
+
// which is fine
|
|
587
|
+
return createHash("sha1").update(str).digest("hex").substring(0, 10);
|
|
588
|
+
}
|
|
589
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaHR0cC1hcGktZ2F0ZXdheS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9hcGktZ2F0ZXdheS9odHRwLWFwaS1nYXRld2F5LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sS0FBSyxVQUFVLE1BQU0sWUFBWSxDQUFBO0FBQ3hDLE9BQU8sS0FBSyxHQUFHLE1BQU0sYUFBYSxDQUFBO0FBR2xDLE9BQU8sS0FBSyxNQUFNLE1BQU0sd0JBQXdCLENBQUE7QUFFaEQsT0FBTyxLQUFLLEtBQUssTUFBTSw4QkFBOEIsQ0FBQTtBQUNyRCxPQUFPLEtBQUssSUFBSSxNQUFNLHNCQUFzQixDQUFBO0FBQzVDLE9BQU8sS0FBSyxHQUFHLE1BQU0scUJBQXFCLENBQUE7QUFDMUMsT0FBTyxLQUFLLGNBQWMsTUFBTSxnQ0FBZ0MsQ0FBQTtBQUNoRSxPQUFPLEtBQUssWUFBWSxNQUFNLDJDQUEyQyxDQUFBO0FBQ3pFLE9BQU8sS0FBSyxXQUFXLE1BQU0sMENBQTBDLENBQUE7QUFFdkUsT0FBTyxLQUFLLFlBQVksTUFBTSwrQkFBK0IsQ0FBQTtBQUM3RCxPQUFPLEVBQUUsVUFBVSxFQUFFLE1BQU0sUUFBUSxDQUFBO0FBQ25DLE9BQU8sS0FBSyxPQUFPLE1BQU0seUJBQXlCLENBQUE7QUFDbEQsT0FBTyxLQUFLLGNBQWMsTUFBTSxpQ0FBaUMsQ0FBQTtBQUNqRSxPQUFPLEtBQUssR0FBRyxNQUFNLG9DQUFvQyxDQUFBO0FBQ3pELE9BQU8sRUFBRSxZQUFZLEVBQUUsTUFBTSxTQUFTLENBQUE7QUFDdEMsT0FBTyxLQUFLLElBQUksTUFBTSxNQUFNLENBQUE7QUFDNUIsT0FBTyxFQUFFLGFBQWEsRUFBRSxNQUFNLEtBQUssQ0FBQTtBQUVuQyxNQUFNLFVBQVUsR0FBRyxhQUFhLENBQUMsTUFBTSxDQUFDLElBQUksQ0FBQyxHQUFHLENBQUMsQ0FBQTtBQUNqRCxNQUFNLFNBQVMsR0FBRyxJQUFJLENBQUMsT0FBTyxDQUFDLFVBQVUsQ0FB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c0NvbnRlbnQiOlsiaW1wb3J0ICogYXMgY29uc3RydWN0cyBmcm9tIFwiY29uc3RydWN0c1wiXG5pbXBvcnQgKiBhcyBjZGsgZnJvbSBcImF3cy1jZGstbGliXCJcbmltcG9ydCB0eXBlICogYXMgZWxiIGZyb20gXCJhd3MtY2RrLWxpYi9hd3MtZWxhc3RpY2xvYWRiYWxhbmNpbmd2MlwiXG5pbXBvcnQgdHlwZSAqIGFzIGVjMiBmcm9tIFwiYXdzLWNkay1saWIvYXdzLWVjMlwiXG5pbXBvcnQgKiBhcyBsYW1iZGEgZnJvbSBcImF3cy1jZGstbGliL2F3cy1sYW1iZGFcIlxuaW1wb3J0IHR5cGUgKiBhcyBzcXMgZnJvbSBcImF3cy1jZGstbGliL2F3cy1zcXNcIlxuaW1wb3J0ICogYXMgYXBpZ3cgZnJvbSBcImF3cy1jZGstbGliL2F3cy1hcGlnYXRld2F5djJcIlxuaW1wb3J0ICogYXMgbG9ncyBmcm9tIFwiYXdzLWNkay1saWIvYXdzLWxvZ3NcIlxuaW1wb3J0ICogYXMgaWFtIGZyb20gXCJhd3MtY2RrLWxpYi9hd3MtaWFtXCJcbmltcG9ydCAqIGFzIHNlY3JldHNtYW5hZ2VyIGZyb20gXCJhd3MtY2RrLWxpYi9hd3Mtc2VjcmV0c21hbmFnZXJcIlxuaW1wb3J0ICogYXMgaW50ZWdyYXRpb25zIGZyb20gXCJhd3MtY2RrLWxpYi9hd3MtYXBpZ2F0ZXdheXYyLWludGVncmF0aW9uc1wiXG5pbXBvcnQgKiBhcyBhdXRob3JpemVycyBmcm9tIFwiYXdzLWNkay1saWIvYXdzLWFwaWdhdGV3YXl2Mi1hdXRob3JpemVyc1wiXG5pbXBvcnQgdHlwZSB7IElVc2VyUG9vbCB9IGZyb20gXCJhd3MtY2RrLWxpYi9hd3MtY29nbml0b1wiXG5pbXBvcnQgKiBhcyBsYW1iZGFOb2RlanMgZnJvbSBcImF3cy1jZGstbGliL2F3cy1sYW1iZGEtbm9kZWpzXCJcbmltcG9ydCB7IGNyZWF0ZUhhc2ggfSBmcm9tIFwiY3J5cHRvXCJcbmltcG9ydCAqIGFzIHJvdXRlNTMgZnJvbSBcImF3cy1jZGstbGliL2F3cy1yb3V0ZTUzXCJcbmltcG9ydCAqIGFzIHJvdXRlNTNUYXJnZXRzIGZyb20gXCJhd3MtY2RrLWxpYi9hd3Mtcm91dGU1My10YXJnZXRzXCJcbmltcG9ydCAqIGFzIGFjbSBmcm9tIFwiYXdzLWNkay1saWIvYXdzLWNlcnRpZmljYXRlbWFuYWdlclwiXG5pbXBvcnQgeyB0YWdSZXNvdXJjZXMgfSBmcm9tIFwiLi4vdGFnc1wiXG5pbXBvcnQgKiBhcyBwYXRoIGZyb20gXCJwYXRoXCJcbmltcG9ydCB7IGZpbGVVUkxUb1BhdGggfSBmcm9tIFwidXJsXCJcblxuY29uc3QgX19maWxlbmFtZSA9IGZpbGVVUkxUb1BhdGgoaW1wb3J0Lm1ldGEudXJsKVxuY29uc3QgX19kaXJuYW1lID0gcGF0aC5kaXJuYW1lKF9fZmlsZW5hbWUpXG5cbi8qKlxuICogUHJvcHMgZm9yIHRoZSB7QGxpbmsgQXBpR2F0ZXdheX0gY29uc3RydWN0LlxuICpcbiAqIEBhdXRob3IgS3Jpc3RpYW4gUmVrc3RhZCA8a3JlQGNhcHJhY29uc3VsdGluZy5ubz5cbiAqIEBhdXRob3IgSGVybWFubiBNw7hya3JpZCA8aGVtQGxpZmxpZy5ubz5cbiAqL1xuZXhwb3J0IHR5cGUgQXBpR2F0ZXdheVByb3BzPEF1dGhTY29wZXNUIGV4dGVuZHMgc3RyaW5nID0gc3RyaW5nPiA9IHtcbiAgLyoqIFNldHRpbmdzIGZvciB0aGUgZXh0ZXJuYWwtZmFjaW5nIHBhcnQgb2YgdGhlIEFQSS1HVy4gKi9cbiAgZG5zOiBBcGlHYXRld2F5RG5zUHJvcHNcblxuICAvKipcbiAgICogSWYgbm8gaW50ZWdyYXRpb24gaXMgc3BlY2lmaWVkIGZvciBhIHJvdXRlLCB0aGlzIGludGVncmF0aW9uIGlzIHVzZWQuXG4gICAqXG4gICAqIFNlZSB7QGxpbmsgSW50ZWdyYXRpb25Qcm9wc30gZm9yIHRoZSBhdmFpbGFibGUgb3B0aW9ucy5cbiAgICovXG4gIGRlZmF1bHRJbnRlZ3JhdGlvbj86IEludGVncmF0aW9uUHJvcHNcblxuICAvKipcbiAgICogSWYgbm8gYXV0aG9yaXphdGlvbiBpcyBzcGVjaWZpZWQgZm9yIGEgcm91dGUsIHRoaXMgYXV0aG9yaXphdGlvbiBpcyB1c2VkLlxuICAgKlxuICAgKiBTZWUge0BsaW5rIEF1dGhvcml6YXRpb25Qcm9wc30gZm9yIHRoZSBhdmFpbGFibGUgb3B0aW9ucy5cbiAgICovXG4gIGRlZmF1bHRBdXRob3JpemF0aW9uPzogQXV0aG9yaXphdGlvblByb3BzPEF1dGhTY29wZXNUPlxuXG4gIHJvdXRlczogQXBpR2F0ZXdheVJvdXRlPEF1dGhTY29wZXNUPltdXG5cbiAgLyoqXG4gICAqIFRoZSBBUEktR1cgYWNjZXNzIGxvZ3MgZm9yIHRoZSBgJGRlZmF1bHRgIHN0YWdlIGFyZSBrZXB0LlxuICAgKiBUaGlzIGhhcyBvcHRpb25zIGZvciB0aGUgbG9ncy5cbiAgICovXG4gIGFjY2Vzc0xvZ3M/OiBBcGlHYXRld2F5QWNjZXNzTG9nc1Byb3BzXG5cbiAgLyoqXG4gICAqIFNldCB0byBmYWxzZSB0byBkaXNhYmxlIHJvdXRlLWxldmVsIG1ldHJpY3MuXG4gICAqIFRoaXMgY2FuIGluY3JlYXNlIENsb3VkV2F0Y2ggY29zdHMgd2hlbiBub3QgZGlzYWJsZWQuXG4gICAqXG4gICAqIFNlZSBbQVdTOiBXb3JraW5nIHdpdGggbWV0cmljcyBmb3IgSFRUUCBBUElzXShodHRwczovL2RvY3MuYXdzLmFtYXpvbi5jb20vYXBpZ2F0ZXdheS9sYXRlc3QvZGV2ZWxvcGVyZ3VpZGUvaHR0cC1hcGktbWV0cmljcy5odG1sP2ljbXBpZD1hcGlnYXRld2F5X2NvbnNvbGVfaGVscCM6fjp0ZXh0PWFuZCUyMHN0YWdlJTIwSUQuLSxBcGlJZCUyQyUyMFN0YWdlJTJDJTIwUm91dGUsLUZpbHRlcnMlMjBBUEklMjBHYXRld2F5KVxuICAgKiBmb3IgbW9yZSBpbmZvLlxuICAgKlxuICAgKiBAZGVmYXVsdCB0cnVlXG4gICAqL1xuICBkZXRhaWxlZE1ldHJpY3M/OiBib29sZWFuXG5cbiAgLyoqXG4gICAqIFRocm90dGxpbmcgb2YgcmVxdWVzdHMuXG4gICAqIElmIG5vdCBzZXQsIHRoZSBBV1MgZGVmYXVsdCBvZiA1MDAwIGJ1cnN0IGFuZCAxMDAwMCByYXRlIGlzIHVzZWQuXG4gICAqXG4gICAqIFRoZSBkZWZhdWx0IHRocm90dGxpbmcgaXMgcGVyLWFjY291bnQsIGFuZCBjb3VudHMgYWxsIEFQSXMgaW4gdGhlIGFjY291bnQgYW5kIHJlZ2lvbi5cbiAgICogSWYgeW91IGhhdmUgYW5vdGhlciBBUEkgaW4gdGhpcyByZWdpb24gZ2V0dGluZyAxMF8wMDAgcmVxdWVzdCByYXRlLCBpdCBtYXkgaW1wYWN0IHRoaXMgQVBJIGFzIHdlbGwuXG4gICAqL1xuICB0aHJvdHRsaW5nPzoge1xuICAgIC8qKlxuICAgICAqIEdvaW5nIG92ZXIgYDVfMDAwYCBtYXkgcmVxdWlyZSB5b3UgdG8gY29udGFjdCBBV1MgU3VwcG9ydCAtIHRoZXkgYXJlIGFjY291bnQgYm91bmQuXG4gICAgICovXG4gICAgYnVyc3Q/OiBudW1iZXJcbiAgICAvKipcbiAgICAgKiBHb2luZyBvdmVyIGAxMF8wMDBgIG1heSByZXF1aXJlIHlvdSB0byBjb250YWN0IEFXUyBTdXBwb3J0IC0gdGhleSBhcmUgYWNjb3VudCBib3VuZC5cbiAgICAgKi9cbiAgICByYXRlPzogbnVtYmVyXG4gIH1cblxuICAvKipcbiAgICogU2V0cyBDT1JTIGhlYWRlcnMgb24gcmVzcG9uc2VzIGZyb20gdGhlIEFQSSBHYXRld2F5IHRvIGFsbG93IGFsbCBvcmlnaW5zLCBoZWFkZXJzIGFuZCBtZXRob2RzLlxuICAgKiBVc2VmdWwgaWYgeW91ciBBUEkgc2hvdWxkIGJlIGFjY2Vzc2VkIGJ5IGFueSBicm93c2VyLlxuICAgKi9cbiAgY29yc0FsbG93QWxsPzogYm9vbGVhblxuXG4gIC8qKlxuICAgKiBJZiBzb21lIHNldHRpbmdzIGluIHRoaXMgY29uc3RydWN0IGRvIG5vdCB3b3JrIGZvciB5b3UsIHRoaXMgaXMgYW4gZXNjYXBlIGhhdGNoIG1lY2hhbmlzbSB0b1xuICAgKiBvdmVycmlkZSBhbnl0aGluZy5cbiAgICovXG4gIHByb3BzT3ZlcnJpZGU/OiB7XG4gICAgLyoqXG4gICAgICogT3ZlcnJpZGUgc2V0dGluZ3MgZm9yIHRoZSB7QGxpbmsgYXBpZ3cuSHR0cEFwaX0gKGFjY2Vzc2libGUgaW4ge0BsaW5rIEFwaUdhdGV3YXkuaHR0cEFwaX0pLlxuICAgICAqXG4gICAgICogRm9yIGV4YW1wbGUsIGlmIHlvdSBoYXZlIGEgZnJvbnRlbmQgYWNjZXNzaW5nIHRoaXMgQVBJLCB5b3UgbWlnaHQgd2FudCB0byBzZXRcbiAgICAgKiBbQ09SUyBwcmVmbGlnaHRdKGh0dHBzOi8vZG9jcy5hd3MuYW1hem9uLmNvbS9jZGsvYXBpL3YxL2RvY3MvYXdzLWFwaWdhdGV3YXl2Mi1yZWFkbWUuaHRtbCNjcm9zcy1vcmlnaW4tcmVzb3VyY2Utc2hhcmluZy1jb3JzKVxuICAgICAqIHNldHRpbmdzLlxuICAgICAqL1xuICAgIGh0dHBBcGk/OiBQYXJ0aWFsPGFwaWd3Lkh0dHBBcGlQcm9wcz5cbiAgfVxufVxuXG5leHBvcnQgdHlwZSBBcGlHYXRld2F5RG5zUHJvcHMgPSB7XG4gIC8qKlxuICAgKiBPbmx5IHRoZSBzdWJkb21haW4gcHJlZml4LCB3aGljaCBzaG91bGQgYmUgdGhlIG5hbWUgb2YgdGhlIHNlcnZpY2UuXG4gICAqIEV4YW1wbGU6IFN1YmRvbWFpbiBgcHJvZHVjdGAgd291bGQgZ2l2ZSB0aGUgZW5kIHJlc3VsdCBvZiBhbiBBUEktR1cgd2l0aFxuICAgKiBgcHJvZHVjdC5wbGF0Zm9ybS5leGFtcGxlLm5vYC5cbiAgICovXG4gIHN1YmRvbWFpbjogc3RyaW5nXG5cbiAgLyoqXG4gICAqIEhvc3RlZCBab25lIGZvciB0aGUgZXh0ZXJuYWwgZmFjaW5nIGRvbWFpbi5cbiAgICogVGhpcyBpcyB3aGVyZSByb3V0ZXMgd2lsbCBiZSBjcmVhdGVkLCB0byByZWRpcmVjdCBjb25zdW1lcnMgdG8gdGhlIEFQSS1HVy5cbiAgICogRm9yIGV4YW1wbGUgYSBIWiBmb3IgYHBsYXRmb3JtLmV4YW1wbGUubm9gLlxuICAgKi9cbiAgaG9zdGVkWm9uZTogcm91dGU1My5JSG9zdGVkWm9uZVxuXG4gIC8qKlxuICAgKiBUaGUgVGltZSBUbyBMaXZlIChUVEwpIGZvciB0aGUgcHVibGljIEROUyBBIHJlY29yZCB0aGF0IHdpbGwgZXhwb3NlIHRoZSBBUEktR1cuXG4gICAqIFRoaXMgaXMgaG93IGxvbmcgRE5TIHNlcnZlcnMgd2lsbCBjYWNoZSB0aGUgcmVjb3JkLlxuICAgKlxuICAgKiBBIGxvbmcgVFRMIChob3VycykgaXMgYmVuZWZpY2lhbCB0byBETlMgc2VydmVycywgYnV0IG1ha2VzIGRldmVsb3BlcnMgKHlvdSkgd2FpdCBsb25nZXIgd2hlblxuICAgKiBkb2luZyBjaGFuZ2VzLlxuICAgKlxuICAgKiBAZGVmYXVsdCA1IG1pbnV0ZXNcbiAgICovXG4gIHR0bD86IGNkay5EdXJhdGlvblxufVxuXG5leHBvcnQgdHlwZSBBcGlHYXRld2F5Um91dGU8QXV0aFNjb3Blc1QgZXh0ZW5kcyBzdHJpbmcgPSBzdHJpbmc+ID0ge1xuICAvKiogVGhlIHBhdGggb2YgdGhlIHJvdXRlIHRvIGV4cG9zZSB0aHJvdWdoIHRoZSBBUEkgR2F0ZXdheS4gVXNlIFwiL1wiIGZvciB0aGUgcm9vdCByb3V0ZS4gKi9cbiAgcGF0aDogc3RyaW5nXG5cbiAgLyoqXG4gICAqIEJ5IGRlZmF1bHQsIHdlIG9ubHkgZm9yd2FyZCByZXF1ZXN0cyB0aGF0IG1hdGNoIHRoZSByb3V0ZSdzIHBhdGggZXhhY3RseS4gU28gZm9yIGEgcm91dGUgd2l0aFxuICAgKiBwYXRoIGAvYXBpL3VzZXJzYCwgYSByZXF1ZXN0IHRvIGAvYXBpL3VzZXJzYCB3aWxsIGJlIGZvcndhcmRlZCwgYnV0IGEgcmVxdWVzdCB0b1xuICAgKiBgL2FwaS91c2Vycy9hZG1pbmAgd2lsbCBub3QuIElmIHlvdSB3YW50IHRvIGZvcndhcmQgcmVxdWVzdHMgdG8gYWxsIHN1Yi1wYXRocyB1bmRlciB0aGUgcm91dGUnc1xuICAgKiBwYXRoLCB5b3UgY2FuIHNldCB0aGlzIHRvIHRydWUuXG4gICAqXG4gICAqIEBkZWZhdWx0IGZhbHNlXG4gICAqL1xuICBpbmNsdWRlU3VicGF0aHM/OiBib29sZWFuXG5cbiAgLyoqXG4gICAqIFRoZSBIVFRQIG1ldGhvZCB0byBleHBvc2UuIGBBTllgIGV4cG9zZXMgYWxsIEhUVFAgbWV0aG9kcyBvbiB0aGUgcGF0aC5cbiAgICpcbiAgICogQGRlZmF1bHQgXCJBTllcIlxuICAgKi9cbiAgbWV0aG9kPzogSHR0cE1ldGhvZFxuXG4gIC8qKlxuICAgKiBUaGUgaW50ZWdyYXRpb24gdGhhdCB0aGUgcm91dGUgd2lsbCBmb3J3YXJkIHRvLiBTZWUge0BsaW5rIEludGVncmF0aW9uUHJvcHN9IGZvciB0aGUgYXZhaWxhYmxlXG4gICAqIG9wdGlvbnMuXG4gICAqXG4gICAqIElmIHVuZGVmaW5lZCwgdXNlcyB0aGUge0BsaW5rIEFwaUdhdGV3YXlQcm9wcy5kZWZhdWx0SW50ZWdyYXRpb259LlxuICAgKi9cbiAgaW50ZWdyYXRpb24/OiBJbnRlZ3JhdGlvblByb3BzXG5cbiAgLyoqXG4gICAqIEhvdyByZXF1ZXN0cyBvbiB0aGUgcm91dGUgYXJlIGF1dGhlbnRpY2F0ZWQuIFNlZSB7QGxpbmsgQXV0aG9yaXphdGlvblByb3BzfSBmb3IgdGhlIGF2YWlsYWJsZVxuICAgKiBvcHRpb25zLlxuICAgKlxuICAgKiBJZiB1bmRlZmluZWQsIHVzZXMgdGhlIHtAbGluayBBcGlHYXRld2F5UHJvcHMuZGVmYXVsdEF1dGhvcml6YXRpb259LlxuICAgKi9cbiAgYXV0aG9yaXphdGlvbj86IEF1dGhvcml6YXRpb25Qcm9wczxBdXRoU2NvcGVzVD5cbn1cblxuZXhwb3J0IHR5cGUgSHR0cE1ldGhvZCA9XG4gIHwgXCJBTllcIlxuICB8IFwiR0VUXCJcbiAgfCBcIlBPU1RcIlxuICB8IFwiUFVUXCJcbiAgfCBcIlBBVENIXCJcbiAgfCBcIkRFTEVURVwiXG4gIHwgXCJPUFRJT05TXCJcbiAgfCBcIkhFQURcIlxuXG5leHBvcnQgdHlwZSBJbnRlZ3JhdGlvblByb3BzID1cbiAgLyoqIFVzZSB0aGlzIHdoZW4gY29ubmVjdGluZyB0aGUgcm91dGUgdG8gYW4gQUxCIChBcHBsaWNhdGlvbiBMb2FkIEJhbGFuY2VyKS4gKi9cbiAgfCAoeyB0eXBlOiBcIkFMQlwiIH0gJiBBbGJJbnRlZ3JhdGlvblByb3BzKVxuICAvKiogVXNlIHRoaXMgd2hlbiBjb25uZWN0aW5nIHJvdXRlIHRvIGEgTGFtYmRhLiAqL1xuICB8ICh7IHR5cGU6IFwiTGFtYmRhXCIgfSAmIExhbWJkYUludGVncmF0aW9uUHJvcHMpXG4gIC8qKiBVc2UgdGhpcyB3aGVuIGNvbm5lY3RpbmcgYSByb3V0ZSB0byBzZW5kIHRvIGFuIFNRUyBxdWV1ZS4gKi9cbiAgfCAoeyB0eXBlOiBcIlNRU1wiIH0gJiBTcXNJbnRlZ3JhdGlvblByb3BzKVxuXG4vKipcbiAqIFByb3BzIGZvciB0aGUgQVBJLUdXIC0+IEFMQiAoQXBwbGljYXRpb24gTG9hZCBCYWxhbmNlcikgaW50ZWdyYXRpb24uXG4gKlxuICogU2VlIHRoZSBub3RlIG9uIHtAbGluayBBcGlHYXRld2F5fSBhYm91dCB0aGUgbG9hZCBiYWxhbmNlciBzZWN1cml0eSBncm91cC5cbiAqL1xuZXhwb3J0IHR5cGUgQWxiSW50ZWdyYXRpb25Qcm9wcyA9IHtcbiAgLyoqXG4gICAqIEEgbGlzdGVuZXIgb24gZS5nLiBwb3J0IDQ0MyAoSFRUUFMpLlxuICAgKlxuICAgKiBTZWUgdGhlIG5vdGUgb24ge0BsaW5rIEFwaUdhdGV3YXl9IGFib3V0IHRoZSBsb2FkIGJhbGFuY2VyIHNlY3VyaXR5IGdyb3VwLlxuICAgKi9cbiAgbG9hZEJhbGFuY2VyTGlzdGVuZXI6IGVsYi5JQXBwbGljYXRpb25MaXN0ZW5lclxuXG4gIC8qKlxuICAgKiBUaGUgaG9zdCBuYW1lIChkb21haW4gbmFtZSkgb2YgdGhlIGJhY2tlbmQgc2VydmljZSB0aGF0IHdlIHdhbnQgdG8gcmVhY2ggdGhyb3VnaCB0aGUgQUxCLlxuICAgKlxuICAgKiBUaGlzIGlzIHVzZWQgdG86XG4gICAqIC0gVmVyaWZ5IHRoZSBIVFRQUyBjZXJ0aWZpY2F0ZSBvZiB0aGUgYmFja2VuZCBzZXJ2aWNlLCBzbyB0aGF0IHRoZSByZXF1ZXN0IGZvcndhcmRlZCBmcm9tXG4gICAqICAgQVBJLUdXIGNhbiB1c2UgVExTXG4gICAqIC0gU2V0IHRoZSBgSG9zdGAgaGVhZGVyIG9uIHRoZSByZXF1ZXN0IHdoZW4gZm9yd2FyZGluZyB0byB0aGUgQUxCLCBzbyB0aGF0IHJlcXVlc3RzIGNhbiBiZVxuICAgKiAgIHJvdXRlZCB0byB0aGUgY29ycmVjdCBUYXJnZXQgR3JvdXBcbiAgICpcbiAgICogRXhhbXBsZSB2YWx1ZTogYDxzZXJ2aWNlPi5zdGFnaW5nLm15LXByb2plY3QubGlmbGlnLmlvYCAobm90IHByZWZpeGVkIGJ5IGBodHRwczovL2ApLlxuICAgKi9cbiAgaG9zdE5hbWU6IHN0cmluZ1xuXG4gIC8qKlxuICAgKiBUaGUgVlBDIHVzZWQgYnkgdGhlIEFMQi4gVGhlIEFQSS1HVyBpbnRlZ3JhdGlvbiB3aWxsIGNvbm5lY3QgdG8gdGhlIEFMQiB1c2luZyBhIFZQQyBMaW5rIGZvclxuICAgKiB0aGlzIFZQQy5cbiAgICpcbiAgICogU2VlIHRoZSBub3RlIG9uIHtAbGluayBBcGlHYXRld2F5fSBhYm91dCB0aGUgbG9hZCBiYWxhbmNlciBzZWN1cml0eSBncm91cC5cbiAgICovXG4gIHZwYzogZWMyLklWcGNcblxuICAvKipcbiAgICogQSBzZWN1cml0eSBncm91cCAoU0cpIHRoYXQgYWxsb3dzIGluY29taW5nIHRyYWZmaWMgdG8gdGhlIEFMQi4gV2lsbCBiZSB1c2VkIGJ5IHRoZSBWUEMgbGluaywgc29cbiAgICogdGhlIEFQSS1HVyBpbnRlZ3JhdGlvbiBjYW4gY29ubmVjdC5cbiAgICpcbiAgICogVGhpcyBpcyB1c3VhbGx5IHRoZSBzYW1lIFNHIGFzIHRoZSBBTEIgdXNlcywgYmVjYXVzZSB0aGV5IGhhdmUgYSBydWxlIHRoYXQgYWxsb3dzIHRyYWZmaWMgZnJvbVxuICAgKiBvdGhlcnMgaW4gdGhlIHNhbWUgU0cuXG4gICAqXG4gICAqIFNlZSB0aGUgbm90ZSBvbiB7QGxpbmsgQXBpR2F0ZXdheX0gYWJvdXQgdGhlIGxvYWQgYmFsYW5jZXIgc2VjdXJpdHkgZ3JvdXAuXG4gICAqL1xuICBzZWN1cml0eUdyb3VwOiBlYzIuSVNlY3VyaXR5R3JvdXBcblxuICAvKipcbiAgICogTWFwIHJlcXVlc3QgcGFyYW1ldGVycyAoYWRkL292ZXJ3cml0ZSBwYXRoL2hlYWRlcnMvcXVlcnkgcGFyYW1zKSBiZWZvcmUgZm9yd2FyZGluZyB0byB0aGVcbiAgICogYmFja2VuZC5cbiAgICpcbiAgICogU2VlIHtAbGluayBodHRwczovL2RvY3MuYXdzLmFtYXpvbi5jb20vYXBpZ2F0ZXdheS9sYXRlc3QvZGV2ZWxvcGVyZ3VpZGUvaHR0cC1hcGktcGFyYW1ldGVyLW1hcHBpbmcuaHRtbH1cbiAgICogZm9yIG1vcmUgb24gdGhpcy4gUmVhZCB0aGUgJ1Jlc2VydmVkIGhlYWRlcnMnIHNlY3Rpb24gZm9yIHdoaWNoIGhlYWRlcnMgY2Fubm90IGJlIG92ZXJyaWRkZW4uXG4gICAqIEluIGFkZGl0aW9uIHRvIHRoZSBBV1MtcmVzZXJ2ZWQgaGVhZGVycywgeW91IHNob3VsZCBub3Qgb3ZlcnJpZGUgdGhlICdIb3N0JyBoZWFkZXIgZWl0aGVyLCBhc1xuICAgKiB0aGF0J3MgdXNlZCBmb3Igcm91dGluZyB0aGUgcmVxdWVzdCB0byB0aGUgY29ycmVjdCBzZXJ2aWNlIGJlaGluZCB0aGUgbG9hZCBiYWxhbmNlci5cbiAgICpcbiAgICogIyMjIEV4YW1wbGU6XG4gICAqXG4gICAqIEFkZGluZyBhIGhlYWRlcjpcbiAgICogYGBgXG4gICAqIG1hcFBhcmFtZXRlcnM6IChwYXJhbWV0ZXJzKSA9PiBwYXJhbWV0ZXJzLm92ZXJ3cml0ZUhlYWRlcihcbiAgICogICAgXCJYLU15LUN1c3RvbS1IZWFkZXJcIixcbiAgICogICAgYXBpZ3cuTWFwcGluZ1ZhbHVlLmN1c3RvbShcIm15LWN1c3RvbS12YWx1ZVwiKSxcbiAgICogKVxuICAgKiBgYGBcbiAgICpcbiAgICogT3ZlcndyaXRpbmcgdGhlIHBhdGggKGlmLCBmb3IgZXhhbXBsZSwgeW91IGNvbmZpZ3VyZSBhIGAvdXNlcnNgIHJvdXRlIG9uIHRoZSBBUEkgR2F0ZXdheSB0aGF0XG4gICAqIHlvdSB3YW50IHRvIGZvcndhcmQgdG8gYC9hcGkvdXNlcnNgIG9uIHRoZSBiYWNrZW5kKTpcbiAgICogYGBgXG4gICAqIG1hcFBhcmFtZXRlcnM6IChwYXJhbWV0ZXJzKSA9PiBwYXJhbWV0ZXJzLm92ZXJ3cml0ZVBhdGgoXCIvYXBpL3VzZXJzXCIpXG4gICAqIGBgYFxuICAgKi9cbiAgbWFwUGFyYW1ldGVycz86IChwYXJhbWV0ZXJzOiBhcGlndy5QYXJhbWV0ZXJNYXBwaW5nKSA9PiB2b2lkXG59XG5cbmV4cG9ydCB0eXBlIExhbWJkYUludGVncmF0aW9uUHJvcHMgPSB7XG4gIC8qKlxuICAgKiBUaGUgTGFtYmRhIGludGVncmF0aW9uIHVzZXMgdGhlIFYyIHBheWxvYWQgZm9ybWF0OlxuICAgKiB7QGxpbmsgaHR0cHM6Ly9kb2NzLmF3cy5hbWF6b24uY29tL2FwaWdhdGV3YXkvbGF0ZXN0L2RldmVsb3Blcmd1aWRlL2h0dHAtYXBpLWRldmVsb3AtaW50ZWdyYXRpb25zLWxhbWJkYS5odG1sfVxuICAgKlxuICAgKiBJZiB3cml0aW5nIHRoZSBMYW1iZGEgaW4gVHlwZVNjcmlwdCwgdGhpcyBtZWFucyB5b3Ugc2hvdWxkIHVzZSBgQVBJR2F0ZXdheVByb3h5RXZlbnRWMmAgYXMgdGhlXG4gICAqIHJlcXVlc3QgdHlwZSwgYW5kIGBBUElHYXRld2F5UHJveHlSZXN1bHRWMmAgYXMgdGhlIHJlc3BvbnNlIHR5cGUuXG4gICAqL1xuICBsYW1iZGE6IGxhbWJkYS5JRnVuY3Rpb25cbn1cblxuZXhwb3J0IHR5cGUgU3FzSW50ZWdyYXRpb25Qcm9wcyA9IHtcbiAgcXVldWU6IHNxcy5JUXVldWVcblxuICAvKipcbiAgICogTWVzc2FnZSBhdHRyaWJ1dGVzIHRvIHBhc3Mgb24gdG8gU1FTLiBUaGUga2V5cyBpbiB0aGlzIG9iamVjdCBhcmUgdGhlIG5hbWVzIG9mIHRoZSBhdHRyaWJ1dGVzLlxuICAgKiBFYWNoIGF0dHJpYnV0ZSBoYXMgYSBEYXRhVHlwZSBmaWVsZCwgYW5kIGVpdGhlciBhIFN0cmluZ1ZhbHVlIG9yIEJpbmFyeVZhbHVlIGZpZWxkIGRlcGVuZGluZyBvblxuICAgKiBpdHMgdHlwZS4gU2VlIEFXUyBkb2NzOlxuICAgKiBodHRwczovL2RvY3MuYXdzLmFtYXpvbi5jb20vQVdTU2ltcGxlUXVldWVTZXJ2aWNlL2xhdGVzdC9BUElSZWZlcmVuY2UvQVBJX01lc3NhZ2VBdHRyaWJ1dGVWYWx1ZS5odG1sXG4gICAqXG4gICAqIEluIHRoZSBTdHJpbmdWYWx1ZSBmaWVsZCwgeW91IGNhbiBkbyBBUEkgR2F0ZXdheSBwYXJhbWV0ZXIgbWFwcGluZzpcbiAgICogaHR0cHM6Ly9kb2NzLmF3cy5hbWF6b24uY29tL2FwaWdhdGV3YXkvbGF0ZXN0L2RldmVsb3Blcmd1aWRlL2h0dHAtYXBpLXBhcmFtZXRlci1tYXBwaW5nLmh0bWxcbiAgICpcbiAgICogRXhhbXBsZTpcbiAgICogYGBgXG4gICAqIG1lc3NhZ2VBdHRyaWJ1dGVzOiB7XG4gICAqICAgY2xpZW50SWQ6IHtcbiAgICogICAgIERhdGFUeXBlOiBcIlN0cmluZ1wiLFxuICAgKiAgICAgU3RyaW5nVmFsdWU6IFwiJHtjb250ZXh0LmF1dGhvcml6ZXIuY2xpZW50SWR9XCIsXG4gICAqICAgfSxcbiAgICogfSxcbiAgICogYGBgXG4gICAqL1xuICBtZXNzYWdlQXR0cmlidXRlcz86IHtcbiAgICBbYXR0cmlidXRlTmFtZTogc3RyaW5nXTpcbiAgICAgIHwgeyBEYXRhVHlwZTogXCJTdHJpbmdcIiB8IFwiTnVtYmVyXCI7IFN0cmluZ1ZhbHVlOiBzdHJpbmcgfVxuICAgICAgfCB7XG4gICAgICAgICAgRGF0YVR5cGU6IFwiQmluYXJ5XCJcbiAgICAgICAgICAvKiogQmFzZTY0LWVuY29kZWQgYmluYXJ5IGRhdGEgb2JqZWN0LiAqL1xuICAgICAgICAgIEJpbmFyeVZhbHVlOiBzdHJpbmdcbiAgICAgICAgfVxuICB9XG59XG5cbmV4cG9ydCB0eXBlIEF1dGhvcml6YXRpb25Qcm9wczxBdXRoU2NvcGVzVCBleHRlbmRzIHN0cmluZyA9IHN0cmluZz4gPVxuICAvKipcbiAgICogTm8gYXV0aGVudGljYXRpb24sIGZvciB3aGVuIHlvdSB3YW50IGEgZnVsbHkgcHVibGljIHJvdXRlIChvciBoYW5kbGUgYXV0aGVudGljYXRpb24gaW4gdGhlXG4gICAqIGJhY2tlbmQgaW50ZWdyYXRpb24pLlxuICAgKi9cbiAgfCB7IHR5cGU6IFwiTk9ORVwiIH1cbiAgLyoqXG4gICAqIEFXUyBJQU0gYXV0aG9yaXphdGlvbi5cbiAgICogaHR0cHM6Ly9kb2NzLmF3cy5hbWF6b24uY29tL2FwaWdhdGV3YXkvbGF0ZXN0L2RldmVsb3Blcmd1aWRlL2h0dHAtYXBpLWFjY2Vzcy1jb250cm9sLWlhbS5odG1sXG4gICAqL1xuICB8IHsgdHlwZTogXCJJQU1cIiB9XG4gIC8qKlxuICAgKiBDcmVhdGVzIGEgY3VzdG9tIGF1dGhvcml6ZXIgbGFtYmRhIHdoaWNoIHJlYWRzIGBBdXRob3JpemF0aW9uOiBCZWFyZXIgPHRva2VuPmAgaGVhZGVyIGFuZFxuICAgKiB2ZXJpZmllcyB0aGUgdG9rZW4gYWdhaW5zdCBhIENvZ25pdG8gdXNlciBwb29sLlxuICAgKi9cbiAgfCAoe1xuICAgICAgdHlwZTogXCJDT0dOSVRPX1VTRVJfUE9PTFwiXG4gICAgfSAmIENvZ25pdG9Vc2VyUG9vbEF1dGhvcml6ZXJQcm9wczxBdXRoU2NvcGVzVD4pXG4gIC8qKlxuICAgKiBDcmVhdGVzIGEgY3VzdG9tIGF1dGhvcml6ZXIgbGFtYmRhIHdoaWNoIHJlYWRzIGBBdXRob3JpemF0aW9uOiBCYXNpYyA8YmFzZTY0LWVuY29kZWQgY3JlZGVudGlhbHM+YFxuICAgKiBoZWFkZXIgYW5kIHZlcmlmaWVzIHRoZSBjcmVkZW50aWFscyBhZ2FpbnN0IGEgZ2l2ZW4gc2VjcmV0LlxuICAgKi9cbiAgfCAoeyB0eXBlOiBcIkJBU0lDX0FVVEhcIiB9ICYgQmFzaWNBdXRoQXV0aG9yaXplclByb3BzKVxuICAvKipcbiAgICogQ3JlYXRlcyBhIGN1c3RvbSBhdXRob3JpemVyIGxhbWJkYSB3aGljaCBhbGxvd3MgYm90aDpcbiAgICogLSBgQXV0aG9yaXphdGlvbjogQmVhcmVyIDx0b2tlbj5gIGhlYWRlciwgZm9yIHdoaWNoIHRoZSB0b2tlbiBpcyBjaGVja2VkIGFnYWluc3QgdGhlIGdpdmVuXG4gICAqICAgQ29nbml0byB1c2VyIHBvb2xcbiAgICogLSBgQXV0aG9yaXphdGlvbjogQmFzaWMgPGJhc2U2NC1lbmNvZGVkIGNyZWRlbnRpYWxzPmAgaGVhZGVyLCBmb3Igd2hpY2ggdGhlIGNyZWRlbnRpYWxzIGFyZVxuICAgKiAgIGNoZWNrZWQgYWdhaW5zdCB0aGUgY3JlZGVudGlhbHMgZnJvbSB0aGUgZ2l2ZW4gYmFzaWMgYXV0aCBzZWNyZXQgbmFtZVxuICAgKlxuICAgKiBJZiBlaXRoZXIgb2YgdGhlc2UgYXJlIGdpdmVuIGFuZCB2YWxpZCwgdGhlIHJlcXVlc3QgaXMgYXV0aGVudGljYXRlZC5cbiAgICovXG4gIHwgKHtcbiAgICAgIHR5cGU6IFwiQ09HTklUT19VU0VSX1BPT0xfT1JfQkFTSUNfQVVUSFwiXG4gICAgfSAmIENvZ25pdG9Vc2VyUG9vbE9yQmFzaWNBdXRoQXV0aG9yaXplclByb3BzPEF1dGhTY29wZXNUPilcblxuZXhwb3J0IHR5cGUgQ29nbml0b1VzZXJQb29sQXV0aG9yaXplclByb3BzPFxuICBBdXRoU2NvcGVzVCBleHRlbmRzIHN0cmluZyA9IHN0cmluZyxcbj4gPSB7XG4gIHVzZXJQb29sOiBJVXNlclBvb2xcblxuICAvKipcbiAgICogVmVyaWZpZXMgdGhhdCB0b2tlbiBjbGFpbXMgY29udGFpbiB0aGUgZ2l2ZW4gc2NvcGUuXG4gICAqXG4gICAqIFdoZW4gZGVmaW5lZCBhcyBwYXJ0IG9mIGEgcmVzb3VyY2Ugc2VydmVyLCBzY29wZXMgYXJlIG9uIHRoZSBmb3JtYXQ6XG4gICAqIGB7cmVzb3VyY2Ugc2VydmVyIGlkZW50aWZpZXJ9L3tzY29wZSBuYW1lfWAsIGUuZy4gYGV4dGVybmFsL3ZpZXdfdXNlcnNgLlxuICAgKlxuICAgKiBUbyBnZXQgbW9yZSB0eXBlIHNhZmV0eSBvbiB0aGlzIHBhcmFtZXRlciwgc2VlIHRoZSBkb2NzIGZvciB0aGUgYEF1dGhTY29wZXNUYCB0eXBlIHBhcmFtZXRlciBvblxuICAgKiB7QGxpbmsgQXBpR2F0ZXdheX0uXG4gICAqL1xuICByZXF1aXJlZFNjb3BlPzogQXV0aFNjb3Blc1RcblxuICAvKipcbiAgICogTmFtZSBvZiBzZWNyZXQgaW4gQVdTIFNlY3JldHMgTWFuYWdlciB0aGF0IHN0b3JlcyBiYXNpYyBhdXRoIGNyZWRlbnRpYWxzIGZvciB0aGUgYmFja2VuZFxuICAgKiBzZXJ2aWNlLCB0byBiZSBmb3J3YXJkZWQgdG8gdGhlIGJhY2tlbmQgaWYgQ29nbml0byB1c2VyIHBvb2wgYXV0aGVudGljYXRpb24gc3VjY2VlZGVkLlxuICAgKlxuICAgKiBUaGUgc2VjcmV0IHZhbHVlIG11c3QgZm9sbG93IHRoaXMgZm9ybWF0OlxuICAgKiBgYGBqc29uXG4gICAqIHtcInVzZXJuYW1lXCI6XCI8dXNlcm5hbWU+XCIsXCJwYXNzd29yZFwiOlwiPHBhc3N3b3JkPlwifVxuICAgKiBgYGBcbiAgICpcbiAgICogVGhpcyBwcm9wIHNvbHZlcyB0aGUgZm9sbG93aW5nIHVzZS1jYXNlOlxuICAgKiAtIFlvdSB3YW50IHRvIGRvIENvZ25pdG8gdXNlciBwb29sIGF1dGhlbnRpY2F0aW9uIGluIHRoZSBBUEkgR2F0ZXdheVxuICAgKiAtIFlvdSB3YW50IGFuIGFkZGl0aW9uYWwgYXV0aCBjaGVjayBpbiB0aGUgYmFja2VuZCwgYnV0IHlvdSBkb24ndCB3YW50IHRvIGRlYWwgd2l0aCBDb2duaXRvXG4gICAqICAgdGhlcmVcbiAgICogLSBUaGUgYmFja2VuZCB1c2VzIGJhc2ljIGF1dGhcbiAgICpcbiAgICogVGhpcyBwcm9wIHNvbHZlcyB0aGlzIGJ5IGxldHRpbmcgeW91IHNwZWNpZnkgY3JlZGVudGlhbHMgdG8gcGFzcyB0byB0aGUgYmFja2VuZCBhZnRlciBBUEktR1dcbiAgICogYXV0aGVudGljYXRpb24gc3VjY2VlZHMuIFlvdSBjYW4gcGFzcyB0aGUgZW5jb2RlZCBjcmVkZW50aWFscyB0aHJvdWdoXG4gICAqIHtAbGluayBBbGJJbnRlZ3JhdGlvblByb3BzLm1hcFBhcmFtZXRlcnN9LCB1c2luZyB0aGUgYGF1dGhvcml6ZXIuaW50ZXJuYWxBdXRob3JpemF0aW9uSGVhZGVyYFxuICAgKiBjb250ZXh0IHZhcmlhYmxlLCBsaWtlIHNvOlxuICAgKiBgYGBcbiAgICogbWFwUGFyYW1ldGVyczogKHBhcmFtZXRlcnMpID0+IHBhcmFtZXRlcnMub3ZlcndyaXRlSGVhZGVyKFxuICAgKiAgIC8vICdBdXRob3JpemF0aW9uJyBoZWFkZXIgY2Fubm90IGJlIG92ZXJyaWRkZW4sIHNvIHdlIHVzZSBhIGN1c3RvbSBoZWFkZXJcbiAgICogICBcIlgtSW50ZXJuYWwtQXV0aG9yaXphdGlvblwiLFxuICAgKiAgIGFwaWd3Lk1hcHBpbmdWYWx1ZS5jb250ZXh0VmFyaWFibGUoXCJhdXRob3JpemVyLmludGVybmFsQXV0aG9yaXphdGlvbkhlYWRlclwiKSxcbiAgICogKVxuICAgKiBgYGBcbiAgICogVGhlIGJhY2tlbmQgY2FuIHRoZW4gY2hlY2sgdGhlIGBYLUludGVybmFsLUF1dGhvcml6YXRpb25gIGhlYWRlci5cbiAgICovXG4gIGNyZWRlbnRpYWxzRm9ySW50ZXJuYWxBdXRob3JpemF0aW9uPzogc3RyaW5nXG59XG5cbmV4cG9ydCB0eXBlIEJhc2ljQXV0aEF1dGhvcml6ZXJQcm9wcyA9IHtcbiAgLyoqXG4gICAqIE5hbWUgb2Ygc2VjcmV0IGluIEFXUyBTZWNyZXRzIE1hbmFnZXIgdGhhdCBzdG9yZXMgYmFzaWMgYXV0aCBjcmVkZW50aWFscy4gVGhlIHNlY3JldCB2YWx1ZSBtdXN0XG4gICAqIGZvbGxvdyB0aGlzIGZvcm1hdDpcbiAgICogYGBganNvblxuICAgKiB7XCJ1c2VybmFtZVwiOlwiPHVzZXJuYW1lPlwiLFwicGFzc3dvcmRcIjpcIjxwYXNzd29yZD5cIn1cbiAgICogYGBgXG4gICAqL1xuICBjcmVkZW50aWFsc1NlY3JldE5hbWU6IHN0cmluZ1xufVxuXG5leHBvcnQgdHlwZSBDb2duaXRvVXNlclBvb2xPckJhc2ljQXV0aEF1dGhvcml6ZXJQcm9wczxcbiAgQXV0aFNjb3Blc1QgZXh0ZW5kcyBzdHJpbmcgPSBzdHJpbmcsXG4+ID0ge1xuICB1c2VyUG9vbDogSVVzZXJQb29sXG5cbiAgLyoqXG4gICAqIE5hbWUgb2Ygc2VjcmV0IGluIEFXUyBTZWNyZXRzIE1hbmFnZXIgdGhhdCBzdG9yZXMgYmFzaWMgYXV0aCBjcmVkZW50aWFscy4gVGhlIHNlY3JldCB2YWx1ZSBtdXN0XG4gICAqIGZvbGxvdyB0aGlzIGZvcm1hdDpcbiAgICogYGBganNvblxuICAgKiB7XCJ1c2VybmFtZVwiOlwiPHVzZXJuYW1lPlwiLFwicGFzc3dvcmRcIjpcIjxwYXNzd29yZD5cIn1cbiAgICogYGBgXG4gICAqL1xuICBiYXNpY0F1dGhDcmVkZW50aWFsc1NlY3JldE5hbWU/OiBzdHJpbmdcblxuICAvKipcbiAgICogVmVyaWZpZXMgdGhhdCB0b2tlbiBjbGFpbXMgY29udGFpbiB0aGUgZ2l2ZW4gc2NvcGUuIE9ubHkgYXBwbGljYWJsZSBmb3IgYEJlYXJlcmAgdG9rZW4gcmVxdWVzdHNcbiAgICogY2hlY2tlZCBhZ2FpbnN0IHRoZSBDb2duaXRvIFVzZXIgUG9vbCAobm90IGFwcGxpY2FibGUgZm9yIGJhc2ljIGF1dGgpLlxuICAgKlxuICAgKiBXaGVuIGRlZmluZWQgYXMgcGFydCBvZiBhIHJlc291cmNlIHNlcnZlciwgc2NvcGVzIGFyZSBvbiB0aGUgZm9ybWF0OlxuICAgKiBge3Jlc291cmNlIHNlcnZlciBpZGVudGlmaWVyfS97c2NvcGUgbmFtZX1gLCBlLmcuIGBleHRlcm5hbC92aWV3X3VzZXJzYC5cbiAgICpcbiAgICogVG8gZ2V0IG1vcmUgdHlwZSBzYWZldHkgb24gdGhpcyBwYXJhbWV0ZXIsIHNlZSB0aGUgZG9jcyBmb3IgdGhlIGBBdXRoU2NvcGVzVGAgdHlwZSBwYXJhbWV0ZXIgb25cbiAgICoge0BsaW5rIEFwaUdhdGV3YXl9LlxuICAgKi9cbiAgcmVxdWlyZWRTY29wZT86IEF1dGhTY29wZXNUXG59XG5cbmV4cG9ydCB0eXBlIEFwaUdhdGV3YXlBY2Nlc3NMb2dzUHJvcHMgPSB7XG4gIC8qKlxuICAgKiBEZWxldGUgdGhlIGFjY2VzcyBsb2dzIGlmIHRoaXMgY29uc3RydWN0IGlzIGRlbGV0ZWQ/XG4gICAqXG4gICAqIE1heWJlIHlvdSB3YW50IHRvIERFU1RST1kgaW5zdGVhZC4gT3IgZm9yIGxlZ2FsIHJlYXNvbnMsIHJldGFpbiBmb3IgYXVkaXQuXG4gICAqXG4gICAqIEBkZWZhdWx0IFJlbW92YWxQb2xpY3kuUkVUQUlOXG4gICAqL1xuICByZW1vdmFsUG9saWN5PzogY2RrLlJlbW92YWxQb2xpY3lcblxuICAvKipcbiAgICogSG93IGxvbmcgdG8ga2VlcCB0aGUgbG9ncy4gSWYgdW5kZWZpbmVkLCB1c2VzIHRoZSBzYW1lIGRlZmF1bHQgYXMgbmV3IEFXUyBsb2cgZ3JvdXBzLlxuICAgKlxuICAgKiBAZGVmYXVsdCBSZXRlbnRpb25EYXlzLlRXT19ZRUFSU1xuICAgKi9cbiAgcmV0ZW50aW9uPzogbG9ncy5SZXRlbnRpb25EYXlzXG5cbiAgLyoqXG4gICAqIEEgY3VzdG9tIEpTT04gbG9nIGZvcm1hdCwgd2hpY2ggdXNlcyB2YXJpYWJsZXMgZnJvbSBgXCIkY29udGV4dFwiYC5cbiAgICpcbiAgICogU2VlIFtBV1M6IENsb3VkV2F0Y2ggbG9nIGZvcm1hdHMgZm9yIEFQSSBHYXRld2F5XShodHRwczovL2RvY3MuYXdzLmFtYXpvbi5jb20vYXBpZ2F0ZXdheS9sYXRlc3QvZGV2ZWxvcGVyZ3VpZGUvc2V0LXVwLWxvZ2dpbmcuaHRtbCNhcGlnYXRld2F5LWNsb3Vkd2F0Y2gtbG9nLWZvcm1hdHMpXG4gICAqIGZvciBmb3JtYXRzIGFuZCBydWxlcy4gSXQgaXMgcG9zc2libGUgdG8gdXNlIG90aGVyIGZvcm1hdHMgbGlrZSBDTEYgYW5kIFhNTCwgYnV0IHRoaXMgY29uc3RydWN0XG4gICAqIG9ubHkgc3VwcG9ydHMgSlNPTiBmb3Igbm93LlxuICAgKlxuICAgKiBGb3IgYSBsaXN0IG9mIGFsbCBwb3NzaWJsZSB2YXJpYWJsZXMgdG8gbG9nLCBzZWVcbiAgICogW0FXUzogJGNvbnRleHQgVmFyaWFibGVzIGZvciBkYXRhIG1vZGVscywgYXV0aG9yaXplcnMsIG1hcHBpbmcgdGVtcGxhdGVzLCBhbmQgQ2xvdWRXYXRjaCBhY2Nlc3MgbG9nZ2luZ10oaHR0cHM6Ly9kb2NzLmF3cy5hbWF6b24uY29tL2FwaWdhdGV3YXkvbGF0ZXN0L2RldmVsb3Blcmd1aWRlL2FwaS1nYXRld2F5LW1hcHBpbmctdGVtcGxhdGUtcmVmZXJlbmNlLmh0bWwjY29udGV4dC12YXJpYWJsZS1yZWZlcmVuY2UpXG4gICAqIGFuZFxuICAgKiBbQVdTOiAkY29udGV4dCBWYXJpYWJsZXMgZm9yIGFjY2VzcyBsb2dnaW5nIG9ubHldKGh0dHBzOi8vZG9jcy5hd3MuYW1hem9uLmNvbS9hcGlnYXRld2F5L2xhdGVzdC9kZXZlbG9wZXJndWlkZS9hcGktZ2F0ZXdheS1tYXBwaW5nLXRlbXBsYXRlLXJlZmVyZW5jZS5odG1sI2NvbnRleHQtdmFyaWFibGUtcmVmZXJlbmNlLWFjY2Vzcy1sb2dnaW5nLW9ubHkpIC5cbiAgICpcbiAgICogQGRlZmF1bHQge0BsaW5rIGRlZmF1bHRBY2Nlc3NMb2dGb3JtYXR9XG4gICAqL1xuICBhY2Nlc3NMb2dGb3JtYXQ/OiBSZWNvcmQ8c3RyaW5nLCBzdHJpbmc+XG59XG5cbi8qKlxuICogVGhpcyBjb25zdHJ1Y3QgdHJpZXMgdG8gc2ltcGxpZnkgdGhlIGNyZWF0aW9uIG9mIGFuIEFQSSBHYXRld2F5IGZvciBhIHNlcnZpY2UsIGJ5IGNvbGxlY3RpbmcgbW9zdFxuICogb2YgdGhlIGNvbW1vbiBzZXR1cCBoZXJlLlxuICpcbiAqIFRoZSBhcHByb2FjaCBmb2xsb3dlZCBpbiB0aGlzIGNvbnN0cnVjdCBpczpcbiAqIDEuIE9uZSBBUEktR1cgcGVyIHNlcnZpY2VcbiAqIDIuIE9uZSBzdWJkb21haW4gcGVyIEFQSS1HVyAvIHNlcnZpY2VcbiAqIDMuIFVzZSBIVFRQIEFQSSwgbm90IFJFU1RcbiAqIDQuIFVzZSBhICRkZWZhdWx0IHN0YWdlIHdpdGggYXV0b2RlcGxveVxuICogNS4gU3VwcG9ydCBtdWx0aXBsZSByb3V0ZXMgKHdpdGggcG9zc2libGUgYC97cHJveHkrfWAgdG8gbGV0IGFsbCBzdWItcGF0aHMgdGhyb3VnaClcbiAqIDYuIEFsbG93IGN1c3RvbSBpbnRlZ3JhdGlvbi9hdXRob3JpemVyIGZvciBlYWNoIHJvdXRlLCBvciBkZWZhdWx0cyBmb3IgdGhlIHdob2xlIGdhdGV3YXlcbiAqXG4gKiBUaGUgcm91dGUgaW50ZWdyYXRpb24gaXMgb25lIG9mIHRoZXNlOlxuICogLSBBTEIgcHJpdmF0ZSBpbnRlZ3JhdGlvbiB3aXRoIFZQQyBMaW5rIHVzaW5nIEhUVFBTIHRvIHRoZSBBTEJcbiAqIC0gTGFtYmRhIGludGVncmF0aW9uXG4gKiAtIFNRUyBpbnRlZ3JhdGlvblxuICpcbiAqICMjIyBMb2FkIEJhbGFuY2VyIFNlY3VyaXR5IEdyb3VwXG4gKlxuICogTm90ZSB0aGF0IHRoZSBsb2FkIGJhbGFuY2VyIHVzZWQgaW4gYW4ge0BsaW5rIEFsYkludGVncmF0aW9uUHJvcHN9IG11c3QgYWxsb3cgb3V0Ym91bmQgSFRUUFNcbiAqIHRyYWZmaWMgdG8gaXRzIFNlY3VyaXR5R3JvdXAuIE90aGVyd2lzZSwgdGhlIFZQQyBMaW5rIHVzZWQgYnkgdGhlIEFQSS1HVyBjYW4ndCBnZXQgdHJhZmZpYyBmcm9tXG4gKiB0aGUgQUxCLlxuICpcbiAqIGBgYFxuICogY29uc3QgbG9hZEJhbGFuY2VyU2VjdXJpdHlHcm91cCA9IG5ldyBlYzIuU2VjdXJpdHlHcm91cCguLi4sIHtcbiAqICAgYWxsb3dBbGxPdXRib3VuZDogZmFsc2UsXG4gKiB9KVxuICpcbiAqIGxvYWRCYWxhbmNlclNlY3VyaXR5R3JvdXAuYWRkRWdyZXNzUnVsZShcbiAqICAgbG9hZEJhbGFuY2VyU2VjdXJpdHlHcm91cCxcbiAqICAgZWMyLlBvcnQudGNwKDQ0MyksXG4gKiAgIFwiT3V0Ym91bmQgdG8gc2VsZiBmb3IgQUxCIHRvIEFQSS1HVyBWUEMtTGlua1wiLFxuICogKVxuICpcbiAqIGNvbnN0IGxvYWRCYWxhbmNlciA9IG5ldyBsaWZsaWdMb2FkQmFsYW5jZXIuTG9hZEJhbGFuY2VyKC4uLixcbiAqICAge1xuICogICAgIG92ZXJyaWRlTG9hZEJhbGFuY2VyUHJvcHM6IHtcbiAqICAgICAgIHNlY3VyaXR5R3JvdXA6IGxvYWRCYWxhbmNlclNlY3VyaXR5R3JvdXAsXG4gKiAgICAgfSxcbiAqICAgfSxcbiAqIClcbiAqIGBgYFxuICpcbiAqIEB0ZW1wbGF0ZSBBdXRoU2NvcGVzVCBUaGlzIHR5cGUgcGFyYW1ldGVyIGFsbG93cyB5b3UgdG8gaW1wcm92ZSB0eXBlIHNhZmV0eSBvbiB0aGVcbiAqIGByZXF1aXJlZFNjb3BlYCBmaWVsZCBvbiB7QGxpbmsgQ29nbml0b1VzZXJQb29sT3JCYXNpY0F1dGhBdXRob3JpemVyUHJvcHN9LCBieSBuYXJyb3dpbmcgdGhlIHR5cGVcbiAqIHRvIHNwZWNpZmljIHN0cmluZ3MuIFlvdSBjYW4gdGhlbiBleHRlbmQgdGhlIGBBcGlHYXRld2F5YCB3aXRoIHRoaXMgdHlwZSB0byBlbmZvcmNlIHRob3NlIHNjb3Blc1xuICogYWNyb3NzIHRoZSBhcHBsaWNhdGlvbi4gUmVtZW1iZXIgdGhhdCBhdXRoIHNjb3BlcyBtdXN0IGJlIG9uIHRoZSBmb3JtYXRcbiAqIGB7cmVzb3VyY2Ugc2VydmVyIGlkZW50aWZpZXJ9L3tzY29wZSBuYW1lfWAuXG4gKlxuICogRXhhbXBsZTpcbiAqIGBgYFxuICogdHlwZSBBdXRoU2NvcGVzID0gXCJleHRlcm5hbC9yZWFkX3VzZXJzXCIgfCBcImludGVybmFsL2NyZWF0ZV91c2Vyc1wiXG4gKlxuICogZXhwb3J0IGNsYXNzIE15UHJvamVjdEFwaUdhdGV3YXkgZXh0ZW5kcyBBcGlHYXRld2F5PEF1dGhTY29wZXM+IHt9XG4gKiBgYGBcbiAqIFR5cGVTY3JpcHQgd2lsbCB0aGVuIGVuZm9yY2UgdGhhdCBgcmVxdWlyZWRTY29wZWAgaXMgb25lIG9mIGBBdXRoU2NvcGVzYCwgYW5kIHByb3ZpZGVcbiAqIGF1dG8tY29tcGxldGUuXG4gKlxuICogQGF1dGhvciBLcmlzdGlhbiBSZWtzdGFkIDxrcmVAY2FwcmFjb25zdWx0aW5nLm5vPlxuICogQGF1dGhvciBIZXJtYW5uIE3DuHJrcmlkIDxoZW1AbGlmbGlnLm5vPlxuICovXG5leHBvcnQgY2xhc3MgQXBpR2F0ZXdheTxcbiAgQXV0aFNjb3Blc1QgZXh0ZW5kcyBzdHJpbmcgPSBzdHJpbmcsXG4+IGV4dGVuZHMgY29uc3RydWN0cy5Db25zdHJ1Y3Qge1xuICAvKiogVGhlIEFQSSBHYXRld2F5IEhUVFAgQVBJLiBUaGlzIGlzIHRoZSBtYWluIGNvbnN0cnVjdCBmb3IgQVBJLUdXLiAqL1xuICBwdWJsaWMgcmVhZG9ubHkgaHR0cEFwaTogYXBpZ3cuSHR0cEFwaVxuXG4gIC8qKiBUaGUgcm91dGVzIHdoaWNoIGNvbm5lY3QgdGhlIHtAbGluayBodHRwQXBpfSB0byB0aGUgYmFja2VuZCBpbnRlZ3JhdGlvbihzKS4gKi9cbiAgcHVibGljIHJlYWRvbmx5IHJvdXRlczogYXBpZ3cuSHR0cFJvdXRlW10gPSBbXVxuXG4gIC8qKiBUaGUgZG9tYWluIHdoaWNoIGNvbnN1bWVycyBtdXN0IHVzZS4qL1xuICBwdWJsaWMgcmVhZG9ubHkgZG9tYWluOiBzdHJpbmdcblxuICAvKiogQWNjZXNzIGxvZyBncm91cC4gKi9cbiAgcHVibGljIHJlYWRvbmx5IGxvZ0dyb3VwOiBsb2dzLkxvZ0dyb3VwXG5cbiAgcHJpdmF0ZSByZWFkb25seSBwcm9wczogQXBpR2F0ZXdheVByb3BzPEF1dGhTY29wZXNUPlxuXG4gIGNvbnN0cnVjdG9yKFxuICAgIHNjb3BlOiBjb25zdHJ1Y3RzLkNvbnN0cnVjdCxcbiAgICBpZDogc3RyaW5nLFxuICAgIHByb3BzOiBBcGlHYXRld2F5UHJvcHM8QXV0aFNjb3Blc1Q+LFxuICApIHtcbiAgICBzdXBlcihzY29wZSwgaWQpXG4gICAgdGhpcy5wcm9wcyA9IHByb3BzXG5cbiAgICBBcGlHYXRld2F5LnZhbGlkYXRlUHJvcHMocHJvcHMsIGlkLCBjZGsuU3RhY2sub2YodGhpcykpXG5cbiAgICBjb25zdCBjdXN0b21Eb21haW4gPSBuZXcgQ3VzdG9tRG9tYWluKHRoaXMsIFwiQ3VzdG9tRG9tYWluXCIsIHByb3BzLmRucylcbiAgICB0aGlzLmRvbWFpbiA9IGN1c3RvbURvbWFpbi5jdXN0b21Eb21haW5OYW1lXG5cbiAgICBsZXQgY29yc09wdGlvbnM6IGFwaWd3LkNvcnNQcmVmbGlnaHRPcHRpb25zIHwgdW5kZWZpbmVkXG4gICAgaWYgKHByb3BzLmNvcnNBbGxvd0FsbCkge1xuICAgICAgY29yc09wdGlvbnMgPSB7XG4gICAgICAgIGFsbG93T3JpZ2luczogW1wiKlwiXSxcbiAgICAgICAgLy8gXCJUaGUgQXV0aG9yaXphdGlvbiBoZWFkZXIgY2FuJ3QgYmUgd2lsZGNhcmRlZCBhbmQgYWx3YXlzIG5lZWRzIHRvIGJlIGxpc3RlZCBleHBsaWNpdGx5XCJcbiAgICAgICAgLy8gaHR0cHM6Ly9kZXZlbG9wZXIubW96aWxsYS5vcmcvZW4tVVMvZG9jcy9XZWIvSFRUUC9IZWFkZXJzL0FjY2Vzcy1Db250cm9sLUFsbG93LUhlYWRlcnNcbiAgICAgICAgLy8gQ29udGVudC1UeXBlIG1heSBhbHNvIHJlcXVpcmUgZXhwbGljaXQgd2hpdGVsaXN0aW5nOiBodHRwczovL3N0YWNrb3ZlcmZsb3cuY29tL2EvNjM1Njc2NDdcbiAgICAgICAgYWxsb3dIZWFkZXJzOiBbXCJBdXRob3JpemF0aW9uXCIsIFwiQ29udGVudC1UeXBlXCIsIFwiKlwiXSxcbiAgICAgICAgLy8gTm90IHVzaW5nICcqJyBoZXJlLCBiZWNhdXNlIFwiSW4gcmVxdWVzdHMgd2l0aCBjcmVkZW50aWFscywgaXQgaXMgdHJlYXRlZCBhcyB0aGUgbGl0ZXJhbFxuICAgICAgICAvLyBtZXRob2QgbmFtZSAnKicgd2l0aG91dCBzcGVjaWFsIHNlbWFudGljc1wiXG4gICAgICAgIC8vIGh0dHBzOi8vZGV2ZWxvcGVyLm1vemlsbGEub3JnL2VuLVVTL2RvY3MvV2ViL0hUVFAvSGVhZGVycy9BY2Nlc3MtQ29udHJvbC1BbGxvdy1NZXRob2RzXG4gICAgICAgIGFsbG93TWV0aG9kczogW1xuICAgICAgICAgIGFwaWd3LkNvcnNIdHRwTWV0aG9kLkdFVCxcbiAgICAgICAgICBhcGlndy5Db3JzSHR0cE1ldGhvZC5QT1NULFxuICAgICAgICAgIGFwaWd3LkNvcnNIdHRwTWV0aG9kLlBVVCxcbiAgICAgICAgICBhcGlndy5Db3JzSHR0cE1ldGhvZC5QQVRDSCxcbiAgICAgICAgICBhcGlndy5Db3JzSHR0cE1ldGhvZC5ERUxFVEUsXG4gICAgICAgICAgYXBpZ3cuQ29yc0h0dHBNZXRob2QuT1BUSU9OUyxcbiAgICAgICAgICBhcGlndy5Db3JzSHR0cE1ldGhvZC5IRUFELFxuICAgICAgICBdLFxuICAgICAgICBtYXhBZ2U6IGNkay5EdXJhdGlvbi5kYXlzKDEpLFxuICAgICAgfVxuICAgIH1cblxuICAgIC8vIFRoZSBhY3R1YWwgQVBJLiBUaGlzIGhvbGRzIHRoZSByb3V0ZXMsIGF1dGhvcml6ZXJzIGFuZCBpbnRlZ3JhdGlvbnMuXG4gICAgY29uc3QgYXBpID0gbmV3IGFwaWd3Lkh0dHBBcGkodGhpcywgXCJIdHRwQXBpLVwiICsgcHJvcHMuZG5zLnN1YmRvbWFpbiwge1xuICAgICAgLy8gZGVmYXVsdEludGVncmF0aW9uOiBkZWZhdWx0SW50ZWdyYXRpb24sIC8vIFRoaXMgaXMgZm9yIGEgY2F0Y2gtYWxsICRkZWZhdWx0IHJvdXRlXG4gICAgICBkZXNjcmlwdGlvbjogYEFuIEhUVFAgQVBJIGZvciAke3Byb3BzLmRucy5zdWJkb21haW59LiR7cHJvcHMuZG5zLmhvc3RlZFpvbmUuem9uZU5hbWV9LmAsXG4gICAgICBkaXNhYmxlRXhlY3V0ZUFwaUVuZHBvaW50OiB0cnVlLCAvLyBGb3JjZSBleHRlcm5hbHMgdG8gZ28gdGhyb3VnaCBjdXN0b20gZG9tYWluLiBNVVNUIGJlIHRydWUgd2hlbiB1c2luZyBNdXR1YWwgVExTLCBmb3Igc2VjdXJpdHkgcmVhc29uc1xuICAgICAgY3JlYXRlRGVmYXVsdFN0YWdlOiB0cnVlLFxuICAgICAgZGVmYXVsdERvbWFpbk1hcHBpbmc6IHsgZG9tYWluTmFtZTogY3VzdG9tRG9tYWluLmFwaUd3Q3VzdG9tRG9tYWluIH0sXG4gICAgICBjb3JzUHJlZmxpZ2h0OiBjb3JzT3B0aW9ucyxcbiAgICAgIC4uLnByb3BzPy5wcm9wc092ZXJyaWRlPy5odHRwQXBpLFxuICAgIH0pXG4gICAgdGhpcy5odHRwQXBpID0gYXBpXG5cbiAgICBjb25zdCBzdGFnZSA9IGFwaS5kZWZhdWx0U3RhZ2U/Lm5vZGUuZGVmYXVsdENoaWxkIGFzIGFwaWd3LkNmblN0YWdlXG5cbiAgICBjb25zdCBsb2dzID0gbmV3IEFwaUdhdGV3YXlBY2Nlc3NMb2dzKFxuICAgICAgdGhpcyxcbiAgICAgIFwiQWNjZXNzTG9nc1wiLFxuICAgICAgc3RhZ2UsXG4gICAgICBwcm9wcy5hY2Nlc3NMb2dzLFxuICAgICAgZGVmYXVsdEFjY2Vzc0xvZ0Zvcm1hdCxcbiAgICApXG4gICAgdGhpcy5sb2dHcm91cCA9IGxvZ3MubG9nR3JvdXBcblxuICAgIHN0YWdlLmRlZmF1bHRSb3V0ZVNldHRpbmdzID0ge1xuICAgICAgLi4uc3RhZ2UuZGVmYXVsdFJvdXRlU2V0dGluZ3MsXG4gICAgICBkZXRhaWxlZE1ldHJpY3NFbmFibGVkOiBwcm9wcy5kZXRhaWxlZE1ldHJpY3MgPz8gdHJ1ZSxcbiAgICAgIHRocm90dGxpbmdCdXJzdExpbWl0OiBwcm9wcy50aHJvdHRsaW5nPy5idXJzdCwgLy8gRGVmYXVsdCBmb3IgYWNjb3VudCBpcyA1XzAwMFxuICAgICAgdGhyb3R0bGluZ1JhdGVMaW1pdDogcHJvcHMudGhyb3R0bGluZz8ucmF0ZSwgLy8gRGVmYXVsdCBmb3IgYWNjb3VudCBpcyAxMF8wMDBcbiAgICB9XG5cbiAgICBjb25zdCBkZWZhdWx0SW50ZWdyYXRpb24gPSBwcm9wcy5kZWZhdWx0SW50ZWdyYXRpb25cbiAgICAgID8gdGhpcy5jcmVhdGVJbnRlZ3JhdGlvbihwcm9wcy5kZWZhdWx0SW50ZWdyYXRpb24sIHByb3BzLmRucylcbiAgICAgIDogdW5kZWZpbmVkXG5cbiAgICBjb25zdCBkZWZhdWx0QXV0aG9yaXplciA9IHByb3BzLmRlZmF1bHRBdXRob3JpemF0aW9uXG4gICAgICA/IHRoaXMuY3JlYXRlQXV0aG9yaXplcihcIkRlZmF1bHRBdXRob3JpemVyXCIsIHByb3BzLmRlZmF1bHRBdXRob3JpemF0aW9uKVxuICAgICAgOiB1bmRlZmluZWRcblxuICAgIGZvciAoY29uc3Qgcm91dGUgb2YgcHJvcHMucm91dGVzKSB7XG4gICAgICBsZXQgaW50ZWdyYXRpb246IGFwaWd3Lkh0dHBSb3V0ZUludGVncmF0aW9uXG4gICAgICBpZiAocm91dGUuaW50ZWdyYXRpb24pIHtcbiAgICAgICAgaW50ZWdyYXRpb24gPSB0aGlzLmNyZWF0ZUludGVncmF0aW9uKHJvdXRlLmludGVncmF0aW9uLCBwcm9wcy5kbnMpXG4gICAgICB9IGVsc2Uge1xuICAgICAgICBpbnRlZ3JhdGlvbiA9IGRlZmF1bHRJbnRlZ3JhdGlvbiEgLy8gVmVyaWZpZWQgaW4gdmFsaWRhdGVQcm9wc1xuICAgICAgfVxuXG4gICAgICBsZXQgYXV0aG9yaXplcjogYXBpZ3cuSUh0dHBSb3V0ZUF1dGhvcml6ZXIgfCB1bmRlZmluZWRcbiAgICAgIGlmIChyb3V0ZS5hdXRob3JpemF0aW9uKSB7XG4gICAgICAgIGF1dGhvcml6ZXIgPSB0aGlzLmNyZWF0ZUF1dGhvcml6ZXIoXG4gICAgICAgICAgLy8gVXNpbmcgdGhlIHJvdXRlIHBhdGggaGVyZSBjYXVzZWQgQ2xvdWRGb3JtYXRpb24gdG8gZmFpbCBkdWUgdG8gcmVzb3VyY2UgbmFtZXMgYmVpbmcgdG9vXG4gICAgICAgICAgLy8gbG9uZy4gVGhlcmVmb3JlLCB3ZSBub3cgaGFzaCB0aGUgcm91dGUgcGF0aCB0byBnZXQgYSBzaG9ydGVyIG5hbWUuXG4gICAgICAgICAgYFJvdXRlQXV0aG9yaXplciR7c2hvcnRIYXNoKGAke3JvdXRlLm1ldGhvZCA/PyBcIlwifSR7cm91dGUucGF0aH1gKX1gLFxuICAgICAgICAgIHJvdXRlLmF1dGhvcml6YXRpb24sXG4gICAgICAgIClcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIGF1dGhvcml6ZXIgPSBkZWZhdWx0QXV0aG9yaXplclxuICAgICAgfVxuXG4gICAgICBjb25zdCByb3V0ZVBhdGhzID0gW3JvdXRlLnBhdGhdXG4gICAgICBpZiAocm91dGUuaW5jbHVkZVN1YnBhdGhzID09PSB0cnVlKSB7XG4gICAgICAgIC8vIElmIHdlIGluY2x1ZGUgc3ViLXBhdGhzLCB3ZSBhZGQgYW4gYWRkaXRpb25hbCByb3V0ZSB3aXRoIC97cHJveHkrfSAodGhlICsgbWVhbnMgaXQgd2lsbFxuICAgICAgICAvLyBtYXRjaCBhbGwgc3Vicm91dGVzKS4gV2Ugd2FudCBib3RoIHRoaXMgcm91dGUgYW5kIHRoZSBub3JtYWwgcm91dGUgd2l0aG91dCAve3Byb3h5K30sXG4gICAgICAgIC8vIHNpbmNlIC97cHJveHkrfSB3aWxsIG9ubHkgbWF0Y2ggdGhlIGJhc2UgcGF0aCB3aXRoIHRyYWlsaW5nIHNsYXNoLlxuICAgICAgICByb3V0ZVBhdGhzLnB1c2goXG4gICAgICAgICAgcm91dGUucGF0aCArIChyb3V0ZS5wYXRoID09PSBcIi9cIiA/IFwiXCIgOiBcIi9cIikgKyBcIntwcm94eSt9XCIsXG4gICAgICAgIClcbiAgICAgIH1cblxuICAgICAgZm9yIChjb25zdCByb3V0ZVBhdGggb2Ygcm91dGVQYXRocykge1xuICAgICAgICAvLyBUaGUgcHJldmlvdXMgdmVyc2lvbiBvZiB0aGUgQVBJIEdhdGV3YXkgY29uc3RydWN0IGhhZCBhIHNpbmdsZSByb3V0ZSB3aXRoIHRoZSBJRFxuICAgICAgICAvLyAnRGVmYXVsdFByb3h5Um91dGUnLCBhbmQgYWxsIGdhdGV3YXlzIHdlIHVzZWQgZXhwb3NlZCB0aGUgcm91dGUgL3twcm94eSt9LiBXaGVuIHRyeWluZyB0b1xuICAgICAgICAvLyBjaGFuZ2UgdG8gdGhpcyBuZXcgdmVyc2lvbiBvZiB0aGUgY29uc3RydWN0LCBkZXBsb3ltZW50IGZhaWxlZCBmb3IgZ2F0ZXdheXMgZXhwb3NpbmdcbiAgICAgICAgLy8gL3twcm94eSt9LCBiZWNhdXNlIFwiYSByb3V0ZSB3aXRoIHRoYXQga2V5IFtpLmUuIHBhdGggKyBtZXRob2RdIGFscmVhZHkgZXhpc3RzXCIuIFdlIHRoaW5rXG4gICAgICAgIC8vIHRoaXMgbWF5IGJlIGJlY2F1c2Ugd2UgdXNlZCB0aGUgc2FtZSByb3V0ZSBwYXRoLCBidXQgd2l0aCBhIG5ldyByb3V0ZSBJRCwgY2F1c2luZ1xuICAgICAgICAvLyBjb25mdXNpb24gZm9yIENsb3VkRm9ybWF0aW9uLiBTbyB3ZSBrZWVwIHRoZSBvbGQgcm91dGUgSUQgaGVyZSBmb3IgL3twcm94eSt9LlxuICAgICAgICBjb25zdCByb3V0ZUlkID1cbiAgICAgICAgICByb3V0ZVBhdGggPT09IFwiL3twcm94eSt9XCIgPyBcIkRlZmF1bHRQcm94eVJvdXRlXCIgOiBgUm91dGUtJHtyb3V0ZVBhdGh9YFxuXG4gICAgICAgIHRoaXMucm91dGVzLnB1c2goXG4gICAgICAgICAgbmV3IGFwaWd3Lkh0dHBSb3V0ZSh0aGlzLCByb3V0ZUlkLCB7XG4gICAgICAgICAgICBodHRwQXBpOiBhcGksXG4gICAgICAgICAgICBpbnRlZ3JhdGlvbjogaW50ZWdyYXRpb24sXG4gICAgICAgICAgICBhdXRob3JpemVyOiBhdXRob3JpemVyLFxuICAgICAgICAgICAgcm91dGVLZXk6IGFwaWd3Lkh0dHBSb3V0ZUtleS53aXRoKFxuICAgICAgICAgICAgICByb3V0ZVBhdGgsXG4gICAgICAgICAgICAgIHJvdXRlLm1ldGhvZFxuICAgICAgICAgICAgICAgID8gYXBpZ3cuSHR0cE1ldGhvZFtyb3V0ZS5tZXRob2RdXG4gICAgICAgICAgICAgICAgOiBhcGlndy5IdHRwTWV0aG9kLkFOWSxcbiAgICAgICAgICAgICksXG4gICAgICAgICAgfSksXG4gICAgICAgIClcbiAgICAgIH1cbiAgICB9XG5cbiAgICB0YWdSZXNvdXJjZXModGhpcywgKCkgPT4gKHsgc2VydmljZTogcHJvcHMuZG5zLnN1YmRvbWFpbiB9KSlcbiAgfVxuXG4gIC8qKiBAdGhyb3dzIEVycm9yICovXG4gIHByaXZhdGUgc3RhdGljIHZhbGlkYXRlUHJvcHMoXG4gICAgcHJvcHM6IEFwaUdhdGV3YXlQcm9wcyxcbiAgICBpZDogc3RyaW5nLFxuICAgIHN0YWNrOiBjZGsuU3RhY2ssXG4gICkge1xuICAgIGZvciAoY29uc3Qgcm91dGUgb2YgcHJvcHMucm91dGVzKSB7XG4gICAgICBpZiAoIXJvdXRlLmludGVncmF0aW9uICYmICFwcm9wcy5kZWZhdWx0SW50ZWdyYXRpb24pIHtcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKFxuICAgICAgICAgIGBObyBpbnRlZ3JhdGlvbiBkZWZpbmVkIGZvciByb3V0ZSAnJHtyb3V0ZS5wYXRofScsIGFuZCBubyBkZWZhdWx0IGludGVncmF0aW9uIHNwZWNpZmllZCBmb3IgdGhlIGdhdGV3YXlgLFxuICAgICAgICApXG4gICAgICB9XG4gICAgICBpZiAoIXJvdXRlLmF1dGhvcml6YXRpb24gJiYgIXByb3BzLmRlZmF1bHRBdXRob3JpemF0aW9uKSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcihcbiAgICAgICAgICBgTm8gYXV0aG9yaXphdGlvbiBkZWZpbmVkIGZvciByb3V0ZSAnJHtyb3V0ZS5wYXRofScsIGFuZCBubyBkZWZhdWx0IGF1dGhvcml6YXRpb24gc3BlY2lmaWVkIGZvciB0aGUgZ2F0ZXdheWAsXG4gICAgICAgIClcbiAgICAgIH1cbiAgICAgIGlmICghcm91dGUucGF0aC5zdGFydHNXaXRoKFwiL1wiKSkge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoXG4gICAgICAgICAgYEludmFsaWQgcGF0aCAnJHtyb3V0ZS5wYXRofSc6IHBhdGhzIG11c3QgYmVnaW4gd2l0aCAnLycgKHVzZSAnLycgZm9yIHJvb3QgcGF0aClgLFxuICAgICAgICApXG4gICAgICB9XG4gICAgICBpZiAocm91dGUucGF0aCAhPT0gXCIvXCIgJiYgcm91dGUucGF0aC5lbmRzV2l0aChcIi9cIikpIHtcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKFxuICAgICAgICAgIGBJbnZhbGlkIHBhdGggJyR7cm91dGUucGF0aH0nOiBwYXRocyBjYW5ub3QgZW5kIHdpdGggJy8nIChleGNlcHQgcm9vdCBwYXRoKWAsXG4gICAgICAgIClcbiAgICAgIH1cbiAgICAgIGlmIChcbiAgICAgICAgcm91dGUuaW50ZWdyYXRpb24/LnR5cGUgPT09IFwiQUxCXCIgJiZcbiAgICAgICAgcm91dGUuaW50ZWdyYXRpb24uaG9zdE5hbWUuc3RhcnRzV2l0aChcImh0dHBzOlwiKVxuICAgICAgKSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcihcbiAgICAgICAgICBgVGhlIGFsYkludGVncmF0aW9uLmhvc3RIdHRwc05hbWUgc2hvdWxkIG5vdCBpbmNsdWRlIGEgcHJvdG9jb2w6ICR7cm91dGUuaW50ZWdyYXRpb24uaG9zdE5hbWV9YCxcbiAgICAgICAgKVxuICAgICAgfVxuICAgIH1cbiAgICBpZiAocHJvcHMuZG5zLnN1YmRvbWFpbiA9PT0gXCJcIiB8fCBwcm9wcy5kbnMuc3ViZG9tYWluLmluY2x1ZGVzKFwiIFwiKSkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKFxuICAgICAgICBgU3ViZG9tYWluIG11c3QgYmUgc2V0LCBhbmQgbm90IGNvbnRhaW4gc3BhY2VzOiAke3Byb3BzLmRucy5zdWJkb21haW59YCxcbiAgICAgIClcbiAgICB9XG4gICAgaWYgKHByb3BzLnRocm90dGxpbmc/LmJ1cnN0ICYmIHByb3BzLnRocm90dGxpbmcuYnVyc3QgPiA1XzAwMCkge1xuICAgICAgY29uc29sZS53YXJuKFxuICAgICAgICBg4pqg77iPIFlvdXIgdGhyb3R0bGluZyBidXJzdCBsaW1pdCAnJHtwcm9wcy50aHJvdHRsaW5nLmJ1cnN0fScgaXMgaGlnaGVyIHRoYW4gdGhlIEFXUyBBY2NvdW50IGxpbWl0LiBNYWtlIHN1cmUgeW91ciBhY2NvdW50IGhhcyB1cGdyYWRlZCB0aGlzIHF1b3RhISBgLFxuICAgICAgICBzdGFjayxcbiAgICAgICAgaWQsXG4gICAgICApXG4gICAgfVxuICAgIGlmIChwcm9wcy50aHJvdHRsaW5nPy5yYXRlICYmIHByb3BzLnRocm90dGxpbmcucmF0ZSA+IDEwXzAwMCkge1xuICAgICAgY29uc29sZS53YXJuKFxuICAgICAgICBg4pqg77iPIFlvdXIgdGhyb3R0bGluZyByYXRlIGxpbWl0ICcke3Byb3BzLnRocm90dGxpbmcucmF0ZX0nIGlzIGhpZ2hlciB0aGFuIHRoZSBBV1MgQWNjb3VudCBsaW1pdC4gTWFrZSBzdXJlIHlvdXIgYWNjb3VudCBoYXMgdXBncmFkZWQgdGhpcyBxdW90YSEgYCxcbiAgICAgICAgc3RhY2ssXG4gICAgICAgIGlkLFxuICAgICAgKVxuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBUaGUgYXV0aG9yaXplciBvbmx5IGFjY2VwdHMgcmVxdWVzdHMgZnJvbSBleHRlcm5hbCB1c2VycyB0aGF0IGFyZSBhdXRob3JpemVkLlxuICAgKiBVbmF1dGhvcml6ZWQgdXNlcnMgYXJlIHN0b3BwZWQgaW4gdGhlIEFQSS1HVywgYW5kIG5vdCBmb3J3YXJkZWQgdG8gdGhlIGludGVncmF0aW9uLlxuICAgKi9cbiAgcHJpdmF0ZSBjcmVhdGVBdXRob3JpemVyPEF1dGhTY29wZXNUIGV4dGVuZHMgc3RyaW5nPihcbiAgICBpZDogc3RyaW5nLFxuICAgIGF1dGhvcml6YXRpb246IEF1dGhvcml6YXRpb25Qcm9wczxBdXRoU2NvcGVzVD4sXG4gICk6IGFwaWd3LklIdHRwUm91dGVBdXRob3JpemVyIHwgdW5kZWZpbmVkIHtcbiAgICBzd2l0Y2ggKGF1dGhvcml6YXRpb24udHlwZSkge1xuICAgICAgY2FzZSBcIk5PTkVcIjoge1xuICAgICAgICByZXR1cm4gdW5kZWZpbmVkXG4gICAgICB9XG4gICAgICBjYXNlIFwiSUFNXCI6IHtcbiAgICAgICAgLy8gQVBJIEdhdGV3YXkgaW52b2tlcyB5b3VyIEFQSSByb3V0ZSBvbmx5IGlmIHRoZSBjbGllbnQgaGFzIGV4ZWN1dGUtYXBpIHBlcm1pc3Npb24gZm9yIHRoZSByb3V0ZS5cbiAgICAgICAgLy8gVGhlIGNsaWVudCBoYXMgdG8gdXNlIEFXUyBTaWdWNCB0byBpZGVudGlmeSB0aGVtc2VsdmVzIGluIHRoZSByZXF1ZXN0LlxuICAgICAgICAvLyBSZWFkIHRoaXMgcGFnZSBmb3IgaGVscCB3aXRoIElBTSBhbmQgQVBJLUdXIGh0dHBzOi8vZG9jcy5hd3MuYW1hem9uLmNvbS9hcGlnYXRld2F5L2xhdGVzdC9kZXZlbG9wZXJndWlkZS9zZWN1cml0eV9pYW1fc2VydmljZS13aXRoLWlhbS5odG1sXG4gICAgICAgIC8vIE5vdGUgdGhhdCBbcmVzb3VyY2UgcG9saWNpZXMgYXJlIG5vdCBzdXBwb3J0ZWQgeWV0IGZvciBIVFRQXShodHRwczovL2RvY3MuYXdzLmFtYXpvbi5jb20vYXBpZ2F0ZXdheS9sYXRlc3QvZGV2ZWxvcGVyZ3VpZGUvaHR0cC1hcGktYWNjZXNzLWNvbnRyb2wtaWFtLmh0bWwpXG4gICAgICAgIHJldHVybiBuZXcgYXV0aG9yaXplcnMuSHR0cElhbUF1dGhvcml6ZXIoKVxuICAgICAgfVxuICAgICAgY2FzZSBcIkNPR05JVE9fVVNFUl9QT09MXCI6IHtcbiAgICAgICAgLy8gV2UgdXNlIGEgY3VzdG9tIGxhbWJkYSBhdXRob3JpemVyIGhlcmUgaW5zdGVhZCBvZiB0aGUgYEh0dHBVc2VyUG9vbEF1dGhvcml6ZXJgIHByb3ZpZGVkXG4gICAgICAgIC8vIGJ5IENESywgaW4gb3JkZXIgdG8gc3VwcG9ydCBgcmVxdWlyZWRTY29wZWAgYW5kIHNldHRpbmcgb2YgY3VzdG9tIGNvbnRleHQgdmFyaWFibGVzLlxuICAgICAgICBjb25zdCBhdXRob3JpemVyID0gbmV3IENvZ25pdG9Vc2VyUG9vbEF1dGhvcml6ZXIoXG4gICAgICAgICAgdGhpcyxcbiAgICAgICAgICBpZCArIFwiTGFtYmRhXCIsXG4gICAgICAgICAgYXV0aG9yaXphdGlvbixcbiAgICAgICAgKVxuXG4gICAgICAgIHJldHVybiBuZXcgYXV0aG9yaXplcnMuSHR0cExhbWJkYUF1dGhvcml6ZXIoaWQsIGF1dGhvcml6ZXIubGFtYmRhLCB7XG4gICAgICAgICAgcmVzcG9uc2VUeXBlczogYXV0aG9yaXplci5yZXNwb25zZVR5cGVzLFxuICAgICAgICAgIC8vIE1heCAxaCwgb3IgZGlzYWJsZWQgKDBzKS4gVGhlIHZhbHVlIG9ubHkgbWF0dGVycyB3aGVuIGludmFsaWRhdGluZy9keW5hbWljIGNyZWRlbnRpYWxzXG4gICAgICAgICAgcmVzdWx0c0NhY2hlVHRsOiBjZGsuRHVyYXRpb24uaG91cnMoMSksXG4gICAgICAgIH0pXG4gICAgICB9XG4gICAgICBjYXNlIFwiQkFTSUNfQVVUSFwiOiB7XG4gICAgICAgIGNvbnN0IGF1dGhvcml6ZXIgPSBuZXcgQmFzaWNBdXRoQXV0aG9yaXplcihcbiAgICAgICAgICB0aGlzLFxuICAgICAgICAgIGlkICsgXCJMYW1iZGFcIixcbiAgICAgICAgICBhdXRob3JpemF0aW9uLFxuICAgICAgICApXG5cbiAgICAgICAgcmV0dXJuIG5ldyBhdXRob3JpemVycy5IdHRwTGFtYmRhQXV0aG9yaXplcihpZCwgYXV0aG9yaXplci5sYW1iZGEsIHtcbiAgICAgICAgICByZXNwb25zZVR5cGVzOiBhdXRob3JpemVyLnJlc3BvbnNlVHlwZXMsXG4gICAgICAgICAgLy8gTWF4IDFoLCBvciBkaXNhYmxlZCAoMHMpLiBUaGUgdmFsdWUgb25seSBtYXR0ZXJzIHdoZW4gaW52YWxpZGF0aW5nL2R5bmFtaWMgY3JlZGVudGlhbHNcbiAgICAgICAgICByZXN1bHRzQ2FjaGVUdGw6IGNkay5EdXJhdGlvbi5taW51dGVzKDMwKSxcbiAgICAgICAgfSlcbiAgICAgIH1cbiAgICAgIGNhc2UgXCJDT0dOSVRPX1VTRVJfUE9PTF9PUl9CQVNJQ19BVVRIXCI6IHtcbiAgICAgICAgY29uc3QgYXV0aG9yaXplciA9IG5ldyBDb2duaXRvVXNlclBvb2xPckJhc2ljQXV0aEF1dGhvcml6ZXIoXG4gICAgICAgICAgdGhpcyxcbiAgICAgICAgICBpZCArIFwiTGFtYmRhXCIsXG4gICAgICAgICAgYXV0aG9yaXphdGlvbixcbiAgICAgICAgKVxuXG4gICAgICAgIHJldHVybiBuZXcgYXV0aG9yaXplcnMuSHR0cExhbWJkYUF1dGhvcml6ZXIoaWQsIGF1dGhvcml6ZXIubGFtYmRhLCB7XG4gICAgICAgICAgcmVzcG9uc2VUeXBlczogYXV0aG9yaXplci5yZXNwb25zZVR5cGVzLFxuICAgICAgICAgIC8vIE1heCAxaCwgb3IgZGlzYWJsZWQgKDBzKS4gVGhlIHZhbHVlIG9ubHkgbWF0dGVycyB3aGVuIGludmFsaWRhdGluZy9keW5hbWljIGNyZWRlbnRpYWxzXG4gICAgICAgICAgcmVzdWx0c0NhY2hlVHRsOiBjZGsuRHVyYXRpb24uaG91cnMoMSksXG4gICAgICAgIH0pXG4gICAgICB9XG4gICAgfVxuICB9XG5cbiAgcHJpdmF0ZSBjcmVhdGVJbnRlZ3JhdGlvbihcbiAgICBpbnRlZ3JhdGlvbjogSW50ZWdyYXRpb25Qcm9wcyxcbiAgICBkbnM6IEFwaUdhdGV3YXlQcm9wc1tcImRuc1wiXSxcbiAgKTogYXBpZ3cuSHR0cFJvdXRlSW50ZWdyYXRpb24ge1xuICAgIHN3aXRjaCAoaW50ZWdyYXRpb24udHlwZSkge1xuICAgICAgY2FzZSBcIkFMQlwiOiB7XG4gICAgICAgIC8vIFRoZSBWUENMaW5rIGNvbm5lY3RzIHRoZSBpbnRlZ3JhdGlvbiBpbnRvIHRoZSBBTEIncyBWUEMuIE11c3QgYmUgbWFudWFsbHkgY3JlYXRlZCB3aGVuXG4gICAgICAgIC8vIHRoZSBWUEMgaXMgaW1wb3J0ZWQsIHdoaWNoIGlzIHRoZSBjYXNlIHdoZW4gdXNpbmcgQ29yZVBsYXRmb3JtQ29uc3VtZXIuXG4gICAgICAgIGNvbnN0IHZwY0xpbmsgPSBuZXcgYXBpZ3cuVnBjTGluayh0aGlzLCBcIkFsYlZwY0xpbmtcIiwge1xuICAgICAgICAgIHZwYzogaW50ZWdyYXRpb24udnBjLFxuICAgICAgICAgIHNlY3VyaXR5R3JvdXBzOiBbaW50ZWdyYXRpb24uc2VjdXJpdHlHcm91cF0sXG4gICAgICAgICAgc3VibmV0czogaW50ZWdyYXRpb24udnBjLnNlbGVjdFN1Ym5ldHMoKSwgLy8gVGhpcyBjb3JyZWN0bHkgc2VsZWN0cyB0aGUgcHJpdmF0ZSBzdWJuZXRzXG4gICAgICAgIH0pXG5cbiAgICAgICAgY29uc3QgcGFyYW1ldGVyTWFwcGluZyA9IG5ldyBhcGlndy5QYXJhbWV0ZXJNYXBwaW5nKClcbiAgICAgICAgICAvKiogU2VlIHtAbGluayBBbGJJbnRlZ3JhdGlvblByb3BzLmhvc3ROYW1lfSAqL1xuICAgICAgICAgIC5vdmVyd3JpdGVIZWFkZXIoXG4gICAgICAgICAgICBcIkhvc3RcIixcbiAgICAgICAgICAgIC8vIFRoZSBIb3N0IGhlYWRlciBjYW4gTk9UIHVzZSBkeW5hbWljIG1hcHBpbmcsIGxpa2UgJGNvbnRleHQuZG9tYWluTmFtZSBldGMuXG4gICAgICAgICAgICBhcGlndy5NYXBwaW5nVmFsdWUuY3VzdG9tKGludGVncmF0aW9uLmhvc3ROYW1lKSxcbiAgICAgICAgICApXG4gICAgICAgIGlmIChpbnRlZ3JhdGlvbi5tYXBQYXJhbWV0ZXJzICE9PSB1bmRlZmluZWQpIHtcbiAgICAgICAgICBpbnRlZ3JhdGlvbi5tYXBQYXJhbWV0ZXJzKHBhcmFtZXRlck1hcHBpbmcpXG4gICAgICAgIH1cblxuICAgICAgICByZXR1cm4gbmV3IGludGVncmF0aW9ucy5IdHRwQWxiSW50ZWdyYXRpb24oXG4gICAgICAgICAgXCJBbGJJbnRlZ3JhdGlvbi1cIiArIGRucy5zdWJkb21haW4sXG4gICAgICAgICAgaW50ZWdyYXRpb24ubG9hZEJhbGFuY2VyTGlzdGVuZXIsXG4gICAgICAgICAge1xuICAgICAgICAgICAgc2VjdXJlU2VydmVyTmFtZTogaW50ZWdyYXRpb24uaG9zdE5hbWUsXG4gICAgICAgICAgICB2cGNMaW5rOiB2cGNMaW5rLFxuICAgICAgICAgICAgbWV0aG9kOiBhcGlndy5IdHRwTWV0aG9kLkFOWSxcbiAgICAgICAgICAgIHBhcmFtZXRlck1hcHBpbmcsXG4gICAgICAgICAgfSxcbiAgICAgICAgKVxuICAgICAgfVxuICAgICAgY2FzZSBcIkxhbWJkYVwiOiB7XG4gICAgICAgIHJldHVybiBuZXcgaW50ZWdyYXRpb25zLkh0dHBMYW1iZGFJbnRlZ3JhdGlvbihcbiAgICAgICAgICBcIkxhbWJkYUludGVncmF0aW9uXCIsXG4gICAgICAgICAgaW50ZWdyYXRpb24ubGFtYmRhLFxuICAgICAgICAgIHtcbiAgICAgICAgICAgIHBheWxvYWRGb3JtYXRWZXJzaW9uOiBhcGlndy5QYXlsb2FkRm9ybWF0VmVyc2lvbi5WRVJTSU9OXzJfMCxcbiAgICAgICAgICAgIHBhcmFtZXRlck1hcHBpbmc6IHVuZGVmaW5lZCxcbiAgICAgICAgICB9LFxuICAgICAgICApXG4gICAgICB9XG4gICAgICBjYXNlIFwiU1FTXCI6IHtcbiAgICAgICAgLy8gQVBJLUdXIGRvZXMgbm90IGhhdmUgYWNjZXNzIHRvIFNRUyBieSBkZWZhdWx0XG4gICAgICAgIGNvbnN0IHJvbGUgPSBuZXcgaWFtLlJvbGUoXG4gICAgICAgICAgdGhpcyxcbiAgICAgICAgICBgQXBpR3dUbyR7aW50ZWdyYXRpb24ucXVldWUubm9kZS5pZH1TZXJ2aWNlUm9sZWAsXG4gICAgICAgICAge1xuICAgICAgICAgICAgZGVzY3JpcHRpb246XG4gICAgICAgICAgICAgIFwiQWxsb3dzIEFQSS1HVyB0byBhZGQgbWVzc2FnZXMgdG8gXCIgKyBpbnRlZ3JhdGlvbi5xdWV1ZS5xdWV1ZUFybixcbiAgICAgICAgICAgIGFzc3VtZWRCeTogbmV3IGlhbS5TZXJ2aWNlUHJpbmNpcGFsKFwiYXBpZ2F0ZXdheS5hbWF6b25hd3MuY29tXCIpLFxuICAgICAgICAgIH0sXG4gICAgICAgIClcbiAgICAgICAgaW50ZWdyYXRpb24ucXVldWUuZ3JhbnRTZW5kTWVzc2FnZXMocm9sZSlcblxuICAgICAgICBsZXQgcGFyYW1ldGVyTWFwcGluZyA9IG5ldyBhcGlndy5QYXJhbWV0ZXJNYXBwaW5nKClcbiAgICAgICAgICAvLyBodHRwczovL2RvY3MuYXdzLmFtYXpvbi5jb20vYXBpZ2F0ZXdheS9sYXRlc3QvZGV2ZWxvcGVyZ3VpZGUvaHR0cC1hcGktZGV2ZWxvcC1pbnRlZ3JhdGlvbnMtYXdzLXNlcnZpY2VzLXJlZmVyZW5jZS5odG1sI1NRUy1TZW5kTWVzc2FnZVxuICAgICAgICAgIC5jdXN0b20oXCJRdWV1ZVVybFwiLCBpbnRlZ3JhdGlvbi5xdWV1ZS5xdWV1ZVVybClcbiAgICAgICAgICAuY3VzdG9tKFwiTWVzc2FnZUJvZHlcIiwgXCIkcmVxdWVzdC5ib2R5XCIpXG4gICAgICAgICAgLmN1c3RvbShcIlJlZ2lvblwiLCBcImV1LXdlc3QtMVwiKSAvLyBDaGFuZ2UgdGhpcyBpZiB0aGUgU1FTIHF1ZXVlIGlzIGluIGFub3RoZXIgcmVnaW9uIVxuXG4gICAgICAgIGlmIChpbnRlZ3JhdGlvbi5tZXNzYWdlQXR0cmlidXRlcykge1xuICAgICAgICAgIHBhcmFtZXRlck1hcHBpbmcgPSBwYXJhbWV0ZXJNYXBwaW5nLmN1c3RvbShcbiAgICAgICAgICAgIFwiTWVzc2FnZUF0dHJpYnV0ZXNcIixcbiAgICAgICAgICAgIEpTT04uc3RyaW5naWZ5KGludGVncmF0aW9uLm1lc3NhZ2VBdHRyaWJ1dGVzKSxcbiAgICAgICAgICApXG4gICAgICAgIH1cblxuICAgICAgICByZXR1cm4gbmV3IFNxc1JvdXRlSW50ZWdyYXRpb24oXCJTcXNJbnRlZ3JhdGlvblwiLCB7XG4gICAgICAgICAgdHlwZTogYXBpZ3cuSHR0cEludGVncmF0aW9uVHlwZS5BV1NfUFJPWFksXG4gICAgICAgICAgc3VidHlwZTogYXBpZ3cuSHR0cEludGVncmF0aW9uU3VidHlwZS5TUVNfU0VORF9NRVNTQUdFLFxuICAgICAgICAgIGNyZWRlbnRpYWxzOiBhcGlndy5JbnRlZ3JhdGlvbkNyZWRlbnRpYWxzLmZyb21Sb2xlKHJvbGUpLFxuICAgICAgICAgIHBhcmFtZXRlck1hcHBpbmc6IHBhcmFtZXRlck1hcHBpbmcsXG4gICAgICAgICAgcGF5bG9hZEZvcm1hdFZlcnNpb246IGFwaWd3LlBheWxvYWRGb3JtYXRWZXJzaW9uLlZFUlNJT05fMV8wLFxuICAgICAgICB9KVxuICAgICAgfVxuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBBbGxvd3MgYSBncmFudGFibGUgdGFyZ2V0IChyb2xlLCB1c2VyIGV0Yy4pIHBlcm1pc3Npb24gdG8gaW52b2tlIHRoZSBBUEkuXG4gICAqIE9ubHkgd29ya3Mgd2hlbiB1c2luZyBgSUFNYCBhcyB7QGxpbmsgQXBpR2F0ZXdheVJvdXRlLmF1dGhvcml6YXRpb259LlxuICAgKlxuICAgKiBAcGFyYW0gdGFyZ2V0IEEgZ3JhbnRhYmxlLCBsaWtlIHtAbGluayBpYW0uUm9sZX1cbiAgICovXG4gIHB1YmxpYyBncmFudEludm9rZSh0YXJnZXQ6IGlhbS5JR3JhbnRhYmxlKSB7XG4gICAgZm9yIChjb25zdCByb3V0ZVByb3BzIG9mIHRoaXMucHJvcHMucm91dGVzKSB7XG4gICAgICBjb25zdCBhdXRoVHlwZSA9XG4gICAgICAgIHJvdXRlUHJvcHMuYXV0aG9yaXphdGlvbj8udHlwZSA/PyB0aGlzLnByb3BzLmRlZmF1bHRBdXRob3JpemF0aW9uPy50eXBlXG5cbiAgICAgIGlmIChhdXRoVHlwZSAhPT0gXCJJQU1cIikge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoXG4gICAgICAgICAgYENhbm5vdCBncmFudCBpbnZva2UgZm9yIGFuIEFQSSBHYXRld2F5IHdoZW4gbm90IHVzaW5nIElBTSBhdXRoIChmb3VuZCBhdXRoICcke2F1dGhUeXBlfScgb24gcm91dGUgJyR7cm91dGVQcm9wcy5wYXRofScpIFske2Nkay5TdGFjay5vZih0aGlzKS5zdGFja05hbWV9XWAsXG4gICAgICAgIClcbiAgICAgIH1cbiAgICB9XG5cbiAgICBmb3IgKGNvbnN0IHJvdXRlIG9mIHRoaXMucm91dGVzKSB7XG4gICAgICByb3V0ZS5ncmFudEludm9rZSh0YXJnZXQpXG4gICAgfVxuICB9XG59XG5cbi8qKlxuICogQ3JlYXRlcyBhIGN1c3RvbSBkb21haW4gZm9yIHRoZSBBUEktR2F0ZXdheSwgYSBSb3V0ZTUzIHJlY29yZCBhbmQgYW4gSFRUUFMgY2VydC5cbiAqIEBhdXRob3IgS3Jpc3RpYW4gUmVrc3RhZCA8a3JlQGNhcHJhY29uc3VsdGluZy5ubz5cbiAqL1xuY2xhc3MgQ3VzdG9tRG9tYWluIGV4dGVuZHMgY29uc3RydWN0cy5Db25zdHJ1Y3Qge1xuICBwdWJsaWMgcmVhZG9ubHkgYXBpR3dDdXN0b21Eb21haW46IGFwaWd3LkRvbWFpbk5hbWVcblxuICAvKiogVGhlIEZ1bGx5IFF1YWxpZmllZCBEb21haW4gTmFtZSAoRlFETikgbGlrZSBgcHJvZHVjdC5wbGF0Zm9ybS5leGFtcGxlLm5vYC4gKi9cbiAgcHVibGljIHJlYWRvbmx5IGN1c3RvbURvbWFpbk5hbWU6IHN0cmluZ1xuXG4gIGNvbnN0cnVjdG9yKFxuICAgIHNjb3BlOiBjb25zdHJ1Y3RzLkNvbnN0cnVjdCxcbiAgICBpZDogc3RyaW5nLFxuICAgIHByb3BzOiBBcGlHYXRld2F5RG5zUHJvcHMsXG4gICkge1xuICAgIHN1cGVyKHNjb3BlLCBpZClcbiAgICB0aGlzLmN1c3RvbURvbWFpbk5hbWUgPSBgJHtwcm9wcy5zdWJkb21haW59LiR7cHJvcHMuaG9zdGVkWm9uZS56b25lTmFtZX1gXG5cbiAgICAvLyBDYW4gYWxzbyB1c2Ugd2lsZGNhcmQgY2VydHMgaW5zdGVhZCEgQ2hlYXBlclxuICAgIC8qKiBBbGxvd3MgZXh0ZXJuYWwgdXNlcnMgdG8gY29ubmVjdCB3aXRoIEhUVFBTLiAqL1xuICAgIGNvbnN0IGN1c3RvbURvbWFpbkNlcnQgPSBuZXcgYWNtLkNlcnRpZmljYXRlKHRoaXMsIFwiSHR0cHNDZXJ0aWZpY2F0ZVwiLCB7XG4gICAgICBkb21haW5OYW1lOiB0aGlzLmN1c3RvbURvbWFpbk5hbWUsXG4gICAgICB2YWxpZGF0aW9uOiBhY20uQ2VydGlmaWNhdGVWYWxpZGF0aW9uLmZyb21EbnMocHJvcHMuaG9zdGVkWm9uZSksXG4gICAgfSlcblxuICAgIC8vIE5vdGUgdGhhdCBBUEktR1cgY2FuIGFsc28gc3VwcG9ydCB3aWxkY2FyZCBkb21haW5zISBodHRwczovL2RvY3MuYXdzLmFtYXpvbi5jb20vYXBpZ2F0ZXdheS9sYXRlc3QvZGV2ZWxvcGVyZ3VpZGUvaHR0cC1hcGktY3VzdG9tLWRvbWFpbi1uYW1lcy5odG1sI2h0dHAtd2lsZGNhcmQtY3VzdG9tLWRvbWFpbi1uYW1lc1xuICAgIC8vIEJ1dCB0aGlzIHdpbGwgbm90IHdvcmsgd2hlbiBBV1MgYWNjb3VudCBYIGhhcyBDdXN0b21Eb21haW4gYHN0YWdpbmcucGxhdGZvcm0uZXhhbXBsZS5ub2AgYW5kIGFjY291bnQgWSBoYXMgQ3VzdG9tRG9tYWluIGAqLnBsYXRmb3JtLmV4YW1wbGUubm9gLlxuICAgIC8vIE5vdCBzdXJlIGhvdyBzdWItc3ViZG9tYWlucyBhcmUgYWZmZWN0ZWQ6IGBteXNlcnZpY2Uuc3RhZ2luZy5wbGF0Zm9ybS5leGFtcGxlLm5vYCBhbmQgYCoucGxhdGZvcm0uZXhhbXBsZS5ub2AuXG4gICAgdGhpcy5hcGlHd0N1c3RvbURvbWFpbiA9IG5ldyBhcGlndy5Eb21haW5OYW1lKFxuICAgICAgdGhpcyxcbiAgICAgIFwiRG9tYWluTmFtZS1cIiArIHByb3BzLnN1YmRvbWFpbixcbiAgICAgIHtcbiAgICAgICAgZG9tYWluTmFtZTogdGhpcy5jdXN0b21Eb21haW5OYW1lLFxuICAgICAgICBjZXJ0aWZpY2F0ZTogY3VzdG9tRG9tYWluQ2VydCxcbiAgICAgICAgZW5kcG9pbnRUeXBlOiBhcGlndy5FbmRwb2ludFR5cGUuUkVHSU9OQUwsXG4gICAgICAgIHNlY3VyaXR5UG9saWN5OiBhcGlndy5TZWN1cml0eVBvbGljeS5UTFNfMV8yLFxuICAgICAgfSxcbiAgICApXG5cbiAgICAvLyBUaGlzIG1ha2VzIHRoZSBBUEktR1cgcHVibGljbHkgYXZhaWxhYmxlIG9uIHRoZSBjdXN0b20gZG9tYWluIG5hbWUuXG4gICAgbmV3IHJvdXRlNTMuQVJlY29yZCh0aGlzLCBcIlJvdXRlNTNBUmVjb3JkQXBpZ3dBbGlhc1wiLCB7XG4gICAgICByZWNvcmROYW1lOiBwcm9wcy5zdWJkb21haW4sXG4gICAgICB6b25lOiBwcm9wcy5ob3N0ZWRab25lLFxuICAgICAgdGFyZ2V0OiByb3V0ZTUzLlJlY29yZFRhcmdldC5mcm9tQWxpYXMoXG4gICAgICAgIG5ldyByb3V0ZTUzVGFyZ2V0cy5BcGlHYXRld2F5djJEb21haW5Qcm9wZXJ0aWVzKFxuICAgICAgICAgIHRoaXMuYXBpR3dDdXN0b21Eb21haW4ucmVnaW9uYWxEb21haW5OYW1lLFxuICAgICAgICAgIHRoaXMuYXBpR3dDdXN0b21Eb21haW4ucmVnaW9uYWxIb3N0ZWRab25lSWQsXG4gICAgICAgICksXG4gICAgICApLFxuICAgICAgdHRsOiBwcm9wcy50dGwgPz8gY2RrLkR1cmF0aW9uLm1pbnV0ZXMoNSksIC8vIExvdyBUVEwgbWFrZXMgaXQgZWFzaWVyIHRvIGRvIGNoYW5nZXNcbiAgICB9KVxuICB9XG59XG5cbi8qKiBBY3RzIGFzIGdsdWUgKGJldHdlZW4gdGhlIGludGVncmF0aW9uIHByb3BzIGFuZCB0aGUgSHR0cEFwaSkgd2hlbiBjcmVhdGluZyBhbiBTcXNJbnRlZ3JhdGlvbi4gKi9cbmNsYXNzIFNxc1JvdXRlSW50ZWdyYXRpb24gZXh0ZW5kcyBhcGlndy5IdHRwUm91dGVJbnRlZ3JhdGlvbiB7XG4gIC8qKlxuICAgKiBAcGFyYW0gaWQgVGhlIGlkIHVzZWQgaW4gdGhlIHtAbGluayBhcGlndy5IdHRwSW50ZWdyYXRpb259IGNvbnN0cnVjdFxuICAgKiAgICBjcmVhdGVkIGludGVybmFsbHkgYnkge0BsaW5rIGFwaWd3Lkh0dHBSb3V0ZUludGVncmF0aW9uLl9iaW5kVG9Sb3V0ZX0uXG4gICAqICAgIFtTb3VyY2UgY29kZV0oaHR0cHM6Ly9naXRodWIuY29tL2F3cy9hd3MtY2RrL2Jsb2IvYjVhZTM3NzgyYmMzY2I2MzdlZWVmOWZiYjFmYmUyYzVlZmRmYzA2OC9wYWNrYWdlcy8lNDBhd3MtY2RrL2F3cy1hcGlnYXRld2F5djIvbGliL2h0dHAvaW50ZWdyYXRpb24udHMjTDMyMSlcbiAgICogQHBhcmFtIGludGVncmF0aW9uUHJvcHMgVGhlIHByb3BzIHRvIHBhc3MgdG8gdGhlIHtAbGluayBhcGlndy5IdHRwSW50ZWdyYXRpb259IGNvbnN0cnVjdC5cbiAgICovXG4gIGNvbnN0cnVjdG9yKFxuICAgIGlkOiBzdHJpbmcsXG4gICAgcHJpdmF0ZSBpbnRlZ3JhdGlvblByb3BzOiBhcGlndy5IdHRwUm91dGVJbnRlZ3JhdGlvbkNvbmZpZyxcbiAgKSB7XG4gICAgc3VwZXIoaWQpXG4gIH1cblxuICAvKipcbiAgICogVGhpcyBzZW5kcyB0aGUgcHJvcGVydGllcyBuZWVkZWQgZm9yIGNyZWF0aW5nIGEge0BsaW5rIGFwaWd3Lkh0dHBJbnRlZ3JhdGlvbn0gdG8gdGhlXG4gICAqIHtAbGluayBhcGlndy5IdHRwUm91dGVJbnRlZ3JhdGlvbn0uXG4gICAqL1xuICBiaW5kKCk6IGFwaWd3Lkh0dHBSb3V0ZUludGVncmF0aW9uQ29uZmlnIHtcbiAgICAvLyBUaGlzIG1ldGhvZCBpcyBjYWxsZWQgYnk6XG4gICAgLy8gaHR0cHM6Ly9naXRodWIuY29tL2F3cy9hd3MtY2RrL2Jsb2IvYjVhZTM3NzgyYmMzY2I2MzdlZWVmOWZiYjFmYmUyYzVlZmRmYzA2OC9wYWNrYWdlcy8lNDBhd3MtY2RrL2F3cy1hcGlnYXRld2F5djIvbGliL2h0dHAvaW50ZWdyYXRpb24udHMjTDMxOVxuICAgIHJldHVybiB0aGlzLmludGVncmF0aW9uUHJvcHNcbiAgfVxufVxuXG4vKipcbiAqIFdoZW4gdXNpbmcgYE5vZGVqc0Z1bmN0aW9uYCB3aXRoIGBlbnRyeWAsIHdlIHBvaW50IENESyB0byBhIGZpbGUgd2l0aCBvdXIgbGFtYmRhIGNvZGUuIFRoaXMgY2FuXG4gKiBlaXRoZXIgYmUgYSBUeXBlU2NyaXB0IG9yIEphdmFTY3JpcHQgZmlsZS4gV2hlbiBjcmVhdGluZyBjb25zdHJ1Y3RzIGluIHRlc3RzIGluc2lkZSB0aGlzIGxpYnJhcnksXG4gKiB0aGUgZW50cnkgd2lsbCBwb2ludCB0byB0aGUgVHlwZVNjcmlwdCBmaWxlIGluIHRoZSBzb3VyY2UgY29kZS4gQnV0IHdoZW4gYSBsaWJyYXJ5IGNvbnN1bWVyIHVzZXNcbiAqIHRoaXMsIGl0IHdpbGwgaW5zdGVhZCBwb2ludCB0byB0aGUgdHJhbnNwaWxlZCBKYXZhU2NyaXB0IGZpbGUuIFNvIHRvIHNldCB0aGUgY29ycmVjdCBmaWxlXG4gKiBleHRlbnNpb24sIHdlIG11c3QgY2hlY2sgaWYgd2UncmUgYmVpbmcgcnVuIGluIHRoZSBjb250ZXh0IG9mIHRoZSBUeXBlU2NyaXB0IHNvdXJjZSBjb2RlXG4gKiAoc3JjL2FwaS1nYXRld2F5KSwgb3RoZXJ3aXNlIHdlJ3JlIGJlaW5nIHJ1biBieSBhIGNvbnN1bWVyIGFzIHRyYW5zcGlsZWQgSmF2YVNjcmlwdC5cbiAqL1xuY29uc3QgYXV0aG9yaXplckZpbGVFeHRlbnNpb24gPSBfX2Rpcm5hbWUuZW5kc1dpdGgoXCJzcmMvYXBpLWdhdGV3YXlcIilcbiAgPyBcInRzXCJcbiAgOiBcImpzXCJcblxuLyoqXG4gKiBDcmVhdGVzIGEgY3VzdG9tIGF1dGhvcml6ZXIgbGFtYmRhIHdoaWNoIHJlYWRzIGBBdXRob3JpemF0aW9uOiBCZWFyZXIgPHRva2VuPmAgaGVhZGVyIGFuZFxuICogdmVyaWZpZXMgdGhlIHRva2VuIGFnYWluc3QgYSBDb2duaXRvIHVzZXIgcG9vbC5cbiAqL1xuY2xhc3MgQ29nbml0b1VzZXJQb29sQXV0aG9yaXplcjxcbiAgQXV0aFNjb3Blc1QgZXh0ZW5kcyBzdHJpbmcsXG4+IGV4dGVuZHMgY29uc3RydWN0cy5Db25zdHJ1Y3Qge1xuICBwdWJsaWMgcmVhZG9ubHkgbGFtYmRhOiBsYW1iZGEuSUZ1bmN0aW9uXG5cbiAgLyoqXG4gICAqIFVzZSBzaW1wbGUgcmVzcG9uc2UgdHlwZSAoYHsgaXNBdXRob3JpemVkOiB0cnVlL2ZhbHNlIH1gKSwgYXMgb3Bwb3NlZCB0byByZXR1cm5pbmcgYW4gSUFNXG4gICAqIFBvbGljeSBkb2N1bWVudC5cbiAgICovXG4gIHB1YmxpYyByZWFkb25seSByZXNwb25zZVR5cGVzOiBhdXRob3JpemVycy5IdHRwTGFtYmRhUmVzcG9uc2VUeXBlW10gPSBbXG4gICAgYXV0aG9yaXplcnMuSHR0cExhbWJkYVJlc3BvbnNlVHlwZS5TSU1QTEUsXG4gIF1cblxuICBjb25zdHJ1Y3RvcihcbiAgICBzY29wZTogY29uc3RydWN0cy5Db25zdHJ1Y3QsXG4gICAgaWQ6IHN0cmluZyxcbiAgICBwcm9wczogQ29nbml0b1VzZXJQb29sQXV0aG9yaXplclByb3BzPEF1dGhTY29wZXNUPixcbiAgKSB7XG4gICAgc3VwZXIoc2NvcGUsIGlkKVxuXG4gICAgdGhpcy5sYW1iZGEgPSBuZXcgbGFtYmRhTm9kZWpzLk5vZGVqc0Z1bmN0aW9uKHRoaXMsIFwiQXV0aG9yaXplckZ1bmN0aW9uXCIsIHtcbiAgICAgIGVudHJ5OiBwYXRoLmpvaW4oXG4gICAgICAgIF9fZGlybmFtZSxcbiAgICAgICAgYGF1dGhvcml6ZXItbGFtYmRhcy9jb2duaXRvLXVzZXItcG9vbC1hdXRob3JpemVyLiR7YXV0aG9yaXplckZpbGVFeHRlbnNpb259YCxcbiAgICAgICksXG4gICAgICBydW50aW1lOiBsYW1iZGEuUnVudGltZS5OT0RFSlNfMjJfWCxcbiAgICAgIHRpbWVvdXQ6IGNkay5EdXJhdGlvbi5zZWNvbmRzKDUpLFxuICAgICAgZW52aXJvbm1lbnQ6IHtcbiAgICAgICAgW1wiVVNFUl9QT09MX0lEXCJdOiBwcm9wcy51c2VyUG9vbC51c2VyUG9vbElkLFxuICAgICAgICBbXCJSRVFVSVJFRF9TQ09QRVwiXTogcHJvcHMucmVxdWlyZWRTY29wZSA/PyBcIlwiLFxuICAgICAgICBbXCJDUkVERU5USUFMU19GT1JfSU5URVJOQUxfQVVUSE9SSVpBVElPTlwiXTpcbiAgICAgICAgICBwcm9wcy5jcmVkZW50aWFsc0ZvckludGVybmFsQXV0aG9yaXphdGlvblxuICAgICAgICAgICAgPyBwcm9wcy5jcmVkZW50aWFsc0ZvckludGVybmFsQXV0aG9yaXphdGlvblxuICAgICAgICAgICAgOiBcIlwiLFxuICAgICAgfSxcbiAgICB9KVxuXG4gICAgaWYgKHByb3BzLmNyZWRlbnRpYWxzRm9ySW50ZXJuYWxBdXRob3JpemF0aW9uKSB7XG4gICAgICBzZWNyZXRzbWFuYWdlci5TZWNyZXQuZnJvbVNlY3JldE5hbWVWMihcbiAgICAgICAgc2NvcGUsXG4gICAgICAgIGlkICsgXCJCYXNpY0F1dGhTZWNyZXRcIixcbiAgICAgICAgcHJvcHMuY3JlZGVudGlhbHNGb3JJbnRlcm5hbEF1dGhvcml6YXRpb24sXG4gICAgICApLmdyYW50UmVhZCh0aGlzLmxhbWJkYSlcbiAgICB9XG4gIH1cbn1cblxuLyoqXG4gKiBDcmVhdGVzIGEgY3VzdG9tIGF1dGhvcml6ZXIgbGFtYmRhIHdoaWNoIHJlYWRzIGBBdXRob3JpemF0aW9uOiBCYXNpYyA8YmFzZTY0LWVuY29kZWQgY3JlZGVudGlhbHM+YFxuICogaGVhZGVyIGFuZCB2ZXJpZmllcyB0aGUgY3JlZGVudGlhbHMgYWdhaW5zdCBhIGdpdmVuIHNlY3JldC5cbiAqL1xuY2xhc3MgQmFzaWNBdXRoQXV0aG9yaXplciBleHRlbmRzIGNvbnN0cnVjdHMuQ29uc3RydWN0IHtcbiAgcHVibGljIHJlYWRvbmx5IGxhbWJkYTogbGFtYmRhLklGdW5jdGlvblxuXG4gIC8vIFNpbXBsZSBpcyBgeyBpc0F1dGhvcml6ZWQ6IHRydWUvZmFsc2UgfWAsIGFzIG9wcG9zZWQgdG8gcmV0dXJuaW5nIGFuIElBTSBQb2xpY3kgZG9jdW1lbnRcbiAgcHVibGljIHJlYWRvbmx5IHJlc3BvbnNlVHlwZXM6IGF1dGhvcml6ZXJzLkh0dHBMYW1iZGFSZXNwb25zZVR5cGVbXSA9IFtcbiAgICBhdXRob3JpemVycy5IdHRwTGFtYmRhUmVzcG9uc2VUeXBlLlNJTVBMRSxcbiAgXVxuXG4gIGNvbnN0cnVjdG9yKFxuICAgIHNjb3BlOiBjb25zdHJ1Y3RzLkNvbnN0cnVjdCxcbiAgICBpZDogc3RyaW5nLFxuICAgIHByb3BzOiBCYXNpY0F1dGhBdXRob3JpemVyUHJvcHMsXG4gICkge1xuICAgIHN1cGVyKHNjb3BlLCBpZClcblxuICAgIHRoaXMubGFtYmRhID0gbmV3IGxhbWJkYU5vZGVqcy5Ob2RlanNGdW5jdGlvbih0aGlzLCBcIkJhc2ljQXV0aExhbWJkYVwiLCB7XG4gICAgICBlbnRyeTogcGF0aC5qb2luKFxuICAgICAgICBfX2Rpcm5hbWUsXG4gICAgICAgIGBhdXRob3JpemVyLWxhbWJkYXMvYmFzaWMtYXV0aC1hdXRob3JpemVyLiR7YXV0aG9yaXplckZpbGVFeHRlbnNpb259YCxcbiAgICAgICksXG4gICAgICBkZXNjcmlwdGlvbjpcbiAgICAgICAgXCJBbiBhdXRob3JpemVyIGZvciBBUEktR2F0ZXdheSB0aGF0IGNoZWNrcyBCYXNpYyBBdXRoIGNyZWRlbnRpYWxzIG9uIHJlcXVlc3RzXCIsXG4gICAgICBydW50aW1lOiBsYW1iZGEuUnVudGltZS5OT0RFSlNfMjJfWCxcbiAgICAgIGVudmlyb25tZW50OiB7XG4gICAgICAgIFtcIkNSRURFTlRJQUxTX1NFQ1JFVF9OQU1FXCJdOiBwcm9wcy5jcmVkZW50aWFsc1NlY3JldE5hbWVcbiAgICAgICAgICA/IHByb3BzLmNyZWRlbnRpYWxzU2VjcmV0TmFtZVxuICAgICAgICAgIDogXCJcIixcbiAgICAgIH0sXG4gICAgfSlcblxuICAgIGlmIChwcm9wcy5jcmVkZW50aWFsc1NlY3JldE5hbWUpIHtcbiAgICAgIHNlY3JldHNtYW5hZ2VyLlNlY3JldC5mcm9tU2VjcmV0TmFtZVYyKFxuICAgICAgICBzY29wZSxcbiAgICAgICAgaWQgKyBcIkJhc2ljQXV0aFNlY3JldFwiLFxuICAgICAgICBwcm9wcy5jcmVkZW50aWFsc1NlY3JldE5hbWUsXG4gICAgICApLmdyYW50UmVhZCh0aGlzLmxhbWJkYSlcbiAgICB9XG4gIH1cbn1cblxuLyoqXG4gKiBDcmVhdGVzIGEgY3VzdG9tIGF1dGhvcml6ZXIgbGFtYmRhIHdoaWNoIGFsbG93cyBib3RoOlxuICogLSBgQXV0aG9yaXphdGlvbjogQmVhcmVyIDx0b2tlbj5gIGhlYWRlciwgZm9yIHdoaWNoIHRoZSB0b2tlbiBpcyBjaGVja2VkIGFnYWluc3QgdGhlIGdpdmVuXG4gKiAgIENvZ25pdG8gdXNlciBwb29sXG4gKiAtIGBBdXRob3JpemF0aW9uOiBCYXNpYyA8YmFzZTY0LWVuY29kZWQgY3JlZGVudGlhbHM+YCBoZWFkZXIsIGZvciB3aGljaCB0aGUgY3JlZGVudGlhbHMgYXJlXG4gKiAgIGNoZWNrZWQgYWdhaW5zdCB0aGUgY3JlZGVudGlhbHMgZnJvbSB0aGUgZ2l2ZW4gYmFzaWMgYXV0aCBzZWNyZXQgbmFtZVxuICpcbiAqIElmIGVpdGhlciBvZiB0aGVzZSBhcmUgZ2l2ZW4gYW5kIHZhbGlkLCB0aGUgcmVxdWVzdCBpcyBhdXRoZW50aWNhdGVkLlxuICovXG5jbGFzcyBDb2duaXRvVXNlclBvb2xPckJhc2ljQXV0aEF1dGhvcml6ZXI8XG4gIEF1dGhTY29wZXNUIGV4dGVuZHMgc3RyaW5nLFxuPiBleHRlbmRzIGNvbnN0cnVjdHMuQ29uc3RydWN0IHtcbiAgcHVibGljIHJlYWRvbmx5IGxhbWJkYTogbGFtYmRhLklGdW5jdGlvblxuXG4gIC8qKlxuICAgKiBVc2Ugc2ltcGxlIHJlc3BvbnNlIHR5cGUgKGB7IGlzQXV0aG9yaXplZDogdHJ1ZS9mYWxzZSB9YCksIGFzIG9wcG9zZWQgdG8gcmV0dXJuaW5nIGFuIElBTVxuICAgKiBQb2xpY3kgZG9jdW1lbnQuXG4gICAqL1xuICBwdWJsaWMgcmVhZG9ubHkgcmVzcG9uc2VUeXBlczogYXV0aG9yaXplcnMuSHR0cExhbWJkYVJlc3BvbnNlVHlwZVtdID0gW1xuICAgIGF1dGhvcml6ZXJzLkh0dHBMYW1iZGFSZXNwb25zZVR5cGUuU0lNUExFLFxuICBdXG5cbiAgY29uc3RydWN0b3IoXG4gICAgc2NvcGU6IGNvbnN0cnVjdHMuQ29uc3RydWN0LFxuICAgIGlkOiBzdHJpbmcsXG4gICAgcHJvcHM6IENvZ25pdG9Vc2VyUG9vbE9yQmFzaWNBdXRoQXV0aG9yaXplclByb3BzPEF1dGhTY29wZXNUPixcbiAgKSB7XG4gICAgc3VwZXIoc2NvcGUsIGlkKVxuXG4gICAgdGhpcy5sYW1iZGEgPSBuZXcgbGFtYmRhTm9kZWpzLk5vZGVqc0Z1bmN0aW9uKHRoaXMsIFwiQXV0aG9yaXplckZ1bmN0aW9uXCIsIHtcbiAgICAgIGVudHJ5OiBwYXRoLmpvaW4oXG4gICAgICAgIF9fZGlybmFtZSxcbiAgICAgICAgYGF1dGhvcml6ZXItbGFtYmRhcy9jb2duaXRvLXVzZXItcG9vbC1vci1iYXNpYy1hdXRoLWF1dGhvcml6ZXIuJHthdXRob3JpemVyRmlsZUV4dGVuc2lvbn1gLFxuICAgICAgKSxcbiAgICAgIHJ1bnRpbWU6IGxhbWJkYS5SdW50aW1lLk5PREVKU18yMl9YLFxuICAgICAgdGltZW91dDogY2RrLkR1cmF0aW9uLnNlY29uZHMoNSksXG4gICAgICBlbnZpcm9ubWVudDoge1xuICAgICAgICBbXCJVU0VSX1BPT0xfSURcIl06IHByb3BzLnVzZXJQb29sID8gcHJvcHMudXNlclBvb2wudXNlclBvb2xJZCA6IFwiXCIsXG4gICAgICAgIFtcIlJFUVVJUkVEX1NDT1BFXCJdOiBwcm9wcy5yZXF1aXJlZFNjb3BlID8/IFwiXCIsXG4gICAgICAgIFtcIkJBU0lDX0FVVEhfQ1JFREVOVElBTFNfU0VDUkVUX05BTUVcIl06XG4gICAgICAgICAgcHJvcHMuYmFzaWNBdXRoQ3JlZGVudGlhbHNTZWNyZXROYW1lXG4gICAgICAgICAgICA/IHByb3BzLmJhc2ljQXV0aENyZWRlbnRpYWxzU2VjcmV0TmFtZVxuICAgICAgICAgICAgOiBcIlwiLFxuICAgICAgfSxcbiAgICB9KVxuXG4gICAgaWYgKHByb3BzLmJhc2ljQXV0aENyZWRlbnRpYWxzU2VjcmV0TmFtZSkge1xuICAgICAgc2VjcmV0c21hbmFnZXIuU2VjcmV0LmZyb21TZWNyZXROYW1lVjIoXG4gICAgICAgIHNjb3BlLFxuICAgICAgICBpZCArIFwiQmFzaWNBdXRoU2VjcmV0XCIsXG4gICAgICAgIHByb3BzLmJhc2ljQXV0aENyZWRlbnRpYWxzU2VjcmV0TmFtZSxcbiAgICAgICkuZ3JhbnRSZWFkKHRoaXMubGFtYmRhKVxuICAgIH1cbiAgfVxufVxuXG4vKipcbiAqIEEgc2xpZ2h0bHkgZXh0ZW5kZWQgdmVyc2lvbiBvZiB0aGUgW2RlZmF1bHQgSlNPTiBmb3JtYXQgc3VnZ2VzdGVkIGJ5IEFXU10oaHR0cHM6Ly9kb2NzLmF3cy5hbWF6b24uY29tL2FwaWdhdGV3YXkvbGF0ZXN0L2RldmVsb3Blcmd1aWRlL2h0dHAtYXBpLWxvZ2dpbmcuaHRtbCNodHRwLWFwaS1lbmFibGUtbG9nZ2luZy5leGFtcGxlcykuXG4gKi9cbmNvbnN0IGRlZmF1bHRBY2Nlc3NMb2dGb3JtYXQgPSB7XG4gIHJlcXVlc3RJZDogXCIkY29udGV4dC5yZXF1ZXN0SWRcIixcbiAgdXNlckFnZW50OiBcIiRjb250ZXh0LmlkZW50aXR5LnVzZXJBZ2VudFwiLFxuICBpcDogXCIkY29udGV4dC5pZGVudGl0eS5zb3VyY2VJcFwiLFxuICAvKiogQ0xGIGZvcm1hdDogYGRkL01NTS95eXl5OkhIOm1tOnNzICstaGhtbWAgKi9cbiAgcmVxdWVzdFRpbWU6IFwiJGNvbnRleHQucmVxdWVzdFRpbWVcIixcbiAgcmVxdWVzdFRpbWVFcG9jaDogXCIkY29udGV4dC5yZXF1ZXN0VGltZUVwb2NoXCIsXG4gIGRhdGFQcm9jZXNzZWQ6IFwiJGNvbnRleHQuZGF0YVByb2Nlc3NlZFwiLFxuICBodHRwTWV0aG9kOiBcIiRjb250ZXh0Lmh0dHBNZXRob2RcIixcbiAgcGF0aDogXCIkY29udGV4dC5wYXRoXCIsXG4gIHJvdXRlS2V5OiBcIiRjb250ZXh0LnJvdXRlS2V5XCIsXG4gIHN0YXR1czogXCIkY29udGV4dC5zdGF0dXNcIixcbiAgcHJvdG9jb2w6IFwiJGNvbnRleHQucHJvdG9jb2xcIixcbiAgcmVzcG9uc2VMZW5ndGg6IFwiJGNvbnRleHQucmVzcG9uc2VMZW5ndGhcIixcbiAgcmVzcG9uc2VMYXRlbmN5OiBcIiRjb250ZXh0LnJlc3BvbnNlTGF0ZW5jeVwiLFxuICBkb21haW5OYW1lOiBcIiRjb250ZXh0LmRvbWFpbk5hbWVcIixcbiAgLy8gIGhvc3RIZWFkZXJPdmVycmlkZTogXCIkY29udGV4dC5yZXF1ZXN0T3ZlcnJpZGUuaGVhZGVyLkhvc3RcIiwgLy9NYXBwaW5nIHRlbXBsYXRlIG92ZXJyaWRlcyBjYW5ub3QgYmUgdXNlZCB3aXRoIHByb3h5IGludGVncmF0aW9uIGVuZHBvaW50cywgd2hpY2ggbGFjayBkYXRhIG1hcHBpbmdzIGh0dHBzOi8vZG9jcy5hd3MuYW1hem9uLmNvbS9hcGlnYXRld2F5L2xhdGVzdC9kZXZlbG9wZXJndWlkZS9hcGlnYXRld2F5LW92ZXJyaWRlLXJlcXVlc3QtcmVzcG9uc2UtcGFyYW1ldGVycy5odG1sIzp+OnRleHQ9TWFwcGluZyUyMHRlbXBsYXRlJTIwb3ZlcnJpZGVzJTIwY2Fubm90JTIwYmUlMjB1c2VkJTIwd2l0aCUyMHByb3h5JTIwaW50ZWdyYXRpb24lMjBlbmRwb2ludHMlMkMlMjB3aGljaCUyMGxhY2slMjBkYXRhJTIwbWFwcGluZ3NcbiAgZXJyb3I6IHtcbiAgICB0eXBlOiBcIiRjb250ZXh0LmVycm9yLnJlc3BvbnNlVHlwZVwiLFxuICAgIGdhdGV3YXlFcnJvcjogXCIkY29udGV4dC5lcnJvci5tZXNzYWdlXCIsXG4gICAgaW50ZWdyYXRpb25FcnJvcjogXCIkY29udGV4dC5pbnRlZ3JhdGlvbi5lcnJvclwiLFxuICAgIGF1dGhvcml6ZXJFcnJvcjogXCIkY29udGV4dC5hdXRob3JpemVyLmVycm9yXCIsXG4gIH0sXG4gIGludGVncmF0aW9uOiB7XG4gICAgbGF0ZW5jeTogXCIkY29udGV4dC5pbnRlZ3JhdGlvbi5sYXRlbmN5XCIsXG4gICAgcmVxdWVzdElkOiBcIiRjb250ZXh0LmludGVncmF0aW9uLnJlcXVlc3RJZFwiLFxuICAgIHJlc3BvbnNlU3RhdHVzOiBcIiRjb250ZXh0LmludGVncmF0aW9uLnN0YXR1c1wiLFxuICB9LFxuICBhdXRoOiB7XG4gICAgaWFtOiB7XG4gICAgICB1c2VyQXJuOiBcIiRjb250ZXh0LmlkZW50aXR5LnVzZXJBcm5cIixcbiAgICAgIGF3c0FjY291bnQ6IFwiJGNvbnRleHQuaWRlbnRpdHkuYWNjb3VudElkXCIsXG4gICAgICBhd3NQcmluY2lwYWw6IFwiJGNvbnRleHQuaWRlbnRpdHkuY2FsbGVyXCIsXG4gICAgICBhd3NQcmluY2lwYWxPcmc6IFwiJGNvbnRleHQuaWRlbnRpdHkucHJpbmNpcGFsT3JnSWRcIixcbiAgICB9LFxuICAgIGJhc2ljOiB7IHVzZXI6IFwiJGNvbnRleHQuYXV0aG9yaXplci51c2VyXCIgfSxcbiAgfSxcbiAgYXdzRW5kcG9pbnRSZXF1ZXN0OiB7XG4gICAgaWQ6IFwiJGNvbnRleHQuYXdzRW5kcG9pbnRSZXF1ZXN0SWRcIixcbiAgICBpZDI6IFwiJGNvbnRleHQuYXdzRW5kcG9pbnRSZXF1ZXN0SWQyXCIsXG4gIH0sXG4gIC8vIEZvciBkYXRhZG9nXG4gIG1lc3NhZ2U6XG4gICAgXCIkY29udGV4dC5pZGVudGl0eS5zb3VyY2VJcCAtICRjb250ZXh0Lmh0dHBNZXRob2QgJGNvbnRleHQuZG9tYWluTmFtZSAkY29udGV4dC5wYXRoICgkY29udGV4dC5yb3V0ZUtleSkgLSAkY29udGV4dC5zdGF0dXMgWyRjb250ZXh0LnJlc3BvbnNlTGF0ZW5jeSBtc11cIixcbn1cblxuLyoqXG4gKiBFbmFibGVzIGFjY2VzcyBsb2dzIG9uIHRoZSBBUEktR2F0ZXdheS5cbiAqXG4gKiBAYXV0aG9yIEtyaXN0aWFuIFJla3N0YWQgPGtyZUBjYXByYWNvbnN1bHRpbmcubm8+XG4gKi9cbmNsYXNzIEFwaUdhdGV3YXlBY2Nlc3NMb2dzIGV4dGVuZHMgY29uc3RydWN0cy5Db25zdHJ1Y3Qge1xuICBwdWJsaWMgcmVhZG9ubHkgbG9nR3JvdXA6IGxvZ3MuTG9nR3JvdXBcblxuICBjb25zdHJ1Y3RvcihcbiAgICBzY29wZTogY29uc3RydWN0cy5Db25zdHJ1Y3QsXG4gICAgaWQ6IHN0cmluZyxcbiAgICBzdGFnZTogYXBpZ3cuQ2ZuU3RhZ2UsXG4gICAgcHJvcHM6IEFwaUdhdGV3YXlBY2Nlc3NMb2dzUHJvcHMgfCB1bmRlZmluZWQsXG4gICAgZGVmYXVsdEFjY2Vzc0xvZ0Zvcm1hdDogUmVjb3JkPHN0cmluZywgdW5rbm93bj4sXG4gICkge1xuICAgIHN1cGVyKHNjb3BlLCBpZClcblxuICAgIC8vIGxvZ0dyb3VwIGlzIHNldCB1cCB3aXRoIGhlbHAgZnJvbTogaHR0cHM6Ly9naXRodWIuY29tL2F3cy9hd3MtY2RrL2lzc3Vlcy8xMTEwMCNpc3N1ZWNvbW1lbnQtOTA0NjI3MDgxXG4gICAgLy8gTm90IHN1cmUgaWYgSFRUUCBBUEkgYWN0dWFsbHkgbmVlZHMgdGhlIHNlcnZpY2Ugcm9sZSB3aXRoIG1hbmFnZWQgcG9saWN5OiBodHRwczovL2RvY3MuYXdzLmFtYXpvbi5jb20vYXBpZ2F0ZXdheS9sYXRlc3QvZGV2ZWxvcGVyZ3VpZGUvaHR0cC1hcGktbG9nZ2luZy5odG1sXG4gICAgY29uc3QgYWNjZXNzTG9ncyA9IG5ldyBsb2dzLkxvZ0dyb3VwKHRoaXMsIFwiQWNjZXNzTG9nR3JvdXBcIiwge1xuICAgICAgcmV0ZW50aW9uOiBwcm9wcz8ucmV0ZW50aW9uLFxuICAgICAgcmVtb3ZhbFBvbGljeTogcHJvcHM/LnJlbW92YWxQb2xpY3kgPz8gY2RrLlJlbW92YWxQb2xpY3kuUkVUQUlOLFxuICAgICAgLy8gQWx3YXlzIHVzZSB0aGUgZGVmYXVsdCBlbmNyeXB0aW9uIGtleS4gT3RoZXJ3aXNlLCB0aGUga2V5IG5lZWRzIHNwZWNpYWwgcG9saWNpZXM6XG4gICAgICAvLyBodHRwczovL2RvY3MuYXdzLmFtYXpvbi5jb20vQW1hem9uQ2xvdWRXYXRjaC9sYXRlc3QvbG9ncy9lbmNyeXB0LWxvZy1kYXRhLWttcy5odG1sI2Ntay1wZXJtaXNzaW9uc1xuICAgICAgZW5jcnlwdGlvbktleTogdW5kZWZpbmVkLFxuICAgIH0pXG4gICAgdGhpcy5sb2dHcm91cCA9IGFjY2Vzc0xvZ3NcblxuICAgIHN0YWdlLmFjY2Vzc0xvZ1NldHRpbmdzID0ge1xuICAgICAgZGVzdGluYXRpb25Bcm46IGFjY2Vzc0xvZ3MubG9nR3JvdXBBcm4sXG4gICAgICBmb3JtYXQ6IEpTT04uc3RyaW5naWZ5KHByb3BzPy5hY2Nlc3NMb2dGb3JtYXQgPz8gZGVmYXVsdEFjY2Vzc0xvZ0Zvcm1hdCksXG4gICAgfVxuXG4gICAgY29uc3QgYXBpR3dMb2dzUm9sZSA9IG5ldyBpYW0uUm9sZShcbiAgICAgIHRoaXMsXG4gICAgICBcIkFwaUdhdGV3YXlQdXNoVG9DbG91ZHdhdGNoTG9nc1JvbGVcIixcbiAgICAgIHtcbiAgICAgICAgYXNzdW1lZEJ5OiBuZXcgaWFtLlNlcnZpY2VQcmluY2lwYWwoXCJhcGlnYXRld2F5LmFtYXpvbmF3cy5jb21cIiksXG4gICAgICAgIG1hbmFnZWRQb2xpY2llczogW1xuICAgICAgICAgIGlhbS5NYW5hZ2VkUG9saWN5LmZyb21Bd3NNYW5hZ2VkUG9saWN5TmFtZShcbiAgICAgICAgICAgIFwic2VydmljZS1yb2xlL0FtYXpvbkFQSUdhdGV3YXlQdXNoVG9DbG91ZFdhdGNoTG9nc1wiLFxuICAgICAgICAgICksXG4gICAgICAgIF0sXG4gICAgICB9LFxuICAgIClcblxuICAgIGFjY2Vzc0xvZ3MuZ3JhbnRXcml0ZShhcGlHd0xvZ3NSb2xlKVxuICB9XG59XG5cbi8qKiBSZXR1cm5zIGEgc2hvcnQgc2VtaS11bmlxdWUgaGFzaCBvZiB0aGUgZ2l2ZW4gc3RyaW5nLiAqL1xuZnVuY3Rpb24gc2hvcnRIYXNoKHN0cjogc3RyaW5nKTogc3RyaW5nIHtcbiAgLy8gU0hBLTEgaXMgbm8tbm8gd2hlbiB3ZSBuZWVkIGNyeXB0b2dyYXBoaWMgc2VjdXJpdHksIGJ1dCBoZXJlIHdlIGp1c3QgaXQgZm9yIHNob3J0ZW5pbmcgYSBuYW1lLFxuICAvLyB3aGljaCBpcyBmaW5lXG4gIHJldHVybiBjcmVhdGVIYXNoKFwic2hhMVwiKS51cGRhdGUoc3RyKS5kaWdlc3QoXCJoZXhcIikuc3Vic3RyaW5nKDAsIDEwKVxufVxuIl19
|