UNPKG

@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
"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