@aws/nx-plugin 1.0.0-rc.84 → 1.0.0-rc.86

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.
Files changed (51) hide show
  1. package/generators.json +7 -0
  2. package/migrations.json +6 -1
  3. package/package.json +1 -1
  4. package/src/internal/test-matrix/generator.js +16 -0
  5. package/src/internal/test-matrix/generator.js.map +1 -1
  6. package/src/migrations/latest/terraform-project-checkov-target-and-bootstrap-destroy/metadata.json +3 -0
  7. package/src/migrations/latest/terraform-project-checkov-target-and-bootstrap-destroy/migration.d.ts +6 -0
  8. package/src/migrations/latest/terraform-project-checkov-target-and-bootstrap-destroy/migration.js +146 -0
  9. package/src/migrations/latest/terraform-project-checkov-target-and-bootstrap-destroy/migration.js.map +1 -0
  10. package/src/open-api/json-metadata/generator.d.ts +24 -0
  11. package/src/open-api/json-metadata/generator.js +37 -0
  12. package/src/open-api/json-metadata/generator.js.map +1 -0
  13. package/src/open-api/json-metadata/schema.d.js +6 -0
  14. package/src/open-api/json-metadata/schema.d.js.map +1 -0
  15. package/src/open-api/json-metadata/schema.d.ts +9 -0
  16. package/src/open-api/json-metadata/schema.json +20 -0
  17. package/src/py/fast-api/__snapshots__/generator.terraform.spec.ts.snap +1604 -559
  18. package/src/py/fast-api/generator.js +2 -1
  19. package/src/py/fast-api/generator.js.map +1 -1
  20. package/src/py/rdb/__snapshots__/generator.spec.ts.snap +21 -9
  21. package/src/py/rdb/files/__name__/utils.py.template +17 -1
  22. package/src/sdk/open-api.d.ts +2 -0
  23. package/src/sdk/open-api.js +2 -1
  24. package/src/sdk/open-api.js.map +1 -1
  25. package/src/smithy/ts/api/__snapshots__/generator.spec.ts.snap +1548 -440
  26. package/src/smithy/ts/api/generator.js +2 -1
  27. package/src/smithy/ts/api/generator.js.map +1 -1
  28. package/src/terraform/project/files/application/bootstrap/providers.tf.template +2 -0
  29. package/src/terraform/project/files/application/scripts/bootstrap-destroy.ts.template +111 -0
  30. package/src/terraform/project/files/application/src/providers.tf.template +2 -0
  31. package/src/terraform/project/files/checkov/checkov.yml.template +8 -0
  32. package/src/terraform/project/generator.js +47 -4
  33. package/src/terraform/project/generator.js.map +1 -1
  34. package/src/trpc/backend/__snapshots__/generator.spec.ts.snap +1552 -525
  35. package/src/trpc/backend/files-operations/scripts/generate-operations.ts.template +40 -0
  36. package/src/trpc/backend/generator.js +10 -0
  37. package/src/trpc/backend/generator.js.map +1 -1
  38. package/src/ts/rdb/__snapshots__/generator.spec.ts.snap +128 -250
  39. package/src/ts/rdb/files/src/utils.ts.template +28 -1
  40. package/src/utils/api-constructs/api-constructs.js +11 -0
  41. package/src/utils/api-constructs/api-constructs.js.map +1 -1
  42. package/src/utils/api-constructs/files/terraform/app/apis/http/__apiNameKebabCase__/__apiNameKebabCase__.tf.template +243 -3
  43. package/src/utils/api-constructs/files/terraform/app/apis/rest/__apiNameKebabCase__/__apiNameKebabCase__.tf.template +428 -1
  44. package/src/utils/api-constructs/open-api-metadata.d.ts +20 -3
  45. package/src/utils/api-constructs/open-api-metadata.js +64 -8
  46. package/src/utils/api-constructs/open-api-metadata.js.map +1 -1
  47. package/src/utils/api-constructs/trpc-operations-metadata.d.ts +21 -0
  48. package/src/utils/api-constructs/trpc-operations-metadata.js +55 -0
  49. package/src/utils/api-constructs/trpc-operations-metadata.js.map +1 -0
  50. package/src/utils/rdb-constructs/files/terraform/app/dbs/__nameKebabCase__/__nameKebabCase__.tf.template +6 -8
  51. package/src/utils/rdb-constructs/files/terraform/core/rdb/aurora/aurora.tf.template +28 -116
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../packages/nx-plugin/src/utils/api-constructs/open-api-metadata.ts"],"sourcesContent":["/**\n * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.\n * SPDX-License-Identifier: Apache-2.0\n */\nimport {\n joinPathFragments,\n type ProjectConfiguration,\n type Tree,\n updateJson,\n} from '@nx/devkit';\nimport { updateGitIgnore } from '../git.js';\nimport type { Iac } from '../iac.js';\nimport { addDependencyToTargetIfNotPresent } from '../nx.js';\nimport {\n PACKAGES_DIR,\n SHARED_CONSTRUCTS_DIR,\n} from '../shared-constructs-constants.js';\n\nexport interface AddOpenApiMetadataGenerateTargetOptions {\n iac: Iac;\n apiNameKebabCase: string;\n specPath: string;\n specBuildTargetName: string;\n}\n\n/**\n * Adds a generate:<api-name>-metadata target to shared constructs (CDK only)\n * This allows for API CDK constructs to have type-safety for integrations based on an OpenAPI Specification\n */\nexport const addSharedConstructsOpenApiMetadataGenerateTarget = (\n tree: Tree,\n {\n iac,\n apiNameKebabCase,\n specPath,\n specBuildTargetName,\n }: AddOpenApiMetadataGenerateTargetOptions,\n) => {\n if (iac !== 'cdk') {\n // For Terraform, we do not support type-safe integration builders, rather only the single lambda\n // router pattern, and therefore do not need to add depenencies on metadata generation.\n return;\n }\n\n const generatedMetadataDir = joinPathFragments('generated', apiNameKebabCase);\n const generatedMetadataDirFromRoot = joinPathFragments(\n joinPathFragments(PACKAGES_DIR, SHARED_CONSTRUCTS_DIR),\n 'src',\n generatedMetadataDir,\n );\n\n updateJson(\n tree,\n joinPathFragments(PACKAGES_DIR, SHARED_CONSTRUCTS_DIR, 'project.json'),\n (config: ProjectConfiguration) => {\n if (!config.targets) {\n config.targets = {};\n }\n if (!config.targets.build) {\n config.targets.build = {};\n }\n // If not already defined, add a target to generate metadata from the OpenAPI spec, used\n // for providing a type-safe CDK construct\n const metadataTargetName = `generate:${apiNameKebabCase}-metadata`;\n if (!config.targets[metadataTargetName]) {\n config.targets[metadataTargetName] = {\n cache: true,\n executor: 'nx:run-commands',\n inputs: [\n {\n dependentTasksOutputFiles: '**/*.json',\n },\n ],\n outputs: [\n joinPathFragments('{workspaceRoot}', generatedMetadataDirFromRoot),\n ],\n options: {\n commands: [\n `nx g @aws/nx-plugin:open-api#ts-metadata --openApiSpecPath=\"${specPath}\" --outputPath=\"${generatedMetadataDirFromRoot}\" --no-interactive`,\n ],\n },\n dependsOn: [specBuildTargetName],\n };\n }\n addDependencyToTargetIfNotPresent(config, 'compile', metadataTargetName);\n return config;\n },\n );\n\n // Ignore the generated metadata by default\n // Users can safely remove the entry from the .gitignore if they prefer to check it in\n updateGitIgnore(\n tree,\n joinPathFragments(PACKAGES_DIR, SHARED_CONSTRUCTS_DIR),\n (patterns) => [...patterns, joinPathFragments('src', generatedMetadataDir)],\n );\n};\n"],"names":["joinPathFragments","updateJson","updateGitIgnore","addDependencyToTargetIfNotPresent","PACKAGES_DIR","SHARED_CONSTRUCTS_DIR","addSharedConstructsOpenApiMetadataGenerateTarget","tree","iac","apiNameKebabCase","specPath","specBuildTargetName","generatedMetadataDir","generatedMetadataDirFromRoot","config","targets","build","metadataTargetName","cache","executor","inputs","dependentTasksOutputFiles","outputs","options","commands","dependsOn","patterns"],"mappings":"AAAA;;;CAGC,GACD,SACEA,iBAAiB,EAGjBC,UAAU,QACL,aAAa;AACpB,SAASC,eAAe,QAAQ,YAAY;AAE5C,SAASC,iCAAiC,QAAQ,WAAW;AAC7D,SACEC,YAAY,EACZC,qBAAqB,QAChB,oCAAoC;AAS3C;;;CAGC,GACD,OAAO,MAAMC,mDAAmD,CAC9DC,MACA,EACEC,GAAG,EACHC,gBAAgB,EAChBC,QAAQ,EACRC,mBAAmB,EACqB;IAE1C,IAAIH,QAAQ,OAAO;QACjB,iGAAiG;QACjG,uFAAuF;QACvF;IACF;IAEA,MAAMI,uBAAuBZ,kBAAkB,aAAaS;IAC5D,MAAMI,+BAA+Bb,kBACnCA,kBAAkBI,cAAcC,wBAChC,OACAO;IAGFX,WACEM,MACAP,kBAAkBI,cAAcC,uBAAuB,iBACvD,CAACS;QACC,IAAI,CAACA,OAAOC,OAAO,EAAE;YACnBD,OAAOC,OAAO,GAAG,CAAC;QACpB;QACA,IAAI,CAACD,OAAOC,OAAO,CAACC,KAAK,EAAE;YACzBF,OAAOC,OAAO,CAACC,KAAK,GAAG,CAAC;QAC1B;QACA,wFAAwF;QACxF,0CAA0C;QAC1C,MAAMC,qBAAqB,CAAC,SAAS,EAAER,iBAAiB,SAAS,CAAC;QAClE,IAAI,CAACK,OAAOC,OAAO,CAACE,mBAAmB,EAAE;YACvCH,OAAOC,OAAO,CAACE,mBAAmB,GAAG;gBACnCC,OAAO;gBACPC,UAAU;gBACVC,QAAQ;oBACN;wBACEC,2BAA2B;oBAC7B;iBACD;gBACDC,SAAS;oBACPtB,kBAAkB,mBAAmBa;iBACtC;gBACDU,SAAS;oBACPC,UAAU;wBACR,CAAC,4DAA4D,EAAEd,SAAS,gBAAgB,EAAEG,6BAA6B,kBAAkB,CAAC;qBAC3I;gBACH;gBACAY,WAAW;oBAACd;iBAAoB;YAClC;QACF;QACAR,kCAAkCW,QAAQ,WAAWG;QACrD,OAAOH;IACT;IAGF,2CAA2C;IAC3C,sFAAsF;IACtFZ,gBACEK,MACAP,kBAAkBI,cAAcC,wBAChC,CAACqB,WAAa;eAAIA;YAAU1B,kBAAkB,OAAOY;SAAsB;AAE/E,EAAE"}
1
+ {"version":3,"sources":["../../../../../../packages/nx-plugin/src/utils/api-constructs/open-api-metadata.ts"],"sourcesContent":["/**\n * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.\n * SPDX-License-Identifier: Apache-2.0\n */\nimport {\n joinPathFragments,\n type ProjectConfiguration,\n type Tree,\n updateJson,\n} from '@nx/devkit';\nimport { updateGitIgnore } from '../git.js';\nimport type { Iac } from '../iac.js';\nimport { addDependencyToTargetIfNotPresent } from '../nx.js';\nimport {\n PACKAGES_DIR,\n SHARED_CONSTRUCTS_DIR,\n SHARED_TERRAFORM_DIR,\n} from '../shared-constructs-constants.js';\n\nexport interface AddOpenApiMetadataGenerateTargetOptions {\n iac: Iac;\n apiNameKebabCase: string;\n specPath: string;\n specBuildTargetName: string;\n /**\n * How integrations are mapped to operations. Terraform only needs the\n * operations metadata for the `isolated` pattern, since the `shared` pattern\n * routes every operation to one proxy Lambda.\n */\n integrationPattern?: 'isolated' | 'shared';\n}\n\n/**\n * Adds a target which generates metadata about the API's operations from its\n * OpenAPI specification, so that infrastructure can define an integration per\n * operation.\n *\n * For CDK this generates TypeScript, giving the construct type-safe integrations.\n * For Terraform this generates JSON, which the vended module reads to build one\n * Lambda function and route per operation.\n */\nexport const addSharedConstructsOpenApiMetadataGenerateTarget = (\n tree: Tree,\n options: AddOpenApiMetadataGenerateTargetOptions,\n) => {\n if (options.iac === 'cdk') {\n addCdkOpenApiMetadataGenerateTarget(tree, options);\n } else if (options.iac === 'terraform') {\n addTerraformOpenApiOperationsGenerateTarget(tree, options);\n }\n};\n\nconst addCdkOpenApiMetadataGenerateTarget = (\n tree: Tree,\n {\n apiNameKebabCase,\n specPath,\n specBuildTargetName,\n }: AddOpenApiMetadataGenerateTargetOptions,\n) => {\n const generatedMetadataDir = joinPathFragments('generated', apiNameKebabCase);\n const generatedMetadataDirFromRoot = joinPathFragments(\n joinPathFragments(PACKAGES_DIR, SHARED_CONSTRUCTS_DIR),\n 'src',\n generatedMetadataDir,\n );\n\n updateJson(\n tree,\n joinPathFragments(PACKAGES_DIR, SHARED_CONSTRUCTS_DIR, 'project.json'),\n (config: ProjectConfiguration) => {\n if (!config.targets) {\n config.targets = {};\n }\n if (!config.targets.build) {\n config.targets.build = {};\n }\n // If not already defined, add a target to generate metadata from the OpenAPI spec, used\n // for providing a type-safe CDK construct\n const metadataTargetName = `generate:${apiNameKebabCase}-metadata`;\n if (!config.targets[metadataTargetName]) {\n config.targets[metadataTargetName] = {\n cache: true,\n executor: 'nx:run-commands',\n inputs: [\n {\n dependentTasksOutputFiles: '**/*.json',\n },\n ],\n outputs: [\n joinPathFragments('{workspaceRoot}', generatedMetadataDirFromRoot),\n ],\n options: {\n commands: [\n `nx g @aws/nx-plugin:open-api#ts-metadata --openApiSpecPath=\"${specPath}\" --outputPath=\"${generatedMetadataDirFromRoot}\" --no-interactive`,\n ],\n },\n dependsOn: [specBuildTargetName],\n };\n }\n addDependencyToTargetIfNotPresent(config, 'compile', metadataTargetName);\n return config;\n },\n );\n\n // Ignore the generated metadata by default\n // Users can safely remove the entry from the .gitignore if they prefer to check it in\n updateGitIgnore(\n tree,\n joinPathFragments(PACKAGES_DIR, SHARED_CONSTRUCTS_DIR),\n (patterns) => [...patterns, joinPathFragments('src', generatedMetadataDir)],\n );\n};\n\n/**\n * Directory the operations metadata for an API is generated into, relative to\n * the shared Terraform project's `src`. The vended module reads the file from\n * here, so the module template and this target must agree on the location.\n */\nexport const terraformOperationsMetadataDir = (apiNameKebabCase: string) =>\n joinPathFragments('generated', apiNameKebabCase);\n\nconst addTerraformOpenApiOperationsGenerateTarget = (\n tree: Tree,\n {\n apiNameKebabCase,\n specPath,\n specBuildTargetName,\n integrationPattern,\n }: AddOpenApiMetadataGenerateTargetOptions,\n) => {\n // The `shared` pattern routes every operation to a single proxy Lambda, so the\n // module has no need for the operations metadata.\n if (integrationPattern !== 'isolated') {\n return;\n }\n\n const generatedDirFromRoot = joinPathFragments(\n PACKAGES_DIR,\n SHARED_TERRAFORM_DIR,\n 'src',\n terraformOperationsMetadataDir(apiNameKebabCase),\n );\n\n updateJson(\n tree,\n joinPathFragments(PACKAGES_DIR, SHARED_TERRAFORM_DIR, 'project.json'),\n (config: ProjectConfiguration) => {\n config.targets ??= {};\n // Terraform reads the operations metadata with `file()`, so it must exist\n // before plan; the target is wired into `build` below.\n const operationsTargetName = `generate:${apiNameKebabCase}-operations`;\n if (!config.targets[operationsTargetName]) {\n config.targets[operationsTargetName] = {\n cache: true,\n executor: 'nx:run-commands',\n inputs: [\n {\n dependentTasksOutputFiles: '**/*.json',\n },\n ],\n outputs: [joinPathFragments('{workspaceRoot}', generatedDirFromRoot)],\n options: {\n commands: [\n `nx g @aws/nx-plugin:open-api#json-metadata --openApiSpecPath=\"${specPath}\" --outputPath=\"${generatedDirFromRoot}\" --no-interactive`,\n ],\n },\n dependsOn: [specBuildTargetName],\n };\n }\n addDependencyToTargetIfNotPresent(config, 'build', operationsTargetName);\n return config;\n },\n );\n\n // Ignore the generated metadata by default\n // Users can safely remove the entry from the .gitignore if they prefer to check it in\n updateGitIgnore(\n tree,\n joinPathFragments(PACKAGES_DIR, SHARED_TERRAFORM_DIR),\n (patterns) => [\n ...patterns,\n joinPathFragments(\n 'src',\n terraformOperationsMetadataDir(apiNameKebabCase),\n ),\n ],\n );\n};\n"],"names":["joinPathFragments","updateJson","updateGitIgnore","addDependencyToTargetIfNotPresent","PACKAGES_DIR","SHARED_CONSTRUCTS_DIR","SHARED_TERRAFORM_DIR","addSharedConstructsOpenApiMetadataGenerateTarget","tree","options","iac","addCdkOpenApiMetadataGenerateTarget","addTerraformOpenApiOperationsGenerateTarget","apiNameKebabCase","specPath","specBuildTargetName","generatedMetadataDir","generatedMetadataDirFromRoot","config","targets","build","metadataTargetName","cache","executor","inputs","dependentTasksOutputFiles","outputs","commands","dependsOn","patterns","terraformOperationsMetadataDir","integrationPattern","generatedDirFromRoot","operationsTargetName"],"mappings":"AAAA;;;CAGC,GACD,SACEA,iBAAiB,EAGjBC,UAAU,QACL,aAAa;AACpB,SAASC,eAAe,QAAQ,YAAY;AAE5C,SAASC,iCAAiC,QAAQ,WAAW;AAC7D,SACEC,YAAY,EACZC,qBAAqB,EACrBC,oBAAoB,QACf,oCAAoC;AAe3C;;;;;;;;CAQC,GACD,OAAO,MAAMC,mDAAmD,CAC9DC,MACAC;IAEA,IAAIA,QAAQC,GAAG,KAAK,OAAO;QACzBC,oCAAoCH,MAAMC;IAC5C,OAAO,IAAIA,QAAQC,GAAG,KAAK,aAAa;QACtCE,4CAA4CJ,MAAMC;IACpD;AACF,EAAE;AAEF,MAAME,sCAAsC,CAC1CH,MACA,EACEK,gBAAgB,EAChBC,QAAQ,EACRC,mBAAmB,EACqB;IAE1C,MAAMC,uBAAuBhB,kBAAkB,aAAaa;IAC5D,MAAMI,+BAA+BjB,kBACnCA,kBAAkBI,cAAcC,wBAChC,OACAW;IAGFf,WACEO,MACAR,kBAAkBI,cAAcC,uBAAuB,iBACvD,CAACa;QACC,IAAI,CAACA,OAAOC,OAAO,EAAE;YACnBD,OAAOC,OAAO,GAAG,CAAC;QACpB;QACA,IAAI,CAACD,OAAOC,OAAO,CAACC,KAAK,EAAE;YACzBF,OAAOC,OAAO,CAACC,KAAK,GAAG,CAAC;QAC1B;QACA,wFAAwF;QACxF,0CAA0C;QAC1C,MAAMC,qBAAqB,CAAC,SAAS,EAAER,iBAAiB,SAAS,CAAC;QAClE,IAAI,CAACK,OAAOC,OAAO,CAACE,mBAAmB,EAAE;YACvCH,OAAOC,OAAO,CAACE,mBAAmB,GAAG;gBACnCC,OAAO;gBACPC,UAAU;gBACVC,QAAQ;oBACN;wBACEC,2BAA2B;oBAC7B;iBACD;gBACDC,SAAS;oBACP1B,kBAAkB,mBAAmBiB;iBACtC;gBACDR,SAAS;oBACPkB,UAAU;wBACR,CAAC,4DAA4D,EAAEb,SAAS,gBAAgB,EAAEG,6BAA6B,kBAAkB,CAAC;qBAC3I;gBACH;gBACAW,WAAW;oBAACb;iBAAoB;YAClC;QACF;QACAZ,kCAAkCe,QAAQ,WAAWG;QACrD,OAAOH;IACT;IAGF,2CAA2C;IAC3C,sFAAsF;IACtFhB,gBACEM,MACAR,kBAAkBI,cAAcC,wBAChC,CAACwB,WAAa;eAAIA;YAAU7B,kBAAkB,OAAOgB;SAAsB;AAE/E;AAEA;;;;CAIC,GACD,OAAO,MAAMc,iCAAiC,CAACjB,mBAC7Cb,kBAAkB,aAAaa,kBAAkB;AAEnD,MAAMD,8CAA8C,CAClDJ,MACA,EACEK,gBAAgB,EAChBC,QAAQ,EACRC,mBAAmB,EACnBgB,kBAAkB,EACsB;IAE1C,+EAA+E;IAC/E,kDAAkD;IAClD,IAAIA,uBAAuB,YAAY;QACrC;IACF;IAEA,MAAMC,uBAAuBhC,kBAC3BI,cACAE,sBACA,OACAwB,+BAA+BjB;IAGjCZ,WACEO,MACAR,kBAAkBI,cAAcE,sBAAsB,iBACtD,CAACY;QACCA,OAAOC,OAAO,KAAK,CAAC;QACpB,0EAA0E;QAC1E,uDAAuD;QACvD,MAAMc,uBAAuB,CAAC,SAAS,EAAEpB,iBAAiB,WAAW,CAAC;QACtE,IAAI,CAACK,OAAOC,OAAO,CAACc,qBAAqB,EAAE;YACzCf,OAAOC,OAAO,CAACc,qBAAqB,GAAG;gBACrCX,OAAO;gBACPC,UAAU;gBACVC,QAAQ;oBACN;wBACEC,2BAA2B;oBAC7B;iBACD;gBACDC,SAAS;oBAAC1B,kBAAkB,mBAAmBgC;iBAAsB;gBACrEvB,SAAS;oBACPkB,UAAU;wBACR,CAAC,8DAA8D,EAAEb,SAAS,gBAAgB,EAAEkB,qBAAqB,kBAAkB,CAAC;qBACrI;gBACH;gBACAJ,WAAW;oBAACb;iBAAoB;YAClC;QACF;QACAZ,kCAAkCe,QAAQ,SAASe;QACnD,OAAOf;IACT;IAGF,2CAA2C;IAC3C,sFAAsF;IACtFhB,gBACEM,MACAR,kBAAkBI,cAAcE,uBAChC,CAACuB,WAAa;eACTA;YACH7B,kBACE,OACA8B,+BAA+BjB;SAElC;AAEL"}
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+ import { type ProjectConfiguration, type Tree } from '@nx/devkit';
6
+ export interface AddTrpcOperationsMetadataTargetOptions {
7
+ apiNameKebabCase: string;
8
+ /** The tRPC backend project the operations are derived from. */
9
+ project: ProjectConfiguration;
10
+ /** Template variables for the vended script (module format). */
11
+ templateOptions: Record<string, unknown>;
12
+ }
13
+ /**
14
+ * Adds operations metadata generation for a tRPC API deployed with Terraform.
15
+ *
16
+ * tRPC has no OpenAPI specification, so the operations are derived by walking
17
+ * the router: a vended script writes the same JSON shape the OpenAPI-based
18
+ * generator produces, which the Terraform module reads to define one Lambda
19
+ * function and route per procedure.
20
+ */
21
+ export declare const addTrpcOperationsMetadataTarget: (tree: Tree, { apiNameKebabCase, project, templateOptions, }: AddTrpcOperationsMetadataTargetOptions) => void;
@@ -0,0 +1,55 @@
1
+ /**
2
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */ import { generateFiles, joinPathFragments, OverwriteStrategy, updateJson } from "@nx/devkit";
5
+ import { OPERATIONS_METADATA_FILE_NAME } from "../../open-api/json-metadata/generator.js";
6
+ import { updateGitIgnore } from "../git.js";
7
+ import { addDependencyToTargetIfNotPresent } from "../nx.js";
8
+ import { PACKAGES_DIR, SHARED_TERRAFORM_DIR } from "../shared-constructs-constants.js";
9
+ import { terraformOperationsMetadataDir } from "./open-api-metadata.js";
10
+ /**
11
+ * Adds operations metadata generation for a tRPC API deployed with Terraform.
12
+ *
13
+ * tRPC has no OpenAPI specification, so the operations are derived by walking
14
+ * the router: a vended script writes the same JSON shape the OpenAPI-based
15
+ * generator produces, which the Terraform module reads to define one Lambda
16
+ * function and route per procedure.
17
+ */ export const addTrpcOperationsMetadataTarget = (tree, { apiNameKebabCase, project, templateOptions })=>{
18
+ generateFiles(tree, joinPathFragments(import.meta.dirname, '..', '..', 'trpc', 'backend', 'files-operations'), project.root, templateOptions, {
19
+ overwriteStrategy: OverwriteStrategy.Overwrite
20
+ });
21
+ const generatedDirFromRoot = joinPathFragments(PACKAGES_DIR, SHARED_TERRAFORM_DIR, 'src', terraformOperationsMetadataDir(apiNameKebabCase));
22
+ const operationsFile = joinPathFragments(generatedDirFromRoot, OPERATIONS_METADATA_FILE_NAME);
23
+ project.targets ??= {};
24
+ project.targets.operations ??= {
25
+ cache: true,
26
+ executor: 'nx:run-commands',
27
+ outputs: [
28
+ joinPathFragments('{workspaceRoot}', generatedDirFromRoot)
29
+ ],
30
+ options: {
31
+ commands: [
32
+ `tsx {projectRoot}/scripts/generate-operations.ts ${operationsFile}`
33
+ ],
34
+ cwd: '{workspaceRoot}'
35
+ },
36
+ dependsOn: [
37
+ 'compile'
38
+ ]
39
+ };
40
+ addDependencyToTargetIfNotPresent(project, 'build', 'operations');
41
+ // The shared Terraform project reads the generated file with `file()`, so it
42
+ // must be written before that project is planned.
43
+ updateJson(tree, joinPathFragments(PACKAGES_DIR, SHARED_TERRAFORM_DIR, 'project.json'), (config)=>{
44
+ addDependencyToTargetIfNotPresent(config, 'build', `${project.name}:operations`);
45
+ return config;
46
+ });
47
+ // Ignore the generated metadata by default
48
+ // Users can safely remove the entry from the .gitignore if they prefer to check it in
49
+ updateGitIgnore(tree, joinPathFragments(PACKAGES_DIR, SHARED_TERRAFORM_DIR), (patterns)=>[
50
+ ...patterns,
51
+ joinPathFragments('src', terraformOperationsMetadataDir(apiNameKebabCase))
52
+ ]);
53
+ };
54
+
55
+ //# sourceMappingURL=trpc-operations-metadata.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../../../packages/nx-plugin/src/utils/api-constructs/trpc-operations-metadata.ts"],"sourcesContent":["/**\n * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.\n * SPDX-License-Identifier: Apache-2.0\n */\nimport {\n generateFiles,\n joinPathFragments,\n OverwriteStrategy,\n type ProjectConfiguration,\n type Tree,\n updateJson,\n} from '@nx/devkit';\nimport { OPERATIONS_METADATA_FILE_NAME } from '../../open-api/json-metadata/generator.js';\nimport { updateGitIgnore } from '../git.js';\nimport { addDependencyToTargetIfNotPresent } from '../nx.js';\nimport {\n PACKAGES_DIR,\n SHARED_TERRAFORM_DIR,\n} from '../shared-constructs-constants.js';\nimport { terraformOperationsMetadataDir } from './open-api-metadata.js';\n\nexport interface AddTrpcOperationsMetadataTargetOptions {\n apiNameKebabCase: string;\n /** The tRPC backend project the operations are derived from. */\n project: ProjectConfiguration;\n /** Template variables for the vended script (module format). */\n templateOptions: Record<string, unknown>;\n}\n\n/**\n * Adds operations metadata generation for a tRPC API deployed with Terraform.\n *\n * tRPC has no OpenAPI specification, so the operations are derived by walking\n * the router: a vended script writes the same JSON shape the OpenAPI-based\n * generator produces, which the Terraform module reads to define one Lambda\n * function and route per procedure.\n */\nexport const addTrpcOperationsMetadataTarget = (\n tree: Tree,\n {\n apiNameKebabCase,\n project,\n templateOptions,\n }: AddTrpcOperationsMetadataTargetOptions,\n) => {\n generateFiles(\n tree,\n joinPathFragments(\n import.meta.dirname,\n '..',\n '..',\n 'trpc',\n 'backend',\n 'files-operations',\n ),\n project.root,\n templateOptions,\n {\n overwriteStrategy: OverwriteStrategy.Overwrite,\n },\n );\n\n const generatedDirFromRoot = joinPathFragments(\n PACKAGES_DIR,\n SHARED_TERRAFORM_DIR,\n 'src',\n terraformOperationsMetadataDir(apiNameKebabCase),\n );\n const operationsFile = joinPathFragments(\n generatedDirFromRoot,\n OPERATIONS_METADATA_FILE_NAME,\n );\n\n project.targets ??= {};\n project.targets.operations ??= {\n cache: true,\n executor: 'nx:run-commands',\n outputs: [joinPathFragments('{workspaceRoot}', generatedDirFromRoot)],\n options: {\n commands: [\n `tsx {projectRoot}/scripts/generate-operations.ts ${operationsFile}`,\n ],\n cwd: '{workspaceRoot}',\n },\n dependsOn: ['compile'],\n };\n addDependencyToTargetIfNotPresent(project, 'build', 'operations');\n\n // The shared Terraform project reads the generated file with `file()`, so it\n // must be written before that project is planned.\n updateJson(\n tree,\n joinPathFragments(PACKAGES_DIR, SHARED_TERRAFORM_DIR, 'project.json'),\n (config: ProjectConfiguration) => {\n addDependencyToTargetIfNotPresent(\n config,\n 'build',\n `${project.name}:operations`,\n );\n return config;\n },\n );\n\n // Ignore the generated metadata by default\n // Users can safely remove the entry from the .gitignore if they prefer to check it in\n updateGitIgnore(\n tree,\n joinPathFragments(PACKAGES_DIR, SHARED_TERRAFORM_DIR),\n (patterns) => [\n ...patterns,\n joinPathFragments(\n 'src',\n terraformOperationsMetadataDir(apiNameKebabCase),\n ),\n ],\n );\n};\n"],"names":["generateFiles","joinPathFragments","OverwriteStrategy","updateJson","OPERATIONS_METADATA_FILE_NAME","updateGitIgnore","addDependencyToTargetIfNotPresent","PACKAGES_DIR","SHARED_TERRAFORM_DIR","terraformOperationsMetadataDir","addTrpcOperationsMetadataTarget","tree","apiNameKebabCase","project","templateOptions","dirname","root","overwriteStrategy","Overwrite","generatedDirFromRoot","operationsFile","targets","operations","cache","executor","outputs","options","commands","cwd","dependsOn","config","name","patterns"],"mappings":"AAAA;;;CAGC,GACD,SACEA,aAAa,EACbC,iBAAiB,EACjBC,iBAAiB,EAGjBC,UAAU,QACL,aAAa;AACpB,SAASC,6BAA6B,QAAQ,4CAA4C;AAC1F,SAASC,eAAe,QAAQ,YAAY;AAC5C,SAASC,iCAAiC,QAAQ,WAAW;AAC7D,SACEC,YAAY,EACZC,oBAAoB,QACf,oCAAoC;AAC3C,SAASC,8BAA8B,QAAQ,yBAAyB;AAUxE;;;;;;;CAOC,GACD,OAAO,MAAMC,kCAAkC,CAC7CC,MACA,EACEC,gBAAgB,EAChBC,OAAO,EACPC,eAAe,EACwB;IAEzCd,cACEW,MACAV,kBACE,YAAYc,OAAO,EACnB,MACA,MACA,QACA,WACA,qBAEFF,QAAQG,IAAI,EACZF,iBACA;QACEG,mBAAmBf,kBAAkBgB,SAAS;IAChD;IAGF,MAAMC,uBAAuBlB,kBAC3BM,cACAC,sBACA,OACAC,+BAA+BG;IAEjC,MAAMQ,iBAAiBnB,kBACrBkB,sBACAf;IAGFS,QAAQQ,OAAO,KAAK,CAAC;IACrBR,QAAQQ,OAAO,CAACC,UAAU,KAAK;QAC7BC,OAAO;QACPC,UAAU;QACVC,SAAS;YAACxB,kBAAkB,mBAAmBkB;SAAsB;QACrEO,SAAS;YACPC,UAAU;gBACR,CAAC,iDAAiD,EAAEP,gBAAgB;aACrE;YACDQ,KAAK;QACP;QACAC,WAAW;YAAC;SAAU;IACxB;IACAvB,kCAAkCO,SAAS,SAAS;IAEpD,6EAA6E;IAC7E,kDAAkD;IAClDV,WACEQ,MACAV,kBAAkBM,cAAcC,sBAAsB,iBACtD,CAACsB;QACCxB,kCACEwB,QACA,SACA,GAAGjB,QAAQkB,IAAI,CAAC,WAAW,CAAC;QAE9B,OAAOD;IACT;IAGF,2CAA2C;IAC3C,sFAAsF;IACtFzB,gBACEM,MACAV,kBAAkBM,cAAcC,uBAChC,CAACwB,WAAa;eACTA;YACH/B,kBACE,OACAQ,+BAA+BG;SAElC;AAEL,EAAE"}
@@ -88,12 +88,6 @@ variable "enable_rds_proxy" {
88
88
  default = true
89
89
  }
90
90
 
91
- variable "enable_credential_rotation" {
92
- description = "Whether to enable automatic credential rotation for the admin secret."
93
- type = bool
94
- default = true
95
- }
96
-
97
91
  variable "enable_cloudwatch_logs" {
98
92
  description = "Whether to export Aurora engine logs to CloudWatch. PostgreSQL logs DDL statements only (log_statement=ddl); MySQL enables Advanced Auditing scoped to connections and DDL (server_audit_events=CONNECT,QUERY_DDL). Neither logs statement parameter values, to avoid leaking PII into log data."
99
93
  type = bool
@@ -177,7 +171,6 @@ module "aurora" {
177
171
  name = "<%= nameKebabCase %>"
178
172
  vpc_id = var.vpc_id
179
173
  subnet_ids = var.database_subnet_ids
180
- lambda_subnet_ids = var.lambda_subnet_ids
181
174
  engine = "<%= engine === 'mysql' ? 'aurora-mysql' : 'aurora-postgresql' %>"
182
175
  engine_version = var.engine_version
183
176
  database_name = "<%= databaseName %>"
@@ -189,7 +182,6 @@ module "aurora" {
189
182
  deletion_protection = var.deletion_protection
190
183
  skip_final_snapshot = var.skip_final_snapshot
191
184
  enable_rds_proxy = var.enable_rds_proxy
192
- enable_credential_rotation = var.enable_credential_rotation
193
185
  enable_cloudwatch_logs = var.enable_cloudwatch_logs
194
186
  enable_performance_insights = var.enable_performance_insights
195
187
  performance_insights_retention_period = var.performance_insights_retention_period
@@ -612,6 +604,9 @@ resource "aws_lambda_function" "create_db_user" {
612
604
  environment {
613
605
  variables = {
614
606
  DATABASE_SECRET_ARN = module.aurora.secret_arn
607
+ DATABASE_HOST = module.aurora.writer_endpoint
608
+ DATABASE_PORT = module.aurora.cluster_port
609
+ DATABASE_NAME = module.aurora.database_name
615
610
  <%_ if (framework === 'prisma') { _%>
616
611
  NODE_EXTRA_CA_CERTS = "/var/runtime/ca-cert.pem"
617
612
  <%_ } _%>
@@ -699,6 +694,9 @@ resource "aws_lambda_function" "migration_handler" {
699
694
  environment {
700
695
  variables = {
701
696
  DATABASE_SECRET_ARN = module.aurora.secret_arn
697
+ DATABASE_HOST = module.aurora.writer_endpoint
698
+ DATABASE_PORT = module.aurora.cluster_port
699
+ DATABASE_NAME = module.aurora.database_name
702
700
  }
703
701
  }
704
702
 
@@ -1,5 +1,6 @@
1
1
  # Core Aurora module
2
- # This module creates an Aurora cluster and a generated admin secret.
2
+ # This module creates an Aurora cluster whose admin credentials are generated
3
+ # and rotated by RDS in Secrets Manager.
3
4
 
4
5
  terraform {
5
6
  required_version = ">= 1.0"
@@ -43,11 +44,6 @@ variable "subnet_ids" {
43
44
  type = list(string)
44
45
  }
45
46
 
46
- variable "lambda_subnet_ids" {
47
- description = "Subnet IDs for the credential-rotation Lambda. Must have outbound egress to AWS service endpoints (e.g. Secrets Manager) — use private subnets with a NAT gateway."
48
- type = list(string)
49
- }
50
-
51
47
  variable "engine" {
52
48
  description = "Aurora engine to use."
53
49
  type = string
@@ -117,12 +113,6 @@ variable "enable_rds_proxy" {
117
113
  default = true
118
114
  }
119
115
 
120
- variable "enable_credential_rotation" {
121
- description = "Whether to enable automatic credential rotation for the admin secret."
122
- type = bool
123
- default = true
124
- }
125
-
126
116
  variable "enable_cloudwatch_logs" {
127
117
  description = "Whether to export Aurora engine logs to CloudWatch. PostgreSQL logs DDL statements only (log_statement=ddl); MySQL enables Advanced Auditing scoped to connections and DDL (server_audit_events=CONNECT,QUERY_DDL). Neither logs statement parameter values, to avoid leaking PII into log data."
128
118
  type = bool
@@ -244,6 +234,20 @@ resource "aws_kms_key" "database" {
244
234
  "kms:CreateGrant"
245
235
  ]
246
236
  Resource = "*"
237
+ },
238
+ {
239
+ Sid = "AllowSecretsManagerService"
240
+ Effect = "Allow"
241
+ Principal = {
242
+ Service = "secretsmanager.amazonaws.com"
243
+ }
244
+ Action = [
245
+ "kms:Encrypt",
246
+ "kms:Decrypt",
247
+ "kms:GenerateDataKey*",
248
+ "kms:DescribeKey"
249
+ ]
250
+ Resource = "*"
247
251
  }
248
252
  ]
249
253
  })
@@ -298,35 +302,6 @@ resource "aws_db_subnet_group" "database" {
298
302
  })
299
303
  }
300
304
 
301
- resource "random_password" "master_password" {
302
- length = 32
303
- special = true
304
- override_special = "!#$%&*()-_=+[]{}<>:?"
305
- }
306
-
307
- resource "aws_secretsmanager_secret" "credentials" {
308
- #checkov:skip=CKV2_AWS_57:Rotation is configured via aws_cloudformation_stack.credentials_rotation using the AWS::SecretsManager-2024-09-16 transform; Checkov cannot resolve rotation schedules created by CloudFormation transforms
309
- name_prefix = "${var.name}-aurora-credentials-"
310
- kms_key_id = aws_kms_key.database.arn
311
-
312
- tags = merge(var.tags, {
313
- Name = "${var.name}-aurora-credentials"
314
- })
315
- }
316
-
317
- resource "aws_secretsmanager_secret_version" "credentials" {
318
- secret_id = aws_secretsmanager_secret.credentials.id
319
- secret_string = jsonencode({
320
- engine = var.engine == "aurora-mysql" ? "mysql" : "postgres"
321
- username = var.admin_user
322
- password = random_password.master_password.result
323
- host = aws_rds_cluster.database.endpoint
324
- port = aws_rds_cluster.database.port
325
- dbname = var.database_name
326
- dbClusterIdentifier = aws_rds_cluster.database.cluster_identifier
327
- })
328
- }
329
-
330
305
  resource "aws_rds_cluster" "database" {
331
306
  #checkov:skip=CKV2_AWS_27:Query logging is enabled by default via enable_cloudwatch_logs; checkov cannot resolve the conditional count expression on aws_rds_cluster_parameter_group.database
332
307
  #checkov:skip=CKV_AWS_139:Deletion protection is enabled but checkov is unable to detect it correctly
@@ -335,7 +310,8 @@ resource "aws_rds_cluster" "database" {
335
310
  engine_version = coalesce(var.engine_version, local.default_engine_version)
336
311
  database_name = var.database_name
337
312
  master_username = var.admin_user
338
- master_password = random_password.master_password.result
313
+ manage_master_user_password = true
314
+ master_user_secret_kms_key_id = aws_kms_key.database.arn
339
315
  db_subnet_group_name = aws_db_subnet_group.database.name
340
316
  db_cluster_parameter_group_name = var.enable_cloudwatch_logs ? aws_rds_cluster_parameter_group.database[0].name : null
341
317
  vpc_security_group_ids = [aws_security_group.database.id]
@@ -387,75 +363,6 @@ resource "aws_rds_cluster_instance" "database" {
387
363
  })
388
364
  }
389
365
 
390
- resource "aws_security_group" "rotation" {
391
- #checkov:skip=CKV2_AWS_5:Security group is attached to the hosted rotation Lambda created by AWS::SecretsManager::RotationSchedule; Checkov cannot resolve resources created by the CloudFormation transform
392
- count = var.enable_credential_rotation ? 1 : 0
393
- name_prefix = "${var.name}-aurora-rotation-"
394
- description = "Security group for Aurora secret rotation ${var.name}"
395
- vpc_id = var.vpc_id
396
-
397
- tags = merge(var.tags, {
398
- Name = "${var.name}-aurora-rotation"
399
- })
400
- }
401
-
402
- resource "aws_vpc_security_group_egress_rule" "rotation_to_database" {
403
- count = var.enable_credential_rotation ? 1 : 0
404
- security_group_id = aws_security_group.rotation[0].id
405
- referenced_security_group_id = aws_security_group.database.id
406
- from_port = coalesce(var.port, local.default_port)
407
- to_port = coalesce(var.port, local.default_port)
408
- ip_protocol = "tcp"
409
- description = "Allow outbound database traffic from secret rotation Lambda"
410
- }
411
-
412
- resource "aws_vpc_security_group_ingress_rule" "rotation_to_database" {
413
- count = var.enable_credential_rotation ? 1 : 0
414
- security_group_id = aws_security_group.database.id
415
- referenced_security_group_id = aws_security_group.rotation[0].id
416
- from_port = coalesce(var.port, local.default_port)
417
- to_port = coalesce(var.port, local.default_port)
418
- ip_protocol = "tcp"
419
- description = "Allow inbound database traffic from secret rotation Lambda"
420
- }
421
-
422
- resource "aws_cloudformation_stack" "credentials_rotation" {
423
- #checkov:skip=CKV_AWS_124:SNS event notifications are not required for the credentials rotation stack
424
- count = var.enable_credential_rotation ? 1 : 0
425
- name = "${local.cluster_name}-credentials-rotation"
426
-
427
- capabilities = ["CAPABILITY_IAM", "CAPABILITY_AUTO_EXPAND"]
428
-
429
- template_body = jsonencode({
430
- AWSTemplateFormatVersion = "2010-09-09"
431
- Transform = "AWS::SecretsManager-2024-09-16"
432
- Resources = {
433
- CredentialsRotationSchedule = {
434
- Type = "AWS::SecretsManager::RotationSchedule"
435
- Properties = {
436
- SecretId = aws_secretsmanager_secret.credentials.arn
437
- HostedRotationLambda = {
438
- RotationType = var.engine == "aurora-mysql" ? "MySQLSingleUser" : "PostgreSQLSingleUser"
439
- RotationLambdaName = "${local.cluster_name}-credentials-rotation"
440
- VpcSecurityGroupIds = aws_security_group.rotation[0].id
441
- VpcSubnetIds = join(",", var.lambda_subnet_ids)
442
- }
443
- RotationRules = {
444
- AutomaticallyAfterDays = 30
445
- }
446
- }
447
- }
448
- }
449
- })
450
-
451
- depends_on = [
452
- aws_secretsmanager_secret_version.credentials,
453
- aws_rds_cluster_instance.database,
454
- aws_vpc_security_group_egress_rule.rotation_to_database,
455
- aws_vpc_security_group_ingress_rule.rotation_to_database,
456
- ]
457
- }
458
-
459
366
  resource "aws_iam_role" "proxy" {
460
367
  count = var.enable_rds_proxy ? 1 : 0
461
368
 
@@ -694,6 +601,11 @@ output "cluster_endpoint" {
694
601
  value = var.enable_rds_proxy ? aws_db_proxy.aurora[0].endpoint : aws_rds_cluster.database.endpoint
695
602
  }
696
603
 
604
+ output "writer_endpoint" {
605
+ description = "Writer endpoint of the Aurora cluster itself, bypassing the RDS Proxy. Used by the admin-credentialled handlers, which connect directly to the cluster."
606
+ value = aws_rds_cluster.database.endpoint
607
+ }
608
+
697
609
  output "reader_endpoint" {
698
610
  description = "Reader endpoint of the Aurora cluster."
699
611
  value = aws_rds_cluster.database.reader_endpoint
@@ -720,12 +632,12 @@ output "proxy_role_name" {
720
632
  }
721
633
 
722
634
  output "secret_arn" {
723
- description = "ARN of the generated admin credentials secret."
724
- value = aws_secretsmanager_secret.credentials.arn
635
+ description = "ARN of the RDS-managed admin credentials secret. Its value is a JSON object with username and password only — connection details come from this module's other outputs."
636
+ value = one(aws_rds_cluster.database.master_user_secret[*].secret_arn)
725
637
  }
726
638
 
727
639
  output "kms_key_arn" {
728
- description = "ARN of the KMS key protecting Aurora and the generated secret."
640
+ description = "ARN of the KMS key protecting Aurora and its admin credentials secret."
729
641
  value = aws_kms_key.database.arn
730
642
  }
731
643
 
@@ -750,6 +662,6 @@ output "database_name" {
750
662
  }
751
663
 
752
664
  output "admin_user" {
753
- description = "Admin username stored in the generated secret."
754
- value = jsondecode(aws_secretsmanager_secret_version.credentials.secret_string).username
665
+ description = "Admin username stored in the RDS-managed credentials secret."
666
+ value = aws_rds_cluster.database.master_username
755
667
  }