Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(sns): contentBasedDeduplication is always false for imported topic #29542

Merged
merged 12 commits into from
Apr 6, 2024

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,59 @@
"SignatureVersion": "2",
"TopicName": "fooTopicSignatureVersion"
}
},
"MyTopic288CE2107": {
"Type": "AWS::SNS::Topic",
"Properties": {
"DisplayName": "fooDisplayName2",
"KmsMasterKeyId": {
"Fn::GetAtt": [
"CustomKey1E6D0D07",
"Arn"
]
},
"TopicName": "fooTopic2"
}
},
"PublishRoleF42F66B6": {
"Type": "AWS::IAM::Role",
"Properties": {
"AssumeRolePolicyDocument": {
"Statement": [
{
"Action": "sts:AssumeRole",
"Effect": "Allow",
"Principal": {
"Service": "s3.amazonaws.com"
}
}
],
"Version": "2012-10-17"
}
}
},
"PublishRoleDefaultPolicy9257B12D": {
"Type": "AWS::IAM::Policy",
"Properties": {
"PolicyDocument": {
"Statement": [
{
"Action": "sns:Publish",
"Effect": "Allow",
"Resource": {
"Ref": "MyTopic288CE2107"
}
}
],
"Version": "2012-10-17"
},
"PolicyName": "PublishRoleDefaultPolicy9257B12D",
"Roles": [
{
"Ref": "PublishRoleF42F66B6"
}
]
}
}
},
"Parameters": {
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -44,11 +44,25 @@ class SNSInteg extends Stack {
successFeedbackSampleRate: 50,
});

// Topic with signatureVersion
new Topic(this, 'MyTopicSignatureVersion', {
topicName: 'fooTopicSignatureVersion',
displayName: 'fooDisplayNameSignatureVersion',
signatureVersion: '2',
});

// Can import topic
const topic2 = new Topic(this, 'MyTopic2', {
topicName: 'fooTopic2',
displayName: 'fooDisplayName2',
masterKey: key,
});
const importedTopic = Topic.fromTopicArn(this, 'ImportedTopic', topic2.topicArn);

const publishRole = new Role(this, 'PublishRole', {
assumedBy: new ServicePrincipal('s3.amazonaws.com'),
});
importedTopic.grantPublish(publishRole);
}
}

Expand Down
46 changes: 41 additions & 5 deletions packages/aws-cdk-lib/aws-sns/lib/topic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,24 @@ export enum LoggingProtocol {
APPLICATION = 'application',
}

/**
* Represents an SNS topic defined outside of this stack.
*/
export interface TopicAttributes {
/**
* The ARN of the SNS topic.
*/
readonly topicArn: string;

/**
* Whether content-based deduplication is enabled.
* Only applicable for FIFO topics.
*
* @default false
*/
readonly contentBasedDeduplication?: boolean;
}

/**
* A new SNS topic
*/
Expand All @@ -166,16 +184,34 @@ export class Topic extends TopicBase {
* @param topicArn topic ARN (i.e. arn:aws:sns:us-east-2:444455556666:MyTopic)
*/
public static fromTopicArn(scope: Construct, id: string, topicArn: string): ITopic {
return Topic.fromTopicAttributes(scope, id, { topicArn });
};

/**
* Import an existing SNS topic provided a topic attributes
*
* @param scope The parent creating construct
* @param id The construct's name
* @param attrs the attributes of the topic to import
*/
public static fromTopicAttributes(scope: Construct, id: string, attrs: TopicAttributes): ITopic {
msambol marked this conversation as resolved.
Show resolved Hide resolved
const topicName = Stack.of(scope).splitArn(attrs.topicArn, ArnFormat.NO_RESOURCE_NAME).resource;
const fifo = topicName.endsWith('.fifo');

if (attrs.contentBasedDeduplication && !fifo) {
throw new Error('Cannot import topic; contentBasedDeduplication is only available for FIFO SNS topics.');
}

class Import extends TopicBase {
public readonly topicArn = topicArn;
public readonly topicName = Stack.of(scope).splitArn(topicArn, ArnFormat.NO_RESOURCE_NAME).resource;
public readonly fifo = this.topicName.endsWith('.fifo');
public readonly contentBasedDeduplication = false;
public readonly topicArn = attrs.topicArn;
public readonly topicName = topicName;
public readonly fifo = fifo;
public readonly contentBasedDeduplication = attrs.contentBasedDeduplication || false;
protected autoCreatePolicy: boolean = false;
}

return new Import(scope, id, {
environmentFromArn: topicArn,
environmentFromArn: attrs.topicArn,
});
}

Expand Down
42 changes: 42 additions & 0 deletions packages/aws-cdk-lib/aws-sns/test/sns.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -455,6 +455,48 @@ describe('Topic', () => {
expect(imported.fifo).toEqual(true);
});

test('fromTopicAttributes contentBasedDeduplication false', () => {
// GIVEN
const stack = new cdk.Stack();

// WHEN
const imported = sns.Topic.fromTopicAttributes(stack, 'Imported', {
topicArn: 'arn:aws:sns:*:123456789012:mytopic',
});

// THEN
expect(imported.topicName).toEqual('mytopic');
expect(imported.topicArn).toEqual('arn:aws:sns:*:123456789012:mytopic');
expect(imported.contentBasedDeduplication).toEqual(false);
});

test('fromTopicAttributes contentBasedDeduplication true', () => {
// GIVEN
const stack = new cdk.Stack();

// WHEN
const imported = sns.Topic.fromTopicAttributes(stack, 'Imported', {
topicArn: 'arn:aws:sns:*:123456789012:mytopic.fifo',
contentBasedDeduplication: true,
});

// THEN
expect(imported.topicName).toEqual('mytopic.fifo');
expect(imported.topicArn).toEqual('arn:aws:sns:*:123456789012:mytopic.fifo');
expect(imported.contentBasedDeduplication).toEqual(true);
});

test('fromTopicAttributes throws with contentBasedDeduplication on non-fifo topic', () => {
// GIVEN
const stack = new cdk.Stack();

// WHEN
expect(() => sns.Topic.fromTopicAttributes(stack, 'Imported', {
topicArn: 'arn:aws:sns:*:123456789012:mytopic',
contentBasedDeduplication: true,
})).toThrow(/Cannot import topic; contentBasedDeduplication is only available for FIFO SNS topics./);
});

test('sets account for imported topic env', () => {
// GIVEN
const stack = new cdk.Stack();
Expand Down