diff --git a/packages/@aws-cdk/aws-sqs/lib/queue-base.ts b/packages/@aws-cdk/aws-sqs/lib/queue-base.ts index 162c149aec2ad..23cf09218cc58 100644 --- a/packages/@aws-cdk/aws-sqs/lib/queue-base.ts +++ b/packages/@aws-cdk/aws-sqs/lib/queue-base.ts @@ -275,4 +275,13 @@ export interface QueueAttributes { * @default - None */ readonly keyArn?: string; + + /** + * Whether this queue is an Amazon SQS FIFO queue. If false, this is a standard queue. + * + * In case of a FIFO queue which is imported from a token, this value has to be explicitly set to true. + * + * @default - if fifo is not specified, the property will be determined based on the queue name (not possible for FIFO queues imported from a token) + */ + readonly fifo?: boolean; } diff --git a/packages/@aws-cdk/aws-sqs/lib/queue.ts b/packages/@aws-cdk/aws-sqs/lib/queue.ts index 414ddaaf8a919..db2992630aa63 100644 --- a/packages/@aws-cdk/aws-sqs/lib/queue.ts +++ b/packages/@aws-cdk/aws-sqs/lib/queue.ts @@ -1,5 +1,5 @@ import * as kms from '@aws-cdk/aws-kms'; -import { Duration, RemovalPolicy, Stack, Token } from '@aws-cdk/core'; +import { Duration, RemovalPolicy, Stack, Token, ArnFormat } from '@aws-cdk/core'; import { Construct } from 'constructs'; import { IQueue, QueueAttributes, QueueBase } from './queue-base'; import { CfnQueue } from './sqs.generated'; @@ -257,7 +257,7 @@ export class Queue extends QueueBase { */ public static fromQueueAttributes(scope: Construct, id: string, attrs: QueueAttributes): IQueue { const stack = Stack.of(scope); - const parsedArn = stack.parseArn(attrs.queueArn); + const parsedArn = stack.splitArn(attrs.queueArn, ArnFormat.NO_RESOURCE_NAME); const queueName = attrs.queueName || parsedArn.resource; const queueUrl = attrs.queueUrl || `https://sqs.${parsedArn.region}.${stack.urlSuffix}/${parsedArn.account}/${queueName}`; @@ -268,9 +268,28 @@ export class Queue extends QueueBase { public readonly encryptionMasterKey = attrs.keyArn ? kms.Key.fromKeyArn(this, 'Key', attrs.keyArn) : undefined; - public readonly fifo = queueName.endsWith('.fifo') ? true : false; + public readonly fifo: boolean = this.determineFifo();; protected readonly autoCreatePolicy = false; + + /** + * Determine fifo flag based on queueName and fifo attribute + */ + private determineFifo(): boolean { + if (Token.isUnresolved(this.queueArn)) { + return attrs.fifo || false; + } else { + if (typeof attrs.fifo !== 'undefined') { + if (attrs.fifo && !queueName.endsWith('.fifo')) { + throw new Error("FIFO queue names must end in '.fifo'"); + } + if (!attrs.fifo && queueName.endsWith('.fifo')) { + throw new Error("Non-FIFO queue name may not end in '.fifo'"); + } + } + return queueName.endsWith('.fifo') ? true : false; + } + } } return new Import(scope, id); diff --git a/packages/@aws-cdk/aws-sqs/test/test.sqs.ts b/packages/@aws-cdk/aws-sqs/test/test.sqs.ts index da8637ba5d4f5..ae334e5fcd4d3 100644 --- a/packages/@aws-cdk/aws-sqs/test/test.sqs.ts +++ b/packages/@aws-cdk/aws-sqs/test/test.sqs.ts @@ -1,7 +1,7 @@ import { expect, haveResource, ResourcePart } from '@aws-cdk/assert-internal'; import * as iam from '@aws-cdk/aws-iam'; import * as kms from '@aws-cdk/aws-kms'; -import { CfnParameter, Duration, Stack, App } from '@aws-cdk/core'; +import { CfnParameter, Duration, Stack, App, Token } from '@aws-cdk/core'; import { Test } from 'nodeunit'; import * as sqs from '../lib'; @@ -191,6 +191,87 @@ export = { test.done(); }, + 'import queueArn from token, fifo and standard queues can be defined'(test: Test) { + // GIVEN + const stack = new Stack(); + + // WHEN + const stdQueue1 = sqs.Queue.fromQueueAttributes(stack, 'StdQueue1', { + queueArn: Token.asString({ Ref: 'ARN' }), + }); + const stdQueue2 = sqs.Queue.fromQueueAttributes(stack, 'StdQueue2', { + queueArn: Token.asString({ Ref: 'ARN' }), + fifo: false, + }); + const fifoQueue = sqs.Queue.fromQueueAttributes(stack, 'FifoQueue', { + queueArn: Token.asString({ Ref: 'ARN' }), + fifo: true, + }); + + // THEN + test.deepEqual(stdQueue1.fifo, false); + test.deepEqual(stdQueue2.fifo, false); + test.deepEqual(fifoQueue.fifo, true); + test.done(); + }, + + 'import queueArn from token, check attributes'(test: Test) { + // GIVEN + const stack = new Stack(); + + // WHEN + const stdQueue1 = sqs.Queue.fromQueueArn(stack, 'StdQueue', Token.asString({ Ref: 'ARN' })); + + // THEN + test.deepEqual(stack.resolve(stdQueue1.queueArn), { + Ref: 'ARN', + }); + test.deepEqual(stack.resolve(stdQueue1.queueName), { + 'Fn::Select': [5, { 'Fn::Split': [':', { Ref: 'ARN' }] }], + }); + test.deepEqual(stack.resolve(stdQueue1.queueUrl), { + 'Fn::Join': + ['', + ['https://sqs.', + { 'Fn::Select': [3, { 'Fn::Split': [':', { Ref: 'ARN' }] }] }, + '.', + { Ref: 'AWS::URLSuffix' }, + '/', + { 'Fn::Select': [4, { 'Fn::Split': [':', { Ref: 'ARN' }] }] }, + '/', + { 'Fn::Select': [5, { 'Fn::Split': [':', { Ref: 'ARN' }] }] }]], + }); + test.deepEqual(stdQueue1.fifo, false); + test.done(); + }, + + 'fails if fifo flag is set for non fifo queue name'(test: Test) { + // GIVEN + const app = new App(); + const stack = new Stack(app, 'my-stack'); + + // THEN + test.throws(() => sqs.Queue.fromQueueAttributes(stack, 'ImportedStdQueue', { + queueArn: 'arn:aws:sqs:us-west-2:123456789012:queue1', + fifo: true, + }), /FIFO queue names must end in '.fifo'/); + test.done(); + }, + + + 'fails if fifo flag is false for fifo queue name'(test: Test) { + // GIVEN + const app = new App(); + const stack = new Stack(app, 'my-stack'); + + // THEN + test.throws(() => sqs.Queue.fromQueueAttributes(stack, 'ImportedFifoQueue', { + queueArn: 'arn:aws:sqs:us-west-2:123456789012:queue1.fifo', + fifo: false, + }), /Non-FIFO queue name may not end in '.fifo'/); + test.done(); + }, + 'importing works correctly for cross region queue'(test: Test) { // GIVEN const stack = new Stack(undefined, 'Stack', { env: { region: 'us-east-1' } });