@scloud/cdk-patterns
Version:
Serverless CDK patterns for common infrastructure needs
171 lines • 27.7 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.WebRoutes = void 0;
const constructs_1 = require("constructs");
const aws_cdk_lib_1 = require("aws-cdk-lib");
const aws_certificatemanager_1 = require("aws-cdk-lib/aws-certificatemanager");
const aws_route53_targets_1 = require("aws-cdk-lib/aws-route53-targets");
const aws_cloudfront_origins_1 = require("aws-cdk-lib/aws-cloudfront-origins");
const aws_cloudfront_1 = require("aws-cdk-lib/aws-cloudfront");
const aws_apigateway_1 = require("aws-cdk-lib/aws-apigateway");
const aws_route53_1 = require("aws-cdk-lib/aws-route53");
const PrivateBucket_1 = require("./PrivateBucket");
const GithubActions_1 = require("./GithubActions");
const RedirectWww_1 = require("./RedirectWww");
/**
* Builds a web application, backed by Lambda functions that serve specific routes (https://github.com/cdk-patterns/serverless/blob/main/the-lambda-trilogy/README.md)
*
* NB This will create an s3 bucket to serve static content and the bucket will be automatically deleted, including all contents, when this construct is deleted, on the basis the contents are assumed to be produced by a CI build.
*
* This construct can also be used for a Web API.
*
* NB us-east-1 is required for Cloudfront certificates:
* https://docs.aws.amazon.com/cdk/api/v1/docs/aws-cloudfront-readme.html
*
*/
class WebRoutes extends constructs_1.Construct {
constructor(scope, id, props) {
super(scope, id);
this.routes = {};
this.origins = {};
this.apis = {};
const domainName = props.domainName || props.zone.zoneName;
// We consider the objects in the bucket to be expendable because
// they're most likely static content we generate from source code (rather than user data).
this.bucket = PrivateBucket_1.PrivateBucket.expendable(scope, `${id}Static`);
(0, GithubActions_1.githubActions)(scope).addGhaBucket(id, this.bucket);
this.certificate = new aws_certificatemanager_1.DnsValidatedCertificate(scope, `${id}Certificate`, {
domainName,
hostedZone: props.zone,
region: 'us-east-1',
subjectAlternativeNames: props.redirectWww !== false ? [`www.${domainName}`] : undefined,
});
const rootObject = typeof props.defaultIndex === 'string' ? props.defaultIndex : 'index.html';
// This enables us to separate out the defaultBehavior props (if any) from the distributionProps (if provided)
// See https://stackoverflow.com/a/34710102/723506 for an explanation of this destructuring
const { defaultBehavior, ...distributionProps } = props.distributionProps || {};
this.distribution = new aws_cloudfront_1.Distribution(scope, `${id}Distribution`, {
domainNames: [domainName],
comment: domainName,
defaultRootObject: props.defaultIndex === false ? undefined : rootObject,
defaultBehavior: {
// All requests that aren't known to the API go to s3.
// This serves static content and also handles spam traffic.
// There are lots of probes for Wordpress installations so this largely avoids invoking lambdas in response to those.
origin: aws_cloudfront_origins_1.S3BucketOrigin.withOriginAccessControl(this.bucket),
viewerProtocolPolicy: aws_cloudfront_1.ViewerProtocolPolicy.REDIRECT_TO_HTTPS,
...defaultBehavior,
},
certificate: this.certificate,
errorResponses: props.errorResponses,
...distributionProps,
});
(0, GithubActions_1.githubActions)(scope).addGhaDistribution(id, this.distribution);
// DNS record for the distribution
new aws_route53_1.ARecord(scope, `${id}ARecord`, {
zone: props.zone,
recordName: domainName,
target: aws_route53_1.RecordTarget.fromAlias(new aws_route53_targets_1.CloudFrontTarget(this.distribution)),
});
if (props.redirectWww !== false) {
// Redirect www -> zone root
new RedirectWww_1.RedirectWww(scope, id, { zone: props.zone, certificate: this.certificate });
}
}
/**
* Add multiple routes to the web app.
*
* NB AWS has a soft limit of 25 origins per distribution.
* If you need more than this you'll need to request a quota increate wia the AWS console.
*/
addRoutes(routes) {
Object.keys(routes).forEach((pathPattern) => {
this.addRoute(pathPattern, routes[pathPattern]);
});
}
/**
* Add a route to the web app.
*
* NB AWS has a soft limit of 25 origins per distribution.
* If you need more than this you'll need to request a quota increate wia the AWS console.
*/
addRoute(pathPattern, handler, lambdaRestApiProps) {
// Look for an existing origin for this handler.
// This is useful if you need to map several path patterns to the same lambda, perhaps while refactoring an application.
// AWS has a limit on the number of origins per distribution so this helps us keep within that limit.
let origin = this.origins[handler.node.id];
// Create a new origin if we don't have one already
if (!origin) {
let props = {
restApiName: `${aws_cdk_lib_1.Stack.of(this).stackName}-${handler.node.id}`,
handler,
proxy: true,
description: `${aws_cdk_lib_1.Stack.of(this).stackName} ${handler.node.id}-${pathPattern}`,
...lambdaRestApiProps,
};
// Add a Cognito authorizer, if configured
if (this.authorizer) {
props = {
...props,
defaultMethodOptions: {
authorizationType: aws_apigateway_1.AuthorizationType.COGNITO,
authorizer: this.authorizer,
},
};
}
// Create the API gateway
const api = new aws_apigateway_1.LambdaRestApi(this, `${handler.node.id}Handler`, props);
this.apis[handler.node.id] = api;
// Create an origin for the Cloudfront distribution
origin = new aws_cloudfront_origins_1.RestApiOrigin(api);
this.origins[handler.node.id] = origin;
}
// TODO add a secret so only Cludfront can access APIg?
// Add the route (pathPattern) to the distribution
this.distribution.addBehavior(pathPattern, origin, {
allowedMethods: aws_cloudfront_1.AllowedMethods.ALLOW_ALL,
viewerProtocolPolicy: aws_cloudfront_1.ViewerProtocolPolicy.REDIRECT_TO_HTTPS,
cachePolicy: aws_cloudfront_1.CachePolicy.CACHING_DISABLED, // Assume dynamic content
originRequestPolicy: aws_cloudfront_1.OriginRequestPolicy.ALL_VIEWER_EXCEPT_HOST_HEADER,
});
this.routes[pathPattern] = handler;
}
/**
* Not yet implemented!
*/
addAuthorizer(cognitoPool) {
this.cognitoPool = cognitoPool;
this.authorizer = new aws_apigateway_1.CognitoUserPoolsAuthorizer(this, 'auth', {
cognitoUserPools: [cognitoPool],
});
// lambdaRestApiProps = {
// ...lambdaRestApiProps,
// defaultMethodOptions: {
// authorizationType: AuthorizationType.COGNITO,
// authorizer,
// },
// };
throw new Error('Not yet implemented');
}
/**
* Builds a WebRoutes construct with multiple routes, based on a set of pre-built Lambda functions.
*
* This is useful if your routes use different runtimes, environment variables an/or function properties.
*
* @param webRoutesProps Properties to configure the WebRoutes construct
* @param lambdaRestApiProps (optional) Properties to configure the LambdaRestApis that will route to your functions
*/
static routes(scope, id, routes, webRoutesProps, lambdaRestApiProps = {}) {
const webRoutes = new WebRoutes(scope, id, {
defaultIndex: false,
redirectWww: true,
...webRoutesProps,
});
Object.keys(routes).forEach((pathPattern) => {
webRoutes.addRoute(pathPattern, routes[pathPattern], lambdaRestApiProps);
});
return webRoutes;
}
}
exports.WebRoutes = WebRoutes;
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiV2ViUm91dGVzLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vc3JjL1dlYlJvdXRlcy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUFBQSwyQ0FBdUM7QUFDdkMsNkNBQW9DO0FBQ3BDLCtFQUE2RTtBQUM3RSx5RUFBbUU7QUFFbkUsK0VBQW1GO0FBQ25GLCtEQU1vQztBQUNwQywrREFFb0M7QUFHcEMseURBQTZFO0FBQzdFLG1EQUFnRDtBQUNoRCxtREFBZ0Q7QUFDaEQsK0NBQTRDO0FBc0I1Qzs7Ozs7Ozs7OztHQVVHO0FBQ0gsTUFBYSxTQUFVLFNBQVEsc0JBQVM7SUFtQnRDLFlBQ0UsS0FBZ0IsRUFDaEIsRUFBVSxFQUNWLEtBQXFCO1FBRXJCLEtBQUssQ0FBQyxLQUFLLEVBQUUsRUFBRSxDQUFDLENBQUM7UUFmbkIsV0FBTSxHQUF5QyxFQUFFLENBQUM7UUFFbEQsWUFBTyxHQUFxQyxFQUFFLENBQUM7UUFFL0MsU0FBSSxHQUFxQyxFQUFFLENBQUM7UUFhMUMsTUFBTSxVQUFVLEdBQUcsS0FBSyxDQUFDLFVBQVUsSUFBSSxLQUFLLENBQUMsSUFBSSxDQUFDLFFBQVEsQ0FBQztRQUUzRCxpRUFBaUU7UUFDakUsMkZBQTJGO1FBQzNGLElBQUksQ0FBQyxNQUFNLEdBQUcsNkJBQWEsQ0FBQyxVQUFVLENBQUMsS0FBSyxFQUFFLEdBQUcsRUFBRSxRQUFRLENBQUMsQ0FBQztRQUM3RCxJQUFBLDZCQUFhLEVBQUMsS0FBSyxDQUFDLENBQUMsWUFBWSxDQUFDLEVBQUUsRUFBRSxJQUFJLENBQUMsTUFBTSxDQUFDLENBQUM7UUFFbkQsSUFBSSxDQUFDLFdBQVcsR0FBRyxJQUFJLGdEQUF1QixDQUFDLEtBQUssRUFBRSxHQUFHLEVBQUUsYUFBYSxFQUFFO1lBQ3hFLFVBQVU7WUFDVixVQUFVLEVBQUUsS0FBSyxDQUFDLElBQUk7WUFDdEIsTUFBTSxFQUFFLFdBQVc7WUFDbkIsdUJBQXVCLEVBQUUsS0FBSyxDQUFDLFdBQVcsS0FBSyxLQUFL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cnVjdCB9IGZyb20gJ2NvbnN0cnVjdHMnO1xuaW1wb3J0IHsgU3RhY2sgfSBmcm9tICdhd3MtY2RrLWxpYic7XG5pbXBvcnQgeyBEbnNWYWxpZGF0ZWRDZXJ0aWZpY2F0ZSB9IGZyb20gJ2F3cy1jZGstbGliL2F3cy1jZXJ0aWZpY2F0ZW1hbmFnZXInO1xuaW1wb3J0IHsgQ2xvdWRGcm9udFRhcmdldCB9IGZyb20gJ2F3cy1jZGstbGliL2F3cy1yb3V0ZTUzLXRhcmdldHMnO1xuaW1wb3J0IHsgQnVja2V0IH0gZnJvbSAnYXdzLWNkay1saWIvYXdzLXMzJztcbmltcG9ydCB7IFJlc3RBcGlPcmlnaW4sIFMzQnVja2V0T3JpZ2luIH0gZnJvbSAnYXdzLWNkay1saWIvYXdzLWNsb3VkZnJvbnQtb3JpZ2lucyc7XG5pbXBvcnQge1xuICBBbGxvd2VkTWV0aG9kcywgQ2FjaGVQb2xpY3ksIERpc3RyaWJ1dGlvbixcbiAgRGlzdHJpYnV0aW9uUHJvcHMsXG4gIEVycm9yUmVzcG9uc2UsXG4gIE9yaWdpblJlcXVlc3RQb2xpY3ksXG4gIFZpZXdlclByb3RvY29sUG9saWN5LFxufSBmcm9tICdhd3MtY2RrLWxpYi9hd3MtY2xvdWRmcm9udCc7XG5pbXBvcnQge1xuICBBdXRob3JpemF0aW9uVHlwZSwgQ29nbml0b1VzZXJQb29sc0F1dGhvcml6ZXIsIExhbWJkYVJlc3RBcGksIExhbWJkYVJlc3RBcGlQcm9wcyxcbn0gZnJvbSAnYXdzLWNkay1saWIvYXdzLWFwaWdhdGV3YXknO1xuaW1wb3J0IHsgRnVuY3Rpb24gfSBmcm9tICdhd3MtY2RrLWxpYi9hd3MtbGFtYmRhJztcbmltcG9ydCB7IFVzZXJQb29sIH0gZnJvbSAnYXdzLWNkay1saWIvYXdzLWNvZ25pdG8nO1xuaW1wb3J0IHsgQVJlY29yZCwgSUhvc3RlZFpvbmUsIFJlY29yZFRhcmdldCB9IGZyb20gJ2F3cy1jZGstbGliL2F3cy1yb3V0ZTUzJztcbmltcG9ydCB7IFByaXZhdGVCdWNrZXQgfSBmcm9tICcuL1ByaXZhdGVCdWNrZXQnO1xuaW1wb3J0IHsgZ2l0aHViQWN0aW9ucyB9IGZyb20gJy4vR2l0aHViQWN0aW9ucyc7XG5pbXBvcnQgeyBSZWRpcmVjdFd3dyB9IGZyb20gJy4vUmVkaXJlY3RXd3cnO1xuXG4vKipcbiAqIEBwYXJhbSB6b25lIFRoZSBETlMgem9uZSBmb3IgdGhpcyB3ZWIgYXBwLiBCeSBkZWZhdWx0IHRoZSBkb21haW4gbmFtZSBpcyBzZXQgdG8gdGhlIHpvbmUgbmFtZVxuICogVGhlIHR5cGUgSUhvc3RlZFpvbmUgZW5hYmxlcyBsb29rdXAgb2YgdGhlIHpvbmUgKElIb3N0ZWRab25lKSBhcyB3ZWxsIGFzIGEgem9uZSBjcmVhdGQgaW4gdGhlIHN0YWNrIChIb3N0ZWRab25lKVxuICogQHBhcmFtIGRvbWFpbiBPcHRpb25hbDogYnkgZGVmYXVsdCB0aGUgem9uZSBuYW1lIHdpbGwgYmUgdXNlZCBhcyB0aGUgRE5TIG5hbWUgZm9yIHRoZSBDbG91ZGZyb250IGRpc3RyaWJ1dGlvbiAoZS5nLiAnZXhhbXBsZS5jb20nKSBidXQgeW91IGNhbiBzcGVjaWZ5IGEgZGlmZmVyZW50IGRvbWFpbiBoZXJlIChlLmcuICdzdWJkb21haW4uZXhhbXBsZS5jb20nKS5cbiAqIEBwYXJhbSBkZWZhdWx0SW5kZXggRGVmYXVsdDogdHJ1ZS4gTWFwcyBhIHZpZXdlciByZXF1ZXN0IGZvciAnLycgdG8gYSByZXF1ZXN0IGZvciAvaW5kZXguaHRtbC5cbiAqIEBwYXJhbSB3d3dSZWRpcmVjdCBEZWZhdWx0OiB0cnVlLiBSZWRpcmVjdHMgcmVxdWVzdHMgZm9yIHd3dy4gdG8gdGhlIGJhcmUgZG9tYWluIG5hbWUsIGUuZy4gd3d3LmV4YW1wbGUuY29tLT5leGFtcGxlLmNvbSwgd3d3LnN1Yi5leGFtcGxlLmNvbS0+c3ViLmV4YW1wbGUuY29tLlxuICogQHBhcmFtIGRpc3RyaWJ1dGlvblByb3BzIEFueSBwcm9wZXJ0aWVzIGZvciB0aGUgZGlzdHJpYnV0aW9uIHlvdSdkIGxpa2UgdG8gYWRkIG9yIG92ZXJyaWRlXG4gKiBAcGFyYW0gZnVuY3Rpb25Bc3NvY2lhdGlvbiBBIENsb3VkZnJvbnQgZnVuY3Rpb24gdG8gYmUgYXNzb2NpYXRlZCB3aXRoIHRoZSBkZWZhdWx0IGJlaGF2aW9yIChzMyBzdGF0aWMgY29udGVudClcbiAqIEBwYXJhbSBkaXN0cmlidXRpb25Qcm9wcyBPcHRpb25hbDogSWYgeW91IHdhbnQgdG8gYWRkIGFkZGl0aW9uYWwgcHJvcGVydGllcyB0byB0aGUgQ2xvdWRmcm9udCBkaXN0cmlidXRpb24sIHlvdSBjYW4gcGFzcyB0aGVtIGhlcmUuXG4gKiBAcGFyYW0gZXJyb3JSZXNwb25zZXMgT3B0aW9uYWw6IElmIHlvdSB3YW50IHRvIGFkZCBjdXN0b20gZXJyb3IgcmVzcG9uc2VzIHRvIHRoZSBDbG91ZGZyb250IGRpc3RyaWJ1dGlvbiwgeW91IGNhbiBwYXNzIHRoZW0gaGVyZS5cbiAqL1xuZXhwb3J0IGludGVyZmFjZSBXZWJSb3V0ZXNQcm9wcyB7XG4gIHpvbmU6IElIb3N0ZWRab25lLFxuICBkb21haW5OYW1lPzogc3RyaW5nLFxuICBkZWZhdWx0SW5kZXg/OiBib29sZWFuIHwgc3RyaW5nLFxuICByZWRpcmVjdFd3dz86IGJvb2xlYW4sXG4gIGRpc3RyaWJ1dGlvblByb3BzPzogUGFydGlhbDxEaXN0cmlidXRpb25Qcm9wcz4sXG4gIGVycm9yUmVzcG9uc2VzPzogRXJyb3JSZXNwb25zZVtdLFxufVxuXG4vKipcbiAqIEJ1aWxkcyBhIHdlYiBhcHBsaWNhdGlvbiwgYmFja2VkIGJ5IExhbWJkYSBmdW5jdGlvbnMgdGhhdCBzZXJ2ZSBzcGVjaWZpYyByb3V0ZXMgKGh0dHBzOi8vZ2l0aHViLmNvbS9jZGstcGF0dGVybnMvc2VydmVybGVzcy9ibG9iL21haW4vdGhlLWxhbWJkYS10cmlsb2d5L1JFQURNRS5tZClcbiAqXG4gKiBOQiBUaGlzIHdpbGwgY3JlYXRlIGFuIHMzIGJ1Y2tldCB0byBzZXJ2ZSBzdGF0aWMgY29udGVudCBhbmQgdGhlIGJ1Y2tldCB3aWxsIGJlIGF1dG9tYXRpY2FsbHkgZGVsZXRlZCwgaW5jbHVkaW5nIGFsbCBjb250ZW50cywgd2hlbiB0aGlzIGNvbnN0cnVjdCBpcyBkZWxldGVkLCBvbiB0aGUgYmFzaXMgdGhlIGNvbnRlbnRzIGFyZSBhc3N1bWVkIHRvIGJlIHByb2R1Y2VkIGJ5IGEgQ0kgYnVpbGQuXG4gKlxuICogVGhpcyBjb25zdHJ1Y3QgY2FuIGFsc28gYmUgdXNlZCBmb3IgYSBXZWIgQVBJLlxuICpcbiAqIE5CIHVzLWVhc3QtMSBpcyByZXF1aXJlZCBmb3IgQ2xvdWRmcm9udCBjZXJ0aWZpY2F0ZXM6XG4gKiBodHRwczovL2RvY3MuYXdzLmFtYXpvbi5jb20vY2RrL2FwaS92MS9kb2NzL2F3cy1jbG91ZGZyb250LXJlYWRtZS5odG1sXG4gKlxuICovXG5leHBvcnQgY2xhc3MgV2ViUm91dGVzIGV4dGVuZHMgQ29uc3RydWN0IHtcbiAgbGFtYmRhOiBGdW5jdGlvbjtcblxuICBidWNrZXQ6IEJ1Y2tldDtcblxuICBkaXN0cmlidXRpb246IERpc3RyaWJ1dGlvbjtcblxuICBjZXJ0aWZpY2F0ZTogRG5zVmFsaWRhdGVkQ2VydGlmaWNhdGU7XG5cbiAgcm91dGVzOiB7IFtwYXRoUGF0dGVybjogc3RyaW5nXTogRnVuY3Rpb247IH0gPSB7fTtcblxuICBvcmlnaW5zOiB7IFtpZDogc3RyaW5nXTogUmVzdEFwaU9yaWdpbjsgfSA9IHt9O1xuXG4gIGFwaXM6IHsgW2lkOiBzdHJpbmddOiBMYW1iZGFSZXN0QXBpOyB9ID0ge307XG5cbiAgY29nbml0b1Bvb2w6IFVzZXJQb29sO1xuXG4gIGF1dGhvcml6ZXI6IENvZ25pdG9Vc2VyUG9vbHNBdXRob3JpemVyO1xuXG4gIGNvbnN0cnVjdG9yKFxuICAgIHNjb3BlOiBDb25zdHJ1Y3QsXG4gICAgaWQ6IHN0cmluZyxcbiAgICBwcm9wczogV2ViUm91dGVzUHJvcHMsXG4gICkge1xuICAgIHN1cGVyKHNjb3BlLCBpZCk7XG5cbiAgICBjb25zdCBkb21haW5OYW1lID0gcHJvcHMuZG9tYWluTmFtZSB8fCBwcm9wcy56b25lLnpvbmVOYW1lO1xuXG4gICAgLy8gV2UgY29uc2lkZXIgdGhlIG9iamVjdHMgaW4gdGhlIGJ1Y2tldCB0byBiZSBleHBlbmRhYmxlIGJlY2F1c2VcbiAgICAvLyB0aGV5J3JlIG1vc3QgbGlrZWx5IHN0YXRpYyBjb250ZW50IHdlIGdlbmVyYXRlIGZyb20gc291cmNlIGNvZGUgKHJhdGhlciB0aGFuIHVzZXIgZGF0YSkuXG4gICAgdGhpcy5idWNrZXQgPSBQcml2YXRlQnVja2V0LmV4cGVuZGFibGUoc2NvcGUsIGAke2lkfVN0YXRpY2ApO1xuICAgIGdpdGh1YkFjdGlvbnMoc2NvcGUpLmFkZEdoYUJ1Y2tldChpZCwgdGhpcy5idWNrZXQpO1xuXG4gICAgdGhpcy5jZXJ0aWZpY2F0ZSA9IG5ldyBEbnNWYWxpZGF0ZWRDZXJ0aWZpY2F0ZShzY29wZSwgYCR7aWR9Q2VydGlmaWNhdGVgLCB7XG4gICAgICBkb21haW5OYW1lLFxuICAgICAgaG9zdGVkWm9uZTogcHJvcHMuem9uZSxcbiAgICAgIHJlZ2lvbjogJ3VzLWVhc3QtMScsXG4gICAgICBzdWJqZWN0QWx0ZXJuYXRpdmVOYW1lczogcHJvcHMucmVkaXJlY3RXd3cgIT09IGZhbHNlID8gW2B3d3cuJHtkb21haW5OYW1lfWBdIDogdW5kZWZpbmVkLFxuICAgIH0pO1xuXG4gICAgY29uc3Qgcm9vdE9iamVjdCA9IHR5cGVvZiBwcm9wcy5kZWZhdWx0SW5kZXggPT09ICdzdHJpbmcnID8gcHJvcHMuZGVmYXVsdEluZGV4IDogJ2luZGV4Lmh0bWwnO1xuICAgIC8vIFRoaXMgZW5hYmxlcyB1cyB0byBzZXBhcmF0ZSBvdXQgdGhlIGRlZmF1bHRCZWhhdmlvciBwcm9wcyAoaWYgYW55KSBmcm9tIHRoZSBkaXN0cmlidXRpb25Qcm9wcyAoaWYgcHJvdmlkZWQpXG4gICAgLy8gU2VlIGh0dHBzOi8vc3RhY2tvdmVyZmxvdy5jb20vYS8zNDcxMDEwMi83MjM1MDYgZm9yIGFuIGV4cGxhbmF0aW9uIG9mIHRoaXMgZGVzdHJ1Y3R1cmluZ1xuICAgIGNvbnN0IHsgZGVmYXVsdEJlaGF2aW9yLCAuLi5kaXN0cmlidXRpb25Qcm9wcyB9ID0gcHJvcHMuZGlzdHJpYnV0aW9uUHJvcHMgfHwgKHt9IGFzIFBhcnRpYWw8RGlzdHJpYnV0aW9uUHJvcHM+KTtcbiAgICB0aGlzLmRpc3RyaWJ1dGlvbiA9IG5ldyBEaXN0cmlidXRpb24oc2NvcGUsIGAke2lkfURpc3RyaWJ1dGlvbmAsIHtcbiAgICAgIGRvbWFpbk5hbWVzOiBbZG9tYWluTmFtZV0sXG4gICAgICBjb21tZW50OiBkb21haW5OYW1lLFxuICAgICAgZGVmYXVsdFJvb3RPYmplY3Q6IHByb3BzLmRlZmF1bHRJbmRleCA9PT0gZmFsc2UgPyB1bmRlZmluZWQgOiByb290T2JqZWN0LFxuICAgICAgZGVmYXVsdEJlaGF2aW9yOiB7XG4gICAgICAgIC8vIEFsbCByZXF1ZXN0cyB0aGF0IGFyZW4ndCBrbm93biB0byB0aGUgQVBJIGdvIHRvIHMzLlxuICAgICAgICAvLyBUaGlzIHNlcnZlcyBzdGF0aWMgY29udGVudCBhbmQgYWxzbyBoYW5kbGVzIHNwYW0gdHJhZmZpYy5cbiAgICAgICAgLy8gVGhlcmUgYXJlIGxvdHMgb2YgcHJvYmVzIGZvciBXb3JkcHJlc3MgaW5zdGFsbGF0aW9ucyBzbyB0aGlzIGxhcmdlbHkgYXZvaWRzIGludm9raW5nIGxhbWJkYXMgaW4gcmVzcG9uc2UgdG8gdGhvc2UuXG4gICAgICAgIG9yaWdpbjogUzNCdWNrZXRPcmlnaW4ud2l0aE9yaWdpbkFjY2Vzc0NvbnRyb2wodGhpcy5idWNrZXQpLFxuICAgICAgICB2aWV3ZXJQcm90b2NvbFBvbGljeTogVmlld2VyUHJvdG9jb2xQb2xpY3kuUkVESVJFQ1RfVE9fSFRUUFMsXG4gICAgICAgIC4uLmRlZmF1bHRCZWhhdmlvcixcbiAgICAgIH0sXG4gICAgICBjZXJ0aWZpY2F0ZTogdGhpcy5jZXJ0aWZpY2F0ZSxcbiAgICAgIGVycm9yUmVzcG9uc2VzOiBwcm9wcy5lcnJvclJlc3BvbnNlcyxcbiAgICAgIC4uLmRpc3RyaWJ1dGlvblByb3BzLFxuICAgIH0pO1xuICAgIGdpdGh1YkFjdGlvbnMoc2NvcGUpLmFkZEdoYURpc3RyaWJ1dGlvbihpZCwgdGhpcy5kaXN0cmlidXRpb24pO1xuXG4gICAgLy8gRE5TIHJlY29yZCBmb3IgdGhlIGRpc3RyaWJ1dGlvblxuICAgIG5ldyBBUmVjb3JkKHNjb3BlLCBgJHtpZH1BUmVjb3JkYCwge1xuICAgICAgem9uZTogcHJvcHMuem9uZSxcbiAgICAgIHJlY29yZE5hbWU6IGRvbWFpbk5hbWUsXG4gICAgICB0YXJnZXQ6IFJlY29yZFRhcmdldC5mcm9tQWxpYXMobmV3IENsb3VkRnJvbnRUYXJnZXQodGhpcy5kaXN0cmlidXRpb24pKSxcbiAgICB9KTtcblxuICAgIGlmIChwcm9wcy5yZWRpcmVjdFd3dyAhPT0gZmFsc2UpIHtcbiAgICAgIC8vIFJlZGlyZWN0IHd3dyAtPiB6b25lIHJvb3RcbiAgICAgIG5ldyBSZWRpcmVjdFd3dyhzY29wZSwgaWQsIHsgem9uZTogcHJvcHMuem9uZSwgY2VydGlmaWNhdGU6IHRoaXMuY2VydGlmaWNhdGUgfSk7XG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIEFkZCBtdWx0aXBsZSByb3V0ZXMgdG8gdGhlIHdlYiBhcHAuXG4gICAqXG4gICAqIE5CIEFXUyBoYXMgYSBzb2Z0IGxpbWl0IG9mIDI1IG9yaWdpbnMgcGVyIGRpc3RyaWJ1dGlvbi5cbiAgICogSWYgeW91IG5lZWQgbW9yZSB0aGFuIHRoaXMgeW91J2xsIG5lZWQgdG8gcmVxdWVzdCBhIHF1b3RhIGluY3JlYXRlIHdpYSB0aGUgQVdTIGNvbnNvbGUuXG4gICAqL1xuICBhZGRSb3V0ZXMocm91dGVzOiB7IFtwYXRoOiBzdHJpbmddOiBGdW5jdGlvbjsgfSkge1xuICAgIE9iamVjdC5rZXlzKHJvdXRlcykuZm9yRWFjaCgocGF0aFBhdHRlcm4pID0+IHtcbiAgICAgIHRoaXMuYWRkUm91dGUocGF0aFBhdHRlcm4sIHJvdXRlc1twYXRoUGF0dGVybl0pO1xuICAgIH0pO1xuICB9XG5cbiAgLyoqXG4gICAqIEFkZCBhIHJvdXRlIHRvIHRoZSB3ZWIgYXBwLlxuICAgKlxuICAgKiBOQiBBV1MgaGFzIGEgc29mdCBsaW1pdCBvZiAyNSBvcmlnaW5zIHBlciBkaXN0cmlidXRpb24uXG4gICAqIElmIHlvdSBuZWVkIG1vcmUgdGhhbiB0aGlzIHlvdSdsbCBuZWVkIHRvIHJlcXVlc3QgYSBxdW90YSBpbmNyZWF0ZSB3aWEgdGhlIEFXUyBjb25zb2xlLlxuICAgKi9cbiAgYWRkUm91dGUocGF0aFBhdHRlcm46IHN0cmluZywgaGFuZGxlcjogRnVuY3Rpb24sIGxhbWJkYVJlc3RBcGlQcm9wcz86IFBhcnRpYWw8TGFtYmRhUmVzdEFwaVByb3BzPikge1xuICAgIC8vIExvb2sgZm9yIGFuIGV4aXN0aW5nIG9yaWdpbiBmb3IgdGhpcyBoYW5kbGVyLlxuICAgIC8vIFRoaXMgaXMgdXNlZnVsIGlmIHlvdSBuZWVkIHRvIG1hcCBzZXZlcmFsIHBhdGggcGF0dGVybnMgdG8gdGhlIHNhbWUgbGFtYmRhLCBwZXJoYXBzIHdoaWxlIHJlZmFjdG9yaW5nIGFuIGFwcGxpY2F0aW9uLlxuICAgIC8vIEFXUyBoYXMgYSBsaW1pdCBvbiB0aGUgbnVtYmVyIG9mIG9yaWdpbnMgcGVyIGRpc3RyaWJ1dGlvbiBzbyB0aGlzIGhlbHBzIHVzIGtlZXAgd2l0aGluIHRoYXQgbGltaXQuXG4gICAgbGV0IG9yaWdpbiA9IHRoaXMub3JpZ2luc1toYW5kbGVyLm5vZGUuaWRdO1xuXG4gICAgLy8gQ3JlYXRlIGEgbmV3IG9yaWdpbiBpZiB3ZSBkb24ndCBoYXZlIG9uZSBhbHJlYWR5XG4gICAgaWYgKCFvcmlnaW4pIHtcbiAgICAgIGxldCBwcm9wczogTGFtYmRhUmVzdEFwaVByb3BzID0ge1xuICAgICAgICByZXN0QXBpTmFtZTogYCR7U3RhY2sub2YodGhpcykuc3RhY2tOYW1lfS0ke2hhbmRsZXIubm9kZS5pZH1gLFxuICAgICAgICBoYW5kbGVyLFxuICAgICAgICBwcm94eTogdHJ1ZSxcbiAgICAgICAgZGVzY3JpcHRpb246IGAke1N0YWNrLm9mKHRoaXMpLnN0YWNrTmFtZX0gJHtoYW5kbGVyLm5vZGUuaWR9LSR7cGF0aFBhdHRlcm59YCxcbiAgICAgICAgLi4ubGFtYmRhUmVzdEFwaVByb3BzLFxuICAgICAgfTtcblxuICAgICAgLy8gQWRkIGEgQ29nbml0byBhdXRob3JpemVyLCBpZiBjb25maWd1cmVkXG4gICAgICBpZiAodGhpcy5hdXRob3JpemVyKSB7XG4gICAgICAgIHByb3BzID0ge1xuICAgICAgICAgIC4uLnByb3BzLFxuICAgICAgICAgIGRlZmF1bHRNZXRob2RPcHRpb25zOiB7XG4gICAgICAgICAgICBhdXRob3JpemF0aW9uVHlwZTogQXV0aG9yaXphdGlvblR5cGUuQ09HTklUTyxcbiAgICAgICAgICAgIGF1dGhvcml6ZXI6IHRoaXMuYXV0aG9yaXplcixcbiAgICAgICAgICB9LFxuICAgICAgICB9O1xuICAgICAgfVxuXG4gICAgICAvLyBDcmVhdGUgdGhlIEFQSSBnYXRld2F5XG4gICAgICBjb25zdCBhcGkgPSBuZXcgTGFtYmRhUmVzdEFwaSh0aGlzLCBgJHtoYW5kbGVyLm5vZGUuaWR9SGFuZGxlcmAsIHByb3BzKTtcbiAgICAgIHRoaXMuYXBpc1toYW5kbGVyLm5vZGUuaWRdID0gYXBpO1xuXG4gICAgICAvLyBDcmVhdGUgYW4gb3JpZ2luIGZvciB0aGUgQ2xvdWRmcm9udCBkaXN0cmlidXRpb25cbiAgICAgIG9yaWdpbiA9IG5ldyBSZXN0QXBpT3JpZ2luKGFwaSk7XG4gICAgICB0aGlzLm9yaWdpbnNbaGFuZGxlci5ub2RlLmlkXSA9IG9yaWdpbjtcbiAgICB9XG5cbiAgICAvLyBUT0RPIGFkZCBhIHNlY3JldCBzbyBvbmx5IENsdWRmcm9udCBjYW4gYWNjZXNzIEFQSWc/XG4gICAgLy8gQWRkIHRoZSByb3V0ZSAocGF0aFBhdHRlcm4pIHRvIHRoZSBkaXN0cmlidXRpb25cbiAgICB0aGlzLmRpc3RyaWJ1dGlvbi5hZGRCZWhhdmlvcihcbiAgICAgIHBhdGhQYXR0ZXJuLFxuICAgICAgb3JpZ2luLFxuICAgICAge1xuICAgICAgICBhbGxvd2VkTWV0aG9kczogQWxsb3dlZE1ldGhvZHMuQUxMT1dfQUxMLFxuICAgICAgICB2aWV3ZXJQcm90b2NvbFBvbGljeTogVmlld2VyUHJvdG9jb2xQb2xpY3kuUkVESVJFQ1RfVE9fSFRUUFMsXG4gICAgICAgIGNhY2hlUG9saWN5OiBDYWNoZVBvbGljeS5DQUNISU5HX0RJU0FCTEVELCAvLyBBc3N1bWUgZHluYW1pYyBjb250ZW50XG4gICAgICAgIG9yaWdpblJlcXVlc3RQb2xpY3k6IE9yaWdpblJlcXVlc3RQb2xpY3kuQUxMX1ZJRVdFUl9FWENFUFRfSE9TVF9IRUFERVIsXG4gICAgICB9LFxuICAgICk7XG4gICAgdGhpcy5yb3V0ZXNbcGF0aFBhdHRlcm5dID0gaGFuZGxlcjtcbiAgfVxuXG4gIC8qKlxuICAgKiBOb3QgeWV0IGltcGxlbWVudGVkIVxuICAgKi9cbiAgYWRkQXV0aG9yaXplcihjb2duaXRvUG9vbDogVXNlclBvb2wpIHtcbiAgICB0aGlzLmNvZ25pdG9Qb29sID0gY29nbml0b1Bvb2w7XG5cbiAgICB0aGlzLmF1dGhvcml6ZXIgPSBuZXcgQ29nbml0b1VzZXJQb29sc0F1dGhvcml6ZXIodGhpcywgJ2F1dGgnLCB7XG4gICAgICBjb2duaXRvVXNlclBvb2xzOiBbY29nbml0b1Bvb2xdLFxuICAgIH0pO1xuICAgIC8vIGxhbWJkYVJlc3RBcGlQcm9wcyA9IHtcbiAgICAvLyAgIC4uLmxhbWJkYVJlc3RBcGlQcm9wcyxcbiAgICAvLyAgIGRlZmF1bHRNZXRob2RPcHRpb25zOiB7XG4gICAgLy8gICAgIGF1dGhvcml6YXRpb25UeXBlOiBBdXRob3JpemF0aW9uVHlwZS5DT0dOSVRPLFxuICAgIC8vICAgICBhdXRob3JpemVyLFxuICAgIC8vICAgfSxcbiAgICAvLyB9O1xuXG4gICAgdGhyb3cgbmV3IEVycm9yKCdOb3QgeWV0IGltcGxlbWVudGVkJyk7XG4gIH1cblxuICAvKipcbiAgICogQnVpbGRzIGEgV2ViUm91dGVzIGNvbnN0cnVjdCB3aXRoIG11bHRpcGxlIHJvdXRlcywgYmFzZWQgb24gYSBzZXQgb2YgcHJlLWJ1aWx0IExhbWJkYSBmdW5jdGlvbnMuXG4gICAqXG4gICAqIFRoaXMgaXMgdXNlZnVsIGlmIHlvdXIgcm91dGVzIHVzZSBkaWZmZXJlbnQgcnVudGltZXMsIGVudmlyb25tZW50IHZhcmlhYmxlcyBhbi9vciBmdW5jdGlvbiBwcm9wZXJ0aWVzLlxuICAgKlxuICAgKiBAcGFyYW0gd2ViUm91dGVzUHJvcHMgUHJvcGVydGllcyB0byBjb25maWd1cmUgdGhlIFdlYlJvdXRlcyBjb25zdHJ1Y3RcbiAgICogQHBhcmFtIGxhbWJkYVJlc3RBcGlQcm9wcyAob3B0aW9uYWwpIFByb3BlcnRpZXMgdG8gY29uZmlndXJlIHRoZSBMYW1iZGFSZXN0QXBpcyB0aGF0IHdpbGwgcm91dGUgdG8geW91ciBmdW5jdGlvbnNcbiAgICovXG4gIHN0YXRpYyByb3V0ZXMoXG4gICAgc2NvcGU6IENvbnN0cnVjdCxcbiAgICBpZDogc3RyaW5nLFxuICAgIHJvdXRlczogeyBbcGF0aFBhdHRlcm46IHN0cmluZ106IEZ1bmN0aW9uOyB9LFxuICAgIHdlYlJvdXRlc1Byb3BzOiBXZWJSb3V0ZXNQcm9wcyxcbiAgICBsYW1iZGFSZXN0QXBpUHJvcHM6IFBhcnRpYWw8TGFtYmRhUmVzdEFwaVByb3BzPiA9IHt9LFxuICApOiBXZWJSb3V0ZXMge1xuICAgIGNvbnN0IHdlYlJvdXRlcyA9IG5ldyBXZWJSb3V0ZXMoc2NvcGUsIGlkLCB7XG4gICAgICBkZWZhdWx0SW5kZXg6IGZhbHNlLFxuICAgICAgcmVkaXJlY3RXd3c6IHRydWUsXG4gICAgICAuLi53ZWJSb3V0ZXNQcm9wcyxcbiAgICB9KTtcbiAgICBPYmplY3Qua2V5cyhyb3V0ZXMpLmZvckVhY2goKHBhdGhQYXR0ZXJuKSA9PiB7XG4gICAgICB3ZWJSb3V0ZXMuYWRkUm91dGUocGF0aFBhdHRlcm4sIHJvdXRlc1twYXRoUGF0dGVybl0sIGxhbWJkYVJlc3RBcGlQcm9wcyk7XG4gICAgfSk7XG4gICAgcmV0dXJuIHdlYlJvdXRlcztcbiAgfVxufVxuIl19