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

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 +695 -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,189 @@
1
+ /**
2
+ * Tests for SSM Builder
3
+ *
4
+ * Tests SSM Parameter Store configuration
5
+ */
6
+
7
+ const { SsmBuilder } = require('./ssm-builder');
8
+ const { ValidationResult } = require('../shared/base-builder');
9
+
10
+ describe('SsmBuilder', () => {
11
+ let ssmBuilder;
12
+
13
+ beforeEach(() => {
14
+ ssmBuilder = new SsmBuilder();
15
+ delete process.env.FRIGG_SKIP_AWS_DISCOVERY;
16
+ });
17
+
18
+ afterEach(() => {
19
+ delete process.env.FRIGG_SKIP_AWS_DISCOVERY;
20
+ });
21
+
22
+ describe('shouldExecute()', () => {
23
+ it('should return true when SSM is enabled', () => {
24
+ const appDefinition = {
25
+ ssm: { enable: true },
26
+ };
27
+
28
+ expect(ssmBuilder.shouldExecute(appDefinition)).toBe(true);
29
+ });
30
+
31
+ it('should return false when SSM is disabled', () => {
32
+ const appDefinition = {
33
+ ssm: { enable: false },
34
+ };
35
+
36
+ expect(ssmBuilder.shouldExecute(appDefinition)).toBe(false);
37
+ });
38
+
39
+ it('should return false when SSM is not defined', () => {
40
+ const appDefinition = {};
41
+
42
+ expect(ssmBuilder.shouldExecute(appDefinition)).toBe(false);
43
+ });
44
+
45
+ it('should return false when FRIGG_SKIP_AWS_DISCOVERY is set (local mode)', () => {
46
+ process.env.FRIGG_SKIP_AWS_DISCOVERY = 'true';
47
+ const appDefinition = {
48
+ ssm: { enable: true },
49
+ };
50
+
51
+ expect(ssmBuilder.shouldExecute(appDefinition)).toBe(false);
52
+ });
53
+ });
54
+
55
+ describe('validate()', () => {
56
+ it('should pass validation for valid SSM config', () => {
57
+ const appDefinition = {
58
+ ssm: {
59
+ enable: true,
60
+ },
61
+ };
62
+
63
+ const result = ssmBuilder.validate(appDefinition);
64
+
65
+ expect(result).toBeInstanceOf(ValidationResult);
66
+ expect(result.valid).toBe(true);
67
+ expect(result.errors).toEqual([]);
68
+ });
69
+
70
+ it('should pass validation with parameters object', () => {
71
+ const appDefinition = {
72
+ ssm: {
73
+ enable: true,
74
+ parameters: {
75
+ DATABASE_URL: '/my-app/database-url',
76
+ API_KEY: '/my-app/api-key',
77
+ },
78
+ },
79
+ };
80
+
81
+ const result = ssmBuilder.validate(appDefinition);
82
+
83
+ expect(result.valid).toBe(true);
84
+ });
85
+
86
+ it('should error if SSM configuration is missing', () => {
87
+ const appDefinition = {};
88
+
89
+ const result = ssmBuilder.validate(appDefinition);
90
+
91
+ expect(result.valid).toBe(false);
92
+ expect(result.errors).toContain('SSM configuration is missing');
93
+ });
94
+
95
+ it('should error if parameters is not an object', () => {
96
+ const appDefinition = {
97
+ ssm: {
98
+ enable: true,
99
+ parameters: 'invalid',
100
+ },
101
+ };
102
+
103
+ const result = ssmBuilder.validate(appDefinition);
104
+
105
+ expect(result.valid).toBe(false);
106
+ expect(result.errors.some(e => e.includes('ssm.parameters must be an object'))).toBe(true);
107
+ });
108
+
109
+ it('should error if parameters is an array', () => {
110
+ const appDefinition = {
111
+ ssm: {
112
+ enable: true,
113
+ parameters: ['param1', 'param2'],
114
+ },
115
+ };
116
+
117
+ const result = ssmBuilder.validate(appDefinition);
118
+
119
+ expect(result.valid).toBe(false);
120
+ expect(result.errors.some(e => e.includes('ssm.parameters must be an object'))).toBe(true);
121
+ });
122
+ });
123
+
124
+ describe('build()', () => {
125
+ it('should add IAM permissions for SSM operations', async () => {
126
+ const appDefinition = {
127
+ ssm: {
128
+ enable: true,
129
+ },
130
+ };
131
+
132
+ const result = await ssmBuilder.build(appDefinition, {});
133
+
134
+ expect(result.iamStatements).toHaveLength(1);
135
+ expect(result.iamStatements[0]).toEqual({
136
+ Effect: 'Allow',
137
+ Action: [
138
+ 'ssm:GetParameter',
139
+ 'ssm:GetParameters',
140
+ 'ssm:GetParametersByPath',
141
+ ],
142
+ Resource: {
143
+ 'Fn::Sub': 'arn:aws:ssm:${AWS::Region}:${AWS::AccountId}:parameter/*',
144
+ },
145
+ });
146
+ });
147
+
148
+ it('should return environment object even if empty', async () => {
149
+ const appDefinition = {
150
+ ssm: {
151
+ enable: true,
152
+ },
153
+ };
154
+
155
+ const result = await ssmBuilder.build(appDefinition, {});
156
+
157
+ expect(result.environment).toBeDefined();
158
+ expect(typeof result.environment).toBe('object');
159
+ });
160
+
161
+ it('should not depend on discovered resources', async () => {
162
+ const appDefinition = {
163
+ ssm: {
164
+ enable: true,
165
+ },
166
+ };
167
+
168
+ const result1 = await ssmBuilder.build(appDefinition, {});
169
+ const result2 = await ssmBuilder.build(appDefinition, { someResource: 'value' });
170
+
171
+ expect(result1.iamStatements).toEqual(result2.iamStatements);
172
+ });
173
+ });
174
+
175
+ describe('getDependencies()', () => {
176
+ it('should have no dependencies', () => {
177
+ const deps = ssmBuilder.getDependencies();
178
+
179
+ expect(deps).toEqual([]);
180
+ });
181
+ });
182
+
183
+ describe('getName()', () => {
184
+ it('should return SsmBuilder', () => {
185
+ expect(ssmBuilder.getName()).toBe('SsmBuilder');
186
+ });
187
+ });
188
+ });
189
+
@@ -0,0 +1,84 @@
1
+ /**
2
+ * SSM Discovery Service
3
+ *
4
+ * Domain Service - Hexagonal Architecture
5
+ *
6
+ * Discovers SSM Parameter Store and Secrets Manager resources
7
+ * using the cloud provider adapter.
8
+ */
9
+
10
+ class SsmDiscovery {
11
+ /**
12
+ * @param {CloudProviderAdapter} provider - Cloud provider adapter instance
13
+ */
14
+ constructor(provider) {
15
+ this.provider = provider;
16
+ }
17
+
18
+ /**
19
+ * Discover SSM parameters and secrets
20
+ *
21
+ * @param {Object} config - Discovery configuration
22
+ * @param {string} [config.parameterPath] - SSM parameter path prefix
23
+ * @param {string} [config.serviceName] - Service name for filtering
24
+ * @param {string} [config.stage] - Deployment stage
25
+ * @param {boolean} [config.includeSecrets] - Whether to include Secrets Manager
26
+ * @returns {Promise<Object>} Discovered parameter resources
27
+ */
28
+ async discover(config) {
29
+ console.log('🔍 Discovering SSM parameters...');
30
+
31
+ try {
32
+ // Build parameter path if not provided
33
+ if (!config.parameterPath && config.serviceName && config.stage) {
34
+ config.parameterPath = `/${config.serviceName}/${config.stage}`;
35
+ }
36
+
37
+ const rawResources = await this.provider.discoverParameters({
38
+ ...config,
39
+ includeSecrets: config.includeSecrets !== false,
40
+ });
41
+
42
+ const result = {
43
+ parameters: rawResources.parameters || [],
44
+ secrets: rawResources.secrets || [],
45
+ parameterPath: config.parameterPath,
46
+ };
47
+
48
+ // Find database secret if exists
49
+ if (result.secrets.length > 0) {
50
+ const dbSecret = result.secrets.find(
51
+ s => s.Name?.includes('database') || s.Name?.includes('rds')
52
+ );
53
+ if (dbSecret) {
54
+ result.databaseSecretArn = dbSecret.ARN;
55
+ result.databaseSecretName = dbSecret.Name;
56
+ }
57
+ }
58
+
59
+ if (result.parameters.length > 0) {
60
+ console.log(` ✓ Found ${result.parameters.length} SSM parameters`);
61
+ }
62
+ if (result.secrets.length > 0) {
63
+ console.log(` ✓ Found ${result.secrets.length} secrets`);
64
+ }
65
+ if (!result.parameters.length && !result.secrets.length) {
66
+ console.log(' ℹ No parameters or secrets found');
67
+ }
68
+
69
+ return result;
70
+ } catch (error) {
71
+ console.error(' ✗ SSM discovery failed:', error.message);
72
+ return {
73
+ parameters: [],
74
+ secrets: [],
75
+ parameterPath: config.parameterPath,
76
+ };
77
+ }
78
+ }
79
+ }
80
+
81
+ module.exports = {
82
+ SsmDiscovery,
83
+ };
84
+
@@ -0,0 +1,210 @@
1
+ /**
2
+ * Tests for SSM Discovery Service
3
+ *
4
+ * Tests SSM Parameter Store and Secrets Manager discovery with mocked provider
5
+ */
6
+
7
+ const { SsmDiscovery } = require('./ssm-discovery');
8
+
9
+ describe('SsmDiscovery', () => {
10
+ let mockProvider;
11
+ let ssmDiscovery;
12
+
13
+ beforeEach(() => {
14
+ mockProvider = {
15
+ discoverParameters: jest.fn(),
16
+ getName: jest.fn().mockReturnValue('aws'),
17
+ };
18
+ ssmDiscovery = new SsmDiscovery(mockProvider);
19
+ });
20
+
21
+ describe('discover()', () => {
22
+ it('should delegate to provider and transform results', async () => {
23
+ const mockProviderResponse = {
24
+ parameters: [
25
+ {
26
+ Name: '/my-service/prod/API_KEY',
27
+ Value: 'encrypted-value',
28
+ Type: 'SecureString',
29
+ },
30
+ {
31
+ Name: '/my-service/prod/DATABASE_URL',
32
+ Value: 'postgres://...',
33
+ Type: 'SecureString',
34
+ },
35
+ ],
36
+ secrets: [
37
+ {
38
+ Name: 'my-service/database-credentials',
39
+ ARN: 'arn:aws:secretsmanager:us-east-1:123456:secret:my-service/database-credentials',
40
+ },
41
+ ],
42
+ };
43
+
44
+ mockProvider.discoverParameters.mockResolvedValue(mockProviderResponse);
45
+
46
+ const result = await ssmDiscovery.discover({
47
+ serviceName: 'my-service',
48
+ stage: 'prod',
49
+ });
50
+
51
+ expect(mockProvider.discoverParameters).toHaveBeenCalled();
52
+ expect(result.parameters).toHaveLength(2);
53
+ expect(result.secrets).toHaveLength(1);
54
+ expect(result.parameterPath).toBe('/my-service/prod');
55
+ });
56
+
57
+ it('should build parameter path from serviceName and stage', async () => {
58
+ mockProvider.discoverParameters.mockResolvedValue({
59
+ parameters: [],
60
+ secrets: [],
61
+ });
62
+
63
+ await ssmDiscovery.discover({
64
+ serviceName: 'test-app',
65
+ stage: 'dev',
66
+ });
67
+
68
+ expect(mockProvider.discoverParameters).toHaveBeenCalledWith(
69
+ expect.objectContaining({
70
+ parameterPath: '/test-app/dev',
71
+ includeSecrets: true,
72
+ })
73
+ );
74
+ });
75
+
76
+ it('should use provided parameterPath if specified', async () => {
77
+ mockProvider.discoverParameters.mockResolvedValue({
78
+ parameters: [],
79
+ secrets: [],
80
+ });
81
+
82
+ await ssmDiscovery.discover({
83
+ parameterPath: '/custom/path',
84
+ });
85
+
86
+ expect(mockProvider.discoverParameters).toHaveBeenCalledWith(
87
+ expect.objectContaining({
88
+ parameterPath: '/custom/path',
89
+ })
90
+ );
91
+ });
92
+
93
+ it('should handle no parameters found', async () => {
94
+ mockProvider.discoverParameters.mockResolvedValue({
95
+ parameters: [],
96
+ secrets: [],
97
+ });
98
+
99
+ const result = await ssmDiscovery.discover({});
100
+
101
+ expect(result.parameters).toEqual([]);
102
+ expect(result.secrets).toEqual([]);
103
+ });
104
+
105
+ it('should find database secret if exists', async () => {
106
+ mockProvider.discoverParameters.mockResolvedValue({
107
+ parameters: [],
108
+ secrets: [
109
+ {
110
+ Name: 'my-app/config',
111
+ ARN: 'arn:aws:secretsmanager:us-east-1:123456:secret:my-app/config',
112
+ },
113
+ {
114
+ Name: 'my-app/database-secret',
115
+ ARN: 'arn:aws:secretsmanager:us-east-1:123456:secret:my-app/database-secret',
116
+ },
117
+ ],
118
+ });
119
+
120
+ const result = await ssmDiscovery.discover({});
121
+
122
+ expect(result.databaseSecretArn).toBe('arn:aws:secretsmanager:us-east-1:123456:secret:my-app/database-secret');
123
+ expect(result.databaseSecretName).toBe('my-app/database-secret');
124
+ });
125
+
126
+ it('should find RDS secret if exists', async () => {
127
+ mockProvider.discoverParameters.mockResolvedValue({
128
+ parameters: [],
129
+ secrets: [
130
+ {
131
+ Name: 'rds/postgres/credentials',
132
+ ARN: 'arn:aws:secretsmanager:us-east-1:123456:secret:rds/postgres/credentials',
133
+ },
134
+ ],
135
+ });
136
+
137
+ const result = await ssmDiscovery.discover({});
138
+
139
+ expect(result.databaseSecretArn).toBe('arn:aws:secretsmanager:us-east-1:123456:secret:rds/postgres/credentials');
140
+ expect(result.databaseSecretName).toBe('rds/postgres/credentials');
141
+ });
142
+
143
+ it('should handle includeSecrets flag', async () => {
144
+ mockProvider.discoverParameters.mockResolvedValue({
145
+ parameters: [],
146
+ secrets: [],
147
+ });
148
+
149
+ await ssmDiscovery.discover({
150
+ includeSecrets: false,
151
+ });
152
+
153
+ expect(mockProvider.discoverParameters).toHaveBeenCalledWith(
154
+ expect.objectContaining({
155
+ includeSecrets: false,
156
+ })
157
+ );
158
+ });
159
+
160
+ it('should default includeSecrets to true', async () => {
161
+ mockProvider.discoverParameters.mockResolvedValue({
162
+ parameters: [],
163
+ secrets: [],
164
+ });
165
+
166
+ await ssmDiscovery.discover({});
167
+
168
+ expect(mockProvider.discoverParameters).toHaveBeenCalledWith(
169
+ expect.objectContaining({
170
+ includeSecrets: true,
171
+ })
172
+ );
173
+ });
174
+
175
+ it('should handle discovery errors gracefully', async () => {
176
+ mockProvider.discoverParameters.mockRejectedValue(new Error('SSM API Error'));
177
+
178
+ const result = await ssmDiscovery.discover({});
179
+
180
+ expect(result.parameters).toEqual([]);
181
+ expect(result.secrets).toEqual([]);
182
+ });
183
+
184
+ it('should preserve parameterPath in result', async () => {
185
+ mockProvider.discoverParameters.mockResolvedValue({
186
+ parameters: [],
187
+ secrets: [],
188
+ });
189
+
190
+ const result = await ssmDiscovery.discover({
191
+ parameterPath: '/my-service/staging',
192
+ });
193
+
194
+ expect(result.parameterPath).toBe('/my-service/staging');
195
+ });
196
+
197
+ it('should handle null/undefined parameters and secrets', async () => {
198
+ mockProvider.discoverParameters.mockResolvedValue({
199
+ parameters: null,
200
+ secrets: undefined,
201
+ });
202
+
203
+ const result = await ssmDiscovery.discover({});
204
+
205
+ expect(result.parameters).toEqual([]);
206
+ expect(result.secrets).toEqual([]);
207
+ });
208
+ });
209
+ });
210
+
@@ -781,7 +781,7 @@ function getFeatureSummary(appDefinition) {
781
781
  * @returns {Object} Basic IAM policy document
782
782
  */
783
783
  function generateBasicIAMPolicy() {
784
- const basicPolicyPath = path.join(__dirname, 'iam-policy-basic.json');
784
+ const basicPolicyPath = path.join(__dirname, 'templates/iam-policy-basic.json');
785
785
  return require(basicPolicyPath);
786
786
  }
787
787
 
@@ -790,7 +790,7 @@ function generateBasicIAMPolicy() {
790
790
  * @returns {Object} Full IAM policy document
791
791
  */
792
792
  function generateFullIAMPolicy() {
793
- const fullPolicyPath = path.join(__dirname, 'iam-policy-full.json');
793
+ const fullPolicyPath = path.join(__dirname, 'templates/iam-policy-full.json');
794
794
  return require(fullPolicyPath);
795
795
  }
796
796