@emarketeer/ts-microservice-commons 8.0.0 → 8.1.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.
@@ -830,7 +830,7 @@ class EmLambdaFunction extends constructs.Construct {
830
830
  class EmDynamoDBTable extends constructs.Construct {
831
831
  constructor(scope, id, config) {
832
832
  super(scope, id);
833
- const tableName = generateTableName(config.stage, config.serviceName, config.tableName);
833
+ const tableName = config.rawTableName ?? generateTableName(config.stage, config.serviceName, config.tableName);
834
834
  // Create DynamoDB table
835
835
  this.table = new awsDynamodb.Table(this, 'Table', {
836
836
  tableName,
@@ -838,11 +838,20 @@ class EmDynamoDBTable extends constructs.Construct {
838
838
  sortKey: config.sortKey,
839
839
  billingMode: config.billingMode || awsDynamodb.BillingMode.PAY_PER_REQUEST,
840
840
  pointInTimeRecovery: config.pointInTimeRecovery ?? config.stage === 'prod',
841
+ deletionProtection: config.deletionProtection ?? config.stage === 'prod',
841
842
  stream: config.stream ? awsDynamodb.StreamViewType.NEW_AND_OLD_IMAGES : undefined,
842
843
  timeToLiveAttribute: config.timeToLiveAttribute,
843
844
  encryption: awsDynamodb.TableEncryption.AWS_MANAGED,
844
845
  removalPolicy: getRemovalPolicy(config.stage)
845
846
  });
847
+ if (config.overrideLogicalId) {
848
+ const cfnTable = this.table.node.defaultChild;
849
+ if (!(cfnTable instanceof awsDynamodb.CfnTable)) {
850
+ throw new Error(`Cannot override table logical ID to "${config.overrideLogicalId}": ` +
851
+ 'table does not have a CfnTable default child.');
852
+ }
853
+ cfnTable.overrideLogicalId(config.overrideLogicalId);
854
+ }
846
855
  // Add Global Secondary Indexes
847
856
  if (config.globalSecondaryIndexes) {
848
857
  config.globalSecondaryIndexes.forEach(gsi => {
@@ -2221,6 +2230,41 @@ class EmStack extends cdk.Stack {
2221
2230
  })
2222
2231
  });
2223
2232
  }
2233
+ /**
2234
+ * Create a Lambda function consuming messages from an SNS topic via SQS.
2235
+ * Combines `createQueueConsumer()` + `subscribeToTopic()` in one call and
2236
+ * inherits the stack's default function config (env vars, VPC, timeout).
2237
+ *
2238
+ * Prefer this over constructing `TopicQueueConsumer` directly — the raw
2239
+ * constructor bypasses default-config merging.
2240
+ *
2241
+ * @example
2242
+ * ```typescript
2243
+ * this.createTopicQueueConsumer('ProcessInvoices', {
2244
+ * topic: invoiceTopic,
2245
+ * subscriptionOptions: { rawMessageDelivery: true },
2246
+ * handlerPath: 'src/handlers/process-invoices',
2247
+ * queueName: 'dev-my-service-invoice-queue',
2248
+ * alarmTopic,
2249
+ * })
2250
+ * ```
2251
+ */
2252
+ createTopicQueueConsumer(id, config) {
2253
+ const { topic, subscriptionOptions, ...queueConfig } = config;
2254
+ const merged = this.mergeConfig(queueConfig);
2255
+ const { functionName } = resolveHandlerPath(merged);
2256
+ return new TopicQueueConsumer(this, id, {
2257
+ ...merged,
2258
+ stage: merged.stage ?? this.stage,
2259
+ serviceName: merged.serviceName ?? this.serviceName,
2260
+ role: merged.role ?? this.sharedRole,
2261
+ topic,
2262
+ subscriptionOptions,
2263
+ ...(this.sharedRole && {
2264
+ serverlessFunctionName: merged.serverlessFunctionName ?? functionName
2265
+ })
2266
+ });
2267
+ }
2224
2268
  /**
2225
2269
  * Create a scheduled Lambda function with an EventBridge rule.
2226
2270
  * Combines `createFunction()` + `EmEventBridgeRule` + `addLambdaTarget()` in one call.
@@ -2363,7 +2407,7 @@ function createRdsVpcConfig(scope, stage, config) {
2363
2407
  const privateSubnets = config.privateSubnetIds.map((subnetId, index) => ec2.Subnet.fromSubnetId(scope, `RdsPrivateSubnet${index}-${stage}`, subnetId));
2364
2408
  const lambdaSecurityGroup = new ec2.SecurityGroup(scope, `RdsLambdaSecurityGroup-${stage}`, {
2365
2409
  vpc,
2366
- description: 'Lambda security group for RDS access',
2410
+ description: config.securityGroupDescription ?? 'Lambda security group for RDS access',
2367
2411
  allowAllOutbound: true
2368
2412
  });
2369
2413
  if (config.overrideLogicalIds?.securityGroup) {
@@ -2423,6 +2467,42 @@ const createEmApp = (options) => {
2423
2467
  return { app, stage: rawStage };
2424
2468
  };
2425
2469
 
2470
+ /**
2471
+ * Account-level RDS networking constants — the VPC, private subnets, and the
2472
+ * RDS security group that Lambda functions need to reach the shared database
2473
+ * cluster.
2474
+ *
2475
+ * These values are account-level constants: every service deploying into the
2476
+ * eMarketeer AWS account uses the same VPC/subnets/SG per stage. The VPC and
2477
+ * private subnets exist solely to give Lambdas a route to RDS — Lambdas that
2478
+ * don't talk to RDS do not need them.
2479
+ *
2480
+ * Designed to compose with `createRdsVpcConfig()` in `./rds-vpc`:
2481
+ *
2482
+ * ```typescript
2483
+ * const account = getAccountRdsVpcConfig(this.stage)
2484
+ * const vpcConfig = createRdsVpcConfig(this, this.stage, { ...account })
2485
+ * ```
2486
+ */
2487
+ function getAccountRdsVpcConfig(stage) {
2488
+ switch (stage) {
2489
+ case 'dev':
2490
+ return {
2491
+ vpcId: 'vpc-d2fd89b5',
2492
+ privateSubnetIds: ['subnet-7c74d81a', 'subnet-de04eb84', 'subnet-e29b26aa'],
2493
+ dbSecurityGroupId: 'sg-711c3f09'
2494
+ };
2495
+ case 'prod':
2496
+ return {
2497
+ vpcId: 'vpc-aeaf41cb',
2498
+ privateSubnetIds: ['subnet-dab14f80', 'subnet-e06ad686', 'subnet-14ea665c'],
2499
+ dbSecurityGroupId: 'sg-427bda39'
2500
+ };
2501
+ default:
2502
+ throw new Error(`Unsupported RDS VPC stage: ${stage}. Only 'dev' and 'prod' are supported.`);
2503
+ }
2504
+ }
2505
+
2426
2506
  exports.DEFAULT_LAMBDA_RUNTIME = DEFAULT_LAMBDA_RUNTIME;
2427
2507
  exports.DlqAlarm = DlqAlarm;
2428
2508
  exports.EVENT_PATTERNS = EVENT_PATTERNS;
@@ -2490,6 +2570,7 @@ exports.generateStackName = generateStackName;
2490
2570
  exports.generateStandardTags = generateStandardTags;
2491
2571
  exports.generateTableName = generateTableName;
2492
2572
  exports.generateTopicName = generateTopicName;
2573
+ exports.getAccountRdsVpcConfig = getAccountRdsVpcConfig;
2493
2574
  exports.getAlarmThresholds = getAlarmThresholds;
2494
2575
  exports.getComplianceTags = getComplianceTags;
2495
2576
  exports.getCostAllocationTags = getCostAllocationTags;