@friggframework/devtools 2.0.0-next.44 → 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.
- package/infrastructure/ARCHITECTURE.md +487 -0
- package/infrastructure/HEALTH.md +468 -0
- package/infrastructure/README.md +51 -0
- package/infrastructure/__tests__/postgres-config.test.js +914 -0
- package/infrastructure/__tests__/template-generation.test.js +687 -0
- package/infrastructure/create-frigg-infrastructure.js +1 -1
- package/infrastructure/docs/POSTGRES-CONFIGURATION.md +630 -0
- package/infrastructure/{DEPLOYMENT-INSTRUCTIONS.md → docs/deployment-instructions.md} +3 -3
- package/infrastructure/{IAM-POLICY-TEMPLATES.md → docs/iam-policy-templates.md} +9 -10
- package/infrastructure/domains/database/aurora-builder.js +809 -0
- package/infrastructure/domains/database/aurora-builder.test.js +950 -0
- package/infrastructure/domains/database/aurora-discovery.js +87 -0
- package/infrastructure/domains/database/aurora-discovery.test.js +188 -0
- package/infrastructure/domains/database/aurora-resolver.js +210 -0
- package/infrastructure/domains/database/aurora-resolver.test.js +347 -0
- package/infrastructure/domains/database/migration-builder.js +633 -0
- package/infrastructure/domains/database/migration-builder.test.js +294 -0
- package/infrastructure/domains/database/migration-resolver.js +163 -0
- package/infrastructure/domains/database/migration-resolver.test.js +337 -0
- package/infrastructure/domains/health/application/ports/IPropertyReconciler.js +164 -0
- package/infrastructure/domains/health/application/ports/IResourceDetector.js +129 -0
- package/infrastructure/domains/health/application/ports/IResourceImporter.js +142 -0
- package/infrastructure/domains/health/application/ports/IStackRepository.js +131 -0
- package/infrastructure/domains/health/application/ports/index.js +26 -0
- package/infrastructure/domains/health/application/use-cases/__tests__/execute-resource-import-use-case.test.js +679 -0
- package/infrastructure/domains/health/application/use-cases/__tests__/mismatch-analyzer-method-name.test.js +167 -0
- package/infrastructure/domains/health/application/use-cases/__tests__/repair-via-import-use-case.test.js +1130 -0
- package/infrastructure/domains/health/application/use-cases/execute-resource-import-use-case.js +221 -0
- package/infrastructure/domains/health/application/use-cases/reconcile-properties-use-case.js +152 -0
- package/infrastructure/domains/health/application/use-cases/reconcile-properties-use-case.test.js +343 -0
- package/infrastructure/domains/health/application/use-cases/repair-via-import-use-case.js +535 -0
- package/infrastructure/domains/health/application/use-cases/repair-via-import-use-case.test.js +376 -0
- package/infrastructure/domains/health/application/use-cases/run-health-check-use-case.js +213 -0
- package/infrastructure/domains/health/application/use-cases/run-health-check-use-case.test.js +441 -0
- package/infrastructure/domains/health/docs/ACME-DEV-DRIFT-ANALYSIS.md +267 -0
- package/infrastructure/domains/health/docs/BUILD-VS-DEPLOYED-TEMPLATE-ANALYSIS.md +324 -0
- package/infrastructure/domains/health/docs/ORPHAN-DETECTION-ANALYSIS.md +386 -0
- package/infrastructure/domains/health/docs/SPEC-CLEANUP-COMMAND.md +1419 -0
- package/infrastructure/domains/health/docs/TDD-IMPLEMENTATION-SUMMARY.md +391 -0
- package/infrastructure/domains/health/docs/TEMPLATE-COMPARISON-IMPLEMENTATION.md +551 -0
- package/infrastructure/domains/health/domain/entities/issue.js +299 -0
- package/infrastructure/domains/health/domain/entities/issue.test.js +528 -0
- package/infrastructure/domains/health/domain/entities/property-mismatch.js +108 -0
- package/infrastructure/domains/health/domain/entities/property-mismatch.test.js +275 -0
- package/infrastructure/domains/health/domain/entities/resource.js +159 -0
- package/infrastructure/domains/health/domain/entities/resource.test.js +432 -0
- package/infrastructure/domains/health/domain/entities/stack-health-report.js +306 -0
- package/infrastructure/domains/health/domain/entities/stack-health-report.test.js +601 -0
- package/infrastructure/domains/health/domain/services/__tests__/health-score-percentage-based.test.js +380 -0
- package/infrastructure/domains/health/domain/services/__tests__/import-progress-monitor.test.js +971 -0
- package/infrastructure/domains/health/domain/services/__tests__/import-template-generator.test.js +1150 -0
- package/infrastructure/domains/health/domain/services/__tests__/logical-id-mapper.test.js +672 -0
- package/infrastructure/domains/health/domain/services/__tests__/template-parser.test.js +496 -0
- package/infrastructure/domains/health/domain/services/__tests__/update-progress-monitor.test.js +419 -0
- package/infrastructure/domains/health/domain/services/health-score-calculator.js +248 -0
- package/infrastructure/domains/health/domain/services/health-score-calculator.test.js +504 -0
- package/infrastructure/domains/health/domain/services/import-progress-monitor.js +195 -0
- package/infrastructure/domains/health/domain/services/import-template-generator.js +435 -0
- package/infrastructure/domains/health/domain/services/logical-id-mapper.js +345 -0
- package/infrastructure/domains/health/domain/services/mismatch-analyzer.js +234 -0
- package/infrastructure/domains/health/domain/services/mismatch-analyzer.test.js +431 -0
- package/infrastructure/domains/health/domain/services/property-mutability-config.js +382 -0
- package/infrastructure/domains/health/domain/services/template-parser.js +245 -0
- package/infrastructure/domains/health/domain/services/update-progress-monitor.js +192 -0
- package/infrastructure/domains/health/domain/value-objects/health-score.js +138 -0
- package/infrastructure/domains/health/domain/value-objects/health-score.test.js +267 -0
- package/infrastructure/domains/health/domain/value-objects/property-mutability.js +161 -0
- package/infrastructure/domains/health/domain/value-objects/property-mutability.test.js +198 -0
- package/infrastructure/domains/health/domain/value-objects/resource-state.js +167 -0
- package/infrastructure/domains/health/domain/value-objects/resource-state.test.js +196 -0
- package/infrastructure/domains/health/domain/value-objects/stack-identifier.js +192 -0
- package/infrastructure/domains/health/domain/value-objects/stack-identifier.test.js +262 -0
- package/infrastructure/domains/health/infrastructure/adapters/__tests__/orphan-detection-cfn-tagged.test.js +312 -0
- package/infrastructure/domains/health/infrastructure/adapters/__tests__/orphan-detection-multi-stack.test.js +367 -0
- package/infrastructure/domains/health/infrastructure/adapters/__tests__/orphan-detection-relationship-analysis.test.js +432 -0
- package/infrastructure/domains/health/infrastructure/adapters/aws-property-reconciler.js +784 -0
- package/infrastructure/domains/health/infrastructure/adapters/aws-property-reconciler.test.js +1133 -0
- package/infrastructure/domains/health/infrastructure/adapters/aws-resource-detector.js +565 -0
- package/infrastructure/domains/health/infrastructure/adapters/aws-resource-detector.test.js +554 -0
- package/infrastructure/domains/health/infrastructure/adapters/aws-resource-importer.js +318 -0
- package/infrastructure/domains/health/infrastructure/adapters/aws-resource-importer.test.js +398 -0
- package/infrastructure/domains/health/infrastructure/adapters/aws-stack-repository.js +777 -0
- package/infrastructure/domains/health/infrastructure/adapters/aws-stack-repository.test.js +580 -0
- package/infrastructure/domains/integration/integration-builder.js +397 -0
- package/infrastructure/domains/integration/integration-builder.test.js +593 -0
- package/infrastructure/domains/integration/integration-resolver.js +170 -0
- package/infrastructure/domains/integration/integration-resolver.test.js +369 -0
- package/infrastructure/domains/integration/websocket-builder.js +69 -0
- package/infrastructure/domains/integration/websocket-builder.test.js +195 -0
- package/infrastructure/domains/networking/vpc-builder.js +1829 -0
- package/infrastructure/domains/networking/vpc-builder.test.js +1262 -0
- package/infrastructure/domains/networking/vpc-discovery.js +177 -0
- package/infrastructure/domains/networking/vpc-discovery.test.js +350 -0
- package/infrastructure/domains/networking/vpc-resolver.js +324 -0
- package/infrastructure/domains/networking/vpc-resolver.test.js +501 -0
- package/infrastructure/domains/parameters/ssm-builder.js +79 -0
- package/infrastructure/domains/parameters/ssm-builder.test.js +189 -0
- package/infrastructure/domains/parameters/ssm-discovery.js +84 -0
- package/infrastructure/domains/parameters/ssm-discovery.test.js +210 -0
- package/infrastructure/{iam-generator.js → domains/security/iam-generator.js} +2 -2
- package/infrastructure/domains/security/kms-builder.js +366 -0
- package/infrastructure/domains/security/kms-builder.test.js +374 -0
- package/infrastructure/domains/security/kms-discovery.js +80 -0
- package/infrastructure/domains/security/kms-discovery.test.js +177 -0
- package/infrastructure/domains/security/kms-resolver.js +96 -0
- package/infrastructure/domains/security/kms-resolver.test.js +216 -0
- package/infrastructure/domains/shared/base-builder.js +112 -0
- package/infrastructure/domains/shared/base-resolver.js +186 -0
- package/infrastructure/domains/shared/base-resolver.test.js +305 -0
- package/infrastructure/domains/shared/builder-orchestrator.js +212 -0
- package/infrastructure/domains/shared/builder-orchestrator.test.js +213 -0
- package/infrastructure/domains/shared/cloudformation-discovery-v2.js +334 -0
- package/infrastructure/domains/shared/cloudformation-discovery.js +375 -0
- package/infrastructure/domains/shared/cloudformation-discovery.test.js +590 -0
- package/infrastructure/domains/shared/environment-builder.js +119 -0
- package/infrastructure/domains/shared/environment-builder.test.js +247 -0
- package/infrastructure/domains/shared/providers/aws-provider-adapter.js +544 -0
- package/infrastructure/domains/shared/providers/aws-provider-adapter.test.js +377 -0
- package/infrastructure/domains/shared/providers/azure-provider-adapter.stub.js +93 -0
- package/infrastructure/domains/shared/providers/cloud-provider-adapter.js +136 -0
- package/infrastructure/domains/shared/providers/gcp-provider-adapter.stub.js +82 -0
- package/infrastructure/domains/shared/providers/provider-factory.js +108 -0
- package/infrastructure/domains/shared/providers/provider-factory.test.js +170 -0
- package/infrastructure/domains/shared/resource-discovery.js +192 -0
- package/infrastructure/domains/shared/resource-discovery.test.js +552 -0
- package/infrastructure/domains/shared/types/app-definition.js +205 -0
- package/infrastructure/domains/shared/types/discovery-result.js +106 -0
- package/infrastructure/domains/shared/types/discovery-result.test.js +258 -0
- package/infrastructure/domains/shared/types/index.js +46 -0
- package/infrastructure/domains/shared/types/resource-ownership.js +108 -0
- package/infrastructure/domains/shared/types/resource-ownership.test.js +101 -0
- package/infrastructure/domains/shared/utilities/base-definition-factory.js +380 -0
- package/infrastructure/domains/shared/utilities/base-definition-factory.js.bak +338 -0
- package/infrastructure/domains/shared/utilities/base-definition-factory.test.js +248 -0
- package/infrastructure/domains/shared/utilities/handler-path-resolver.js +134 -0
- package/infrastructure/domains/shared/utilities/handler-path-resolver.test.js +268 -0
- package/infrastructure/domains/shared/utilities/prisma-layer-manager.js +55 -0
- package/infrastructure/domains/shared/utilities/prisma-layer-manager.test.js +138 -0
- package/infrastructure/{env-validator.js → domains/shared/validation/env-validator.js} +2 -1
- package/infrastructure/domains/shared/validation/env-validator.test.js +173 -0
- package/infrastructure/esbuild.config.js +53 -0
- package/infrastructure/infrastructure-composer.js +87 -0
- package/infrastructure/{serverless-template.test.js → infrastructure-composer.test.js} +115 -24
- package/infrastructure/scripts/build-prisma-layer.js +553 -0
- package/infrastructure/scripts/build-prisma-layer.test.js +102 -0
- package/infrastructure/{build-time-discovery.js → scripts/build-time-discovery.js} +80 -48
- package/infrastructure/{build-time-discovery.test.js → scripts/build-time-discovery.test.js} +5 -4
- package/layers/prisma/nodejs/package.json +8 -0
- package/management-ui/server/utils/cliIntegration.js +1 -1
- package/management-ui/server/utils/environment/awsParameterStore.js +29 -18
- package/package.json +11 -11
- package/frigg-cli/.eslintrc.js +0 -141
- package/frigg-cli/__tests__/unit/commands/build.test.js +0 -251
- package/frigg-cli/__tests__/unit/commands/db-setup.test.js +0 -548
- package/frigg-cli/__tests__/unit/commands/install.test.js +0 -400
- package/frigg-cli/__tests__/unit/commands/ui.test.js +0 -346
- package/frigg-cli/__tests__/unit/utils/database-validator.test.js +0 -366
- package/frigg-cli/__tests__/unit/utils/error-messages.test.js +0 -304
- package/frigg-cli/__tests__/unit/utils/prisma-runner.test.js +0 -486
- package/frigg-cli/__tests__/utils/mock-factory.js +0 -270
- package/frigg-cli/__tests__/utils/prisma-mock.js +0 -194
- package/frigg-cli/__tests__/utils/test-fixtures.js +0 -463
- package/frigg-cli/__tests__/utils/test-setup.js +0 -287
- package/frigg-cli/build-command/index.js +0 -65
- package/frigg-cli/db-setup-command/index.js +0 -193
- package/frigg-cli/deploy-command/index.js +0 -175
- package/frigg-cli/generate-command/__tests__/generate-command.test.js +0 -301
- package/frigg-cli/generate-command/azure-generator.js +0 -43
- package/frigg-cli/generate-command/gcp-generator.js +0 -47
- package/frigg-cli/generate-command/index.js +0 -332
- package/frigg-cli/generate-command/terraform-generator.js +0 -555
- package/frigg-cli/generate-iam-command.js +0 -118
- package/frigg-cli/index.js +0 -75
- package/frigg-cli/index.test.js +0 -158
- package/frigg-cli/init-command/backend-first-handler.js +0 -756
- package/frigg-cli/init-command/index.js +0 -93
- package/frigg-cli/init-command/template-handler.js +0 -143
- package/frigg-cli/install-command/backend-js.js +0 -33
- package/frigg-cli/install-command/commit-changes.js +0 -16
- package/frigg-cli/install-command/environment-variables.js +0 -127
- package/frigg-cli/install-command/environment-variables.test.js +0 -136
- package/frigg-cli/install-command/index.js +0 -54
- package/frigg-cli/install-command/install-package.js +0 -13
- package/frigg-cli/install-command/integration-file.js +0 -30
- package/frigg-cli/install-command/logger.js +0 -12
- package/frigg-cli/install-command/template.js +0 -90
- package/frigg-cli/install-command/validate-package.js +0 -75
- package/frigg-cli/jest.config.js +0 -124
- package/frigg-cli/package.json +0 -54
- package/frigg-cli/start-command/index.js +0 -149
- package/frigg-cli/start-command/start-command.test.js +0 -297
- package/frigg-cli/test/init-command.test.js +0 -180
- package/frigg-cli/test/npm-registry.test.js +0 -319
- package/frigg-cli/ui-command/index.js +0 -154
- package/frigg-cli/utils/app-resolver.js +0 -319
- package/frigg-cli/utils/backend-path.js +0 -25
- package/frigg-cli/utils/database-validator.js +0 -161
- package/frigg-cli/utils/error-messages.js +0 -257
- package/frigg-cli/utils/npm-registry.js +0 -167
- package/frigg-cli/utils/prisma-runner.js +0 -280
- package/frigg-cli/utils/process-manager.js +0 -199
- package/frigg-cli/utils/repo-detection.js +0 -405
- package/infrastructure/aws-discovery.js +0 -1176
- package/infrastructure/aws-discovery.test.js +0 -1220
- package/infrastructure/serverless-template.js +0 -2074
- /package/infrastructure/{WEBSOCKET-CONFIGURATION.md → docs/WEBSOCKET-CONFIGURATION.md} +0 -0
- /package/infrastructure/{GENERATE-IAM-DOCS.md → docs/generate-iam-command.md} +0 -0
- /package/infrastructure/{iam-generator.test.js → domains/security/iam-generator.test.js} +0 -0
- /package/infrastructure/{frigg-deployment-iam-stack.yaml → domains/security/templates/frigg-deployment-iam-stack.yaml} +0 -0
- /package/infrastructure/{iam-policy-basic.json → domains/security/templates/iam-policy-basic.json} +0 -0
- /package/infrastructure/{iam-policy-full.json → domains/security/templates/iam-policy-full.json} +0 -0
- /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
|
|