@gradientedge/cdk-utils 8.113.0 → 8.115.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,3 @@
1
+ export * from './main';
2
+ export * from './target';
3
+ export * from './types';
@@ -0,0 +1,19 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./main"), exports);
18
+ __exportStar(require("./target"), exports);
19
+ __exportStar(require("./types"), exports);
@@ -0,0 +1,48 @@
1
+ import { Resource } from 'aws-cdk-lib/aws-apigateway';
2
+ import { ISecret } from 'aws-cdk-lib/aws-secretsmanager';
3
+ import { Construct } from 'constructs';
4
+ import { CommonConstruct } from '../../common';
5
+ import { ApiToAnyTargetProps, ApiToAnyTargetRestApiResource, ApiToAnyTargetRestApiType } from './types';
6
+ /**
7
+ * @classdesc Provides a construct to create and deploy a shallow API Gateway
8
+ * @example
9
+ * import { ApiToAnyTarget, ApiToAnyTargetProps } '@gradientedge/cdk-utils'
10
+ * import { Construct } from 'constructs'
11
+ *
12
+ * class CustomConstruct extends ApiToAnyTarget {
13
+ * constructor(parent: Construct, id: string, props: ApiToAnyTargetProps) {
14
+ * super(parent, id, props)
15
+ * this.props = props
16
+ * this.id = id
17
+ * this.initResources()
18
+ * }
19
+ * }
20
+ */
21
+ export declare class ApiToAnyTarget extends CommonConstruct {
22
+ props: ApiToAnyTargetProps;
23
+ id: string;
24
+ applicationSecrets: ISecret[];
25
+ apiToAnyTargetRestApi: ApiToAnyTargetRestApiType;
26
+ apiResource: string;
27
+ constructor(parent: Construct, id: string, props: ApiToAnyTargetProps);
28
+ protected initResources(): void;
29
+ /**
30
+ * @summary Method to resolve secrets from SecretsManager
31
+ * - To be implemented in the overriding method in the implementation class
32
+ */
33
+ protected resolveSecrets(): void;
34
+ /**
35
+ * @summary Method to resolve a hosted zone based on domain attributes
36
+ */
37
+ protected resolveHostedZone(): void;
38
+ /**
39
+ * @summary Method to resolve a certificate based on attributes
40
+ */
41
+ protected resolveCertificate(): void;
42
+ protected createApiToAnyTargetRestApiLogGroup(): void;
43
+ protected createApiToAnyTargetRestApi(): void;
44
+ protected createApiToAnyTargetResource(apiResourceProps: ApiToAnyTargetRestApiResource): Resource | undefined;
45
+ protected createApiDomain(): void;
46
+ protected createApiBasePathMapping(): void;
47
+ protected createApiRouteAssets(): void;
48
+ }
@@ -0,0 +1,159 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ApiToAnyTarget = void 0;
4
+ const aws_cdk_lib_1 = require("aws-cdk-lib");
5
+ const aws_apigateway_1 = require("aws-cdk-lib/aws-apigateway");
6
+ const common_1 = require("../../common");
7
+ const target_1 = require("./target");
8
+ /**
9
+ * @classdesc Provides a construct to create and deploy a shallow API Gateway
10
+ * @example
11
+ * import { ApiToAnyTarget, ApiToAnyTargetProps } '@gradientedge/cdk-utils'
12
+ * import { Construct } from 'constructs'
13
+ *
14
+ * class CustomConstruct extends ApiToAnyTarget {
15
+ * constructor(parent: Construct, id: string, props: ApiToAnyTargetProps) {
16
+ * super(parent, id, props)
17
+ * this.props = props
18
+ * this.id = id
19
+ * this.initResources()
20
+ * }
21
+ * }
22
+ */
23
+ class ApiToAnyTarget extends common_1.CommonConstruct {
24
+ props;
25
+ id;
26
+ /* application related resources */
27
+ applicationSecrets;
28
+ /* rest restApi related resources */
29
+ apiToAnyTargetRestApi;
30
+ apiResource;
31
+ constructor(parent, id, props) {
32
+ super(parent, id, props);
33
+ this.props = props;
34
+ this.id = id;
35
+ this.apiToAnyTargetRestApi = new target_1.ApiToAnyTargetRestApi();
36
+ }
37
+ initResources() {
38
+ /* application related resources */
39
+ this.resolveSecrets();
40
+ /* core resources */
41
+ this.resolveHostedZone();
42
+ this.resolveCertificate();
43
+ /* restApi related resources */
44
+ this.createApiToAnyTargetRestApiLogGroup();
45
+ this.createApiToAnyTargetRestApi();
46
+ this.createApiDomain();
47
+ this.createApiBasePathMapping();
48
+ this.createApiRouteAssets();
49
+ }
50
+ /**
51
+ * @summary Method to resolve secrets from SecretsManager
52
+ * - To be implemented in the overriding method in the implementation class
53
+ */
54
+ resolveSecrets() {
55
+ this.applicationSecrets = [];
56
+ }
57
+ /**
58
+ * @summary Method to resolve a hosted zone based on domain attributes
59
+ */
60
+ resolveHostedZone() {
61
+ this.apiToAnyTargetRestApi.hostedZone = this.route53Manager.withHostedZoneFromFullyQualifiedDomainName(`${this.id}-hosted-zone`, this, this.props.useExistingHostedZone);
62
+ }
63
+ /**
64
+ * @summary Method to resolve a certificate based on attributes
65
+ */
66
+ resolveCertificate() {
67
+ if (this.props.api.useExisting)
68
+ return;
69
+ if (this.props.api.certificate.useExistingCertificate &&
70
+ this.props.api.certificate.certificateSsmName &&
71
+ this.props.api.certificate.certificateRegion) {
72
+ this.props.api.certificate.certificateArn = this.ssmManager.readStringParameterFromRegion(`${this.id}-certificate-param`, this, this.props.api.certificate.certificateSsmName, this.props.api.certificate.certificateRegion);
73
+ }
74
+ this.apiToAnyTargetRestApi.certificate = this.acmManager.resolveCertificate(`${this.id}-certificate`, this, this.props.api.certificate);
75
+ }
76
+ createApiToAnyTargetRestApiLogGroup() {
77
+ this.apiToAnyTargetRestApi.accessLogGroup = this.logManager.createLogGroup(`${this.id}-rest-api-access-log`, this, {
78
+ logGroupName: `/custom/api/${this.id}-rest-api-access`,
79
+ removalPolicy: aws_cdk_lib_1.RemovalPolicy.DESTROY,
80
+ });
81
+ }
82
+ createApiToAnyTargetRestApi() {
83
+ if (this.props.api.useExisting && this.props.api.importedRestApiRef) {
84
+ this.apiToAnyTargetRestApi.api = aws_apigateway_1.RestApi.fromRestApiId(this, `${this.id}-rest-api`, aws_cdk_lib_1.Fn.importValue(this.props.api.importedRestApiRef));
85
+ return;
86
+ }
87
+ this.apiToAnyTargetRestApi.api = new aws_apigateway_1.RestApi(this, `${this.id}-rest-api`, {
88
+ ...{
89
+ cloudWatchRole: this.props.api.restApi?.cloudWatchRole ?? true,
90
+ defaultCorsPreflightOptions: {
91
+ allowHeaders: aws_apigateway_1.Cors.DEFAULT_HEADERS,
92
+ allowMethods: aws_apigateway_1.Cors.ALL_METHODS,
93
+ allowOrigins: aws_apigateway_1.Cors.ALL_ORIGINS,
94
+ },
95
+ defaultIntegration: this.apiToAnyTargetRestApi.integration,
96
+ defaultMethodOptions: {
97
+ methodResponses: [this.apiToAnyTargetRestApi.methodResponse, this.apiToAnyTargetRestApi.methodErrorResponse],
98
+ },
99
+ deploy: this.props.api.restApi?.deploy ?? true,
100
+ deployOptions: {
101
+ accessLogDestination: new aws_apigateway_1.LogGroupLogDestination(this.apiToAnyTargetRestApi.accessLogGroup),
102
+ accessLogFormat: aws_apigateway_1.AccessLogFormat.jsonWithStandardFields(),
103
+ dataTraceEnabled: this.props.api.restApi?.deployOptions?.dataTraceEnabled,
104
+ description: `${this.id} - ${this.props.stage} stage`,
105
+ loggingLevel: aws_apigateway_1.MethodLoggingLevel.INFO,
106
+ metricsEnabled: true,
107
+ stageName: this.props.stage,
108
+ tracingEnabled: this.props.api.restApi?.deployOptions?.tracingEnabled,
109
+ },
110
+ endpointConfiguration: {
111
+ types: [this.isProductionStage() ? aws_apigateway_1.EndpointType.EDGE : aws_apigateway_1.EndpointType.REGIONAL],
112
+ },
113
+ restApiName: `${this.id}-rest-api-${this.props.stage}`,
114
+ },
115
+ ...this.props.api.restApi,
116
+ });
117
+ this.addCfnOutput(`${this.id}-restApiId`, this.apiToAnyTargetRestApi.api.restApiId);
118
+ this.addCfnOutput(`${this.id}-restApiRootResourceId`, this.apiToAnyTargetRestApi.api.root.resourceId);
119
+ }
120
+ createApiToAnyTargetResource(apiResourceProps) {
121
+ if (!this.props.api.withResource)
122
+ return;
123
+ let rootResource;
124
+ if (this.props.api.withResource && this.props.api.importedRestApiRootResourceRef) {
125
+ rootResource = aws_apigateway_1.Resource.fromResourceAttributes(this, `${this.id}-root-resource-for-${apiResourceProps.path}`, {
126
+ path: '/',
127
+ resourceId: aws_cdk_lib_1.Fn.importValue(this.props.api.importedRestApiRootResourceRef),
128
+ restApi: this.apiToAnyTargetRestApi.api,
129
+ });
130
+ }
131
+ else {
132
+ rootResource = this.apiToAnyTargetRestApi.api.root;
133
+ }
134
+ return this.apiManager.createApiResource(`${this.id}-resource-${apiResourceProps.path}}`, this, apiResourceProps.parent ?? rootResource, apiResourceProps.path, apiResourceProps.integration, apiResourceProps.addProxy, apiResourceProps.authorizer, apiResourceProps.allowedOrigins, apiResourceProps.allowedMethods, apiResourceProps.allowedHeaders, apiResourceProps.methodRequestParameters, apiResourceProps.proxyIntegration);
135
+ }
136
+ createApiDomain() {
137
+ if (this.props.api.useExisting)
138
+ return;
139
+ this.apiToAnyTargetRestApi.domain = this.apiManager.createApiDomain(`${this.id}-api-domain`, this, this.isProductionStage() || this.props.skipStageForARecords
140
+ ? `${this.props.apiSubDomain}.${this.fullyQualifiedDomainName}`
141
+ : `${this.props.apiSubDomain}-${this.props.stage}.${this.fullyQualifiedDomainName}`, this.apiToAnyTargetRestApi.certificate);
142
+ }
143
+ createApiBasePathMapping() {
144
+ if (this.props.api.useExisting)
145
+ return;
146
+ new aws_apigateway_1.BasePathMapping(this, `${this.id}-base-bath-mapping`, {
147
+ basePath: '',
148
+ domainName: this.apiToAnyTargetRestApi.domain,
149
+ restApi: this.apiToAnyTargetRestApi.api,
150
+ stage: this.apiToAnyTargetRestApi.api.deploymentStage,
151
+ });
152
+ }
153
+ createApiRouteAssets() {
154
+ if (this.props.api.useExisting)
155
+ return;
156
+ this.route53Manager.createApiGatewayARecord(`${this.id}-custom-domain-a-record`, this, this.props.apiSubDomain, this.apiToAnyTargetRestApi.domain, this.apiToAnyTargetRestApi.hostedZone, this.props.skipStageForARecords);
157
+ }
158
+ }
159
+ exports.ApiToAnyTarget = ApiToAnyTarget;
@@ -0,0 +1,23 @@
1
+ import { IRestApi, IAuthorizer, BasePathMapping, DomainName, Integration, Method, MethodResponse, Resource } from 'aws-cdk-lib/aws-apigateway';
2
+ import { ICertificate } from 'aws-cdk-lib/aws-certificatemanager';
3
+ import { LogGroup } from 'aws-cdk-lib/aws-logs';
4
+ import { IHostedZone } from 'aws-cdk-lib/aws-route53';
5
+ import { ApiToAnyTargetRestApiType } from './types';
6
+ export declare class ApiToAnyTargetRestApi implements ApiToAnyTargetRestApiType {
7
+ accessLogGroup: LogGroup;
8
+ api: IRestApi;
9
+ authoriser?: IAuthorizer;
10
+ basePathMappings: BasePathMapping[];
11
+ certificate: ICertificate;
12
+ domain: DomainName;
13
+ hostedZone: IHostedZone;
14
+ integration: Integration;
15
+ method: {
16
+ [httpMethod: string]: Method;
17
+ };
18
+ methodErrorResponse: MethodResponse;
19
+ methodResponse: MethodResponse;
20
+ resource: {
21
+ [path: string]: Resource;
22
+ };
23
+ }
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ApiToAnyTargetRestApi = void 0;
4
+ class ApiToAnyTargetRestApi {
5
+ accessLogGroup;
6
+ api;
7
+ authoriser;
8
+ basePathMappings;
9
+ certificate;
10
+ domain;
11
+ hostedZone;
12
+ integration;
13
+ method;
14
+ methodErrorResponse;
15
+ methodResponse;
16
+ resource;
17
+ }
18
+ exports.ApiToAnyTargetRestApi = ApiToAnyTargetRestApi;
@@ -0,0 +1,57 @@
1
+ import { BasePathMapping, DomainName, IAuthorizer, IResource, IRestApi, Integration, Method, MethodResponse, Resource, RestApiProps } from 'aws-cdk-lib/aws-apigateway';
2
+ import { ICertificate } from 'aws-cdk-lib/aws-certificatemanager';
3
+ import { LogGroup } from 'aws-cdk-lib/aws-logs';
4
+ import { IHostedZone } from 'aws-cdk-lib/aws-route53';
5
+ import { CommonStackProps } from '../../common';
6
+ import { AcmProps } from '../../services';
7
+ export interface ApiToAnyTargetRestApiType {
8
+ accessLogGroup: LogGroup;
9
+ api: IRestApi;
10
+ authoriser?: IAuthorizer;
11
+ basePathMappings: BasePathMapping[];
12
+ certificate: ICertificate;
13
+ domain: DomainName;
14
+ hostedZone: IHostedZone;
15
+ integration: Integration;
16
+ method: {
17
+ [httpMethod: string]: Method;
18
+ };
19
+ methodErrorResponse: MethodResponse;
20
+ methodResponse: MethodResponse;
21
+ resource: {
22
+ [path: string]: Resource;
23
+ };
24
+ }
25
+ export interface ApiToAnyTargetRestApiResource {
26
+ addProxy: boolean;
27
+ authorizer?: IAuthorizer;
28
+ allowedOrigins?: string[];
29
+ allowedMethods?: string[];
30
+ allowedHeaders?: string[];
31
+ integration: Integration;
32
+ methodRequestParameters?: {
33
+ [param: string]: boolean;
34
+ };
35
+ path: string;
36
+ parent?: IResource;
37
+ proxyIntegration?: Integration;
38
+ }
39
+ export interface ApiToAnyTargetRestApiProps {
40
+ certificate: AcmProps;
41
+ importedRestApiRef?: string;
42
+ importedRestApiRootResourceRef?: string;
43
+ methodErrorResponse: MethodResponse;
44
+ methodResponse: MethodResponse;
45
+ restApi: RestApiProps;
46
+ useExisting: boolean;
47
+ withResource?: boolean;
48
+ }
49
+ export interface ApiToAnyTargetProps extends CommonStackProps {
50
+ api: ApiToAnyTargetRestApiProps;
51
+ apiRootPaths?: string[];
52
+ apiSubDomain: string;
53
+ logLevel: string;
54
+ nodeEnv: string;
55
+ timezone: string;
56
+ useExistingHostedZone: boolean;
57
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -1,3 +1,4 @@
1
+ export * from './api-to-any-target';
1
2
  export * from './api-to-eventbridge-target';
2
3
  export * from './api-to-eventbridge-target-with-sns';
3
4
  export * from './api-to-lambda-target';
@@ -14,6 +14,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
14
  for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
15
  };
