@mbc-cqrs-serverless/cli 0.1.73-beta.0 → 0.1.75-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mbc-cqrs-serverless/cli",
3
- "version": "0.1.73-beta.0",
3
+ "version": "0.1.75-beta.0",
4
4
  "description": "a CLI to get started with MBC CQRS serverless framework",
5
5
  "keywords": [
6
6
  "mbc",
@@ -58,5 +58,5 @@
58
58
  "@faker-js/faker": "^8.3.1",
59
59
  "copyfiles": "^2.4.1"
60
60
  },
61
- "gitHead": "3399562c3c03284ea21e96a40cd61fd0a68977cb"
61
+ "gitHead": "a9c91c6ab9ca15188d7b7075989e90048248721c"
62
62
  }
@@ -0,0 +1,105 @@
1
+ import {
2
+ IChainable,
3
+ JsonPath,
4
+ ProcessorConfig,
5
+ ProcessorMode,
6
+ Map as SfnMap,
7
+ } from 'aws-cdk-lib/aws-stepfunctions'
8
+
9
+ export type DistributedMapS3Parameter =
10
+ | {
11
+ readonly 'Bucket.$': string
12
+ readonly 'Key.$': string
13
+ }
14
+ | {
15
+ readonly Bucket: JsonPath | string
16
+ readonly Key: JsonPath | string
17
+ }
18
+
19
+ export interface DistributedMapItemReader {
20
+ readonly Resource:
21
+ | 'arn:aws:states:::s3:getObject'
22
+ | 'arn:aws:states:::s3:listObjectsV2'
23
+ readonly ReaderConfig: {
24
+ readonly InputType: 'CSV' | 'JSON' | 'MANIFEST'
25
+ readonly CSVHeaderLocation?: 'FIRST_ROW' | 'GIVEN'
26
+ readonly CSVHeaders?: string[]
27
+ readonly MaxItems?: number
28
+ }
29
+ readonly Parameters: DistributedMapS3Parameter
30
+ }
31
+
32
+ export interface DistributedMapResultWriter {
33
+ readonly Resource: 'arn:aws:states:::s3:putObject'
34
+ readonly Parameters: DistributedMapS3Parameter
35
+ }
36
+
37
+ export interface DistributedMapItemBatcher {
38
+ readonly MaxItemsPerBatch?: number
39
+ readonly MaxItemsPerBatchPath?: string
40
+ readonly MaxInputBytesPerBatch?: number
41
+ readonly MaxInputBytesPerBatchPath?: number
42
+ readonly BatchInput?: Readonly<Record<string, JsonPath | string>>
43
+ }
44
+
45
+ export class DistributedMap extends SfnMap {
46
+ public itemReader?: DistributedMapItemReader
47
+ public resultWriter?: DistributedMapResultWriter
48
+ public itemBatcher?: DistributedMapItemBatcher
49
+ declare public itemSelector?: Readonly<Record<string, JsonPath | string>>
50
+ public label?: string
51
+
52
+ public override toStateJson(): object {
53
+ const mapStateJson = super.toStateJson()
54
+ return {
55
+ ...mapStateJson,
56
+ ItemReader: this.itemReader,
57
+ ResultWriter: this.resultWriter,
58
+ ItemBatcher: this.itemBatcher,
59
+ ItemSelector: this.itemSelector,
60
+ Label: this.label,
61
+ }
62
+ }
63
+
64
+ public itemProcessor(
65
+ processor: IChainable,
66
+ config: ProcessorConfig = {},
67
+ ): DistributedMap {
68
+ super.itemProcessor(processor, {
69
+ ...config,
70
+ mode: ProcessorMode.DISTRIBUTED,
71
+ })
72
+ return this
73
+ }
74
+
75
+ public setLabel(label: string): DistributedMap {
76
+ this.label = label
77
+ return this
78
+ }
79
+
80
+ public setItemSelector(
81
+ itemSelector: Readonly<Record<string, JsonPath | string>>,
82
+ ): DistributedMap {
83
+ this.itemSelector = itemSelector
84
+ return this
85
+ }
86
+
87
+ public setItemBatcher(
88
+ itemBatcher: DistributedMapItemBatcher,
89
+ ): DistributedMap {
90
+ this.itemBatcher = itemBatcher
91
+ return this
92
+ }
93
+
94
+ public setResultWriter(
95
+ resultWriter: DistributedMapResultWriter,
96
+ ): DistributedMap {
97
+ this.resultWriter = resultWriter
98
+ return this
99
+ }
100
+
101
+ public setItemReader(itemReader: DistributedMapItemReader): DistributedMap {
102
+ this.itemReader = itemReader
103
+ return this
104
+ }
105
+ }
@@ -20,6 +20,7 @@ import {
20
20
  } from '../config'
