@aws-solutions-constructs/aws-apigatewayv2websocket-sqs
Version:
CDK constructs for defining an interaction between an AWS Lambda function and an Amazon S3 bucket.
502 lines • 67.5 kB
JavaScript
"use strict";
/**
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance
* with the License. A copy of the License is located at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* or in the 'license' file accompanying this file. This file is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES
* OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific language governing permissions
* and limitations under the License.
*/
Object.defineProperty(exports, "__esModule", { value: true });
const cdk = require("aws-cdk-lib");
const apigwv2 = require("aws-cdk-lib/aws-apigatewayv2");
const lambda = require("aws-cdk-lib/aws-lambda");
const core_1 = require("@aws-solutions-constructs/core");
const assertions_1 = require("aws-cdk-lib/assertions");
const aws_apigatewayv2_authorizers_1 = require("aws-cdk-lib/aws-apigatewayv2-authorizers");
const aws_apigatewayv2_integrations_1 = require("aws-cdk-lib/aws-apigatewayv2-integrations");
const lib_1 = require("../lib");
describe("When instantiating the ApiGatewayV2WebSocketToSqs construct with WebSocketApiProps", () => {
let template;
beforeAll(() => {
const app = new cdk.App();
const stack = new cdk.Stack(app, "TestStack");
const mockConnectLambda = new lambda.Function(stack, "mockConnectFunction", {
code: lambda.Code.fromAsset(`${__dirname}/lambda`),
runtime: core_1.COMMERCIAL_REGION_LAMBDA_NODE_RUNTIME,
handler: "connect.handler",
});
const mockDisconnectLambda = new lambda.Function(stack, "mockDisconnectFunction", {
code: lambda.Code.fromAsset(`${__dirname}/lambda`),
runtime: core_1.COMMERCIAL_REGION_LAMBDA_NODE_RUNTIME,
handler: "disconnect.handler",
});
new lib_1.ApiGatewayV2WebSocketToSqs(stack, "ApiGatewayV2WebSocketToSqs", {
webSocketApiProps: {
connectRouteOptions: {
integration: new aws_apigatewayv2_integrations_1.WebSocketLambdaIntegration("ConnectIntegration", mockConnectLambda),
authorizer: new aws_apigatewayv2_authorizers_1.WebSocketIamAuthorizer(),
},
disconnectRouteOptions: {
integration: new aws_apigatewayv2_integrations_1.WebSocketLambdaIntegration("DisconnectIntegration", mockDisconnectLambda),
},
},
createDefaultRoute: true,
});
template = assertions_1.Template.fromStack(stack);
});
it("should have a FIFO queue and a DLQ", () => {
template.resourceCountIs("AWS::SQS::Queue", 2);
template.hasResourceProperties("AWS::SQS::Queue", {
DeduplicationScope: "messageGroup",
FifoQueue: true,
FifoThroughputLimit: "perMessageGroupId",
RedriveAllowPolicy: {
redrivePermission: "denyAll",
},
});
template.hasResourceProperties("AWS::SQS::Queue", {
KmsMasterKeyId: "alias/aws/sqs",
FifoQueue: true,
DeduplicationScope: "messageGroup",
FifoThroughputLimit: "perMessageGroupId",
RedriveAllowPolicy: assertions_1.Match.absent(),
});
});
it("should create an instance of the WebSocket", () => {
template.hasResourceProperties("AWS::ApiGatewayV2::Api", {
Name: "WebSocketApiApiGatewayV2WebSocketToSqs",
ProtocolType: "WEBSOCKET",
RouteSelectionExpression: "$request.body.action",
});
});
it("should have a websocket api with different route options", () => {
template.resourceCountIs("AWS::ApiGatewayV2::Route", 3);
template.hasResourceProperties("AWS::ApiGatewayV2::Route", {
RouteKey: "$disconnect",
AuthorizationType: "NONE",
Target: {
"Fn::Join": ["", ["integrations/", assertions_1.Match.anyValue()]],
},
});
template.hasResourceProperties("AWS::ApiGatewayV2::Route", {
RouteKey: "$connect",
AuthorizationType: "AWS_IAM",
Target: {
"Fn::Join": ["", ["integrations/", assertions_1.Match.anyValue()]],
},
});
});
it("should have role with policy for cloudwatch, sqs, and KMS", () => {
template.hasResourceProperties("AWS::IAM::Role", {
AssumeRolePolicyDocument: {
Statement: [
{
Action: "sts:AssumeRole",
Effect: "Allow",
Principal: {
Service: "apigateway.amazonaws.com",
},
},
],
Version: "2012-10-17",
},
});
const apigatewayRoleCapture = new assertions_1.Capture();
template.hasResourceProperties("AWS::IAM::Policy", {
PolicyDocument: {
Statement: [
{
Action: ["sqs:SendMessage", "sqs:GetQueueAttributes", "sqs:GetQueueUrl"],
Effect: "Allow",
Resource: {
"Fn::GetAtt": [assertions_1.Match.anyValue(), "Arn"],
},
},
{
Action: [
"logs:CreateLogGroup",
"logs:CreateLogStream",
"logs:DescribeLogGroups",
"logs:DescribeLogStreams",
"logs:PutLogEvents",
"logs:GetLogEvents",
"logs:FilterLogEvents",
],
Effect: "Allow",
Resource: {
"Fn::GetAtt": [assertions_1.Match.anyValue(), "Arn"],
},
},
],
Version: "2012-10-17",
},
PolicyName: assertions_1.Match.anyValue(),
Roles: [
{
Ref: apigatewayRoleCapture,
},
],
});
template.hasResourceProperties("AWS::IAM::Role", {
AssumeRolePolicyDocument: {
Statement: [
{
Action: "sts:AssumeRole",
Effect: "Allow",
Principal: {
Service: "apigateway.amazonaws.com",
},
},
],
},
});
});
it("should define following types of integration", () => {
template.hasResourceProperties("AWS::ApiGatewayV2::Integration", {
IntegrationType: "AWS_PROXY",
IntegrationUri: assertions_1.Match.anyValue(),
});
});
});
describe("When the option of creating default route and custom route is provided", () => {
let template;
const routeName = "fakeroutename";
beforeAll(() => {
const app = new cdk.App();
const stack = new cdk.Stack(app, "TestStack");
const mockConnectLambda = new lambda.Function(stack, "mockConnectFunction", {
code: lambda.Code.fromAsset(`${__dirname}/lambda`),
runtime: core_1.COMMERCIAL_REGION_LAMBDA_NODE_RUNTIME,
handler: "connect.handler",
});
const mockDisconnectLambda = new lambda.Function(stack, "mockDisconnectFunction", {
code: lambda.Code.fromAsset(`${__dirname}/lambda`),
runtime: core_1.COMMERCIAL_REGION_LAMBDA_NODE_RUNTIME,
handler: "disconnect.handler",
});
new lib_1.ApiGatewayV2WebSocketToSqs(stack, "ApiGatewayV2WebSocketToSqs", {
webSocketApiProps: {
connectRouteOptions: {
integration: new aws_apigatewayv2_integrations_1.WebSocketLambdaIntegration("ConnectIntegration", mockConnectLambda),
},
disconnectRouteOptions: {
integration: new aws_apigatewayv2_integrations_1.WebSocketLambdaIntegration("DisconnectIntegration", mockDisconnectLambda),
},
},
createDefaultRoute: true,
customRouteName: routeName
});
template = assertions_1.Template.fromStack(stack);
});
it("should have the $default and custom rpute routing option", () => {
template.hasResourceProperties("AWS::ApiGatewayV2::Integration", {
ApiId: { Ref: "ApiGatewayV2WebSocketToSqsWebSocketApiApiGatewayV2WebSocketToSqs92E2576D" },
CredentialsArn: { "Fn::GetAtt": [assertions_1.Match.anyValue(), "Arn"] },
IntegrationMethod: "POST",
IntegrationType: "AWS",
IntegrationUri: { "Fn::Join": assertions_1.Match.anyValue() },
PassthroughBehavior: "NEVER",
RequestParameters: { "integration.request.header.Content-Type": "'application/x-www-form-urlencoded'" },
RequestTemplates: {
$default: core_1.DEFAULT_ROUTE_QUEUE_VTL_CONFIG,
},
TemplateSelectionExpression: "\\$default",
});
template.hasResourceProperties("AWS::ApiGatewayV2::Route", {
RouteKey: "$default",
AuthorizationType: "NONE",
Target: {
"Fn::Join": ["", ["integrations/", assertions_1.Match.anyValue()]],
},
});
template.hasResourceProperties("AWS::ApiGatewayV2::Integration", {
ApiId: { Ref: "ApiGatewayV2WebSocketToSqsWebSocketApiApiGatewayV2WebSocketToSqs92E2576D" },
CredentialsArn: { "Fn::GetAtt": [assertions_1.Match.anyValue(), "Arn"] },
IntegrationMethod: "POST",
IntegrationType: "AWS",
IntegrationUri: assertions_1.Match.anyValue(),
PassthroughBehavior: "NEVER",
RequestParameters: {
"integration.request.header.Content-Type": "'application/x-www-form-urlencoded'",
},
RequestTemplates: {
[routeName]: core_1.DEFAULT_ROUTE_QUEUE_VTL_CONFIG,
},
TemplateSelectionExpression: routeName,
});
template.hasResourceProperties("AWS::ApiGatewayV2::Route", {
ApiId: {
Ref: assertions_1.Match.anyValue(),
},
RouteKey: routeName,
Target: {
"Fn::Join": [
"",
[
"integrations/",
{
Ref: assertions_1.Match.stringLikeRegexp("ApiGatewayV2WebSocketToSqsWebSocketApiApiGatewayV2WebSocketToSqsfakeroutenameRoutefakeroutename"),
},
],
],
},
});
});
});
describe("When instantiating the construct with existing WebSocketApi", () => {
let template;
beforeAll(() => {
const app = new cdk.App();
const stack = new cdk.Stack(app, "TestStack");
const mockConnectLambda = new lambda.Function(stack, "mockConnectFunction", {
code: lambda.Code.fromAsset(`${__dirname}/lambda`),
runtime: core_1.COMMERCIAL_REGION_LAMBDA_NODE_RUNTIME,
handler: "connect.handler",
});
const mockDisconnectLambda = new lambda.Function(stack, "mockDisconnectFunction", {
code: lambda.Code.fromAsset(`${__dirname}/lambda`),
runtime: core_1.COMMERCIAL_REGION_LAMBDA_NODE_RUNTIME,
handler: "disconnect.handler",
});
new lib_1.ApiGatewayV2WebSocketToSqs(stack, "ApiGatewayV2WebSocketToSqs", {
existingWebSocketApi: new apigwv2.WebSocketApi(stack, "TestWebSocket", {
apiName: "TestWebSocket",
description: "Test WebSocket",
connectRouteOptions: {
integration: new aws_apigatewayv2_integrations_1.WebSocketLambdaIntegration("ConnectIntegration", mockConnectLambda),
authorizer: new aws_apigatewayv2_authorizers_1.WebSocketIamAuthorizer(),
},
disconnectRouteOptions: {
integration: new aws_apigatewayv2_integrations_1.WebSocketLambdaIntegration("DisconnectIntegration", mockDisconnectLambda),
},
}),
});
template = assertions_1.Template.fromStack(stack);
});
it("should not create a new websocket but use existing one", () => {
template.resourceCountIs("AWS::ApiGatewayV2::Api", 1);
template.hasResourceProperties("AWS::ApiGatewayV2::Api", {
Name: "TestWebSocket",
Description: "Test WebSocket",
});
});
it("should have 2 routes configured", () => {
template.resourceCountIs("AWS::ApiGatewayV2::Route", 2);
template.hasResourceProperties("AWS::ApiGatewayV2::Route", {
RouteKey: "$disconnect",
AuthorizationType: "NONE",
Target: {
"Fn::Join": ["", ["integrations/", assertions_1.Match.anyValue()]],
},
});
template.hasResourceProperties("AWS::ApiGatewayV2::Route", {
RouteKey: "$connect",
AuthorizationType: "AWS_IAM",
Target: {
"Fn::Join": ["", ["integrations/", assertions_1.Match.anyValue()]],
},
});
});
});
describe("When an existing instance of WebSocketApi and WebSocketApiProps both are provided", () => {
it("should throw an error", () => {
const app = new cdk.App();
const stack = new cdk.Stack(app, "TestStack");
const mockConnectLambda = new lambda.Function(stack, "mockConnectFunction", {
code: lambda.Code.fromAsset(`${__dirname}/lambda`),
runtime: core_1.COMMERCIAL_REGION_LAMBDA_NODE_RUNTIME,
handler: "connect.handler",
});
const mockDisconnectLambda = new lambda.Function(stack, "mockDisconnectFunction", {
code: lambda.Code.fromAsset(`${__dirname}/lambda`),
runtime: core_1.COMMERCIAL_REGION_LAMBDA_NODE_RUNTIME,
handler: "disconnect.handler",
});
try {
new lib_1.ApiGatewayV2WebSocketToSqs(stack, "ApiGatewayV2WebSocketToSqs", {
existingWebSocketApi: new apigwv2.WebSocketApi(stack, "TestWebSocket", {
apiName: "TestWebSocket",
description: "Test WebSocket",
connectRouteOptions: {
integration: new aws_apigatewayv2_integrations_1.WebSocketLambdaIntegration("ConnectIntegration", mockConnectLambda),
authorizer: new aws_apigatewayv2_authorizers_1.WebSocketIamAuthorizer(),
},
disconnectRouteOptions: {
integration: new aws_apigatewayv2_integrations_1.WebSocketLambdaIntegration("DisconnectIntegration", mockDisconnectLambda),
},
}),
webSocketApiProps: {
connectRouteOptions: {
integration: new aws_apigatewayv2_integrations_1.WebSocketLambdaIntegration("ConnectIntegration", mockConnectLambda),
},
disconnectRouteOptions: {
integration: new aws_apigatewayv2_integrations_1.WebSocketLambdaIntegration("DisconnectIntegration", mockDisconnectLambda),
},
},
});
}
catch (error) {
expect(error).toBeInstanceOf(Error);
expect(error.message).toEqual("Provide either an existing WebSocketApi instance or WebSocketApiProps, not both");
}
});
});
describe("When instantiating the ApiGatewayV2WebSocketToSqs construct when not setting defaultIamAuthorizer", () => {
let template;
beforeAll(() => {
const app = new cdk.App();
const stack = new cdk.Stack(app, "TestStack");
const mockDisconnectLambda = new lambda.Function(stack, "mockDisconnectFunction", {
code: lambda.Code.fromAsset(`${__dirname}/lambda`),
runtime: core_1.COMMERCIAL_REGION_LAMBDA_NODE_RUNTIME,
handler: "disconnect.handler",
});
new lib_1.ApiGatewayV2WebSocketToSqs(stack, "ApiGatewayV2WebSocketToSqs", {
webSocketApiProps: {
disconnectRouteOptions: {
integration: new aws_apigatewayv2_integrations_1.WebSocketLambdaIntegration("DisconnectIntegration", mockDisconnectLambda),
},
},
createDefaultRoute: true
});
template = assertions_1.Template.fromStack(stack);
});
it("should contain a websocket endpoint with a default implementation of $connect", () => {
template.hasResourceProperties("AWS::ApiGatewayV2::Route", {
RouteKey: "$connect",
AuthorizationType: "AWS_IAM",
Target: {
"Fn::Join": [
"",
[
"integrations/",
{
Ref: assertions_1.Match.stringLikeRegexp("ApiGatewayV2WebSocketToSqsWebSocketApiApiGatewayV2WebSocketToSqsconnectRouteconnect"),
},
],
],
},
});
});
});
describe("When instantiating the ApiGatewayV2WebSocketToSqs construct when not setting defaultIamAuthorizer", () => {
let template;
beforeAll(() => {
const app = new cdk.App();
const stack = new cdk.Stack(app, "TestStack");
const mockConnectLambda = new lambda.Function(stack, "mockConnectFunction", {
code: lambda.Code.fromAsset(`${__dirname}/lambda`),
runtime: core_1.COMMERCIAL_REGION_LAMBDA_NODE_RUNTIME,
handler: "connect.handler",
});
new lib_1.ApiGatewayV2WebSocketToSqs(stack, "ApiGatewayV2WebSocketToSqs", {
webSocketApiProps: {
connectRouteOptions: {
integration: new aws_apigatewayv2_integrations_1.WebSocketMockIntegration("connect"),
authorizer: new aws_apigatewayv2_authorizers_1.WebSocketLambdaAuthorizer("custom-authorizer", mockConnectLambda, {
identitySource: ["route.request.querystring.Authorization"],
}),
},
},
createDefaultRoute: true
});
template = assertions_1.Template.fromStack(stack);
});
it("should contain a websocket endpoint with a default implementation of $connect", () => {
template.hasResourceProperties("AWS::ApiGatewayV2::Authorizer", {
ApiId: {
Ref: assertions_1.Match.stringLikeRegexp("ApiGatewayV2WebSocketToSqsWebSocketApiApiGatewayV2WebSocketToSqs"),
},
AuthorizerType: "REQUEST",
AuthorizerUri: {
"Fn::Join": [
"",
[
"arn:",
{
Ref: "AWS::Partition",
},
":apigateway:",
{
Ref: "AWS::Region",
},
":lambda:path/2015-03-31/functions/",
{
"Fn::GetAtt": [assertions_1.Match.stringLikeRegexp("mockConnectFunction"), "Arn"],
},
"/invocations",
],
],
},
IdentitySource: ["route.request.querystring.Authorization"],
Name: "custom-authorizer",
});
template.hasResourceProperties("AWS::ApiGatewayV2::Route", {
RouteKey: "$connect",
AuthorizationType: "CUSTOM",
Target: {
"Fn::Join": [
"",
[
"integrations/",
{
Ref: assertions_1.Match.stringLikeRegexp("ApiGatewayV2WebSocketToSqsWebSocketApiApiGatewayV2WebSocketToSqsconnectRouteconnect"),
},
],
],
},
});
});
});
describe("When instantiating the ApiGatewayV2WebSocketToSqs construct when setting defaultIamAuthorizer as false", () => {
let template;
beforeAll(() => {
const app = new cdk.App();
const stack = new cdk.Stack(app, "TestStack");
new lib_1.ApiGatewayV2WebSocketToSqs(stack, "ApiGatewayV2WebSocketToSqs", {
defaultIamAuthorization: false,
createDefaultRoute: true,
});
template = assertions_1.Template.fromStack(stack);
});
it("should contain a websocket endpoint with a default implementation of $connect", () => {
template.resourceCountIs("AWS::ApiGatewayV2::Authorizer", 0);
template.resourceCountIs("AWS::ApiGatewayV2::Route", 1);
template.resourceCountIs("AWS::ApiGatewayV2::Integration", 1);
template.resourceCountIs("AWS::ApiGatewayV2::Api", 1);
});
});
describe("When neither existingWebSocketApi nor webSocketApiProps are provided", () => {
it("should throw an error", () => {
const app = new cdk.App();
const stack = new cdk.Stack(app, "TestStack");
try {
new lib_1.ApiGatewayV2WebSocketToSqs(stack, "ApiGatewayV2WebSocketToSqs", {
createDefaultRoute: true
});
}
catch (error) {
expect(error).toBeInstanceOf(Error);
expect(error.message).toEqual("Provide either an existing WebSocketApi instance or WebSocketApiProps, but not both");
}
});
describe("When neither customRouteName is provided nor createDefaultRoute is set to true", () => {
it("should throw an error", () => {
const app = new cdk.App();
const stack = new cdk.Stack(app, "TestStack");
try {
new lib_1.ApiGatewayV2WebSocketToSqs(stack, "ApiGatewayV2WebSocketToSqs", {});
}
catch (error) {
expect(error).toBeInstanceOf(Error);
expect(error.message).toEqual("Either createDefaultRoute or customRouteName must be specified when creating a WebSocketApi");
}
});
});
});
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYXBpZ2F0ZXdheXYyd2Vic29ja2V0LXNxcy50ZXN0LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiYXBpZ2F0ZXdheXYyd2Vic29ja2V0LXNxcy50ZXN0LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7QUFBQTs7Ozs7Ozs7Ozs7R0FXRzs7QUFFSCxtQ0FBbUM7QUFDbkMsd0RBQXdEO0FBQ3hELGlEQUFpRDtBQUVqRCx5REFBdUg7QUFDdkgsdURBQWtFO0FBQ2xFLDJGQUE2RztBQUM3Ryw2RkFBaUg7QUFDakgsZ0NBQW9EO0FBRXBELFFBQVEsQ0FBQyxvRkFBb0YsRUFBRSxHQUFHLEVBQUU7SUFDbEcsSUFBSSxRQUFrQixDQUFDO0lBRXZCLFNBQVMsQ0FBQyxHQUFHLEVBQUU7UUFDYixNQUFNLEdBQUcsR0FBRyxJQUFJLEdBQUcsQ0FBQyxHQUFHLEVBQUUsQ0FBQztRQUMxQixNQUFNLEtBQUssR0FBRyxJQUFJLEdBQUcsQ0FBQyxLQUFLLENBQUMsR0FBRyxFQUFFLFdBQVcsQ0FBQyxDQUFDO1FBQzlDLE1BQU0saUJBQWlCLEdBQUcsSUFBSSxNQUFNLENBQUMsUUFBUSxDQUFDLEtBQUssRUFBRSxxQkFBcUIsRUFBRTtZQUMxRSxJQUFJLEVBQUUsTUFBTSxDQUFDLElBQUksQ0FBQyxTQUFTLENBQUMsR0FBRyxTQUFTLFNBQVMsQ0FBQztZQUNsRCxPQUFPLEVBQUUsNENBQXFDO1lBQzlDLE9BQU8sRUFBRSxpQkFBaUI7U0FDM0IsQ0FBQyxDQUFDO1FBRUgsTUFBTSxvQkFBb0IsR0FBRyxJQUFJLE1BQU0sQ0FBQyxRQUFRLENBQUMsS0FBSyxFQUFFLHdCQUF3QixFQUFFO1lBQ2hGLElBQUksRUFBRSxNQUFNLENBQUMsSUFBSSxDQUFDLFNBQVMsQ0FBQyxHQUFHLFNBQVMsU0FBUyxDQUFDO1lBQ2xELE9BQU8sRUFBRSw0Q0FBcUM7WUFDOUMsT0FBTyxFQUFFLG9CQUFvQjtTQUM5QixDQUFDLENBQUM7UUFFSCxJQUFJLGdDQUEwQixDQUFDLEtBQUssRUFB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ICogIENvcHlyaWdodCBBbWF6b24uY29tLCBJbmMuIG9yIGl0cyBhZmZpbGlhdGVzLiBBbGwgUmlnaHRzIFJlc2VydmVkLlxuICpcbiAqICBMaWNlbnNlZCB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wICh0aGUgXCJMaWNlbnNlXCIpLiBZb3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlXG4gKiAgd2l0aCB0aGUgTGljZW5zZS4gQSBjb3B5IG9mIHRoZSBMaWNlbnNlIGlzIGxvY2F0ZWQgYXRcbiAqXG4gKiAgICAgIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMFxuICpcbiAqICBvciBpbiB0aGUgJ2xpY2Vuc2UnIGZpbGUgYWNjb21wYW55aW5nIHRoaXMgZmlsZS4gVGhpcyBmaWxlIGlzIGRpc3RyaWJ1dGVkIG9uIGFuICdBUyBJUycgQkFTSVMsIFdJVEhPVVQgV0FSUkFOVElFU1xuICogIE9SIENPTkRJVElPTlMgT0YgQU5ZIEtJTkQsIGV4cHJlc3Mgb3IgaW1wbGllZC4gU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zXG4gKiAgYW5kIGxpbWl0YXRpb25zIHVuZGVyIHRoZSBMaWNlbnNlLlxuICovXG5cbmltcG9ydCAqIGFzIGNkayBmcm9tIFwiYXdzLWNkay1saWJcIjtcbmltcG9ydCAqIGFzIGFwaWd3djIgZnJvbSBcImF3cy1jZGstbGliL2F3cy1hcGlnYXRld2F5djJcIjtcbmltcG9ydCAqIGFzIGxhbWJkYSBmcm9tIFwiYXdzLWNkay1saWIvYXdzLWxhbWJkYVwiO1xuXG5pbXBvcnQgeyBDT01NRVJDSUFMX1JFR0lPTl9MQU1CREFfTk9ERV9SVU5USU1FLCBERUZBVUxUX1JPVVRFX1FVRVVFX1ZUTF9DT05GSUcgfSBmcm9tIFwiQGF3cy1zb2x1dGlvbnMtY29uc3RydWN0cy9jb3JlXCI7XG5pbXBvcnQgeyBDYXB0dXJlLCBNYXRjaCwgVGVtcGxhdGUgfSBmcm9tIFwiYXdzLWNkay1saWIvYXNzZXJ0aW9uc1wiO1xuaW1wb3J0IHsgV2ViU29ja2V0SWFtQXV0aG9yaXplciwgV2ViU29ja2V0TGFtYmRhQXV0aG9yaXplciB9IGZyb20gXCJhd3MtY2RrLWxpYi9hd3MtYXBpZ2F0ZXdheXYyLWF1dGhvcml6ZXJzXCI7XG5pbXBvcnQgeyBXZWJTb2NrZXRMYW1iZGFJbnRlZ3JhdGlvbiwgV2ViU29ja2V0TW9ja0ludGVncmF0aW9uIH0gZnJvbSBcImF3cy1jZGstbGliL2F3cy1hcGlnYXRld2F5djItaW50ZWdyYXRpb25zXCI7XG5pbXBvcnQgeyBBcGlHYXRld2F5VjJXZWJTb2NrZXRUb1NxcyB9IGZyb20gXCIuLi9saWJcIjtcblxuZGVzY3JpYmUoXCJXaGVuIGluc3RhbnRpYXRpbmcgdGhlIEFwaUdhdGV3YXlWMldlYlNvY2tldFRvU3FzIGNvbnN0cnVjdCB3aXRoIFdlYlNvY2tldEFwaVByb3BzXCIsICgpID0+IHtcbiAgbGV0IHRlbXBsYXRlOiBUZW1wbGF0ZTtcblxuICBiZWZvcmVBbGwoKCkgPT4ge1xuICAgIGNvbnN0IGFwcCA9IG5ldyBjZGsuQXBwKCk7XG4gICAgY29uc3Qgc3RhY2sgPSBuZXcgY2RrLlN0YWNrKGFwcCwgXCJUZXN0U3RhY2tcIik7XG4gICAgY29uc3QgbW9ja0Nvbm5lY3RMYW1iZGEgPSBuZXcgbGFtYmRhLkZ1bmN0aW9uKHN0YWNrLCBcIm1vY2tDb25uZWN0RnVuY3Rpb25cIiwge1xuICAgICAgY29kZTogbGFtYmRhLkNvZGUuZnJvbUFzc2V0KGAke19fZGlybmFtZX0vbGFtYmRhYCksXG4gICAgICBydW50aW1lOiBDT01NRVJDSUFMX1JFR0lPTl9MQU1CREFfTk9ERV9SVU5USU1FLFxuICAgICAgaGFuZGxlcjogXCJjb25uZWN0LmhhbmRsZXJcIixcbiAgICB9KTtcblxuICAgIGNvbnN0IG1vY2tEaXNjb25uZWN0TGFtYmRhID0gbmV3IGxhbWJkYS5GdW5jdGlvbihzdGFjaywgXCJtb2NrRGlzY29ubmVjdEZ1bmN0aW9uXCIsIHtcbiAgICAgIGNvZGU6IGxhbWJkYS5Db2RlLmZyb21Bc3NldChgJHtfX2Rpcm5hbWV9L2xhbWJkYWApLFxuICAgICAgcnVudGltZTogQ09NTUVSQ0lBTF9SRUdJT05fTEFNQkRBX05PREVfUlVOVElNRSxcbiAgICAgIGhhbmRsZXI6IFwiZGlzY29ubmVjdC5oYW5kbGVyXCIsXG4gICAgfSk7XG5cbiAgICBuZXcgQXBpR2F0ZXdheVYyV2ViU29ja2V0VG9TcXMoc3RhY2ssIFwiQXBpR2F0ZXdheVYyV2ViU29ja2V0VG9TcXNcIiwge1xuICAgICAgd2ViU29ja2V0QXBpUHJvcHM6IHtcbiAgICAgICAgY29ubmVjdFJvdXRlT3B0aW9uczoge1xuICAgICAgICAgIGludGVncmF0aW9uOiBuZXcgV2ViU29ja2V0TGFtYmRhSW50ZWdyYXRpb24oXCJDb25uZWN0SW50ZWdyYXRpb25cIiwgbW9ja0Nvbm5lY3RMYW1iZGEpLFxuICAgICAgICAgIGF1dGhvcml6ZXI6IG5ldyBXZWJTb2NrZXRJYW1BdXRob3JpemVyKCksXG4gICAgICAgIH0sXG4gICAgICAgIGRpc2Nvbm5lY3RSb3V0ZU9wdGlvbnM6IHtcbiAgICAgICAgICBpbnRlZ3JhdGlvbjogbmV3IFdlYlNvY2tldExhbWJkYUludGVncmF0aW9uKFwiRGlzY29ubmVjdEludGVncmF0aW9uXCIsIG1vY2tEaXNjb25uZWN0TGFtYmRhKSxcbiAgICAgICAgfSxcbiAgICAgIH0sXG4gICAgICBjcmVhdGVEZWZhdWx0Um91dGU6IHRydWUsXG4gICAgfSk7XG4gICAgdGVtcGxhdGUgPSBUZW1wbGF0ZS5mcm9tU3RhY2soc3RhY2spO1xuICB9KTtcblxuICBpdChcInNob3VsZCBoYXZlIGEgRklGTyBxdWV1ZSBhbmQgYSBETFFcIiwgKCkgPT4ge1xuICAgIHRlbXBsYXRlLnJlc291cmNlQ291bnRJcyhcIkFXUzo6U1FTOjpRdWV1ZVwiLCAyKTtcbiAgICB0ZW1wbGF0ZS5oYXNSZXNvdXJjZVByb3BlcnRpZXMoXCJBV1M6OlNRUzo6UXVldWVcIiwge1xuICAgICAgRGVkdXBsaWNhdGlvblNjb3BlOiBcIm1lc3NhZ2VHcm91cFwiLFxuICAgICAgRmlmb1F1ZXVlOiB0cnVlLFxuICAgICAgRmlmb1Rocm91Z2hwdXRMaW1pdDogXCJwZXJNZXNzYWdlR3JvdXBJZFwiLFxuICAgICAgUmVkcml2ZUFsbG93UG9saWN5OiB7XG4gICAgICAgIHJlZHJpdmVQZXJtaXNzaW9uOiBcImRlbnlBbGxcIixcbiAgICAgIH0sXG4gICAgfSk7XG5cbiAgICB0ZW1wbGF0ZS5oYXNSZXNvdXJjZVByb3BlcnRpZXMoXCJBV1M6OlNRUzo6UXVldWVcIiwge1xuICAgICAgS21zTWFzdGVyS2V5SWQ6IFwiYWxpYXMvYXdzL3Nxc1wiLFxuICAgICAgRmlmb1F1ZXVlOiB0cnVlLFxuICAgICAgRGVkdXBsaWNhdGlvblNjb3BlOiBcIm1lc3NhZ2VHcm91cFwiLFxuICAgICAgRmlmb1Rocm91Z2hwdXRMaW1pdDogXCJwZXJNZXNzYWdlR3JvdXBJZFwiLFxuICAgICAgUmVkcml2ZUFsbG93UG9saWN5OiBNYXRjaC5hYnNlbnQoKSxcbiAgICB9KTtcbiAgfSk7XG5cbiAgaXQoXCJzaG91bGQgY3JlYXRlIGFuIGluc3RhbmNlIG9mIHRoZSBXZWJTb2NrZXRcIiwgKCkgPT4ge1xuICAgIHRlbXBsYXRlLmhhc1Jlc291cmNlUHJvcGVydGllcyhcIkFXUzo6QXBpR2F0ZXdheVYyOjpBcGlcIiwge1xuICAgICAgTmFtZTogXCJXZWJTb2NrZXRBcGlBcGlHYXRld2F5VjJXZWJTb2NrZXRUb1Nxc1wiLFxuICAgICAgUHJvdG9jb2xUeXBlOiBcIldFQlNPQ0tFVFwiLFxuICAgICAgUm91dGVTZWxlY3Rpb25FeHByZXNzaW9uOiBcIiRyZXF1ZXN0LmJvZHkuYWN0aW9uXCIsXG4gICAgfSk7XG4gIH0pO1xuXG4gIGl0KFwic2hvdWxkIGhhdmUgYSB3ZWJzb2NrZXQgYXBpIHdpdGggZGlmZmVyZW50IHJvdXRlIG9wdGlvbnNcIiwgKCkgPT4ge1xuICAgIHRlbXBsYXRlLnJlc291cmNlQ291bnRJcyhcIkFXUzo6QXBpR2F0ZXdheVYyOjpSb3V0ZVwiLCAzKTtcblxuICAgIHRlbXBsYXRlLmhhc1Jlc291cmNlUHJvcGVydGllcyhcIkFXUzo6QXBpR2F0ZXdheVYyOjpSb3V0ZVwiLCB7XG4gICAgICBSb3V0ZUtleTogXCIkZGlzY29ubmVjdFwiLFxuICAgICAgQXV0aG9yaXphdGlvblR5cGU6IFwiTk9ORVwiLFxuICAgICAgVGFyZ2V0OiB7XG4gICAgICAgIFwiRm46OkpvaW5cIjogW1wiXCIsIFtcImludGVncmF0aW9ucy9cIiwgTWF0Y2guYW55VmFsdWUoKV1dLFxuICAgICAgfSxcbiAgICB9KTtcblxuICAgIHRlbXBsYXRlLmhhc1Jlc291cmNlUHJvcGVydGllcyhcIkFXUzo6QXBpR2F0ZXdheVYyOjpSb3V0ZVwiLCB7XG4gICAgICBSb3V0ZUtleTogXCIkY29ubmVjdFwiLFxuICAgICAgQXV0aG9yaXphdGlvblR5cGU6IFwiQVdTX0lBTVwiLFxuICAgICAgVGFyZ2V0OiB7XG4gICAgICAgIFwiRm46OkpvaW5cIjogW1wiXCIsIFtcImludGVncmF0aW9ucy9cIiwgTWF0Y2guYW55VmFsdWUoKV1dLFxuICAgICAgfSxcbiAgICB9KTtcbiAgfSk7XG5cbiAgaXQoXCJzaG91bGQgaGF2ZSByb2xlIHdpdGggcG9saWN5IGZvciBjbG91ZHdhdGNoLCBzcXMsIGFuZCBLTVNcIiwgKCkgPT4ge1xuICAgIHRlbXBsYXRlLmhhc1Jlc291cmNlUHJvcGVydGllcyhcIkFXUzo6SUFNOjpSb2xlXCIsIHtcbiAgICAgIEFzc3VtZVJvbGVQb2xpY3lEb2N1bWVudDoge1xuICAgICAgICBTdGF0ZW1lbnQ6IFtcbiAgICAgICAgICB7XG4gICAgICAgICAgICBBY3Rpb246IFwic3RzOkFzc3VtZVJvbGVcIixcbiAgICAgICAgICAgIEVmZmVjdDogXCJBbGxvd1wiLFxuICAgICAgICAgICAgUHJpbmNpcGFsOiB7XG4gICAgICAgICAgICAgIFNlcnZpY2U6IFwiYXBpZ2F0ZXdheS5hbWF6b25hd3MuY29tXCIsXG4gICAgICAgICAgICB9LFxuICAgICAgICAgIH0sXG4gICAgICAgIF0sXG4gICAgICAgIFZlcnNpb246IFwiMjAxMi0xMC0xN1wiLFxuICAgICAgfSxcbiAgICB9KTtcblxuICAgIGNvbnN0IGFwaWdhdGV3YXlSb2xlQ2FwdHVyZSA9IG5ldyBDYXB0dXJlKCk7XG4gICAgdGVtcGxhdGUuaGFzUmVzb3VyY2VQcm9wZXJ0aWVzKFwiQVdTOjpJQU06OlBvbGljeVwiLCB7XG4gICAgICBQb2xpY3lEb2N1bWVudDoge1xuICAgICAgICBTdGF0ZW1lbnQ6IFtcbiAgICAgICAgICB7XG4gICAgICAgICAgICBBY3Rpb246IFtcInNxczpTZW5kTWVzc2FnZVwiLCBcInNxczpHZXRRdWV1ZUF0dHJpYnV0ZXNcIiwgXCJzcXM6R2V0UXVldWVVcmxcIl0sXG4gICAgICAgICAgICBFZmZlY3Q6IFwiQWxsb3dcIixcbiAgICAgICAgICAgIFJlc291cmNlOiB7XG4gICAgICAgICAgICAgIFwiRm46OkdldEF0dFwiOiBbTWF0Y2guYW55VmFsdWUoKSwgXCJBcm5cIl0sXG4gICAgICAgICAgICB9LFxuICAgICAgICAgIH0sXG4gICAgICAgICAge1xuICAgICAgICAgICAgQWN0aW9uOiBbXG4gICAgICAgICAgICAgIFwibG9nczpDcmVhdGVMb2dHcm91cFwiLFxuICAgICAgICAgICAgICBcImxvZ3M6Q3JlYXRlTG9nU3RyZWFtXCIsXG4gICAgICAgICAgICAgIFwibG9nczpEZXNjcmliZUxvZ0dyb3Vwc1wiLFxuICAgICAgICAgICAgICBcImxvZ3M6RGVzY3JpYmVMb2dTdHJlYW1zXCIsXG4gICAgICAgICAgICAgIFwibG9nczpQdXRMb2dFdmVudHNcIixcbiAgICAgICAgICAgICAgXCJsb2dzOkdldExvZ0V2ZW50c1wiLFxuICAgICAgICAgICAgICBcImxvZ3M6RmlsdGVyTG9nRXZlbnRzXCIsXG4gICAgICAgICAgICBdLFxuICAgICAgICAgICAgRWZmZWN0OiBcIkFsbG93XCIsXG4gICAgICAgICAgICBSZXNvdXJjZToge1xuICAgICAgICAgICAgICBcIkZuOjpHZXRBdHRcIjogW01hdGNoLmFueVZhbHVlKCksIFwiQXJuXCJdLFxuICAgICAgICAgICAgfSxcbiAgICAgICAgICB9LFxuICAgICAgICBdLFxuICAgICAgICBWZXJzaW9uOiBcIjIwMTItMTAtMTdcIixcbiAgICAgIH0sXG4gICAgICBQb2xpY3lOYW1lOiBNYXRjaC5hbnlWYWx1ZSgpLFxuICAgICAgUm9sZXM6IFtcbiAgICAgICAge1xuICAgICAgICAgIFJlZjogYXBpZ2F0ZXdheVJvbGVDYXB0dXJlLFxuICAgICAgICB9LFxuICAgICAgXSxcbiAgICB9KTtcblxuICAgIHRlbXBsYXRlLmhhc1Jlc291cmNlUHJvcGVydGllcyhcIkFXUzo6SUFNOjpSb2xlXCIsIHtcbiAgICAgIEFzc3VtZVJvbGVQb2xpY3lEb2N1bWVudDoge1xuICAgICAgICBTdGF0ZW1lbnQ6IFtcbiAgICAgICAgICB7XG4gICAgICAgICAgICBBY3Rpb246IFwic3RzOkFzc3VtZVJvbGVcIixcbiAgICAgICAgICAgIEVmZmVjdDogXCJBbGxvd1wiLFxuICAgICAgICAgICAgUHJpbmNpcGFsOiB7XG4gICAgICAgICAgICAgIFNlcnZpY2U6IFwiYXBpZ2F0ZXdheS5hbWF6b25hd3MuY29tXCIsXG4gICAgICAgICAgICB9LFxuICAgICAgICAgIH0sXG4gICAgICAgIF0sXG4gICAgICB9LFxuICAgIH0pO1xuICB9KTtcblxuICBpdChcInNob3VsZCBkZWZpbmUgZm9sbG93aW5nIHR5cGVzIG9mIGludGVncmF0aW9uXCIsICgpID0+IHtcbiAgICB0ZW1wbGF0ZS5oYXNSZXNvdXJjZVByb3BlcnRpZXMoXCJBV1M6OkFwaUdhdGV3YXlWMjo6SW50ZWdyYXRpb25cIiwge1xuICAgICAgSW50ZWdyYXRpb25UeXBlOiBcIkFXU19QUk9YWVwiLFxuICAgICAgSW50ZWdyYXRpb25Vcmk6IE1hdGNoLmFueVZhbHVlKCksXG4gICAgfSk7XG4gIH0pO1xufSk7XG5cbmRlc2NyaWJlKFwiV2hlbiB0aGUgb3B0aW9uIG9mIGNyZWF0aW5nIGRlZmF1bHQgcm91dGUgYW5kIGN1c3RvbSByb3V0ZSBpcyBwcm92aWRlZFwiLCAoKSA9PiB7XG4gIGxldCB0ZW1wbGF0ZTogVGVtcGxhdGU7XG4gIGNvbnN0IHJvdXRlTmFtZSA9IFwiZmFrZXJvdXRlbmFtZVwiO1xuXG4gIGJlZm9yZUFsbCgoKSA9PiB7XG4gICAgY29uc3QgYXBwID0gbmV3IGNkay5BcHAoKTtcbiAgICBjb25zdCBzdGFjayA9IG5ldyBjZGsuU3RhY2soYXBwLCBcIlRlc3RTdGFja1wiKTtcblxuICAgIGNvbnN0IG1vY2tDb25uZWN0TGFtYmRhID0gbmV3IGxhbWJkYS5GdW5jdGlvbihzdGFjaywgXCJtb2NrQ29ubmVjdEZ1bmN0aW9uXCIsIHtcbiAgICAgIGNvZGU6IGxhbWJkYS5Db2RlLmZyb21Bc3NldChgJHtfX2Rpcm5hbWV9L2xhbWJkYWApLFxuICAgICAgcnVudGltZTogQ09NTUVSQ0lBTF9SRUdJT05fTEFNQkRBX05PREVfUlVOVElNRSxcbiAgICAgIGhhbmRsZXI6IFwiY29ubmVjdC5oYW5kbGVyXCIsXG4gICAgfSk7XG5cbiAgICBjb25zdCBtb2NrRGlzY29ubmVjdExhbWJkYSA9IG5ldyBsYW1iZGEuRnVuY3Rpb24oc3RhY2ssIFwibW9ja0Rpc2Nvbm5lY3RGdW5jdGlvblwiLCB7XG4gICAgICBjb2RlOiBsYW1iZGEuQ29kZS5mcm9tQXNzZXQoYCR7X19kaXJ