@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,305 @@
|
|
|
1
|
+
const BaseResourceResolver = require('./base-resolver');
|
|
2
|
+
const { ResourceOwnership } = require('./types');
|
|
3
|
+
|
|
4
|
+
describe('BaseResourceResolver', () => {
|
|
5
|
+
let resolver;
|
|
6
|
+
|
|
7
|
+
beforeEach(() => {
|
|
8
|
+
resolver = new BaseResourceResolver();
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
describe('helper methods', () => {
|
|
12
|
+
const mockDiscovery = {
|
|
13
|
+
stackManaged: [
|
|
14
|
+
{ logicalId: 'FriggVPC', physicalId: 'vpc-123', resourceType: 'AWS::EC2::VPC' },
|
|
15
|
+
{ logicalId: 'FriggLambdaSecurityGroup', physicalId: 'sg-456', resourceType: 'AWS::EC2::SecurityGroup' }
|
|
16
|
+
],
|
|
17
|
+
external: [
|
|
18
|
+
{ physicalId: 'vpc-external', resourceType: 'AWS::EC2::VPC', source: 'tag-search' }
|
|
19
|
+
],
|
|
20
|
+
fromCloudFormation: true
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
describe('findInStack', () => {
|
|
24
|
+
it('should find resource in stack', () => {
|
|
25
|
+
const resource = resolver.findInStack('FriggVPC', mockDiscovery);
|
|
26
|
+
|
|
27
|
+
expect(resource).toEqual({
|
|
28
|
+
logicalId: 'FriggVPC',
|
|
29
|
+
physicalId: 'vpc-123',
|
|
30
|
+
resourceType: 'AWS::EC2::VPC'
|
|
31
|
+
});
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it('should return null if not found', () => {
|
|
35
|
+
const resource = resolver.findInStack('NonExistent', mockDiscovery);
|
|
36
|
+
expect(resource).toBeNull();
|
|
37
|
+
});
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
describe('findExternal', () => {
|
|
41
|
+
it('should find external resource by type', () => {
|
|
42
|
+
const resource = resolver.findExternal('AWS::EC2::VPC', mockDiscovery);
|
|
43
|
+
|
|
44
|
+
expect(resource).toEqual({
|
|
45
|
+
physicalId: 'vpc-external',
|
|
46
|
+
resourceType: 'AWS::EC2::VPC',
|
|
47
|
+
source: 'tag-search'
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it('should return null if not found', () => {
|
|
52
|
+
const resource = resolver.findExternal('AWS::RDS::DBCluster', mockDiscovery);
|
|
53
|
+
expect(resource).toBeNull();
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
describe('isInStack', () => {
|
|
58
|
+
it('should return true if resource is in stack', () => {
|
|
59
|
+
expect(resolver.isInStack('FriggVPC', mockDiscovery)).toBe(true);
|
|
60
|
+
expect(resolver.isInStack('FriggLambdaSecurityGroup', mockDiscovery)).toBe(true);
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
it('should return false if resource is not in stack', () => {
|
|
64
|
+
expect(resolver.isInStack('NonExistent', mockDiscovery)).toBe(false);
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
describe('requireExternalIds', () => {
|
|
69
|
+
it('should not throw if IDs are provided', () => {
|
|
70
|
+
expect(() => resolver.requireExternalIds('vpc-123', 'vpcId')).not.toThrow();
|
|
71
|
+
expect(() => resolver.requireExternalIds(['sg-1', 'sg-2'], 'securityGroupIds')).not.toThrow();
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
it('should throw if IDs are missing', () => {
|
|
75
|
+
expect(() => resolver.requireExternalIds(undefined, 'vpcId')).toThrow(
|
|
76
|
+
"ownership='external' for vpcId requires external.vpcId"
|
|
77
|
+
);
|
|
78
|
+
expect(() => resolver.requireExternalIds(null, 'vpcId')).toThrow();
|
|
79
|
+
expect(() => resolver.requireExternalIds([], 'securityGroupIds')).toThrow();
|
|
80
|
+
});
|
|
81
|
+
});
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
describe('resolveResourceOwnership', () => {
|
|
85
|
+
describe('with explicit stack intent', () => {
|
|
86
|
+
it('should return STACK ownership', () => {
|
|
87
|
+
const discovery = {
|
|
88
|
+
stackManaged: [],
|
|
89
|
+
external: [{ physicalId: 'vpc-ext', resourceType: 'AWS::EC2::VPC', source: 'tag-search' }],
|
|
90
|
+
fromCloudFormation: false
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
const decision = resolver.resolveResourceOwnership(
|
|
94
|
+
'stack',
|
|
95
|
+
'FriggVPC',
|
|
96
|
+
'AWS::EC2::VPC',
|
|
97
|
+
discovery
|
|
98
|
+
);
|
|
99
|
+
|
|
100
|
+
expect(decision.ownership).toBe(ResourceOwnership.STACK);
|
|
101
|
+
expect(decision.reason).toContain('User explicitly specified ownership=stack');
|
|
102
|
+
});
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
describe('with explicit external intent', () => {
|
|
106
|
+
it('should return EXTERNAL ownership', () => {
|
|
107
|
+
const discovery = {
|
|
108
|
+
stackManaged: [
|
|
109
|
+
{ logicalId: 'FriggVPC', physicalId: 'vpc-123', resourceType: 'AWS::EC2::VPC' }
|
|
110
|
+
],
|
|
111
|
+
external: [],
|
|
112
|
+
fromCloudFormation: true
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
const decision = resolver.resolveResourceOwnership(
|
|
116
|
+
'external',
|
|
117
|
+
'FriggVPC',
|
|
118
|
+
'AWS::EC2::VPC',
|
|
119
|
+
discovery
|
|
120
|
+
);
|
|
121
|
+
|
|
122
|
+
expect(decision.ownership).toBe(ResourceOwnership.EXTERNAL);
|
|
123
|
+
expect(decision.reason).toContain('User explicitly specified ownership=external');
|
|
124
|
+
});
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
describe('with auto intent', () => {
|
|
128
|
+
it('should return STACK if resource is in stack (CRITICAL)', () => {
|
|
129
|
+
const discovery = {
|
|
130
|
+
stackManaged: [
|
|
131
|
+
{ logicalId: 'FriggLambdaSecurityGroup', physicalId: 'sg-069629001ade41c9a', resourceType: 'AWS::EC2::SecurityGroup' }
|
|
132
|
+
],
|
|
133
|
+
external: [],
|
|
134
|
+
fromCloudFormation: true
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
const decision = resolver.resolveResourceOwnership(
|
|
138
|
+
'auto',
|
|
139
|
+
'FriggLambdaSecurityGroup',
|
|
140
|
+
'AWS::EC2::SecurityGroup',
|
|
141
|
+
discovery
|
|
142
|
+
);
|
|
143
|
+
|
|
144
|
+
expect(decision.ownership).toBe(ResourceOwnership.STACK);
|
|
145
|
+
expect(decision.physicalId).toBe('sg-069629001ade41c9a');
|
|
146
|
+
expect(decision.reason).toContain('Found in CloudFormation stack');
|
|
147
|
+
expect(decision.reason).toContain('must keep in template to avoid deletion');
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
it('should return EXTERNAL if found externally but not in stack', () => {
|
|
151
|
+
const discovery = {
|
|
152
|
+
stackManaged: [],
|
|
153
|
+
external: [
|
|
154
|
+
{ physicalId: 'vpc-external', resourceType: 'AWS::EC2::VPC', source: 'tag-search' }
|
|
155
|
+
],
|
|
156
|
+
fromCloudFormation: false
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
const decision = resolver.resolveResourceOwnership(
|
|
160
|
+
'auto',
|
|
161
|
+
'FriggVPC',
|
|
162
|
+
'AWS::EC2::VPC',
|
|
163
|
+
discovery
|
|
164
|
+
);
|
|
165
|
+
|
|
166
|
+
expect(decision.ownership).toBe(ResourceOwnership.EXTERNAL);
|
|
167
|
+
expect(decision.physicalId).toBe('vpc-external');
|
|
168
|
+
expect(decision.reason).toContain('Found external resource via discovery');
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
it('should return STACK if not found anywhere (create new)', () => {
|
|
172
|
+
const discovery = {
|
|
173
|
+
stackManaged: [],
|
|
174
|
+
external: [],
|
|
175
|
+
fromCloudFormation: false
|
|
176
|
+
};
|
|
177
|
+
|
|
178
|
+
const decision = resolver.resolveResourceOwnership(
|
|
179
|
+
'auto',
|
|
180
|
+
'FriggVPC',
|
|
181
|
+
'AWS::EC2::VPC',
|
|
182
|
+
discovery
|
|
183
|
+
);
|
|
184
|
+
|
|
185
|
+
expect(decision.ownership).toBe(ResourceOwnership.STACK);
|
|
186
|
+
expect(decision.physicalId).toBeUndefined();
|
|
187
|
+
expect(decision.reason).toContain('No existing resource found - will create in stack');
|
|
188
|
+
});
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
describe('metadata', () => {
|
|
192
|
+
it('should include complete metadata', () => {
|
|
193
|
+
const discovery = {
|
|
194
|
+
stackManaged: [
|
|
195
|
+
{ logicalId: 'FriggVPC', physicalId: 'vpc-123', resourceType: 'AWS::EC2::VPC' }
|
|
196
|
+
],
|
|
197
|
+
external: [],
|
|
198
|
+
fromCloudFormation: true
|
|
199
|
+
};
|
|
200
|
+
|
|
201
|
+
const decision = resolver.resolveResourceOwnership(
|
|
202
|
+
'auto',
|
|
203
|
+
'FriggVPC',
|
|
204
|
+
'AWS::EC2::VPC',
|
|
205
|
+
discovery
|
|
206
|
+
);
|
|
207
|
+
|
|
208
|
+
expect(decision.metadata).toEqual({
|
|
209
|
+
logicalId: 'FriggVPC',
|
|
210
|
+
resourceType: 'AWS::EC2::VPC',
|
|
211
|
+
userIntent: 'auto',
|
|
212
|
+
inStack: true,
|
|
213
|
+
foundExternal: false
|
|
214
|
+
});
|
|
215
|
+
});
|
|
216
|
+
});
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
describe('createExternalDecision', () => {
|
|
220
|
+
it('should create external decision with single ID', () => {
|
|
221
|
+
const decision = resolver.createExternalDecision('vpc-external');
|
|
222
|
+
|
|
223
|
+
expect(decision).toEqual({
|
|
224
|
+
ownership: ResourceOwnership.EXTERNAL,
|
|
225
|
+
physicalId: 'vpc-external',
|
|
226
|
+
physicalIds: ['vpc-external'],
|
|
227
|
+
reason: 'Using external resource reference',
|
|
228
|
+
metadata: {
|
|
229
|
+
source: 'user-provided'
|
|
230
|
+
}
|
|
231
|
+
});
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
it('should create external decision with multiple IDs', () => {
|
|
235
|
+
const decision = resolver.createExternalDecision(['sg-1', 'sg-2'], 'Custom reason');
|
|
236
|
+
|
|
237
|
+
expect(decision).toEqual({
|
|
238
|
+
ownership: ResourceOwnership.EXTERNAL,
|
|
239
|
+
physicalId: 'sg-1',
|
|
240
|
+
physicalIds: ['sg-1', 'sg-2'],
|
|
241
|
+
reason: 'Custom reason',
|
|
242
|
+
metadata: {
|
|
243
|
+
source: 'user-provided'
|
|
244
|
+
}
|
|
245
|
+
});
|
|
246
|
+
});
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
describe('createStackDecision', () => {
|
|
250
|
+
it('should create stack decision for new resource', () => {
|
|
251
|
+
const decision = resolver.createStackDecision();
|
|
252
|
+
|
|
253
|
+
expect(decision).toEqual({
|
|
254
|
+
ownership: ResourceOwnership.STACK,
|
|
255
|
+
physicalId: null,
|
|
256
|
+
reason: 'Managed by CloudFormation stack',
|
|
257
|
+
metadata: {
|
|
258
|
+
source: 'new'
|
|
259
|
+
}
|
|
260
|
+
});
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
it('should create stack decision for existing resource', () => {
|
|
264
|
+
const decision = resolver.createStackDecision('vpc-123', 'Found in stack');
|
|
265
|
+
|
|
266
|
+
expect(decision).toEqual({
|
|
267
|
+
ownership: ResourceOwnership.STACK,
|
|
268
|
+
physicalId: 'vpc-123',
|
|
269
|
+
reason: 'Found in stack',
|
|
270
|
+
metadata: {
|
|
271
|
+
source: 'discovered'
|
|
272
|
+
}
|
|
273
|
+
});
|
|
274
|
+
});
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
describe('backwards compatibility with flat discovery', () => {
|
|
278
|
+
it('should work with old flat discovery structure', () => {
|
|
279
|
+
const flatDiscovery = {
|
|
280
|
+
fromCloudFormationStack: true,
|
|
281
|
+
existingLogicalIds: ['FriggVPC', 'FriggLambdaSecurityGroup'],
|
|
282
|
+
defaultVpcId: 'vpc-123',
|
|
283
|
+
securityGroupId: 'sg-456',
|
|
284
|
+
_structured: {
|
|
285
|
+
stackManaged: [
|
|
286
|
+
{ logicalId: 'FriggVPC', physicalId: 'vpc-123', resourceType: 'AWS::EC2::VPC' },
|
|
287
|
+
{ logicalId: 'FriggLambdaSecurityGroup', physicalId: 'sg-456', resourceType: 'AWS::EC2::SecurityGroup' }
|
|
288
|
+
],
|
|
289
|
+
external: [],
|
|
290
|
+
fromCloudFormation: true
|
|
291
|
+
}
|
|
292
|
+
};
|
|
293
|
+
|
|
294
|
+
const decision = resolver.resolveResourceOwnership(
|
|
295
|
+
'auto',
|
|
296
|
+
'FriggLambdaSecurityGroup',
|
|
297
|
+
'AWS::EC2::SecurityGroup',
|
|
298
|
+
flatDiscovery
|
|
299
|
+
);
|
|
300
|
+
|
|
301
|
+
expect(decision.ownership).toBe(ResourceOwnership.STACK);
|
|
302
|
+
expect(decision.physicalId).toBe('sg-456');
|
|
303
|
+
});
|
|
304
|
+
});
|
|
305
|
+
});
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Infrastructure Builder Orchestrator
|
|
3
|
+
*
|
|
4
|
+
* Application Layer - Hexagonal Architecture
|
|
5
|
+
*
|
|
6
|
+
* Orchestrates the execution of all infrastructure builders with:
|
|
7
|
+
* - Dependency resolution
|
|
8
|
+
* - Parallel execution where possible
|
|
9
|
+
* - Error handling and validation
|
|
10
|
+
* - Progress reporting
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
const { gatherDiscoveredResources } = require('./resource-discovery');
|
|
14
|
+
const { getAppEnvironmentVars, buildEnvironment } = require('./environment-builder');
|
|
15
|
+
|
|
16
|
+
class BuilderOrchestrator {
|
|
17
|
+
constructor(builders = []) {
|
|
18
|
+
this.builders = new Map();
|
|
19
|
+
builders.forEach(builder => this.registerBuilder(builder));
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Register a builder
|
|
24
|
+
*/
|
|
25
|
+
registerBuilder(builder) {
|
|
26
|
+
this.builders.set(builder.getName(), builder);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Validate all applicable builders
|
|
31
|
+
*/
|
|
32
|
+
async validateAll(appDefinition) {
|
|
33
|
+
console.log('\n🔍 Validating infrastructure configuration...');
|
|
34
|
+
|
|
35
|
+
const validationResults = [];
|
|
36
|
+
let hasErrors = false;
|
|
37
|
+
|
|
38
|
+
for (const [name, builder] of this.builders) {
|
|
39
|
+
if (builder.shouldExecute(appDefinition)) {
|
|
40
|
+
const result = builder.validate(appDefinition);
|
|
41
|
+
validationResults.push({ builder: name, result });
|
|
42
|
+
|
|
43
|
+
if (result.hasErrors()) {
|
|
44
|
+
hasErrors = true;
|
|
45
|
+
console.log(`❌ ${name} validation failed:`);
|
|
46
|
+
result.errors.forEach(error => console.log(` - ${error}`));
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (result.hasWarnings()) {
|
|
50
|
+
console.log(`⚠️ ${name} warnings:`);
|
|
51
|
+
result.warnings.forEach(warning => console.log(` - ${warning}`));
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (hasErrors) {
|
|
57
|
+
throw new Error('Infrastructure validation failed. See errors above.');
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
console.log('✅ All infrastructure validation passed\n');
|
|
61
|
+
return validationResults;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Resolve builder execution order based on dependencies
|
|
66
|
+
*/
|
|
67
|
+
resolveBuildOrder(appDefinition) {
|
|
68
|
+
const executionOrder = [];
|
|
69
|
+
const visited = new Set();
|
|
70
|
+
const visiting = new Set();
|
|
71
|
+
|
|
72
|
+
const visit = (builderName) => {
|
|
73
|
+
if (visited.has(builderName)) return;
|
|
74
|
+
if (visiting.has(builderName)) {
|
|
75
|
+
throw new Error(`Circular dependency detected: ${builderName}`);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const builder = this.builders.get(builderName);
|
|
79
|
+
if (!builder || !builder.shouldExecute(appDefinition)) {
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
visiting.add(builderName);
|
|
84
|
+
|
|
85
|
+
// Visit dependencies first
|
|
86
|
+
const dependencies = builder.getDependencies() || [];
|
|
87
|
+
dependencies.forEach(dep => visit(dep));
|
|
88
|
+
|
|
89
|
+
visiting.delete(builderName);
|
|
90
|
+
visited.add(builderName);
|
|
91
|
+
executionOrder.push(builderName);
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
// Visit all builders
|
|
95
|
+
for (const [builderName] of this.builders) {
|
|
96
|
+
visit(builderName);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return executionOrder;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Build all infrastructure
|
|
104
|
+
*/
|
|
105
|
+
async buildAll(appDefinition) {
|
|
106
|
+
console.log('\n🏗️ Building infrastructure...');
|
|
107
|
+
|
|
108
|
+
// Step 1: Validate configuration
|
|
109
|
+
await this.validateAll(appDefinition);
|
|
110
|
+
|
|
111
|
+
// Step 2: Discover AWS resources
|
|
112
|
+
const discoveredResources = await gatherDiscoveredResources(appDefinition);
|
|
113
|
+
|
|
114
|
+
// Step 3: Resolve build order
|
|
115
|
+
const buildOrder = this.resolveBuildOrder(appDefinition);
|
|
116
|
+
console.log(`📋 Build order: ${buildOrder.join(' → ')}\n`);
|
|
117
|
+
|
|
118
|
+
// Step 4: Execute builders in order
|
|
119
|
+
const buildResults = {};
|
|
120
|
+
|
|
121
|
+
for (const builderName of buildOrder) {
|
|
122
|
+
const builder = this.builders.get(builderName);
|
|
123
|
+
try {
|
|
124
|
+
const result = await builder.build(appDefinition, discoveredResources);
|
|
125
|
+
buildResults[builderName] = result;
|
|
126
|
+
} catch (error) {
|
|
127
|
+
console.error(`❌ ${builderName} build failed:`, error.message);
|
|
128
|
+
throw error;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// Step 5: Merge results
|
|
133
|
+
return this.mergeResults(buildResults, appDefinition, discoveredResources);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Merge builder results into cohesive definition
|
|
138
|
+
*/
|
|
139
|
+
mergeResults(buildResults, appDefinition, discoveredResources) {
|
|
140
|
+
console.log('\n🔗 Merging infrastructure results...');
|
|
141
|
+
|
|
142
|
+
const merged = {
|
|
143
|
+
resources: {},
|
|
144
|
+
iamStatements: [],
|
|
145
|
+
environment: {},
|
|
146
|
+
functions: {},
|
|
147
|
+
layers: {},
|
|
148
|
+
plugins: [],
|
|
149
|
+
custom: {},
|
|
150
|
+
vpcConfig: null,
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
// Merge results from each builder
|
|
154
|
+
for (const [builderName, result] of Object.entries(buildResults)) {
|
|
155
|
+
// Merge resources
|
|
156
|
+
if (result.resources) {
|
|
157
|
+
Object.assign(merged.resources, result.resources);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// Merge IAM statements
|
|
161
|
+
if (result.iamStatements) {
|
|
162
|
+
merged.iamStatements.push(...result.iamStatements);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// Merge environment variables
|
|
166
|
+
if (result.environment) {
|
|
167
|
+
Object.assign(merged.environment, result.environment);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// Merge functions
|
|
171
|
+
if (result.functions) {
|
|
172
|
+
Object.assign(merged.functions, result.functions);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// Merge layers
|
|
176
|
+
if (result.layers) {
|
|
177
|
+
Object.assign(merged.layers, result.layers);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// Merge plugins
|
|
181
|
+
if (result.plugins) {
|
|
182
|
+
merged.plugins.push(...result.plugins);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// Merge custom configuration
|
|
186
|
+
if (result.pluginConfig) {
|
|
187
|
+
Object.assign(merged.custom, result.pluginConfig);
|
|
188
|
+
}
|
|
189
|
+
if (result.custom) {
|
|
190
|
+
Object.assign(merged.custom, result.custom);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// Capture VPC config (from VpcBuilder)
|
|
194
|
+
if (result.vpcConfig) {
|
|
195
|
+
merged.vpcConfig = result.vpcConfig;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
console.log(` ✓ Merged ${builderName} results`);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
console.log('✅ Infrastructure build completed successfully\n');
|
|
202
|
+
|
|
203
|
+
return {
|
|
204
|
+
merged,
|
|
205
|
+
discoveredResources,
|
|
206
|
+
appEnvironmentVars: getAppEnvironmentVars(appDefinition),
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
module.exports = { BuilderOrchestrator };
|
|
212
|
+
|