@friggframework/devtools 2.0.0-next.45 → 2.0.0-next.46

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 (212) hide show
  1. package/infrastructure/ARCHITECTURE.md +487 -0
  2. package/infrastructure/HEALTH.md +468 -0
  3. package/infrastructure/README.md +51 -0
  4. package/infrastructure/__tests__/postgres-config.test.js +914 -0
  5. package/infrastructure/__tests__/template-generation.test.js +687 -0
  6. package/infrastructure/create-frigg-infrastructure.js +1 -1
  7. package/infrastructure/docs/POSTGRES-CONFIGURATION.md +630 -0
  8. package/infrastructure/{DEPLOYMENT-INSTRUCTIONS.md → docs/deployment-instructions.md} +3 -3
  9. package/infrastructure/{IAM-POLICY-TEMPLATES.md → docs/iam-policy-templates.md} +9 -10
  10. package/infrastructure/domains/database/aurora-builder.js +809 -0
  11. package/infrastructure/domains/database/aurora-builder.test.js +950 -0
  12. package/infrastructure/domains/database/aurora-discovery.js +87 -0
  13. package/infrastructure/domains/database/aurora-discovery.test.js +188 -0
  14. package/infrastructure/domains/database/aurora-resolver.js +210 -0
  15. package/infrastructure/domains/database/aurora-resolver.test.js +347 -0
  16. package/infrastructure/domains/database/migration-builder.js +633 -0
  17. package/infrastructure/domains/database/migration-builder.test.js +294 -0
  18. package/infrastructure/domains/database/migration-resolver.js +163 -0
  19. package/infrastructure/domains/database/migration-resolver.test.js +337 -0
  20. package/infrastructure/domains/health/application/ports/IPropertyReconciler.js +164 -0
  21. package/infrastructure/domains/health/application/ports/IResourceDetector.js +129 -0
  22. package/infrastructure/domains/health/application/ports/IResourceImporter.js +142 -0
  23. package/infrastructure/domains/health/application/ports/IStackRepository.js +131 -0
  24. package/infrastructure/domains/health/application/ports/index.js +26 -0
  25. package/infrastructure/domains/health/application/use-cases/__tests__/execute-resource-import-use-case.test.js +679 -0
  26. package/infrastructure/domains/health/application/use-cases/__tests__/mismatch-analyzer-method-name.test.js +167 -0
  27. package/infrastructure/domains/health/application/use-cases/__tests__/repair-via-import-use-case.test.js +1130 -0
  28. package/infrastructure/domains/health/application/use-cases/execute-resource-import-use-case.js +221 -0
  29. package/infrastructure/domains/health/application/use-cases/reconcile-properties-use-case.js +152 -0
  30. package/infrastructure/domains/health/application/use-cases/reconcile-properties-use-case.test.js +343 -0
  31. package/infrastructure/domains/health/application/use-cases/repair-via-import-use-case.js +535 -0
  32. package/infrastructure/domains/health/application/use-cases/repair-via-import-use-case.test.js +376 -0
  33. package/infrastructure/domains/health/application/use-cases/run-health-check-use-case.js +213 -0
  34. package/infrastructure/domains/health/application/use-cases/run-health-check-use-case.test.js +441 -0
  35. package/infrastructure/domains/health/docs/ACME-DEV-DRIFT-ANALYSIS.md +267 -0
  36. package/infrastructure/domains/health/docs/BUILD-VS-DEPLOYED-TEMPLATE-ANALYSIS.md +324 -0
  37. package/infrastructure/domains/health/docs/ORPHAN-DETECTION-ANALYSIS.md +386 -0
  38. package/infrastructure/domains/health/docs/SPEC-CLEANUP-COMMAND.md +1419 -0
  39. package/infrastructure/domains/health/docs/TDD-IMPLEMENTATION-SUMMARY.md +391 -0
  40. package/infrastructure/domains/health/docs/TEMPLATE-COMPARISON-IMPLEMENTATION.md +551 -0
  41. package/infrastructure/domains/health/domain/entities/issue.js +299 -0
  42. package/infrastructure/domains/health/domain/entities/issue.test.js +528 -0
  43. package/infrastructure/domains/health/domain/entities/property-mismatch.js +108 -0
  44. package/infrastructure/domains/health/domain/entities/property-mismatch.test.js +275 -0
  45. package/infrastructure/domains/health/domain/entities/resource.js +159 -0
  46. package/infrastructure/domains/health/domain/entities/resource.test.js +432 -0
  47. package/infrastructure/domains/health/domain/entities/stack-health-report.js +306 -0
  48. package/infrastructure/domains/health/domain/entities/stack-health-report.test.js +601 -0
  49. package/infrastructure/domains/health/domain/services/__tests__/health-score-percentage-based.test.js +380 -0
  50. package/infrastructure/domains/health/domain/services/__tests__/import-progress-monitor.test.js +971 -0
  51. package/infrastructure/domains/health/domain/services/__tests__/import-template-generator.test.js +1150 -0
  52. package/infrastructure/domains/health/domain/services/__tests__/logical-id-mapper.test.js +672 -0
  53. package/infrastructure/domains/health/domain/services/__tests__/template-parser.test.js +496 -0
  54. package/infrastructure/domains/health/domain/services/__tests__/update-progress-monitor.test.js +419 -0
  55. package/infrastructure/domains/health/domain/services/health-score-calculator.js +248 -0
  56. package/infrastructure/domains/health/domain/services/health-score-calculator.test.js +504 -0
  57. package/infrastructure/domains/health/domain/services/import-progress-monitor.js +195 -0
  58. package/infrastructure/domains/health/domain/services/import-template-generator.js +435 -0
  59. package/infrastructure/domains/health/domain/services/logical-id-mapper.js +345 -0
  60. package/infrastructure/domains/health/domain/services/mismatch-analyzer.js +234 -0
  61. package/infrastructure/domains/health/domain/services/mismatch-analyzer.test.js +431 -0
  62. package/infrastructure/domains/health/domain/services/property-mutability-config.js +382 -0
  63. package/infrastructure/domains/health/domain/services/template-parser.js +245 -0
  64. package/infrastructure/domains/health/domain/services/update-progress-monitor.js +192 -0
  65. package/infrastructure/domains/health/domain/value-objects/health-score.js +138 -0
  66. package/infrastructure/domains/health/domain/value-objects/health-score.test.js +267 -0
  67. package/infrastructure/domains/health/domain/value-objects/property-mutability.js +161 -0
  68. package/infrastructure/domains/health/domain/value-objects/property-mutability.test.js +198 -0
  69. package/infrastructure/domains/health/domain/value-objects/resource-state.js +167 -0
  70. package/infrastructure/domains/health/domain/value-objects/resource-state.test.js +196 -0
  71. package/infrastructure/domains/health/domain/value-objects/stack-identifier.js +192 -0
  72. package/infrastructure/domains/health/domain/value-objects/stack-identifier.test.js +262 -0
  73. package/infrastructure/domains/health/infrastructure/adapters/__tests__/orphan-detection-cfn-tagged.test.js +312 -0
  74. package/infrastructure/domains/health/infrastructure/adapters/__tests__/orphan-detection-multi-stack.test.js +367 -0
  75. package/infrastructure/domains/health/infrastructure/adapters/__tests__/orphan-detection-relationship-analysis.test.js +432 -0
  76. package/infrastructure/domains/health/infrastructure/adapters/aws-property-reconciler.js +784 -0
  77. package/infrastructure/domains/health/infrastructure/adapters/aws-property-reconciler.test.js +1133 -0
  78. package/infrastructure/domains/health/infrastructure/adapters/aws-resource-detector.js +565 -0
  79. package/infrastructure/domains/health/infrastructure/adapters/aws-resource-detector.test.js +554 -0
  80. package/infrastructure/domains/health/infrastructure/adapters/aws-resource-importer.js +318 -0
  81. package/infrastructure/domains/health/infrastructure/adapters/aws-resource-importer.test.js +398 -0
  82. package/infrastructure/domains/health/infrastructure/adapters/aws-stack-repository.js +777 -0
  83. package/infrastructure/domains/health/infrastructure/adapters/aws-stack-repository.test.js +580 -0
  84. package/infrastructure/domains/integration/integration-builder.js +397 -0
  85. package/infrastructure/domains/integration/integration-builder.test.js +593 -0
  86. package/infrastructure/domains/integration/integration-resolver.js +170 -0
  87. package/infrastructure/domains/integration/integration-resolver.test.js +369 -0
  88. package/infrastructure/domains/integration/websocket-builder.js +69 -0
  89. package/infrastructure/domains/integration/websocket-builder.test.js +195 -0
  90. package/infrastructure/domains/networking/vpc-builder.js +1829 -0
  91. package/infrastructure/domains/networking/vpc-builder.test.js +1262 -0
  92. package/infrastructure/domains/networking/vpc-discovery.js +177 -0
  93. package/infrastructure/domains/networking/vpc-discovery.test.js +350 -0
  94. package/infrastructure/domains/networking/vpc-resolver.js +324 -0
  95. package/infrastructure/domains/networking/vpc-resolver.test.js +501 -0
  96. package/infrastructure/domains/parameters/ssm-builder.js +79 -0
  97. package/infrastructure/domains/parameters/ssm-builder.test.js +189 -0
  98. package/infrastructure/domains/parameters/ssm-discovery.js +84 -0
  99. package/infrastructure/domains/parameters/ssm-discovery.test.js +210 -0
  100. package/infrastructure/{iam-generator.js → domains/security/iam-generator.js} +2 -2
  101. package/infrastructure/domains/security/kms-builder.js +366 -0
  102. package/infrastructure/domains/security/kms-builder.test.js +374 -0
  103. package/infrastructure/domains/security/kms-discovery.js +80 -0
  104. package/infrastructure/domains/security/kms-discovery.test.js +177 -0
  105. package/infrastructure/domains/security/kms-resolver.js +96 -0
  106. package/infrastructure/domains/security/kms-resolver.test.js +216 -0
  107. package/infrastructure/domains/shared/base-builder.js +112 -0
  108. package/infrastructure/domains/shared/base-resolver.js +186 -0
  109. package/infrastructure/domains/shared/base-resolver.test.js +305 -0
  110. package/infrastructure/domains/shared/builder-orchestrator.js +212 -0
  111. package/infrastructure/domains/shared/builder-orchestrator.test.js +213 -0
  112. package/infrastructure/domains/shared/cloudformation-discovery-v2.js +334 -0
  113. package/infrastructure/domains/shared/cloudformation-discovery.js +375 -0
  114. package/infrastructure/domains/shared/cloudformation-discovery.test.js +590 -0
  115. package/infrastructure/domains/shared/environment-builder.js +119 -0
  116. package/infrastructure/domains/shared/environment-builder.test.js +247 -0
  117. package/infrastructure/domains/shared/providers/aws-provider-adapter.js +544 -0
  118. package/infrastructure/domains/shared/providers/aws-provider-adapter.test.js +377 -0
  119. package/infrastructure/domains/shared/providers/azure-provider-adapter.stub.js +93 -0
  120. package/infrastructure/domains/shared/providers/cloud-provider-adapter.js +136 -0
  121. package/infrastructure/domains/shared/providers/gcp-provider-adapter.stub.js +82 -0
  122. package/infrastructure/domains/shared/providers/provider-factory.js +108 -0
  123. package/infrastructure/domains/shared/providers/provider-factory.test.js +170 -0
  124. package/infrastructure/domains/shared/resource-discovery.js +192 -0
  125. package/infrastructure/domains/shared/resource-discovery.test.js +552 -0
  126. package/infrastructure/domains/shared/types/app-definition.js +205 -0
  127. package/infrastructure/domains/shared/types/discovery-result.js +106 -0
  128. package/infrastructure/domains/shared/types/discovery-result.test.js +258 -0
  129. package/infrastructure/domains/shared/types/index.js +46 -0
  130. package/infrastructure/domains/shared/types/resource-ownership.js +108 -0
  131. package/infrastructure/domains/shared/types/resource-ownership.test.js +101 -0
  132. package/infrastructure/domains/shared/utilities/base-definition-factory.js +380 -0
  133. package/infrastructure/domains/shared/utilities/base-definition-factory.js.bak +338 -0
  134. package/infrastructure/domains/shared/utilities/base-definition-factory.test.js +248 -0
  135. package/infrastructure/domains/shared/utilities/handler-path-resolver.js +134 -0
  136. package/infrastructure/domains/shared/utilities/handler-path-resolver.test.js +268 -0
  137. package/infrastructure/domains/shared/utilities/prisma-layer-manager.js +55 -0
  138. package/infrastructure/domains/shared/utilities/prisma-layer-manager.test.js +138 -0
  139. package/infrastructure/{env-validator.js → domains/shared/validation/env-validator.js} +2 -1
  140. package/infrastructure/domains/shared/validation/env-validator.test.js +173 -0
  141. package/infrastructure/esbuild.config.js +53 -0
  142. package/infrastructure/infrastructure-composer.js +87 -0
  143. package/infrastructure/{serverless-template.test.js → infrastructure-composer.test.js} +115 -24
  144. package/infrastructure/scripts/build-prisma-layer.js +553 -0
  145. package/infrastructure/scripts/build-prisma-layer.test.js +102 -0
  146. package/infrastructure/{build-time-discovery.js → scripts/build-time-discovery.js} +80 -48
  147. package/infrastructure/{build-time-discovery.test.js → scripts/build-time-discovery.test.js} +5 -4
  148. package/layers/prisma/nodejs/package.json +8 -0
  149. package/management-ui/server/utils/cliIntegration.js +1 -1
  150. package/management-ui/server/utils/environment/awsParameterStore.js +29 -18
  151. package/package.json +11 -11
  152. package/frigg-cli/.eslintrc.js +0 -141
  153. package/frigg-cli/__tests__/unit/commands/build.test.js +0 -251
  154. package/frigg-cli/__tests__/unit/commands/db-setup.test.js +0 -548
  155. package/frigg-cli/__tests__/unit/commands/install.test.js +0 -400
  156. package/frigg-cli/__tests__/unit/commands/ui.test.js +0 -346
  157. package/frigg-cli/__tests__/unit/utils/database-validator.test.js +0 -366
  158. package/frigg-cli/__tests__/unit/utils/error-messages.test.js +0 -304
  159. package/frigg-cli/__tests__/unit/utils/prisma-runner.test.js +0 -486
  160. package/frigg-cli/__tests__/utils/mock-factory.js +0 -270
  161. package/frigg-cli/__tests__/utils/prisma-mock.js +0 -194
  162. package/frigg-cli/__tests__/utils/test-fixtures.js +0 -463
  163. package/frigg-cli/__tests__/utils/test-setup.js +0 -287
  164. package/frigg-cli/build-command/index.js +0 -65
  165. package/frigg-cli/db-setup-command/index.js +0 -193
  166. package/frigg-cli/deploy-command/index.js +0 -175
  167. package/frigg-cli/generate-command/__tests__/generate-command.test.js +0 -301
  168. package/frigg-cli/generate-command/azure-generator.js +0 -43
  169. package/frigg-cli/generate-command/gcp-generator.js +0 -47
  170. package/frigg-cli/generate-command/index.js +0 -332
  171. package/frigg-cli/generate-command/terraform-generator.js +0 -555
  172. package/frigg-cli/generate-iam-command.js +0 -118
  173. package/frigg-cli/index.js +0 -75
  174. package/frigg-cli/index.test.js +0 -158
  175. package/frigg-cli/init-command/backend-first-handler.js +0 -756
  176. package/frigg-cli/init-command/index.js +0 -93
  177. package/frigg-cli/init-command/template-handler.js +0 -143
  178. package/frigg-cli/install-command/backend-js.js +0 -33
  179. package/frigg-cli/install-command/commit-changes.js +0 -16
  180. package/frigg-cli/install-command/environment-variables.js +0 -127
  181. package/frigg-cli/install-command/environment-variables.test.js +0 -136
  182. package/frigg-cli/install-command/index.js +0 -54
  183. package/frigg-cli/install-command/install-package.js +0 -13
  184. package/frigg-cli/install-command/integration-file.js +0 -30
  185. package/frigg-cli/install-command/logger.js +0 -12
  186. package/frigg-cli/install-command/template.js +0 -90
  187. package/frigg-cli/install-command/validate-package.js +0 -75
  188. package/frigg-cli/jest.config.js +0 -124
  189. package/frigg-cli/package.json +0 -54
  190. package/frigg-cli/start-command/index.js +0 -149
  191. package/frigg-cli/start-command/start-command.test.js +0 -297
  192. package/frigg-cli/test/init-command.test.js +0 -180
  193. package/frigg-cli/test/npm-registry.test.js +0 -319
  194. package/frigg-cli/ui-command/index.js +0 -154
  195. package/frigg-cli/utils/app-resolver.js +0 -319
  196. package/frigg-cli/utils/backend-path.js +0 -25
  197. package/frigg-cli/utils/database-validator.js +0 -161
  198. package/frigg-cli/utils/error-messages.js +0 -257
  199. package/frigg-cli/utils/npm-registry.js +0 -167
  200. package/frigg-cli/utils/prisma-runner.js +0 -280
  201. package/frigg-cli/utils/process-manager.js +0 -199
  202. package/frigg-cli/utils/repo-detection.js +0 -405
  203. package/infrastructure/aws-discovery.js +0 -1176
  204. package/infrastructure/aws-discovery.test.js +0 -1220
  205. package/infrastructure/serverless-template.js +0 -2094
  206. /package/infrastructure/{WEBSOCKET-CONFIGURATION.md → docs/WEBSOCKET-CONFIGURATION.md} +0 -0
  207. /package/infrastructure/{GENERATE-IAM-DOCS.md → docs/generate-iam-command.md} +0 -0
  208. /package/infrastructure/{iam-generator.test.js → domains/security/iam-generator.test.js} +0 -0
  209. /package/infrastructure/{frigg-deployment-iam-stack.yaml → domains/security/templates/frigg-deployment-iam-stack.yaml} +0 -0
  210. /package/infrastructure/{iam-policy-basic.json → domains/security/templates/iam-policy-basic.json} +0 -0
  211. /package/infrastructure/{iam-policy-full.json → domains/security/templates/iam-policy-full.json} +0 -0
  212. /package/infrastructure/{run-discovery.js → scripts/run-discovery.js} +0 -0
