@@ -27,16 +27,21 @@ describe('#compileNotifications', () => {
2727 serverlessStepFunctions = new ServerlessStepFunctions ( serverless , options ) ;
2828 } ) ;
2929
30- const validateCloudWatchEvent = ( event , status ) => {
30+ const validateCloudWatchEvent = ( resources , logicalId , status ) => {
31+ expect ( resources ) . to . haveOwnProperty ( logicalId ) ;
32+ const event = resources [ logicalId ] ;
3133 expect ( event . Type ) . to . equal ( 'AWS::Events::Rule' ) ;
3234 expect ( event . Properties . EventPattern . source ) . to . deep . equal ( [ 'aws.states' ] ) ;
3335 expect ( event . Properties . EventPattern . detail . status ) . to . deep . equal ( [ status ] ) ;
3436 expect ( event . Properties . Targets ) . to . have . lengthOf ( 8 ) ;
3537
3638 for ( const target of event . Properties . Targets ) {
37- expect ( _ . isString ( target . Arn ) ) . to . equal ( true ) ;
38- expect ( typeof target . Arn ) . to . equal ( 'string' ) ;
39- expect ( _ . isString ( target . Id ) ) . to . equal ( true ) ;
39+ const isStringOrFn =
40+ _ . isString ( target . Arn ) ||
41+ ( target . Arn . Ref && _ . isString ( target . Arn . Ref ) ) ||
42+ ( target . Arn [ 'Fn::GetAtt' ] && _ . isArray ( target . Arn [ 'Fn::GetAtt' ] ) ) ;
43+
44+ expect ( isStringOrFn ) . to . equal ( true ) ;
4045 }
4146
4247 const sqsWithParam = event . Properties . Targets . find ( t => t . SqsParameters ) ;
@@ -47,33 +52,22 @@ describe('#compileNotifications', () => {
4752
4853 const validateHasPermission = ( iamRole , action , resource ) => {
4954 const statements = iamRole . Properties . Policies [ 0 ] . PolicyDocument . Statement ;
50- expect ( statements . find ( x => x . Action === action && x . Resource === resource ) )
55+ expect ( statements . find ( x => x . Action === action && _ . isEqual ( x . Resource , resource ) ) )
5156 . to . not . equal ( undefined ) ;
5257 } ;
5358
54- const validateCloudWatchIamRole = ( iamRole ) => {
55- // 8 targets, 5 event rules = 5 * 8 = 40 statements
56- expect ( iamRole . Properties . Policies [ 0 ] . PolicyDocument . Statement ) . to . have . lengthOf ( 40 ) ;
57- validateHasPermission ( iamRole , 'sns:Publish' , 'SNS_TOPIC_ARN' ) ;
58- validateHasPermission ( iamRole , 'sqs:SendMessage' , 'SQS_QUEUE_ARN' ) ;
59- validateHasPermission ( iamRole , 'kinesis:PutRecord' , 'KINESIS_STREAM_ARN' ) ;
60- validateHasPermission ( iamRole , 'firehose:PutRecord' , 'FIREHOSE_STREAM_ARN' ) ;
61- validateHasPermission ( iamRole , 'lambda:InvokeFunction' , 'LAMBDA_FUNCTION_ARN' ) ;
62- validateHasPermission ( iamRole , 'states:StartExecution' , 'STATE_MACHINE_ARN' ) ;
63- } ;
59+ it ( 'should generate CloudWatch Event Rules with strig ARNs' , ( ) => {
60+ const targets = [
61+ { sns : 'SNS_TOPIC_ARN' } ,
62+ { sqs : 'SQS_QUEUE_ARN' } ,
63+ { sqs : { arn : 'SQS_QUEUE_ARN' , messageGroupId : '12345' } } ,
64+ { lambda : 'LAMBDA_FUNCTION_ARN' } ,
65+ { kinesis : 'KINESIS_STREAM_ARN' } ,
66+ { kinesis : { arn : 'KINESIS_STREAM_ARN' , partitionKeyPath : '$.id' } } ,
67+ { firehose : 'FIREHOSE_STREAM_ARN' } ,
68+ { stepFunctions : 'STATE_MACHINE_ARN' } ,
69+ ] ;
6470
65- const targets = [
66- { sns : 'SNS_TOPIC_ARN' } ,
67- { sqs : 'SQS_QUEUE_ARN' } ,
68- { sqs : { arn : 'SQS_QUEUE_ARN' , messageGroupId : '12345' } } ,
69- { lambda : 'LAMBDA_FUNCTION_ARN' } ,
70- { kinesis : 'KINESIS_STREAM_ARN' } ,
71- { kinesis : { arn : 'KINESIS_STREAM_ARN' , partitionKeyPath : '$.id' } } ,
72- { firehose : 'FIREHOSE_STREAM_ARN' } ,
73- { stepFunctions : 'STATE_MACHINE_ARN' } ,
74- ] ;
75-
76- it ( 'should generate CloudWatch Event Rules' , ( ) => {
7771 const genStateMachine = ( name ) => ( {
7872 id : name ,
7973 name,
@@ -105,18 +99,109 @@ describe('#compileNotifications', () => {
10599 serverlessStepFunctions . compileNotifications ( ) ;
106100 const resources = serverlessStepFunctions . serverless . service
107101 . provider . compiledCloudFormationTemplate . Resources ;
108- validateCloudWatchEvent ( resources . Beta1NotificationsABORTEDEventRule , 'ABORTED' ) ;
109- validateCloudWatchEvent ( resources . Beta1NotificationsFAILEDEventRule , 'FAILED' ) ;
110- validateCloudWatchEvent ( resources . Beta1NotificationsRUNNINGEventRule , 'RUNNING' ) ;
111- validateCloudWatchEvent ( resources . Beta1NotificationsSUCCEEDEDEventRule , 'SUCCEEDED' ) ;
112- validateCloudWatchEvent ( resources . Beta1NotificationsTIMEDOUTEventRule , 'TIMED_OUT' ) ;
113- validateCloudWatchIamRole ( resources . Beta1NotificationsIamRole ) ;
114- validateCloudWatchEvent ( resources . Beta2NotificationsABORTEDEventRule , 'ABORTED' ) ;
115- validateCloudWatchEvent ( resources . Beta2NotificationsFAILEDEventRule , 'FAILED' ) ;
116- validateCloudWatchEvent ( resources . Beta2NotificationsRUNNINGEventRule , 'RUNNING' ) ;
117- validateCloudWatchEvent ( resources . Beta2NotificationsSUCCEEDEDEventRule , 'SUCCEEDED' ) ;
118- validateCloudWatchEvent ( resources . Beta2NotificationsTIMEDOUTEventRule , 'TIMED_OUT' ) ;
119- validateCloudWatchIamRole ( resources . Beta2NotificationsIamRole ) ;
102+
103+ const validateCloudWatchEvents = ( prefix ) => {
104+ validateCloudWatchEvent ( resources , `${ prefix } NotificationsABORTEDEventRule` , 'ABORTED' ) ;
105+ validateCloudWatchEvent ( resources , `${ prefix } NotificationsFAILEDEventRule` , 'FAILED' ) ;
106+ validateCloudWatchEvent ( resources , `${ prefix } NotificationsRUNNINGEventRule` , 'RUNNING' ) ;
107+ validateCloudWatchEvent ( resources , `${ prefix } NotificationsSUCCEEDEDEventRule` , 'SUCCEEDED' ) ;
108+ validateCloudWatchEvent ( resources , `${ prefix } NotificationsTIMEDOUTEventRule` , 'TIMED_OUT' ) ;
109+ } ;
110+
111+ validateCloudWatchEvents ( 'Beta1' ) ;
112+ validateCloudWatchEvents ( 'Beta2' ) ;
113+
114+ const validateIamRole = ( iamRole ) => {
115+ // 8 targets, 5 event rules = 5 * 8 = 40 statements
116+ expect ( iamRole . Properties . Policies [ 0 ] . PolicyDocument . Statement ) . to . have . lengthOf ( 40 ) ;
117+ validateHasPermission ( iamRole , 'sns:Publish' , 'SNS_TOPIC_ARN' ) ;
118+ validateHasPermission ( iamRole , 'sqs:SendMessage' , 'SQS_QUEUE_ARN' ) ;
119+ validateHasPermission ( iamRole , 'kinesis:PutRecord' , 'KINESIS_STREAM_ARN' ) ;
120+ validateHasPermission ( iamRole , 'firehose:PutRecord' , 'FIREHOSE_STREAM_ARN' ) ;
121+ validateHasPermission ( iamRole , 'lambda:InvokeFunction' , 'LAMBDA_FUNCTION_ARN' ) ;
122+ validateHasPermission ( iamRole , 'states:StartExecution' , 'STATE_MACHINE_ARN' ) ;
123+ } ;
124+
125+ validateIamRole ( resources . Beta1NotificationsIamRole ) ;
126+ validateIamRole ( resources . Beta2NotificationsIamRole ) ;
127+
128+ expect ( consoleLogSpy . callCount ) . equal ( 0 ) ;
129+ } ) ;
130+
131+ it ( 'should generate CloudWatch Event Rules with Ref ang Fn::GetAtt' , ( ) => {
132+ const snsArn = { Ref : 'MyTopic' } ;
133+ const sqsArn = { 'Fn::GetAtt' : [ 'MyQueue' , 'Arn' ] } ;
134+ const lambdaArn = { 'Fn::GetAtt' : [ 'MyFunction' , 'Arn' ] } ;
135+ const kinesisArn = { 'Fn::GetAtt' : [ 'MyStream' , 'Arn' ] } ;
136+ const firehoseArn = { 'Fn::GetAtt' : [ 'MyDeliveryStream' , 'Arn' ] } ;
137+ const stepFunctionsArn = { Ref : 'MyStateMachine' } ;
138+ const targets = [
139+ { sns : snsArn } ,
140+ { sqs : sqsArn } ,
141+ { sqs : { arn : sqsArn , messageGroupId : '12345' } } ,
142+ { lambda : lambdaArn } ,
143+ { kinesis : kinesisArn } ,
144+ { kinesis : { arn : kinesisArn , partitionKeyPath : '$.id' } } ,
145+ { firehose : firehoseArn } ,
146+ { stepFunctions : stepFunctionsArn } ,
147+ ] ;
148+
149+ const genStateMachine = ( name ) => ( {
150+ id : name ,
151+ name,
152+ definition : {
153+ StartAt : 'A' ,
154+ States : {
155+ A : {
156+ Type : 'Pass' ,
157+ End : true ,
158+ } ,
159+ } ,
160+ } ,
161+ notifications : {
162+ ABORTED : targets ,
163+ FAILED : targets ,
164+ RUNNING : targets ,
165+ SUCCEEDED : targets ,
166+ TIMED_OUT : targets ,
167+ } ,
168+ } ) ;
169+
170+ serverless . service . stepFunctions = {
171+ stateMachines : {
172+ beta1 : genStateMachine ( 'Beta1' ) ,
173+ beta2 : genStateMachine ( 'Beta2' ) ,
174+ } ,
175+ } ;
176+
177+ serverlessStepFunctions . compileNotifications ( ) ;
178+ const resources = serverlessStepFunctions . serverless . service
179+ . provider . compiledCloudFormationTemplate . Resources ;
180+
181+ const validateCloudWatchEvents = ( prefix ) => {
182+ validateCloudWatchEvent ( resources , `${ prefix } NotificationsABORTEDEventRule` , 'ABORTED' ) ;
183+ validateCloudWatchEvent ( resources , `${ prefix } NotificationsFAILEDEventRule` , 'FAILED' ) ;
184+ validateCloudWatchEvent ( resources , `${ prefix } NotificationsRUNNINGEventRule` , 'RUNNING' ) ;
185+ validateCloudWatchEvent ( resources , `${ prefix } NotificationsSUCCEEDEDEventRule` , 'SUCCEEDED' ) ;
186+ validateCloudWatchEvent ( resources , `${ prefix } NotificationsTIMEDOUTEventRule` , 'TIMED_OUT' ) ;
187+ } ;
188+
189+ validateCloudWatchEvents ( 'Beta1' ) ;
190+ validateCloudWatchEvents ( 'Beta2' ) ;
191+
192+ const validateIamRole = ( iamRole ) => {
193+ // 8 targets, 5 event rules = 5 * 8 = 40 statements
194+ expect ( iamRole . Properties . Policies [ 0 ] . PolicyDocument . Statement ) . to . have . lengthOf ( 40 ) ;
195+ validateHasPermission ( iamRole , 'sns:Publish' , snsArn ) ;
196+ validateHasPermission ( iamRole , 'sqs:SendMessage' , sqsArn ) ;
197+ validateHasPermission ( iamRole , 'kinesis:PutRecord' , kinesisArn ) ;
198+ validateHasPermission ( iamRole , 'firehose:PutRecord' , firehoseArn ) ;
199+ validateHasPermission ( iamRole , 'lambda:InvokeFunction' , lambdaArn ) ;
200+ validateHasPermission ( iamRole , 'states:StartExecution' , stepFunctionsArn ) ;
201+ } ;
202+
203+ validateIamRole ( resources . Beta1NotificationsIamRole ) ;
204+ validateIamRole ( resources . Beta2NotificationsIamRole ) ;
120205
121206 expect ( consoleLogSpy . callCount ) . equal ( 0 ) ;
122207 } ) ;
@@ -217,6 +302,9 @@ describe('#compileNotifications', () => {
217302 } ) ;
218303
219304 it ( 'should log the validation errors if notifications contains non-existent status' , ( ) => {
305+ const targets = [
306+ { sns : 'SNS_TOPIC_ARN' } ,
307+ ] ;
220308 const genStateMachine = ( name ) => ( {
221309 id : name ,
222310 name,
0 commit comments