@aws-amplify/backend-notifications 0.0.0-test-20260708085627
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/README.md +235 -0
- package/lib/campaign-association-asset/index.js +48413 -0
- package/lib/constants.d.ts +122 -0
- package/lib/constants.js +125 -0
- package/lib/construct.d.ts +283 -0
- package/lib/construct.js +824 -0
- package/lib/factory.d.ts +53 -0
- package/lib/factory.js +158 -0
- package/lib/handler-asset/index.js +530 -0
- package/lib/index.d.ts +7 -0
- package/lib/index.js +5 -0
- package/lib/lambda/campaign-association/handler.d.ts +29 -0
- package/lib/lambda/campaign-association/handler.js +80 -0
- package/lib/lambda/campaign-association/integration.d.ts +59 -0
- package/lib/lambda/campaign-association/integration.js +108 -0
- package/lib/lambda/campaign-association/onboarding.d.ts +25 -0
- package/lib/lambda/campaign-association/onboarding.js +74 -0
- package/lib/lambda/identify/device_resolver.d.ts +18 -0
- package/lib/lambda/identify/device_resolver.js +56 -0
- package/lib/lambda/identify/handler.d.ts +17 -0
- package/lib/lambda/identify/handler.js +124 -0
- package/lib/lambda/identify/mapping.d.ts +61 -0
- package/lib/lambda/identify/mapping.js +171 -0
- package/lib/lambda/identify/merge_resolver.d.ts +26 -0
- package/lib/lambda/identify/merge_resolver.js +36 -0
- package/lib/lambda/identify/principal.d.ts +68 -0
- package/lib/lambda/identify/principal.js +48 -0
- package/lib/lambda/identify/profile_resolver.d.ts +35 -0
- package/lib/lambda/identify/profile_resolver.js +65 -0
- package/lib/lambda/identify/types.d.ts +90 -0
- package/lib/lambda/identify/types.js +10 -0
- package/lib/lambda/identify/validation.d.ts +15 -0
- package/lib/lambda/identify/validation.js +99 -0
- package/lib/lambda/push/delivery.d.ts +49 -0
- package/lib/lambda/push/delivery.js +120 -0
- package/lib/lambda/push/device_lookup.d.ts +35 -0
- package/lib/lambda/push/device_lookup.js +88 -0
- package/lib/lambda/push/eum_client.d.ts +31 -0
- package/lib/lambda/push/eum_client.js +140 -0
- package/lib/lambda/push/event.d.ts +32 -0
- package/lib/lambda/push/event.js +122 -0
- package/lib/lambda/push/fixtures/real_journey_event.d.ts +19 -0
- package/lib/lambda/push/fixtures/real_journey_event.js +22 -0
- package/lib/lambda/push/handler.d.ts +15 -0
- package/lib/lambda/push/handler.js +86 -0
- package/lib/lambda/push/message_template.d.ts +72 -0
- package/lib/lambda/push/message_template.js +270 -0
- package/lib/lambda/push/payload.d.ts +25 -0
- package/lib/lambda/push/payload.js +53 -0
- package/lib/lambda/push/types.d.ts +153 -0
- package/lib/lambda/push/types.js +4 -0
- package/lib/lambda/shared/retry.d.ts +18 -0
- package/lib/lambda/shared/retry.js +45 -0
- package/lib/object_types.d.ts +63 -0
- package/lib/object_types.js +131 -0
- package/lib/push-handler-asset/index.js +75517 -0
- package/lib/types.d.ts +125 -0
- package/lib/types.js +2 -0
- package/package.json +49 -0
package/lib/construct.js
ADDED
|
@@ -0,0 +1,824 @@
|
|
|
1
|
+
import * as path from 'path';
|
|
2
|
+
import { createHash } from 'node:crypto';
|
|
3
|
+
import { fileURLToPath } from 'node:url';
|
|
4
|
+
import { Arn, ArnFormat, CfnOutput, CustomResource, Duration, Stack, } from 'aws-cdk-lib';
|
|
5
|
+
import { Construct } from 'constructs';
|
|
6
|
+
import * as lambda from 'aws-cdk-lib/aws-lambda';
|
|
7
|
+
import * as iam from 'aws-cdk-lib/aws-iam';
|
|
8
|
+
import { Provider } from 'aws-cdk-lib/custom-resources';
|
|
9
|
+
import { CfnDomain, CfnIntegration, CfnObjectType, } from 'aws-cdk-lib/aws-customerprofiles';
|
|
10
|
+
import { CfnInstance, CfnIntegrationAssociation, } from 'aws-cdk-lib/aws-connect';
|
|
11
|
+
import { CfnKnowledgeBase } from 'aws-cdk-lib/aws-wisdom';
|
|
12
|
+
import { CfnAPNSChannel, CfnAPNSSandboxChannel, CfnApp, CfnGCMChannel, } from 'aws-cdk-lib/aws-pinpoint';
|
|
13
|
+
import * as apigwv2 from 'aws-cdk-lib/aws-apigatewayv2';
|
|
14
|
+
import { HttpIamAuthorizer, HttpJwtAuthorizer, } from 'aws-cdk-lib/aws-apigatewayv2-authorizers';
|
|
15
|
+
import { HttpLambdaIntegration } from 'aws-cdk-lib/aws-apigatewayv2-integrations';
|
|
16
|
+
import { CONNECT_CAMPAIGNS_SERVICE_NAME, CONNECT_INVOKE_SERVICE_PRINCIPALS, DEFAULT_EXPIRATION_DAYS, ENV_DOMAIN_NAME, ENV_EUM_APPLICATION_ID, } from './constants.js';
|
|
17
|
+
import { AMPLIFY_DEVICE_FIELDS, AMPLIFY_DEVICE_KEYS, AMPLIFY_GUEST_PROFILE_FIELDS, AMPLIFY_GUEST_PROFILE_KEYS, AMPLIFY_PROFILE_FIELDS, AMPLIFY_PROFILE_KEYS, OBJECT_TYPE_NAMES, } from './object_types.js';
|
|
18
|
+
/**
|
|
19
|
+
* The pre-bundled identify-user Lambda asset ships alongside the compiled
|
|
20
|
+
* construct at `<packageRoot>/lib/handler-asset` (produced by the package's
|
|
21
|
+
* `post:compile` esbuild step). Resolving relative to the package root — rather
|
|
22
|
+
* than to this module — means the path is correct whether the code runs from
|
|
23
|
+
* the compiled `lib/` output or from `src/` under `tsx` (as the unit tests do).
|
|
24
|
+
*/
|
|
25
|
+
const packageRoot = fileURLToPath(new URL('..', import.meta.url));
|
|
26
|
+
const defaultLambdaCodePath = path.join(packageRoot, 'lib', 'handler-asset');
|
|
27
|
+
const defaultPushLambdaCodePath = path.join(packageRoot, 'lib', 'push-handler-asset');
|
|
28
|
+
const defaultCampaignAssociationLambdaCodePath = path.join(packageRoot, 'lib', 'campaign-association-asset');
|
|
29
|
+
/**
|
|
30
|
+
* Amazon Connect Customer Profiles backend for the identifyUser contract,
|
|
31
|
+
* packaged as an AWS CDK construct. Provisions:
|
|
32
|
+
* - AmplifyProfile object type: searchable `cognitoUserKey` (PROFILE + UNIQUE)
|
|
33
|
+
* identity key + person / targeting attribute schema.
|
|
34
|
+
* - AmplifyDevice object type: PROFILE-resolution key = `cognitoUserKey`,
|
|
35
|
+
* UNIQUE object key = stable `deviceId`; device fields stored raw.
|
|
36
|
+
* - identify-user Lambda with least-privilege profile:* on this domain only.
|
|
37
|
+
* - HTTP API + JWT authorizer bound to the supplied issuer / audience.
|
|
38
|
+
* - push-delivery Lambda (Connect Journey Custom-action target) + a minimal
|
|
39
|
+
* AWS End User Messaging (Pinpoint) application for `SendMessages`.
|
|
40
|
+
*
|
|
41
|
+
* It operates in one of two modes, chosen by whether `domainName` is provided:
|
|
42
|
+
*
|
|
43
|
+
* - CREATE-FROM-SCRATCH (default, `domainName` omitted): the construct also
|
|
44
|
+
* provisions a brand-new Amazon Connect instance (`AWS::Connect::Instance`,
|
|
45
|
+
* CONNECT_MANAGED, inbound/outbound enabled) AND a brand-new Customer
|
|
46
|
+
* Profiles domain (`AWS::CustomerProfiles::Domain`) with generated, stable
|
|
47
|
+
* names, then registers the object types INTO that new domain. The object
|
|
48
|
+
* types depend on the created domain so they provision after it and are torn
|
|
49
|
+
* down before it. A caller needs zero pre-existing Connect setup.
|
|
50
|
+
* - ATTACH (`domainName` provided): the construct registers the two object
|
|
51
|
+
* types INTO the existing `domainName` (additive) — such as the domain
|
|
52
|
+
* Amazon Connect auto-creates when Customer Profiles is enabled on an
|
|
53
|
+
* instance — WITHOUT creating an instance or a domain, and without touching
|
|
54
|
+
* the domain's other integrations (CTR, Outbound Campaigns) or its Identity
|
|
55
|
+
* Resolution setting.
|
|
56
|
+
*
|
|
57
|
+
* In either mode, identify works against the domain standalone. Outbound
|
|
58
|
+
* Campaigns association — so journeys can target these profiles — is AUTOMATIC
|
|
59
|
+
* in create-from-scratch mode (a Lambda-backed custom resource associates the
|
|
60
|
+
* new domain with the new instance's Outbound Campaigns v2 at deploy time). In
|
|
61
|
+
* attach mode, associating the pre-existing domain is the user's responsibility
|
|
62
|
+
* and is left untouched.
|
|
63
|
+
*
|
|
64
|
+
* This construct is framework-agnostic (aws-cdk-lib + constructs only). The
|
|
65
|
+
* `defineNotifications` factory wraps it for Amplify Gen2 backends, wiring the
|
|
66
|
+
* JWT authorizer to the app's Cognito user pool and surfacing the endpoint via
|
|
67
|
+
* backend outputs.
|
|
68
|
+
*/
|
|
69
|
+
export class AmplifyNotifications extends Construct {
|
|
70
|
+
/** CDK resources for lower-level access. */
|
|
71
|
+
resources;
|
|
72
|
+
/** The stack this construct belongs to. */
|
|
73
|
+
stack;
|
|
74
|
+
/** Base invoke URL. Clients call `POST {apiEndpoint}/identify-user`. */
|
|
75
|
+
apiEndpoint;
|
|
76
|
+
/** The route path appended to {@link apiEndpoint}. */
|
|
77
|
+
identifyUserPath = '/identify-user';
|
|
78
|
+
/**
|
|
79
|
+
* The GUEST route path appended to {@link apiEndpoint}.
|
|
80
|
+
* IAM/SigV4-authorized; unauthenticated Identity Pool callers sign requests to
|
|
81
|
+
* `POST {apiEndpoint}/identify-user-guest`.
|
|
82
|
+
*/
|
|
83
|
+
guestIdentifyUserPath = '/identify-user-guest';
|
|
84
|
+
/**
|
|
85
|
+
* The `execute-api:Invoke` ARN of the GUEST route, to
|
|
86
|
+
* grant to the app's Cognito Identity Pool UNAUTHENTICATED role so guests can
|
|
87
|
+
* call the route. Scoped to POST on the guest path of this API's default
|
|
88
|
+
* stage.
|
|
89
|
+
*/
|
|
90
|
+
guestRouteInvokeArn;
|
|
91
|
+
/** The Customer Profiles domain name the object types are registered into. */
|
|
92
|
+
domainName;
|
|
93
|
+
/**
|
|
94
|
+
* `true` when this construct created the Customer Profiles domain + Connect
|
|
95
|
+
* instance (create-from-scratch mode); `false` when it attached to an existing
|
|
96
|
+
* domain named by `domainName`.
|
|
97
|
+
*/
|
|
98
|
+
createsResources;
|
|
99
|
+
/**
|
|
100
|
+
* Id of the Amazon Connect instance created in create-from-scratch mode.
|
|
101
|
+
* `undefined` in attach mode.
|
|
102
|
+
*/
|
|
103
|
+
connectInstanceId;
|
|
104
|
+
/**
|
|
105
|
+
* ARN of the Amazon Connect instance created in create-from-scratch mode.
|
|
106
|
+
* `undefined` in attach mode.
|
|
107
|
+
*/
|
|
108
|
+
connectInstanceArn;
|
|
109
|
+
/**
|
|
110
|
+
* ARN of the push-delivery Lambda, to wire as a Connect Journey Custom-action
|
|
111
|
+
* (Invoke Lambda) target.
|
|
112
|
+
*/
|
|
113
|
+
pushFunctionArn;
|
|
114
|
+
/**
|
|
115
|
+
* The AWS End User Messaging / Pinpoint application id this construct created
|
|
116
|
+
* and the push Lambda calls `SendMessages` against.
|
|
117
|
+
*/
|
|
118
|
+
eumApplicationId;
|
|
119
|
+
/**
|
|
120
|
+
* Registers the AmplifyProfile / AmplifyDevice object types into the Customer
|
|
121
|
+
* Profiles domain (created here in create-from-scratch mode, or the existing
|
|
122
|
+
* `domainName` in attach mode), the identify-user Lambda + JWT-authorized HTTP
|
|
123
|
+
* API, and the push-delivery Lambda + AWS End User Messaging application for
|
|
124
|
+
* this notifications backend.
|
|
125
|
+
*/
|
|
126
|
+
constructor(scope, id, props) {
|
|
127
|
+
super(scope, id);
|
|
128
|
+
const stack = Stack.of(this);
|
|
129
|
+
this.stack = stack;
|
|
130
|
+
const expirationDays = props.expirationDays ?? DEFAULT_EXPIRATION_DAYS;
|
|
131
|
+
// ---- Mode selection: create-from-scratch (default) vs attach -----------
|
|
132
|
+
// If a `domainName` is supplied, ATTACH the object types to that existing
|
|
133
|
+
// domain (create nothing). Otherwise (the zero-config default) CREATE a
|
|
134
|
+
// brand-new Connect instance + Customer Profiles domain with generated,
|
|
135
|
+
// stable names and register the object types into the new domain.
|
|
136
|
+
const createFromScratch = !props.domainName;
|
|
137
|
+
this.createsResources = createFromScratch;
|
|
138
|
+
let domainName;
|
|
139
|
+
let profilesDomain;
|
|
140
|
+
let connectInstance;
|
|
141
|
+
if (createFromScratch) {
|
|
142
|
+
// Deterministic-yet-unique base name derived from the construct's scope so
|
|
143
|
+
// it is STABLE across deploy/delete (same tree → same name) and unique per
|
|
144
|
+
// app / environment (the root stack name embeds the Amplify app + branch).
|
|
145
|
+
const baseName = this.generateResourceName();
|
|
146
|
+
domainName = baseName;
|
|
147
|
+
connectInstance = new CfnInstance(this, 'ConnectInstance', {
|
|
148
|
+
// Minimal telephony surface: Connect requires at least one of
|
|
149
|
+
// inbound/outbound; enable both so the instance is usable for outbound
|
|
150
|
+
// journeys later. Directory is Connect-managed (no external identity).
|
|
151
|
+
//
|
|
152
|
+
// highVolumeOutBound enables the instance's Outbound Campaigns feature.
|
|
153
|
+
// This is what makes Amazon Connect attach the
|
|
154
|
+
// `HighVolumeOutboundCommunicationAccess` inline policy to the instance's
|
|
155
|
+
// OWN service-linked role — the policy granting the `connect-campaigns:*`
|
|
156
|
+
// (CreateCampaign / ListCampaigns / ...) actions the Connect console
|
|
157
|
+
// drives Journey + campaign authoring through. Onboarding to Outbound
|
|
158
|
+
// Campaigns v2 (the campaign-association custom resource) does NOT set
|
|
159
|
+
// it, so without this the console's Journey / segment builder fails with
|
|
160
|
+
// connect-campaigns AccessDenied on the instance SLR.
|
|
161
|
+
attributes: {
|
|
162
|
+
inboundCalls: true,
|
|
163
|
+
outboundCalls: true,
|
|
164
|
+
highVolumeOutBound: true,
|
|
165
|
+
},
|
|
166
|
+
identityManagementType: 'CONNECT_MANAGED',
|
|
167
|
+
instanceAlias: this.sanitizeInstanceAlias(props.instanceAlias ?? baseName),
|
|
168
|
+
});
|
|
169
|
+
profilesDomain = new CfnDomain(this, 'ProfilesDomain', {
|
|
170
|
+
domainName,
|
|
171
|
+
defaultExpirationDays: expirationDays,
|
|
172
|
+
});
|
|
173
|
+
this.connectInstanceId = connectInstance.attrId;
|
|
174
|
+
this.connectInstanceArn = connectInstance.attrArn;
|
|
175
|
+
// Instance-level Customer Profiles feature binding: registers the created
|
|
176
|
+
// domain as the instance's CTR (contact-record) profiles store — the
|
|
177
|
+
// "Customer Profiles enabled" integration the Connect console and Journey
|
|
178
|
+
// segment builder require, distinct from the Outbound Campaigns
|
|
179
|
+
// integration the association custom resource wires below. PutIntegration
|
|
180
|
+
// auto-resolves the Connect service-linked role for a connect-instance
|
|
181
|
+
// URI; that SLR can only reach the domain because it is named
|
|
182
|
+
// `amazon-connect-*` (see generateResourceName).
|
|
183
|
+
const ctrIntegration = new CfnIntegration(this, 'ConnectProfilesIntegration', {
|
|
184
|
+
domainName,
|
|
185
|
+
uri: connectInstance.attrArn,
|
|
186
|
+
objectTypeName: 'CTR',
|
|
187
|
+
});
|
|
188
|
+
ctrIntegration.addDependency(connectInstance);
|
|
189
|
+
ctrIntegration.addDependency(profilesDomain);
|
|
190
|
+
// Message-templates knowledge base + its instance association. Both the
|
|
191
|
+
// Connect console's message-template authoring UI and the push-delivery
|
|
192
|
+
// Lambda's runtime discovery (ListIntegrationAssociations filtered to
|
|
193
|
+
// Q_MESSAGE_TEMPLATES → the KB it renders templates from) require a
|
|
194
|
+
// MESSAGE_TEMPLATES knowledge base linked to the instance. We provision
|
|
195
|
+
// the empty KB + the Q_MESSAGE_TEMPLATES association only; the templates
|
|
196
|
+
// themselves are the marketer's job (a template's name must match the
|
|
197
|
+
// journey Custom-action id, e.g. "Push Notification"). A MESSAGE_TEMPLATES
|
|
198
|
+
// KB needs no Q assistant. The name is derived from the same deterministic
|
|
199
|
+
// base as the instance/domain so redeploys stay stable and avoid the
|
|
200
|
+
// CreateKnowledgeBase ConflictException on duplicate names.
|
|
201
|
+
const templatesKb = new CfnKnowledgeBase(this, 'MessageTemplatesKb', {
|
|
202
|
+
name: `${baseName}-message-templates`,
|
|
203
|
+
knowledgeBaseType: 'MESSAGE_TEMPLATES',
|
|
204
|
+
});
|
|
205
|
+
const templatesAssociation = new CfnIntegrationAssociation(this, 'MessageTemplatesAssociation', {
|
|
206
|
+
instanceId: connectInstance.attrArn,
|
|
207
|
+
integrationType: 'Q_MESSAGE_TEMPLATES',
|
|
208
|
+
integrationArn: templatesKb.attrKnowledgeBaseArn,
|
|
209
|
+
});
|
|
210
|
+
templatesAssociation.addDependency(connectInstance);
|
|
211
|
+
templatesAssociation.addDependency(templatesKb);
|
|
212
|
+
}
|
|
213
|
+
else {
|
|
214
|
+
domainName = props.domainName;
|
|
215
|
+
}
|
|
216
|
+
this.domainName = domainName;
|
|
217
|
+
// ---- Object types ------------------------------------------------------
|
|
218
|
+
// Registered INTO `domainName`. In attach mode this is purely additive to a
|
|
219
|
+
// pre-existing (e.g. Connect-managed) domain. In create mode the object
|
|
220
|
+
// types depend on the domain created above, so CFN provisions the domain
|
|
221
|
+
// first and — on teardown — removes the object types before the domain (a
|
|
222
|
+
// clean, in-construct dependency; no cross-stack ordering hack needed).
|
|
223
|
+
const profileType = new CfnObjectType(this, 'AmplifyProfileType', {
|
|
224
|
+
domainName,
|
|
225
|
+
objectTypeName: OBJECT_TYPE_NAMES.profile,
|
|
226
|
+
description: 'Amplify identifyUser person profile (find-or-create by verified Cognito sub)',
|
|
227
|
+
allowProfileCreation: true,
|
|
228
|
+
fields: AMPLIFY_PROFILE_FIELDS,
|
|
229
|
+
keys: AMPLIFY_PROFILE_KEYS,
|
|
230
|
+
expirationDays,
|
|
231
|
+
});
|
|
232
|
+
const deviceType = new CfnObjectType(this, 'AmplifyDeviceType', {
|
|
233
|
+
domainName,
|
|
234
|
+
objectTypeName: OBJECT_TYPE_NAMES.device,
|
|
235
|
+
description: 'Amplify identifyUser device (unique per stable deviceId; token is a mutable field; resolves to profile by cognitoUserKey)',
|
|
236
|
+
allowProfileCreation: true,
|
|
237
|
+
fields: AMPLIFY_DEVICE_FIELDS,
|
|
238
|
+
keys: AMPLIFY_DEVICE_KEYS,
|
|
239
|
+
expirationDays,
|
|
240
|
+
});
|
|
241
|
+
// A distinct object type for GUEST profiles. Customer
|
|
242
|
+
// Profiles permits exactly one UNIQUE key per object type and PutProfileObject
|
|
243
|
+
// requires the ingested object to carry it; the authed AmplifyProfile reserves
|
|
244
|
+
// its UNIQUE key for cognitoSub, so guest profiles (keyed on the Identity Pool
|
|
245
|
+
// cognitoIdentityId) get their own type with cognitoIdentityKey as UNIQUE.
|
|
246
|
+
const guestProfileType = new CfnObjectType(this, 'AmplifyGuestProfileType', {
|
|
247
|
+
domainName,
|
|
248
|
+
objectTypeName: OBJECT_TYPE_NAMES.guestProfile,
|
|
249
|
+
description: 'Amplify identifyUser GUEST person profile (find-or-create by verified Cognito Identity Pool identityId)',
|
|
250
|
+
allowProfileCreation: true,
|
|
251
|
+
fields: AMPLIFY_GUEST_PROFILE_FIELDS,
|
|
252
|
+
keys: AMPLIFY_GUEST_PROFILE_KEYS,
|
|
253
|
+
expirationDays,
|
|
254
|
+
});
|
|
255
|
+
if (profilesDomain) {
|
|
256
|
+
profileType.addDependency(profilesDomain);
|
|
257
|
+
deviceType.addDependency(profilesDomain);
|
|
258
|
+
guestProfileType.addDependency(profilesDomain);
|
|
259
|
+
}
|
|
260
|
+
// ---- Outbound Campaigns association (create-from-scratch ONLY) ----------
|
|
261
|
+
// When this construct created the instance + domain, a Lambda-backed custom
|
|
262
|
+
// resource associates the new domain with the instance's Outbound Campaigns
|
|
263
|
+
// v2 at deploy time — so Connect Journeys can target the domain's profiles
|
|
264
|
+
// with NO manual console step. In attach mode the existing domain's
|
|
265
|
+
// association is the user's responsibility, so this resource is NOT added.
|
|
266
|
+
if (createFromScratch && connectInstance && profilesDomain) {
|
|
267
|
+
this.addCampaignAssociation(connectInstance, profilesDomain, domainName, props.campaignAssociationLambdaCodePath ??
|
|
268
|
+
defaultCampaignAssociationLambdaCodePath);
|
|
269
|
+
}
|
|
270
|
+
// ---- Lambda handler ----------------------------------------------------
|
|
271
|
+
// Pre-bundled at build time (esbuild) into a self-contained asset, so the
|
|
272
|
+
// construct works regardless of the consuming project's root (Amplify's
|
|
273
|
+
// NodejsFunction guardrail forbids an entry outside the app root).
|
|
274
|
+
const codePath = props.lambdaCodePath ?? defaultLambdaCodePath;
|
|
275
|
+
const fn = new lambda.Function(this, 'IdentifyUserFn', {
|
|
276
|
+
code: lambda.Code.fromAsset(codePath),
|
|
277
|
+
handler: 'index.handler',
|
|
278
|
+
runtime: lambda.Runtime.NODEJS_20_X,
|
|
279
|
+
timeout: Duration.seconds(15),
|
|
280
|
+
memorySize: 256,
|
|
281
|
+
environment: {
|
|
282
|
+
[ENV_DOMAIN_NAME]: domainName,
|
|
283
|
+
},
|
|
284
|
+
});
|
|
285
|
+
// ---- Least-privilege IAM ----------------------------------------------
|
|
286
|
+
const domainArn = Arn.format({
|
|
287
|
+
service: 'profile',
|
|
288
|
+
resource: 'domains',
|
|
289
|
+
resourceName: domainName,
|
|
290
|
+
arnFormat: ArnFormat.SLASH_RESOURCE_NAME,
|
|
291
|
+
}, stack);
|
|
292
|
+
const objectTypesArn = Arn.format({
|
|
293
|
+
service: 'profile',
|
|
294
|
+
resource: 'domains',
|
|
295
|
+
resourceName: `${domainName}/object-types/*`,
|
|
296
|
+
arnFormat: ArnFormat.SLASH_RESOURCE_NAME,
|
|
297
|
+
}, stack);
|
|
298
|
+
fn.addToRolePolicy(new iam.PolicyStatement({
|
|
299
|
+
effect: iam.Effect.ALLOW,
|
|
300
|
+
actions: [
|
|
301
|
+
// PutProfileObject find-or-create (AmplifyProfile) + read-back.
|
|
302
|
+
'profile:PutProfileObject',
|
|
303
|
+
'profile:SearchProfiles',
|
|
304
|
+
// Read back the existing device object to preserve immutable createdAt.
|
|
305
|
+
'profile:ListProfileObjects',
|
|
306
|
+
// Set targeting / person attributes on the resolved profile.
|
|
307
|
+
'profile:UpdateProfile',
|
|
308
|
+
],
|
|
309
|
+
resources: [domainArn, objectTypesArn],
|
|
310
|
+
}));
|
|
311
|
+
// Merge-on-sign-in folds a prior guest profile
|
|
312
|
+
// (+ its device objects) into the authed profile via MergeProfiles.
|
|
313
|
+
// NOTE: the Service Authorization Reference lists MergeProfiles under the
|
|
314
|
+
// `domains` resource type, but the service ACTUALLY authorizes it against
|
|
315
|
+
// an undocumented, differently-shaped resource ARN (note the leading slash
|
|
316
|
+
// and the /profiles/objects/merge suffix):
|
|
317
|
+
// arn:<partition>:profile:<region>:<account>:/domains/<domain>/profiles/objects/merge
|
|
318
|
+
// Granting the documented domain ARN is NOT sufficient. This dedicated
|
|
319
|
+
// statement targets the enforced resource.
|
|
320
|
+
const mergeProfilesArn = `arn:${stack.partition}:profile:${stack.region}:${stack.account}:/domains/${domainName}/profiles/objects/merge`;
|
|
321
|
+
fn.addToRolePolicy(new iam.PolicyStatement({
|
|
322
|
+
effect: iam.Effect.ALLOW,
|
|
323
|
+
actions: ['profile:MergeProfiles'],
|
|
324
|
+
resources: [mergeProfilesArn],
|
|
325
|
+
}));
|
|
326
|
+
// ---- HTTP API + JWT authorizer ----------------------------------------
|
|
327
|
+
const authorizer = new HttpJwtAuthorizer('JwtAuthorizer', props.jwtIssuer, {
|
|
328
|
+
jwtAudience: props.jwtAudience,
|
|
329
|
+
identitySource: ['$request.header.Authorization'],
|
|
330
|
+
});
|
|
331
|
+
const httpApi = new apigwv2.HttpApi(this, 'IdentifyUserApi', {
|
|
332
|
+
apiName: 'identify-user-api',
|
|
333
|
+
createDefaultStage: true,
|
|
334
|
+
});
|
|
335
|
+
httpApi.addRoutes({
|
|
336
|
+
path: this.identifyUserPath,
|
|
337
|
+
methods: [apigwv2.HttpMethod.POST],
|
|
338
|
+
integration: new HttpLambdaIntegration('IdentifyUserIntegration', fn),
|
|
339
|
+
authorizer,
|
|
340
|
+
});
|
|
341
|
+
// ---- Guest (unauthenticated) route ------------------------------------
|
|
342
|
+
// A second route to the SAME identify Lambda, but
|
|
343
|
+
// authorized with IAM/SigV4 instead of a JWT — so an UNAUTHENTICATED Cognito
|
|
344
|
+
// Identity Pool caller (guest credentials) can register a device / seed a
|
|
345
|
+
// profile BEFORE login (the pre-login push-token case).
|
|
346
|
+
//
|
|
347
|
+
// The integration MUST use payload format 1.0: HTTP API payload format 2.0
|
|
348
|
+
// has NO `requestContext.identity` block, so the verified Cognito
|
|
349
|
+
// `cognitoIdentityId` (+ `cognitoAuthenticationType`) the Lambda keys the
|
|
350
|
+
// guest profile on is only present under format 1.0. The Lambda derives the
|
|
351
|
+
// guest identity SOLELY from that authorizer-verified field.
|
|
352
|
+
const iamAuthorizer = new HttpIamAuthorizer();
|
|
353
|
+
httpApi.addRoutes({
|
|
354
|
+
path: this.guestIdentifyUserPath,
|
|
355
|
+
methods: [apigwv2.HttpMethod.POST],
|
|
356
|
+
integration: new HttpLambdaIntegration('GuestIdentifyIntegration', fn, {
|
|
357
|
+
payloadFormatVersion: apigwv2.PayloadFormatVersion.VERSION_1_0,
|
|
358
|
+
}),
|
|
359
|
+
authorizer: iamAuthorizer,
|
|
360
|
+
});
|
|
361
|
+
// `execute-api:Invoke` ARN for the guest route — the app grants this to its
|
|
362
|
+
// Identity Pool UNAUTHENTICATED role so guests can call the route. Scoped as
|
|
363
|
+
// tightly as execute-api allows: `$default/POST/identify-user-guest` pins the
|
|
364
|
+
// grant to the POST method on the guest path of the API's single default
|
|
365
|
+
// stage, so the unauth role can invoke NEITHER other methods, NOR other
|
|
366
|
+
// stages, NOR the authed `/identify-user` route. (HTTP APIs auto-create the
|
|
367
|
+
// `$default` stage; this construct never adds another.)
|
|
368
|
+
this.guestRouteInvokeArn = Arn.format({
|
|
369
|
+
service: 'execute-api',
|
|
370
|
+
resource: httpApi.apiId,
|
|
371
|
+
resourceName: `$default/POST${this.guestIdentifyUserPath}`,
|
|
372
|
+
arnFormat: ArnFormat.SLASH_RESOURCE_NAME,
|
|
373
|
+
}, stack);
|
|
374
|
+
this.apiEndpoint = httpApi.apiEndpoint;
|
|
375
|
+
// Scoped to the stack (not `this`) so the output keeps a stable, readable
|
|
376
|
+
// logical id (`IdentifyUserApiEndpoint`) that a human can find in the
|
|
377
|
+
// CloudFormation console. The construct is provisioned once per stack (the
|
|
378
|
+
// Gen2 factory places it in a dedicated nested stack), so there is no
|
|
379
|
+
// collision risk from the verbatim id.
|
|
380
|
+
new CfnOutput(stack, 'IdentifyUserApiEndpoint', {
|
|
381
|
+
value: httpApi.apiEndpoint,
|
|
382
|
+
description: 'POST {value}/identify-user',
|
|
383
|
+
});
|
|
384
|
+
// Surface the guest route's invoke ARN so a human /
|
|
385
|
+
// the app can grant it to the Identity Pool unauthenticated role.
|
|
386
|
+
new CfnOutput(stack, 'GuestIdentifyRouteInvokeArn', {
|
|
387
|
+
value: this.guestRouteInvokeArn,
|
|
388
|
+
description: 'Grant execute-api:Invoke on this ARN to the Cognito Identity Pool unauthenticated role',
|
|
389
|
+
});
|
|
390
|
+
// ---- Push-delivery path -----------------------------------------------
|
|
391
|
+
// Amazon Connect has no native mobile-push channel, so a Lambda bridges the
|
|
392
|
+
// gap: Connect invokes it via a Journey Custom-action to deliver push
|
|
393
|
+
// through AWS End User Messaging (Pinpoint SendMessages). A minimal Pinpoint
|
|
394
|
+
// application is always created so SendMessages has a valid ApplicationId.
|
|
395
|
+
// The APNs / GCM channels on it are configured only when the caller supplies
|
|
396
|
+
// credentials (see `apnsChannel` / `fcmChannel`); otherwise the application
|
|
397
|
+
// is created with no channel enabled (delivery will 404 until configured).
|
|
398
|
+
const pushApplication = new CfnApp(this, 'PushApp', {
|
|
399
|
+
name: `${domainName}-push`,
|
|
400
|
+
});
|
|
401
|
+
const eumApplicationId = pushApplication.ref;
|
|
402
|
+
this.eumApplicationId = eumApplicationId;
|
|
403
|
+
// ---- Optional push channels (secret-driven) ---------------------------
|
|
404
|
+
// The Amplify Gen2 factory resolves the APNs `.p8` key / FCM service-account
|
|
405
|
+
// JSON from Amplify `secret()`s to deploy-time CFN tokens and passes them as
|
|
406
|
+
// plain strings. Channel credentials flow through CloudFormation the same way
|
|
407
|
+
// Amplify's own external-auth-provider secrets do (defineAuth) — via the
|
|
408
|
+
// secret custom resource's token, never as literal template plain text. The
|
|
409
|
+
// channels attach to THIS construct's own EUM app, so they are provisioned
|
|
410
|
+
// after it and torn down with the stack. The `applicationId` prop resolves to
|
|
411
|
+
// a `Ref` on the EUM app, so CloudFormation infers the create/delete ordering
|
|
412
|
+
// — no explicit `addDependency` needed.
|
|
413
|
+
const { apnsChannel: apnsConfig, fcmChannel: fcmConfig } = props;
|
|
414
|
+
let apnsChannel;
|
|
415
|
+
if (apnsConfig) {
|
|
416
|
+
// Token (`.p8`) authentication: the CFN `DefaultAuthenticationMethod`
|
|
417
|
+
// property accepts `TOKEN` (signing-key auth) or `CERTIFICATE`; token auth
|
|
418
|
+
// uses the `.p8` signing key. Sandbox selects the development APNs endpoint
|
|
419
|
+
// used by development-signed builds.
|
|
420
|
+
const apnsProps = {
|
|
421
|
+
applicationId: eumApplicationId,
|
|
422
|
+
enabled: true,
|
|
423
|
+
defaultAuthenticationMethod: 'TOKEN',
|
|
424
|
+
tokenKey: apnsConfig.tokenKey,
|
|
425
|
+
tokenKeyId: apnsConfig.keyId,
|
|
426
|
+
teamId: apnsConfig.teamId,
|
|
427
|
+
bundleId: apnsConfig.bundleId,
|
|
428
|
+
};
|
|
429
|
+
apnsChannel = apnsConfig.sandbox
|
|
430
|
+
? new CfnAPNSSandboxChannel(this, 'ApnsSandboxChannel', apnsProps)
|
|
431
|
+
: new CfnAPNSChannel(this, 'ApnsChannel', apnsProps);
|
|
432
|
+
}
|
|
433
|
+
let gcmChannel;
|
|
434
|
+
if (fcmConfig) {
|
|
435
|
+
// FCM HTTP v1: `TOKEN` auth with a service-account JSON credential. Google
|
|
436
|
+
// deprecated the legacy server-key (`ApiKey` / `KEY`) API, so only the v1
|
|
437
|
+
// `ServiceJson` (FCM v1) path is used.
|
|
438
|
+
gcmChannel = new CfnGCMChannel(this, 'GcmChannel', {
|
|
439
|
+
applicationId: eumApplicationId,
|
|
440
|
+
enabled: true,
|
|
441
|
+
defaultAuthenticationMethod: 'TOKEN',
|
|
442
|
+
serviceJson: fcmConfig.serviceJson,
|
|
443
|
+
});
|
|
444
|
+
}
|
|
445
|
+
const pushCodePath = props.pushLambdaCodePath ?? defaultPushLambdaCodePath;
|
|
446
|
+
const pushFn = new lambda.Function(this, 'PushHandlerFn', {
|
|
447
|
+
code: lambda.Code.fromAsset(pushCodePath),
|
|
448
|
+
handler: 'index.handler',
|
|
449
|
+
runtime: lambda.Runtime.NODEJS_20_X,
|
|
450
|
+
timeout: Duration.seconds(30),
|
|
451
|
+
memorySize: 256,
|
|
452
|
+
environment: {
|
|
453
|
+
[ENV_DOMAIN_NAME]: domainName,
|
|
454
|
+
[ENV_EUM_APPLICATION_ID]: eumApplicationId,
|
|
455
|
+
},
|
|
456
|
+
});
|
|
457
|
+
// Least-privilege: read + delete device objects on THIS domain only, and
|
|
458
|
+
// SendMessages scoped to THIS EUM application.
|
|
459
|
+
pushFn.addToRolePolicy(new iam.PolicyStatement({
|
|
460
|
+
effect: iam.Effect.ALLOW,
|
|
461
|
+
actions: [
|
|
462
|
+
// Resolve a profile's registered devices (tokens + channels).
|
|
463
|
+
'profile:ListProfileObjects',
|
|
464
|
+
// Stale-token cleanup: delete a device whose token is dead.
|
|
465
|
+
'profile:DeleteProfileObject',
|
|
466
|
+
],
|
|
467
|
+
resources: [domainArn, objectTypesArn],
|
|
468
|
+
}));
|
|
469
|
+
const appMessagesArn = Arn.format({
|
|
470
|
+
service: 'mobiletargeting',
|
|
471
|
+
resource: 'apps',
|
|
472
|
+
resourceName: `${eumApplicationId}/messages`,
|
|
473
|
+
arnFormat: ArnFormat.SLASH_RESOURCE_NAME,
|
|
474
|
+
}, stack);
|
|
475
|
+
pushFn.addToRolePolicy(new iam.PolicyStatement({
|
|
476
|
+
effect: iam.Effect.ALLOW,
|
|
477
|
+
actions: ['mobiletargeting:SendMessages'],
|
|
478
|
+
resources: [appMessagesArn],
|
|
479
|
+
}));
|
|
480
|
+
// ---- Runtime message-template resolution (Q in Connect) ---------------
|
|
481
|
+
// The push Lambda resolves per-profile copy from a Q in Connect (Wisdom)
|
|
482
|
+
// PUSH message template at runtime: it discovers the knowledge base from the
|
|
483
|
+
// journey's campaign (DescribeCampaign -> connectInstanceId ->
|
|
484
|
+
// ListIntegrationAssociations) and renders the template whose name matches
|
|
485
|
+
// the Custom-action ActionId. Least-privilege, scoped to this account /
|
|
486
|
+
// region. IAM prefixes confirmed empirically: campaign actions use
|
|
487
|
+
// `connect-campaigns`, integration listing uses `connect`, and Q in Connect
|
|
488
|
+
// message-template actions use the `wisdom` prefix (Q in Connect is the
|
|
489
|
+
// rebrand of Amazon Connect Wisdom; the SDK client is `@aws-sdk/client-qconnect`
|
|
490
|
+
// but IAM/ARNs remain `wisdom`).
|
|
491
|
+
pushFn.addToRolePolicy(new iam.PolicyStatement({
|
|
492
|
+
effect: iam.Effect.ALLOW,
|
|
493
|
+
actions: ['connect-campaigns:DescribeCampaign'],
|
|
494
|
+
resources: [
|
|
495
|
+
Arn.format({
|
|
496
|
+
service: 'connect-campaigns',
|
|
497
|
+
resource: 'campaign',
|
|
498
|
+
resourceName: '*',
|
|
499
|
+
arnFormat: ArnFormat.SLASH_RESOURCE_NAME,
|
|
500
|
+
}, stack),
|
|
501
|
+
],
|
|
502
|
+
}));
|
|
503
|
+
pushFn.addToRolePolicy(new iam.PolicyStatement({
|
|
504
|
+
effect: iam.Effect.ALLOW,
|
|
505
|
+
actions: ['connect:ListIntegrationAssociations'],
|
|
506
|
+
resources: [
|
|
507
|
+
Arn.format({
|
|
508
|
+
service: 'connect',
|
|
509
|
+
resource: 'instance',
|
|
510
|
+
resourceName: '*',
|
|
511
|
+
arnFormat: ArnFormat.SLASH_RESOURCE_NAME,
|
|
512
|
+
}, stack),
|
|
513
|
+
],
|
|
514
|
+
}));
|
|
515
|
+
pushFn.addToRolePolicy(new iam.PolicyStatement({
|
|
516
|
+
effect: iam.Effect.ALLOW,
|
|
517
|
+
actions: [
|
|
518
|
+
'wisdom:ListMessageTemplates',
|
|
519
|
+
'wisdom:GetMessageTemplate',
|
|
520
|
+
'wisdom:RenderMessageTemplate',
|
|
521
|
+
],
|
|
522
|
+
resources: [
|
|
523
|
+
Arn.format({
|
|
524
|
+
service: 'wisdom',
|
|
525
|
+
resource: 'knowledge-base',
|
|
526
|
+
resourceName: '*',
|
|
527
|
+
arnFormat: ArnFormat.SLASH_RESOURCE_NAME,
|
|
528
|
+
}, stack),
|
|
529
|
+
Arn.format({
|
|
530
|
+
service: 'wisdom',
|
|
531
|
+
resource: 'message-template',
|
|
532
|
+
resourceName: '*',
|
|
533
|
+
arnFormat: ArnFormat.SLASH_RESOURCE_NAME,
|
|
534
|
+
}, stack),
|
|
535
|
+
],
|
|
536
|
+
}));
|
|
537
|
+
// ---- Invoke resource policy for Amazon Connect ------------------------
|
|
538
|
+
// A Connect Journey Custom-action (and Outbound Campaigns v2) invokes this
|
|
539
|
+
// Lambda through the connect / connect-campaigns service principal. Grant
|
|
540
|
+
// lambda:InvokeFunction to both, scoped to THIS account (`sourceAccount`) so
|
|
541
|
+
// only Connect instances in the deploying account — not any Connect instance
|
|
542
|
+
// anywhere — can invoke it (confused-deputy guard). The Connect instance ARN
|
|
543
|
+
// isn't known at synth time, so account scoping is the tightest guard here.
|
|
544
|
+
for (const servicePrincipal of CONNECT_INVOKE_SERVICE_PRINCIPALS) {
|
|
545
|
+
pushFn.addPermission(`Invoke-${servicePrincipal.replace(/[^a-zA-Z0-9]/g, '-')}`, {
|
|
546
|
+
principal: new iam.ServicePrincipal(servicePrincipal),
|
|
547
|
+
action: 'lambda:InvokeFunction',
|
|
548
|
+
sourceAccount: stack.account,
|
|
549
|
+
});
|
|
550
|
+
}
|
|
551
|
+
this.pushFunctionArn = pushFn.functionArn;
|
|
552
|
+
// Scoped to the stack (not `this`) so the output keeps a stable, readable
|
|
553
|
+
// logical id (`PushHandlerFunctionArn`) that a human can find in the
|
|
554
|
+
// CloudFormation console and copy to wire the Journey Custom-action.
|
|
555
|
+
new CfnOutput(stack, 'PushHandlerFunctionArn', {
|
|
556
|
+
value: pushFn.functionArn,
|
|
557
|
+
description: 'Wire as a Connect Journey Custom-action (Invoke Lambda) target',
|
|
558
|
+
});
|
|
559
|
+
// ---- Create-mode outputs ----------------------------------------------
|
|
560
|
+
// Surface the resources this construct provisioned from scratch so a human
|
|
561
|
+
// can find the new instance / domain in the console. Their Outbound
|
|
562
|
+
// Campaigns association is handled automatically by the custom resource above.
|
|
563
|
+
if (connectInstance) {
|
|
564
|
+
new CfnOutput(stack, 'ConnectInstanceId', {
|
|
565
|
+
value: connectInstance.attrId,
|
|
566
|
+
description: 'Amazon Connect instance created for notifications',
|
|
567
|
+
});
|
|
568
|
+
new CfnOutput(stack, 'ConnectInstanceArn', {
|
|
569
|
+
value: connectInstance.attrArn,
|
|
570
|
+
description: 'Amazon Connect instance ARN',
|
|
571
|
+
});
|
|
572
|
+
}
|
|
573
|
+
if (profilesDomain) {
|
|
574
|
+
new CfnOutput(stack, 'ProfilesDomainName', {
|
|
575
|
+
value: domainName,
|
|
576
|
+
description: 'Customer Profiles domain created for notifications',
|
|
577
|
+
});
|
|
578
|
+
}
|
|
579
|
+
this.resources = {
|
|
580
|
+
identifyUserFunction: fn,
|
|
581
|
+
httpApi,
|
|
582
|
+
profileObjectType: profileType,
|
|
583
|
+
guestProfileObjectType: guestProfileType,
|
|
584
|
+
deviceObjectType: deviceType,
|
|
585
|
+
pushFunction: pushFn,
|
|
586
|
+
pushApplication,
|
|
587
|
+
apnsChannel,
|
|
588
|
+
gcmChannel,
|
|
589
|
+
connectInstance,
|
|
590
|
+
profilesDomain,
|
|
591
|
+
};
|
|
592
|
+
}
|
|
593
|
+
/**
|
|
594
|
+
* Create-from-scratch ONLY: a Lambda-backed CDK custom resource that
|
|
595
|
+
* associates the created Customer Profiles domain with the created Amazon
|
|
596
|
+
* Connect instance's Outbound Campaigns v2 at deploy time, so Connect Journeys
|
|
597
|
+
* can target the domain's profiles with no manual console step.
|
|
598
|
+
*
|
|
599
|
+
* A `custom-resources.Provider` fronts the handler because onboarding is
|
|
600
|
+
* asynchronous: the handler starts the instance-onboarding job, then POLLS
|
|
601
|
+
* GetInstanceOnboardingJobStatus until SUCCEEDED (an `AwsCustomResource` can't
|
|
602
|
+
* poll). onCreate/onUpdate are idempotent; onDelete best-effort reverses the
|
|
603
|
+
* integrations + offboards, never failing teardown. The resource depends on
|
|
604
|
+
* both the instance and the domain so it runs after they exist and is torn
|
|
605
|
+
* down before them.
|
|
606
|
+
*/
|
|
607
|
+
addCampaignAssociation(connectInstance, profilesDomain, domainName, codePath) {
|
|
608
|
+
const stack = this.stack;
|
|
609
|
+
const associationFn = new lambda.Function(this, 'CampaignAssociationFn', {
|
|
610
|
+
code: lambda.Code.fromAsset(codePath),
|
|
611
|
+
handler: 'index.handler',
|
|
612
|
+
runtime: lambda.Runtime.NODEJS_20_X,
|
|
613
|
+
// Onboarding polls GetInstanceOnboardingJobStatus until SUCCEEDED (~20s,
|
|
614
|
+
// capped ~2min); allow generous headroom for onboarding + both integrations.
|
|
615
|
+
timeout: Duration.minutes(10),
|
|
616
|
+
memorySize: 256,
|
|
617
|
+
});
|
|
618
|
+
// These instance-onboarding / instance-integration actions operate on the
|
|
619
|
+
// Connect instance's Outbound Campaigns configuration, not on a campaign
|
|
620
|
+
// resource. connect-campaigns does NOT support resource-level permissions
|
|
621
|
+
// for them (IAM evaluates them against `campaign/*` and denies an
|
|
622
|
+
// `instance/*`-scoped grant), so they must be granted on `*`.
|
|
623
|
+
associationFn.addToRolePolicy(new iam.PolicyStatement({
|
|
624
|
+
effect: iam.Effect.ALLOW,
|
|
625
|
+
actions: [
|
|
626
|
+
'connect-campaigns:StartInstanceOnboardingJob',
|
|
627
|
+
'connect-campaigns:GetInstanceOnboardingJobStatus',
|
|
628
|
+
'connect-campaigns:DeleteInstanceOnboardingJob',
|
|
629
|
+
'connect-campaigns:PutConnectInstanceIntegration',
|
|
630
|
+
'connect-campaigns:DeleteConnectInstanceIntegration',
|
|
631
|
+
],
|
|
632
|
+
resources: ['*'],
|
|
633
|
+
}));
|
|
634
|
+
// connect-campaigns onboarding + PutConnectInstanceIntegration validate the
|
|
635
|
+
// target Connect instance on the caller's behalf, so the handler role also
|
|
636
|
+
// needs connect:DescribeInstance on the (construct-owned) instance.
|
|
637
|
+
associationFn.addToRolePolicy(new iam.PolicyStatement({
|
|
638
|
+
effect: iam.Effect.ALLOW,
|
|
639
|
+
actions: ['connect:DescribeInstance'],
|
|
640
|
+
resources: [connectInstance.attrArn],
|
|
641
|
+
}));
|
|
642
|
+
// Reciprocal Customer Profiles integration on THIS domain only (IAM prefix
|
|
643
|
+
// for Customer Profiles is `profile`). GetDomain is read-only and required
|
|
644
|
+
// because PutConnectInstanceIntegration validates the Customer Profiles
|
|
645
|
+
// domain before wiring the integration.
|
|
646
|
+
const domainArn = Arn.format({
|
|
647
|
+
service: 'profile',
|
|
648
|
+
resource: 'domains',
|
|
649
|
+
resourceName: domainName,
|
|
650
|
+
arnFormat: ArnFormat.SLASH_RESOURCE_NAME,
|
|
651
|
+
}, stack);
|
|
652
|
+
associationFn.addToRolePolicy(new iam.PolicyStatement({
|
|
653
|
+
effect: iam.Effect.ALLOW,
|
|
654
|
+
actions: [
|
|
655
|
+
'profile:PutIntegration',
|
|
656
|
+
'profile:DeleteIntegration',
|
|
657
|
+
'profile:GetDomain',
|
|
658
|
+
],
|
|
659
|
+
resources: [
|
|
660
|
+
domainArn,
|
|
661
|
+
Arn.format({
|
|
662
|
+
service: 'profile',
|
|
663
|
+
resource: 'domains',
|
|
664
|
+
resourceName: `${domainName}/integrations/*`,
|
|
665
|
+
arnFormat: ArnFormat.SLASH_RESOURCE_NAME,
|
|
666
|
+
}, stack),
|
|
667
|
+
],
|
|
668
|
+
}));
|
|
669
|
+
// PutConnectInstanceIntegration validates the four built-in campaign
|
|
670
|
+
// object-type TEMPLATES referenced by objectTypeNames (Campaign-Email,
|
|
671
|
+
// Campaign-SMS, Campaign-Telephony, Campaign-Orchestration) via
|
|
672
|
+
// profile:GetProfileObjectTypeTemplate, using the CALLER's credentials.
|
|
673
|
+
// Templates are account/region-level resources whose ARN carries a leading
|
|
674
|
+
// slash (`:/templates/<name>`), distinct from the domain, so they need their
|
|
675
|
+
// own grant.
|
|
676
|
+
const profileTemplatesArn = `arn:${stack.partition}:profile:${stack.region}:${stack.account}:/templates/*`;
|
|
677
|
+
associationFn.addToRolePolicy(new iam.PolicyStatement({
|
|
678
|
+
effect: iam.Effect.ALLOW,
|
|
679
|
+
actions: [
|
|
680
|
+
'profile:GetProfileObjectTypeTemplate',
|
|
681
|
+
'profile:ListProfileObjectTypeTemplates',
|
|
682
|
+
],
|
|
683
|
+
resources: [profileTemplatesArn],
|
|
684
|
+
}));
|
|
685
|
+
// Onboarding auto-creates the Outbound Campaigns service-linked role; the
|
|
686
|
+
// handler then lists roles to resolve its ARN. iam:ListRoles has no
|
|
687
|
+
// resource-level scoping, so it must target `*`.
|
|
688
|
+
associationFn.addToRolePolicy(new iam.PolicyStatement({
|
|
689
|
+
effect: iam.Effect.ALLOW,
|
|
690
|
+
actions: ['iam:ListRoles'],
|
|
691
|
+
resources: ['*'],
|
|
692
|
+
}));
|
|
693
|
+
// Each StartInstanceOnboardingJob for a NEW Connect instance creates its own
|
|
694
|
+
// connect-campaigns service-linked role (the role name carries a
|
|
695
|
+
// service-generated random suffix), so the grant must use `*` for the
|
|
696
|
+
// resource and is instead scoped safely by the iam:AWSServiceName condition
|
|
697
|
+
// to ONLY the connect-campaigns SLR. A path-scoped resource ARN is denied by
|
|
698
|
+
// IAM here (surfaced as the onboarding job's IAM_ACCESS_DENIED failureCode).
|
|
699
|
+
const campaignsSlrArn = `arn:${stack.partition}:iam::${stack.account}:role/aws-service-role/${CONNECT_CAMPAIGNS_SERVICE_NAME}/*`;
|
|
700
|
+
associationFn.addToRolePolicy(new iam.PolicyStatement({
|
|
701
|
+
effect: iam.Effect.ALLOW,
|
|
702
|
+
actions: ['iam:CreateServiceLinkedRole'],
|
|
703
|
+
resources: ['*'],
|
|
704
|
+
conditions: {
|
|
705
|
+
StringEquals: {
|
|
706
|
+
'iam:AWSServiceName': CONNECT_CAMPAIGNS_SERVICE_NAME,
|
|
707
|
+
},
|
|
708
|
+
},
|
|
709
|
+
}));
|
|
710
|
+
associationFn.addToRolePolicy(new iam.PolicyStatement({
|
|
711
|
+
effect: iam.Effect.ALLOW,
|
|
712
|
+
actions: ['iam:PassRole'],
|
|
713
|
+
resources: [campaignsSlrArn],
|
|
714
|
+
}));
|
|
715
|
+
// During onboarding, connect-campaigns customizes its service-linked role
|
|
716
|
+
// with instance-specific inline permissions (the "additional permissions
|
|
717
|
+
// added for the service-linked role to access the resources", incl. the
|
|
718
|
+
// Customer Profiles integration access) via iam:PutRolePolicy /
|
|
719
|
+
// iam:AttachRolePolicy, performed with the CALLER's credentials. Without
|
|
720
|
+
// these the onboarding job fails with failureCode IAM_ACCESS_DENIED. The
|
|
721
|
+
// reverse (Delete/Detach) mirrors this so the best-effort teardown can
|
|
722
|
+
// remove those inline permissions when disassociating. Scoped to ONLY the
|
|
723
|
+
// connect-campaigns service-linked role.
|
|
724
|
+
associationFn.addToRolePolicy(new iam.PolicyStatement({
|
|
725
|
+
effect: iam.Effect.ALLOW,
|
|
726
|
+
actions: [
|
|
727
|
+
'iam:PutRolePolicy',
|
|
728
|
+
'iam:AttachRolePolicy',
|
|
729
|
+
'iam:DeleteRolePolicy',
|
|
730
|
+
'iam:DetachRolePolicy',
|
|
731
|
+
],
|
|
732
|
+
resources: [campaignsSlrArn],
|
|
733
|
+
}));
|
|
734
|
+
// Onboarding also provisions a managed EventBridge rule (named
|
|
735
|
+
// `ConnectCampaignsRule*`) that drives campaign event delivery, created with
|
|
736
|
+
// the CALLER's credentials. Without these the onboarding job fails with
|
|
737
|
+
// failureCode EVENT_BRIDGE_ACCESS_DENIED. ListRules has no resource-level
|
|
738
|
+
// scoping; the mutating actions are scoped to the ConnectCampaignsRule* name.
|
|
739
|
+
const connectCampaignsRuleArn = `arn:${stack.partition}:events:${stack.region}:${stack.account}:rule/ConnectCampaignsRule*`;
|
|
740
|
+
associationFn.addToRolePolicy(new iam.PolicyStatement({
|
|
741
|
+
effect: iam.Effect.ALLOW,
|
|
742
|
+
actions: ['events:ListRules'],
|
|
743
|
+
resources: ['*'],
|
|
744
|
+
}));
|
|
745
|
+
associationFn.addToRolePolicy(new iam.PolicyStatement({
|
|
746
|
+
effect: iam.Effect.ALLOW,
|
|
747
|
+
actions: [
|
|
748
|
+
'events:PutRule',
|
|
749
|
+
'events:PutTargets',
|
|
750
|
+
'events:DeleteRule',
|
|
751
|
+
'events:RemoveTargets',
|
|
752
|
+
'events:ListTargetsByRule',
|
|
753
|
+
'events:DescribeRule',
|
|
754
|
+
],
|
|
755
|
+
resources: [connectCampaignsRuleArn],
|
|
756
|
+
}));
|
|
757
|
+
const provider = new Provider(this, 'CampaignAssociationProvider', {
|
|
758
|
+
onEventHandler: associationFn,
|
|
759
|
+
});
|
|
760
|
+
const association = new CustomResource(this, 'CampaignAssociation', {
|
|
761
|
+
serviceToken: provider.serviceToken,
|
|
762
|
+
resourceType: 'Custom::OutboundCampaignsDomainAssociation',
|
|
763
|
+
properties: {
|
|
764
|
+
ConnectInstanceId: connectInstance.attrId,
|
|
765
|
+
DomainName: domainName,
|
|
766
|
+
Account: stack.account,
|
|
767
|
+
Region: stack.region,
|
|
768
|
+
},
|
|
769
|
+
});
|
|
770
|
+
// Provision after — and tear down before — the instance + domain it links.
|
|
771
|
+
association.node.addDependency(connectInstance);
|
|
772
|
+
association.node.addDependency(profilesDomain);
|
|
773
|
+
}
|
|
774
|
+
/**
|
|
775
|
+
* Deterministic, stable, per-app base name for the created Connect instance
|
|
776
|
+
* and Customer Profiles domain (create-from-scratch mode).
|
|
777
|
+
*
|
|
778
|
+
* The name must be STABLE across deploy and delete (so CFN resolves the same
|
|
779
|
+
* resource on teardown) and UNIQUE per app / environment (so two apps in the
|
|
780
|
+
* same account don't collide on the globally-unique Connect alias). It is
|
|
781
|
+
* derived from a short hash of the root stack name (which, in Amplify Gen2,
|
|
782
|
+
* embeds the app namespace + branch) plus this construct's path — both stable
|
|
783
|
+
* inputs.
|
|
784
|
+
*
|
|
785
|
+
* The `amazon-connect-` prefix is REQUIRED, not cosmetic: Amazon Connect's
|
|
786
|
+
* AWS-managed service-linked-role policy (`AmazonConnectServiceLinkedRolePolicy`,
|
|
787
|
+
* statement `AllowCustomerProfilesForConnectDomain`) grants the instance's
|
|
788
|
+
* role `profile:*` ONLY on `arn:aws:profile:*:*:domains/amazon-connect-*`. A
|
|
789
|
+
* domain named otherwise is unreachable by the instance — the Connect console
|
|
790
|
+
* and Journey segment builder then report "does not have permissions to access
|
|
791
|
+
* Customer Profiles". The SLR is AWS-protected (no inline policy can be added)
|
|
792
|
+
* and the managed policy is not editable, so the domain name is the only lever.
|
|
793
|
+
*/
|
|
794
|
+
generateResourceName() {
|
|
795
|
+
let root = this.stack;
|
|
796
|
+
while (root.nestedStackParent) {
|
|
797
|
+
root = root.nestedStackParent;
|
|
798
|
+
}
|
|
799
|
+
const seed = `${root.stackName}::${this.node.path}`;
|
|
800
|
+
const suffix = createHash('sha256').update(seed).digest('hex').slice(0, 12);
|
|
801
|
+
return `amazon-connect-notifications-${suffix}`;
|
|
802
|
+
}
|
|
803
|
+
/**
|
|
804
|
+
* Coerce an arbitrary string into a valid Amazon Connect instance alias:
|
|
805
|
+
* lowercase alphanumeric characters + single hyphens, no leading/trailing hyphen, not
|
|
806
|
+
* starting with the reserved `d-` prefix, and within Connect's 1–62 char
|
|
807
|
+
* bound.
|
|
808
|
+
*/
|
|
809
|
+
sanitizeInstanceAlias(raw) {
|
|
810
|
+
let alias = raw
|
|
811
|
+
.toLowerCase()
|
|
812
|
+
.replace(/[^a-z0-9-]/g, '-')
|
|
813
|
+
.replace(/-+/g, '-')
|
|
814
|
+
.replace(/^-+|-+$/g, '');
|
|
815
|
+
if (alias.startsWith('d-')) {
|
|
816
|
+
alias = `a${alias}`;
|
|
817
|
+
}
|
|
818
|
+
if (alias.length === 0) {
|
|
819
|
+
alias = 'amplify-notifications';
|
|
820
|
+
}
|
|
821
|
+
return alias.slice(0, 62).replace(/-+$/g, '');
|
|
822
|
+
}
|
|
823
|
+
}
|
|
824
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY29uc3RydWN0LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vc3JjL2NvbnN0cnVjdC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssSUFBSSxNQUFNLE1BQU0sQ0FBQztBQUM3QixPQUFPLEVBQUUsVUFBVSxFQUFFLE1BQU0sYUFBYSxDQUFDO0FBQ3pDLE9BQU8sRUFBRSxhQUFhLEVBQUUsTUFBTSxVQUFVLENBQUM7QUFDekMsT0FBTyxFQUNMLEdBQUcsRUFDSCxTQUFTLEVBQ1QsU0FBUyxFQUNULGNBQWMsRUFDZCxRQUFRLEVBQ1IsS0FBSyxHQUNOLE1BQU0sYUFBYSxDQUFDO0FBQ3JCLE9BQU8sRUFBRSxTQUFTLEVBQUUsTUFBTSxZQUFZLENBQUM7QUFDdkMsT0FBTyxLQUFLLE1BQU0sTUFBTSx3QkFBd0IsQ0FBQztBQUNqRCxPQUFPLEtBQUssR0FBRyxNQUFNLHFCQUFxQixDQUFDO0FBQzNDLE9BQU8sRUFBRSxRQUFRLEVBQUUsTUFBTSw4QkFBOEIsQ0FBQztBQUN4RCxPQUFPLEVBQ0wsU0FBUyxFQUNULGNBQWMsRUFDZCxhQUFhLEdBQ2QsTUFBTSxrQ0FBa0MsQ0FBQztBQUMxQyxPQUFPLEVBQ0wsV0FBVyxFQUNYLHlCQUF5QixHQUMxQixNQUFNLHlCQUF5QixDQUFDO0FBQ2pDLE9BQU8sRUFBRSxnQkFBZ0IsRUFBRSxNQUFNLHdCQUF3QixDQUFDO0FBQzFELE9BQU8sRUFDTCxjQUFjLEVBQ2QscUJBQXFCLEVBQ3JCLE1BQU0sRUFDTixhQUFhLEdBQ2QsTUFBTSwwQkFBMEIsQ0FBQztBQUNsQyxPQUFPLEtBQUssT0FBTyxNQUFNLDhCQUE4QixDQUFDO0FBQ3hELE9BQU8sRUFDTCxpQkFBaUIsRUFDakIsaUJBQWlCLEdBQ2xCLE1BQU0sMENBQTBDLENBQUM7QUFDbEQsT0FBTyxFQUFFLHFCQUFxQixFQUFFLE1BQU0sMkNBQTJDLENBQUM7QUFHbEYsT0FBTyxFQUNMLDhCQUE4QixFQUM5QixpQ0FBaUMsRUFDakMs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QUFDLEVBQUUsRUFBRSxDQUFDLENBQUMsT0FBTyxDQUFDLE1BQU0sRUFBRSxFQUFFLENBQUMsQ0FBQztJQUNoRCxDQUFDO0NBQ0YiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgKiBhcyBwYXRoIGZyb20gJ3BhdGgnO1xuaW1wb3J0IHsgY3JlYXRlSGFzaCB9IGZyb20gJ25vZGU6Y3J5cHRvJztcbmltcG9ydCB7IGZpbGVVUkxUb1BhdGggfSBmcm9tICdub2RlOnVybCc7XG5pbXBvcnQge1xuICBBcm4sXG4gIEFybkZvcm1hdCxcbiAgQ2ZuT3V0cHV0LFxuICBDdXN0b21SZXNvdXJjZSxcbiAgRHVyYXRpb24sXG4gIFN0YWNrLFxufSBmcm9tICdhd3MtY2RrLWxpYic7XG5pbXBvcnQgeyBDb25zdHJ1Y3QgfSBmcm9tICdjb25zdHJ1Y3RzJztcbmltcG9ydCAqIGFzIGxhbWJkYSBmcm9tICdhd3MtY2RrLWxpYi9hd3MtbGFtYmRhJztcbmltcG9ydCAqIGFzIGlhbSBmcm9tICdhd3MtY2RrLWxpYi9hd3MtaWFtJztcbmltcG9ydCB7IFByb3ZpZGVyIH0gZnJvbSAnYXdzLWNkay1saWIvY3VzdG9tLXJlc291cmNlcyc7XG5pbXBvcnQge1xuICBDZm5Eb21haW4sXG4gIENmbkludGVncmF0aW9uLFxuICBDZm5PYmplY3RUeXBlLFxufSBmcm9tICdhd3MtY2RrLWxpYi9hd3MtY3VzdG9tZXJwcm9maWxlcyc7XG5pbXBvcnQge1xuICBDZm5JbnN0YW5jZSxcbiAgQ2ZuSW50ZWdyYXRpb25Bc3NvY2lhdGlvbixcbn0gZnJvbSAnYXdzLWNkay1saWIvYXdzLWNvbm5lY3QnO1xuaW1wb3J0IHsgQ2ZuS25vd2xlZGdlQmFzZSB9IGZyb20gJ2F3cy1jZGstbGliL2F3cy13aXNkb20nO1xuaW1wb3J0IHtcbiAgQ2ZuQVBOU0NoYW5uZWwsXG4gIENmbkFQTlNTYW5kYm94Q2hhbm5lbCxcbiAgQ2ZuQXBwLFxuICBDZm5HQ01DaGFubmVsLFxufSBmcm9tICdhd3MtY2RrLWxpYi9hd3MtcGlucG9pbnQnO1xuaW1wb3J0ICogYXMgYXBpZ3d2MiBmcm9tICdhd3MtY2RrLWxpYi9hd3MtYXBpZ2F0ZXdheXYyJztcbmltcG9ydCB7XG4gIEh0dHBJYW1BdXRob3JpemVyLFxuICBIdHRwSnd0QXV0aG9yaXplcixcbn0gZnJvbSAnYXdzLWNkay1saWIvYXdzLWFwaWdhdGV3YXl2Mi1hdXRob3JpemVycyc7XG5pbXBvcnQgeyBIdHRwTGFtYmRhSW50ZWdyYXRpb24gfSBmcm9tICdhd3MtY2RrLWxpYi9hd3MtYXBpZ2F0ZXdheXYyLWludGVncmF0aW9ucyc7XG5pbXBvcnQgeyBSZXNvdXJjZVByb3ZpZGVyLCBTdGFja1Byb3ZpZGVyIH0gZnJvbSAnQGF3cy1hbXBsaWZ5L3BsdWdpbi10eXBlcyc7XG5cbmltcG9ydCB7XG4gIENPTk5FQ1RfQ0FNUEFJR05TX1NFUlZJQ0VfTkFNRSxcbiAgQ09OTkVDVF9JTlZPS0VfU0VSVklDRV9QUklOQ0lQQUxTLFxuICBERUZBVUxUX0VYUElSQVRJT05fREFZUyxcbiAgRU5WX0RPTUFJTl9OQU1FLFxuICBFTlZfRVVNX0FQUExJQ0FUSU9OX0lELFxufSBmcm9tICcuL2NvbnN0YW50cy5qcyc7XG5pbXBvcnQge1xuICBBTVBMSUZZX0RFVklDRV9GSUVMRFMsXG4gIEFNUExJRllfREVWSUNFX0tFWVMsXG4gIEFNUExJRllfR1VFU1RfUFJPRklMRV9GSUVMRFMsXG4gIEFNUExJRllfR1VFU1RfUFJPRklMRV9LRVlTLFxuICBBTVBMSUZZX1BST0ZJTEVfRklFTERTLFxuICBBTVBMSUZZX1BST0ZJTEVfS0VZUyxcbiAgT0JKRUNUX1RZUEVfTkFNRVMsXG59IGZyb20gJy4vb2JqZWN0X3R5cGVzLmpzJztcblxuLyoqXG4gKiBUaGUgcHJlLWJ1bmRsZWQgaWRlbnRpZnktdXNlciBMYW1iZGEgYXNzZXQgc2hpcHMgYWxvbmdzaWRlIHRoZSBjb21waWxlZFxuICogY29uc3RydWN0IGF0IGA8cGFja2FnZVJvb3Q+L2xpYi9oYW5kbGVyLWFzc2V0YCAocHJvZHVjZWQgYnkgdGhlIHBhY2thZ2Unc1xuICogYHBvc3Q6Y29tcGlsZWAgZXNidWlsZCBzdGVwKS4gUmVzb2x2aW5nIHJlbGF0aXZlIHRvIHRoZSBwYWNrYWdlIHJvb3Qg4oCUIHJhdGhlclxuICogdGhhbiB0byB0aGlzIG1vZHVsZSDigJQgbWVhbnMgdGhlIHBhdGggaXMgY29ycmVjdCB3aGV0aGVyIHRoZSBjb2RlIHJ1bnMgZnJvbVxuICogdGhlIGNvbXBpbGVkIGBsaWIvYCBvdXRwdXQgb3IgZnJvbSBgc3JjL2AgdW5kZXIgYHRzeGAgKGFzIHRoZSB1bml0IHRlc3RzIGRvKS5cbiAqL1xuY29uc3QgcGFja2FnZVJvb3QgPSBmaWxlVVJMVG9QYXRoKG5ldyBVUkwoJy4uJywgaW1wb3J0Lm1ldGEudXJsKSk7XG5jb25zdCBkZWZhdWx0TGFtYmRhQ29kZVBhdGggPSBwYXRoLmpvaW4ocGFja2FnZVJvb3QsICdsaWInLCAnaGFuZGxlci1hc3NldCcpO1xuY29uc3QgZGVmYXVsdFB1c2hMYW1iZGFDb2RlUGF0aCA9IHBhdGguam9pbihcbiAgcGFja2FnZVJvb3QsXG4gICdsaWInLFxuICAncHVzaC1oYW5kbGVyLWFzc2V0Jyxcbik7XG5jb25zdCBkZWZhdWx0Q2FtcGFpZ25Bc3NvY2lhdGlvbkxhbWJkYUNvZGVQYXRoID0gcGF0aC5qb2luKFxuICBwYWNrYWdlUm9vdCxcbiAgJ2xpYicsXG4gICdjYW1wYWlnbi1hc3NvY2lhdGlvbi1hc3NldCcsXG4pO1xuXG4vKiogQ0RLIHJlc291cmNlcyBleHBvc2VkIGJ5IHtAbGluayBBbXBsaWZ5Tm90aWZpY2F0aW9uc30uICovXG5leHBvcnQgdHlwZSBOb3RpZmljYXRpb25zUmVzb3VyY2VzID0ge1xuICAvKiogVGhlIGlkZW50aWZ5LXVzZXIgTGFtYmRhIGZ1bmN0aW9uLiAqL1xuICBpZGVudGlmeVVzZXJGdW5jdGlvbjogbGFtYmRhLklGdW5jdGlvbjtcbiAgLyoqIFRoZSBIVFRQIEFQSSBmcm9udGluZyB0aGUgaWRlbnRpZnktdXNlciByb3V0ZS4gKi9cbiAgaHR0cEFwaTogYXBpZ3d2Mi5IdHRwQXBpO1xuICAvKiogVGhlIEFtcGxpZnlQcm9maWxlIG9iamVjdCB0eXBlIHJlZ2lzdGVyZWQgb24gdGhlIGRvbWFpbi4gKi9cbiAgcHJvZmlsZU9iamVjdFR5cGU6IENmbk9iamVjdFR5cGU7XG4gIC8qKiBUaGUgQW1wbGlmeUd1ZXN0UHJvZmlsZSBvYmplY3QgdHlwZSByZWdpc3RlcmVkIG9uIHRoZSBkb21haW4uICovXG4gIGd1ZXN0UHJvZmlsZU9iamVjdFR5cGU6IENmbk9iamVjdFR5cGU7XG4gIC8qKiBUaGUgQW1wbGlmeURldmljZSBvYmplY3QgdHlwZSByZWdpc3RlcmVkIG9uIHRoZSBkb21haW4uICovXG4gIGRldmljZU9iamVjdFR5cGU6IENmbk9iamVjdFR5cGU7XG4gIC8qKlxuICAgKiBUaGUgcHVzaC1kZWxpdmVyeSBMYW1iZGEgZnVuY3Rpb24sIHdpcmVkIGFzIHRoZSB0YXJnZXQgb2YgYSBDb25uZWN0IEpvdXJuZXlcbiAgICogQ3VzdG9tLWFjdGlvbiAoSW52b2tlIExhbWJkYSkuXG4gICAqL1xuICBwdXNoRnVuY3Rpb246IGxhbWJkYS5JRnVuY3Rpb247XG4gIC8qKiBUaGUgQVdTIEVuZCBVc2VyIE1lc3NhZ2luZyAvIFBpbnBvaW50IGFwcGxpY2F0aW9uIGJhY2tpbmcgcHVzaCBkZWxpdmVyeS4gKi9cbiAgcHVzaEFwcGxpY2F0aW9uOiBDZm5BcHA7XG4gIC8qKlxuICAgKiBUaGUgQVBOcyBjaGFubmVsIGVuYWJsZWQgb24gdGhlIHB1c2ggYXBwbGljYXRpb24sIHdoZW4gYGFwbnNDaGFubmVsYCBpc1xuICAgKiBjb25maWd1cmVkLiBgdW5kZWZpbmVkYCB3aGVuIG5vIEFQTnMgY29uZmlndXJhdGlvbiB3YXMgcHJvdmlkZWQuIEVpdGhlciB0aGVcbiAgICogcHJvZHVjdGlvbiAoYENmbkFQTlNDaGFubmVsYCkgb3Igc2FuZGJveCAoYENmbkFQTlNTYW5kYm94Q2hhbm5lbGApIGNoYW5uZWwsXG4gICAqIGRlcGVuZGluZyBvbiBgYXBuc0NoYW5uZWwuc2FuZGJveGAuXG4gICAqL1xuICBhcG5zQ2hhbm5lbD86IENmbkFQTlNDaGFubmVsIHwgQ2ZuQVBOU1NhbmRib3hDaGFubmVsO1xuICAvKipcbiAgICogVGhlIEdDTS9GQ00gY2hhbm5lbCBlbmFibGVkIG9uIHRoZSBwdXNoIGFwcGxpY2F0aW9uLCB3aGVuIGBmY21DaGFubmVsYCBpc1xuICAgKiBjb25maWd1cmVkLiBgdW5kZWZpbmVkYCB3aGVuIG5vIEZDTSBjb25maWd1cmF0aW9uIHdhcyBwcm92aWRlZC5cbiAgICovXG4gIGdjbUNoYW5uZWw/OiBDZm5HQ01DaGFubmVsO1xuICAvKipcbiAgICogVGhlIEFtYXpvbiBDb25uZWN0IGluc3RhbmNlIGNyZWF0ZWQgaW4gY3JlYXRlLWZyb20tc2NyYXRjaCBtb2RlICh3aGVuXG4gICAqIGBkb21haW5OYW1lYCBpcyBvbWl0dGVkKS4gYHVuZGVmaW5lZGAgaW4gYXR0YWNoIG1vZGUuXG4gICAqL1xuICBjb25uZWN0SW5zdGFuY2U/OiBDZm5JbnN0YW5jZTtcbiAgLyoqXG4gICAqIFRoZSBDdXN0b21lciBQcm9maWxlcyBkb21haW4gY3JlYXRlZCBpbiBjcmVhdGUtZnJvbS1zY3JhdGNoIG1vZGUgKHdoZW5cbiAgICogYGRvbWFpbk5hbWVgIGlzIG9taXR0ZWQpLiBgdW5kZWZpbmVkYCBpbiBhdHRhY2ggbW9kZSwgd2hlcmUgdGhlIG9iamVjdCB0eXBlc1xuICAgKiBhcmUgcmVnaXN0ZXJlZCBpbnRvIGEgcHJlLWV4aXN0aW5nIGRvbWFpbiB0aGUgY29uc3RydWN0IGRvZXMgbm90IG93bi5cbiAgICovXG4gIHByb2ZpbGVzRG9tYWluPzogQ2ZuRG9tYWluO1xufTtcblxuZXhwb3J0IHR5cGUgQW1wbGlmeU5vdGlmaWNhdGlvbnNQcm9wcyA9IHtcbiAgLyoqXG4gICAqIFRoZSBKV1QgaXNzdWVyIFVSTCB0aGUgSFRUUCBBUEkgYXV0aG9yaXplciB0cnVzdHMuIEZvciBhIENvZ25pdG8gdXNlciBwb29sXG4gICAqIHRoaXMgaXMgYGh0dHBzOi8vY29nbml0by1pZHAuPHJlZ2lvbj4uYW1hem9uYXdzLmNvbS88dXNlclBvb2xJZD5gLlxuICAgKi9cbiAgcmVhZG9ubHkgand0SXNzdWVyOiBzdHJpbmc7XG5cbiAgLyoqXG4gICAqIEFsbG93ZWQgSldUIGF1ZGllbmNlcy4gRm9yIGEgQ29nbml0byB1c2VyIHBvb2wgdGhpcyBpcyB0aGUgYXBwIGNsaWVudCBpZChzKVxuICAgKiAobWF0Y2hlZCBhZ2FpbnN0IGBhdWRgIGZvciBpZCB0b2tlbnMgLyBgY2xpZW50X2lkYCBmb3IgYWNjZXNzIHRva2VucykuXG4gICAqL1xuICByZWFkb25seSBqd3RBdWRpZW5jZTogc3RyaW5nW107XG5cbiAgLyoqXG4gICAqIE5hbWUgb2YgYW4gRVhJU1RJTkcgQ3VzdG9tZXIgUHJvZmlsZXMgZG9tYWluIHRvIGF0dGFjaCB0byDigJQgZS5nLiB0aGUgZG9tYWluXG4gICAqIEFtYXpvbiBDb25uZWN0IGF1dG8tY3JlYXRlcyB3aGVuIEN1c3RvbWVyIFByb2ZpbGVzIGlzIGVuYWJsZWQgb24gYW5cbiAgICogaW5zdGFuY2UuIFdoZW4gcHJvdmlkZWQsIHRoZSBjb25zdHJ1Y3QgcnVucyBpbiBBVFRBQ0ggbW9kZTogaXQgcmVnaXN0ZXJzIHRoZVxuICAgKiBBbXBsaWZ5UHJvZmlsZSAvIEFtcGxpZnlEZXZpY2Ugb2JqZWN0IHR5cGVzIElOVE8gdGhpcyBkb21haW4gYW5kIG5ldmVyXG4gICAqIGNyZWF0ZXMgYW4gaW5zdGFuY2Ugb3IgYSBkb21haW4uXG4gICAqXG4gICAqIE9NSVQgdGhpcyB0byBydW4gaW4gdGhlIGRlZmF1bHQgQ1JFQVRFLUZST00tU0NSQVRDSCBtb2RlOiB0aGUgY29uc3RydWN0XG4gICAqIHByb3Zpc2lvbnMgYSBicmFuZC1uZXcgQW1hem9uIENvbm5lY3QgaW5zdGFuY2UgQU5EIGEgYnJhbmQtbmV3IEN1c3RvbWVyXG4gICAqIFByb2ZpbGVzIGRvbWFpbiAod2l0aCBnZW5lcmF0ZWQsIHN0YWJsZSBuYW1lcykgYW5kIHdpcmVzIGV2ZXJ5dGhpbmcgaW50b1xuICAgKiB0aGF0IG5ldyBkb21haW4g4oCUIHNvIGEgY2FsbGVyIG5lZWRzIHplcm8gcHJlLWV4aXN0aW5nIENvbm5lY3Qgc2V0dXAuXG4gICAqL1xuICByZWFkb25seSBkb21haW5OYW1lPzogc3RyaW5nO1xuXG4gIC8qKlxuICAgKiBDUkVBVEUgbW9kZSBvbmx5OiBvdmVycmlkZSB0aGUgYXV0by1nZW5lcmF0ZWQgQW1hem9uIENvbm5lY3QgaW5zdGFuY2UgYWxpYXMuXG4gICAqIElnbm9yZWQgaW4gYXR0YWNoIG1vZGUuIFdoZW4gb21pdHRlZCwgYSBkZXRlcm1pbmlzdGljLXlldC11bmlxdWUgYWxpYXMgaXNcbiAgICogZGVyaXZlZCBmcm9tIHRoZSBjb25zdHJ1Y3QncyBzY29wZSBzbyBpdCBpcyBzdGFibGUgYWNyb3NzIGRlcGxveS9kZWxldGUgYW5kXG4gICAqIHVuaXF1ZSBwZXIgYXBwLiBMb3dlcmNhc2UgYWxwaGFudW1lcmljIGNoYXJhY3RlcnMgKyBoeXBoZW5zOyBtdXN0IG5vdCBzdGFydFxuICAgKiB3aXRoXG4gICAqIGBkLWAuXG4gICAqL1xuICByZWFkb25seSBpbnN0YW5jZUFsaWFzPzogc3RyaW5nO1xuXG4gIC8qKlxuICAgKiBPdmVycmlkZSB0aGUgZGlyZWN0b3J5IGNvbnRhaW5pbmcgdGhlIHByZS1idW5kbGVkIExhbWJkYSBhc3NldCAoYW5cbiAgICogYGluZGV4LmpzYCBleHBvcnRpbmcgYGhhbmRsZXJgKS4gRGVmYXVsdHMgdG8gdGhlIGhhbmRsZXIgYnVuZGxlZCBpbnNpZGUgdGhpc1xuICAgKiBwYWNrYWdlIChgbGliL2hhbmRsZXItYXNzZXRgLCBwcm9kdWNlZCBieSBgcG9zdDpjb21waWxlYCkuXG4gICAqL1xuICByZWFkb25seSBsYW1iZGFDb2RlUGF0aD86IHN0cmluZztcblxuICAvKiogUHJvZmlsZSAvIG9iamVjdC10eXBlIGV4cGlyYXRpb24gaW4gZGF5cy4gRGVmYXVsdDogMzY2LiAqL1xuICByZWFkb25seSBleHBpcmF0aW9uRGF5cz86IG51bWJlcjtcblxuICAvKipcbiAgICogT3ZlcnJpZGUgdGhlIGRpcmVjdG9yeSBjb250YWluaW5nIHRoZSBwcmUtYnVuZGxlZCBwdXNoIExhbWJkYSBhc3NldCAoYW5cbiAgICogYGluZGV4LmpzYCBleHBvcnRpbmcgYGhhbmRsZXJgKS4gRGVmYXVsdHMgdG8gdGhlIGhhbmRsZXIgYnVuZGxlZCBpbnNpZGUgdGhpc1xuICAgKiBwYWNrYWdlIChgbGliL3B1c2gtaGFuZGxlci1hc3NldGAsIHByb2R1Y2VkIGJ5IGBwb3N0OmNvbXBpbGVgKS5cbiAgICovXG4gIHJlYWRvbmx5IHB1c2hMYW1iZGFDb2RlUGF0aD86IHN0cmluZztcblxuICAvKipcbiAgICogT3ZlcnJpZGUgdGhlIGRpcmVjdG9yeSBjb250YWluaW5nIHRoZSBwcmUtYnVuZGxlZCBjYW1wYWlnbi1hc3NvY2lhdGlvblxuICAgKiBjdXN0b20tcmVzb3VyY2UgTGFtYmRhIGFzc2V0IChhbiBgaW5kZXguanNgIGV4cG9ydGluZyBgaGFuZGxlcmApLiBEZWZhdWx0cyB0b1xuICAgKiB0aGUgaGFuZGxlciBidW5kbGVkIGluc2lkZSB0aGlzIHBhY2thZ2UgKGBsaWIvY2FtcGFpZ24tYXNzb2NpYXRpb24tYXNzZXRgLFxuICAgKiBwcm9kdWNlZCBieSBgcG9zdDpjb21waWxlYCkuIE9ubHkgdXNlZCBpbiBjcmVhdGUtZnJvbS1zY3JhdGNoIG1vZGUuXG4gICAqL1xuICByZWFkb25seSBjYW1wYWlnbkFzc29jaWF0aW9uTGFtYmRhQ29kZVBhdGg/OiBzdHJpbmc7XG5cbiAgLyoqXG4gICAqIE9QVElPTkFMIEFQTnMgY2hhbm5lbCBjb25maWd1cmF0aW9uICh0b2tlbiAvIGAucDhgIGF1dGgpLiBXaGVuIHByb3ZpZGVkLCB0aGVcbiAgICogY29uc3RydWN0IGVuYWJsZXMgdGhlIEFQTnMgY2hhbm5lbCBvbiB0aGUgY3JlYXRlZCBFbmQgVXNlciBNZXNzYWdpbmdcbiAgICogKFBpbnBvaW50KSBhcHBsaWNhdGlvbi4gQWxsIHZhbHVlcyBhcmUgcGxhaW4gc3RyaW5ncyDigJQgdGhlIEFtcGxpZnkgR2VuMlxuICAgKiBmYWN0b3J5IHJlc29sdmVzIHRoZSBgLnA4YCBrZXkgZnJvbSBhbiBBbXBsaWZ5IGBzZWNyZXQoKWAgYmVmb3JlIHBhc3NpbmcgaXRcbiAgICogaGVyZSwga2VlcGluZyB0aGlzIGNvbnN0cnVjdCBmcmFtZXdvcmstYWdub3N0aWMuIFdoZW4gb21pdHRlZCwgbm8gQVBOc1xuICAgKiBjaGFubmVsIGlzIGNvbmZpZ3VyZWQuXG4gICAqL1xuICByZWFkb25seSBhcG5zQ2hhbm5lbD86IHtcbiAgICAvKiogVGhlIEFQTnMgdG9rZW4gc2lnbmluZyBrZXkgY29udGVudHMgKHRoZSBgQXV0aEtleV88a2V5SWQ+LnA4YCBmaWxlKS4gKi9cbiAgICByZWFkb25seSB0b2tlbktleTogc3RyaW5nO1xuICAgIC8qKiBUaGUgMTAtY2hhcmFjdGVyIGtleSBpZGVudGlmaWVyIGFzc2lnbmVkIHRvIHRoZSBBUE5zIHNpZ25pbmcga2V5LiAqL1xuICAgIHJlYWRvbmx5IGtleUlkOiBzdHJpbmc7XG4gICAgLyoqIFRoZSBBcHBsZSBEZXZlbG9wZXIgYWNjb3VudCB0ZWFtIGlkZW50aWZpZXIuICovXG4gICAgcmVhZG9ubHkgdGVhbUlkOiBzdHJpbmc7XG4gICAgLyoqIFRoZSBpT1MgYXBwIGJ1bmRsZSBpZGVudGlmaWVyLiAqL1xuICAgIHJlYWRvbmx5IGJ1bmRsZUlkOiBzdHJpbmc7XG4gICAgLyoqXG4gICAgICogQ29uZmlndXJlIHRoZSBBUE5zICoqc2FuZGJveCoqIGNoYW5uZWwgaW5zdGVhZCBvZiB0aGUgcHJvZHVjdGlvbiBjaGFubmVsLlxuICAgICAqIEBkZWZhdWx0IGZhbHNlXG4gICAgICovXG4gICAgcmVhZG9ubHkgc2FuZGJveD86IGJvb2xlYW47XG4gIH07XG5cbiAgLyoqXG4gICAqIE9QVElPTkFMIEZDTS9HQ00gY2hhbm5lbCBjb25maWd1cmF0aW9uIChGQ00gSFRUUCB2MSBhdXRoKS4gV2hlbiBwcm92aWRlZCwgdGhlXG4gICAqIGNvbnN0cnVjdCBlbmFibGVzIHRoZSBHQ00gY2hhbm5lbCBvbiB0aGUgY3JlYXRlZCBFbmQgVXNlciBNZXNzYWdpbmdcbiAgICogKFBpbnBvaW50KSBhcHBsaWNhdGlvbi4gVGhlIHZhbHVlIGlzIGEgcGxhaW4gc3RyaW5nIOKAlCB0aGUgQW1wbGlmeSBHZW4yXG4gICAqIGZhY3RvcnkgcmVzb2x2ZXMgdGhlIHNlcnZpY2UtYWNjb3VudCBKU09OIGZyb20gYW4gQW1wbGlmeSBgc2VjcmV0KClgIGJlZm9yZVxuICAgKiBwYXNzaW5nIGl0IGhlcmUuIFdoZW4gb21pdHRlZCwgbm8gR0NNIGNoYW5uZWwgaXMgY29uZmlndXJlZC5cbiAgICovXG4gIHJlYWRvbmx5IGZjbUNoYW5uZWw/OiB7XG4gICAgLyoqIFRoZSBGQ00gSFRUUCB2MSBzZXJ2aWNlLWFjY291bnQgSlNPTiBjcmVkZW50aWFsIGNvbnRlbnRzLiAqL1xuICAgIHJlYWRvbmx5IHNlcnZpY2VKc29uOiBzdHJpbmc7XG4gIH07XG59O1xuXG4vKipcbiAqIEFtYXpvbiBDb25uZWN0IEN1c3RvbWVyIFByb2ZpbGVzIGJhY2tlbmQgZm9yIHRoZSBpZGVudGlmeVVzZXIgY29udHJhY3QsXG4gKiBwYWNrYWdlZCBhcyBhbiBBV1MgQ0RLIGNvbnN0cnVjdC4gUHJvdmlzaW9uczpcbiAqICAgLSBBbXBsaWZ5UHJvZmlsZSBvYmplY3QgdHlwZTogc2VhcmNoYWJsZSBgY29nbml0b1VzZXJLZXlgIChQUk9GSUxFICsgVU5JUVVFKVxuICogICAgIGlkZW50aXR5IGtleSArIHBlcnNvbiAvIHRhcmdldGluZyBhdHRyaWJ1dGUgc2NoZW1hLlxuICogICAtIEFtcGxpZnlEZXZpY2Ugb2JqZWN0IHR5cGU6IFBST0ZJTEUtcmVzb2x1dGlvbiBrZXkgPSBgY29nbml0b1VzZXJLZXlgLFxuICogICAgIFVOSVFVRSBvYmplY3Qga2V5ID0gc3RhYmxlIGBkZXZpY2VJZGA7IGRldmljZSBmaWVsZHMgc3RvcmVkIHJhdy5cbiAqICAgLSBpZGVudGlmeS11c2VyIExhbWJkYSB3aXRoIGxlYXN0LXByaXZpbGVnZSBwcm9maWxlOiogb24gdGhpcyBkb21haW4gb25seS5cbiAqICAgLSBIVFRQIEFQSSArIEpXVCBhdXRob3JpemVyIGJvdW5kIHRvIHRoZSBzdXBwbGllZCBpc3N1ZXIgLyBhdWRpZW5jZS5cbiAqICAgLSBwdXNoLWRlbGl2ZXJ5IExhbWJkYSAoQ29ubmVjdCBKb3VybmV5IEN1c3RvbS1hY3Rpb24gdGFyZ2V0KSArIGEgbWluaW1hbFxuICogICAgIEFXUyBFbmQgVXNlciBNZXNzYWdpbmcgKFBpbnBvaW50KSBhcHBsaWNhdGlvbiBmb3IgYFNlbmRNZXNzYWdlc2AuXG4gKlxuICogSXQgb3BlcmF0ZXMgaW4gb25lIG9mIHR3byBtb2RlcywgY2hvc2VuIGJ5IHdoZXRoZXIgYGRvbWFpbk5hbWVgIGlzIHByb3ZpZGVkOlxuICpcbiAqICAgLSBDUkVBVEUtRlJPTS1TQ1JBVENIIChkZWZhdWx0LCBgZG9tYWluTmFtZWAgb21pdHRlZCk6IHRoZSBjb25zdHJ1Y3QgYWxzb1xuICogICAgIHByb3Zpc2lvbnMgYSBicmFuZC1uZXcgQW1hem9uIENvbm5lY3QgaW5zdGFuY2UgKGBBV1M6OkNvbm5lY3Q6Okluc3RhbmNlYCxcbiAqICAgICBDT05ORUNUX01BTkFHRUQsIGluYm91bmQvb3V0Ym91bmQgZW5hYmxlZCkgQU5EIGEgYnJhbmQtbmV3IEN1c3RvbWVyXG4gKiAgICAgUHJvZmlsZXMgZG9tYWluIChgQVdTOjpDdXN0b21lclByb2ZpbGVzOjpEb21haW5gKSB3aXRoIGdlbmVyYXRlZCwgc3RhYmxlXG4gKiAgICAgbmFtZXMsIHRoZW4gcmVnaXN0ZXJzIHRoZSBvYmplY3QgdHlwZXMgSU5UTyB0aGF0IG5ldyBkb21haW4uIFRoZSBvYmplY3RcbiAqICAgICB0eXBlcyBkZXBlbmQgb24gdGhlIGNyZWF0ZWQgZG9tYWluIHNvIHRoZXkgcHJvdmlzaW9uIGFmdGVyIGl0IGFuZCBhcmUgdG9yblxuICogICAgIGRvd24gYmVmb3JlIGl0LiBBIGNhbGxlciBuZWVkcyB6ZXJvIHByZS1leGlzdGluZyBDb25uZWN0IHNldHVwLlxuICogICAtIEFUVEFDSCAoYGRvbWFpbk5hbWVgIHByb3ZpZGVkKTogdGhlIGNvbnN0cnVjdCByZWdpc3RlcnMgdGhlIHR3byBvYmplY3RcbiAqICAgICB0eXBlcyBJTlRPIHRoZSBleGlzdGluZyBgZG9tYWluTmFtZWAgKGFkZGl0aXZlKSDigJQgc3VjaCBhcyB0aGUgZG9tYWluXG4gKiAgICAgQW1hem9uIENvbm5lY3QgYXV0by1jcmVhdGVzIHdoZW4gQ3VzdG9tZXIgUHJvZmlsZXMgaXMgZW5hYmxlZCBvbiBhblxuICogICAgIGluc3RhbmNlIOKAlCBXSVRIT1VUIGNyZWF0aW5nIGFuIGluc3RhbmNlIG9yIGEgZG9tYWluLCBhbmQgd2l0aG91dCB0b3VjaGluZ1xuICogICAgIHRoZSBkb21haW4ncyBvdGhlciBpbnRlZ3JhdGlvbnMgKENUUiwgT3V0Ym91bmQgQ2FtcGFpZ25zKSBvciBpdHMgSWRlbnRpdHlcbiAqICAgICBSZXNvbHV0aW9uIHNldHRpbmcuXG4gKlxuICogSW4gZWl0aGVyIG1vZGUsIGlkZW50aWZ5IHdvcmtzIGFnYWluc3QgdGhlIGRvbWFpbiBzdGFuZGFsb25lLiBPdXRib3VuZFxuICogQ2FtcGFpZ25zIGFzc29jaWF0aW9uIOKAlCBzbyBqb3VybmV5cyBjYW4gdGFyZ2V0IHRoZXNlIHByb2ZpbGVzIOKAlCBpcyBBVVRPTUFUSUNcbiAqIGluIGNyZWF0ZS1mcm9tLXNjcmF0Y2ggbW9kZSAoYSBMYW1iZGEtYmFja2VkIGN1c3RvbSByZXNvdXJjZSBhc3NvY2lhdGVzIHRoZVxuICogbmV3IGRvbWFpbiB3aXRoIHRoZSBuZXcgaW5zdGFuY2UncyBPdXRib3VuZCBDYW1wYWlnbnMgdjIgYXQgZGVwbG95IHRpbWUpLiBJblxuICogYXR0YWNoIG1vZGUsIGFzc29jaWF0aW5nIHRoZSBwcmUtZXhpc3RpbmcgZG9tYWluIGlzIHRoZSB1c2VyJ3MgcmVzcG9uc2liaWxpdHlcbiAqIGFuZCBpcyBsZWZ0IHVudG91Y2hlZC5cbiAqXG4gKiBUaGlzIGNvbnN0cnVjdCBpcyBmcmFtZXdvcmstYWdub3N0aWMgKGF3cy1jZGstbGliICsgY29uc3RydWN0cyBvbmx5KS4gVGhlXG4gKiBgZGVmaW5lTm90aWZpY2F0aW9uc2AgZmFjdG9yeSB3cmFwcyBpdCBmb3IgQW1wbGlmeSBHZW4yIGJhY2tlbmRzLCB3aXJpbmcgdGhlXG4gKiBKV1QgYXV0aG9yaXplciB0byB0aGUgYXBwJ3MgQ29nbml0byB1c2VyIHBvb2wgYW5kIHN1cmZhY2luZyB0aGUgZW5kcG9pbnQgdmlhXG4gKiBiYWNrZW5kIG91dHB1dHMuXG4gKi9cbmV4cG9ydCBjbGFzcyBBbXBsaWZ5Tm90aWZpY2F0aW9uc1xuICBleHRlbmRzIENvbnN0cnVjdFxuICBpbXBsZW1lbnRzIFJlc291cmNlUHJvdmlkZXI8Tm90aWZpY2F0aW9uc1Jlc291cmNlcz4sIFN0YWNrUHJvdmlkZXJcbntcbiAgLyoqIENESyByZXNvdXJjZXMgZm9yIGxvd2VyLWxldmVsIGFjY2Vzcy4gKi9cbiAgcHVibGljIHJlYWRvbmx5IHJlc291cmNlczogTm90aWZpY2F0aW9uc1Jlc291cmNlcztcbiAgLyoqIFRoZSBzdGFjayB0aGlzIGNvbnN0cnVjdCBiZWxvbmdzIHRvLiAqL1xuICBwdWJsaWMgcmVhZG9ubHkgc3RhY2s6IFN0YWNrO1xuICAvKiogQmFzZSBpbnZva2UgVVJMLiBDbGllbnRzIGNhbGwgYFBPU1Qge2FwaUVuZHBvaW50fS9pZGVudGlmeS11c2VyYC4gKi9cbiAgcHVibGljIHJlYWRvbmx5IGFwaUVuZHBvaW50OiBzdHJpbmc7XG4gIC8qKiBUaGUgcm91dGUgcGF0aCBhcHBlbmRlZCB0byB7QGxpbmsgYXBpRW5kcG9pbnR9LiAqL1xuICBwdWJsaWMgcmVhZG9ubHkgaWRlbnRpZnlVc2VyUGF0aCA9ICcvaWRlbnRpZnktdXNlcic7XG4gIC8qKlxuICAgKiBUaGUgR1VFU1Qgcm91dGUgcGF0aCBhcHBlbmRlZCB0byB7QGxpbmsgYXBpRW5kcG9pbnR9LlxuICAgKiBJQU0vU2lnVjQtYXV0aG9yaXplZDsgdW5hdXRoZW50aWNhdGVkIElkZW50aXR5IFBvb2wgY2FsbGVycyBzaWduIHJlcXVlc3RzIHRvXG4gICAqIGBQT1NUIHthcGlFbmRwb2ludH0vaWRlbnRpZnktdXNlci1ndWVzdGAuXG4gICAqL1xuICBwdWJsaWMgcmVhZG9ubHkgZ3Vlc3RJZGVudGlmeVVzZXJQYXRoID0gJy9pZGVudGlmeS11c2VyLWd1ZXN0JztcbiAgLyoqXG4gICAqIFRoZSBgZXhlY3V0ZS1hcGk6SW52b2tlYCBBUk4gb2YgdGhlIEdVRVNUIHJvdXRlLCB0b1xuICAgKiBncmFudCB0byB0aGUgYXBwJ3MgQ29nbml0byBJZGVudGl0eSBQb29sIFVOQVVUSEVOVElDQVRFRCByb2xlIHNvIGd1ZXN0cyBjYW5cbiAgICogY2FsbCB0aGUgcm91dGUuIFNjb3BlZCB0byBQT1NUIG9uIHRoZSBndWVzdCBwYXRoIG9mIHRoaXMgQVBJJ3MgZGVmYXVsdFxuICAgKiBzdGFnZS5cbiAgICovXG4gIHB1YmxpYyByZWFkb25seSBndWVzdFJvdXRlSW52b2tlQXJuITogc3RyaW5nO1xuICAvKiogVGhlIEN1c3RvbWVyIFByb2ZpbGVzIGRvbWFpbiBuYW1lIHRoZSBvYmplY3QgdHlwZXMgYXJlIHJlZ2lzdGVyZWQgaW50by4gKi9cbiAgcHVibGljIHJlYWRvbmx5IGRvbWFpbk5hbWU6IHN0cmluZztcbiAgLyoqXG4gICAqIGB0cnVlYCB3aGVuIHRoaXMgY29uc3RydWN0IGNyZWF0ZWQgdGhlIEN1c3RvbWVyIFByb2ZpbGVzIGRvbWFpbiArIENvbm5lY3RcbiAgICogaW5zdGFuY2UgKGNyZWF0ZS1mcm9tLXNjcmF0Y2ggbW9kZSk7IGBmYWxzZWAgd2hlbiBpdCBhdHRhY2hlZCB0byBhbiBleGlzdGluZ1xuICAgKiBkb21haW4gbmFtZWQgYnkgYGRvbWFpbk5hbWVgLlxuICAgKi9cbiAgcHVibGljIHJlYWRvbmx5IGNyZWF0ZXNSZXNvdXJjZXM6IGJvb2xlYW47XG4gIC8qKlxuICAgKiBJZCBvZiB0aGUgQW1hem9uIENvbm5lY3QgaW5zdGFuY2UgY3JlYXRlZCBpbiBjcmVhdGUtZnJvbS1zY3JhdGNoIG1vZGUuXG4gICAqIGB1bmRlZmluZWRgIGluIGF0dGFjaCBtb2RlLlxuICAgKi9cbiAgcHVibGljIHJlYWRvbmx5IGNvbm5lY3RJbnN0YW5jZUlkPzogc3RyaW5nO1xuICAvKipcbiAgICogQVJOIG9mIHRoZSBBbWF6b24gQ29ubmVjdCBpbnN0YW5jZSBjcmVhdGVkIGluIGNyZWF0ZS1mcm9tLXNjcmF0Y2ggbW9kZS5cbiAgICogYHVuZGVmaW5lZGAgaW4gYXR0YWNoIG1vZGUuXG4gICAqL1xuICBwdWJsaWMgcmVhZG9ubHkgY29ubmVjdEluc3RhbmNlQXJuPzogc3RyaW5nO1xuICAvKipcbiAgICogQVJOIG9mIHRoZSBwdXNoLWRlbGl2ZXJ5IExhbWJkYSwgdG8gd2lyZSBhcyBhIENvbm5lY3QgSm91cm5leSBDdXN0b20tYWN0aW9uXG4gICAqIChJbnZva2UgTGFtYmRhKSB0YXJnZXQuXG4gICAqL1xuICBwdWJsaWMgcmVhZG9ubHkgcHVzaEZ1bmN0aW9uQXJuOiBzdHJpbmc7XG4gIC8qKlxuICAgKiBUaGUgQVdTIEVuZCBVc2VyIE1lc3NhZ2luZyAvIFBpbnBvaW50IGFwcGxpY2F0aW9uIGlkIHRoaXMgY29uc3RydWN0IGNyZWF0ZWRcbiAgICogYW5kIHRoZSBwdXNoIExhbWJkYSBjYWxscyBgU2VuZE1lc3NhZ2VzYCBhZ2FpbnN0LlxuICAgKi9cbiAgcHVibGljIHJlYWRvbmx5IGV1bUFwcGxpY2F0aW9uSWQ6IHN0cmluZztcblxuICAvKipcbiAgICogUmVnaXN0ZXJzIHRoZSBBbXBsaWZ5UHJvZmlsZSAvIEFtcGxpZnlEZXZpY2Ugb2JqZWN0IHR5cGVzIGludG8gdGhlIEN1c3RvbWVyXG4gICAqIFByb2ZpbGVzIGRvbWFpbiAoY3JlYXRlZCBoZXJlIGluIGNyZWF0ZS1mcm9tLXNjcmF0Y2ggbW9kZSwgb3IgdGhlIGV4aXN0aW5nXG4gICAqIGBkb21haW5OYW1lYCBpbiBhdHRhY2ggbW9kZSksIHRoZSBpZGVudGlmeS11c2VyIExhbWJkYSArIEpXVC1hdXRob3JpemVkIEhUVFBcbiAgICogQVBJLCBhbmQgdGhlIHB1c2gtZGVsaXZlcnkgTGFtYmRhICsgQVdTIEVuZCBVc2VyIE1lc3NhZ2luZyBhcHBsaWNhdGlvbiBmb3JcbiAgICogdGhpcyBub3RpZmljYXRpb25zIGJhY2tlbmQuXG4gICAqL1xuICBjb25zdHJ1Y3RvcihzY29wZTogQ29uc3RydWN0LCBpZDogc3RyaW5nLCBwcm9wczogQW1wbGlmeU5vdGlmaWNhdGlvbnNQcm9wcykge1xuICAgIHN1cGVyKHNjb3BlLCBpZCk7XG5cbiAgICBjb25zdCBzdGFjayA9IFN0YWNrLm9mKHRoaXMpO1xuICAgIHRoaXMuc3RhY2sgPSBzdGFjaztcbiAgICBjb25zdCBleHBpcmF0aW9uRGF5cyA9IHByb3BzLmV4cGlyYXRpb25EYXlzID8/IERFRkFVTFRfRVhQSVJBVElPTl9EQVlTO1xuXG4gICAgLy8gLS0tLSBNb2RlIHNlbGVjdGlvbjogY3JlYXRlLWZyb20tc2NyYXRjaCAoZGVmYXVsdCkgdnMgYXR0YWNoIC0tLS0tLS0tLS0tXG4gICAgLy8gSWYgYSBgZG9tYWluTmFtZWAgaXMgc3VwcGxpZWQsIEFUVEFDSCB0aGUgb2JqZWN0IHR5cGVzIHRvIHRoYXQgZXhpc3RpbmdcbiAgICAvLyBkb21haW4gKGNyZWF0ZSBub3RoaW5nKS4gT3RoZXJ3aXNlICh0aGUgemVyby1jb25maWcgZGVmYXVsdCkgQ1JFQVRFIGFcbiAgICAvLyBicmFuZC1uZXcgQ29ubmVjdCBpbnN0YW5jZSArIEN1c3RvbWVyIFByb2ZpbGVzIGRvbWFpbiB3aXRoIGdlbmVyYXRlZCxcbiAgICAvLyBzdGFibGUgbmFtZXMgYW5kIHJlZ2lzdGVyIHRoZSBvYmplY3QgdHlwZXMgaW50byB0aGUgbmV3IGRvbWFpbi5cbiAgICBjb25zdCBjcmVhdGVGcm9tU2NyYXRjaCA9ICFwcm9wcy5kb21haW5OYW1lO1xuICAgIHRoaXMuY3JlYXRlc1Jlc291cmNlcyA9IGNyZWF0ZUZyb21TY3JhdGNoO1xuXG4gICAgbGV0IGRvbWFpbk5hbWU6IHN0cmluZztcbiAgICBsZXQgcHJvZmlsZXNEb21haW46IENmbkRvbWFpbiB8IHVuZGVmaW5lZDtcbiAgICBsZXQgY29ubmVjdEluc3RhbmNlOiBDZm5JbnN0YW5jZSB8IHVuZGVmaW5lZDtcblxuICAgIGlmIChjcmVhdGVGcm9tU2NyYXRjaCkge1xuICAgICAgLy8gRGV0ZXJtaW5pc3RpYy15ZXQtdW5pcXVlIGJhc2UgbmFtZSBkZXJpdmVkIGZyb20gdGhlIGNvbnN0cnVjdCdzIHNjb3BlIHNvXG4gICAgICAvLyBpdCBpcyBTVEFCTEUgYWNyb3NzIGRlcGxveS9kZWxldGUgKHNhbWUgdHJlZSDihpIgc2FtZSBuYW1lKSBhbmQgdW5pcXVlIHBlclxuICAgICAgLy8gYXBwIC8gZW52aXJvbm1lbnQgKHRoZSByb290IHN0YWNrIG5hbWUgZW1iZWRzIHRoZSBBbXBsaWZ5IGFwcCArIGJyYW5jaCkuXG4gICAgICBjb25zdCBiYXNlTmFtZSA9IHRoaXMuZ2VuZXJhdGVSZXNvdXJjZU5hbWUoKTtcbiAgICAgIGRvbWFpbk5hbWUgPSBiYXNlTmFtZTtcblxuICAgICAgY29ubmVjdEluc3RhbmNlID0gbmV3IENmbkluc3RhbmNlKHRoaXMsICdDb25uZWN0SW5zdGFuY2UnLCB7XG4gICAgICAgIC8vIE1pbmltYWwgdGVsZXBob255IHN1cmZhY2U6IENvbm5lY3QgcmVxdWlyZXMgYXQgbGVhc3Qgb25lIG9mXG4gICAgICAgIC8vIGluYm91bmQvb3V0Ym91bmQ7IGVuYWJsZSBib3RoIHNvIHRoZSBpbnN0YW5jZSBpcyB1c2FibGUgZm9yIG91dGJvdW5kXG4gICAgICAgIC8vIGpvdXJuZXlzIGxhdGVyLiBEaXJlY3RvcnkgaXMgQ29ubmVjdC1tYW5hZ2VkIChubyBleHRlcm5hbCBpZGVudGl0eSkuXG4gICAgICAgIC8vXG4gICAgICAgIC8vIGhpZ2hWb2x1bWVPdXRCb3VuZCBlbmFibGVzIHRoZSBpbnN0YW5jZSdzIE91dGJvdW5kIENhbXBhaWducyBmZWF0dXJlLlxuICAgICAgICAvLyBUaGlzIGlzIHdoYXQgbWFrZXMgQW1hem9uIENvbm5lY3QgYXR0YWNoIHRoZVxuICAgICAgICAvLyBgSGlnaFZvbHVtZU91dGJvdW5kQ29tbXVuaWNhdGlvbkFjY2Vzc2AgaW5saW5lIHBvbGljeSB0byB0aGUgaW5zdGFuY2Unc1xuICAgICAgICAvLyBPV04gc2VydmljZS1saW5rZWQgcm9sZSDigJQgdGhlIHBvbGljeSBncmFudGluZyB0aGUgYGNvbm5lY3QtY2FtcGFpZ25zOipgXG4gICAgICAgIC8vIChDcmVhdGVDYW1wYWlnbiAvIExpc3RDYW1wYWlnbnMgLyAuLi4pIGFjdGlvbnMgdGhlIENvbm5lY3QgY29uc29sZVxuICAgICAgICAvLyBkcml2ZXMgSm91cm5leSArIGNhbXBhaWduIGF1dGhvcmluZyB0aHJvdWdoLiBPbmJvYXJkaW5nIHRvIE91dGJvdW5kXG4gICAgICAgIC8vIENhbXBhaWducyB2MiAodGhlIGNhbXBhaWduLWFzc29jaWF0aW9uIGN1c3RvbSByZXNvdXJjZSkgZG9lcyBOT1Qgc2V0XG4gICAgICAgIC8vIGl0LCBzbyB3aXRob3V0IHRoaXMgdGhlIGNvbnNvbGUncyBKb3VybmV5IC8gc2VnbWVudCBidWlsZGVyIGZhaWxzIHdpdGhcbiAgICAgICAgLy8gY29ubmVjdC1jYW1wYWlnbnMgQWNjZXNzRGVuaWVkIG9uIHRoZSBpbnN0YW5jZSBTTFIuXG4gICAgICAgIGF0dHJpYnV0ZXM6IHtcbiAgICAgICAgICBpbmJvdW5kQ2FsbHM6IHRydWUsXG4gICAgICAgICAgb3V0Ym91bmRDYWxsczogdHJ1ZSxcbiAgICAgICAgICBoaWdoVm9sdW1lT3V0Qm91bmQ6IHRydWUsXG4gICAgICAgIH0sXG4gICAgICAgIGlkZW50aXR5TWFuYWdlbWVudFR5cGU6ICdDT05ORUNUX01BTkFHRUQnLFxuICAgICAgICBpbnN0YW5jZUFsaWFzOiB0aGlzLnNhbml0aXplSW5zdGFuY2VBbGlhcyhcbiAgICAgICAgICBwcm9wcy5pbnN0YW5jZUFsaWFzID8/IGJhc2VOYW1lLFxuICAgICAgICApLFxuICAgICAgfSk7XG5cbiAgICAgIHByb2ZpbGVzRG9tYWluID0gbmV3IENmbkRvbWFpbih0aGlzLCAnUHJvZmlsZXNEb21haW4nLCB7XG4gICAgICAgIGRvbWFpbk5hbWUsXG4gICAgICAgIGRlZmF1bHRFeHBpcmF0aW9uRGF5czogZXhwaXJhdGlvbkRheXMsXG4gICAgICB9KTtcblxuICAgICAgdGhpcy5jb25uZWN0SW5zdGFuY2VJZCA9IGNvbm5lY3RJbnN0YW5jZS5hdHRySWQ7XG4gICAgICB0aGlzLmNvbm5lY3RJbnN0YW5jZUFybiA9IGNvbm5lY3RJbnN0YW5jZS5hdHRyQXJuO1xuXG4gICAgICAvLyBJbnN0YW5jZS1sZXZlbCBDdXN0b21lciBQcm9maWxlcyBmZWF0dXJlIGJpbmRpbmc6IHJlZ2lzdGVycyB0aGUgY3JlYXRlZFxuICAgICAgLy8gZG9tYWluIGFzIHRoZSBpbnN0YW5jZSdzIENUUiAoY29udGFjdC1yZWNvcmQpIHByb2ZpbGVzIHN0b3JlIOKAlCB0aGVcbiAgICAgIC8vIFwiQ3VzdG9tZXIgUHJvZmlsZXMgZW5hYmxlZFwiIGludGVncmF0aW9uIHRoZSBDb25uZWN0IGNvbnNvbGUgYW5kIEpvdXJuZXlcbiAgICAgIC8vIHNlZ21lbnQgYnVpbGRlciByZXF1aXJlLCBkaXN0aW5jdCBmcm9tIHRoZSBPdXRib3VuZCBDYW1wYWlnbnNcbiAgICAgIC8vIGludGVncmF0aW9uIHRoZSBhc3NvY2lhdGlvbiBjdXN0b20gcmVzb3VyY2Ugd2lyZXMgYmVsb3cuIFB1dEludGVncmF0aW9uXG4gICAgICAvLyBhdXRvLXJlc29sdmVzIHRoZSBDb25uZWN0IHNlcnZpY2UtbGlua2VkIHJvbGUgZm9yIGEgY29ubmVjdC1pbnN0YW5jZVxuICAgICAgLy8gVVJJOyB0aGF0IFNMUiBjYW4gb25seSByZWFjaCB0aGUgZG9tYWluIGJlY2F1c2UgaXQgaXMgbmFtZWRcbiAgICAgIC8vIGBhbWF6b24tY29ubmVjdC0qYCAoc2VlIGdlbmVyYXRlUmVzb3VyY2VOYW1lKS5cbiAgICAgIGNvbnN0IGN0ckludGVncmF0aW9uID0gbmV3IENmbkludGVncmF0aW9uKFxuICAgICAgICB0aGlzLFxuICAgICAgICAnQ29ubmVjdFByb2ZpbGVzSW50ZWdyYXRpb24nLFxuICAgICAgICB7XG4gICAgICAgICAgZG9tYWluTmFtZSxcbiAgICAgICAgICB1cmk6IGNvbm5lY3RJbnN0YW5jZS5hdHRyQXJuLFxuICAgICAgICAgIG9iamVjdFR5cGVOYW1lOiAnQ1RSJyxcbiAgICAgICAgfSxcbiAgICAgICk7XG4gICAgICBjdHJJbnRlZ3JhdGlvbi5hZGREZXBlbmRlbmN5KGNvbm5lY3RJbnN0YW5jZSk7XG4gICAgICBjdHJJbnRlZ3JhdGlvbi5hZGREZXBlbmRlbmN5KHByb2ZpbGVzRG9tYWluKTtcblxuICAgICAgLy8gTWVzc2FnZS10ZW1wbGF0ZXMga25vd2xlZGdlIGJhc2UgKyBpdHMgaW5zdGFuY2UgYXNzb2NpYXRpb24uIEJvdGggdGhlXG4gICAgICAvLyBDb25uZWN0IGNvbnNvbGUncyBtZXNzYWdlLXRlbXBsYXRlIGF1dGhvcmluZyBVSSBhbmQgdGhlIHB1c2gtZGVsaXZlcnlcbiAgICAgIC8vIExhbWJkYSdzIHJ1bnRpbWUgZGlzY292ZXJ5IChMaXN0SW50ZWdyYXRpb25Bc3NvY2lhdGlvbnMgZmlsdGVyZWQgdG9cbiAgICAgIC8vIFFfTUVTU0FHRV9URU1QTEFURVMg4oaSIHRoZSBLQiBpdCByZW5kZXJzIHRlbXBsYXRlcyBmcm9tKSByZXF1aXJlIGFcbiAgICAgIC8vIE1FU1NBR0VfVEVNUExBVEVTIGtub3dsZWRnZSBiYXNlIGxpbmtlZCB0byB0aGUgaW5zdGFuY2UuIFdlIHByb3Zpc2lvblxuICAgICAgLy8gdGhlIGVtcHR5IEtCICsgdGhlIFFfTUVTU0FHRV9URU1QTEFURVMgYXNzb2NpYXRpb24gb25seTsgdGhlIHRlbXBsYXRlc1xuICAgICAgLy8gdGhlbXNlbHZlcyBhcmUgdGhlIG1hcmtldGVyJ3Mgam9iIChhIHRlbXBsYXRlJ3MgbmFtZSBtdXN0IG1hdGNoIHRoZVxuICAgICAgLy8gam91cm5leSBDdXN0b20tYWN0aW9uIGlkLCBlLmcuIFwiUHVzaCBOb3RpZmljYXRpb25cIikuIEEgTUVTU0FHRV9URU1QTEFURVNcbiAgICAgIC8vIEtCIG5lZWRzIG5vIFEgYXNzaXN0YW50LiBUaGUgbmFtZSBpcyBkZXJpdmVkIGZyb20gdGhlIHNhbWUgZGV0ZXJtaW5pc3RpY1xuICAgICAgLy8gYmFzZSBhcyB0aGUgaW5zdGFuY2UvZG9tYWluIHNvIHJlZGVwbG95cyBzdGF5IHN0YWJsZSBhbmQgYXZvaWQgdGhlXG4gICAgICAvLyBDcmVhdGVLbm93bGVkZ2VCYXNlIENvbmZsaWN0RXhjZXB0aW9uIG9uIGR1cGxpY2F0ZSBuYW1lcy5cbiAgICAgIGNvbnN0IHRlbXBsYXRlc0tiID0gbmV3IENmbktub3dsZWRnZUJhc2UodGhpcywgJ01lc3NhZ2VUZW1wbGF0ZXNLYicsIHtcbiAgICAgICAgbmFtZTogYCR7YmFzZU5hbWV9LW1lc3NhZ2UtdGVtcGxhdGVzYCxcbiAgICAgICAga25vd2xlZGdlQmFzZVR5cGU6ICdNRVNTQUdFX1RFTVBMQVRFUycsXG4gICAgICB9KTtcblxuICAgICAgY29uc3QgdGVtcGxhdGVzQXNzb2NpYXRpb24gPSBuZXcgQ2ZuSW50ZWdyYXRpb25Bc3NvY2lhdGlvbihcbiAgICAgICAgdGhpcyxcbiAgICAgICAgJ01lc3NhZ2VUZW1wbGF0ZXNBc3NvY2lhdGlvbicsXG4gICAgICAgIHtcbiAgICAgICAgICBpbnN0YW5jZUlkOiBjb25uZWN0SW5zdGFuY2UuYXR0ckFybixcbiAgICAgICAgICBpbnRlZ3JhdGlvblR5cGU6ICdRX01FU1NBR0VfVEVNUExBVEVTJyxcbiAgICAgICAgICBpbnRlZ3JhdGlvbkFybjogdGVtcGxhdGVzS2IuYXR0cktub3dsZWRnZUJhc2VBcm4sXG4gICAgICAgIH0sXG4gICAgICApO1xuICAgICAgdGVtcGxhdGVzQXNzb2NpYXRpb24uYWRkRGVwZW5kZW5jeShjb25uZWN0SW5zdGFuY2UpO1xuICAgICAgdGVtcGxhdGVzQXNzb2NpYXRpb24uYWRkRGVwZW5kZW5jeSh0ZW1wbGF0ZXNLYik7XG4gICAgfSBlbHNlIHtcbiAgICAgIGRvbWFpbk5hbWUgPSBwcm9wcy5kb21haW5OYW1lIGFzIHN0cmluZztcbiAgICB9XG4gICAgdGhpcy5kb21haW5OYW1lID0gZG9tYWluTmFtZTtcblxuICAgIC8vIC0tLS0gT2JqZWN0IHR5cGVzIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuICAgIC8vIFJlZ2lzdGVyZWQgSU5UTyBgZG9tYWluTmFtZWAuIEluIGF0dGFjaCBtb2RlIHRoaXMgaXMgcHVyZWx5IGFkZGl0aXZlIHRvIGFcbiAgICAvLyBwcmUtZXhpc3RpbmcgKGUuZy4gQ29ubmVjdC1tYW5hZ2VkKSBkb21haW4uIEluIGNyZWF0ZSBtb2RlIHRoZSBvYmplY3RcbiAgICAvLyB0eXBlcyBkZXBlbmQgb24gdGhlIGRvbWFpbiBjcmVhdGVkIGFib3ZlLCBzbyBDRk4gcHJvdmlzaW9ucyB0aGUgZG9tYWluXG4gICAgLy8gZmlyc3QgYW5kIOKAlCBvbiB0ZWFyZG93biDigJQgcmVtb3ZlcyB0aGUgb2JqZWN0IHR5cGVzIGJlZm9yZSB0aGUgZG9tYWluIChhXG4gICAgLy8gY2xlYW4sIGluLWNvbnN0cnVjdCBkZXBlbmRlbmN5OyBubyBjcm9zcy1zdGFjayBvcmRlcmluZyBoYWNrIG5lZWRlZCkuXG4gICAgY29uc3QgcHJvZmlsZVR5cGUgPSBuZXcgQ2ZuT2JqZWN0VHlwZSh0aGlzLCAnQW1wbGlmeVByb2ZpbGVUeXBlJywge1xuICAgICAgZG9tYWluTmFtZSxcbiAgICAgIG9iamVjdFR5cGVOYW1lOiBPQkpFQ1RfVFlQRV9OQU1FUy5wcm9maWxlLFxuICAgICAgZGVzY3JpcHRpb246XG4gICAgICAgICdBbXBsaWZ5IGlkZW50aWZ5VXNlciBwZXJzb24gcHJvZmlsZSAoZmluZC1vci1jcmVhdGUgYnkgdmVyaWZpZWQgQ29nbml0byBzdWIpJyxcbiAgICAgIGFsbG93UHJvZmlsZUNyZWF0aW9uOiB0cnVlLFxuICAgICAgZmllbGRzOiBBTVBMSUZZX1BST0ZJTEVfRklFTERTLFxuICAgICAga2V5czogQU1QTElGWV9QUk9GSUxFX0tFWVMsXG4gICAgICBleHBpcmF0aW9uRGF5cyxcbiAgICB9KTtcblxuICAgIGNvbnN0IGRldmljZVR5cGUgPSBuZXcgQ2ZuT2JqZWN0VHlwZSh0aGlzLCAnQW1wbGlmeURldmljZVR5cGUnLCB7XG4gICAgICBkb21haW5OYW1lLFxuICAgICAgb2JqZWN0VHlwZU5hbWU6IE9CSkVDVF9UWVBFX05BTUVTLmRldmljZSxcbiAgICAgIGRlc2NyaXB0aW9uOlxuICAgICAgICAnQW1wbGlmeSBpZGVudGlmeVVzZXIgZGV2aWNlICh1bmlxdWUgcGVyIHN0YWJsZSBkZXZpY2VJZDsgdG9rZW4gaXMgYSBtdXRhYmxlIGZpZWxkOyByZXNvbHZlcyB0byBwcm9maWxlIGJ5IGNvZ25pdG9Vc2VyS2V5KScsXG4gICAgICBhbGxvd1Byb2ZpbGVDcmVhdGlvbjogdHJ1ZSxcbiAgICAgIGZpZWxkczogQU1QTElGWV9ERVZJQ0VfRklFTERTLFxuICAgICAga2V5czogQU1QTElGWV9ERVZJQ0VfS0VZUyxcbiAgICAgIGV4cGlyYXRpb25EYXlzLFxuICAgIH0pO1xuXG4gICAgLy8gQSBkaXN0aW5jdCBvYmplY3QgdHlwZSBmb3IgR1VFU1QgcHJvZmlsZXMuIEN1c3RvbWVyXG4gICAgLy8gUHJvZmlsZXMgcGVybWl0cyBleGFjdGx5IG9uZSBVTklRVUUga2V5IHBlciBvYmplY3QgdHlwZSBhbmQgUHV0UHJvZmlsZU9iamVjdFxuICAgIC8vIHJlcXVpcmVzIHRoZSBpbmdlc3RlZCBvYmplY3QgdG8gY2FycnkgaXQ7IHRoZSBhdXRoZWQgQW1wbGlmeVByb2ZpbGUgcmVzZXJ2ZXNcbiAgICAvLyBpdHMgVU5JUVVFIGtleSBmb3IgY29nbml0b1N1Yiwgc28gZ3Vlc3QgcHJvZmlsZXMgKGtleWVkIG9uIHRoZSBJZGVudGl0eSBQb29sXG4gICAgLy8gY29nbml0b0lkZW50aXR5SWQpIGdldCB0aGVpciBvd24gdHlwZSB3aXRoIGNvZ25pdG9JZGVudGl0eUtleSBhcyBVTklRVUUuXG4gICAgY29uc3QgZ3Vlc3RQcm9maWxlVHlwZSA9IG5ldyBDZm5PYmplY3RUeXBlKFxuICAgICAgdGhpcyxcbiAgICAgICdBbXBsaWZ5R3Vlc3RQcm9maWxlVHlwZScsXG4gICAgICB7XG4gICAgICAgIGRvbWFpbk5hbWUsXG4gICAgICAgIG9iamVjdFR5cGVOYW1lOiBPQkpFQ1RfVFlQRV9OQU1FUy5ndWVzdFByb2ZpbGUsXG4gICAgICAgIGRlc2NyaXB0aW9uOlxuICAgICAgICAgICdBbXBsaWZ5IGlkZW50aWZ5VXNlciBHVUVTVCBwZXJzb24gcHJvZmlsZSAoZmluZC1vci1jcmVhdGUgYnkgdmVyaWZpZWQgQ29nbml0byBJZGVudGl0eSBQb29sIGlkZW50aXR5SWQpJyxcbiAgICAgICAgYWxsb3dQcm9maWxlQ3JlYXRpb246IHRydWUsXG4gICAgICAgIGZpZWxkczogQU1QTElGWV9HVUVTVF9QUk9GSUxFX0ZJRUxEUyxcbiAgICAgICAga2V5czogQU1QTElGWV9HVUVTVF9QUk9GSUxFX0tFWVMsXG4gICAgICAgIGV4cGlyYXRpb25EYXlzLFxuICAgICAgfSxcbiAgICApO1xuXG4gICAgaWYgKHByb2ZpbGVzRG9tYWluKSB7XG4gICAgICBwcm9maWxlVHlwZS5hZGREZXBlbmRlbmN5KHByb2ZpbGVzRG9tYWluKTtcbiAgICAgIGRldmljZVR5cGUuYWRkRGVwZW5kZW5jeShwcm9maWxlc0RvbWFpbik7XG4gICAgICBndWVzdFByb2ZpbGVUeXBlLmFkZERlcGVuZGVuY3kocHJvZmlsZXNEb21haW4pO1xuICAgIH1cblxuICAgIC8vIC0tLS0gT3V0Ym91bmQgQ2FtcGFpZ25zIGFzc29jaWF0aW9uIChjcmVhdGUtZnJvbS1zY3JhdGNoIE9OTFkpIC0tLS0tLS0tLS1cbiAgICAvLyBXaGVuIHRoaXMgY29uc3RydWN0IGNyZWF0ZWQgdGhlIGluc3RhbmNlICsgZG9tYWluLCBhIExhbWJkYS1iYWNrZWQgY3VzdG9tXG4gICAgLy8gcmVzb3VyY2UgYXNzb2NpYXRlcyB0aGUgbmV3IGRvbWFpbiB3aXRoIHRoZSBpbnN0YW5jZSdzIE91dGJvdW5kIENhbXBhaWduc1xuICAgIC8vIHYyIGF0IGRlcGxveSB0aW1lIOKAlCBzbyBDb25uZWN0IEpvdXJuZXlzIGNhbiB0YXJnZXQgdGhlIGRvbWFpbidzIHByb2ZpbGVzXG4gICAgLy8gd2l0aCBOTyBtYW51YWwgY29uc29sZSBzdGVwLiBJbiBhdHRhY2ggbW9kZSB0aGUgZXhpc3RpbmcgZG9tYWluJ3NcbiAgICAvLyBhc3NvY2lhdGlvbiBpcyB0aGUgdXNlcidzIHJlc3BvbnNpYmlsaXR5LCBzbyB0aGlzIHJlc291cmNlIGlzIE5PVCBhZGRlZC5cbiAgICBpZiAoY3JlYXRlRnJvbVNjcmF0Y2ggJiYgY29ubmVjdEluc3RhbmNlICYmIHByb2ZpbGVzRG9tYWluKSB7XG4gICAgICB0aGlzLmFkZENhbXBhaWduQXNzb2NpYXRpb24oXG4gICAgICAgIGNvbm5lY3RJbnN0YW5jZSxcbiAgICAgICAgcHJvZmlsZXNEb21haW4sXG4gICAgICAgIGRvbWFpbk5hbWUsXG4gICAgICAgIHByb3BzLmNhbXBhaWduQXNzb2NpYXRpb25MYW1iZGFDb2RlUGF0aCA/P1xuICAgICAgICAgIGRlZmF1bHRDYW1wYWlnbkFzc29jaWF0aW9uTGFtYmRhQ29kZVBhdGgsXG4gICAgICApO1xuICAgIH1cblxuICAgIC8vIC0tLS0gTGFtYmRhIGhhbmRsZXIgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuICAgIC8vIFByZS1idW5kbGVkIGF0IGJ1aWxkIHRpbWUgKGVzYnVpbGQpIGludG8gYSBzZWxmLWNvbnRhaW5lZCBhc3NldCwgc28gdGhlXG4gICAgLy8gY29uc3RydWN0IHdvcmtzIHJlZ2FyZGxlc3Mgb2YgdGhlIGNvbnN1bWluZyBwcm9qZWN0J3Mgcm9vdCAoQW1wbGlmeSdzXG4gICAgLy8gTm9kZWpzRnVuY3Rpb24gZ3VhcmRyYWlsIGZvcmJpZHMgYW4gZW50cnkgb3V0c2lkZSB0aGUgYXBwIHJvb3QpLlxuICAgIGNvbnN0IGNvZGVQYXRoID0gcHJvcHMubGFtYmRhQ29kZVBhdGggPz8gZGVmYXVsdExhbWJkYUNvZGVQYXRoO1xuXG4gICAgY29uc3QgZm4gPSBuZXcgbGFtYmRhLkZ1bmN0aW9uKHRoaXMsICdJZGVudGlmeVVzZXJGbicsIHtcbiAgICAgIGNvZGU6IGxhbWJkYS5Db2RlLmZyb21Bc3NldChjb2RlUGF0aCksXG4gICAgICBoYW5kbGVyOiAnaW5kZXguaGFuZGxlcicsXG4gICAgICBydW50aW1lOiBsYW1iZGEuUnVudGltZS5OT0RFSlNfMjBfWCxcbiAgICAgIHRpbWVvdXQ6IER1cmF0aW9uLnNlY29uZHMoMTUpLFxuICAgICAgbWVtb3J5U2l6ZTogMjU2LFxuICAgICAgZW52aXJvbm1lbnQ6IHtcbiAgICAgICAgW0VOVl9ET01BSU5fTkFNRV06IGRvbWFpbk5hbWUsXG4gICAgICB9LFxuICAgIH0pO1xuXG4gICAgLy8gLS0tLSBMZWFzdC1wcml2aWxlZ2UgSUFNIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cbiAgICBjb25zdCBkb21haW5Bcm4gPSBBcm4uZm9ybWF0KFxuICAgICAge1xuICAgICAgICBzZXJ2aWNlOiAncHJvZmlsZScsXG4gICAgICAgIHJlc291cmNlOiAnZG9tYWlucycsXG4gICAgICAgIHJlc291cmNlTmFtZTogZG9tYWluTmFtZSxcbiAgICAgICAgYXJuRm9ybWF0OiBBcm5Gb3JtYXQuU0xBU0hfUkVTT1VSQ0VfTkFNRSxcbiAgICAgIH0sXG4gICAgICBzdGFjayxcbiAgICApO1xuICAgIGNvbnN0IG9iamVjdFR5cGVzQXJuID0gQXJuLmZvcm1hdChcbiAgICAgIHtcbiAgICAgICAgc2VydmljZTogJ3Byb2ZpbGUnLFxuICAgICAgICByZXNvdXJjZTogJ2RvbWFpbnMnLFxuICAgICAgICByZXNvdXJjZU5hbWU6IGAke2RvbWFpbk5hbWV9L29iamVjdC10eXBlcy8qYCxcbiAgICAgICAgYXJuRm9ybWF0OiBBcm5Gb3JtYXQuU0xBU0hfUkVTT1VSQ0VfTkFNRSxcbiAgICAgIH0sXG4gICAgICBzdGFjayxcbiAgICApO1xuICAgIGZuLmFkZFRvUm9sZVBvbGljeShcbiAgICAgIG5ldyBpYW0uUG9saWN5U3RhdGVtZW50KHtcbiAgICAgICAgZWZmZWN0OiBpYW0uRWZmZWN0LkFMTE9XLFxuICAgICAgICBhY3Rpb25zOiBbXG4gICAgICAgICAgLy8gUHV0UHJvZmlsZU9iamVjdCBmaW5kLW9yLWNyZWF0ZSAoQW1wbGlmeVByb2ZpbGUpICsgcmVhZC1iYWNrLlxuICAgICAgICAgICdwcm9maWxlOlB1dFByb2ZpbGVPYmplY3QnLFxuICAgICAgICAgICdwcm9maWxlOlNlYXJjaFByb2ZpbGVzJyxcbiAgICAgICAgICAvLyBSZWFkIGJhY2sgdGhlIGV4aXN0aW5nIGRldmljZSBvYmplY3QgdG8gcHJlc2VydmUgaW1tdXRhYmxlIGNyZWF0ZWRBdC5cbiAgICAgICAgICAncHJvZmlsZTpMaXN0UHJvZmlsZU9iamVjdHMnLFxuICAgICAgICAgIC8vIFNldCB0YXJnZXRpbmcgLyBwZXJzb24gYXR0cmlidXRlcyBvbiB0aGUgcmVzb2x2ZWQgcHJvZmlsZS5cbiAgICAgICAgICAncHJvZmlsZTpVcGRhdGVQcm9maWxlJyxcbiAgICAgICAgXSxcbiAgICAgICAgcmVzb3VyY2VzOiBbZG9tYWluQXJuLCBvYmplY3RUeXBlc0Fybl0sXG4gICAgICB9KSxcbiAgICApO1xuXG4gICAgLy8gTWVyZ2Utb24tc2lnbi1pbiBmb2xkcyBhIHByaW9yIGd1ZXN0IHByb2ZpbGVcbiAgICAvLyAoKyBpdHMgZGV2aWNlIG9iamVjdHMpIGludG8gdGhlIGF1dGhlZCBwcm9maWxlIHZpYSBNZXJnZVByb2ZpbGVzLlxuICAgIC8vIE5PVEU6IHRoZSBTZXJ2aWNlIEF1dGhvcml6YXRpb24gUmVmZXJlbmNlIGxpc3RzIE1lcmdlUHJvZmlsZXMgdW5kZXIgdGhlXG4gICAgLy8gYGRvbWFpbnNgIHJlc291cmNlIHR5cGUsIGJ1dCB0aGUgc2VydmljZSBBQ1RVQUxMWSBhdXRob3JpemVzIGl0IGFnYWluc3RcbiAgICAvLyBhbiB1bmRvY3VtZW50ZWQsIGRpZmZlcmVudGx5LXNoYXBlZCByZXNvdXJjZSBBUk4gKG5vdGUgdGhlIGxlYWRpbmcgc2xhc2hcbiAgICAvLyBhbmQgdGhlIC9wcm9maWxlcy9vYmplY3RzL21lcmdlIHN1ZmZpeCk6XG4gICAgLy8gICBhcm46PHBhcnRpdGlvbj46cHJvZmlsZTo8cmVnaW9uPjo8YWNjb3VudD46L2RvbWFpbnMvPGRvbWFpbj4vcHJvZmlsZXMvb2JqZWN0cy9tZXJnZVxuICAgIC8vIEdyYW50aW5nIHRoZSBkb2N1bWVudGVkIGRvbWFpbiBBUk4gaXMgTk9UIHN1ZmZpY2llbnQuIFRoaXMgZGVkaWNhdGVkXG4gICAgLy8gc3RhdGVtZW50IHRhcmdldHMgdGhlIGVuZm9yY2VkIHJlc291cmNlLlxuICAgIGNvbnN0IG1lcmdlUHJvZmlsZXNBcm4gPSBgYXJuOiR7c3RhY2sucGFydGl0aW9ufTpwcm9maWxlOiR7c3RhY2sucmVnaW9ufToke3N0YWNrLmFjY291bnR9Oi9kb21haW5zLyR7ZG9tYWluTmFtZX0vcHJvZmlsZXMvb2JqZWN0cy9tZXJnZWA7XG4gICAgZm4uYWRkVG9Sb2xlUG9saWN5KFxuICAgICAgbmV3IGlhbS5Qb2xpY3lTdGF0ZW1lbnQoe1xuICAgICAgICBlZmZlY3Q6IGlhbS5FZmZlY3QuQUxMT1csXG4gICAgICAgIGFjdGlvbnM6IFsncHJvZmlsZTpNZXJnZVByb2ZpbGVzJ10sXG4gICAgICAgIHJlc291cmNlczogW21lcmdlUHJvZmlsZXNBcm5dLFxuICAgICAgfSksXG4gICAgKTtcblxuICAgIC8vIC0tLS0gSFRUUCBBUEkgKyBKV1QgYXV0aG9yaXplciAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG4gICAgY29uc3QgYXV0aG9yaXplciA9IG5ldyBIdHRwSnd0QXV0aG9yaXplcignSnd0QXV0aG9yaXplcicsIHByb3BzLmp3dElzc3Vlciwge1xuICAgICAgand0QXVkaWVuY2U6IHByb3BzLmp3dEF1ZGllbmNlLFxuICAgICAgaWRlbnRpdHlTb3VyY2U6IFsnJHJlcXVlc3QuaGVhZGVyLkF1dGhvcml6YXRpb24nXSxcbiAgICB9KTtcblxuICAgIGNvbnN0IGh0dHBBcGkgPSBuZXcgYXBpZ3d2Mi5IdHRwQXBpKHRoaXMsICdJZGVudGlmeVVzZXJBcGknLCB7XG4gICAgICBhcGlOYW1lOiAnaWRlbnRpZnktdXNlci1hcGknLFxuICAgICAgY3JlYXRlRGVmYXVsdFN0YWdlOiB0cnVlLFxuICAgIH0pO1xuXG4gICAgaHR0cEFwaS5hZGRSb3V0ZXMoe1xuICAgICAgcGF0aDogdGhpcy5pZGVudGlmeVVzZXJQYXRoLFxuICAgICAgbWV0aG9kczogW2FwaWd3djIuSHR0cE1ldGhvZC5QT1NUXSxcbiAgICAgIGludGVncmF0aW9uOiBuZXcgSHR0cExhbWJkYUludGVncmF0aW9uKCdJZGVudGlmeVVzZXJJbnRlZ3JhdGlvbicsIGZuKSxcbiAgICAgIGF1dGhvcml6ZXIsXG4gICAgfSk7XG5cbiAgICAvLyAtLS0tIEd1ZXN0ICh1bmF1dGhlbnRpY2F0ZWQpIHJvdXRlIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuICAgIC8vIEEgc2Vjb25kIHJvdXRlIHRvIHRoZSBTQU1FIGlkZW50aWZ5IExhbWJkYSwgYnV0XG4gICAgLy8gYXV0aG9yaXplZCB3aXRoIElBTS9TaWdWNCBpbnN0ZWFkIG9mIGEgSldUIOKAlCBzbyBhbiBVTkFVVEhFTlRJQ0FURUQgQ29nbml0b1xuICAgIC8vIElkZW50aXR5IFBvb2wgY2FsbGVyIChndWVzdCBjcmVkZW50aWFscykgY2FuIHJlZ2lzdGVyIGEgZGV2aWNlIC8gc2VlZCBhXG4gICAgLy8gcHJvZmlsZSBCRUZPUkUgbG9naW4gKHRoZSBwcmUtbG9naW4gcHVzaC10b2tlbiBjYXNlKS5cbiAgICAvL1xuICAgIC8vIFRoZSBpbnRlZ3JhdGlvbiBNVVNUIHVzZSBwYXlsb2FkIGZvcm1hdCAxLjA6IEhUVFAgQVBJIHBheWxvYWQgZm9ybWF0IDIuMFxuICAgIC8vIGhhcyBOTyBgcmVxdWVzdENvbnRleHQuaWRlbnRpdHlgIGJsb2NrLCBzbyB0aGUgdmVyaWZpZWQgQ29nbml0b1xuICAgIC8vIGBjb2duaXRvSWRlbnRpdHlJZGAgKCsgYGNvZ25pdG9BdXRoZW50aWNhdGlvblR5cGVgKSB0aGUgTGFtYmRhIGtleXMgdGhlXG4gICAgLy8gZ3Vlc3QgcHJvZmlsZSBvbiBpcyBvbmx5IHByZXNlbnQgdW5kZXIgZm9ybWF0IDEuMC4gVGhlIExhbWJkYSBkZXJpdmVzIHRoZVxuICAgIC8vIGd1ZXN0IGlkZW50aXR5IFNPTEVMWSBmcm9tIHRoYXQgYXV0aG9yaXplci12ZXJpZmllZCBmaWVsZC5cbiAgICBjb25zdCBpYW1BdXRob3JpemVyID0gbmV3IEh0dHBJYW1BdXRob3JpemVyKCk7XG4gICAgaHR0cEFwaS5hZGRSb3V0ZXMoe1xuICAgICAgcGF0aDogdGhpcy5ndWVzdElkZW50aWZ5VXNlclBhdGgsXG4gICAgICBtZXRob2RzOiBbYXBpZ3d2Mi5IdHRwTWV0aG9kLlBPU1RdLFxuICAgICAgaW50ZWdyYXRpb246IG5ldyBIdHRwTGFtYmRhSW50ZWdyYXRpb24oJ0d1ZXN0SWRlbnRpZnlJbnRlZ3JhdGlvbicsIGZuLCB7XG4gICAgICAgIHBheWxvYWRGb3JtYXRWZXJzaW9uOiBhcGlnd3YyLlBheWxvYWRGb3JtYXRWZXJzaW9uLlZFUlNJT05fMV8wLFxuICAgICAgfSksXG4gICAgICBhdXRob3JpemVyOiBpYW1BdXRob3JpemVyLFxuICAgIH0pO1xuXG4gICAgLy8gYGV4ZWN1dGUtYXBpOkludm9rZWAgQVJOIGZvciB0aGUgZ3Vlc3Qgcm91dGUg4oCUIHRoZSBhcHAgZ3JhbnRzIHRoaXMgdG8gaXRzXG4gICAgLy8gSWRlbnRpdHkgUG9vbCBVTkFVVEhFTlRJQ0FURUQgcm9sZSBzbyBndWVzdHMgY2FuIGNhbGwgdGhlIHJvdXRlLiBTY29wZWQgYXNcbiAgICAvLyB0aWdodGx5IGFzIGV4ZWN1dGUtYXBpIGFsbG93czogYCRkZWZhdWx0L1BPU1QvaWRlbnRpZnktdXNlci1ndWVzdGAgcGlucyB0aGVcbiAgICAvLyBncmFudCB0byB0aGUgUE9TVCBtZXRob2Qgb24gdGhlIGd1ZXN0IHBhdGggb2YgdGhlIEFQSSdzIHNpbmdsZSBkZWZhdWx0XG4gICAgLy8gc3RhZ2UsIHNvIHRoZSB1bmF1dGggcm9sZSBjYW4gaW52b2tlIE5FSVRIRVIgb3RoZXIgbWV0aG9kcywgTk9SIG90aGVyXG4gICAgLy8gc3RhZ2VzLCBOT1IgdGhlIGF1dGhlZCBgL2lkZW50aWZ5LXVzZXJgIHJvdXRlLiAoSFRUUCBBUElzIGF1dG8tY3JlYXRlIHRoZVxuICAgIC8vIGAkZGVmYXVsdGAgc3RhZ2U7IHRoaXMgY29uc3RydWN0IG5ldmVyIGFkZHMgYW5vdGhlci4pXG4gICAgdGhpcy5ndWVzdFJvdXRlSW52b2tlQXJuID0gQXJuLmZvcm1hdChcbiAgICAgIHtcbiAgICAgICAgc2VydmljZTogJ2V4ZWN1dGUtYXBpJyxcbiAgICAgICAgcmVzb3VyY2U6IGh0dHBBcGkuYXBpSWQsXG4gICAgICAgIHJlc291cmNlTmFtZTogYCRkZWZhdWx0L1BPU1Qke3RoaXMuZ3Vlc3RJZGVudGlmeVVzZXJQYXRofWAsXG4gICAgICAgIGFybkZvcm1hdDogQXJuRm9ybWF0LlNMQVNIX1JFU09VUkNFX05BTUUsXG4gICAgICB9LFxuICAgICAgc3RhY2ssXG4gICAgKTtcblxuICAgIHRoaXMuYXBpRW5kcG9pbnQgPSBodHRwQXBpLmFwaUVuZHBvaW50O1xuXG4gICAgLy8gU2NvcGVkIHRvIHRoZSBzdGFjayAobm90IGB0aGlzYCkgc28gdGhlIG91dHB1dCBrZWVwcyBhIHN0YWJsZSwgcmVhZGFibGVcbiAgICAvLyBsb2dpY2FsIGlkIChgSWRlbnRpZnlVc2VyQXBpRW5kcG9pbnRgKSB0aGF0IGEgaHVtYW4gY2FuIGZpbmQgaW4gdGhlXG4gICAgLy8gQ2xvdWRGb3JtYXRpb24gY29uc29sZS4gVGhlIGNvbnN0cnVjdCBpcyBwcm92aXNpb25lZCBvbmNlIHBlciBzdGFjayAodGhlXG4gICAgLy8gR2VuMiBmYWN0b3J5IHBsYWNlcyBpdCBpbiBhIGRlZGljYXRlZCBuZXN0ZWQgc3RhY2spLCBzbyB0aGVyZSBpcyBub1xuICAgIC8vIGNvbGxpc2lvbiByaXNrIGZyb20gdGhlIHZlcmJhdGltIGlkLlxuICAgIG5ldyBDZm5PdXRwdXQoc3RhY2ssICdJZGVudGlmeVVzZXJBcGlFbmRwb2ludCcsIHtcbiAgICAgIHZhbHVlOiBodHRwQXBpLmFwaUVuZHBvaW50LFxuICAgICAgZGVzY3JpcHRpb246ICdQT1NUIHt2YWx1ZX0vaWRlbnRpZnktdXNlcicsXG4gICAgfSk7XG5cbiAgICAvLyBTdXJmYWNlIHRoZSBndWVzdCByb3V0ZSdzIGludm9rZSBBUk4gc28gYSBodW1hbiAvXG4gICAgLy8gdGhlIGFwcCBjYW4gZ3JhbnQgaXQgdG8gdGhlIElkZW50aXR5IFBvb2wgdW5hdXRoZW50aWNhdGVkIHJvbGUuXG4gICAgbmV3IENmbk91dHB1dChzdGFjaywgJ0d1ZXN0SWRlbnRpZnlSb3V0ZUludm9rZUFybicsIHtcbiAgICAgIHZhbHVlOiB0aGlzLmd1ZXN0Um91dGVJbnZva2VBcm4sXG4gICAgICBkZXNjcmlwdGlvbjpcbiAgICAgICAgJ0dyYW50IGV4ZWN1dGUtYXBpOkludm9rZSBvbiB0aGlzIEFSTiB0byB0aGUgQ29nbml0byBJZGVudGl0eSBQb29sIHVuYXV0aGVudGljYXRlZCByb2xlJyxcbiAgICB9KTtcblxuICAgIC8vIC0tLS0gUHVzaC1kZWxpdmVyeSBwYXRoIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG4gICAgLy8gQW1hem9uIENvbm5lY3QgaGFzIG5vIG5hdGl2ZSBtb2JpbGUtcHVzaCBjaGFubmVsLCBzbyBhIExhbWJkYSBicmlkZ2VzIHRoZVxuICAgIC8vIGdhcDogQ29ubmVjdCBpbnZva2VzIGl0IHZpYSBhIEpvdXJuZXkgQ3VzdG9tLWFjdGlvbiB0byBkZWxpdmVyIHB1c2hcbiAgICAvLyB0aHJvdWdoIEFXUyBFbmQgVXNlciBNZXNzYWdpbmcgKFBpbnBvaW50IFNlbmRNZXNzYWdlcykuIEEgbWluaW1hbCBQaW5wb2ludFxuICAgIC8vIGFwcGxpY2F0aW9uIGlzIGFsd2F5cyBjcmVhdGVkIHNvIFNlbmRNZXNzYWdlcyBoYXMgYSB2YWxpZCBBcHBsaWNhdGlvbklkLlxuICAgIC8vIFRoZSBBUE5zIC8gR0NNIGNoYW5uZWxzIG9uIGl0IGFyZSBjb25maWd1cmVkIG9ubHkgd2hlbiB0aGUgY2FsbGVyIHN1cHBsaWVzXG4gICAgLy8gY3JlZGVudGlhbHMgKHNlZSBgYXBuc0NoYW5uZWxgIC8gYGZjbUNoYW5uZWxgKTsgb3RoZXJ3aXNlIHRoZSBhcHBsaWNhdGlvblxuICAgIC8vIGlzIGNyZWF0ZWQgd2l0aCBubyBjaGFubmVsIGVuYWJsZWQgKGRlbGl2ZXJ5IHdpbGwgNDA0IHVudGlsIGNvbmZpZ3VyZWQpLlxuICAgIGNvbnN0IHB1c2hBcHBsaWNhdGlvbiA9IG5ldyBDZm5BcHAodGhpcywgJ1B1c2hBcHAnLCB7XG4gICAgICBuYW1lOiBgJHtkb21haW5OYW1lfS1wdXNoYCxcbiAgICB9KTtcbiAgICBjb25zdCBldW1BcHBsaWNhdGlvbklkID0gcHVzaEFwcGxpY2F0aW9uLnJlZjtcbiAgICB0aGlzLmV1bUFwcGxpY2F0aW9uSWQgPSBldW1BcHBsaWNhdGlvbklkO1xuXG4gICAgLy8gLS0tLSBPcHRpb25hbCBwdXNoIGNoYW5uZWxzIChzZWNyZXQtZHJpdmVuKSAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cbiAgICAvLyBUaGUgQW1wbGlmeSBHZW4yIGZhY3RvcnkgcmVzb2x2ZXMgdGhlIEFQTnMgYC5wOGAga2V5IC8gRkNNIHNlcnZpY2UtYWNjb3VudFxuICAgIC8vIEpTT04gZnJvbSBBbXBsaWZ5IGBzZWNyZXQoKWBzIHRvIGRlcGxveS10aW1lIENGTiB0b2tlbnMgYW5kIHBhc3NlcyB0aGVtIGFzXG4gICAgLy8gcGxhaW4gc3RyaW5ncy4gQ2hhbm5lbCBjcmVkZW50aWFscyBmbG93IHRocm91Z2ggQ2xvdWRGb3JtYXRpb24gdGhlIHNhbWUgd2F5XG4gICAgLy8gQW1wbGlmeSdzIG93biBleHRlcm5hbC1hdXRoLXByb3ZpZGVyIHNlY3JldHMgZG8gKGRlZmluZUF1dGgpIOKAlCB2aWEgdGhlXG4gICAgLy8gc2VjcmV0IGN1c3RvbSByZXNvdXJjZSdzIHRva2VuLCBuZXZlciBhcyBsaXRlcmFsIHRlbXBsYXRlIHBsYWluIHRleHQuIFRoZVxuICAgIC8vIGNoYW5uZWxzIGF0dGFjaCB0byBUSElTIGNvbnN0cnVjdCdzIG93biBFVU0gYXBwLCBzbyB0aGV5IGFyZSBwcm92aXNpb25lZFxuICAgIC8vIGFmdGVyIGl0IGFuZCB0b3JuIGRvd24gd2l0aCB0aGUgc3RhY2suIFRoZSBgYXBwbGljYXRpb25JZGAgcHJvcCByZXNvbHZlcyB0b1xuICAgIC8vIGEgYFJlZmAgb24gdGhlIEVVTSBhcHAsIHNvIENsb3VkRm9ybWF0aW9uIGluZmVycyB0aGUgY3JlYXRlL2RlbGV0ZSBvcmRlcmluZ1xuICAgIC8vIOKAlCBubyBleHBsaWNpdCBgYWRkRGVwZW5kZW5jeWAgbmVlZGVkLlxuICAgIGNvbnN0IHsgYXBuc0NoYW5uZWw6IGFwbnNDb25maWcsIGZjbUNoYW5uZWw6IGZjbUNvbmZpZyB9ID0gcHJvcHM7XG5cbiAgICBsZXQgYXBuc0NoYW5uZWw6IENmbkFQTlNDaGFubmVsIHwgQ2ZuQVBOU1NhbmRib3hDaGFubmVsIHwgdW5kZWZpbmVkO1xuICAgIGlmIChhcG5zQ29uZmlnKSB7XG4gICAgICAvLyBUb2tlbiAoYC5wOGApIGF1dGhlbnRpY2F0aW9uOiB0aGUgQ0ZOIGBEZWZhdWx0QXV0aGVudGljYXRpb25NZXRob2RgXG4gICAgICAvLyBwcm9wZXJ0eSBhY2NlcHRzIGBUT0tFTmAgKHNpZ25pbmcta2V5IGF1dGgpIG9yIGBDRVJUSUZJQ0FURWA7IHRva2VuIGF1dGhcbiAgICAgIC8vIHVzZXMgdGhlIGAucDhgIHNpZ25pbmcga2V5LiBTYW5kYm94IHNlbGVjdHMgdGhlIGRldmVsb3BtZW50IEFQTnMgZW5kcG9pbnRcbiAgICAgIC8vIHVzZWQgYnkgZGV2ZWxvcG1lbnQtc2lnbmVkIGJ1aWxkcy5cbiAgICAgIGNvbnN0IGFwbnNQcm9wcyA9IHtcbiAgICAgICAgYXBwbGljYXRpb25JZDogZXVtQXBwbGljYXRpb25JZCxcbiAgICAgICAgZW5hYmxlZDogdHJ1ZSxcbiAgICAgICAgZGVmYXVsdEF1dGhlbnRpY2F0aW9uTWV0aG9kOiAnVE9LRU4nLFxuICAgICAgICB0b2tlbktleTogYXBuc0NvbmZpZy50b2tlbktleSxcbiAgICAgICAgdG9rZW5LZXlJZDogYXBuc0NvbmZpZy5rZXlJZCxcbiAgICAgICAgdGVhbUlkOiBhcG5zQ29uZmlnLnRlYW1JZCxcbiAgICAgICAgYnVuZGxlSWQ6IGFwbnNDb25maWcuYnVuZGxlSWQsXG4gICAgICB9O1xuICAgICAgYXBuc0NoYW5uZWwgPSBhcG5zQ29uZmlnLnNhbmRib3hcbiAgICAgICAgPyBuZXcgQ2ZuQVBOU1NhbmRib3hDaGFubmVsKHRoaXMsICdBcG5zU2FuZGJveENoYW5uZWwnLCBhcG5zUHJvcHMpXG4gICAgICAgIDogbmV3IENmbkFQTlNDaGFubmVsKHRoaXMsICdBcG5zQ2hhbm5lbCcsIGFwbnNQcm9wcyk7XG4gICAgfVxuXG4gICAgbGV0IGdjbUNoYW5uZWw6IENmbkdDTUNoYW5uZWwgfCB1bmRlZmluZWQ7XG4gICAgaWYgKGZjbUNvbmZpZykge1xuICAgICAgLy8gRkNNIEhUVFAgdjE6IGBUT0tFTmAgYXV0aCB3aXRoIGEgc2VydmljZS1hY2NvdW50IEpTT04gY3JlZGVudGlhbC4gR29vZ2xlXG4gICAgICAvLyBkZXByZWNhdGVkIHRoZSBsZWdhY3kgc2VydmVyLWtleSAoYEFwaUtleWAgLyBgS0VZYCkgQVBJLCBzbyBvbmx5IHRoZSB2MVxuICAgICAgLy8gYFNlcnZpY2VKc29uYCAoRkNNIHYxKSBwYXRoIGlzIHVzZWQuXG4gICAgICBnY21DaGFubmVsID0gbmV3IENmbkdDTUNoYW5uZWwodGhpcywgJ0djbUNoYW5uZWwnLCB7XG4gICAgICAgIGFwcGxpY2F0aW9uSWQ6IGV1bUFwcGxpY2F0aW9uSWQsXG4gICAgICAgIGVuYWJsZWQ6IHRydWUsXG4gICAgICAgIGRlZmF1bHRBdXRoZW50aWNhdGlvbk1ldGhvZDogJ1RPS0VOJyxcbiAgICAgICAgc2VydmljZUpzb246IGZjbUNvbmZpZy5zZXJ2aWNlSnNvbixcbiAgICAgIH0pO1xuICAgIH1cblxuICAgIGNvbnN0IHB1c2hDb2RlUGF0aCA9IHByb3BzLnB1c2hMYW1iZGFDb2RlUGF0aCA/PyBkZWZhdWx0UHVzaExhbWJkYUNvZGVQYXRoO1xuICAgIGNvbnN0IHB1c2hGbiA9IG5ldyBsYW1iZGEuRnVuY3Rpb24odGhpcywgJ1B1c2hIYW5kbGVyRm4nLCB7XG4gICAgICBjb2RlOiBsYW1iZGEuQ29kZS5mcm9tQXNzZXQocHVzaENvZGVQYXRoKSxcbiAgICAgIGhhbmRsZXI6ICdpbmRleC5oYW5kbGVyJyxcbiAgICAgIHJ1bnRpbWU6IGxhbWJkYS5SdW50aW1lLk5PREVKU18yMF9YLFxuICAgICAgdGltZW91dDogRHVyYXRpb24uc2Vjb25kcygzMCksXG4gICAgICBtZW1vcnlTaXplOiAyNTYsXG4gICAgICBlbnZpcm9ubWVudDoge1xuICAgICAgICBbRU5WX0RPTUFJTl9OQU1FXTogZG9tYWluTmFtZSxcbiAgICAgICAgW0VOVl9FVU1fQVBQTElDQVRJT05fSURdOiBldW1BcHBsaWNhdGlvbklkLFxuICAgICAgfSxcbiAgICB9KTtcblxuICAgIC8vIExlYXN0LXByaXZpbGVnZTogcmVhZCArIGRlbGV0ZSBkZXZpY2Ugb2JqZWN0cyBvbiBUSElTIGRvbWFpbiBvbmx5LCBhbmRcbiAgICAvLyBTZW5kTWVzc2FnZXMgc2NvcGVkIHRvIFRISVMgRVVNIGFwcGxpY2F0aW9uLlxuICAgIHB1c2hGbi5hZGRUb1JvbGVQb2xpY3koXG4gICAgICBuZXcgaWFtLlBvbGljeVN0YXRlbWVudCh7XG4gICAgICAgIGVmZmVjdDogaWFtLkVmZmVjdC5BTExPVyxcbiAgICAgICAgYWN0aW9uczogW1xuICAgICAgICAgIC8vIFJlc29sdmUgYSBwcm9maWxlJ3MgcmVnaXN0ZXJlZCBkZXZpY2VzICh0b2tlbnMgKyBjaGFubmVscykuXG4gICAgICAgICAgJ3Byb2ZpbGU6TGlzdFByb2ZpbGVPYmplY3RzJyxcbiAgICAgICAgICAvLyBTdGFsZS10b2tlbiBjbGVhbnVwOiBkZWxldGUgYSBkZXZpY2Ugd2hvc2UgdG9rZW4gaXMgZGVhZC5cbiAgICAgICAgICAncHJvZmlsZTpEZWxldGVQcm9maWxlT2JqZWN0JyxcbiAgICAgICAgXSxcbiAgICAgICAgcmVzb3VyY2VzOiBbZG9tYWluQXJuLCBvYmplY3RUeXBlc0Fybl0sXG4gICAgICB9KSxcbiAgICApO1xuXG4gICAgY29uc3QgYXBwTWVzc2FnZXNBcm4gPSBBcm4uZm9ybWF0KFxuICAgICAge1xuICAgICAgICBzZXJ2aWNlOiAnbW9iaWxldGFyZ2V0aW5nJyxcbiAgICAgICAgcmVzb3VyY2U6ICdhcHBzJyxcbiAgICAgICAgcmVzb3VyY2VOYW1lOiBgJHtldW1BcHBsaWNhdGlvbklkfS9tZXNzYWdlc2AsXG4gICAgICAgIGFybkZvcm1hdDogQXJuRm9ybWF0LlNMQVNIX1JFU09VUkNFX05BTUUsXG4gICAgICB9LFxuICAgICAgc3RhY2ssXG4gICAgKTtcbiAgICBwdXNoRm4uYWRkVG9Sb2xlUG9saWN5KFxuICAgICAgbmV3IGlhbS5Qb2xpY3lTdGF0ZW1lbnQoe1xuICAgICAgICBlZmZlY3Q6IGlhbS5FZmZlY3QuQUxMT1csXG4gICAgICAgIGFjdGlvbnM6IFsnbW9iaWxldGFyZ2V0aW5nOlNlbmRNZXNzYWdlcyddLFxuICAgICAgICByZXNvdXJjZXM6IFthcHBNZXNzYWdlc0Fybl0sXG4gICAgICB9KSxcbiAgICApO1xuXG4gICAgLy8gLS0tLSBSdW50aW1lIG1lc3NhZ2UtdGVtcGxhdGUgcmVzb2x1dGlvbiAoUSBpbiBDb25uZWN0KSAtLS0tLS0tLS0tLS0tLS1cbiAgICAvLyBUaGUgcHVzaCBMYW1iZGEgcmVzb2x2ZXMgcGVyLXByb2ZpbGUgY29weSBmcm9tIGEgUSBpbiBDb25uZWN0IChXaXNkb20pXG4gICAgLy8gUFVTSCBtZXNzYWdlIHRlbXBsYXRlIGF0IHJ1bnRpbWU6IGl0IGRpc2NvdmVycyB0aGUga25vd2xlZGdlIGJhc2UgZnJvbSB0aGVcbiAgICAvLyBqb3VybmV5J3MgY2FtcGFpZ24gKERlc2NyaWJlQ2FtcGFpZ24gLT4gY29ubmVjdEluc3RhbmNlSWQgLT5cbiAgICAvLyBMaXN0SW50ZWdyYXRpb25Bc3NvY2lhdGlvbnMpIGFuZCByZW5kZXJzIHRoZSB0ZW1wbGF0ZSB3aG9zZSBuYW1lIG1hdGNoZXNcbiAgICAvLyB0aGUgQ3VzdG9tLWFjdGlvbiBBY3Rpb25JZC4gTGVhc3QtcHJpdmlsZWdlLCBzY29wZWQgdG8gdGhpcyBhY2NvdW50IC9cbiAgICAvLyByZWdpb24uIElBTSBwcmVmaXhlcyBjb25maXJtZWQgZW1waXJpY2FsbHk6IGNhbXBhaWduIGFjdGlvbnMgdXNlXG4gICAgLy8gYGNvbm5lY3QtY2FtcGFpZ25zYCwgaW50ZWdyYXRpb24gbGlzdGluZyB1c2VzIGBjb25uZWN0YCwgYW5kIFEgaW4gQ29ubmVjdFxuICAgIC8vIG1lc3NhZ2UtdGVtcGxhdGUgYWN0aW9ucyB1c2UgdGhlIGB3aXNkb21gIHByZWZpeCAoUSBpbiBDb25uZWN0IGlzIHRoZVxuICAgIC8vIHJlYnJhbmQgb2YgQW1hem9uIENvbm5lY3QgV2lzZG9tOyB0aGUgU0RLIGNsaWVudCBpcyBgQGF3cy1zZGsvY2xpZW50LXFjb25uZWN0YFxuICAgIC8vIGJ1dCBJQU0vQVJOcyByZW1haW4gYHdpc2RvbWApLlxuICAgIHB1c2hGbi5hZGRUb1JvbGVQb2xpY3koXG4gICAgICBuZXcgaWFtLlBvbGljeVN0YXRlbWVudCh7XG4gICAgICAgIGVmZmVjdDogaWFtLkVmZmVjdC5BTExPVyxcbiAgICAgICAgYWN0aW9uczogWydjb25uZWN0LWNhbXBhaWduczpEZXNjcmliZUNhbXBhaWduJ10sXG4gICAgICAgIHJlc291cmNlczogW1xuICAgICAgICAgIEFybi5mb3JtYXQoXG4gICAgICAgICAgICB7XG4gICAgICAgICAgICAgIHNlcnZpY2U6ICdjb25uZWN0LWNhbXBhaWducycsXG4gICAgICAgICAgICAgIHJlc291cmNlOiAnY2FtcGFpZ24nLFxuICAgICAgICAgICAgICByZXNvdXJjZU5hbWU6ICcqJyxcbiAgICAgICAgICAgICAgYXJuRm9ybWF0OiBBcm5Gb3JtYXQuU0xBU0hfUkVTT1VSQ0VfTkFNRSxcbiAgICAgICAgICAgIH0sXG4gICAgICAgICAgICBzdGFjayxcbiAgICAgICAgICApLFxuICAgICAgICBdLFxuICAgICAgfSksXG4gICAgKTtcbiAgICBwdXNoRm4uYWRkVG9Sb2xlUG9saWN5KFxuICAgICAgbmV3IGlhbS5Qb2xpY3lTdGF0ZW1lbnQoe1xuICAgICAgICBlZmZlY3Q6IGlhbS5FZmZlY3QuQUxMT1csXG4gICAgICAgIGFjdGlvbnM6IFsnY29ubmVjdDpMaXN0SW50ZWdyYXRpb25Bc3NvY2lhdGlvbnMnXSxcbiAgICAgICAgcmVzb3VyY2VzOiBbXG4gICAgICAgICAgQXJuLmZvcm1hdChcbiAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgc2VydmljZTogJ2Nvbm5lY3QnLFxuICAgICAgICAgICAgICByZXNvdXJjZTogJ2luc3RhbmNlJyxcbiAgICAgICAgICAgICAgcmVzb3VyY2VOYW1lOiAnKicsXG4gICAgICAgICAgICAgIGFybkZvcm1hdDogQXJuRm9ybWF0LlNMQVNIX1JFU09VUkNFX05BTUUsXG4gICAgICAgICAgICB9LFxuICAgICAgICAgICAgc3RhY2ssXG4gICAgICAgICAgKSxcbiAgICAgICAgXSxcbiAgICAgIH0pLFxuICAgICk7XG4gICAgcHVzaEZuLmFkZFRvUm9sZVBvbGljeShcbiAgICAgIG5ldyBpYW0uUG9saWN5U3RhdGVtZW50KHtcbiAgICAgICAgZWZmZWN0OiBpYW0uRWZmZWN0LkFMTE9XLFxuICAgICAgICBhY3Rpb25zOiBbXG4gICAgICAgICAgJ3dpc2RvbTpMaXN0TWVzc2FnZVRlbXBsYXRlcycsXG4gICAgICAgICAgJ3dpc2RvbTpHZXRNZXNzYWdlVGVtcGxhdGUnLFxuICAgICAgICAgICd3aXNkb206UmVuZGVyTWVzc2FnZVRlbXBsYXRlJyxcbiAgICAgICAgXSxcbiAgICAgICAgcmVzb3VyY2VzOiBbXG4gICAgICAgICAgQXJuLmZvcm1hdChcbiAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgc2VydmljZTogJ3dpc2RvbScsXG4gICAgICAgICAgICAgIHJlc291cmNlOiAna25vd2xlZGdlLWJhc2UnLFxuICAgICAgICAgICAgICByZXNvdXJjZU5hbWU6ICcqJyxcbiAgICAgICAgICAgICAgYXJuRm9ybWF0OiBBcm5Gb3JtYXQuU0xBU0hfUkVTT1VSQ0VfTkFNRSxcbiAgICAgICAgICAgIH0sXG4gICAgICAgICAgICBzdGFjayxcbiAgICAgICAgICApLFxuICAgICAgICAgIEFybi5mb3JtYXQoXG4gICAgICAgICAgICB7XG4gICAgICAgICAgICAgIHNlcnZpY2U6ICd3aXNkb20nLFxuICAgICAgICAgICAgICByZXNvdXJjZTogJ21lc3NhZ2UtdGVtcGxhdGUnLFxuICAgICAgICAgICAgICByZXNvdXJjZU5hbWU6ICcqJyxcbiAgICAgICAgICAgICAgYXJuRm9ybWF0OiBBcm5Gb3JtYXQuU0xBU0hfUkVTT1VSQ0VfTkFNRSxcbiAgICAgICAgICAgIH0sXG4gICAgICAgICAgICBzdGFjayxcbiAgICAgICAgICApLFxuICAgICAgICBdLFxuICAgICAgfSksXG4gICAgKTtcblxuICAgIC8vIC0tLS0gSW52b2tlIHJlc291cmNlIHBvbGljeSBmb3IgQW1hem9uIENvbm5lY3QgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG4gICAgLy8gQSBDb25uZWN0IEpvdXJuZXkgQ3VzdG9tLWFjdGlvbiAoYW5kIE91dGJvdW5kIENhbXBhaWducyB2MikgaW52b2tlcyB0aGlzXG4gICAgLy8gTGFtYmRhIHRocm91Z2ggdGhlIGNvbm5lY3QgLyBjb25uZWN0LWNhbXBhaWducyBzZXJ2aWNlIHByaW5jaXBhbC4gR3JhbnRcbiAgICAvLyBsYW1iZGE6SW52b2tlRnVuY3Rpb24gdG8gYm90aCwgc2NvcGVkIHRvIFRISVMgYWNjb3VudCAoYHNvdXJjZUFjY291bnRgKSBzb1xuICAgIC8vIG9ubHkgQ29ubmVjdCBpbnN0YW5jZXMgaW4gdGhlIGRlcGxveWluZyBhY2NvdW50IOKAlCBub3QgYW55IENvbm5lY3QgaW5zdGFuY2VcbiAgICAvLyBhbnl3aGVyZSDigJQgY2FuIGludm9rZSBpdCAoY29uZnVzZWQtZGVwdXR5IGd1YXJkKS4gVGhlIENvbm5lY3QgaW5zdGFuY2UgQVJOXG4gICAgLy8gaXNuJ3Qga25vd24gYXQgc3ludGggdGltZSwgc28gYWNjb3VudCBzY29waW5nIGlzIHRoZSB0aWdodGVzdCBndWFyZCBoZXJlLlxuICAgIGZvciAoY29uc3Qgc2VydmljZVByaW5jaXBhbCBvZiBDT05ORUNUX0lOVk9LRV9TRVJWSUNFX1BSSU5DSVBBTFMpIHtcbiAgICAgIHB1c2hGbi5hZGRQZXJtaXNzaW9uKFxuICAgICAgICBgSW52b2tlLSR7c2VydmljZVByaW5jaXBhbC5yZXBsYWNlKC9bXmEtekEtWjAtOV0vZywgJy0nKX1gLFxuICAgICAgICB7XG4gICAgICAgICAgcHJpbmNpcGFsOiBuZXcgaWFtLlNlcnZpY2VQcmluY2lwYWwoc2VydmljZVByaW5jaXBhbCksXG4gICAgICAgICAgYWN0aW9uOiAnbGFtYmRhOkludm9rZUZ1bmN0aW9uJyxcbiAgICAgICAgICBzb3VyY2VBY2NvdW50OiBzdGFjay5hY2NvdW50LFxuICAgICAgICB9LFxuICAgICAgKTtcbiAgICB9XG5cbiAgICB0aGlzLnB1c2hGdW5jdGlvbkFybiA9IHB1c2hGbi5mdW5jdGlvbkFybjtcblxuICAgIC8vIFNjb3BlZCB0byB0aGUgc3RhY2sgKG5vdCBgdGhpc2ApIHNvIHRoZSBvdXRwdXQga2VlcHMgYSBzdGFibGUsIHJlYWRhYmxlXG4gICAgLy8gbG9naWNhbCBpZCAoYFB1c2hIYW5kbGVyRnVuY3Rpb25Bcm5gKSB0aGF0IGEgaHVtYW4gY2FuIGZpbmQgaW4gdGhlXG4gICAgLy8gQ2xvdWRGb3JtYXRpb24gY29uc29sZSBhbmQgY29weSB0byB3aXJlIHRoZSBKb3VybmV5IEN1c3RvbS1hY3Rpb24uXG4gICAgbmV3IENmbk91dHB1dChzdGFjaywgJ1B1c2hIYW5kbGVyRnVuY3Rpb25Bcm4nLCB7XG4gICAgICB2YWx1ZTogcHVzaEZuLmZ1bmN0aW9uQXJuLFxuICAgICAgZGVzY3JpcHRpb246XG4gICAgICAgICdXaXJlIGFzIGEgQ29ubmVjdCBKb3VybmV5IEN1c3RvbS1hY3Rpb24gKEludm9rZSBMYW1iZGEpIHRhcmdldCcsXG4gICAgfSk7XG5cbiAgICAvLyAtLS0tIENyZWF0ZS1tb2RlIG91dHB1dHMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuICAgIC8vIFN1cmZhY2UgdGhlIHJlc291cmNlcyB0aGlzIGNvbnN0cnVjdCBwcm92aXNpb25lZCBmcm9tIHNjcmF0Y2ggc28gYSBodW1hblxuICAgIC8vIGNhbiBmaW5kIHRoZSBuZXcgaW5zdGFuY2UgLyBkb21haW4gaW4gdGhlIGNvbnNvbGUuIFRoZWlyIE91dGJvdW5kXG4gICAgLy8gQ2FtcGFpZ25zIGFzc29jaWF0aW9uIGlzIGhhbmRsZWQgYXV0b21hdGljYWxseSBieSB0aGUgY3VzdG9tIHJlc291cmNlIGFib3ZlLlxuICAgIGlmIChjb25uZWN0SW5zdGFuY2UpIHtcbiAgICAgIG5ldyBDZm5PdXRwdXQoc3RhY2ssICdDb25uZWN0SW5zdGFuY2VJZCcsIHtcbiAgICAgICAgdmFsdWU6IGNvbm5lY3RJbnN0YW5jZS5hdHRySWQsXG4gICAgICAgIGRlc2NyaXB0aW9uOiAnQW1hem9uIENvbm5lY3QgaW5zdGFuY2UgY3JlYXRlZCBmb3Igbm90aWZpY2F0aW9ucycsXG4gICAgICB9KTtcbiAgICAgIG5ldyBDZm5PdXRwdXQoc3RhY2ssICdDb25uZWN0SW5zdGFuY2VBcm4nLCB7XG4gICAgICAgIHZhbHVlOiBjb25uZWN0SW5zdGFuY2UuYXR0ckFybixcbiAgICAgICAgZGVzY3JpcHRpb246ICdBbWF6b24gQ29ubmVjdCBpbnN0YW5jZSBBUk4nLFxuICAgICAgfSk7XG4gICAgfVxuICAgIGlmIChwcm9maWxlc0RvbWFpbikge1xuICAgICAgbmV3IENmbk91dHB1dChzdGFjaywgJ1Byb2ZpbGVzRG9tYWluTmFtZScsIHtcbiAgICAgICAgdmFsdWU6IGRvbWFpbk5hbWUsXG4gICAgICAgIGRlc2NyaXB0aW9uOiAnQ3VzdG9tZXIgUHJvZmlsZXMgZG9tYWluIGNyZWF0ZWQgZm9yIG5vdGlmaWNhdGlvbnMnLFxuICAgICAgfSk7XG4gICAgfVxuXG4gICAgdGhpcy5yZXNvdXJjZXMgPSB7XG4gICAgICBpZGVudGlmeVVzZXJGdW5jdGlvbjogZm4sXG4gICAgICBodHRwQXBpLFxuICAgICAgcHJvZmlsZU9iamVjdFR5cGU6IHByb2ZpbGVUeXBlLFxuICAgICAgZ3Vlc3RQcm9maWxlT2JqZWN0VHlwZTogZ3Vlc3RQcm9maWxlVHlwZSxcbiAgICAgIGRldmljZU9iamVjdFR5cGU6IGRldmljZVR5cGUsXG4gICAgICBwdXNoRnVuY3Rpb246IHB1c2hGbixcbiAgICAgIHB1c2hBcHBsaWNhdGlvbixcbiAgICAgIGFwbnNDaGFubmVsLFxuICAgICAgZ2NtQ2hhbm5lbCxcbiAgICAgIGNvbm5lY3RJbnN0YW5jZSxcbiAgICAgIHByb2ZpbGVzRG9tYWluLFxuICAgIH07XG4gIH1cblxuICAvKipcbiAgICogQ3JlYXRlLWZyb20tc2NyYXRjaCBPTkxZOiBhIExhbWJkYS1iYWNrZWQgQ0RLIGN1c3RvbSByZXNvdXJjZSB0aGF0XG4gICAqIGFzc29jaWF0ZXMgdGhlIGNyZWF0ZWQgQ3VzdG9tZXIgUHJvZmlsZXMgZG9tYWluIHdpdGggdGhlIGNyZWF0ZWQgQW1hem9uXG4gICAqIENvbm5lY3QgaW5zdGFuY2UncyBPdXRib3VuZCBDYW1wYWlnbnMgdjIgYXQgZGVwbG95IHRpbWUsIHNvIENvbm5lY3QgSm91cm5leXNcbiAgICogY2FuIHRhcmdldCB0aGUgZG9tYWluJ3MgcHJvZmlsZXMgd2l0aCBubyBtYW51YWwgY29uc29sZSBzdGVwLlxuICAgKlxuICAgKiBBIGBjdXN0b20tcmVzb3VyY2VzLlByb3ZpZGVyYCBmcm9udHMgdGhlIGhhbmRsZXIgYmVjYXVzZSBvbmJvYXJkaW5nIGlzXG4gICAqIGFzeW5jaHJvbm91czogdGhlIGhhbmRsZXIgc3RhcnRzIHRoZSBpbnN0YW5jZS1vbmJvYXJkaW5nIGpvYiwgdGhlbiBQT0xMU1xuICAgKiBHZXRJbnN0YW5jZU9uYm9hcmRpbmdKb2JTdGF0dXMgdW50aWwgU1VDQ0VFREVEIChhbiBgQXdzQ3VzdG9tUmVzb3VyY2VgIGNhbid0XG4gICAqIHBvbGwpLiBvbkNyZWF0ZS9vblVwZGF0ZSBhcmUgaWRlbXBvdGVudDsgb25EZWxldGUgYmVzdC1lZmZvcnQgcmV2ZXJzZXMgdGhlXG4gICAqIGludGVncmF0aW9ucyArIG9mZmJvYXJkcywgbmV2ZXIgZmFpbGluZyB0ZWFyZG93bi4gVGhlIHJlc291cmNlIGRlcGVuZHMgb25cbiAgICogYm90aCB0aGUgaW5zdGFuY2UgYW5kIHRoZSBkb21haW4gc28gaXQgcnVucyBhZnRlciB0aGV5IGV4aXN0IGFuZCBpcyB0b3JuXG4gICAqIGRvd24gYmVmb3JlIHRoZW0uXG4gICAqL1xuICBwcml2YXRlIGFkZENhbXBhaWduQXNzb2NpYXRpb24oXG4gICAgY29ubmVjdEluc3RhbmNlOiBDZm5JbnN0YW5jZSxcbiAgICBwcm9maWxlc0RvbWFpbjogQ2ZuRG9tYWluLFxuICAgIGRvbWFpbk5hbWU6IHN0cmluZyxcbiAgICBjb2RlUGF0aDogc3RyaW5nLFxuICApOiB2b2lkIHtcbiAgICBjb25zdCBzdGFjayA9IHRoaXMuc3RhY2s7XG5cbiAgICBjb25zdCBhc3NvY2lhdGlvbkZuID0gbmV3IGxhbWJkYS5GdW5jdGlvbih0aGlzLCAnQ2FtcGFpZ25Bc3NvY2lhdGlvbkZuJywge1xuICAgICAgY29kZTogbGFtYmRhLkNvZGUuZnJvbUFzc2V0KGNvZGVQYXRoKSxcbiAgICAgIGhhbmRsZXI6ICdpbmRleC5oYW5kbGVyJyxcbiAgICAgIHJ1bnRpbWU6IGxhbWJkYS5SdW50aW1lLk5PREVKU18yMF9YLFxuICAgICAgLy8gT25ib2FyZGluZyBwb2xscyBHZXRJbnN0YW5jZU9uYm9hcmRpbmdKb2JTdGF0dXMgdW50aWwgU1VDQ0VFREVEICh+MjBzLFxuICAgICAgLy8gY2FwcGVkIH4ybWluKTsgYWxsb3cgZ2VuZXJvdXMgaGVhZHJvb20gZm9yIG9uYm9hcmRpbmcgKyBib3RoIGludGVncmF0aW9ucy5cbiAgICAgIHRpbWVvdXQ6IER1cmF0aW9uLm1pbnV0ZXMoMTApLFxuICAgICAgbWVtb3J5U2l6ZTogMjU2LFxuICAgIH0pO1xuXG4gICAgLy8gVGhlc2UgaW5zdGFuY2Utb25ib2FyZGluZyAvIGluc3RhbmNlLWludGVncmF0aW9uIGFjdGlvbnMgb3BlcmF0ZSBvbiB0aGVcbiAgICAvLyBDb25uZWN0IGluc3RhbmNlJ3MgT3V0Ym91bmQgQ2FtcGFpZ25zIGNvbmZpZ3VyYXRpb24sIG5vdCBvbiBhIGNhbXBhaWduXG4gICAgLy8gcmVzb3VyY2UuIGNvbm5lY3QtY2FtcGFpZ25zIGRvZXMgTk9UIHN1cHBvcnQgcmVzb3VyY2UtbGV2ZWwgcGVybWlzc2lvbnNcbiAgICAvLyBmb3IgdGhlbSAoSUFNIGV2YWx1YXRlcyB0aGVtIGFnYWluc3QgYGNhbXBhaWduLypgIGFuZCBkZW5pZXMgYW5cbiAgICAvLyBgaW5zdGFuY2UvKmAtc2NvcGVkIGdyYW50KSwgc28gdGhleSBtdXN0IGJlIGdyYW50ZWQgb24gYCpgLlxuICAgIGFzc29jaWF0aW9uRm4uYWRkVG9Sb2xlUG9saWN5KFxuICAgICAgbmV3IGlhbS5Qb2xpY3lTdGF0ZW1lbnQoe1xuICAgICAgICBlZmZlY3Q6IGlhbS5FZmZlY3QuQUxMT1csXG4gICAgICAgIGFjdGlvbnM6IFtcbiAgICAgICAgICAnY29ubmVjdC1jYW1wYWlnbnM6U3RhcnRJbnN0YW5jZU9uYm9hcmRpbmdKb2InLFxuICAgICAgICAgICdjb25uZWN0LWNhbXBhaWduczpHZXRJbnN0YW5jZU9uYm9hcmRpbmdKb2JTdGF0dXMnLFxuICAgICAgICAgICdjb25uZWN0LWNhbXBhaWduczpEZWxldGVJbnN0YW5jZU9uYm9hcmRpbmdKb2InLFxuICAgICAgICAgICdjb25uZWN0LWNhbXBhaWduczpQdXRDb25uZWN0SW5zdGFuY2VJbnRlZ3JhdGlvbicsXG4gICAgICAgICAgJ2Nvbm5lY3QtY2FtcGFpZ25zOkRlbGV0ZUNvbm5lY3RJbnN0YW5jZUludGVncmF0aW9uJyxcbiAgICAgICAgXSxcbiAgICAgICAgcmVzb3VyY2VzOiBbJyonXSxcbiAgICAgIH0pLFxuICAgICk7XG5cbiAgICAvLyBjb25uZWN0LWNhbXBhaWducyBvbmJvYXJkaW5nICsgUHV0Q29ubmVjdEluc3RhbmNlSW50ZWdyYXRpb24gdmFsaWRhdGUgdGhlXG4gICAgLy8gdGFyZ2V0IENvbm5lY3QgaW5zdGFuY2Ugb24gdGhlIGNhbGxlcidzIGJlaGFsZiwgc28gdGhlIGhhbmRsZXIgcm9sZSBhbHNvXG4gICAgLy8gbmVlZHMgY29ubmVjdDpEZXNjcmliZUluc3RhbmNlIG9uIHRoZSAoY29uc3RydWN0LW93bmVkKSBpbnN0YW5jZS5cbiAgICBhc3NvY2lhdGlvbkZuLmFkZFRvUm9sZVBvbGljeShcbiAgICAgIG5ldyBpYW0uUG9saWN5U3RhdGVtZW50KHtcbiAgICAgICAgZWZmZWN0OiBpYW0uRWZmZWN0LkFMTE9XLFxuICAgICAgICBhY3Rpb25zOiBbJ2Nvbm5lY3Q6RGVzY3JpYmVJbnN0YW5jZSddLFxuICAgICAgICByZXNvdXJjZXM6IFtjb25uZWN0SW5zdGFuY2UuYXR0ckFybl0sXG4gICAgICB9KSxcbiAgICApO1xuXG4gICAgLy8gUmVjaXByb2NhbCBDdXN0b21lciBQcm9maWxlcyBpbnRlZ3JhdGlvbiBvbiBUSElTIGRvbWFpbiBvbmx5IChJQU0gcHJlZml4XG4gICAgLy8gZm9yIEN1c3RvbWVyIFByb2ZpbGVzIGlzIGBwcm9maWxlYCkuIEdldERvbWFpbiBpcyByZWFkLW9ubHkgYW5kIHJlcXVpcmVkXG4gICAgLy8gYmVjYXVzZSBQdXRDb25uZWN0SW5zdGFuY2VJbnRlZ3JhdGlvbiB2YWxpZGF0ZXMgdGhlIEN1c3RvbWVyIFByb2ZpbGVzXG4gICAgLy8gZG9tYWluIGJlZm9yZSB3aXJpbmcgdGhlIGludGVncmF0aW9uLlxuICAgIGNvbnN0IGRvbWFpbkFybiA9IEFybi5mb3JtYXQoXG4gICAgICB7XG4gICAgICAgIHNlcnZpY2U6ICdwcm9maWxlJyxcbiAgICAgICAgcmVzb3VyY2U6ICdkb21haW5zJyxcbiAgICAgICAgcmVzb3VyY2VOYW1lOiBkb21haW5OYW1lLFxuICAgICAgICBhcm5Gb3JtYXQ6IEFybkZvcm1hdC5TTEFTSF9SRVNPVVJDRV9OQU1FLFxuICAgICAgfSxcbiAgICAgIHN0YWNrLFxuICAgICk7XG4gICAgYXNzb2NpYXRpb25Gbi5hZGRUb1JvbGVQb2xpY3koXG4gICAgICBuZXcgaWFtLlBvbGljeVN0YXRlbWVudCh7XG4gICAgICAgIGVmZmVjdDogaWFtLkVmZmVjdC5BTExPVyxcbiAgICAgICAgYWN0aW9uczogW1xuICAgICAgICAgICdwcm9maWxlOlB1dEludGVncmF0aW9uJyxcbiAgICAgICAgICAncHJvZmlsZTpEZWxldGVJbnRlZ3JhdGlvbicsXG4gICAgICAgICAgJ3Byb2ZpbGU6R2V0RG9tYWluJyxcbiAgICAgICAgXSxcbiAgICAgICAgcmVzb3VyY2VzOiBbXG4gICAgICAgICAgZG9tYWluQXJuLFxuICAgICAgICAgIEFybi5mb3JtYXQoXG4gICAgICAgICAgICB7XG4gICAgICAgICAgICAgIHNlcnZpY2U6ICdwcm9maWxlJyxcbiAgICAgICAgICAgICAgcmVzb3VyY2U6ICdkb21haW5zJyxcbiAgICAgICAgICAgICAgcmVzb3VyY2VOYW1lOiBgJHtkb21haW5OYW1lfS9pbnRlZ3JhdGlvbnMvKmAsXG4gICAgICAgICAgICAgIGFybkZvcm1hdDogQXJuRm9ybWF0LlNMQVNIX1JFU09VUkNFX05BTUUsXG4gICAgICAgICAgICB9LFxuICAgICAgICAgICAgc3RhY2ssXG4gICAgICAgICAgKSxcbiAgICAgICAgXSxcbiAgICAgIH0pLFxuICAgICk7XG4gICAgLy8gUHV0Q29ubmVjdEluc3RhbmNlSW50ZWdyYXRpb24gdmFsaWRhdGVzIHRoZSBmb3VyIGJ1aWx0LWluIGNhbXBhaWduXG4gICAgLy8gb2JqZWN0LXR5cGUgVEVNUExBVEVTIHJlZmVyZW5jZWQgYnkgb2JqZWN0VHlwZU5hbWVzIChDYW1wYWlnbi1FbWFpbCxcbiAgICAvLyBDYW1wYWlnbi1TTVMsIENhbXBhaWduLVRlbGVwaG9ueSwgQ2FtcGFpZ24tT3JjaGVzdHJhdGlvbikgdmlhXG4gICAgLy8gcHJvZmlsZTpHZXRQcm9maWxlT2JqZWN0VHlwZVRlbXBsYXRlLCB1c2luZyB0aGUgQ0FMTEVSJ3MgY3JlZGVudGlhbHMuXG4gICAgLy8gVGVtcGxhdGVzIGFyZSBhY2NvdW50L3JlZ2lvbi1sZXZlbCByZXNvdXJjZXMgd2hvc2UgQVJOIGNhcnJpZXMgYSBsZWFkaW5nXG4gICAgLy8gc2xhc2ggKGA6L3RlbXBsYXRlcy88bmFtZT5gKSwgZGlzdGluY3QgZnJvbSB0aGUgZG9tYWluLCBzbyB0aGV5IG5lZWQgdGhlaXJcbiAgICAvLyBvd24gZ3JhbnQuXG4gICAgY29uc3QgcHJvZmlsZVRlbXBsYXRlc0FybiA9IGBhcm46JHtzdGFjay5wYXJ0aXRpb259OnByb2ZpbGU6JHtzdGFjay5yZWdpb259OiR7c3RhY2suYWNjb3VudH06L3RlbXBsYXRlcy8qYDtcbiAgICBhc3NvY2lhdGlvbkZuLmFkZFRvUm9sZVBvbGljeShcbiAgICAgIG5ldyBpYW0uUG9saWN5U3RhdGVtZW50KHtcbiAgICAgICAgZWZmZWN0OiBpYW0uRWZmZWN0LkFMTE9XLFxuICAgICAgICBhY3Rpb25zOiBbXG4gICAgICAgICAgJ3Byb2ZpbGU6R2V0UHJvZmlsZU9iamVjdFR5cGVUZW1wbGF0ZScsXG4gICAgICAgICAgJ3Byb2ZpbGU6TGlzdFByb2ZpbGVPYmplY3RUeXBlVGVtcGxhdGVzJyxcbiAgICAgICAgXSxcbiAgICAgICAgcmVzb3VyY2VzOiBbcHJvZmlsZVRlbXBsYXRlc0Fybl0sXG4gICAgICB9KSxcbiAgICApO1xuICAgIC8vIE9uYm9hcmRpbmcgYXV0by1jcmVhdGVzIHRoZSBPdXRib3VuZCBDYW1wYWlnbnMgc2VydmljZS1saW5rZWQgcm9sZTsgdGhlXG4gICAgLy8gaGFuZGxlciB0aGVuIGxpc3RzIHJvbGVzIHRvIHJlc29sdmUgaXRzIEFSTi4gaWFtOkxpc3RSb2xlcyBoYXMgbm9cbiAgICAvLyByZXNvdXJjZS1sZXZlbCBzY29waW5nLCBzbyBpdCBtdXN0IHRhcmdldCBgKmAuXG4gICAgYXNzb2NpYXRpb25Gbi5hZGRUb1JvbGVQb2xpY3koXG4gICAgICBuZXcgaWFtLlBvbGljeVN0YXRlbWVudCh7XG4gICAgICAgIGVmZmVjdDogaWFtLkVmZmVjdC5BTExPVyxcbiAgICAgICAgYWN0aW9uczogWydpYW06TGlzdFJvbGVzJ10sXG4gICAgICAgIHJlc291cmNlczogWycqJ10sXG4gICAgICB9KSxcbiAgICApO1xuICAgIC8vIEVhY2ggU3RhcnRJbnN0YW5jZU9uYm9hcmRpbmdKb2IgZm9yIGEgTkVXIENvbm5lY3QgaW5zdGFuY2UgY3JlYXRlcyBpdHMgb3duXG4gICAgLy8gY29ubmVjdC1jYW1wYWlnbnMgc2VydmljZS1saW5rZWQgcm9sZSAodGhlIHJvbGUgbmFtZSBjYXJyaWVzIGFcbiAgICAvLyBzZXJ2aWNlLWdlbmVyYXRlZCByYW5kb20gc3VmZml4KSwgc28gdGhlIGdyYW50IG11c3QgdXNlIGAqYCBmb3IgdGhlXG4gICAgLy8gcmVzb3VyY2UgYW5kIGlzIGluc3RlYWQgc2NvcGVkIHNhZmVseSBieSB0aGUgaWFtOkFXU1NlcnZpY2VOYW1lIGNvbmRpdGlvblxuICAgIC8vIHRvIE9OTFkgdGhlIGNvbm5lY3QtY2FtcGFpZ25zIFNMUi4gQSBwYXRoLXNjb3BlZCByZXNvdXJjZSBBUk4gaXMgZGVuaWVkIGJ5XG4gICAgLy8gSUFNIGhlcmUgKHN1cmZhY2VkIGFzIHRoZSBvbmJvYXJkaW5nIGpvYidzIElBTV9BQ0NFU1NfREVOSUVEIGZhaWx1cmVDb2RlKS5cbiAgICBjb25zdCBjYW1wYWlnbnNTbHJBcm4gPSBgYXJuOiR7c3RhY2sucGFydGl0aW9ufTppYW06OiR7c3RhY2suYWNjb3VudH06cm9sZS9hd3Mtc2VydmljZS1yb2xlLyR7Q09OTkVDVF9DQU1QQUlHTlNfU0VSVklDRV9OQU1FfS8qYDtcbiAgICBhc3NvY2lhdGlvbkZuLmFkZFRvUm9sZVBvbGljeShcbiAgICAgIG5ldyBpYW0uUG9saWN5U3RhdGVtZW50KHtcbiAgICAgICAgZWZmZWN0OiBpYW0uRWZmZWN0LkFMTE9XLFxuICAgICAgICBhY3Rpb25zOiBbJ2lhbTpDcmVhdGVTZXJ2aWNlTGlua2VkUm9sZSddLFxuICAgICAgICByZXNvdXJjZXM6IFsnKiddLFxuICAgICAgICBjb25kaXRpb25zOiB7XG4gICAgICAgICAgU3RyaW5nRXF1YWxzOiB7XG4gICAgICAgICAgICAnaWFtOkFXU1NlcnZpY2VOYW1lJzogQ09OTkVDVF9DQU1QQUlHTlNfU0VSVklDRV9OQU1FLFxuICAgICAgICAgIH0sXG4gICAgICAgIH0sXG4gICAgICB9KSxcbiAgICApO1xuICAgIGFzc29jaWF0aW9uRm4uYWRkVG9Sb2xlUG9saWN5KFxuICAgICAgbmV3IGlhbS5Qb2xpY3lTdGF0ZW1lbnQoe1xuICAgICAgICBlZmZlY3Q6IGlhbS5FZmZlY3QuQUxMT1csXG4gICAgICAgIGFjdGlvbnM6IFsnaWFtOlBhc3NSb2xlJ10sXG4gICAgICAgIHJlc291cmNlczogW2NhbXBhaWduc1NsckFybl0sXG4gICAgICB9KSxcbiAgICApO1xuICAgIC8vIER1cmluZyBvbmJvYXJkaW5nLCBjb25uZWN0LWNhbXBhaWducyBjdXN0b21pemVzIGl0cyBzZXJ2aWNlLWxpbmtlZCByb2xlXG4gICAgLy8gd2l0aCBpbnN0YW5jZS1zcGVjaWZpYyBpbmxpbmUgcGVybWlzc2lvbnMgKHRoZSBcImFkZGl0aW9uYWwgcGVybWlzc2lvbnNcbiAgICAvLyBhZGRlZCBmb3IgdGhlIHNlcnZpY2UtbGlua2VkIHJvbGUgdG8gYWNjZXNzIHRoZSByZXNvdXJjZXNcIiwgaW5jbC4gdGhlXG4gICAgLy8gQ3VzdG9tZXIgUHJvZmlsZXMgaW50ZWdyYXRpb24gYWNjZXNzKSB2aWEgaWFtOlB1dFJvbGVQb2xpY3kgL1xuICAgIC8vIGlhbTpBdHRhY2hSb2xlUG9saWN5LCBwZXJmb3JtZWQgd2l0aCB0aGUgQ0FMTEVSJ3MgY3JlZGVudGlhbHMuIFdpdGhvdXRcbiAgICAvLyB0aGVzZSB0aGUgb25ib2FyZGluZyBqb2IgZmFpbHMgd2l0aCBmYWlsdXJlQ29kZSBJQU1fQUNDRVNTX0RFTklFRC4gVGhlXG4gICAgLy8gcmV2ZXJzZSAoRGVsZXRlL0RldGFjaCkgbWlycm9ycyB0aGlzIHNvIHRoZSBiZXN0LWVmZm9ydCB0ZWFyZG93biBjYW5cbiAgICAvLyByZW1vdmUgdGhvc2UgaW5saW5lIHBlcm1pc3Npb25zIHdoZW4gZGlzYXNzb2NpYXRpbmcuIFNjb3BlZCB0byBPTkxZIHRoZVxuICAgIC8vIGNvbm5lY3QtY2FtcGFpZ25zIHNlcnZpY2UtbGlua2VkIHJvbGUuXG4gICAgYXNzb2NpYXRpb25Gbi5hZGRUb1JvbGVQb2xpY3koXG4gICAgICBuZXcgaWFtLlBvbGljeVN0YXRlbWVudCh7XG4gICAgICAgIGVmZmVjdDogaWFtLkVmZmVjdC5BTExPVyxcbiAgICAgICAgYWN0aW9uczogW1xuICAgICAgICAgICdpYW06UHV0Um9sZVBvbGljeScsXG4gICAgICAgICAgJ2lhbTpBdHRhY2hSb2xlUG9saWN5JyxcbiAgICAgICAgICAnaWFtOkRlbGV0ZVJvbGVQb2xpY3knLFxuICAgICAgICAgICdpYW06RGV0YWNoUm9sZVBvbGljeScsXG4gICAgICAgIF0sXG4gICAgICAgIHJlc291cmNlczogW2NhbXBhaWduc1NsckFybl0sXG4gICAgICB9KSxcbiAgICApO1xuICAgIC8vIE9uYm9hcmRpbmcgYWxzbyBwcm92aXNpb25zIGEgbWFuYWdlZCBFdmVudEJyaWRnZSBydWxlIChuYW1lZFxuICAgIC8vIGBDb25uZWN0Q2FtcGFpZ25zUnVsZSpgKSB0aGF0IGRyaXZlcyBjYW1wYWlnbiBldmVudCBkZWxpdmVyeSwgY3JlYXRlZCB3aXRoXG4gICAgLy8gdGhlIENBTExFUidzIGNyZWRlbnRpYWxzLiBXaXRob3V0IHRoZXNlIHRoZSBvbmJvYXJkaW5nIGpvYiBmYWlscyB3aXRoXG4gICAgLy8gZmFpbHVyZUNvZGUgRVZFTlRfQlJJREdFX0FDQ0VTU19ERU5JRUQuIExpc3RSdWxlcyBoYXMgbm8gcmVzb3VyY2UtbGV2ZWxcbiAgICAvLyBzY29waW5nOyB0aGUgbXV0YXRpbmcgYWN0aW9ucyBhcmUgc2NvcGVkIHRvIHRoZSBDb25uZWN0Q2FtcGFpZ25zUnVsZSogbmFtZS5cbiAgICBjb25zdCBjb25uZWN0Q2FtcGFpZ25zUnVsZUFybiA9IGBhcm46JHtzdGFjay5wYXJ0aXRpb259OmV2ZW50czoke3N0YWNrLnJlZ2lvbn06JHtzdGFjay5hY2NvdW50fTpydWxlL0Nvbm5lY3RDYW1wYWlnbnNSdWxlKmA7XG4gICAgYXNzb2NpYXRpb25Gbi5hZGRUb1JvbGVQb2xpY3koXG4gICAgICBuZXcgaWFtLlBvbGljeVN0YXRlbWVudCh7XG4gICAgICAgIGVmZmVjdDogaWFtLkVmZmVjdC5BTExPVyxcbiAgICAgICAgYWN0aW9uczogWydldmVudHM6TGlzdFJ1bGVzJ10sXG4gICAgICAgIHJlc291cmNlczogWycqJ10sXG4gICAgICB9KSxcbiAgICApO1xuICAgIGFzc29jaWF0aW9uRm4uYWRkVG9Sb2xlUG9saWN5KFxuICAgICAgbmV3IGlhbS5Qb2xpY3lTdGF0ZW1lbnQoe1xuICAgICAgICBlZmZlY3Q6IGlhbS5FZmZlY3QuQUxMT1csXG4gICAgICAgIGFjdGlvbnM6IFtcbiAgICAgICAgICAnZXZlbnRzOlB1dFJ1bGUnLFxuICAgICAgICAgICdldmVudHM6UHV0VGFyZ2V0cycsXG4gICAgICAgICAgJ2V2ZW50czpEZWxldGVSdWxlJyxcbiAgICAgICAgICAnZXZlbnRzOlJlbW92ZVRhcmdldHMnLFxuICAgICAgICAgICdldmVudHM6TGlzdFRhcmdldHNCeVJ1bGUnLFxuICAgICAgICAgICdldmVudHM6RGVzY3JpYmVSdWxlJyxcbiAgICAgICAgXSxcbiAgICAgICAgcmVzb3VyY2VzOiBbY29ubmVjdENhbXBhaWduc1J1bGVBcm5dLFxuICAgICAgfSksXG4gICAgKTtcblxuICAgIGNvbnN0IHByb3ZpZGVyID0gbmV3IFByb3ZpZGVyKHRoaXMsICdDYW1wYWlnbkFzc29jaWF0aW9uUHJvdmlkZXInLCB7XG4gICAgICBvbkV2ZW50SGFuZGxlcjogYXNzb2NpYXRpb25GbixcbiAgICB9KTtcblxuICAgIGNvbnN0IGFzc29jaWF0aW9uID0gbmV3IEN1c3RvbVJlc291cmNlKHRoaXMsICdDYW1wYWlnbkFzc29jaWF0aW9uJywge1xuICAgICAgc2VydmljZVRva2VuOiBwcm92aWRlci5zZXJ2aWNlVG9rZW4sXG4gICAgICByZXNvdXJjZVR5cGU6ICdDdXN0b206Ok91dGJvdW5kQ2FtcGFpZ25zRG9tYWluQXNzb2NpYXRpb24nLFxuICAgICAgcHJvcGVydGllczoge1xuICAgICAgICBDb25uZWN0SW5zdGFuY2VJZDogY29ubmVjdEluc3RhbmNlLmF0dHJJZCxcbiAgICAgICAgRG9tYWluTmFtZTogZG9tYWluTmFtZSxcbiAgICAgICAgQWNjb3VudDogc3RhY2suYWNjb3VudCxcbiAgICAgICAgUmVnaW9uOiBzdGFjay5yZWdpb24sXG4gICAgICB9LFxuICAgIH0pO1xuXG4gICAgLy8gUHJvdmlzaW9uIGFmdGVyIOKAlCBhbmQgdGVhciBkb3duIGJlZm9yZSDigJQgdGhlIGluc3RhbmNlICsgZG9tYWluIGl0IGxpbmtzLlxuICAgIGFzc29jaWF0aW9uLm5vZGUuYWRkRGVwZW5kZW5jeShjb25uZWN0SW5zdGFuY2UpO1xuICAgIGFzc29jaWF0aW9uLm5vZGUuYWRkRGVwZW5kZW5jeShwcm9maWxlc0RvbWFpbik7XG4gIH1cblxuICAvKipcbiAgICogRGV0ZXJtaW5pc3RpYywgc3RhYmxlLCBwZXItYXBwIGJhc2UgbmFtZSBmb3IgdGhlIGNyZWF0ZWQgQ29ubmVjdCBpbnN0YW5jZVxuICAgKiBhbmQgQ3VzdG9tZXIgUHJvZmlsZXMgZG9tYWluIChjcmVhdGUtZnJvbS1zY3JhdGNoIG1vZGUpLlxuICAgKlxuICAgKiBUaGUgbmFtZSBtdXN0IGJlIFNUQUJMRSBhY3Jvc3MgZGVwbG95IGFuZCBkZWxldGUgKHNvIENGTiByZXNvbHZlcyB0aGUgc2FtZVxuICAgKiByZXNvdXJjZSBvbiB0ZWFyZG93bikgYW5kIFVOSVFVRSBwZXIgYXBwIC8gZW52aXJvbm1lbnQgKHNvIHR3byBhcHBzIGluIHRoZVxuICAgKiBzYW1lIGFjY291bnQgZG9uJ3QgY29sbGlkZSBvbiB0aGUgZ2xvYmFsbHktdW5pcXVlIENvbm5lY3QgYWxpYXMpLiBJdCBpc1xuICAgKiBkZXJpdmVkIGZyb20gYSBzaG9ydCBoYXNoIG9mIHRoZSByb290IHN0YWNrIG5hbWUgKHdoaWNoLCBpbiBBbXBsaWZ5IEdlbjIsXG4gICAqIGVtYmVkcyB0aGUgYXBwIG5hbWVzcGFjZSArIGJyYW5jaCkgcGx1cyB0aGlzIGNvbnN0cnVjdCdzIHBhdGgg4oCUIGJvdGggc3RhYmxlXG4gICAqIGlucHV0cy5cbiAgICpcbiAgICogVGhlIGBhbWF6b24tY29ubmVjdC1gIHByZWZpeCBpcyBSRVFVSVJFRCwgbm90IGNvc21ldGljOiBBbWF6b24gQ29ubmVjdCdzXG4gICAqIEFXUy1tYW5hZ2VkIHNlcnZpY2UtbGlua2VkLXJvbGUgcG9saWN5IChgQW1hem9uQ29ubmVjdFNlcnZpY2VMaW5rZWRSb2xlUG9saWN5YCxcbiAgICogc3RhdGVtZW50IGBBbGxvd0N1c3RvbWVyUHJvZmlsZXNGb3JDb25uZWN0RG9tYWluYCkgZ3JhbnRzIHRoZSBpbnN0YW5jZSdzXG4gICAqIHJvbGUgYHByb2ZpbGU6KmAgT05MWSBvbiBgYXJuOmF3czpwcm9maWxlOio6Kjpkb21haW5zL2FtYXpvbi1jb25uZWN0LSpgLiBBXG4gICAqIGRvbWFpbiBuYW1lZCBvdGhlcndpc2UgaXMgdW5yZWFjaGFibGUgYnkgdGhlIGluc3RhbmNlIOKAlCB0aGUgQ29ubmVjdCBjb25zb2xlXG4gICAqIGFuZCBKb3VybmV5IHNlZ21lbnQgYnVpbGRlciB0aGVuIHJlcG9ydCBcImRvZXMgbm90IGhhdmUgcGVybWlzc2lvbnMgdG8gYWNjZXNzXG4gICAqIEN1c3RvbWVyIFByb2ZpbGVzXCIuIFRoZSBTTFIgaXMgQVdTLXByb3RlY3RlZCAobm8gaW5saW5lIHBvbGljeSBjYW4gYmUgYWRkZWQpXG4gICAqIGFuZCB0aGUgbWFuYWdlZCBwb2xpY3kgaXMgbm90IGVkaXRhYmxlLCBzbyB0aGUgZG9tYWluIG5hbWUgaXMgdGhlIG9ubHkgbGV2ZXIuXG4gICAqL1xuICBwcml2YXRlIGdlbmVyYXRlUmVzb3VyY2VOYW1lKCk6IHN0cmluZyB7XG4gICAgbGV0IHJvb3Q6IFN0YWNrID0gdGhpcy5zdGFjaztcbiAgICB3aGlsZSAocm9vdC5uZXN0ZWRTdGFja1BhcmVudCkge1xuICAgICAgcm9vdCA9IHJvb3QubmVzdGVkU3RhY2tQYXJlbnQ7XG4gICAgfVxuICAgIGNvbnN0IHNlZWQgPSBgJHtyb290LnN0YWNrTmFtZX06OiR7dGhpcy5ub2RlLnBhdGh9YDtcbiAgICBjb25zdCBzdWZmaXggPSBjcmVhdGVIYXNoKCdzaGEyNTYnKS51cGRhdGUoc2VlZCkuZGlnZXN0KCdoZXgnKS5zbGljZSgwLCAxMik7XG4gICAgcmV0dXJuIGBhbWF6b24tY29ubmVjdC1ub3RpZmljYXRpb25zLSR7c3VmZml4fWA7XG4gIH1cblxuICAvKipcbiAgICogQ29lcmNlIGFuIGFyYml0cmFyeSBzdHJpbmcgaW50byBhIHZhbGlkIEFtYXpvbiBDb25uZWN0IGluc3RhbmNlIGFsaWFzOlxuICAgKiBsb3dlcmNhc2UgYWxwaGFudW1lcmljIGNoYXJhY3RlcnMgKyBzaW5nbGUgaHlwaGVucywgbm8gbGVhZGluZy90cmFpbGluZyBoeXBoZW4sIG5vdFxuICAgKiBzdGFydGluZyB3aXRoIHRoZSByZXNlcnZlZCBgZC1gIHByZWZpeCwgYW5kIHdpdGhpbiBDb25uZWN0J3MgMeKAkzYyIGNoYXJcbiAgICogYm91bmQuXG4gICAqL1xuICBwcml2YXRlIHNhbml0aXplSW5zdGFuY2VBbGlhcyhyYXc6IHN0cmluZyk6IHN0cmluZyB7XG4gICAgbGV0IGFsaWFzID0gcmF3XG4gICAgICAudG9Mb3dlckNhc2UoKVxuICAgICAgLnJlcGxhY2UoL1teYS16MC05LV0vZywgJy0nKVxuICAgICAgLnJlcGxhY2UoLy0rL2csICctJylcbiAgICAgIC5yZXBsYWNlKC9eLSt8LSskL2csICcnKTtcbiAgICBpZiAoYWxpYXMuc3RhcnRzV2l0aCgnZC0nKSkge1xuICAgICAgYWxpYXMgPSBgYSR7YWxpYXN9YDtcbiAgICB9XG4gICAgaWYgKGFsaWFzLmxlbmd0aCA9PT0gMCkge1xuICAgICAgYWxpYXMgPSAnYW1wbGlmeS1ub3RpZmljYXRpb25zJztcbiAgICB9XG4gICAgcmV0dXJuIGFsaWFzLnNsaWNlKDAsIDYyKS5yZXBsYWNlKC8tKyQvZywgJycpO1xuICB9XG59XG4iXX0=
|