diff --git a/API.md b/API.md index 0ac3430..250cff4 100644 --- a/API.md +++ b/API.md @@ -235,6 +235,8 @@ const internalApiGatewayProps: InternalApiGatewayProps = { ... } | stage | string | Stage name used for all cloudformation resource names and internal aws resource names. | | vpcEndpoint | aws-cdk-lib.aws_ec2.IInterfaceVpcEndpoint | VPC endpoint id of execute-api vpc endpoint. | | apiBasePathMappingPath | string | Path for custom domain base path mapping that will be attached to the api gateway. | +| binaryMediaTypes | string[] | Binary media types for the internal api gateway. | +| minimumCompressionSize | number | minimum compression size for the internal api gateway. | --- @@ -288,6 +290,30 @@ Path for custom domain base path mapping that will be attached to the api gatewa --- +##### `binaryMediaTypes`Optional + +```typescript +public readonly binaryMediaTypes: string[]; +``` + +- *Type:* string[] + +Binary media types for the internal api gateway. + +--- + +##### `minimumCompressionSize`Optional + +```typescript +public readonly minimumCompressionSize: number; +``` + +- *Type:* number + +minimum compression size for the internal api gateway. + +--- + ### InternalServiceProps Properties for InternalService. @@ -304,7 +330,7 @@ const internalServiceProps: InternalServiceProps = { ... } | **Name** | **Type** | **Description** | | --- | --- | --- | -| hostedZoneName | string | Name of hosted zone that will be used for the custom domain. | +| hostedZone | aws-cdk-lib.aws_route53.IHostedZone | Hosted zone that will be used for the custom domain. | | subDomain | string | Subdomain attached to hosted zone name. | | subjectAlternativeNames | string[] | List of alternative domains attached to the solution. | | subnetSelection | aws-cdk-lib.aws_ec2.SubnetSelection | Subnets attached to the application load balancer. | @@ -313,15 +339,15 @@ const internalServiceProps: InternalServiceProps = { ... } --- -##### `hostedZoneName`Required +##### `hostedZone`Required ```typescript -public readonly hostedZoneName: string; +public readonly hostedZone: IHostedZone; ``` -- *Type:* string +- *Type:* aws-cdk-lib.aws_route53.IHostedZone -Name of hosted zone that will be used for the custom domain. +Hosted zone that will be used for the custom domain. --- diff --git a/README.md b/README.md index 22d25a3..eb14805 100644 --- a/README.md +++ b/README.md @@ -1,132 +1,164 @@ # CDK Internal Gateway -This CDK construct is used to create internal serverless applications and websites. - -By default the aws api gateway endpoints are public and accessible from the internet. -Even in times of zero trust, for larger companies it`s not a bad idea to have an additional security layer for internal applications. -But it is still a huge of effort to configure and implement all aws components in a secure manner. - -This construct tries to simplify the setup. +This CDK construct simplifies the setup of **internal serverless applications** for large companies and enterprises. ## Features -- no traffic will be routed over the internet -- restricts access to the internal network of your company -- create and attach custom domains and certificates -- create your internal websites and backends using api gateway integrations (see samples folder) +- creates all aws components needed to allow access only from the internal network of your company in a secure manner +- modularized approach with separate constructs + - add multiple internal api gateways to the same internal service to save costs and keep flexibility + +--- ## Requirements -- CDK V2 -- VPC -- VPC Endpoint for execute-api +- CDK V2 (2.46.0) +- A VPC +- A VPC Endpoint for execute-api +- A Hosted Zone +- Internally accessible subnets (for the load balancer) ## Installation +Using Typescript for aws cdk + ```bash -npm i +npm i cdk-internal-gateway ``` ## Architecture -![cdk internal gateway](cdk-internal-gateway.drawio.png ) +![cdk-internal-gateway-architecture](cdk-internal-gateway.drawio.png ) + +### Technical Details -## How to use it ? +- creates an internal application loadbalancer + - forwards traffic to VPC endpoint for execute-api + - redirect http to https +- provides a securely configured apigateway resource out of the box + - attach your aws components to the internal apigateway resource + - sets api gateway to PRIVATE mode + - sets resource policies to only allow traffic from vpc endpoint +- generates and attaches custom domains to the API Gateway +- generates and attaches certificates to the the API Gateway and the loadbalancer -Let`s assume we want to create a simple internal api for our company. +--- + +## Usage + +> Let`s assume we create a simple internal api for our company and start with a single lambda function... 1. Create a file called `/lib/my-new-stack.ts` -```typescript -import { aws_apigateway as apigateway, aws_ec2 as ec2, aws_lambda as lambda, Stack, StackProps } from 'aws-cdk-lib'; -import { HttpMethod } from 'aws-cdk-lib/aws-events'; -import { InternalApiGatewayStack, InternalApiGatewayStackProps, InternalServiceStack } from 'cdk-internal-gateway'; -import { Construct } from 'constructs'; -import * as path from 'path'; - - -// Create a new stack that inherits from the InternalApiGateway Construct -// Attach your lambda function to the internalApiGateway (member variable) -export class ServerlessStack extends InternalApiGatewayStack { - constructor(scope: Construct, id: string, props: InternalApiGatewayStackProps) { - super(scope, id, props); - this.internalApiGateway.root; - const defaultLambdaJavascript = this.internalApiGateway.root.resourceForPath("hey-js"); - const defaultHandlerJavascript = new lambda.Function( - this, - `backendLambdaJavascript`, - { - functionName: `js-lambda`, - runtime: lambda.Runtime.NODEJS_14_X, - handler: "index.handler", - code: lambda.Code.fromAsset(path.join(__dirname, "../src")), - } - ); - - defaultLambdaJavascript.addMethod( - HttpMethod.GET, - new apigateway.LambdaIntegration(defaultHandlerJavascript) - ); + ```typescript + import { aws_apigateway as apigateway, aws_ec2 as ec2, aws_lambda as lambda, Stack, StackProps } from 'aws-cdk-lib'; + import { HttpMethod } from 'aws-cdk-lib/aws-events'; + import { InternalApiGatewayStack, InternalApiGatewayStackProps, InternalServiceStack } from 'cdk-internal-gateway'; + import { Construct } from 'constructs'; + import * as path from 'path'; + + + // Create a new stack that inherits from the InternalApiGateway Construct + export class ServerlessStack extends InternalApiGatewayStack { + constructor(scope: Construct, id: string, props: InternalApiGatewayStackProps) { + super(scope, id, props); + + // The internal api gateway is available as member variable + // Attach your lambda function to the this.internalApiGateway + const defaultLambdaJavascript = this.internalApiGateway.root.resourceForPath("hey-js"); + const defaultHandlerJavascript = new lambda.Function( + this, + `backendLambdaJavascript`, + { + functionName: `js-lambda`, + runtime: lambda.Runtime.NODEJS_14_X, + handler: "index.handler", + code: lambda.Code.fromAsset(path.join(__dirname, "../src")), + } + ); + + defaultLambdaJavascript.addMethod( + HttpMethod.GET, + new apigateway.LambdaIntegration(defaultHandlerJavascript) + ); + } } -} - -// Create a new stack that contains the whole service with all nested stacks -export class ServiceStack extends Stack { - constructor(scope: Construct, id: string, props: StackProps) { - super(scope, id, props); - - // get all parameters to create the internal service stack - const vpc = ec2.Vpc.fromLookup(this, 'vpcLookup', { vpcId: 'vpc-1234567890' }); - const internalSubnetIds = ['subnet-0b1e1c6c7d8e9f0a2', 'subnet-0b1e1c6c7d8e9f0a3']; - const subnetSelection = { - subnets: internalSubnetIds.map((ip, index) => - ec2.Subnet.fromSubnetId(this, `Subnet${index}`, ip), - ), - }; - - // create the internal service stack - const internalServiceStack = new InternalServiceStack(this, 'InternalServiceStack', { - hostedZoneName: 'example.com', - subnetSelection: subnetSelection, - vpcEndpointId: 'vpce-1234567890', - vpcEndpointIPAddresses: ['192.168.2.1', '192.168.2.2'], - vpc: vpc, - subjectAlternativeNames: ['internal.example.com'], - subDomain: "internal-service" - }) - - // create your stack that inherits from the InternalApiGatewayStack - new ServerlessStack(this, 'MyProjectStack', { - domains: serviceStack.domains, - stage: "dev", - vpcEndpointId: serviceStack.vpcEndpointId, - }) + + // Create a new stack that contains the whole service with all nested stacks + export class ServiceStack extends Stack { + constructor(scope: Construct, id: string, props: StackProps) { + super(scope, id, props); + + // get all parameters to create the internal service stack + const vpc = ec2.Vpc.fromLookup(this, 'vpcLookup', { vpcId: 'vpc-1234567890' }); + const subnetSelection = { + subnets: ['subnet-0b1e1c6c7d8e9f0a2', 'subnet-0b1e1c6c7d8e9f0a3'].map((ip, index) => + ec2.Subnet.fromSubnetId(this, `Subnet${index}`, ip), + ), + }; + const hostedZone = route53.HostedZone.fromLookup(stack, 'hostedzone', { + domainName: 'test.aws1234.com', + privateZone: true, + vpcId: vpc.vpcId, + }); + const vpcEndpoint = + ec2.InterfaceVpcEndpoint.fromInterfaceVpcEndpointAttributes( + stack, + 'vpcEndpoint', + { + port: 443, + vpcEndpointId: 'vpce-1234567890', + }, + ); + + // create the internal service stack + const internalServiceStack = new InternalServiceStack(this, 'InternalServiceStack', { + hostedZone: hostedZone, + subnetSelection: subnetSelection, + vpcEndpointIPAddresses: ['192.168.2.1', '192.168.2.2'], + vpc: vpc, + subjectAlternativeNames: ['internal.example.com'], + subDomain: "internal-service" + }) + + // create your stack that inherits from the InternalApiGatewayStack + new ServerlessStack(this, 'MyProjectStack', { + domains: serviceStack.domains, + stage: "dev", + vpcEndpoint: vpcEndpoint, + }) + } } -} -``` + ``` -2. Reference the newly created `ServiceStack` in the default `/bin/{project}.ts` file e.g. like this +1. Reference the newly created `ServiceStack` in the default `/bin/{project}.ts` file e.g. like this -```typescript -new ServiceStack(app, 'MyProjectStack', { - env: - { - account: process.env.CDK_DEPLOY_ACCOUNT || process.env.CDK_DEFAULT_ACCOUNT, - region: process.env.CDK_DEPLOY_REGION || process.env.CDK_DEFAULT_REGION - } -``` + ```typescript + new ServiceStack(app, 'MyProjectStack', { + env: + { + account: process.env.CDK_DEPLOY_ACCOUNT || process.env.CDK_DEFAULT_ACCOUNT, + region: process.env.CDK_DEPLOY_REGION || process.env.CDK_DEFAULT_REGION + } + ``` + +--- + +## Background + +By default the aws api gateway endpoints are public and accessible from the internet. + +Even in times of zero trust, for larger companies with a lof of engineering teams it makes sense to have an additional network security layer for internal applications to reduce the attack surface. + +It is still a huge effort to configure and implement all aws components in a secure manner and this is why I created this construct. ## Costs You have to expect basic infra costs for 2 components in this setup: -| Count | Type | Estimated Costs | +| Count | Type | Estimated Costs | |---|---|---| -|1 x| application load balancer | 20 $ | +|1 x| application load balancer | 20 $ | |2 x| network interfaces for the vpc endpoint | 16 $ | A shared vpc can lower the costs as vpc endpoint and their network interfaces can be used together... - - - - diff --git a/src/internal-apigateway.ts b/src/internal-apigateway.ts index f4e3eaa..4d5bac5 100644 --- a/src/internal-apigateway.ts +++ b/src/internal-apigateway.ts @@ -1,7 +1,6 @@ import { aws_apigateway as apigateway, aws_iam as iam, - Names, } from 'aws-cdk-lib'; import { IInterfaceVpcEndpoint } from 'aws-cdk-lib/aws-ec2'; import { Construct } from 'constructs'; @@ -30,6 +29,16 @@ export interface InternalApiGatewayProps { * Path for custom domain base path mapping that will be attached to the api gateway */ readonly apiBasePathMappingPath?: string; + + /** + * Binary media types for the internal api gateway + */ + readonly binaryMediaTypes?: string[] | undefined; + + /** + * minimum compression size for the internal api gateway + */ + readonly minimumCompressionSize?: number | undefined; } export abstract class InternalApiGateway extends Construct { @@ -41,10 +50,10 @@ export abstract class InternalApiGateway extends Construct { * It is not exposed to the internet. * It is only accessible from the load balancer`s target group. */ - protected readonly internalApiGateway: apigateway.LambdaRestApi; + protected readonly apiGateway: apigateway.LambdaRestApi; constructor(scope: Construct, id: string, props: InternalApiGatewayProps) { super(scope, id); - const uid: string = Names.uniqueId(scope); + const apiResourcePolicy = new iam.PolicyDocument({ statements: [ new iam.PolicyStatement({ @@ -72,11 +81,11 @@ export abstract class InternalApiGateway extends Construct { ], }); - this.internalApiGateway = new apigateway.RestApi( + this.apiGateway = new apigateway.RestApi( this, - `Gateway-${uid}`, + `Gateway-${id}`, { - restApiName: `gateway-${uid}`, + restApiName: `gateway-${id}`, description: 'This service serves an internal api gateway', endpointConfiguration: { types: [apigateway.EndpointType.PRIVATE], @@ -86,8 +95,8 @@ export abstract class InternalApiGateway extends Construct { deployOptions: { stageName: props.stage, }, - binaryMediaTypes: ['*/*'], - minimumCompressionSize: 1000, + binaryMediaTypes: props.binaryMediaTypes, + minimumCompressionSize: props.minimumCompressionSize, }, ); @@ -97,8 +106,8 @@ export abstract class InternalApiGateway extends Construct { `-${domainItem}`, { domainName: domainItem, - restApi: this.internalApiGateway, - stage: this.internalApiGateway.deploymentStage, + restApi: this.apiGateway, + stage: this.apiGateway.deploymentStage, basePath: props.apiBasePathMappingPath ? props.apiBasePathMappingPath : '', }, ); diff --git a/src/internal-service.ts b/src/internal-service.ts index c9d22ed..51a91d6 100644 --- a/src/internal-service.ts +++ b/src/internal-service.ts @@ -7,7 +7,6 @@ import { aws_route53 as route53, aws_route53_targets as targets, CfnOutput, - Names, } from 'aws-cdk-lib'; import { IVpc, SubnetSelection } from 'aws-cdk-lib/aws-ec2'; import { Construct } from 'constructs'; @@ -42,9 +41,9 @@ export interface InternalServiceProps { readonly subDomain: string; /** - * Name of hosted zone that will be used for the custom domain. + * Hosted zone that will be used for the custom domain. */ - readonly hostedZoneName: string; + readonly hostedZone: route53.IHostedZone; } export class InternalService extends Construct { @@ -56,35 +55,29 @@ export class InternalService extends Construct { constructor(scope: Construct, id: string, props: InternalServiceProps) { super(scope, id); - const uid: string = Names.uniqueId(scope); + const domainName = `${props.subDomain}.${props.hostedZone.zoneName}`; - const domainName = `${props.subDomain}.${props.hostedZoneName}`; - const hostedZone = route53.HostedZone.fromLookup(this, `HostedZone-${uid}`, { - domainName: `${props.hostedZoneName}`, - privateZone: true, - vpcId: props.vpc.vpcId, - }); - - const certificate = new certificatemanager.Certificate(this, `SSLCertificate-${uid}`, { + const certificate = new certificatemanager.Certificate(this, `SSLCertificate-${id}`, { domainName: domainName, subjectAlternativeNames: props.subjectAlternativeNames, validation: certificatemanager.CertificateValidation.fromDnsMultiZone({ - domainName: hostedZone, + domainName: props.hostedZone, }), }); - const domain = new apigateway.DomainName(this, `ApiGatewayCustomDomain-${uid}`, { - domainName: `${props.subDomain}.${props.hostedZoneName}`, + const domain = new apigateway.DomainName(this, `ApiGatewayCustomDomain-${id}`, { + domainName: `${props.subDomain}.${props.hostedZone.zoneName}`, certificate: certificate, endpointType: apigateway.EndpointType.REGIONAL, securityPolicy: apigateway.SecurityPolicy.TLS_1_2, }); + this.domains = [domain]; for (const domainItem of props.subjectAlternativeNames) { const sanDomain = new apigateway.DomainName( this, - `Domain-${domainItem}-${uid}`, + `Domain-${domainItem}-${id}`, { domainName: domainItem, certificate: certificate, @@ -97,7 +90,7 @@ export class InternalService extends Construct { const loadBalancerSecurityGroup = new ec2.SecurityGroup( this, - `LoadBalancerSecurityGroup-${uid}`, + `LoadBalancerSecurityGroup-${id}`, { securityGroupName: '-lb-sg', vpc: props.vpc, @@ -114,7 +107,7 @@ export class InternalService extends Construct { const applicationLoadBalancer = new elb.ApplicationLoadBalancer( this, - `ApplicationLoadBalancer-${uid}`, + `ApplicationLoadBalancer-${id}`, { vpc: props.vpc, vpcSubnets: { @@ -122,15 +115,15 @@ export class InternalService extends Construct { }, internetFacing: false, securityGroup: loadBalancerSecurityGroup, - loadBalancerName: `lb-${uid}`, + loadBalancerName: `lb-${id}`, }, ); // Add http-to-https redirect applicationLoadBalancer.addRedirect(); - new route53.ARecord(this, `Route53Record-${uid}`, { - zone: hostedZone, + new route53.ARecord(this, `Route53Record-${id}`, { + zone: props.hostedZone, target: route53.RecordTarget.fromAlias( new targets.LoadBalancerTarget(applicationLoadBalancer), ), @@ -143,25 +136,25 @@ export class InternalService extends Construct { targetGroupTargets.push(new elasticloadbalancingv2targets.IpTarget(ip)); } - const targetGroup = new elb.ApplicationTargetGroup(this, `TargetGroup-${uid}`, { + const targetGroup = new elb.ApplicationTargetGroup(this, `TargetGroup-${id}`, { port: 443, vpc: props.vpc, protocol: elb.ApplicationProtocol.HTTPS, targetType: elb.TargetType.IP, targets: targetGroupTargets, - targetGroupName: `tg-${uid}`, + targetGroupName: `tg-${id}`, }); - const listener = applicationLoadBalancer.addListener(`Listener-${uid}`, { + const listener = applicationLoadBalancer.addListener(`Listener-${id}`, { port: 443, certificates: [certificate], }); - listener.addTargetGroups(`TargetGroupAttachment-${uid}`, { + listener.addTargetGroups(`TargetGroupAttachment-${id}`, { targetGroups: [targetGroup], }); - new CfnOutput(this, `DomainUrl-${uid}`, { + new CfnOutput(this, `DomainUrl-${id}`, { value: `https://${domainName}`, description: 'service url', exportName: '-DomainUrl', diff --git a/test/apigateway.test.ts b/test/apigateway.test.ts index ee90d57..6380e26 100644 --- a/test/apigateway.test.ts +++ b/test/apigateway.test.ts @@ -1,43 +1,37 @@ -import { App, aws_ec2 as ec2, Stack } from 'aws-cdk-lib'; +import { App, aws_ec2 as ec2, aws_route53 as route53, Stack } from 'aws-cdk-lib'; import { Match, Template } from 'aws-cdk-lib/assertions'; -import { MockIntegration, PassthroughBehavior } from 'aws-cdk-lib/aws-apigateway'; import { Construct } from 'constructs'; import { InternalApiGateway, InternalApiGatewayProps, InternalService } from '../src'; export class ApiGatewayStackTest extends InternalApiGateway { - internalApiGateway: any; constructor(scope: Construct, id: string, props: InternalApiGatewayProps) { super(scope, id, props); - this.internalApiGateway.root.addMethod('GET', new MockIntegration( - { - integrationResponses: [ - { - statusCode: '200', - }, - ], - passthroughBehavior: PassthroughBehavior.WHEN_NO_MATCH, - requestTemplates: { - 'application/json': '{"statusCode": 200}', - }, - }, - )); + this.apiGateway.root.addMethod('GET', undefined); } } - -let app = new App(); +let app: App; +let stack: Stack; let internalServiceStack: InternalService; -const stack = new Stack(app, 'test', { - env: { - account: '123456789012', - region: 'us-east-1', - }, -}); +let vpcEndpointId: ec2.IInterfaceVpcEndpoint; -beforeAll(() => { +beforeEach(() => { + app = new App(); + stack = new Stack(app, 'test', { + env: { + account: '123456789012', + region: 'us-east-1', + }, + }); const vpc = ec2.Vpc.fromLookup(stack, 'vpc', { vpcId: 'vpc-1234567' }); const internalSubnetIds = ['subnet-1234567890', 'subnet-1234567890']; + const hostedZone = route53.HostedZone.fromLookup(stack, 'hostedzone', { + domainName: 'test.aws1234.com', + privateZone: true, + vpcId: vpc.vpcId, + }); + internalServiceStack = new InternalService(stack, 'internalServiceStack', { vpc: vpc, subnetSelection: { @@ -47,15 +41,10 @@ beforeAll(() => { }, vpcEndpointIPAddresses: ['192.168.2.1', '192.168.2.2'], subjectAlternativeNames: ['internalservice-dev.test.com', 'internalservice-dev.test2.com'], - hostedZoneName: 'test.aws1234.com', + hostedZone: hostedZone, subDomain: 'internalservice-dev', }); - -}); - - -test('Api Gateway Stack provider', () => { - const vpcEndpointId = + vpcEndpointId = ec2.InterfaceVpcEndpoint.fromInterfaceVpcEndpointAttributes( stack, 'vpcEndpoint', @@ -64,6 +53,10 @@ test('Api Gateway Stack provider', () => { vpcEndpointId: 'vpce-1234567890', }, ); +}); + + +test('Api Gateway Stack provider - set default values', () => { new ApiGatewayStackTest(stack, 'apiGatewayStack', { stage: 'dev', @@ -71,7 +64,6 @@ test('Api Gateway Stack provider', () => { vpcEndpoint: vpcEndpointId, }); - const template = Template.fromStack(stack); template.hasResourceProperties('AWS::ApiGateway::RestApi', Match.objectLike({ EndpointConfiguration: { @@ -81,6 +73,12 @@ test('Api Gateway Stack provider', () => { }, }, )); + + template.hasResourceProperties('AWS::ApiGateway::BasePathMapping', Match.objectLike({ + BasePath: '', + }, + )); + template.hasResourceProperties('AWS::ApiGateway::RestApi', Match.objectLike({ Policy: { Statement: [ @@ -119,14 +117,14 @@ test('Api Gateway Stack provider', () => { expect(template).toMatchInlineSnapshot(` Object { "Outputs": Object { - "apiGatewayStackGatewaytestEndpointDBCBAA9A": Object { + "apiGatewayStackGatewayapiGatewayStackEndpointD06AFED4": Object { "Value": Object { "Fn::Join": Array [ "", Array [ "https://", Object { - "Ref": "apiGatewayStackGatewaytestB21206F6", + "Ref": "apiGatewayStackGatewayapiGatewayStackC685BA6E", }, ".execute-api.us-east-1.", Object { @@ -134,14 +132,14 @@ Object { }, "/", Object { - "Ref": "apiGatewayStackGatewaytestDeploymentStagedev417B627A", + "Ref": "apiGatewayStackGatewayapiGatewayStackDeploymentStagedevF51461BA", }, "/", ], ], }, }, - "internalServiceStackDomainUrltest2935E3B3": Object { + "internalServiceStackDomainUrlinternalServiceStackB921B240": Object { "Description": "service url", "Export": Object { "Name": "-DomainUrl", @@ -157,15 +155,15 @@ Object { }, }, "Resources": Object { - "apiGatewayStackGatewaytestAccount62BFF2F7": Object { + "apiGatewayStackGatewayapiGatewayStackAccount7F19EDD7": Object { "DeletionPolicy": "Retain", "DependsOn": Array [ - "apiGatewayStackGatewaytestB21206F6", + "apiGatewayStackGatewayapiGatewayStackC685BA6E", ], "Properties": Object { "CloudWatchRoleArn": Object { "Fn::GetAtt": Array [ - "apiGatewayStackGatewaytestCloudWatchRoleA79896DE", + "apiGatewayStackGatewayapiGatewayStackCloudWatchRole492310A9", "Arn", ], }, @@ -173,11 +171,8 @@ Object { "Type": "AWS::ApiGateway::Account", "UpdateReplacePolicy": "Retain", }, - "apiGatewayStackGatewaytestB21206F6": Object { + "apiGatewayStackGatewayapiGatewayStackC685BA6E": Object { "Properties": Object { - "BinaryMediaTypes": Array [ - "*/*", - ], "Description": "This service serves an internal api gateway", "EndpointConfiguration": Object { "Types": Array [ @@ -187,8 +182,7 @@ Object { "vpce-1234567890", ], }, - "MinimumCompressionSize": 1000, - "Name": "gateway-test", + "Name": "gateway-apiGatewayStack", "Policy": Object { "Statement": Array [ Object { @@ -223,7 +217,7 @@ Object { }, "Type": "AWS::ApiGateway::RestApi", }, - "apiGatewayStackGatewaytestCloudWatchRoleA79896DE": Object { + "apiGatewayStackGatewayapiGatewayStackCloudWatchRole492310A9": Object { "DeletionPolicy": "Retain", "Properties": Object { "AssumeRolePolicyDocument": Object { @@ -256,107 +250,98 @@ Object { "Type": "AWS::IAM::Role", "UpdateReplacePolicy": "Retain", }, - "apiGatewayStackGatewaytestDeployment476DB131ece923dbf5d8c9cdacaf528a759f2763": Object { + "apiGatewayStackGatewayapiGatewayStackDeployment62F75EF65a7060afb32f6c45f7e741ade92eab17": Object { "DependsOn": Array [ - "apiGatewayStackGatewaytestGET54FBF6E8", + "apiGatewayStackGatewayapiGatewayStackGETF8D24D55", ], "Properties": Object { "Description": "This service serves an internal api gateway", "RestApiId": Object { - "Ref": "apiGatewayStackGatewaytestB21206F6", + "Ref": "apiGatewayStackGatewayapiGatewayStackC685BA6E", }, }, "Type": "AWS::ApiGateway::Deployment", }, - "apiGatewayStackGatewaytestDeploymentStagedev417B627A": Object { + "apiGatewayStackGatewayapiGatewayStackDeploymentStagedevF51461BA": Object { "DependsOn": Array [ - "apiGatewayStackGatewaytestAccount62BFF2F7", + "apiGatewayStackGatewayapiGatewayStackAccount7F19EDD7", ], "Properties": Object { "DeploymentId": Object { - "Ref": "apiGatewayStackGatewaytestDeployment476DB131ece923dbf5d8c9cdacaf528a759f2763", + "Ref": "apiGatewayStackGatewayapiGatewayStackDeployment62F75EF65a7060afb32f6c45f7e741ade92eab17", }, "RestApiId": Object { - "Ref": "apiGatewayStackGatewaytestB21206F6", + "Ref": "apiGatewayStackGatewayapiGatewayStackC685BA6E", }, "StageName": "dev", }, "Type": "AWS::ApiGateway::Stage", }, - "apiGatewayStackGatewaytestGET54FBF6E8": Object { + "apiGatewayStackGatewayapiGatewayStackGETF8D24D55": Object { "Properties": Object { "AuthorizationType": "NONE", "HttpMethod": "GET", "Integration": Object { - "IntegrationResponses": Array [ - Object { - "StatusCode": "200", - }, - ], - "PassthroughBehavior": "WHEN_NO_MATCH", - "RequestTemplates": Object { - "application/json": "{\\"statusCode\\": 200}", - }, "Type": "MOCK", }, "ResourceId": Object { "Fn::GetAtt": Array [ - "apiGatewayStackGatewaytestB21206F6", + "apiGatewayStackGatewayapiGatewayStackC685BA6E", "RootResourceId", ], }, "RestApiId": Object { - "Ref": "apiGatewayStackGatewaytestB21206F6", + "Ref": "apiGatewayStackGatewayapiGatewayStackC685BA6E", }, }, "Type": "AWS::ApiGateway::Method", }, - "apiGatewayStacktestinternalServiceStackApiGatewayCustomDomaintest5FD0C666": Object { + "apiGatewayStacktestinternalServiceStackApiGatewayCustomDomaininternalServiceStack4041A8F9": Object { "Properties": Object { "BasePath": "", "DomainName": Object { - "Ref": "internalServiceStackApiGatewayCustomDomaintest813BB8C2", + "Ref": "internalServiceStackApiGatewayCustomDomaininternalServiceStack836326F5", }, "RestApiId": Object { - "Ref": "apiGatewayStackGatewaytestB21206F6", + "Ref": "apiGatewayStackGatewayapiGatewayStackC685BA6E", }, "Stage": Object { - "Ref": "apiGatewayStackGatewaytestDeploymentStagedev417B627A", + "Ref": "apiGatewayStackGatewayapiGatewayStackDeploymentStagedevF51461BA", }, }, "Type": "AWS::ApiGateway::BasePathMapping", }, - "apiGatewayStacktestinternalServiceStackDomaininternalservicedevtest2comtest2D55BBDF": Object { + "apiGatewayStacktestinternalServiceStackDomaininternalservicedevtest2cominternalServiceStackBE72A12C": Object { "Properties": Object { "BasePath": "", "DomainName": Object { - "Ref": "internalServiceStackDomaininternalservicedevtest2comtestCB38E02B", + "Ref": "internalServiceStackDomaininternalservicedevtest2cominternalServiceStack5EFB1D61", }, "RestApiId": Object { - "Ref": "apiGatewayStackGatewaytestB21206F6", + "Ref": "apiGatewayStackGatewayapiGatewayStackC685BA6E", }, "Stage": Object { - "Ref": "apiGatewayStackGatewaytestDeploymentStagedev417B627A", + "Ref": "apiGatewayStackGatewayapiGatewayStackDeploymentStagedevF51461BA", }, }, "Type": "AWS::ApiGateway::BasePathMapping", }, - "apiGatewayStacktestinternalServiceStackDomaininternalservicedevtestcomtestD3261655": Object { + "apiGatewayStacktestinternalServiceStackDomaininternalservicedevtestcominternalServiceStack5EFA7076": Object { "Properties": Object { "BasePath": "", "DomainName": Object { - "Ref": "internalServiceStackDomaininternalservicedevtestcomtest2CE9FA76", + "Ref": "internalServiceStackDomaininternalservicedevtestcominternalServiceStackB6868874", }, "RestApiId": Object { - "Ref": "apiGatewayStackGatewaytestB21206F6", + "Ref": "apiGatewayStackGatewayapiGatewayStackC685BA6E", }, "Stage": Object { - "Ref": "apiGatewayStackGatewaytestDeploymentStagedev417B627A", + "Ref": "apiGatewayStackGatewayapiGatewayStackDeploymentStagedevF51461BA", }, }, "Type": "AWS::ApiGateway::BasePathMapping", }, - "internalServiceStackApiGatewayCustomDomaintest813BB8C2": Object { + "internalServiceStackApiGatewayCustomDomaininternalServiceStack836326F5": Object { "Properties": Object { "DomainName": "internalservice-dev.test.aws1234.com", "EndpointConfiguration": Object { @@ -365,13 +350,13 @@ Object { ], }, "RegionalCertificateArn": Object { - "Ref": "internalServiceStackSSLCertificatetest1C36642E", + "Ref": "internalServiceStackSSLCertificateinternalServiceStack283B9A17", }, "SecurityPolicy": "TLS_1_2", }, "Type": "AWS::ApiGateway::DomainName", }, - "internalServiceStackApplicationLoadBalancertestF81D1559": Object { + "internalServiceStackApplicationLoadBalancerinternalServiceStackA9484EF3": Object { "Properties": Object { "LoadBalancerAttributes": Array [ Object { @@ -379,12 +364,12 @@ Object { "Value": "false", }, ], - "Name": "lb-test", + "Name": "lb-internalServiceStack", "Scheme": "internal", "SecurityGroups": Array [ Object { "Fn::GetAtt": Array [ - "internalServiceStackLoadBalancerSecurityGrouptestC15E9D39", + "internalServiceStackLoadBalancerSecurityGroupinternalServiceStackB6066852", "GroupId", ], }, @@ -397,32 +382,32 @@ Object { }, "Type": "AWS::ElasticLoadBalancingV2::LoadBalancer", }, - "internalServiceStackApplicationLoadBalancertestListenertestAB9280FB": Object { + "internalServiceStackApplicationLoadBalancerinternalServiceStackListenerinternalServiceStackAC542223": Object { "Properties": Object { "Certificates": Array [ Object { "CertificateArn": Object { - "Ref": "internalServiceStackSSLCertificatetest1C36642E", + "Ref": "internalServiceStackSSLCertificateinternalServiceStack283B9A17", }, }, ], "DefaultActions": Array [ Object { "TargetGroupArn": Object { - "Ref": "internalServiceStackTargetGrouptestB800F1B7", + "Ref": "internalServiceStackTargetGroupinternalServiceStackB131C63B", }, "Type": "forward", }, ], "LoadBalancerArn": Object { - "Ref": "internalServiceStackApplicationLoadBalancertestF81D1559", + "Ref": "internalServiceStackApplicationLoadBalancerinternalServiceStackA9484EF3", }, "Port": 443, "Protocol": "HTTPS", }, "Type": "AWS::ElasticLoadBalancingV2::Listener", }, - "internalServiceStackApplicationLoadBalancertestRedirect80To443AC430414": Object { + "internalServiceStackApplicationLoadBalancerinternalServiceStackRedirect80To4439382502D": Object { "Properties": Object { "DefaultActions": Array [ Object { @@ -435,14 +420,14 @@ Object { }, ], "LoadBalancerArn": Object { - "Ref": "internalServiceStackApplicationLoadBalancertestF81D1559", + "Ref": "internalServiceStackApplicationLoadBalancerinternalServiceStackA9484EF3", }, "Port": 80, "Protocol": "HTTP", }, "Type": "AWS::ElasticLoadBalancingV2::Listener", }, - "internalServiceStackDomaininternalservicedevtest2comtestCB38E02B": Object { + "internalServiceStackDomaininternalservicedevtest2cominternalServiceStack5EFB1D61": Object { "Properties": Object { "DomainName": "internalservice-dev.test2.com", "EndpointConfiguration": Object { @@ -451,13 +436,13 @@ Object { ], }, "RegionalCertificateArn": Object { - "Ref": "internalServiceStackSSLCertificatetest1C36642E", + "Ref": "internalServiceStackSSLCertificateinternalServiceStack283B9A17", }, "SecurityPolicy": "TLS_1_2", }, "Type": "AWS::ApiGateway::DomainName", }, - "internalServiceStackDomaininternalservicedevtestcomtest2CE9FA76": Object { + "internalServiceStackDomaininternalservicedevtestcominternalServiceStackB6868874": Object { "Properties": Object { "DomainName": "internalservice-dev.test.com", "EndpointConfiguration": Object { @@ -466,13 +451,13 @@ Object { ], }, "RegionalCertificateArn": Object { - "Ref": "internalServiceStackSSLCertificatetest1C36642E", + "Ref": "internalServiceStackSSLCertificateinternalServiceStack283B9A17", }, "SecurityPolicy": "TLS_1_2", }, "Type": "AWS::ApiGateway::DomainName", }, - "internalServiceStackLoadBalancerSecurityGrouptestC15E9D39": Object { + "internalServiceStackLoadBalancerSecurityGroupinternalServiceStackB6066852": Object { "Properties": Object { "GroupDescription": "security group for a load balancer", "GroupName": "-lb-sg", @@ -503,7 +488,7 @@ Object { }, "Type": "AWS::EC2::SecurityGroup", }, - "internalServiceStackRoute53Recordtest42769C99": Object { + "internalServiceStackRoute53RecordinternalServiceStack0A03BF43": Object { "Properties": Object { "AliasTarget": Object { "DNSName": Object { @@ -513,7 +498,7 @@ Object { "dualstack.", Object { "Fn::GetAtt": Array [ - "internalServiceStackApplicationLoadBalancertestF81D1559", + "internalServiceStackApplicationLoadBalancerinternalServiceStackA9484EF3", "DNSName", ], }, @@ -522,7 +507,7 @@ Object { }, "HostedZoneId": Object { "Fn::GetAtt": Array [ - "internalServiceStackApplicationLoadBalancertestF81D1559", + "internalServiceStackApplicationLoadBalancerinternalServiceStackA9484EF3", "CanonicalHostedZoneID", ], }, @@ -533,7 +518,7 @@ Object { }, "Type": "AWS::Route53::RecordSet", }, - "internalServiceStackSSLCertificatetest1C36642E": Object { + "internalServiceStackSSLCertificateinternalServiceStack283B9A17": Object { "Properties": Object { "DomainName": "internalservice-dev.test.aws1234.com", "SubjectAlternativeNames": Array [ @@ -543,16 +528,16 @@ Object { "Tags": Array [ Object { "Key": "Name", - "Value": "test/internalServiceStack/SSLCertificate-test", + "Value": "test/internalServiceStack/SSLCertificate-internalServiceStack", }, ], "ValidationMethod": "DNS", }, "Type": "AWS::CertificateManager::Certificate", }, - "internalServiceStackTargetGrouptestB800F1B7": Object { + "internalServiceStackTargetGroupinternalServiceStackB131C63B": Object { "Properties": Object { - "Name": "tg-test", + "Name": "tg-internalServiceStack", "Port": 443, "Protocol": "HTTPS", "TargetGroupAttributes": Array [ @@ -606,3 +591,33 @@ Object { `); }); +test('Api Gateway Stack provider - set optional parameters', () => { + + new ApiGatewayStackTest(stack, 'apiGatewayStackOptionalParameters', { + stage: 'dev', + domains: internalServiceStack.domains, + vpcEndpoint: vpcEndpointId, + minimumCompressionSize: 1024, + binaryMediaTypes: ['application/octet-stream'], + apiBasePathMappingPath: 'test', + }); + + const template = Template.fromStack(stack); + template.hasResourceProperties('AWS::ApiGateway::RestApi', Match.objectLike({ + MinimumCompressionSize: 1024, + }, + )); + + template.hasResourceProperties('AWS::ApiGateway::RestApi', Match.objectLike({ + BinaryMediaTypes: [ + 'application/octet-stream', + ], + }, + )); + + template.hasResourceProperties('AWS::ApiGateway::BasePathMapping', Match.objectLike({ + BasePath: 'test', + }, + )); +}); + diff --git a/test/internal-service.test.ts b/test/internal-service.test.ts index 4304b0d..04f7f11 100644 --- a/test/internal-service.test.ts +++ b/test/internal-service.test.ts @@ -1,27 +1,34 @@ -import * as cdk from 'aws-cdk-lib'; +import { App, aws_ec2 as ec2, aws_route53 as route53, Stack } from 'aws-cdk-lib'; import { Template } from 'aws-cdk-lib/assertions'; import { InternalService } from '../src'; test('Internal Service provider', () => { - const app = new cdk.App();; - const stack = new cdk.Stack(app, 'test', { + const app = new App();; + const stack = new Stack(app, 'test', { env: { account: '123456789012', region: 'us-east-1', }, }); - const vpc = cdk.aws_ec2.Vpc.fromLookup(stack, 'vpc', { vpcId: 'vpc-1234567' }); + const vpc = ec2.Vpc.fromLookup(stack, 'vpc', { vpcId: 'vpc-1234567' }); const internalSubnetIds = ['subnet-1234567890', 'subnet-1234567890']; + const hostedZone = route53.HostedZone.fromLookup(stack, 'hostedzone', { + domainName: 'test.aws1234.com', + privateZone: true, + vpcId: vpc.vpcId, + }); + + new InternalService(stack, 'internalServiceStack', { vpc: vpc, subnetSelection: { subnets: internalSubnetIds.map((ip, index) => - cdk.aws_ec2.Subnet.fromSubnetId(stack, `Subnet${index}`, ip), + ec2.Subnet.fromSubnetId(stack, `Subnet${index}`, ip), ), }, vpcEndpointIPAddresses: ['192.168.2.1', '192.168.2.2'], subjectAlternativeNames: ['internalservice-dev.test.com', 'internalservice-dev.test2.com'], - hostedZoneName: 'test.aws1234.com', + hostedZone: hostedZone, subDomain: 'internalservice-dev', }); @@ -29,7 +36,7 @@ test('Internal Service provider', () => { expect(template).toMatchInlineSnapshot(` Object { "Outputs": Object { - "internalServiceStackDomainUrltest2935E3B3": Object { + "internalServiceStackDomainUrlinternalServiceStackB921B240": Object { "Description": "service url", "Export": Object { "Name": "-DomainUrl", @@ -45,7 +52,7 @@ Object { }, }, "Resources": Object { - "internalServiceStackApiGatewayCustomDomaintest813BB8C2": Object { + "internalServiceStackApiGatewayCustomDomaininternalServiceStack836326F5": Object { "Properties": Object { "DomainName": "internalservice-dev.test.aws1234.com", "EndpointConfiguration": Object { @@ -54,13 +61,13 @@ Object { ], }, "RegionalCertificateArn": Object { - "Ref": "internalServiceStackSSLCertificatetest1C36642E", + "Ref": "internalServiceStackSSLCertificateinternalServiceStack283B9A17", }, "SecurityPolicy": "TLS_1_2", }, "Type": "AWS::ApiGateway::DomainName", }, - "internalServiceStackApplicationLoadBalancertestF81D1559": Object { + "internalServiceStackApplicationLoadBalancerinternalServiceStackA9484EF3": Object { "Properties": Object { "LoadBalancerAttributes": Array [ Object { @@ -68,12 +75,12 @@ Object { "Value": "false", }, ], - "Name": "lb-test", + "Name": "lb-internalServiceStack", "Scheme": "internal", "SecurityGroups": Array [ Object { "Fn::GetAtt": Array [ - "internalServiceStackLoadBalancerSecurityGrouptestC15E9D39", + "internalServiceStackLoadBalancerSecurityGroupinternalServiceStackB6066852", "GroupId", ], }, @@ -86,32 +93,32 @@ Object { }, "Type": "AWS::ElasticLoadBalancingV2::LoadBalancer", }, - "internalServiceStackApplicationLoadBalancertestListenertestAB9280FB": Object { + "internalServiceStackApplicationLoadBalancerinternalServiceStackListenerinternalServiceStackAC542223": Object { "Properties": Object { "Certificates": Array [ Object { "CertificateArn": Object { - "Ref": "internalServiceStackSSLCertificatetest1C36642E", + "Ref": "internalServiceStackSSLCertificateinternalServiceStack283B9A17", }, }, ], "DefaultActions": Array [ Object { "TargetGroupArn": Object { - "Ref": "internalServiceStackTargetGrouptestB800F1B7", + "Ref": "internalServiceStackTargetGroupinternalServiceStackB131C63B", }, "Type": "forward", }, ], "LoadBalancerArn": Object { - "Ref": "internalServiceStackApplicationLoadBalancertestF81D1559", + "Ref": "internalServiceStackApplicationLoadBalancerinternalServiceStackA9484EF3", }, "Port": 443, "Protocol": "HTTPS", }, "Type": "AWS::ElasticLoadBalancingV2::Listener", }, - "internalServiceStackApplicationLoadBalancertestRedirect80To443AC430414": Object { + "internalServiceStackApplicationLoadBalancerinternalServiceStackRedirect80To4439382502D": Object { "Properties": Object { "DefaultActions": Array [ Object { @@ -124,14 +131,14 @@ Object { }, ], "LoadBalancerArn": Object { - "Ref": "internalServiceStackApplicationLoadBalancertestF81D1559", + "Ref": "internalServiceStackApplicationLoadBalancerinternalServiceStackA9484EF3", }, "Port": 80, "Protocol": "HTTP", }, "Type": "AWS::ElasticLoadBalancingV2::Listener", }, - "internalServiceStackDomaininternalservicedevtest2comtestCB38E02B": Object { + "internalServiceStackDomaininternalservicedevtest2cominternalServiceStack5EFB1D61": Object { "Properties": Object { "DomainName": "internalservice-dev.test2.com", "EndpointConfiguration": Object { @@ -140,13 +147,13 @@ Object { ], }, "RegionalCertificateArn": Object { - "Ref": "internalServiceStackSSLCertificatetest1C36642E", + "Ref": "internalServiceStackSSLCertificateinternalServiceStack283B9A17", }, "SecurityPolicy": "TLS_1_2", }, "Type": "AWS::ApiGateway::DomainName", }, - "internalServiceStackDomaininternalservicedevtestcomtest2CE9FA76": Object { + "internalServiceStackDomaininternalservicedevtestcominternalServiceStackB6868874": Object { "Properties": Object { "DomainName": "internalservice-dev.test.com", "EndpointConfiguration": Object { @@ -155,13 +162,13 @@ Object { ], }, "RegionalCertificateArn": Object { - "Ref": "internalServiceStackSSLCertificatetest1C36642E", + "Ref": "internalServiceStackSSLCertificateinternalServiceStack283B9A17", }, "SecurityPolicy": "TLS_1_2", }, "Type": "AWS::ApiGateway::DomainName", }, - "internalServiceStackLoadBalancerSecurityGrouptestC15E9D39": Object { + "internalServiceStackLoadBalancerSecurityGroupinternalServiceStackB6066852": Object { "Properties": Object { "GroupDescription": "security group for a load balancer", "GroupName": "-lb-sg", @@ -192,7 +199,7 @@ Object { }, "Type": "AWS::EC2::SecurityGroup", }, - "internalServiceStackRoute53Recordtest42769C99": Object { + "internalServiceStackRoute53RecordinternalServiceStack0A03BF43": Object { "Properties": Object { "AliasTarget": Object { "DNSName": Object { @@ -202,7 +209,7 @@ Object { "dualstack.", Object { "Fn::GetAtt": Array [ - "internalServiceStackApplicationLoadBalancertestF81D1559", + "internalServiceStackApplicationLoadBalancerinternalServiceStackA9484EF3", "DNSName", ], }, @@ -211,7 +218,7 @@ Object { }, "HostedZoneId": Object { "Fn::GetAtt": Array [ - "internalServiceStackApplicationLoadBalancertestF81D1559", + "internalServiceStackApplicationLoadBalancerinternalServiceStackA9484EF3", "CanonicalHostedZoneID", ], }, @@ -222,7 +229,7 @@ Object { }, "Type": "AWS::Route53::RecordSet", }, - "internalServiceStackSSLCertificatetest1C36642E": Object { + "internalServiceStackSSLCertificateinternalServiceStack283B9A17": Object { "Properties": Object { "DomainName": "internalservice-dev.test.aws1234.com", "SubjectAlternativeNames": Array [ @@ -232,16 +239,16 @@ Object { "Tags": Array [ Object { "Key": "Name", - "Value": "test/internalServiceStack/SSLCertificate-test", + "Value": "test/internalServiceStack/SSLCertificate-internalServiceStack", }, ], "ValidationMethod": "DNS", }, "Type": "AWS::CertificateManager::Certificate", }, - "internalServiceStackTargetGrouptestB800F1B7": Object { + "internalServiceStackTargetGroupinternalServiceStackB131C63B": Object { "Properties": Object { - "Name": "tg-test", + "Name": "tg-internalServiceStack", "Port": 443, "Protocol": "HTTPS", "TargetGroupAttributes": Array [ diff --git a/yarn.lock b/yarn.lock index ec1fe5c..1bb787d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -807,6 +807,16 @@ dependencies: "@babel/types" "^7.3.0" +"@types/cacheable-request@^6.0.2": + version "6.0.2" + resolved "https://registry.yarnpkg.com/@types/cacheable-request/-/cacheable-request-6.0.2.tgz#c324da0197de0a98a2312156536ae262429ff6b9" + integrity sha512-B3xVo+dlKM6nnKTcmm5ZtY/OL8bOAOd2Olee9M1zft65ox50OzjEHW91sDiU9j6cvW8Ejg1/Qkf4xd2kugApUA== + dependencies: + "@types/http-cache-semantics" "*" + "@types/keyv" "*" + "@types/node" "*" + "@types/responselike" "*" + "@types/glob@*": version "8.0.0" resolved "https://registry.yarnpkg.com/@types/glob/-/glob-8.0.0.tgz#321607e9cbaec54f687a0792b2d1d370739455d2" @@ -822,7 +832,7 @@ dependencies: "@types/node" "*" -"@types/http-cache-semantics@^4.0.1": +"@types/http-cache-semantics@*": version "4.0.1" resolved "https://registry.yarnpkg.com/@types/http-cache-semantics/-/http-cache-semantics-4.0.1.tgz#0ea7b61496902b95890dc4c3a116b60cb8dae812" integrity sha512-SZs7ekbP8CN0txVG2xVRH6EgKmEm31BOxA07vkFaETzZz1xh+cbt8BcI0slpymvwhx5dlFnQG2rTlPVQn+iRPQ== @@ -864,6 +874,13 @@ resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee" integrity sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ== +"@types/keyv@*": + version "4.2.0" + resolved "https://registry.yarnpkg.com/@types/keyv/-/keyv-4.2.0.tgz#65b97868ab757906f2dbb653590d7167ad023fa0" + integrity sha512-xoBtGl5R9jeKUhc8ZqeYaRDx04qqJ10yhhXYGmJ4Jr8qKpvMsDQQrNUvF/wUJ4klOtmJeJM+p2Xo3zp9uaC3tw== + dependencies: + keyv "*" + "@types/minimatch@*": version "5.1.2" resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-5.1.2.tgz#07508b45797cb81ec3f273011b054cd0755eddca" @@ -894,6 +911,13 @@ resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.6.0.tgz#efcbd41937f9ae7434c714ab698604822d890759" integrity sha512-G/AdOadiZhnJp0jXCaBQU449W2h716OW/EoXeYkCytxKL06X1WCXB4DZpp8TpZ8eyIJVS1cw4lrlkkSYU21cDw== +"@types/responselike@*", "@types/responselike@^1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@types/responselike/-/responselike-1.0.0.tgz#251f4fe7d154d2bad125abe1b429b23afd262e29" + integrity sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA== + dependencies: + "@types/node" "*" + "@types/semver@^7.3.12": version "7.3.13" resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.3.13.tgz#da4bfd73f49bd541d28920ab0e2bf0ee80f71c91" @@ -1428,23 +1452,23 @@ cacache@^17.0.0: tar "^6.1.11" unique-filename "^3.0.0" -cacheable-lookup@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/cacheable-lookup/-/cacheable-lookup-7.0.0.tgz#3476a8215d046e5a3202a9209dd13fec1f933a27" - integrity sha512-+qJyx4xiKra8mZrcwhjMRMUhD5NR1R8esPkzIYxX96JiecFoxAXFuz/GpR3+ev4PE1WamHip78wV0vcmPQtp8w== +cacheable-lookup@^6.0.4: + version "6.1.0" + resolved "https://registry.yarnpkg.com/cacheable-lookup/-/cacheable-lookup-6.1.0.tgz#0330a543471c61faa4e9035db583aad753b36385" + integrity sha512-KJ/Dmo1lDDhmW2XDPMo+9oiy/CeqosPguPCrgcVzKyZrL6pM1gU2GmPY/xo6OQPTUaA/c0kwHuywB4E6nmT9ww== -cacheable-request@^10.2.1: - version "10.2.2" - resolved "https://registry.yarnpkg.com/cacheable-request/-/cacheable-request-10.2.2.tgz#07c3d5afcaa2de2e9f66959bacb3ff78da3735fd" - integrity sha512-KxjQZM3UIo7/J6W4sLpwFvu1GB3Whv8NtZ8ZrUL284eiQjiXeeqWTdhixNrp/NLZ/JNuFBo6BD4ZaO8ZJ5BN8Q== +cacheable-request@^7.0.2: + version "7.0.2" + resolved "https://registry.yarnpkg.com/cacheable-request/-/cacheable-request-7.0.2.tgz#ea0d0b889364a25854757301ca12b2da77f91d27" + integrity sha512-pouW8/FmiPQbuGpkXQ9BAPv/Mo5xDGANgSNXzTzJ8DrKGuXOssM4wIQRjfanNRh3Yu5cfYPvcorqbhg2KIJtew== dependencies: - "@types/http-cache-semantics" "^4.0.1" - get-stream "^6.0.1" - http-cache-semantics "^4.1.0" - keyv "^4.5.0" - mimic-response "^4.0.0" - normalize-url "^7.2.0" - responselike "^3.0.0" + clone-response "^1.0.2" + get-stream "^5.1.0" + http-cache-semantics "^4.0.0" + keyv "^4.0.0" + lowercase-keys "^2.0.0" + normalize-url "^6.0.1" + responselike "^2.0.0" call-bind@^1.0.0, call-bind@^1.0.2: version "1.0.2" @@ -1561,6 +1585,13 @@ cliui@^7.0.2: strip-ansi "^6.0.0" wrap-ansi "^7.0.0" +clone-response@^1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/clone-response/-/clone-response-1.0.3.tgz#af2032aa47816399cf5f0a1d0db902f517abb8c3" + integrity sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA== + dependencies: + mimic-response "^1.0.0" + clone@^2.1.2: version "2.1.2" resolved "https://registry.yarnpkg.com/clone/-/clone-2.1.2.tgz#1b7f4b9f591f1e8f83670401600345a02887435f" @@ -2134,6 +2165,13 @@ encoding@^0.1.13: dependencies: iconv-lite "^0.6.2" +end-of-stream@^1.1.0: + version "1.4.4" + resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" + integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q== + dependencies: + once "^1.4.0" + enhanced-resolve@^5.10.0: version "5.10.0" resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.10.0.tgz#0dc579c3bb2a1032e357ac45b8f3a6f3ad4fb1e6" @@ -2559,7 +2597,7 @@ flatted@^3.1.0, flatted@^3.2.7: resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.7.tgz#609f39207cb614b89d0765b477cb2d437fbf9787" integrity sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ== -form-data-encoder@^2.1.2: +form-data-encoder@^2.0.1: version "2.1.3" resolved "https://registry.yarnpkg.com/form-data-encoder/-/form-data-encoder-2.1.3.tgz#682cd821a8423605093992ff895e6b2ed5a9d429" integrity sha512-KqU0nnPMgIJcCOFTNJFEA8epcseEaoox4XZffTgy8jlI6pL/5EFyR54NRG7CnCJN0biY7q52DO3MH6/sJ/TKlQ== @@ -2696,6 +2734,13 @@ get-stdin@^8.0.0: resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-8.0.0.tgz#cbad6a73feb75f6eeb22ba9e01f89aa28aa97a53" integrity sha512-sY22aA6xchAzprjyqmSEQv4UbAAzRN0L2dQB0NlN5acTTK9Don6nhoc3eAbUnpZiCANAMfd/+40kVdKfFygohg== +get-stream@^5.1.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-5.2.0.tgz#4966a1795ee5ace65e706c4b7beb71257d6e22d3" + integrity sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA== + dependencies: + pump "^3.0.0" + get-stream@^6.0.0, get-stream@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-6.0.1.tgz#a262d8eef67aced57c2852ad6167526a43cbf7b7" @@ -2845,21 +2890,23 @@ globrex@^0.1.2: integrity sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg== got@^12.1.0: - version "12.5.2" - resolved "https://registry.yarnpkg.com/got/-/got-12.5.2.tgz#2c1b390918961cf50e61cb02d2085ba203d0df45" - integrity sha512-guHGMSEcsA5m1oPRweXUJnug0vuvlkX9wx5hzOka+ZBrBUOJHU0Z1JcNu3QE5IPGnA5aXUsQHdWOD4eJg9/v3A== + version "12.3.1" + resolved "https://registry.yarnpkg.com/got/-/got-12.3.1.tgz#79d6ebc0cb8358c424165698ddb828be56e74684" + integrity sha512-tS6+JMhBh4iXMSXF6KkIsRxmloPln31QHDlcb6Ec3bzxjjFJFr/8aXdpyuLmVc9I4i2HyBHYw1QU5K1ruUdpkw== dependencies: "@sindresorhus/is" "^5.2.0" "@szmarczak/http-timer" "^5.0.1" - cacheable-lookup "^7.0.0" - cacheable-request "^10.2.1" + "@types/cacheable-request" "^6.0.2" + "@types/responselike" "^1.0.0" + cacheable-lookup "^6.0.4" + cacheable-request "^7.0.2" decompress-response "^6.0.0" - form-data-encoder "^2.1.2" + form-data-encoder "^2.0.1" get-stream "^6.0.1" http2-wrapper "^2.1.10" lowercase-keys "^3.0.0" p-cancelable "^3.0.0" - responselike "^3.0.0" + responselike "^2.0.0" graceful-fs@4.2.10, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.4, graceful-fs@^4.2.6, graceful-fs@^4.2.9: version "4.2.10" @@ -2970,7 +3017,7 @@ html-escaper@^2.0.0: resolved "https://registry.yarnpkg.com/html-escaper/-/html-escaper-2.0.2.tgz#dfd60027da36a36dfcbe236262c00a5822681453" integrity sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg== -http-cache-semantics@^4.1.0: +http-cache-semantics@^4.0.0, http-cache-semantics@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz#49e91c5cbf36c9b94bcfcd71c23d5249ec74e390" integrity sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ== @@ -4055,7 +4102,7 @@ jsonschema@^1.4.1: resolved "https://registry.yarnpkg.com/jsonschema/-/jsonschema-1.4.1.tgz#cc4c3f0077fb4542982973d8a083b6b34f482dab" integrity sha512-S6cATIPVv1z0IlxdN+zUk5EPjkGCdnhN4wVSBlvoUO1tOLJootbo9CquNJmbIh4yikWHiUedhRYrNPn1arpEmQ== -keyv@^4.5.0: +keyv@*, keyv@^4.0.0: version "4.5.0" resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.5.0.tgz#dbce9ade79610b6e641a9a65f2f6499ba06b9bc6" integrity sha512-2YvuMsA+jnFGtBareKqgANOEKe1mk3HKiXu2fRmAfyxG0MJAywNhi5ttWA3PMjl4NmpyjZNbFifR2vNjW1znfA== @@ -4181,6 +4228,11 @@ log4js@^6.7.0: rfdc "^1.3.0" streamroller "^3.1.3" +lowercase-keys@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-2.0.0.tgz#2603e78b7b4b0006cbca2fbcc8a3202558ac9479" + integrity sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA== + lowercase-keys@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-3.0.0.tgz#c5e7d442e37ead247ae9db117a9d0a467c89d4f2" @@ -4306,16 +4358,16 @@ mimic-fn@^2.1.0: resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== +mimic-response@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-1.0.1.tgz#4923538878eef42063cb8a3e3b0798781487ab1b" + integrity sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ== + mimic-response@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-3.1.0.tgz#2d1d59af9c1b129815accc2c46a022a5ce1fa3c9" integrity sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ== -mimic-response@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-4.0.0.tgz#35468b19e7c75d10f5165ea25e75a5ceea7cf70f" - integrity sha512-e5ISH9xMYU0DzrT+jl8q2ze9D6eWBto+I8CNpe+VI+K2J/F/k3PdkdTdz4wvGVH4NTpo+NRYTVIuMQEMMcsLqg== - min-indent@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/min-indent/-/min-indent-1.0.1.tgz#a63f681673b30571fbe8bc25686ae746eefa9869" @@ -4524,10 +4576,10 @@ normalize-path@^3.0.0: resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== -normalize-url@^7.2.0: - version "7.2.0" - resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-7.2.0.tgz#5317f78cff95f5fa1e76cc0b5e33245c43781e11" - integrity sha512-uhXOdZry0L6M2UIo9BTt7FdpBDiAGN/7oItedQwPKh8jh31ZlvC8U9Xl/EJ3aijDHaywXTW3QbZ6LuCocur1YA== +normalize-url@^6.0.1: + version "6.1.0" + resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-6.1.0.tgz#40d0885b535deffe3f3147bec877d05fe4c5668a" + integrity sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A== npm-bundled@^1.1.1: version "1.1.2" @@ -4684,7 +4736,7 @@ object.values@^1.1.5: define-properties "^1.1.3" es-abstract "^1.19.1" -once@^1.3.0: +once@^1.3.0, once@^1.3.1, once@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w== @@ -5037,6 +5089,14 @@ psl@^1.1.33: resolved "https://registry.yarnpkg.com/psl/-/psl-1.9.0.tgz#d0df2a137f00794565fcaf3b2c00cd09f8d5a5a7" integrity sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag== +pump@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64" + integrity sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww== + dependencies: + end-of-stream "^1.1.0" + once "^1.3.1" + punycode@^2.1.0, punycode@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" @@ -5282,12 +5342,12 @@ resolve@^1.1.6, resolve@^1.10.0, resolve@^1.20.0, resolve@^1.22.0: path-parse "^1.0.7" supports-preserve-symlinks-flag "^1.0.0" -responselike@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/responselike/-/responselike-3.0.0.tgz#20decb6c298aff0dbee1c355ca95461d42823626" - integrity sha512-40yHxbNcl2+rzXvZuVkrYohathsSJlMTXKryG5y8uciHv1+xDLHQpgjG64JUO9nrEq2jGLH6IZ8BcZyw3wrweg== +responselike@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/responselike/-/responselike-2.0.1.tgz#9a0bc8fdc252f3fb1cca68b016591059ba1422bc" + integrity sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw== dependencies: - lowercase-keys "^3.0.0" + lowercase-keys "^2.0.0" retry@^0.12.0: version "0.12.0"