21
21
  import { Config } from '../config/type'
22
22
  import { buildApp } from './build-app'
23
+ import { DistributedMap } from './distributed-map'
23
24
 
24
25
  export interface InfraStackProps extends cdk.StackProps {
25
26
  config: Config
@@ -115,6 +116,10 @@ export class InfraStack extends cdk.Stack {
115
116
  },
116
117
  })
117
118
 
119
+ const importActionSqs = new cdk.aws_sqs.Queue(this, 'import-action-sqs', {
120
+ queueName: prefix + 'import-action-queue',
121
+ })
122
+
118
123
  const subTaskStatusSqs = new cdk.aws_sqs.Queue(
119
124
  this,
120
125
  'sub-task-status-sqs',
@@ -163,6 +168,17 @@ export class InfraStack extends cdk.Stack {
163
168
  },
164
169
  }),
165
170
  )
171
+
172
+ mainSns.addSubscription(
173
+ new cdk.aws_sns_subscriptions.SqsSubscription(importActionSqs, {
174
+ rawMessageDelivery: true,
175
+ filterPolicy: {
176
+ action: cdk.aws_sns.SubscriptionFilter.stringFilter({
177
+ allowlist: ['import-execute'],
178
+ }),
179
+ },
180
+ }),
181
+ )
166
182
  // host zone
167
183
  const hostedZone = cdk.aws_route53.HostedZone.fromHostedZoneAttributes(
168
184
  this,
@@ -353,6 +369,18 @@ export class InfraStack extends cdk.Stack {
353
369
  arnFormat: cdk.ArnFormat.COLON_RESOURCE_NAME,
354
370
  })
355
371
 
372
+ const importCsvStateMachineName = prefix + 'import-csv-handler'
373
+
374
+ const importCsvSfnArn = cdk.Arn.format({
375
+ partition: 'aws',
376
+ region: this.region,
377
+ account: this.account,
378
+ service: 'states',
379
+ resource: 'stateMachine',
380
+ resourceName: importCsvStateMachineName,
381
+ arnFormat: cdk.ArnFormat.COLON_RESOURCE_NAME,
382
+ })
383
+
356
384
  // Lambda api ( arm64 )