@@ -0,0 +1,87 @@
1
+ /**
2
+ * Aurora Discovery Service
3
+ *
4
+ * Domain Service - Hexagonal Architecture
5
+ *
6
+ * Discovers Aurora/RDS database resources using the cloud provider adapter.
7
+ * Adds domain-specific validation and connection string generation.
8
+ */
9
+
10
+ class AuroraDiscovery {
11
+ /**
12
+ * @param {CloudProviderAdapter} provider - Cloud provider adapter instance
13
+ */
14
+ constructor(provider) {
15
+ this.provider = provider;
16
+ }
17
+
18
+ /**
19
+ * Discover Aurora/RDS database resources
20
+ *
21
+ * @param {Object} config - Discovery configuration
22
+ * @param {string} [config.databaseId] - Specific database cluster/instance ID
23
+ * @param {string} [config.serviceName] - Service name for filtering
24
+ * @param {string} [config.stage] - Deployment stage
25
+ * @returns {Promise<Object>} Discovered database resources
26
+ */
27
+ async discover(config) {
28
+ console.log('🔍 Discovering Aurora/RDS databases...');
29
+
30
+ try {
31
+ const rawResources = await this.provider.discoverDatabase(config);
32
+
33
+ const result = {
34
+ auroraClusterEndpoint: null,
35
+ auroraPort: null,
36
+ auroraEngine: null,
37
+ databaseEndpoint: null,
38
+ databasePort: null,
39
+ databaseEngine: null,
40
+ clusters: rawResources.clusters,
41
+ instances: rawResources.instances,
42
+ };
43
+
44
+ // Set discovered endpoint
45
+ if (rawResources.endpoint) {
46
+ result.auroraClusterEndpoint = rawResources.endpoint;
47
+ result.databaseEndpoint = rawResources.endpoint;
48
+ result.auroraPort = rawResources.port || 5432;
49
+ result.databasePort = rawResources.port || 5432;
50
+ result.auroraEngine = rawResources.engine || 'aurora-postgresql';
51
+ result.databaseEngine = rawResources.engine || 'aurora-postgresql';
52
+
53
+ // Capture security group IDs
54
+ if (rawResources.securityGroupIds && rawResources.securityGroupIds.length > 0) {
55
+ result.auroraSecurityGroupId = rawResources.securityGroupIds[0]; // Use first security group
56
+ console.log(` ✓ Found security group: ${result.auroraSecurityGroupId}`);
57
+ }
58
+
59
+ console.log(` ✓ Found database: ${result.auroraClusterEndpoint}:${result.auroraPort}`);
60
+ console.log(` ✓ Engine: ${result.auroraEngine}`);
61
+ } else {
62
+ console.log(' ℹ No database found');
63
+ }
64
+
65
+ // Look for associated secrets in Secrets Manager
66
+ // This would be discovered via the provider's discoverParameters method
67
+ // with includeSecrets flag if needed
68
+
69
+ return result;
70
+ } catch (error) {
71
+ console.error(' ✗ Database discovery failed:', error.message);
72
+ return {
73
+ auroraClusterEndpoint: null,
74
+ auroraPort: null,
75
+ auroraEngine: null,
76
+ databaseEndpoint: null,
77
+ databasePort: null,
78
+ databaseEngine: null,
79
+ };
80
+ }
81
+ }
82
+ }
83
+
84
+ module.exports = {
85
+ AuroraDiscovery,
86
+ };
87
+
@@ -0,0 +1,188 @@
1
+ /**
2
+ * Tests for Aurora Discovery Service
3
+ *
4
+ * Tests Aurora/RDS database discovery with mocked cloud provider
5
+ */
6
+
7
+ const { AuroraDiscovery } = require('./aurora-discovery');
8
+
9
+ describe('AuroraDiscovery', () => {
10
+ let mockProvider;
11
+ let auroraDiscovery;
12
+
13
+ beforeEach(() => {
14
+ mockProvider = {
15
+ discoverDatabase: jest.fn(),
16
+ getName: jest.fn().mockReturnValue('aws'),
17
+ };
18
+ auroraDiscovery = new AuroraDiscovery(mockProvider);
19
+ });
20
+
21
+ describe('discover()', () => {
22
+ it('should delegate to provider and transform results', async () => {
23
+ const mockProviderResponse = {
24
+ clusters: [
25
+ {
26
+ DBClusterIdentifier: 'aurora-cluster-1',
27
+ Endpoint: 'aurora-cluster-1.cluster-xyz.us-east-1.rds.amazonaws.com',
28
+ Port: 5432,
29
+ Engine: 'aurora-postgresql',
30
+ },
31
+ ],
32
+ instances: [],
33
+ endpoint: 'aurora-cluster-1.cluster-xyz.us-east-1.rds.amazonaws.com',
34
+ port: 5432,
35
+ engine: 'aurora-postgresql',
36
+ };
37
+
38
+ mockProvider.discoverDatabase.mockResolvedValue(mockProviderResponse);
39
+
40
+ const result = await auroraDiscovery.discover({});
41
+
42
+ expect(mockProvider.discoverDatabase).toHaveBeenCalledWith({});
43
+ expect(result.auroraClusterEndpoint).toBe('aurora-cluster-1.cluster-xyz.us-east-1.rds.amazonaws.com');
44
+ expect(result.databaseEndpoint).toBe('aurora-cluster-1.cluster-xyz.us-east-1.rds.amazonaws.com');
45
+ expect(result.auroraPort).toBe(5432);
46
+ expect(result.databasePort).toBe(5432);
47
+ expect(result.auroraEngine).toBe('aurora-postgresql');
48
+ expect(result.databaseEngine).toBe('aurora-postgresql');
49
+ });
50
+
51
+ it('should handle no database found', async () => {
52
+ mockProvider.discoverDatabase.mockResolvedValue({
53
+ clusters: [],
54
+ instances: [],
55
+ endpoint: null,
56
+ port: null,
57
+ engine: null,
58
+ });
59
+
60
+ const result = await auroraDiscovery.discover({});
61
+
62
+ expect(result.auroraClusterEndpoint).toBeNull();
63
+ expect(result.databaseEndpoint).toBeNull();
64
+ expect(result.auroraPort).toBeNull();
65
+ expect(result.databasePort).toBeNull();
66
+ });
67
+
68
+ it('should default to port 5432 if not specified', async () => {
69
+ mockProvider.discoverDatabase.mockResolvedValue({
70
+ clusters: [],
71
+ instances: [],
72
+ endpoint: 'db.example.com',
73
+ port: null,
74
+ engine: 'aurora-postgresql',
75
+ });
76
+
77
+ const result = await auroraDiscovery.discover({});
78
+
79
+ expect(result.auroraPort).toBe(5432);
80
+ expect(result.databasePort).toBe(5432);
81
+ });
82
+
83
+ it('should default to aurora-postgresql engine if not specified', async () => {
84
+ mockProvider.discoverDatabase.mockResolvedValue({
85
+ clusters: [],
86
+ instances: [],
87
+ endpoint: 'db.example.com',
88
+ port: 5432,
89
+ engine: null,
90
+ });
91
+
92
+ const result = await auroraDiscovery.discover({});
93
+
94
+ expect(result.auroraEngine).toBe('aurora-postgresql');
95
+ expect(result.databaseEngine).toBe('aurora-postgresql');
96
+ });
97
+
98
+ it('should handle MySQL Aurora', async () => {
99
+ mockProvider.discoverDatabase.mockResolvedValue({
100
+ clusters: [],
101
+ instances: [],
102
+ endpoint: 'mysql-cluster.example.com',
103
+ port: 3306,
104
+ engine: 'aurora-mysql',
105
+ });
106
+
107
+ const result = await auroraDiscovery.discover({});
108
+
109
+ expect(result.auroraPort).toBe(3306);
110
+ expect(result.auroraEngine).toBe('aurora-mysql');
111
+ });
112
+
113
+ it('should pass config to provider', async () => {
114
+ mockProvider.discoverDatabase.mockResolvedValue({
115
+ clusters: [],
116
+ instances: [],
117
+ endpoint: null,
118
+ port: null,
119
+ engine: null,
120
+ });
121
+
122
+ const config = {
123
+ databaseId: 'my-cluster',
124
+ serviceName: 'test-service',
125
+ stage: 'prod',
126
+ };
127
+
128
+ await auroraDiscovery.discover(config);
129
+
130
+ expect(mockProvider.discoverDatabase).toHaveBeenCalledWith(config);
131
+ });
132
+
133
+ it('should handle discovery errors gracefully', async () => {
134
+ mockProvider.discoverDatabase.mockRejectedValue(new Error('RDS API Error'));
135
+
136
+ const result = await auroraDiscovery.discover({});
137
+
138
+ expect(result.auroraClusterEndpoint).toBeNull();
139
+ expect(result.auroraPort).toBeNull();
140
+ expect(result.auroraEngine).toBeNull();
141
+ });
142
+
143
+ it('should preserve cluster and instance lists for reference', async () => {
144
+ const mockClusters = [
145
+ { DBClusterIdentifier: 'cluster-1', Endpoint: 'endpoint-1' },
146
+ { DBClusterIdentifier: 'cluster-2', Endpoint: 'endpoint-2' },
147
+ ];
148
+ const mockInstances = [
149
+ { DBInstanceIdentifier: 'instance-1' },
150
+ ];
151
+
152
+ mockProvider.discoverDatabase.mockResolvedValue({
153
+ clusters: mockClusters,
154
+ instances: mockInstances,
155
+ endpoint: 'endpoint-1',
156
+ port: 5432,
157
+ engine: 'aurora-postgresql',
158
+ });
159
+
160
+ const result = await auroraDiscovery.discover({});
161
+
162
+ expect(result.clusters).toEqual(mockClusters);
163
+ expect(result.instances).toEqual(mockInstances);
164
+ });
165
+
166
+ it('should handle RDS instances (non-Aurora)', async () => {
167
+ mockProvider.discoverDatabase.mockResolvedValue({
168
+ clusters: [],
169
+ instances: [
170
+ {
171
+ DBInstanceIdentifier: 'rds-instance',
172
+ Endpoint: { Address: 'rds.example.com', Port: 5432 },
173
+ Engine: 'postgres',
174
+ },
175
+ ],
176
+ endpoint: 'rds.example.com',
177
+ port: 5432,
178
+ engine: 'postgres',
179
+ });
180
+
181
+ const result = await auroraDiscovery.discover({});
182
+
183
+ expect(result.databaseEndpoint).toBe('rds.example.com');
184
+ expect(result.databaseEngine).toBe('postgres');
185
+ });
186
+ });
187
+ });
188
+
@@ -0,0 +1,210 @@
1
+ /**
2
+ * Aurora Resource Resolver
3
+ *
4
+ * Resolves ownership for Aurora PostgreSQL resources following the ownership-based architecture.
5
+ *
6
+ * Resources managed:
7
+ * - Aurora Cluster (RDS::DBCluster)
8
+ * - Aurora Instance (RDS::DBInstance)
9
+ * - DB Subnet Group (RDS::DBSubnetGroup)
10
+ * - DB Secret (SecretsManager::Secret)
11
+ *
12
+ * Ownership types:
13
+ * - STACK: Resource defined in our CloudFormation template (use Refs)
14
+ * - EXTERNAL: Resource outside our stack (use physical IDs)
15
+ * - AUTO: System decides based on discovery
16
+ */
17
+
18
+ const BaseResourceResolver = require('../shared/base-resolver');
19
+ const { ResourceOwnership } = require('../shared/types/resource-ownership');
20
+
21
+ class AuroraResourceResolver extends BaseResourceResolver {
22
+ /**
23
+ * Resolve Aurora Cluster ownership
24
+ * @param {Object} appDefinition - App definition
25
+ * @param {Object} discovery - Discovery result
26
+ * @returns {Object} Resource decision
27
+ */
28
+ resolveCluster(appDefinition, discovery) {
29
+ const userIntent = appDefinition.database?.postgres?.ownership?.cluster || 'auto';
30
+
31
+ // Explicit external - use provided cluster identifier
32
+ if (userIntent === 'external') {
33
+ this.requireExternalIds(
34
+ appDefinition.database?.postgres?.external?.clusterIdentifier,
35
+ 'clusterIdentifier'
36
+ );
37
+ return this.createExternalDecision(
38
+ appDefinition.database.postgres.external.clusterIdentifier,
39
+ 'User specified ownership=external for Aurora cluster'
40
+ );
41
+ }
42
+
43
+ // For stack or auto: check if cluster exists in stack
44
+ const inStack = this.findInStack('FriggAuroraCluster', discovery);
45
+
46
+ if (inStack) {
47
+ return this.createStackDecision(
48
+ inStack.physicalId,
49
+ 'Found FriggAuroraCluster in CloudFormation stack'
50
+ );
51
+ }
52
+
53
+ // Check for external cluster
54
+ const external = this.findExternal('AWS::RDS::DBCluster', discovery);
55
+ if (external && userIntent === 'auto') {
56
+ return this.createExternalDecision(
57
+ external.physicalId,
58
+ 'Found external Aurora cluster via discovery'
59
+ );
60
+ }
61
+
62
+ // Create new cluster in stack
63
+ return this.createStackDecision(
64
+ null,
65
+ 'No existing Aurora cluster - will create in stack'
66
+ );
67
+ }
68
+
69
+ /**
70
+ * Resolve Aurora Instance ownership
71
+ * @param {Object} appDefinition - App definition
72
+ * @param {Object} discovery - Discovery result
73
+ * @returns {Object} Resource decision
74
+ */
75
+ resolveInstance(appDefinition, discovery) {
76
+ const userIntent = appDefinition.database?.postgres?.ownership?.instance || 'auto';
77
+
78
+ // Explicit external
79
+ if (userIntent === 'external') {
80
+ this.requireExternalIds(
81
+ appDefinition.database?.postgres?.external?.instanceIdentifier,
82
+ 'instanceIdentifier'
83
+ );
84
+ return this.createExternalDecision(
85
+ appDefinition.database.postgres.external.instanceIdentifier,
86
+ 'User specified ownership=external for Aurora instance'
87
+ );
88
+ }
89
+
90
+ // Check if instance exists in stack
91
+ const inStack = this.findInStack('FriggAuroraInstance', discovery);
92
+
93
+ if (inStack) {
94
+ return this.createStackDecision(
95
+ inStack.physicalId,
96
+ 'Found FriggAuroraInstance in CloudFormation stack'
97
+ );
98
+ }
99
+
100
+ // Check for external instance
101
+ const external = this.findExternal('AWS::RDS::DBInstance', discovery);
102
+ if (external && userIntent === 'auto') {
103
+ return this.createExternalDecision(
104
+ external.physicalId,
105
+ 'Found external Aurora instance via discovery'
106
+ );
107
+ }
108
+
109
+ // Create new instance in stack
110
+ return this.createStackDecision(
111
+ null,
112
+ 'No existing Aurora instance - will create in stack'
113
+ );
114
+ }
115
+
116
+ /**
117
+ * Resolve DB Subnet Group ownership
118
+ * @param {Object} appDefinition - App definition
119
+ * @param {Object} discovery - Discovery result
120
+ * @returns {Object} Resource decision
121
+ */
122
+ resolveSubnetGroup(appDefinition, discovery) {
123
+ const userIntent = appDefinition.database?.postgres?.ownership?.subnetGroup || 'auto';
124
+
125
+ // Explicit external
126
+ if (userIntent === 'external') {
127
+ this.requireExternalIds(
128
+ appDefinition.database?.postgres?.external?.subnetGroupName,
129
+ 'subnetGroupName'
130
+ );
131
+ return this.createExternalDecision(
132
+ appDefinition.database.postgres.external.subnetGroupName,
133
+ 'User specified ownership=external for DB subnet group'
134
+ );
135
+ }
136
+
137
+ // Check if subnet group exists in stack
138
+ const inStack = this.findInStack('FriggDBSubnetGroup', discovery);
139
+
140
+ if (inStack) {
141
+ return this.createStackDecision(
142
+ inStack.physicalId,
143
+ 'Found FriggDBSubnetGroup in CloudFormation stack'
144
+ );
145
+ }
146
+
147
+ // For subnet groups, always create in stack (they're cheap and cluster-specific)
148
+ return this.createStackDecision(
149
+ null,
150
+ 'No existing DB subnet group - will create in stack'
151
+ );
152
+ }
153
+
154
+ /**
155
+ * Resolve DB Secret ownership
156
+ * @param {Object} appDefinition - App definition
157
+ * @param {Object} discovery - Discovery result
158
+ * @returns {Object} Resource decision
159
+ */
160
+ resolveSecret(appDefinition, discovery) {
161
+ const userIntent = appDefinition.database?.postgres?.ownership?.secret || 'auto';
162
+
163
+ // Explicit external - use provided secret ARN
164
+ if (userIntent === 'external') {
165
+ this.requireExternalIds(
166
+ appDefinition.database?.postgres?.external?.secretArn,
167
+ 'secretArn'
168
+ );
169
+ return this.createExternalDecision(
170
+ appDefinition.database.postgres.external.secretArn,
171
+ 'User specified ownership=external for DB secret'
172
+ );
173
+ }
174
+
175
+ // Check if secret exists in stack
176
+ const inStack = this.findInStack('FriggDBSecret', discovery);
177
+
178
+ if (inStack) {
179
+ return this.createStackDecision(
180
+ inStack.physicalId,
181
+ 'Found FriggDBSecret in CloudFormation stack'
182
+ );
183
+ }
184
+
185
+ // For secrets tied to the cluster, always create in stack
186
+ return this.createStackDecision(
187
+ null,
188
+ 'No existing DB secret - will create in stack'
189
+ );
190
+ }
191
+
192
+ /**
193
+ * Resolve all Aurora resources at once
194
+ * Convenience method that returns decisions for all Aurora resources
195
+ *
196
+ * @param {Object} appDefinition - App definition
197
+ * @param {Object} discovery - Discovery result
198
+ * @returns {Object} Decisions for all Aurora resources
199
+ */
200
+ resolveAll(appDefinition, discovery) {
201
+ return {
202
+ cluster: this.resolveCluster(appDefinition, discovery),
203
+ instance: this.resolveInstance(appDefinition, discovery),
204
+ subnetGroup: this.resolveSubnetGroup(appDefinition, discovery),
205
+ secret: this.resolveSecret(appDefinition, discovery)
206
+ };
207
+ }
208
+ }
209
+
210
+ module.exports = AuroraResourceResolver;