16
16
  Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./api-to-any-target"), exports);
17
18
  __exportStar(require("./api-to-eventbridge-target"), exports);
18
19
  __exportStar(require("./api-to-eventbridge-target-with-sns"), exports);
19
20
  __exportStar(require("./api-to-lambda-target"), exports);
@@ -12,7 +12,7 @@ import * as s3 from 'aws-cdk-lib/aws-s3';
12
12
  import * as efs from 'aws-cdk-lib/aws-efs';
13
13
  import { Construct } from 'constructs';
14
14
  import { CommonConstruct } from '../../common';
15
- import { SiteWithEcsBackendProps, SiteResponseHeadersPolicyProps } from './types';
15
+ import { SiteWithEcsBackendProps, SiteResponseHeadersPolicyProps, SiteCachePolicyProps } from './types';
16
16
  /**
17
17
  * @classdesc Provides a construct to create and deploy a site hosted with an clustered ECS/ELB backend
18
18
  * @example
@@ -126,7 +126,8 @@ export declare class SiteWithEcsBackend extends CommonConstruct {
126
126
  * Method to create log bucket for site distribution
127
127
  */
128
128
  protected createSiteLogBucket(): void;
129
- protected createSiteCacheConfigPolicy(): void;
129
+ protected createSiteCachePolicy(id: string, siteCachePolicy: SiteCachePolicyProps): cdk.aws_cloudfront.CachePolicy;
130
+ protected createSiteOriginCachePolicy(): void;
130
131
  protected createSiteOriginRequestPolicy(): void;