357
385
  const execEnv = {
358
386
  NODE_OPTIONS: '--enable-source-maps',
@@ -365,6 +393,7 @@ export class InfraStack extends cdk.Stack {
365
393
  S3_BUCKET_NAME: ddbBucket.bucketName,
366
394
  SFN_COMMAND_ARN: commandSfnArn,
367
395
  SFN_TASK_ARN: taskSfnArn,
396
+ SFN_IMPORT_CSV_ARN: importCsvSfnArn,
368
397
  SNS_TOPIC_ARN: mainSns.topicArn,
369
398
  SNS_ALARM_TOPIC_ARN: alarmSns.topicArn,
370
399
  COGNITO_USER_POOL_ID: userPool.userPoolId,
@@ -851,6 +880,65 @@ export class InfraStack extends cdk.Stack {
851
880
  },
852
881
  )
853
882
 
883
+ // Csv import
884
+ const csvRowsHandlerState = lambdaInvoke(
885
+ 'csv_rows_handler',
886
+ null,
887
+ cdk.aws_stepfunctions.IntegrationPattern.REQUEST_RESPONSE,
888
+ )
889
+
890
+ const sfnImportCsvDefinition = new DistributedMap(this, 'import-csv', {
891
+ maxConcurrency: 50,
892
+ })
893
+ .setLabel('import-csv')
894
+ .setItemReader({
895
+ Resource: 'arn:aws:states:::s3:getObject',
896
+ ReaderConfig: {
897
+ InputType: 'CSV',
898
+ CSVHeaderLocation: 'FIRST_ROW',
899
+ },
900
+ Parameters: {
901
+ 'Bucket.$': '$.bucket',
902
+ 'Key.$': '$.key',
903
+ },
904
+ })
905
+ .setItemBatcher({
906
+ MaxInputBytesPerBatch: 10,
907
+ BatchInput: {
908
+ 'Attributes.$': '$',
909
+ },
910
+ })
911
+ .itemProcessor(csvRowsHandlerState, {
912
+ executionType: cdk.aws_stepfunctions.ProcessorType.EXPRESS,
913
+ })
914
+
915
+ const sfnImportCsvLogGroup = new cdk.aws_logs.LogGroup(
916
+ this,
917
+ 'import-csv-handler-sfn-log',
918
+ {
919
+ logGroupName: `/aws/vendedlogs/states/${prefix}-import-csv-handler-state-machine-logs`, // Specify a log group name
920
+ removalPolicy: cdk.RemovalPolicy.DESTROY, // Policy for log group removal
921
+ retention: cdk.aws_logs.RetentionDays.SIX_MONTHS,
922
+ },
923
+ )
924
+
925
+ const importCsvStateMachine = new cdk.aws_stepfunctions.StateMachine(
926
+ this,
927
+ 'import-csv-state-machine',
928
+ {
929
+ stateMachineName: importCsvStateMachineName,
930
+ comment: 'A state machine for import-csv module handler',
931
+ definitionBody: cdk.aws_stepfunctions.DefinitionBody.fromChainable(
932
+ sfnImportCsvDefinition,
933
+ ),
934
+ tracingEnabled: true,
935
+ logs: {
936
+ destination: sfnImportCsvLogGroup,
937
+ level: cdk.aws_stepfunctions.LogLevel.ALL, // Log level (ALL, ERROR, or FATAL)
938
+ },
939
+ },
940
+ )
941
+
854
942
  // add event sources to lambda event
855
943
  lambdaApi.addEventSource(
856
944
  new cdk.aws_lambda_event_sources.SqsEventSource(taskSqs, {
@@ -867,8 +955,13 @@ export class InfraStack extends cdk.Stack {
867
955
  batchSize: 1,
868
956
  }),
869
957
  )
958
+ lambdaApi.addEventSource(
959
+ new cdk.aws_lambda_event_sources.SqsEventSource(importActionSqs, {
960
+ batchSize: 1,
961
+ }),
962
+ )
870
963
  // dynamodb event source
871
- const tableNames = ['tasks', 'sample-command']
964
+ const tableNames = ['tasks', 'sample-command', 'import_tmp']
872
965
  for (const tableName of tableNames) {
873
966
  const tableDesc = new cdk.custom_resources.AwsCustomResource(
874
967
  this,
@@ -926,6 +1019,17 @@ export class InfraStack extends cdk.Stack {
926
1019
  'cognito-idp:AdminUpdateUserAttributes',
927
1020
  )
928
1021
  ddbBucket.grantReadWrite(lambdaApi)
1022
+ ddbBucket.grantRead(importCsvStateMachine)
1023
+ importCsvStateMachine.role?.attachInlinePolicy(
1024
+ new cdk.aws_iam.Policy(this, 'csv-import-map-policy', {
1025
+ statements: [
1026
+ new cdk.aws_iam.PolicyStatement({
1027
+ actions: ['states:StartExecution'],
1028
+ resources: [importCsvSfnArn],
1029
+ }),
1030
+ ],
1031
+ }),
1032
+ )
929
1033
  publicBucket.grantReadWrite(lambdaApi)
930
1034
  mainSns.grantPublish(lambdaApi)
931
1035
  alarmSns.grantPublish(lambdaApi)
@@ -973,6 +1077,11 @@ export class InfraStack extends cdk.Stack {
973
1077
  resources: [taskSfnArn], // Access to all resources
974
1078
  })
975
1079
 
1080
+ const importCsvSfnPolicy = new cdk.aws_iam.PolicyStatement({
1081
+ actions: ['states:*'],
1082
+ resources: [importCsvSfnArn], // Access to all resources
1083
+ })
1084
+
976
1085
  // Attach the policy to the Lambda function's execution role
977
1086
  lambdaApi.role?.attachInlinePolicy(
978
1087
  new cdk.aws_iam.Policy(this, 'lambda-event-sfn-policy', {
@@ -991,6 +1100,16 @@ export class InfraStack extends cdk.Stack {
991
1100
  }),
992
1101
  )
993
1102
 
1103
+ lambdaApi.role?.attachInlinePolicy(
1104
+ new cdk.aws_iam.Policy(
1105
+ this,
1106
+ 'lambda-import-csv-sfn-step-function-policy',
1107
+ {
1108
+ statements: [importCsvSfnPolicy.copy()],
1109
+ },
1110
+ ),
1111
+ )
1112
+
994
1113
  // Attach the policy to the Lambda function's execution role
995
1114
  lambdaApi.role?.attachInlinePolicy(
996
1115
  new cdk.aws_iam.Policy(this, 'lambda-ses-policy', {
@@ -25,6 +25,11 @@ queues {
25
25
  delay = 5 seconds
26
26
  receiveMessageWait = 0 seconds
27
27
  }
28
+ import-action-queue {
29
+ defaultVisibilityTimeout = 60 seconds
30
+ delay = 5 seconds
31
+ receiveMessageWait = 0 seconds
32
+ }
28
33
  }
29
34
 
30
35
  aws {
@@ -45,6 +45,11 @@ custom:
45
45
  rawMessageDelivery: 'true'
46
46
  filterPolicy: { 'action': ['sub-task-status'] }
47
47
  queue: http://localhost:9324/101010101010/sub-task-status-queue
48
+ - topic:
49
+ topicName: CqrsSnsTopic
50
+ rawMessageDelivery: 'true'
51
+ filterPolicy: { 'action': ['import-execute'] }
52
+ queue: http://localhost:9324/101010101010/import-action-queue
48
53
  - topic:
49
54
  topicName: AlarmSnsTopic
50
55
  rawMessageDelivery: 'true'
@@ -157,6 +162,11 @@ functions:
157
162
  Fn::GetAtt:
158
163
  - NotificationQueue
159
164
  - Arn
165
+ - sqs:
166
+ arn:
167
+ Fn::GetAtt:
168
+ - ImportActionQueue
169
+ - Arn
160
170
  - stream:
161
171
  type: dynamodb
162
172
  maximumRetryAttempts: 10
@@ -169,6 +179,13 @@ functions:
169
179
  arn: ${env:LOCAL_DDB_TASKS_STREAM}
170
180
  filterPatterns:
171
181
  - eventName: [INSERT]
182
+ - stream:
183
+ type: dynamodb
184
+ maximumRetryAttempts: 10
185
+ arn: ${env:LOCAL_DDB_IMPORT_TMP_STREAM}
186
+ filterPatterns:
187
+ - eventName:
188
+ - INSERT
172
189
 
173
190
  stepFunctions:
174
191
  # https://goessner.net/articles/JsonPath/index.html
@@ -366,6 +383,48 @@ stepFunctions:
366
383
  End: true
367
384
  End: true
368
385
  # MaxConcurrency: 2
386
+ import-csv:
387
+ name: import-csv
388
+ definition:
389
+ Comment: import csv data
390
+ StartAt: csv_loader
391
+ States:
392
+ csv_loader:
393
+ Type: Task
394
+ Resource: arn:aws:states:::lambda:invoke
395
+ Parameters:
396
+ FunctionName: arn:aws:lambda:ap-northeast-1:101010101010:function:serverless-example-dev-main
397
+ Payload:
398
+ input.$: $
399
+ context.$: $$
400
+ Retry:
401
+ - ErrorEquals:
402
+ - Lambda.ServiceException
403
+ - Lambda.AWSLambdaException
404
+ - Lambda.SdkClientException
405
+ IntervalSeconds: 2
406
+ MaxAttempts: 5
407
+ BackoffRate: 2
408
+ OutputPath: $.Payload[0][0]
409
+ Next: read_csv
410
+ read_csv:
411
+ Type: Task
412
+ Resource: arn:aws:states:::lambda:invoke
413
+ Parameters:
414
+ FunctionName: arn:aws:lambda:ap-northeast-1:101010101010:function:serverless-example-dev-main
415
+ Payload:
416
+ input.$: $
417
+ context.$: $$
418
+ Retry:
419
+ - ErrorEquals:
420
+ - Lambda.ServiceException
421
+ - Lambda.AWSLambdaException
422
+ - Lambda.SdkClientException
423
+ IntervalSeconds: 2
424
+ MaxAttempts: 5
425
+ BackoffRate: 2
426
+ OutputPath: $.Payload[0][0]
427
+ End: true
369
428
  resources:
370
429
  Resources:
371
430
  TaskActionQueue:
@@ -380,3 +439,7 @@ resources:
380
439
  Type: AWS::SQS::Queue
381
440
  Properties:
382
441
  QueueName: notification-queue
442
+ ImportActionQueue:
443
+ Type: AWS::SQS::Queue
444
+ Properties:
445
+ QueueName: import-action-queue