Skip to content

Commit 2cd5719

Browse files
author
Raffaele Garofalo
committed
Final solution with instructions
1 parent da16ab3 commit 2cd5719

File tree

12 files changed

+300
-16
lines changed

12 files changed

+300
-16
lines changed

sns-sqs-message-content-router-cdk/README.md

Lines changed: 41 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
# Amazon SNS to Amazon SQS by filtering content
1+
# Amazon SNS to Amazon SQS by filtering content (Message-content router pattern)
22

3-
This pattern creates an SNS topic and various SQS destination queues. Messages received by the SNS topic are routed to various SQS queues according to routing logic.
3+
This pattern creates an SNS topic and various SQS destination queues. Messages received by the SNS topic are routed to various SQS queues according to routing logic. The pattern is an implementation of the Integration pattern: "Content-Based Message route" available here: [https://www.enterpriseintegrationpatterns.com/patterns/messaging/ContentBasedRouter.html](https://www.enterpriseintegrationpatterns.com/patterns/messaging/ContentBasedRouter.html)
44

5-
Learn more about this pattern at Serverless Land Patterns: << Add the live URL here [https://serverlessland.com/patterns/sns-sqs-message-content-router-cdk](https://serverlessland.com/patterns/sns-sqs-message-content-router-cdk)
5+
Learn more about this pattern at Serverless Land Patterns: [https://serverlessland.com/patterns/sns-sqs-message-content-router-cdk](https://serverlessland.com/patterns/sns-sqs-message-content-router-cdk)
66

77
Important: this application uses various AWS services and there are costs associated with these services after the Free Tier usage - please see the [AWS Pricing page](https://aws.amazon.com/pricing/) for details. You are responsible for any AWS costs incurred. No warranty is implied in this example.
88

@@ -31,20 +31,47 @@ Important: this application uses various AWS services and there are costs associ
3131
3232
## How it works
3333
34-
The CDK Stack pick-up a configuration from `stackconfiguration.js` stored inside the `src` folder. This files contains a JSON structure that describe the routing logic. The stack will also create a DeadQueue letter for each Queue and a Default Queue for messages that do not match any
34+
The CDK Stack pick-up a configuration from `stackconfiguration.json` stored inside the `src` folder. This files contains a JSON structure that describe the routing logic. The stack will also create a DeadQueue letter for each Queue.
35+
36+
The parameter `IsAttribute` specifies to CDK if the filter is of type attribute or content.
3537
3638
```json
3739
{
3840
"Sns": {
39-
"Name": "The name of the SNS Topic"
41+
"TopicName": "content-router"
4042
},
4143
"Sqs": [
4244
{
43-
"Name": "The name of the SQS queue",
44-
"ContentFilter": "True - the filter will act on the message content; False - the filter will act on the message attribute",
45-
"Filter: {
46-
"Name": "The property name of the filter",
47-
"Values": ["value1", "value2"]
45+
"QueueName": "country-usa",
46+
"IsAttribute": true,
47+
"Filter": {
48+
"FilterName": "country",
49+
"FilterValues": [
50+
"united states of america",
51+
"usa"
52+
]
53+
}
54+
},
55+
{
56+
"QueueName": "country-germany",
57+
"IsAttribute": true,
58+
"Filter": {
59+
"FilterName": "country",
60+
"FilterValues": [
61+
"germany",
62+
"de"
63+
]
64+
}
65+
},
66+
{
67+
"QueueName": "language-english",
68+
"IsAttribute": false,
69+
"Filter": {
70+
"FilterName": "language",
71+
"FilterValues": [
72+
"english",
73+
"en"
74+
]
4875
}
4976
}
5077
]
@@ -65,7 +92,7 @@ Message Body:
6592
"Message":"Test Message to be received by Queue3 only"
6693
}
6794
Message Attribute:
68-
Type - String; Name - Filter1Name; Value - Filter1Value1
95+
Type - String; Name - country; Value - usa
6996
```
7097

7198
**Scenario-2 Content based**
@@ -77,7 +104,9 @@ Message body based filter. Copy Json to the message body and add attibutes given
77104
Message Body:
78105
{
79106
"Message":"Test Message to be received by Queue2 only",
80-
"Filter1Name":"Filter1Value1"
107+
"Filter":{
108+
"Language": ["en"]
109+
}
81110
}
82111
```
83112

sns-sqs-message-content-router-cdk/example-pattern.json

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,16 @@
99
"text": [
1010
"This CDK project demonstrates how to create a Message Content-based routing pattern using Amazon SNS and Amazon SQS.",
1111
"The number of Queues and the Filters are dynamically defined inside the stackconfiguration.json file. The solution can be adapted for any specific requirement.",
12-
"This architecture is an implementation of the Integration pattern: Content-Based Message Router explained in the Integration Patterns book."
12+
"This architecture is an implementation of the Integration pattern: Content-Based Message Router explained in the Integration Patterns book.",
13+
"The content can be filtered by attributes or by message body which grants full flexibility."
1314
]
1415
},
1516
"gitHub": {
1617
"template": {
1718
"repoURL": "https://github.com/aws-samples/serverless-patterns/tree/main/sns-sqs-message-content-router-cdk",
1819
"templateURL": "serverless-patterns/sns-sqs-message-content-router-cdk",
1920
"projectFolder": "sns-sqs-message-content-router-cdk",
20-
"templateFile": "sns-sqs-message-content-router-cdk/src/Cdk/CdkStack.ts"
21+
"templateFile": "sns-sqs-message-content-router-cdk/src/lib/src-stack.ts"
2122
}
2223
},
2324
"resources": {
@@ -48,8 +49,8 @@
4849
"name": "Raffaele Garofalo (Raf)",
4950
"image": "https://avatars.githubusercontent.com/raffaeu",
5051
"bio": "Raffaele Garofalo (Raf) is a Senior Solutions Architect at AWS and member of the AWS Serverless TFC (Technical Field Communities).",
51-
"linkedin": "linked-in-ID",
52-
"twitter": "twitter-handle"
52+
"linkedin": "raffaeu",
53+
"twitter": "raffaeu"
5354
}
5455
]
5556
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
*.js
2+
!jest.config.js
3+
*.d.ts
4+
node_modules
5+
6+
# CDK asset staging directory
7+
.cdk.staging
8+
cdk.out
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
*.ts
2+
!*.d.ts
3+
4+
# CDK asset staging directory
5+
.cdk.staging
6+
cdk.out
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
#!/usr/bin/env node
2+
import 'source-map-support/register';
3+
import * as cdk from 'aws-cdk-lib';
4+
import { SrcStack } from '../lib/src-stack';
5+
6+
const app = new cdk.App();
7+
new SrcStack(app, 'cdk-stack-message-router', {
8+
9+
});
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
{
2+
"app": "npx ts-node --prefer-ts-exts bin/src.ts",
3+
"watch": {
4+
"include": [
5+
"**"
6+
],
7+
"exclude": [
8+
"README.md",
9+
"cdk*.json",
10+
"**/*.d.ts",
11+
"**/*.js",
12+
"tsconfig.json",
13+
"package*.json",
14+
"yarn.lock",
15+
"node_modules",
16+
"test"
17+
]
18+
},
19+
"context": {
20+
"@aws-cdk/aws-lambda:recognizeLayerVersion": true,
21+
"@aws-cdk/core:checkSecretUsage": true,
22+
"@aws-cdk/core:target-partitions": [
23+
"aws",
24+
"aws-cn"
25+
],
26+
"@aws-cdk-containers/ecs-service-extensions:enableDefaultLogDriver": true,
27+
"@aws-cdk/aws-ec2:uniqueImdsv2TemplateName": true,
28+
"@aws-cdk/aws-ecs:arnFormatIncludesClusterName": true,
29+
"@aws-cdk/aws-iam:minimizePolicies": true,
30+
"@aws-cdk/core:validateSnapshotRemovalPolicy": true,
31+
"@aws-cdk/aws-codepipeline:crossAccountKeyAliasStackSafeResourceName": true,
32+
"@aws-cdk/aws-s3:createDefaultLoggingPolicy": true,
33+
"@aws-cdk/aws-sns-subscriptions:restrictSqsDescryption": true,
34+
"@aws-cdk/aws-apigateway:disableCloudWatchRole": true,
35+
"@aws-cdk/core:enablePartitionLiterals": true,
36+
"@aws-cdk/aws-events:eventsTargetQueueSameAccount": true,
37+
"@aws-cdk/aws-iam:standardizedServicePrincipals": true,
38+
"@aws-cdk/aws-ecs:disableExplicitDeploymentControllerForCircuitBreaker": true,
39+
"@aws-cdk/aws-iam:importedRoleStackSafeDefaultPolicyName": true,
40+
"@aws-cdk/aws-s3:serverAccessLogsUseBucketPolicy": true,
41+
"@aws-cdk/aws-route53-patters:useCertificate": true,
42+
"@aws-cdk/customresources:installLatestAwsSdkDefault": false
43+
}
44+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
module.exports = {
2+
testEnvironment: 'node',
3+
roots: ['<rootDir>/test'],
4+
testMatch: ['**/*.test.ts'],
5+
transform: {
6+
'^.+\\.tsx?$': 'ts-jest'
7+
}
8+
};
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
import * as cdk from 'aws-cdk-lib';
2+
import { Construct } from 'constructs';
3+
import * as configuration from '../stackconfiguration.json';
4+
import { AllowListReceiptFilter } from 'aws-cdk-lib/aws-ses';
5+
6+
export class SrcStack extends cdk.Stack {
7+
constructor(scope: Construct, id: string, props?: cdk.StackProps) {
8+
super(scope, id, props);
9+
10+
// parameters
11+
const account = cdk.Stack.of(this).account;
12+
const stackName = cdk.Stack.of(this).stackName;
13+
const snsName = `${account}-${stackName}-topic-${configuration.Sns.TopicName}`;
14+
15+
// sns topic
16+
const sns = new cdk.aws_sns.Topic(this, 'cdk-sns-topic', {
17+
displayName: snsName,
18+
topicName: snsName
19+
});
20+
21+
// create all required sqs
22+
for (let q = 0; q < configuration.Sqs.length; q++) {
23+
const queueConfig = configuration.Sqs[q];
24+
// dead queue
25+
const sqsDead = new cdk.aws_sqs.Queue(this, `cdk-sqs-dead-${q + 1}`, {
26+
queueName: `${account}-${stackName}-dead-${queueConfig.QueueName}`,
27+
removalPolicy: cdk.RemovalPolicy.DESTROY
28+
});
29+
// queue
30+
const sqs = new cdk.aws_sqs.Queue(this, `cdk-sqs-queue-${q + 1}`, {
31+
queueName: `${account}-${stackName}-queue-${queueConfig.QueueName}`,
32+
removalPolicy: cdk.RemovalPolicy.DESTROY,
33+
deadLetterQueue: {
34+
maxReceiveCount: 3,
35+
queue: sqsDead
36+
}
37+
});
38+
if (queueConfig.IsAttribute) {
39+
// subscription attribute
40+
sns.addSubscription(new cdk.aws_sns_subscriptions.SqsSubscription(sqs, {
41+
filterPolicy: {
42+
[queueConfig.Filter.FilterName]: cdk.aws_sns.SubscriptionFilter.stringFilter({
43+
allowlist: queueConfig.Filter.FilterValues
44+
})
45+
},
46+
rawMessageDelivery: true
47+
}));
48+
} else {
49+
// subscription content
50+
sns.addSubscription(new cdk.aws_sns_subscriptions.SqsSubscription(sqs, {
51+
filterPolicyWithMessageBody: {
52+
filter: cdk.aws_sns.FilterOrPolicy.policy({
53+
[queueConfig.Filter.FilterName]: cdk.aws_sns.FilterOrPolicy.filter(cdk.aws_sns.SubscriptionFilter.stringFilter({
54+
allowlist: queueConfig.Filter.FilterValues
55+
}))
56+
})
57+
58+
},
59+
rawMessageDelivery: true
60+
}));
61+
}
62+
}
63+
}
64+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
{
2+
"name": "src",
3+
"version": "0.1.0",
4+
"bin": {
5+
"src": "bin/src.js"
6+
},
7+
"scripts": {
8+
"build": "tsc",
9+
"watch": "tsc -w",
10+
"test": "jest",
11+
"cdk": "cdk"
12+
},
13+
"devDependencies": {
14+
"@types/jest": "^29.2.5",
15+
"@types/node": "18.11.18",
16+
"aws-cdk": "2.61.0",
17+
"jest": "^29.3.1",
18+
"ts-jest": "^29.0.3",
19+
"ts-node": "^10.9.1",
20+
"typescript": "~4.9.4"
21+
},
22+
"dependencies": {
23+
"aws-cdk-lib": "^2.84.0",
24+
"constructs": "^10.0.0",
25+
"source-map-support": "^0.5.21"
26+
}
27+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
{
2+
"Sns": {
3+
"TopicName": "content-router"
4+
},
5+
"Sqs": [
6+
{
7+
"QueueName": "country-usa",
8+
"IsAttribute": true,
9+
"Filter": {
10+
"FilterName": "country",
11+
"FilterValues": [
12+
"united states of america",
13+
"usa"
14+
]
15+
}
16+
},
17+
{
18+
"QueueName": "country-germany",
19+
"IsAttribute": true,
20+
"Filter": {
21+
"FilterName": "country",
22+
"FilterValues": [
23+
"germany",
24+
"de"
25+
]
26+
}
27+
},
28+
{
29+
"QueueName": "language-english",
30+
"IsAttribute": false,
31+
"Filter": {
32+
"FilterName": "language",
33+
"FilterValues": [
34+
"english",
35+
"en"
36+
]
37+
}
38+
}
39+
]
40+
}

0 commit comments

Comments
 (0)