131
132
  protected createResponseHeaderPolicy(props: SiteResponseHeadersPolicyProps): cdk.aws_cloudfront.ResponseHeadersPolicy | undefined;
132
133
  protected createSiteOriginResponseHeadersPolicy(): void;
@@ -104,7 +104,7 @@ class SiteWithEcsBackend extends common_1.CommonConstruct {
104
104
  this.createEcsBuildArgs();
105
105
  this.createEcsContainerImage();
106
106
  this.createEcsService();
107
- this.createSiteCacheConfigPolicy();
107
+ this.createSiteOriginCachePolicy();
108
108
  this.createSiteOriginRequestPolicy();
109
109
  this.createSiteOriginResponseHeadersPolicy();
110
110
  this.createSiteOrigin();
@@ -347,20 +347,23 @@ class SiteWithEcsBackend extends common_1.CommonConstruct {
347
347
  createSiteLogBucket() {
348
348
  this.siteLogBucket = this.s3Manager.createS3Bucket(`${this.id}-site-logs`, this, this.props.siteLogBucket);
349
349
  }
350
- createSiteCacheConfigPolicy() {
351
- if (!this.props.siteCachePolicy)
352
- return;
353
- this.siteCachePolicy = new cloudfront.CachePolicy(this, `${this.id}-site-cache-policy`, {
354
- cachePolicyName: `${this.id}-site-cache-policy`,
350
+ createSiteCachePolicy(id, siteCachePolicy) {
351
+ return new cloudfront.CachePolicy(this, `${id}`, {
352
+ cachePolicyName: `${this.id}-${siteCachePolicy.cachePolicyName}`,
355
353
  comment: `Policy for ${this.id}-distribution - ${this.props.stage} stage`,
356
- defaultTtl: cdk.Duration.seconds(this.props.siteCachePolicy.defaultTtlInSeconds),
357
- minTtl: cdk.Duration.seconds(this.props.siteCachePolicy.minTtlInSeconds),
358
- maxTtl: cdk.Duration.seconds(this.props.siteCachePolicy.maxTtlInSeconds),
359
- enableAcceptEncodingGzip: this.props.siteCachePolicy.enableAcceptEncodingGzip,
360
- queryStringBehavior: this.props.siteCachePolicy.queryStringBehavior,
361
- headerBehavior: this.props.siteCachePolicy.headerBehavior,
362
- cookieBehavior: this.props.siteCachePolicy.cookieBehavior,
354
+ defaultTtl: cdk.Duration.seconds(siteCachePolicy.defaultTtlInSeconds),
355
+ minTtl: cdk.Duration.seconds(siteCachePolicy.minTtlInSeconds),
356
+ maxTtl: cdk.Duration.seconds(siteCachePolicy.maxTtlInSeconds),
357
+ enableAcceptEncodingGzip: siteCachePolicy.enableAcceptEncodingGzip,
358
+ queryStringBehavior: siteCachePolicy.queryStringBehavior,
359
+ headerBehavior: siteCachePolicy.headerBehavior,
360
+ cookieBehavior: siteCachePolicy.cookieBehavior,
363
361
  });
362
+ }
363
+ createSiteOriginCachePolicy() {
364
+ if (!this.props.siteCachePolicy)
365
+ return;
366
+ this.siteCachePolicy = this.createSiteCachePolicy(`${this.id}-site-cache-policy`, this.props.siteCachePolicy);
364
367
  _.assign(this.props.siteDistribution.defaultBehavior, {
365
368
  cachePolicy: this.siteCachePolicy,
366
369
  });
@@ -387,6 +390,7 @@ class SiteWithEcsBackend extends common_1.CommonConstruct {
387
390
  comment: `Response Header Policy for ${props.type} for ${this.id}-distribution - ${this.props.stage} stage`,
388
391
  responseHeadersPolicyName: `${this.id}-${props.type}-response`,
389
392
  securityHeadersBehavior: {
393
+ ...props.securityHeadersBehavior,
390
394
  strictTransportSecurity: {
391
395
  ...props.securityHeadersBehavior?.strictTransportSecurity,
392
396
  accessControlMaxAge: cdk.Duration.seconds(props.securityHeadersBehavior?.strictTransportSecurity?.accessControlMaxAgeInSeconds),
@@ -52,7 +52,9 @@ export declare class ApiManager {
52
52
  * @param methodRequestParameters
53
53
  * @param proxyIntegration
54
54
  */
55
- createApiResource(id: string, scope: CommonConstruct, parent: apig.IResource, path: string, integration: apig.Integration, addProxy: boolean, authorizer?: apig.IAuthorizer, allowedOrigins?: string[], allowedMethods?: string[], allowedHeaders?: string[], methodRequestParameters?: any, proxyIntegration?: apig.Integration): apig.Resource;
55
+ createApiResource(id: string, scope: CommonConstruct, parent: apig.IResource, path: string, integration: apig.Integration, addProxy: boolean, authorizer?: apig.IAuthorizer, allowedOrigins?: string[], allowedMethods?: string[], allowedHeaders?: string[], methodRequestParameters?: {
56
+ [param: string]: boolean;
57
+ }, proxyIntegration?: apig.Integration): apig.Resource;
56
58
  /**
57
59
  * @summary Method to create an api deployment
58
60
  * @param id
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gradientedge/cdk-utils",
3
- "version": "8.113.0",
3
+ "version": "8.115.0",
4
4
  "description": "Utilities for AWS CDK provisioning",
5
5
  "main": "dist/index.js",
6
6
  "engines": {
@@ -46,14 +46,14 @@
46
46
  }
47
47
  },
48
48
  "dependencies": {
49
- "@aws-sdk/client-secrets-manager": "^3.385.0",
50
- "@aws-sdk/credential-providers": "^3.385.0",
51
- "@aws-sdk/types": "^3.378.0",
52
- "@types/lodash": "^4.14.196",
53
- "@types/node": "^20.4.8",
49
+ "@aws-sdk/client-secrets-manager": "^3.395.0",
50
+ "@aws-sdk/credential-providers": "^3.395.0",
51
+ "@aws-sdk/types": "^3.391.0",
52
+ "@types/lodash": "^4.14.197",
53
+ "@types/node": "^20.5.1",
54
54
  "@types/uuid": "^9.0.2",
55
55
  "app-root-path": "^3.1.0",
56
- "aws-cdk-lib": "^2.90.0",
56
+ "aws-cdk-lib": "^2.92.0",
57
57
  "constructs": "^10.2.69",
58
58
  "lodash": "^4.17.21",
59
59
  "moment": "^2.29.4",
@@ -63,22 +63,22 @@
63
63
  "uuid": "^9.0.0"
64
64
  },
65
65
  "devDependencies": {
66
- "@babel/core": "^7.22.9",
67
- "@babel/eslint-parser": "^7.22.9",
66
+ "@babel/core": "^7.22.10",
67
+ "@babel/eslint-parser": "^7.22.10",
68
68
  "@babel/plugin-proposal-class-properties": "^7.18.6",
69
69
  "@types/jest": "^29.5.3",
70
- "@typescript-eslint/eslint-plugin": "^6.2.1",
71
- "@typescript-eslint/parser": "^6.2.1",
72
- "aws-cdk": "^2.90.0",
70
+ "@typescript-eslint/eslint-plugin": "^6.4.0",
71
+ "@typescript-eslint/parser": "^6.4.0",
72
+ "aws-cdk": "^2.92.0",
73
73
  "better-docs": "^2.7.2",
74
74
  "codecov": "^3.8.3",
75
75
  "commitizen": "^4.3.0",
76
76
  "docdash": "^2.0.1",
77
77
  "dotenv": "^16.3.1",
78
- "eslint": "^8.46.0",
78
+ "eslint": "^8.47.0",
79
79
  "eslint-config-prettier": "^9.0.0",
80
- "eslint-plugin-import": "^2.28.0",
81
- "eslint-plugin-jsdoc": "^46.4.6",
80
+ "eslint-plugin-import": "^2.28.1",
81
+ "eslint-plugin-jsdoc": "^46.5.0",
82
82
  "husky": "^8.0.3",
83
83
  "jest": "^29.6.2",
84
84
  "jest-extended": "^4.0.1",
@@ -88,10 +88,10 @@
88
88
  "jsdoc-mermaid": "^1.0.0",
89
89
  "jsdoc-plugin-typescript": "^2.2.1",
90
90
  "jsdoc-to-markdown": "^8.0.0",
91
- "prettier": "^3.0.1",
91
+ "prettier": "^3.0.2",
92
92
  "prettier-plugin-organize-imports": "^3.2.3",
93
93
  "rimraf": "^5.0.1",
94
- "semantic-release": "^21.0.7",
94
+ "semantic-release": "^21.0.9",
95
95
  "taffydb": "^2.7.3",
96
96
  "ts-jest": "^29.1.1",
97
97
  "ts-node": "^10.9.1",
@@ -0,0 +1,3 @@
1
+ export * from './main'
2
+ export * from './target'
3
+ export * from './types'
@@ -0,0 +1,225 @@
1
+ import { Fn, RemovalPolicy } from 'aws-cdk-lib'
2
+ import {
3
+ AccessLogFormat,
4
+ BasePathMapping,
5
+ Cors,
6
+ EndpointType,
7
+ LogGroupLogDestination,
8
+ MethodLoggingLevel,
9
+ Resource,
10
+ RestApi,
11
+ } from 'aws-cdk-lib/aws-apigateway'
12
+ import { ISecret } from 'aws-cdk-lib/aws-secretsmanager'
13
+ import { Construct } from 'constructs'
14
+ import { CommonConstruct } from '../../common'
15
+ import { ApiToAnyTargetProps, ApiToAnyTargetRestApiResource, ApiToAnyTargetRestApiType } from './types'
16
+ import { ApiToAnyTargetRestApi } from './target'
17
+
18
+ /**
19
+ * @classdesc Provides a construct to create and deploy a shallow API Gateway
20
+ * @example
21
+ * import { ApiToAnyTarget, ApiToAnyTargetProps } '@gradientedge/cdk-utils'
22
+ * import { Construct } from 'constructs'
23
+ *
24
+ * class CustomConstruct extends ApiToAnyTarget {
25
+ * constructor(parent: Construct, id: string, props: ApiToAnyTargetProps) {
26
+ * super(parent, id, props)
27
+ * this.props = props
28
+ * this.id = id
29
+ * this.initResources()
30
+ * }
31
+ * }
32
+ */
33
+ export class ApiToAnyTarget extends CommonConstruct {
34
+ props: ApiToAnyTargetProps
35
+ id: string
36
+
37
+ /* application related resources */
38
+ applicationSecrets: ISecret[]
39
+
40
+ /* rest restApi related resources */
41
+ apiToAnyTargetRestApi: ApiToAnyTargetRestApiType
42
+ apiResource: string
43
+
44
+ constructor(parent: Construct, id: string, props: ApiToAnyTargetProps) {
45
+ super(parent, id, props)
46
+ this.props = props
47
+ this.id = id
48
+
49
+ this.apiToAnyTargetRestApi = new ApiToAnyTargetRestApi()
50
+ }
51
+
52
+ protected initResources() {
53
+ /* application related resources */
54
+ this.resolveSecrets()
55
+
56
+ /* core resources */
57
+ this.resolveHostedZone()
58
+ this.resolveCertificate()
59
+
60
+ /* restApi related resources */
61
+ this.createApiToAnyTargetRestApiLogGroup()
62
+ this.createApiToAnyTargetRestApi()
63
+ this.createApiDomain()
64
+ this.createApiBasePathMapping()
65
+ this.createApiRouteAssets()
66
+ }
67
+
68
+ /**
69
+ * @summary Method to resolve secrets from SecretsManager
70
+ * - To be implemented in the overriding method in the implementation class
71
+ */
72
+ protected resolveSecrets() {
73
+ this.applicationSecrets = []
74
+ }
75
+
76
+ /**
77
+ * @summary Method to resolve a hosted zone based on domain attributes
78
+ */
79
+ protected resolveHostedZone() {
80
+ this.apiToAnyTargetRestApi.hostedZone = this.route53Manager.withHostedZoneFromFullyQualifiedDomainName(
81
+ `${this.id}-hosted-zone`,
82
+ this,
83
+ this.props.useExistingHostedZone
84
+ )
85
+ }
86
+
87
+ /**
88
+ * @summary Method to resolve a certificate based on attributes
89
+ */
90
+ protected resolveCertificate() {
91
+ if (this.props.api.useExisting) return
92
+ if (
93
+ this.props.api.certificate.useExistingCertificate &&
94
+ this.props.api.certificate.certificateSsmName &&
95
+ this.props.api.certificate.certificateRegion
96
+ ) {
97
+ this.props.api.certificate.certificateArn = this.ssmManager.readStringParameterFromRegion(
98
+ `${this.id}-certificate-param`,
99
+ this,
100
+ this.props.api.certificate.certificateSsmName,
101
+ this.props.api.certificate.certificateRegion
102
+ )
103
+ }
104
+
105
+ this.apiToAnyTargetRestApi.certificate = this.acmManager.resolveCertificate(
106
+ `${this.id}-certificate`,
107
+ this,
108
+ this.props.api.certificate
109
+ )
110
+ }
111
+
112
+ protected createApiToAnyTargetRestApiLogGroup() {
113
+ this.apiToAnyTargetRestApi.accessLogGroup = this.logManager.createLogGroup(`${this.id}-rest-api-access-log`, this, {
114
+ logGroupName: `/custom/api/${this.id}-rest-api-access`,
115
+ removalPolicy: RemovalPolicy.DESTROY,
116
+ })
117
+ }
118
+
119
+ protected createApiToAnyTargetRestApi() {
120
+ if (this.props.api.useExisting && this.props.api.importedRestApiRef) {
121
+ this.apiToAnyTargetRestApi.api = RestApi.fromRestApiId(
122
+ this,
123
+ `${this.id}-rest-api`,
124
+ Fn.importValue(this.props.api.importedRestApiRef)
125
+ )
126
+ return
127
+ }
128
+
129
+ this.apiToAnyTargetRestApi.api = new RestApi(this, `${this.id}-rest-api`, {
130
+ ...{
131
+ cloudWatchRole: this.props.api.restApi?.cloudWatchRole ?? true,
132
+ defaultCorsPreflightOptions: {
133
+ allowHeaders: Cors.DEFAULT_HEADERS,
134
+ allowMethods: Cors.ALL_METHODS,
135
+ allowOrigins: Cors.ALL_ORIGINS,
136
+ },
137
+ defaultIntegration: this.apiToAnyTargetRestApi.integration,
138
+ defaultMethodOptions: {
139
+ methodResponses: [this.apiToAnyTargetRestApi.methodResponse, this.apiToAnyTargetRestApi.methodErrorResponse],
140
+ },
141
+ deploy: this.props.api.restApi?.deploy ?? true,
142
+ deployOptions: {
143
+ accessLogDestination: new LogGroupLogDestination(this.apiToAnyTargetRestApi.accessLogGroup),
144
+ accessLogFormat: AccessLogFormat.jsonWithStandardFields(),
145
+ dataTraceEnabled: this.props.api.restApi?.deployOptions?.dataTraceEnabled,
146
+ description: `${this.id} - ${this.props.stage} stage`,
147
+ loggingLevel: MethodLoggingLevel.INFO,
148
+ metricsEnabled: true,
149
+ stageName: this.props.stage,
150
+ tracingEnabled: this.props.api.restApi?.deployOptions?.tracingEnabled,
151
+ },
152
+ endpointConfiguration: {
153
+ types: [this.isProductionStage() ? EndpointType.EDGE : EndpointType.REGIONAL],
154
+ },
155
+ restApiName: `${this.id}-rest-api-${this.props.stage}`,
156
+ },
157
+ ...this.props.api.restApi,
158
+ })
159
+ this.addCfnOutput(`${this.id}-restApiId`, this.apiToAnyTargetRestApi.api.restApiId)
160
+ this.addCfnOutput(`${this.id}-restApiRootResourceId`, this.apiToAnyTargetRestApi.api.root.resourceId)
161
+ }
162
+
163
+ protected createApiToAnyTargetResource(apiResourceProps: ApiToAnyTargetRestApiResource) {
164
+ if (!this.props.api.withResource) return
165
+ let rootResource
166
+ if (this.props.api.withResource && this.props.api.importedRestApiRootResourceRef) {
167
+ rootResource = Resource.fromResourceAttributes(this, `${this.id}-root-resource-for-${apiResourceProps.path}`, {
168
+ path: '/',
169
+ resourceId: Fn.importValue(this.props.api.importedRestApiRootResourceRef),
170
+ restApi: this.apiToAnyTargetRestApi.api,
171
+ })
172
+ } else {
173
+ rootResource = this.apiToAnyTargetRestApi.api.root
174
+ }
175
+
176
+ return this.apiManager.createApiResource(
177
+ `${this.id}-resource-${apiResourceProps.path}}`,
178
+ this,
179
+ apiResourceProps.parent ?? rootResource,
180
+ apiResourceProps.path,
181
+ apiResourceProps.integration,
182
+ apiResourceProps.addProxy,
183
+ apiResourceProps.authorizer,
184
+ apiResourceProps.allowedOrigins,
185
+ apiResourceProps.allowedMethods,
186
+ apiResourceProps.allowedHeaders,
187
+ apiResourceProps.methodRequestParameters,
188
+ apiResourceProps.proxyIntegration
189
+ )
190
+ }
191
+
192
+ protected createApiDomain() {
193
+ if (this.props.api.useExisting) return
194
+ this.apiToAnyTargetRestApi.domain = this.apiManager.createApiDomain(
195
+ `${this.id}-api-domain`,
196
+ this,
197
+ this.isProductionStage() || this.props.skipStageForARecords
198
+ ? `${this.props.apiSubDomain}.${this.fullyQualifiedDomainName}`
199
+ : `${this.props.apiSubDomain}-${this.props.stage}.${this.fullyQualifiedDomainName}`,
200
+ this.apiToAnyTargetRestApi.certificate
201
+ )
202
+ }
203
+
204
+ protected createApiBasePathMapping() {
205
+ if (this.props.api.useExisting) return
206
+ new BasePathMapping(this, `${this.id}-base-bath-mapping`, {
207
+ basePath: '',
208
+ domainName: this.apiToAnyTargetRestApi.domain,
209
+ restApi: this.apiToAnyTargetRestApi.api,
210
+ stage: this.apiToAnyTargetRestApi.api.deploymentStage,
211
+ })
212
+ }
213
+
214
+ protected createApiRouteAssets() {
215
+ if (this.props.api.useExisting) return
216
+ this.route53Manager.createApiGatewayARecord(
217
+ `${this.id}-custom-domain-a-record`,
218
+ this,
219
+ this.props.apiSubDomain,
220
+ this.apiToAnyTargetRestApi.domain,
221
+ this.apiToAnyTargetRestApi.hostedZone,
222
+ this.props.skipStageForARecords
223
+ )
224
+ }
225
+ }
@@ -0,0 +1,29 @@
1
+ import {
2
+ IRestApi,
3
+ IAuthorizer,
4
+ BasePathMapping,
5
+ DomainName,
6
+ Integration,
7
+ Method,
8
+ MethodResponse,
9
+ Resource,
10
+ } from 'aws-cdk-lib/aws-apigateway'
11
+ import { ICertificate } from 'aws-cdk-lib/aws-certificatemanager'
12
+ import { LogGroup } from 'aws-cdk-lib/aws-logs'
13
+ import { IHostedZone } from 'aws-cdk-lib/aws-route53'
14
+ import { ApiToAnyTargetRestApiType } from './types'
15
+
16
+ export class ApiToAnyTargetRestApi implements ApiToAnyTargetRestApiType {
17
+ accessLogGroup: LogGroup
18
+ api: IRestApi
19
+ authoriser?: IAuthorizer
20
+ basePathMappings: BasePathMapping[]
21
+ certificate: ICertificate
22
+ domain: DomainName
23
+ hostedZone: IHostedZone
24
+ integration: Integration
25
+ method: { [httpMethod: string]: Method }
26
+ methodErrorResponse: MethodResponse
27
+ methodResponse: MethodResponse
28
+ resource: { [path: string]: Resource }
29
+ }
@@ -0,0 +1,66 @@
1
+ import {
2
+ BasePathMapping,
3
+ DomainName,
4
+ IAuthorizer,
5
+ IResource,
6
+ IRestApi,
7
+ Integration,
8
+ Method,
9
+ MethodResponse,
10
+ Resource,
11
+ RestApiProps,
12
+ } from 'aws-cdk-lib/aws-apigateway'
13
+ import { ICertificate } from 'aws-cdk-lib/aws-certificatemanager'
14
+ import { LogGroup } from 'aws-cdk-lib/aws-logs'
15
+ import { IHostedZone } from 'aws-cdk-lib/aws-route53'
16
+ import { CommonStackProps } from '../../common'
17
+ import { AcmProps } from '../../services'
18
+
19
+ export interface ApiToAnyTargetRestApiType {
20
+ accessLogGroup: LogGroup
21
+ api: IRestApi
22
+ authoriser?: IAuthorizer
23
+ basePathMappings: BasePathMapping[]
24
+ certificate: ICertificate
25
+ domain: DomainName
26
+ hostedZone: IHostedZone
27
+ integration: Integration
28
+ method: { [httpMethod: string]: Method }
29
+ methodErrorResponse: MethodResponse
30
+ methodResponse: MethodResponse
31
+ resource: { [path: string]: Resource }
32
+ }
33
+
34
+ export interface ApiToAnyTargetRestApiResource {
35
+ addProxy: boolean
36
+ authorizer?: IAuthorizer
37
+ allowedOrigins?: string[]
38
+ allowedMethods?: string[]
39
+ allowedHeaders?: string[]
40
+ integration: Integration
41
+ methodRequestParameters?: { [param: string]: boolean }
42
+ path: string
43
+ parent?: IResource
44
+ proxyIntegration?: Integration
45
+ }
46
+
47
+ export interface ApiToAnyTargetRestApiProps {
48
+ certificate: AcmProps
49
+ importedRestApiRef?: string
50
+ importedRestApiRootResourceRef?: string
51
+ methodErrorResponse: MethodResponse
52
+ methodResponse: MethodResponse
53
+ restApi: RestApiProps
54
+ useExisting: boolean
55
+ withResource?: boolean
56
+ }
57
+
58
+ export interface ApiToAnyTargetProps extends CommonStackProps {
59
+ api: ApiToAnyTargetRestApiProps
60
+ apiRootPaths?: string[]
61
+ apiSubDomain: string
62
+ logLevel: string
63
+ nodeEnv: string
64
+ timezone: string
65
+ useExistingHostedZone: boolean
66
+ }
@@ -1,3 +1,4 @@
1
+ export * from './api-to-any-target'
1
2
  export * from './api-to-eventbridge-target'
2
3
  export * from './api-to-eventbridge-target-with-sns'
3
4
  export * from './api-to-lambda-target'
@@ -14,7 +14,7 @@ import * as s3 from 'aws-cdk-lib/aws-s3'
14
14
  import * as efs from 'aws-cdk-lib/aws-efs'
15
15
  import { Construct } from 'constructs'
16
16
  import { CommonConstruct } from '../../common'
17
- import { SiteWithEcsBackendProps, SiteResponseHeadersPolicyProps } from './types'
17
+ import { SiteWithEcsBackendProps, SiteResponseHeadersPolicyProps, SiteCachePolicyProps } from './types'
18
18
 
19
19
  /**
20
20
  * @classdesc Provides a construct to create and deploy a site hosted with an clustered ECS/ELB backend
@@ -92,7 +92,7 @@ export class SiteWithEcsBackend extends CommonConstruct {
92
92
  this.createEcsBuildArgs()
93
93
  this.createEcsContainerImage()
94
94
  this.createEcsService()
95
- this.createSiteCacheConfigPolicy()
95
+ this.createSiteOriginCachePolicy()
96
96
  this.createSiteOriginRequestPolicy()
97
97
  this.createSiteOriginResponseHeadersPolicy()
98
98
  this.createSiteOrigin()
@@ -410,20 +410,23 @@ export class SiteWithEcsBackend extends CommonConstruct {
410
410
  this.siteLogBucket = this.s3Manager.createS3Bucket(`${this.id}-site-logs`, this, this.props.siteLogBucket)
411
411
  }
412
412
 
413
- protected createSiteCacheConfigPolicy() {
414
- if (!this.props.siteCachePolicy) return
415
- this.siteCachePolicy = new cloudfront.CachePolicy(this, `${this.id}-site-cache-policy`, {
416
- cachePolicyName: `${this.id}-site-cache-policy`,
413
+ protected createSiteCachePolicy(id: string, siteCachePolicy: SiteCachePolicyProps) {
414
+ return new cloudfront.CachePolicy(this, `${id}`, {
415
+ cachePolicyName: `${this.id}-${siteCachePolicy.cachePolicyName}`,
417
416
  comment: `Policy for ${this.id}-distribution - ${this.props.stage} stage`,
418
- defaultTtl: cdk.Duration.seconds(this.props.siteCachePolicy.defaultTtlInSeconds),
419
- minTtl: cdk.Duration.seconds(this.props.siteCachePolicy.minTtlInSeconds),
420
- maxTtl: cdk.Duration.seconds(this.props.siteCachePolicy.maxTtlInSeconds),
421
- enableAcceptEncodingGzip: this.props.siteCachePolicy.enableAcceptEncodingGzip,
422
- queryStringBehavior: this.props.siteCachePolicy.queryStringBehavior,
423
- headerBehavior: this.props.siteCachePolicy.headerBehavior,
424
- cookieBehavior: this.props.siteCachePolicy.cookieBehavior,
417
+ defaultTtl: cdk.Duration.seconds(siteCachePolicy.defaultTtlInSeconds),
418
+ minTtl: cdk.Duration.seconds(siteCachePolicy.minTtlInSeconds),
419
+ maxTtl: cdk.Duration.seconds(siteCachePolicy.maxTtlInSeconds),
420
+ enableAcceptEncodingGzip: siteCachePolicy.enableAcceptEncodingGzip,
421
+ queryStringBehavior: siteCachePolicy.queryStringBehavior,
422
+ headerBehavior: siteCachePolicy.headerBehavior,
423
+ cookieBehavior: siteCachePolicy.cookieBehavior,
425
424
  })
425
+ }
426
426
 
427
+ protected createSiteOriginCachePolicy() {
428
+ if (!this.props.siteCachePolicy) return
429
+ this.siteCachePolicy = this.createSiteCachePolicy(`${this.id}-site-cache-policy`, this.props.siteCachePolicy)
427
430
  _.assign(this.props.siteDistribution.defaultBehavior, {
428
431
  cachePolicy: this.siteCachePolicy,
429
432
  })
@@ -451,6 +454,7 @@ export class SiteWithEcsBackend extends CommonConstruct {
451
454
  comment: `Response Header Policy for ${props.type} for ${this.id}-distribution - ${this.props.stage} stage`,
452
455
  responseHeadersPolicyName: `${this.id}-${props.type}-response`,
453
456
  securityHeadersBehavior: {
457
+ ...props.securityHeadersBehavior,
454
458
  strictTransportSecurity: {
455
459
  ...props.securityHeadersBehavior?.strictTransportSecurity,
456
460
  accessControlMaxAge: cdk.Duration.seconds(
@@ -135,7 +135,7 @@ export class ApiManager {
135
135
  allowedOrigins?: string[],
136
136
  allowedMethods?: string[],
137
137
  allowedHeaders?: string[],
138
- methodRequestParameters?: any,
138
+ methodRequestParameters?: { [param: string]: boolean },
139
139
  proxyIntegration?: apig.Integration
140
140
  ) {
141
141
  const methods = allowedMethods ?? apig.Cors.ALL_METHODS