Skip to content

Commit 8e5d552

Browse files
authored
Merge pull request #1218 from awslabs/bump/2.72.0
chore(release): 2.72.0
2 parents 88a0530 + ba11444 commit 8e5d552

File tree

115 files changed

+3393
-710
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

115 files changed

+3393
-710
lines changed

CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,14 @@
22

33
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
44

5+
## [2.72.0](https://github.com/awslabs/aws-solutions-constructs/compare/v2.71.0...v2.72.0) (2024-10-06)
6+
7+
Build on CDK v2.161.0
8+
9+
### Features
10+
11+
* **aws-apigateway-sqs:** add schema validation for create operation ([#1216](https://github.com/awslabs/aws-solutions-constructs/issues/1216)) ([9d42398](https://github.com/awslabs/aws-solutions-constructs/commit/9d423986b8aaee4c921000e8738ce8ad5ef78765))
12+
513
## [2.71.0](https://github.com/awslabs/aws-solutions-constructs/compare/v2.70.0...v2.71.0) (2024-09-27)
614

715
Build on CDK v2.160.0

deployment/v2/align-version.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ const nullVersionMarker = process.argv[2];
1010
const targetSolutionsConstructsVersion = process.argv[3];
1111

1212
// these versions need to be sourced from a config file
13-
const awsCdkLibVersion = '2.160.0';
13+
const awsCdkLibVersion = '2.161.0';
1414

1515
for (const file of process.argv.splice(4)) {
1616
const pkg = JSON.parse(fs.readFileSync(file).toString());

source/lerna.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,5 @@
55
"patterns/@aws-solutions-constructs/*"
66
],
77
"rejectCycles": "true",
8-
"version": "2.71.0"
8+
"version": "2.72.0"
99
}

source/patterns/@aws-solutions-constructs/aws-apigateway-sqs/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ new ApiGatewayToSqs(this, "ApiGatewayToSqsPattern", new ApiGatewayToSqsProps.Bui
8181
|enableEncryptionWithCustomerManagedKey?|`boolean`|If no key is provided, this flag determines whether the queue is encrypted with a new CMK or an AWS managed key. This flag is ignored if any of the following are defined: queueProps.encryptionMasterKey, encryptionKey or encryptionKeyProps.|
8282
|encryptionKey?|[`kms.Key`](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_kms.Key.html)|An optional, imported encryption key to encrypt the SQS Queue with.|
8383
|encryptionKeyProps?|[`kms.KeyProps`](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_kms.Key.html#construct-props)|Optional user provided properties to override the default properties for the KMS encryption key used to encrypt the SQS queue with.|
84+
|messageSchema?|{ [contentType: string]: [api.JsonSchema](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_apigateway.JsonSchema.html); }|Optional schema to define format of incoming message in API request body. Example: { "application/json": { schema: api.JsonSchemaVersion.DRAFT4, title: 'pollResponse', type: api.JsonSchemaType.OBJECT, required: ['firstProperty', 'antotherProperty'], additionalProperties: false, properties: { firstProperty: { type: api.JsonSchemaType.STRING }, antotherProperty: { type: api.JsonSchemaType.STRING } } } Only relevant for create operation, if allowCreateOperation is not true, then supplying this is an error. Sending this value causes this construct to turn on validation for the request body. @default - None|
8485

8586
## Pattern Properties
8687

source/patterns/@aws-solutions-constructs/aws-apigateway-sqs/lib/index.ts

Lines changed: 59 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,29 @@ export interface ApiGatewayToSqsProps {
226226
* @default - None
227227
*/
228228
readonly encryptionKeyProps?: kms.KeyProps;
229+
/**
230+
* Optional schema to define format of incoming message in API request body. Example:
231+
* {
232+
* "application/json": {
233+
* schema: api.JsonSchemaVersion.DRAFT4,
234+
* title: 'pollResponse',
235+
* type: api.JsonSchemaType.OBJECT,
236+
* required: ['firstProperty', 'antotherProperty'],
237+
* additionalProperties: false,
238+
* properties: {
239+
* firstProperty: { type: api.JsonSchemaType.STRING },
240+
* antotherProperty: { type: api.JsonSchemaType.STRING }
241+
* }
242+
* }
243+
*
244+
* Only relevant for create operation, if allowCreateOperation is not true, then supplying this
245+
* is an error.
246+
*
247+
* Sending this value causes this construct to turn on validation for the request body.
248+
*
249+
* @default - None
250+
*/
251+
readonly messageSchema?: { [contentType: string]: api.JsonSchema; };
229252
}
230253

231254
/**
@@ -257,15 +280,15 @@ export class ApiGatewayToSqs extends Construct {
257280

258281
if (this.CheckCreateRequestProps(props)) {
259282
throw new Error(`The 'allowCreateOperation' property must be set to true when setting any of the following: ` +
260-
`'createRequestTemplate', 'additionalCreateRequestTemplates', 'createIntegrationResponses'`);
283+
`'createRequestTemplate', 'additionalCreateRequestTemplates', 'createIntegrationResponses', 'messageSchema'`);
261284
}
262285
if (this.CheckReadRequestProps(props)) {
263286
throw new Error(`The 'allowReadOperation' property must be set to true or undefined when setting any of the following: ` +
264287
`'readRequestTemplate', 'additionalReadRequestTemplates', 'readIntegrationResponses'`);
265288
}
266289
if (this.CheckDeleteRequestProps(props)) {
267290
throw new Error(`The 'allowDeleteOperation' property must be set to true when setting any of the following: ` +
268-
`'deleteRequestTemplate', 'additionalDeleteRequestTemplates', 'deleteIntegrationResponses'`);
291+
`'deleteRequestTemplate', 'additionalDeleteRequestTemplates', 'deleteIntegrationResponses'`);
269292
}
270293

271294
// Setup the queue
@@ -296,7 +319,35 @@ export class ApiGatewayToSqs extends Construct {
296319
// Create
297320
const createRequestTemplate = props.createRequestTemplate ?? this.defaultCreateRequestTemplate;
298321
if (props.allowCreateOperation && props.allowCreateOperation === true) {
299-
const createMethodOptions: api.MethodOptions = props.createMethodResponses ? { methodResponses: props.createMethodResponses } : {};
322+
let createMethodOptions: api.MethodOptions = {};
323+
324+
// If the client supplied model definitions, set requestModels
325+
if (props.messageSchema) {
326+
const requestModels: { [contentType: string]: api.IModel; } = {};
327+
Object.keys(props.messageSchema).forEach(key => {
328+
const contentType = key;
329+
const schema = props.messageSchema![key];
330+
331+
const newModel = new api.Model(this, `${id}-model-${defaults.removeNonAlphanumeric(contentType)}`, {
332+
restApi: this.apiGateway,
333+
contentType,
334+
schema
335+
});
336+
337+
requestModels[contentType] = newModel;
338+
createMethodOptions = defaults.overrideProps(createMethodOptions, {
339+
requestModels,
340+
requestValidatorOptions: {
341+
validateRequestBody: true
342+
}
343+
});
344+
});
345+
}
346+
347+
if (props.createMethodResponses) {
348+
createMethodOptions = defaults.overrideProps(createMethodOptions, { methodResponses: props.createMethodResponses });
349+
}
350+
300351
this.addActionToPolicy("sqs:SendMessage");
301352
defaults.addProxyMethodToApiResource({
302353
service: "sqs",
@@ -353,22 +404,22 @@ export class ApiGatewayToSqs extends Construct {
353404
}
354405
private CheckReadRequestProps(props: ApiGatewayToSqsProps): boolean {
355406
if ((props.readRequestTemplate || props.additionalReadRequestTemplates || props.readIntegrationResponses)
356-
&& props.allowReadOperation === false) {
407+
&& props.allowReadOperation === false) {
357408
return true;
358409
}
359410
return false;
360411
}
361412
private CheckDeleteRequestProps(props: ApiGatewayToSqsProps): boolean {
362413
if ((props.deleteRequestTemplate || props.additionalDeleteRequestTemplates || props.deleteIntegrationResponses)
363-
&& props.allowDeleteOperation !== true) {
414+
&& props.allowDeleteOperation !== true) {
364415
return true;
365416
}
366417
return false;
367418
}
368419

369420
private CheckCreateRequestProps(props: ApiGatewayToSqsProps): boolean {
370-
if ((props.createRequestTemplate || props.additionalCreateRequestTemplates || props.createIntegrationResponses)
371-
&& props.allowCreateOperation !== true) {
421+
if ((props.createRequestTemplate || props.additionalCreateRequestTemplates || props.createIntegrationResponses || props.messageSchema)
422+
&& props.allowCreateOperation !== true) {
372423
return true;
373424
}
374425
return false;
@@ -379,7 +430,7 @@ export class ApiGatewayToSqs extends Construct {
379430
resources: [
380431
this.sqsQueue.queueArn
381432
],
382-
actions: [ `${action}` ]
433+
actions: [`${action}`]
383434
}));
384435
}
385436
}

source/patterns/@aws-solutions-constructs/aws-apigateway-sqs/test/apigateway-sqs.test.ts

Lines changed: 108 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,11 @@
1313

1414
// Imports
1515
import { RemovalPolicy, Stack } from "aws-cdk-lib";
16-
import { ApiGatewayToSqs } from '../lib';
16+
import { ApiGatewayToSqs, ApiGatewayToSqsProps } from '../lib';
1717
import * as api from "aws-cdk-lib/aws-apigateway";
1818
import * as kms from 'aws-cdk-lib/aws-kms';
1919
import * as sqs from "aws-cdk-lib/aws-sqs";
20-
import { Template } from "aws-cdk-lib/assertions";
20+
import { Match, Template } from "aws-cdk-lib/assertions";
2121

2222
test('Test deployment w/o DLQ', () => {
2323
const stack = new Stack();
@@ -69,7 +69,7 @@ test('Test properties', () => {
6969
deployDeadLetterQueue: true,
7070
maxReceiveCount: 3
7171
});
72-
// Assertion 1
72+
// Assertion 1
7373
expect(pattern.apiGateway).toBeDefined();
7474
// Assertion 2
7575
expect(pattern.sqsQueue).toBeDefined();
@@ -113,7 +113,7 @@ test('Test deployment for override ApiGateway createRequestTemplate', () => {
113113
const stack = new Stack();
114114

115115
new ApiGatewayToSqs(stack, 'api-gateway-sqs', {
116-
createRequestTemplate: "Action=SendMessage&MessageBody=$util.urlEncode(\"HelloWorld\")",
116+
createRequestTemplate: "Action=SendMessage&MessageBody=$util.urlEncode(\"HelloWorld\")",
117117
allowCreateOperation: true
118118
});
119119
const template = Template.fromStack(stack);
@@ -131,7 +131,7 @@ test('Test deployment for override ApiGateway getRequestTemplate', () => {
131131
const stack = new Stack();
132132

133133
new ApiGatewayToSqs(stack, 'api-gateway-sqs', {
134-
readRequestTemplate: "Action=HelloWorld",
134+
readRequestTemplate: "Action=HelloWorld",
135135
allowReadOperation: true
136136
});
137137
const template = Template.fromStack(stack);
@@ -149,7 +149,7 @@ test('Test deployment for override ApiGateway deleteRequestTemplate', () => {
149149
const stack = new Stack();
150150

151151
new ApiGatewayToSqs(stack, 'api-gateway-sqs', {
152-
deleteRequestTemplate: "Action=HelloWorld",
152+
deleteRequestTemplate: "Action=HelloWorld",
153153
allowDeleteOperation: true
154154
});
155155
const template = Template.fromStack(stack);
@@ -577,13 +577,22 @@ test('Construct uses custom deleteIntegrationResponses property', () => {
577577
});
578578
});
579579

580+
test('Construct throws error when messageSchema is set and allowCreateOperation is not true', () => {
581+
const stack = new Stack();
582+
const app = () => new ApiGatewayToSqs(stack, 'api-gateway-sqs', {
583+
messageSchema: '{}' as any,
584+
});
585+
586+
expect(app).toThrowError(/The 'allowCreateOperation' property must be set to true when setting any of the following: 'createRequestTemplate', 'additionalCreateRequestTemplates', 'createIntegrationResponses', 'messageSchema'/);
587+
});
588+
580589
test('Construct throws error when createRequestTemplate is set and allowCreateOperation is not true', () => {
581590
const stack = new Stack();
582591
const app = () => new ApiGatewayToSqs(stack, 'api-gateway-sqs', {
583592
createRequestTemplate: '{}',
584593
});
585594

586-
expect(app).toThrowError(/The 'allowCreateOperation' property must be set to true when setting any of the following: 'createRequestTemplate', 'additionalCreateRequestTemplates', 'createIntegrationResponses'/);
595+
expect(app).toThrowError(/The 'allowCreateOperation' property must be set to true when setting any of the following: 'createRequestTemplate', 'additionalCreateRequestTemplates', 'createIntegrationResponses', 'messageSchema'/);
587596
});
588597

589598
test('Construct throws error when additionalCreateRequestTemplates is set and allowCreateOperation is not true', () => {
@@ -592,7 +601,7 @@ test('Construct throws error when additionalCreateRequestTemplates is set and al
592601
additionalCreateRequestTemplates: {}
593602
});
594603

595-
expect(app).toThrowError(/The 'allowCreateOperation' property must be set to true when setting any of the following: 'createRequestTemplate', 'additionalCreateRequestTemplates', 'createIntegrationResponses'/);
604+
expect(app).toThrowError(/The 'allowCreateOperation' property must be set to true when setting any of the following: 'createRequestTemplate', 'additionalCreateRequestTemplates', 'createIntegrationResponses', 'messageSchema'/);
596605
});
597606

598607
test('Construct throws error when createIntegrationResponses is set and allowCreateOperation is not true', () => {
@@ -601,7 +610,7 @@ test('Construct throws error when createIntegrationResponses is set and allowCre
601610
createIntegrationResponses: []
602611
});
603612

604-
expect(app).toThrowError(/The 'allowCreateOperation' property must be set to true when setting any of the following: 'createRequestTemplate', 'additionalCreateRequestTemplates', 'createIntegrationResponses'/);
613+
expect(app).toThrowError(/The 'allowCreateOperation' property must be set to true when setting any of the following: 'createRequestTemplate', 'additionalCreateRequestTemplates', 'createIntegrationResponses', 'messageSchema'/);
605614
});
606615

607616
test('Construct throws error when readRequestTemplate is set and allowReadOperation is false', () => {
@@ -694,6 +703,7 @@ test('Construct uses custom createMethodResponses property', () => {
694703
});
695704

696705
const template = Template.fromStack(stack);
706+
697707
template.hasResourceProperties('AWS::ApiGateway::Method', {
698708
HttpMethod: 'POST',
699709
MethodResponses: [{
@@ -712,7 +722,7 @@ test('Construct uses custom createMethodResponses property', () => {
712722
});
713723
});
714724

715-
test.only('Construct uses custom deleteMethodResponses property', () => {
725+
test('Construct uses custom deleteMethodResponses property', () => {
716726
const stack = new Stack();
717727
new ApiGatewayToSqs(stack, 'api-gateway-sqs', {
718728
allowCreateOperation: false,
@@ -750,7 +760,7 @@ test.only('Construct uses custom deleteMethodResponses property', () => {
750760
});
751761
});
752762

753-
test.only('Construct uses custom readMethodResponses property', () => {
763+
test('Construct uses custom readMethodResponses property', () => {
754764
const stack = new Stack();
755765
new ApiGatewayToSqs(stack, 'api-gateway-sqs', {
756766
allowCreateOperation: false,
@@ -787,3 +797,90 @@ test.only('Construct uses custom readMethodResponses property', () => {
787797
}
788798
});
789799
});
800+
801+
test('Construct uses mulitple custom messageSchema', () => {
802+
const stack = new Stack();
803+
const props: ApiGatewayToSqsProps = {
804+
allowCreateOperation: true,
805+
messageSchema: {
806+
"application/json": {
807+
schema: api.JsonSchemaVersion.DRAFT4,
808+
title: 'pollResponse',
809+
type: api.JsonSchemaType.OBJECT,
810+
required: ['state', 'greeting'],
811+
additionalProperties: false,
812+
properties: {
813+
state: { type: api.JsonSchemaType.STRING },
814+
greeting: { type: api.JsonSchemaType.STRING }
815+
}
816+
},
817+
"application/text": {
818+
schema: api.JsonSchemaVersion.DRAFT4,
819+
title: 'pollResponse',
820+
type: api.JsonSchemaType.OBJECT,
821+
additionalProperties: false,
822+
properties: {
823+
textstate: { type: api.JsonSchemaType.STRING },
824+
textgreeting: { type: api.JsonSchemaType.STRING }
825+
}
826+
}
827+
}
828+
};
829+
830+
new ApiGatewayToSqs(stack, 'test-api-gateway-sqs', props);
831+
const template = Template.fromStack(stack);
832+
template.resourceCountIs("AWS::ApiGateway::Model", 2);
833+
template.hasResourceProperties("AWS::ApiGateway::Model", {
834+
ContentType: "application/json",
835+
RestApiId: Match.anyValue(),
836+
Schema: {
837+
$schema: "http://json-schema.org/draft-04/schema#",
838+
title: "pollResponse",
839+
type: "object",
840+
required: [
841+
"state",
842+
"greeting"
843+
],
844+
additionalProperties: false,
845+
properties: {
846+
state: {
847+
type: "string"
848+
},
849+
greeting: {
850+
type: "string"
851+
}
852+
}
853+
}
854+
});
855+
template.hasResourceProperties("AWS::ApiGateway::Model", {
856+
ContentType: "application/text",
857+
RestApiId: Match.anyValue(),
858+
Schema: {
859+
$schema: "http://json-schema.org/draft-04/schema#",
860+
title: "pollResponse",
861+
type: "object",
862+
additionalProperties: false,
863+
properties: {
864+
textstate: {
865+
type: "string"
866+
},
867+
textgreeting: {
868+
type: "string"
869+
}
870+
}
871+
}
872+
});
873+
template.hasResourceProperties("AWS::ApiGateway::Method", {
874+
Integration: {
875+
IntegrationHttpMethod: "POST"
876+
},
877+
RequestModels: {
878+
'application/json': {
879+
Ref: "testapigatewaysqstestapigatewaysqsmodelapplicationjson94EAC0E9"
880+
},
881+
'application/text': {
882+
Ref: "testapigatewaysqstestapigatewaysqsmodelapplicationtext6A72CC47"
883+
}
884+
}
885+
});
886+
});

source/patterns/@aws-solutions-constructs/aws-apigateway-sqs/test/integ.apisqs-custom-method-responses.js.snapshot/apisqs-custom-method-responses.assets.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
11
{
2-
"version": "36.0.0",
2+
"version": "38.0.1",
33
"files": {
4-
"ffcb28580155609b6742f44a39e21a4bc1428993a1c8d27ff75ccd916dc7fc83": {
4+
"a833b41dd73a597b3f3639956815c07229fc3e8ffc7c95190083a2f2150d1741": {
55
"source": {
66
"path": "apisqs-custom-method-responses.template.json",
77
"packaging": "file"
88
},
99
"destinations": {
1010
"current_account-current_region": {
1111
"bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}",
12-
"objectKey": "ffcb28580155609b6742f44a39e21a4bc1428993a1c8d27ff75ccd916dc7fc83.json",
12+
"objectKey": "a833b41dd73a597b3f3639956815c07229fc3e8ffc7c95190083a2f2150d1741.json",
1313
"assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}"
1414
}
1515
}

0 commit comments

Comments
 (0)