UNPKG

cdk-openapi-to-http-api

Version:

CDK Construct that lets you build AWS Api Gateway Http Api, backed by Lambdas, based on a OpenAPI spec file.

186 lines 30.4 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.RootResource = exports.HttpOpenApi = void 0; const fs = require("fs"); const aws_apigatewayv2_alpha_1 = require("@aws-cdk/aws-apigatewayv2-alpha"); const aws_cdk_lib_1 = require("aws-cdk-lib"); const constructs_1 = require("constructs"); const YAML = require("yaml"); const aws_apigateway_1 = require("aws-cdk-lib/aws-apigateway"); const aws_apigatewayv2_1 = require("aws-cdk-lib/aws-apigatewayv2"); const aws_wafv2_1 = require("aws-cdk-lib/aws-wafv2"); const AUTHORIZER_KEY = 'custom_authorizer'; class HttpOpenApi extends constructs_1.Construct { constructor(scope, id, props) { super(scope, id); this.routes = []; this.functions = {}; this.permissions = {}; const file = fs.readFileSync(props.openApiSpec, 'utf8'); const spec = YAML.parse(file); const stack = aws_cdk_lib_1.Stack.of(this); this.methodMappings = this.buildMethodMappings(spec); this.cfnApi = new aws_apigatewayv2_1.CfnApi(this, `${props.functionNamePrefix}-api`, { name: `${props.functionNamePrefix}-api`, corsConfiguration: props.corsConfig, protocolType: 'HTTP' }); this.apiStage = new aws_apigatewayv2_1.CfnStage(this, `${props.functionNamePrefix}-stage`, { apiId: this.cfnApi.attrApiId, stageName: '$default', autoDeploy: true }); if (props.acls) { this.association = new aws_wafv2_1.CfnWebACLAssociation(this, `${props.functionNamePrefix}-waf-association`, { resourceArn: `arn:${stack.partition}:execute-api:${stack.region}:${stack.account}:${this.cfnApi.attrApiId}`, webAclArn: props.acls.attrArn }); } props.integrations.forEach((integration) => { const method = this.methodMappings[integration.operationId]; if (!method) { throw new Error(`There is no path in the Open API Spec matching ${integration.operationId}`); } else { const functionName = `${props.functionNamePrefix}-${integration.operationId}`; const func = new aws_cdk_lib_1.aws_lambda.Function(this, functionName, { ...integration, functionName, code: aws_cdk_lib_1.aws_lambda.AssetCode.fromAsset(integration.sourcePath), logRetention: integration.logRetention ?? 90, timeout: integration.timeout ?? aws_cdk_lib_1.Duration.seconds(3), memorySize: integration.memorySize ?? 128 }); this.functions[integration.operationId] = func; const intgr = new aws_apigatewayv2_1.CfnIntegration(this, `integration-${method.path.replace(/\//g, '-')}-${method.method}`, { apiId: this.cfnApi.attrApiId, integrationType: aws_apigateway_1.IntegrationType.AWS_PROXY, payloadFormatVersion: aws_apigatewayv2_1.PayloadFormatVersion.VERSION_2_0.version, integrationUri: `arn:${stack.partition}:apigateway:${stack.region}:lambda:path/2015-03-31/functions/${func.functionArn}/invocations` }); const route = new aws_apigatewayv2_1.CfnRoute(this, `route-${method.path.replace(/\//g, '-')}-${method.method}`, { apiId: this.cfnApi.attrApiId, routeKey: `${method.method.toUpperCase()} ${method.path}`, target: `integrations/${intgr.attrIntegrationId}` }); this.routes.push(route); if (props.customAuthorizerLambdaArn) { spec.paths[method.path][method.method].security = [ { [AUTHORIZER_KEY]: [] } ]; } } }); // First loop with authorizers to add their configurations to the spec if (props.customAuthorizerLambdaArn) { spec.components.securitySchemes = {}; // https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-swagger-extensions-authorizer.html spec.components.securitySchemes[AUTHORIZER_KEY] = this.toAuthorizerSpec(props.customAuthorizerLambdaArn, stack.region); } // Second loop with Authorizers, in order to add InvokeFunction permission // to the created API. It has to be separated because we need the ref from cfnApi if (props.customAuthorizerLambdaArn) { const permission = new aws_cdk_lib_1.aws_lambda.CfnPermission(this, 'AuthorizerPermission', { action: 'lambda:InvokeFunction', principal: 'apigateway.amazonaws.com', functionName: props.customAuthorizerLambdaArn, sourceArn: `arn:${stack.partition}:execute-api:${stack.region}:${stack.account}:${this.cfnApi.attrApiId}/*/*/*` }); this.permissions[AUTHORIZER_KEY] = permission; } Object.keys(this.functions).forEach((funcKey, idx) => { const func = this.functions[funcKey]; const permission = new aws_cdk_lib_1.aws_lambda.CfnPermission(this, `LambdaPermission_${idx}`, { action: 'lambda:InvokeFunction', principal: 'apigateway.amazonaws.com', functionName: func.functionName, sourceArn: `arn:${stack.partition}:execute-api:${stack.region}:${stack.account}:${this.cfnApi.attrApiId}/*/*` }); this.permissions[funcKey] = permission; }); } /** * Enable custom domain for this API * @param customDomainName - customDomainName to be created in Api Gateway * @param certificateArn Arn of the certificate needed for the creation of custom domain. It must be a regional certificate. */ enableCustomDomain(customDomainName, certificateArn, zoneName) { const certificate = aws_cdk_lib_1.aws_certificatemanager.Certificate.fromCertificateArn(this, 'DomainCertificate', certificateArn); const domainName = new aws_apigatewayv2_alpha_1.DomainName(this, 'CustomDomainName', { domainName: customDomainName, certificate }); const routeConfig = { recordName: customDomainName, zone: aws_cdk_lib_1.aws_route53.HostedZone.fromLookup(this, 'ZoneLookup', { domainName: zoneName }), target: aws_cdk_lib_1.aws_route53.RecordTarget.fromAlias({ bind: () => ({ dnsName: domainName.regionalDomainName, hostedZoneId: domainName.regionalHostedZoneId }) }) }; const aRecord = new aws_cdk_lib_1.aws_route53.ARecord(this, 'CustomDomainARecord', routeConfig); const aaaaRecord = new aws_cdk_lib_1.aws_route53.AaaaRecord(this, 'CustomDomainAAAARecord', routeConfig); const apiMapping = new aws_cdk_lib_1.aws_apigatewayv2.CfnApiMapping(this, 'CustomDomainApiMapping', { apiId: this.cfnApi.attrApiId, domainName: customDomainName, stage: this.apiStage.stageName }); apiMapping.node.addDependency(this.cfnApi); apiMapping.node.addDependency(this.apiStage); apiMapping.node.addDependency(domainName); apiMapping.node.addDependency(aRecord); apiMapping.node.addDependency(aaaaRecord); } /** * Extracts path and method that map to the operationId needed * So finding the right place on the spec is just a matter of accessing the right attribute * @param spec * @returns methods */ // eslint-disable-next-line @typescript-eslint/no-explicit-any buildMethodMappings(spec) { const methods = {}; Object.entries(spec.paths).forEach(([path, pathObj]) => { Object.keys(pathObj).forEach((method) => { methods[pathObj[method]['x-amazon-apigateway-integration'].uri] = { path, method }; }); }); return methods; } toAuthorizerSpec(lambdaAuthorizerArn, region) { const uri = `arn:aws:apigateway:${region}:lambda:path/2015-03-31/functions/${lambdaAuthorizerArn}/invocations`; return { type: 'apiKey', name: 'Authorization', in: 'header', 'x-amazon-apigateway-authorizer': { type: 'request', identitySource: '$request.header.Authorization', // Request parameter mapping expression of the identity source. In this example, it is the 'auth' header. authorizerUri: uri, authorizerPayloadFormatVersion: '2.0', authorizerResultTtlInSeconds: 300 } }; } } exports.HttpOpenApi = HttpOpenApi; class RootResource extends aws_apigateway_1.ResourceBase { constructor(api, resourceId) { super(api, resourceId); this.api = api; this.resourceId = resourceId; this.path = '/'; } } exports.RootResource = RootResource; //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaHR0cC1vcGVuYXBpLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vc3JjL2h0dHAtb3BlbmFwaS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUFBQSx5QkFBd0I7QUFFeEIsNEVBQTREO0FBQzVELDZDQUlvQjtBQUNwQiwyQ0FBc0M7QUFDdEMsNkJBQTRCO0FBRTVCLCtEQUF3STtBQUN4SSxtRUFBK0c7QUFDL0cscURBQTREO0FBRzVELE1BQU0sY0FBYyxHQUFHLG1CQUFtQixDQUFBO0FBRTFDLE1BQWEsV0FBWSxTQUFRLHNCQUFTO0lBMkJ4QyxZQUFZLEtBQWdCLEVBQUUsRUFBVSxFQUFFLEtBQW1CO1FBQzNELEtBQUssQ0FBQyxLQUFLLEVBQUUsRUFBRSxDQUFDLENBQUE7UUFWRixXQUFNLEdBQWUsRUFBRSxDQUFBO1FBWXJDLElBQUksQ0FBQyxTQUFTLEdBQUcsRUFBRSxDQUFBO1FBQ25CLElBQUksQ0FBQyxXQUFXLEdBQUcsRUFBRSxDQUFBO1FBRXJCLE1BQU0sSUFBSSxHQUFHLEVBQUUsQ0FBQyxZQUFZLENBQUMsS0FBSyxDQUFDLFdBQVcsRUFBRSxNQUFNLENBQUMsQ0FBQTtRQUN2RCxNQUFNLElBQUksR0FBRyxJQUFJLENBQUMsS0FBSyxDQUFDLElBQUksQ0FBQyxDQUFBO1FBQzdCLE1BQU0sS0FBSyxHQUFHLG1CQUFLLENBQUMsRUFBRSxDQUFDLElBQUksQ0FBQyxDQUFBO1FBRTVCLElBQUksQ0FBQyxjQUFjLEdBQUcsSUFBSSxDQUFDLG1CQUFtQixDQUFDLElBQUksQ0FBQyxDQUFBO1FBRXBELElBQUksQ0FBQyxNQUFNLEdBQUcsSUFBSSx5QkFBTSxDQUFDLElBQUksRUFBRSxHQUFHLEtBQUssQ0FBQyxrQkFBa0IsTUFBTSxFQUFFO1lBQ2hFLElBQUksRUFBRSxHQUFHLEtBQUssQ0FBQyxrQkFBa0IsTUFBTTtZQUN2QyxpQkFBaUIsRUFBRSxLQUFLLENBQUMsVUFBVTtZQUNuQyxZQUFZLEVBQUUsTUFBTTtTQUNyQixDQUFD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QUFDLENBQUE7UUFDdEIsSUFBSSxDQUFDLEdBQUcsR0FBRyxHQUFHLENBQUE7UUFDZCxJQUFJLENBQUMsVUFBVSxHQUFHLFVBQVUsQ0FBQTtRQUM1QixJQUFJLENBQUMsSUFBSSxHQUFHLEdBQUcsQ0FBQTtJQUNqQixDQUFDO0NBQ0Y7QUFmRCxvQ0FlQyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCAqIGFzIGZzIGZyb20gJ2ZzJ1xuXG5pbXBvcnQgeyBEb21haW5OYW1lIH0gZnJvbSAnQGF3cy1jZGsvYXdzLWFwaWdhdGV3YXl2Mi1hbHBoYSdcbmltcG9ydCB7XG4gIGF3c19jZXJ0aWZpY2F0ZW1hbmFnZXIgYXMgYWNtLFxuICBhd3NfYXBpZ2F0ZXdheXYyIGFzIGFwaWd3djIsIER1cmF0aW9uLCBhd3NfbGFtYmRhIGFzIGxhbWJkYSwgYXdzX3JvdXRlNTMgYXMgcm91dGU1MyxcbiAgU3RhY2tcbn0gZnJvbSAnYXdzLWNkay1saWInXG5pbXBvcnQgeyBDb25zdHJ1Y3QgfSBmcm9tICdjb25zdHJ1Y3RzJ1xuaW1wb3J0ICogYXMgWUFNTCBmcm9tICd5YW1sJ1xuXG5pbXBvcnQgeyBDb3JzT3B0aW9ucywgSW50ZWdyYXRpb24sIEludGVncmF0aW9uVHlwZSwgSVJlc291cmNlLCBJUmVzdEFwaSwgTWV0aG9kT3B0aW9ucywgUmVzb3VyY2VCYXNlIH0gZnJvbSAnYXdzLWNkay1saWIvYXdzLWFwaWdhdGV3YXknXG5pbXBvcnQgeyBDZm5BcGksIENmbkludGVncmF0aW9uLCBDZm5Sb3V0ZSwgQ2ZuU3RhZ2UsIFBheWxvYWRGb3JtYXRWZXJzaW9uIH0gZnJvbSAnYXdzLWNkay1saWIvYXdzLWFwaWdhdGV3YXl2MidcbmltcG9ydCB7IENmbldlYkFDTEFzc29jaWF0aW9uIH0gZnJvbSAnYXdzLWNkay1saWIvYXdzLXdhZnYyJ1xuaW1wb3J0IHsgSHR0cEFwaVByb3BzLCBNZXRob2RNYXBwaW5nIH0gZnJvbSAnLi90eXBlcydcblxuY29uc3QgQVVUSE9SSVpFUl9LRVkgPSAnY3VzdG9tX2F1dGhvcml6ZXInXG5cbmV4cG9ydCBjbGFzcyBIdHRwT3BlbkFwaSBleHRlbmRzIENvbnN0cnVjdCB7XG4gIC8qKlxuICAgKiAgQXBpIFJlc291cmNlIGJlaW5nIGNyZWF0ZWQgYmFzZWQgb24gb3BlbkFQSSBkZWZpbml0aW9uXG4gICAqL1xuICBwdWJsaWMgcmVhZG9ubHkgY2ZuQXBpOiBhcGlnd3YyLkNmbkFwaVxuXG4gIC8qKlxuICAgKiBEZWZhdWx0IHN0YWdlIGJlaW5nIGNyZWF0ZWQgJiBkZXBsb3llZCBmb3IgdGhlIEFQSVxuICAgKi9cbiAgcHVibGljIHJlYWRvbmx5IGFwaVN0YWdlOiBhcGlnd3YyLkNmblN0YWdlXG5cbiAgLyoqXG4gICAqIE1hcHMgb3BlcmF0aW9uSWQgdG8gbGFtYmRhIEZ1bmN0aW9uIHRoYXQgaXMgYmVpbmcgY3JlYXRlZFxuICAgKi9cbiAgcHVibGljIHJlYWRvbmx5IGZ1bmN0aW9uczogUmVjb3JkPHN0cmluZywgbGFtYmRhLkZ1bmN0aW9uPlxuXG4gIHB1YmxpYyByZWFkb25seSBwZXJtaXNzaW9uczogUmVjb3JkPHN0cmluZywgbGFtYmRhLkNmblBlcm1pc3Npb24+XG5cbiAgcHVibGljIHJlYWRvbmx5IHJvdXRlczogQ2ZuUm91dGVbXSA9IFtdXG5cbiAgLyoqXG4gICAqIE1hcHMgb3BlcmF0aW9uSWQgdG8gaHR0cCBwYXRoIGFuZCBtZXRob2QgLSBmb3Igcm91dGluZyBwdXJwb3Nlc1xuICAgKi9cbiAgcHVibGljIHJlYWRvbmx5IG1ldGhvZE1hcHBpbmdzOiBSZWNvcmQ8c3RyaW5nLCBNZXRob2RNYXBwaW5nPlxuXG4gIHB1YmxpYyByZWFkb25seSBhc3NvY2lhdGlvbj86IENmbldlYkFDTEFzc29jaWF0aW9uXG5cbiAgY29uc3RydWN0b3Ioc2NvcGU6IENvbnN0cnVjdCwgaWQ6IHN0cmluZywgcHJvcHM6IEh0dHBBcGlQcm9wcykge1xuICAgIHN1cGVyKHNjb3BlLCBpZClcblxuICAgIHRoaXMuZnVuY3Rpb25zID0ge31cbiAgICB0aGlzLnBlcm1pc3Npb25zID0ge31cblxuICAgIGNvbnN0IGZpbGUgPSBmcy5yZWFkRmlsZVN5bmMocHJvcHMub3BlbkFwaVNwZWMsICd1dGY4JylcbiAgICBjb25zdCBzcGVjID0gWUFNTC5wYXJzZShmaWxlKVxuICAgIGNvbnN0IHN0YWNrID0gU3RhY2sub2YodGhpcylcblxuICAgIHRoaXMubWV0aG9kTWFwcGluZ3MgPSB0aGlzLmJ1aWxkTWV0aG9kTWFwcGluZ3Moc3BlYylcblxuICAgIHRoaXMuY2ZuQXBpID0gbmV3IENmbkFwaSh0aGlzLCBgJHtwcm9wcy5mdW5jdGlvbk5hbWVQcmVmaXh9LWFwaWAsIHtcbiAgICAgIG5hbWU6IGAke3Byb3BzLmZ1bmN0aW9uTmFtZVByZWZpeH0tYXBpYCxcbiAgICAgIGNvcnNDb25maWd1cmF0aW9uOiBwcm9wcy5jb3JzQ29uZmlnLFxuICAgICAgcHJvdG9jb2xUeXBlOiAnSFRUUCdcbiAgICB9KVxuXG4gICAgdGhpcy5hcGlTdGFnZSA9IG5ldyBDZm5TdGFnZSh0aGlzLCBgJHtwcm9wcy5mdW5jdGlvbk5hbWVQcmVmaXh9LXN0YWdlYCwge1xuICAgICAgYXBpSWQ6IHRoaXMuY2ZuQXBpLmF0dHJBcGlJZCxcbiAgICAgIHN0YWdlTmFtZTogJyRkZWZhdWx0JyxcbiAgICAgIGF1dG9EZXBsb3k6IHRydWVcbiAgICB9KVxuXG4gICAgaWYgKHByb3BzLmFjbHMpIHtcbiAgICAgIHRoaXMuYXNzb2NpYXRpb24gPSBuZXcgQ2ZuV2ViQUNMQXNzb2NpYXRpb24odGhpcywgYCR7cHJvcHMuZnVuY3Rpb25OYW1lUHJlZml4fS13YWYtYXNzb2NpYXRpb25gLCB7XG4gICAgICAgIHJlc291cmNlQXJuOiBgYXJuOiR7c3RhY2sucGFydGl0aW9ufTpleGVjdXRlLWFwaToke3N0YWNrLnJlZ2lvbn06JHtzdGFjay5hY2NvdW50fToke3RoaXMuY2ZuQXBpLmF0dHJBcGlJZH1gLFxuICAgICAgICB3ZWJBY2xBcm46IHByb3BzLmFjbHMuYXR0ckFyblxuICAgICAgfSlcbiAgICB9XG5cbiAgICBwcm9wcy5pbnRlZ3JhdGlvbnMuZm9yRWFjaCgoaW50ZWdyYXRpb24pID0+IHtcbiAgICAgIGNvbnN0IG1ldGhvZCA9IHRoaXMubWV0aG9kTWFwcGluZ3NbaW50ZWdyYXRpb24ub3BlcmF0aW9uSWRdXG4gICAgICBpZiAoIW1ldGhvZCkge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoYFRoZXJlIGlzIG5vIHBhdGggaW4gdGhlIE9wZW4gQVBJIFNwZWMgbWF0Y2hpbmcgJHtpbnRlZ3JhdGlvbi5vcGVyYXRpb25JZH1gKVxuICAgICAgfSBlbHNlIHtcbiAgICAgICAgY29uc3QgZnVuY3Rpb25OYW1lID0gYCR7cHJvcHMuZnVuY3Rpb25OYW1lUHJlZml4fS0ke2ludGVncmF0aW9uLm9wZXJhdGlvbklkfWBcbiAgICAgICAgY29uc3QgZnVuYyA9IG5ldyBsYW1iZGEuRnVuY3Rpb24odGhpcywgZnVuY3Rpb25OYW1lLCB7XG4gICAgICAgICAgLi4uaW50ZWdyYXRpb24sXG4gICAgICAgICAgZnVuY3Rpb25OYW1lLFxuICAgICAgICAgIGNvZGU6IGxhbWJkYS5Bc3NldENvZGUuZnJvbUFzc2V0KFxuICAgICAgICAgICAgaW50ZWdyYXRpb24uc291cmNlUGF0aFxuICAgICAgICAgICksXG4gICAgICAgICAgbG9nUmV0ZW50aW9uOiBpbnRlZ3JhdGlvbi5sb2dSZXRlbnRpb24gPz8gOTAsXG4gICAgICAgICAgdGltZW91dDogaW50ZWdyYXRpb24udGltZW91dCA/PyBEdXJhdGlvbi5zZWNvbmRzKDMpLFxuICAgICAgICAgIG1lbW9yeVNpemU6IGludGVncmF0aW9uLm1lbW9yeVNpemUgPz8gMTI4XG4gICAgICAgIH0pXG5cbiAgICAgICAgdGhpcy5mdW5jdGlvbnNbaW50ZWdyYXRpb24ub3BlcmF0aW9uSWRdID0gZnVuY1xuXG4gICAgICAgIGNvbnN0IGludGdyID0gbmV3IENmbkludGVncmF0aW9uKHRoaXMsIGBpbnRlZ3JhdGlvbi0ke21ldGhvZC5wYXRoLnJlcGxhY2UoL1xcLy9nLCAnLScpfS0ke21ldGhvZC5tZXRob2R9YCwge1xuICAgICAgICAgIGFwaUlkOiB0aGlzLmNmbkFwaS5hdHRyQXBpSWQsXG4gICAgICAgICAgaW50ZWdyYXRpb25UeXBlOiBJbnRlZ3JhdGlvblR5cGUuQVdTX1BST1hZLFxuICAgICAgICAgIHBheWxvYWRGb3JtYXRWZXJzaW9uOiBQYXlsb2FkRm9ybWF0VmVyc2lvbi5WRVJTSU9OXzJfMC52ZXJzaW9uLFxuICAgICAgICAgIGludGVncmF0aW9uVXJpOiBgYXJuOiR7c3RhY2sucGFydGl0aW9ufTphcGlnYXRld2F5OiR7c3RhY2sucmVnaW9ufTpsYW1iZGE6cGF0aC8yMDE1LTAzLTMxL2Z1bmN0aW9ucy8ke2Z1bmMuZnVuY3Rpb25Bcm59L2ludm9jYXRpb25zYFxuICAgICAgICB9KVxuXG4gICAgICAgIGNvbnN0IHJvdXRlID0gbmV3IENmblJvdXRlKHRoaXMsIGByb3V0ZS0ke21ldGhvZC5wYXRoLnJlcGxhY2UoL1xcLy9nLCAnLScpfS0ke21ldGhvZC5tZXRob2R9YCwge1xuICAgICAgICAgIGFwaUlkOiB0aGlzLmNmbkFwaS5hdHRyQXBpSWQsXG4gICAgICAgICAgcm91dGVLZXk6IGAke21ldGhvZC5tZXRob2QudG9VcHBlckNhc2UoKX0gJHttZXRob2QucGF0aH1gLFxuICAgICAgICAgIHRhcmdldDogYGludGVncmF0aW9ucy8ke2ludGdyLmF0dHJJbnRlZ3JhdGlvbklkfWBcbiAgICAgICAgfSlcbiAgICAgICAgdGhpcy5yb3V0ZXMucHVzaChyb3V0ZSlcblxuICAgICAgICBpZiAocHJvcHMuY3VzdG9tQXV0aG9yaXplckxhbWJkYUFybikge1xuICAgICAgICAgIHNwZWMucGF0aHNbbWV0aG9kLnBhdGhdW21ldGhvZC5tZXRob2RdLnNlY3VyaXR5ID0gW1xuICAgICAgICAgICAge1xuICAgICAgICAgICAgICBbQVVUSE9SSVpFUl9LRVldOiBbXVxuICAgICAgICAgICAgfVxuICAgICAgICAgIF1cbiAgICAgICAgfVxuICAgICAgfVxuICAgIH0pXG5cbiAgICAvLyBGaXJzdCBsb29wIHdpdGggYXV0aG9yaXplcnMgdG8gYWRkIHRoZWlyIGNvbmZpZ3VyYXRpb25zIHRvIHRoZSBzcGVjXG4gICAgaWYgKHByb3BzLmN1c3RvbUF1dGhvcml6ZXJMYW1iZGFBcm4pIHtcbiAgICAgIHNwZWMuY29tcG9uZW50cy5zZWN1cml0eVNjaGVtZXMgPSB7fVxuICAgICAgLy8gaHR0cHM6Ly9kb2NzLmF3cy5hbWF6b24uY29tL2FwaWdhdGV3YXkvbGF0ZXN0L2RldmVsb3Blcmd1aWRlL2FwaS1nYXRld2F5LXN3YWdnZXItZXh0ZW5zaW9ucy1hdXRob3JpemVyLmh0bWxcbiAgICAgIHNwZWMuY29tcG9uZW50cy5zZWN1cml0eVNjaGVtZXNbQVVUSE9SSVpFUl9LRVldID1cbiAgICAgICAgdGhpcy50b0F1dGhvcml6ZXJTcGVjKHByb3BzLmN1c3RvbUF1dGhvcml6ZXJMYW1iZGFBcm4sIHN0YWNrLnJlZ2lvbilcbiAgICB9XG5cbiAgICAvLyBTZWNvbmQgbG9vcCB3aXRoIEF1dGhvcml6ZXJzLCBpbiBvcmRlciB0byBhZGQgSW52b2tlRnVuY3Rpb24gcGVybWlzc2lvblxuICAgIC8vIHRvIHRoZSBjcmVhdGVkIEFQSS4gSXQgaGFzIHRvIGJlIHNlcGFyYXRlZCBiZWNhdXNlIHdlIG5lZWQgdGhlIHJlZiBmcm9tIGNmbkFwaVxuICAgIGlmIChwcm9wcy5jdXN0b21BdXRob3JpemVyTGFtYmRhQXJuKSB7XG4gICAgICBjb25zdCBwZXJtaXNzaW9uID0gbmV3IGxhbWJkYS5DZm5QZXJtaXNzaW9uKHRoaXMsICdBdXRob3JpemVyUGVybWlzc2lvbicsIHtcbiAgICAgICAgYWN0aW9uOiAnbGFtYmRhOkludm9rZUZ1bmN0aW9uJyxcbiAgICAgICAgcHJpbmNpcGFsOiAnYXBpZ2F0ZXdheS5hbWF6b25hd3MuY29tJyxcbiAgICAgICAgZnVuY3Rpb25OYW1lOiBwcm9wcy5jdXN0b21BdXRob3JpemVyTGFtYmRhQXJuLFxuICAgICAgICBzb3VyY2VBcm46IGBhcm46JHtzdGFjay5wYXJ0aXRpb259OmV4ZWN1dGUtYXBpOiR7c3RhY2sucmVnaW9ufToke3N0YWNrLmFjY291bnR9OiR7dGhpcy5jZm5BcGkuYXR0ckFwaUlkfS8qLyovKmBcbiAgICAgIH0pXG4gICAgICB0aGlzLnBlcm1pc3Npb25zW0FVVEhPUklaRVJfS0VZXSA9IHBlcm1pc3Npb25cbiAgICB9XG5cbiAgICBPYmplY3Qua2V5cyh0aGlzLmZ1bmN0aW9ucykuZm9yRWFjaCgoZnVuY0tleSwgaWR4KSA9PiB7XG4gICAgICBjb25zdCBmdW5jID0gdGhpcy5mdW5jdGlvbnNbZnVuY0tleV1cbiAgICAgIGNvbnN0IHBlcm1pc3Npb24gPSBuZXcgbGFtYmRhLkNmblBlcm1pc3Npb24odGhpcywgYExhbWJkYVBlcm1pc3Npb25fJHtpZHh9YCwge1xuICAgICAgICBhY3Rpb246ICdsYW1iZGE6SW52b2tlRnVuY3Rpb24nLFxuICAgICAgICBwcmluY2lwYWw6ICdhcGlnYXRld2F5LmFtYXpvbmF3cy5jb20nLFxuICAgICAgICBmdW5jdGlvbk5hbWU6IGZ1bmMuZnVuY3Rpb25OYW1lLFxuICAgICAgICBzb3VyY2VBcm46IGBhcm46JHtzdGFjay5wYXJ0aXRpb259OmV4ZWN1dGUtYXBpOiR7c3RhY2sucmVnaW9ufToke3N0YWNrLmFjY291bnR9OiR7dGhpcy5jZm5BcGkuYXR0ckFwaUlkfS8qLypgXG4gICAgICB9KVxuICAgICAgdGhpcy5wZXJtaXNzaW9uc1tmdW5jS2V5XSA9IHBlcm1pc3Npb25cbiAgICB9KVxuICB9XG5cbiAgLyoqXG4gICAqIEVuYWJsZSBjdXN0b20gZG9tYWluIGZvciB0aGlzIEFQSVxuICAgKiBAcGFyYW0gY3VzdG9tRG9tYWluTmFtZSAtIGN1c3RvbURvbWFpbk5hbWUgdG8gYmUgY3JlYXRlZCBpbiBBcGkgR2F0ZXdheVxuICAgKiBAcGFyYW0gY2VydGlmaWNhdGVBcm4gQXJuIG9mIHRoZSBjZXJ0aWZpY2F0ZSBuZWVkZWQgZm9yIHRoZSBjcmVhdGlvbiBvZiBjdXN0b20gZG9tYWluLiBJdCBtdXN0IGJlIGEgcmVnaW9uYWwgY2VydGlmaWNhdGUuXG4gICAqL1xuICBwdWJsaWMgZW5hYmxlQ3VzdG9tRG9tYWluKFxuICAgIGN1c3RvbURvbWFpbk5hbWU6IHN0cmluZyxcbiAgICBjZXJ0aWZpY2F0ZUFybjogc3RyaW5nLFxuICAgIHpvbmVOYW1lOiBzdHJpbmdcbiAgKSB7XG4gICAgY29uc3QgY2VydGlmaWNhdGUgPSBhY20uQ2VydGlmaWNhdGUuZnJvbUNlcnRpZmljYXRlQXJuKFxuICAgICAgdGhpcyxcbiAgICAgICdEb21haW5DZXJ0aWZpY2F0ZScsXG4gICAgICBjZXJ0aWZpY2F0ZUFyblxuICAgIClcblxuICAgIGNvbnN0IGRvbWFpbk5hbWUgPSBuZXcgRG9tYWluTmFtZSh0aGlzLCAnQ3VzdG9tRG9tYWluTmFtZScsIHtcbiAgICAgIGRvbWFpbk5hbWU6IGN1c3RvbURvbWFpbk5hbWUsXG4gICAgICBjZXJ0aWZpY2F0ZVxuICAgIH0pXG5cbiAgICBjb25zdCByb3V0ZUNvbmZpZzogcm91dGU1My5BUmVjb3JkUHJvcHMgPSB7XG4gICAgICByZWNvcmROYW1lOiBjdXN0b21Eb21haW5OYW1lLFxuICAgICAgem9uZTogcm91dGU1My5Ib3N0ZWRab25lLmZyb21Mb29rdXAodGhpcywgJ1pvbmVMb29rdXAnLCB7XG4gICAgICAgIGRvbWFpbk5hbWU6IHpvbmVOYW1lXG4gICAgICB9KSxcbiAgICAgIHRhcmdldDogcm91dGU1My5SZWNvcmRUYXJnZXQuZnJvbUFsaWFzKHtcbiAgICAgICAgYmluZDogKCkgPT4gKHtcbiAgICAgICAgICBkbnNOYW1lOiBkb21haW5OYW1lLnJlZ2lvbmFsRG9tYWluTmFtZSxcbiAgICAgICAgICBob3N0ZWRab25lSWQ6IGRvbWFpbk5hbWUucmVnaW9uYWxIb3N0ZWRab25lSWRcbiAgICAgICAgfSlcbiAgICAgIH0pXG4gICAgfVxuICAgIGNvbnN0IGFSZWNvcmQgPSBuZXcgcm91dGU1My5BUmVjb3JkKHRoaXMsICdDdXN0b21Eb21haW5BUmVjb3JkJywgcm91dGVDb25maWcpXG4gICAgY29uc3QgYWFhYVJlY29yZCA9IG5ldyByb3V0ZTUzLkFhYWFSZWNvcmQodGhpcywgJ0N1c3RvbURvbWFpbkFBQUFSZWNvcmQnLCByb3V0ZUNvbmZpZylcblxuICAgIGNvbnN0IGFwaU1hcHBpbmcgPSBuZXcgYXBpZ3d2Mi5DZm5BcGlNYXBwaW5nKHRoaXMsICdDdXN0b21Eb21haW5BcGlNYXBwaW5nJywge1xuICAgICAgYXBpSWQ6IHRoaXMuY2ZuQXBpLmF0dHJBcGlJZCxcbiAgICAgIGRvbWFpbk5hbWU6IGN1c3RvbURvbWFpbk5hbWUsXG4gICAgICBzdGFnZTogdGhpcy5hcGlTdGFnZS5zdGFnZU5hbWVcbiAgICB9KVxuXG4gICAgYXBpTWFwcGluZy5ub2RlLmFkZERlcGVuZGVuY3kodGhpcy5jZm5BcGkpXG4gICAgYXBpTWFwcGluZy5ub2RlLmFkZERlcGVuZGVuY3kodGhpcy5hcGlTdGFnZSlcbiAgICBhcGlNYXBwaW5nLm5vZGUuYWRkRGVwZW5kZW5jeShkb21haW5OYW1lKVxuICAgIGFwaU1hcHBpbmcubm9kZS5hZGREZXBlbmRlbmN5KGFSZWNvcmQpXG4gICAgYXBpTWFwcGluZy5ub2RlLmFkZERlcGVuZGVuY3koYWFhYVJlY29yZClcbiAgfVxuXG4gIC8qKlxuICAgKiBFeHRyYWN0cyBwYXRoIGFuZCBtZXRob2QgdGhhdCBtYXAgdG8gdGhlIG9wZXJhdGlvbklkIG5lZWRlZFxuICAgKiBTbyBmaW5kaW5nIHRoZSByaWdodCBwbGFjZSBvbiB0aGUgc3BlYyBpcyBqdXN0IGEgbWF0dGVyIG9mIGFjY2Vzc2luZyB0aGUgcmlnaHQgYXR0cmlidXRlXG4gICAqIEBwYXJhbSBzcGVjXG4gICAqIEByZXR1cm5zIG1ldGhvZHNcbiAgICovXG4gIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBAdHlwZXNjcmlwdC1lc2xpbnQvbm8tZXhwbGljaXQtYW55XG4gIHByaXZhdGUgYnVpbGRNZXRob2RNYXBwaW5ncyhzcGVjOiBhbnkpIHtcbiAgICBjb25zdCBtZXRob2RzID0ge30gYXMgUmVjb3JkPHN0cmluZywgTWV0aG9kTWFwcGluZz5cblxuICAgIE9iamVjdC5lbnRyaWVzKHNwZWMucGF0aHMpLmZvckVhY2goKFtwYXRoLCBwYXRoT2JqXTogW3N0cmluZywgYW55XSkgPT4ge1xuICAgICAgT2JqZWN0LmtleXMocGF0aE9iaikuZm9yRWFjaCgobWV0aG9kKSA9PiB7XG4gICAgICAgIG1ldGhvZHNbcGF0aE9ialttZXRob2RdWyd4LWFtYXpvbi1hcGlnYXRld2F5LWludGVncmF0aW9uJ10udXJpXSA9IHtcbiAgICAgICAgICBwYXRoLFxuICAgICAgICAgIG1ldGhvZFxuICAgICAgICB9XG4gICAgICB9KVxuICAgIH0pXG5cbiAgICByZXR1cm4gbWV0aG9kc1xuICB9XG5cbiAgcHJpdmF0ZSB0b0F1dGhvcml6ZXJTcGVjKGxhbWJkYUF1dGhvcml6ZXJBcm46IHN0cmluZywgcmVnaW9uOiBzdHJpbmcpIHtcbiAgICBjb25zdCB1cmkgPSBgYXJuOmF3czphcGlnYXRld2F5OiR7cmVnaW9ufTpsYW1iZGE6cGF0aC8yMDE1LTAzLTMxL2Z1bmN0aW9ucy8ke2xhbWJkYUF1dGhvcml6ZXJBcm59L2ludm9jYXRpb25zYFxuXG4gICAgcmV0dXJuIHtcbiAgICAgIHR5cGU6ICdhcGlLZXknLFxuICAgICAgbmFtZTogJ0F1dGhvcml6YXRpb24nLFxuICAgICAgaW46ICdoZWFkZXInLFxuICAgICAgJ3gtYW1hem9uLWFwaWdhdGV3YXktYXV0aG9yaXplcic6IHtcbiAgICAgICAgdHlwZTogJ3JlcXVlc3QnLFxuICAgICAgICBpZGVudGl0eVNvdXJjZTogJyRyZXF1ZXN0LmhlYWRlci5BdXRob3JpemF0aW9uJywgLy8gUmVxdWVzdCBwYXJhbWV0ZXIgbWFwcGluZyBleHByZXNzaW9uIG9mIHRoZSBpZGVudGl0eSBzb3VyY2UuIEluIHRoaXMgZXhhbXBsZSwgaXQgaXMgdGhlICdhdXRoJyBoZWFkZXIuXG4gICAgICAgIGF1dGhvcml6ZXJVcmk6IHVyaSxcbiAgICAgICAgYXV0aG9yaXplclBheWxvYWRGb3JtYXRWZXJzaW9uOiAnMi4wJyxcbiAgICAgICAgYXV0aG9yaXplclJlc3VsdFR0bEluU2Vjb25kczogMzAwXG4gICAgICB9XG4gICAgfVxuICB9XG59XG5cbmV4cG9ydCBjbGFzcyBSb290UmVzb3VyY2UgZXh0ZW5kcyBSZXNvdXJjZUJhc2Uge1xuICBwYXJlbnRSZXNvdXJjZT86IElSZXNvdXJjZSB8IHVuZGVmaW5lZFxuICBhcGk6IElSZXN0QXBpXG4gIHJlc291cmNlSWQ6IHN0cmluZ1xuICBwYXRoOiBzdHJpbmdcbiAgZGVmYXVsdEludGVncmF0aW9uPzogSW50ZWdyYXRpb24gfCB1bmRlZmluZWRcbiAgZGVmYXVsdE1ldGhvZE9wdGlvbnM/OiBNZXRob2RPcHRpb25zIHwgdW5kZWZpbmVkXG4gIGRlZmF1bHRDb3JzUHJlZmxpZ2h0T3B0aW9ucz86IENvcnNPcHRpb25zIHwgdW5kZWZpbmVkXG5cbiAgY29uc3RydWN0b3IoYXBpOiBJUmVzdEFwaSwgcmVzb3VyY2VJZDogc3RyaW5nKSB7XG4gICAgc3VwZXIoYXBpLCByZXNvdXJjZUlkKVxuICAgIHRoaXMuYXBpID0gYXBpXG4gICAgdGhpcy5yZXNvdXJjZUlkID0gcmVzb3VyY2VJZFxuICAgIHRoaXMucGF0aCA9ICcvJ1xuICB9XG59XG4iXX0=