@friggframework/devtools 2.0.0--canary.474.6ec870b.0 → 2.0.0--canary.474.d64c550.0
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/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/domain/entities/issue.js +250 -0
- package/infrastructure/domains/health/domain/entities/issue.test.js +417 -0
- package/infrastructure/domains/health/domain/entities/property-mismatch.js +7 -4
- package/infrastructure/domains/health/domain/entities/property-mismatch.test.js +28 -4
- 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/health-score-calculator.js +165 -0
- package/infrastructure/domains/health/domain/services/health-score-calculator.test.js +400 -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/value-objects/stack-identifier.js +13 -0
- package/infrastructure/domains/health/domain/value-objects/stack-identifier.test.js +29 -0
- package/package.json +6 -6
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* IPropertyReconciler Port Interface
|
|
3
|
+
*
|
|
4
|
+
* Defines operations for reconciling property mismatches between CloudFormation
|
|
5
|
+
* template definitions and actual cloud resource properties. This is used by
|
|
6
|
+
* the "frigg repair --reconcile" command to fix mutable property drift.
|
|
7
|
+
*
|
|
8
|
+
* This is a port in the hexagonal architecture that will be implemented
|
|
9
|
+
* by provider-specific adapters (e.g., AWSPropertyReconciler).
|
|
10
|
+
*
|
|
11
|
+
* Purpose: Abstract property update operations from the domain layer
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
class IPropertyReconciler {
|
|
15
|
+
/**
|
|
16
|
+
* Check if a property mismatch can be auto-fixed
|
|
17
|
+
*
|
|
18
|
+
* @param {PropertyMismatch} mismatch - Property mismatch to evaluate
|
|
19
|
+
* @returns {Promise<boolean>} True if mismatch can be automatically fixed
|
|
20
|
+
*/
|
|
21
|
+
async canReconcile(mismatch) {
|
|
22
|
+
throw new Error(
|
|
23
|
+
'IPropertyReconciler.canReconcile() must be implemented by adapter'
|
|
24
|
+
);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Reconcile a single property mismatch
|
|
29
|
+
*
|
|
30
|
+
* @param {Object} params
|
|
31
|
+
* @param {StackIdentifier} params.stackIdentifier - Stack identifier
|
|
32
|
+
* @param {string} params.logicalId - Logical resource ID
|
|
33
|
+
* @param {PropertyMismatch} params.mismatch - Property mismatch to fix
|
|
34
|
+
* @param {string} [params.mode='template'] - Reconciliation mode:
|
|
35
|
+
* - 'template': Update CloudFormation template to match actual (default)
|
|
36
|
+
* - 'resource': Update cloud resource to match template
|
|
37
|
+
* @returns {Promise<Object>} Reconciliation result
|
|
38
|
+
* @returns {Promise<Object>} Result with properties:
|
|
39
|
+
* - success: boolean
|
|
40
|
+
* - mode: string ('template' or 'resource')
|
|
41
|
+
* - propertyPath: string
|
|
42
|
+
* - oldValue: any
|
|
43
|
+
* - newValue: any
|
|
44
|
+
* - message: string
|
|
45
|
+
* @throws {Error} If reconciliation fails
|
|
46
|
+
*/
|
|
47
|
+
async reconcileProperty({ stackIdentifier, logicalId, mismatch, mode = 'template' }) {
|
|
48
|
+
throw new Error(
|
|
49
|
+
'IPropertyReconciler.reconcileProperty() must be implemented by adapter'
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Reconcile multiple property mismatches for a resource
|
|
55
|
+
*
|
|
56
|
+
* @param {Object} params
|
|
57
|
+
* @param {StackIdentifier} params.stackIdentifier - Stack identifier
|
|
58
|
+
* @param {string} params.logicalId - Logical resource ID
|
|
59
|
+
* @param {PropertyMismatch[]} params.mismatches - Property mismatches to fix
|
|
60
|
+
* @param {string} [params.mode='template'] - Reconciliation mode
|
|
61
|
+
* @returns {Promise<Object>} Reconciliation result
|
|
62
|
+
* @returns {Promise<Object>} Result with properties:
|
|
63
|
+
* - reconciledCount: number
|
|
64
|
+
* - failedCount: number
|
|
65
|
+
* - results: Array<Object> (per-property results)
|
|
66
|
+
* - message: string
|
|
67
|
+
*/
|
|
68
|
+
async reconcileMultipleProperties({
|
|
69
|
+
stackIdentifier,
|
|
70
|
+
logicalId,
|
|
71
|
+
mismatches,
|
|
72
|
+
mode = 'template',
|
|
73
|
+
}) {
|
|
74
|
+
throw new Error(
|
|
75
|
+
'IPropertyReconciler.reconcileMultipleProperties() must be implemented by adapter'
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Preview property reconciliation without applying changes
|
|
81
|
+
*
|
|
82
|
+
* @param {Object} params
|
|
83
|
+
* @param {StackIdentifier} params.stackIdentifier - Stack identifier
|
|
84
|
+
* @param {string} params.logicalId - Logical resource ID
|
|
85
|
+
* @param {PropertyMismatch} params.mismatch - Property mismatch to preview
|
|
86
|
+
* @param {string} [params.mode='template'] - Reconciliation mode
|
|
87
|
+
* @returns {Promise<Object>} Preview result
|
|
88
|
+
* @returns {Promise<Object>} Result with properties:
|
|
89
|
+
* - canReconcile: boolean
|
|
90
|
+
* - mode: string
|
|
91
|
+
* - propertyPath: string
|
|
92
|
+
* - currentValue: any
|
|
93
|
+
* - proposedValue: any
|
|
94
|
+
* - impact: string (description of impact)
|
|
95
|
+
* - warnings: Array<string>
|
|
96
|
+
*/
|
|
97
|
+
async previewReconciliation({ stackIdentifier, logicalId, mismatch, mode = 'template' }) {
|
|
98
|
+
throw new Error(
|
|
99
|
+
'IPropertyReconciler.previewReconciliation() must be implemented by adapter'
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Update CloudFormation template property
|
|
105
|
+
*
|
|
106
|
+
* @param {Object} params
|
|
107
|
+
* @param {StackIdentifier} params.stackIdentifier - Stack identifier
|
|
108
|
+
* @param {string} params.logicalId - Logical resource ID
|
|
109
|
+
* @param {string} params.propertyPath - Property path (e.g., "Properties.Tags")
|
|
110
|
+
* @param {*} params.newValue - New property value
|
|
111
|
+
* @returns {Promise<Object>} Update result
|
|
112
|
+
* @returns {Promise<Object>} Result with properties:
|
|
113
|
+
* - success: boolean
|
|
114
|
+
* - changeSetId: string (CloudFormation change set ID)
|
|
115
|
+
* - message: string
|
|
116
|
+
* @throws {Error} If update fails
|
|
117
|
+
*/
|
|
118
|
+
async updateTemplateProperty({ stackIdentifier, logicalId, propertyPath, newValue }) {
|
|
119
|
+
throw new Error(
|
|
120
|
+
'IPropertyReconciler.updateTemplateProperty() must be implemented by adapter'
|
|
121
|
+
);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Update cloud resource property directly
|
|
126
|
+
*
|
|
127
|
+
* @param {Object} params
|
|
128
|
+
* @param {string} params.resourceType - CloudFormation resource type
|
|
129
|
+
* @param {string} params.physicalId - Physical resource ID
|
|
130
|
+
* @param {string} params.region - AWS region
|
|
131
|
+
* @param {string} params.propertyPath - Property path
|
|
132
|
+
* @param {*} params.newValue - New property value
|
|
133
|
+
* @returns {Promise<Object>} Update result
|
|
134
|
+
* @returns {Promise<Object>} Result with properties:
|
|
135
|
+
* - success: boolean
|
|
136
|
+
* - message: string
|
|
137
|
+
* - updatedAt: Date
|
|
138
|
+
* @throws {Error} If update fails or is not supported
|
|
139
|
+
*/
|
|
140
|
+
async updateResourceProperty({ resourceType, physicalId, region, propertyPath, newValue }) {
|
|
141
|
+
throw new Error(
|
|
142
|
+
'IPropertyReconciler.updateResourceProperty() must be implemented by adapter'
|
|
143
|
+
);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Get reconciliation strategy for a resource type
|
|
148
|
+
*
|
|
149
|
+
* @param {string} resourceType - CloudFormation resource type
|
|
150
|
+
* @returns {Promise<Object>} Strategy information
|
|
151
|
+
* @returns {Promise<Object>} Strategy with properties:
|
|
152
|
+
* - supportsTemplateUpdate: boolean
|
|
153
|
+
* - supportsResourceUpdate: boolean
|
|
154
|
+
* - recommendedMode: string ('template' or 'resource')
|
|
155
|
+
* - limitations: Array<string>
|
|
156
|
+
*/
|
|
157
|
+
async getReconciliationStrategy(resourceType) {
|
|
158
|
+
throw new Error(
|
|
159
|
+
'IPropertyReconciler.getReconciliationStrategy() must be implemented by adapter'
|
|
160
|
+
);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
module.exports = IPropertyReconciler;
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* IResourceDetector Port Interface
|
|
3
|
+
*
|
|
4
|
+
* Defines operations for detecting cloud resources directly (outside of CloudFormation).
|
|
5
|
+
* This is used to find orphaned resources that exist in the cloud but are not managed
|
|
6
|
+
* by CloudFormation.
|
|
7
|
+
*
|
|
8
|
+
* This is a port in the hexagonal architecture that will be implemented
|
|
9
|
+
* by provider-specific adapters (e.g., AWSResourceDetector).
|
|
10
|
+
*
|
|
11
|
+
* Purpose: Abstract cloud resource discovery APIs from the domain layer
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
class IResourceDetector {
|
|
15
|
+
/**
|
|
16
|
+
* Detect all resources of a specific type in a region
|
|
17
|
+
*
|
|
18
|
+
* @param {Object} params
|
|
19
|
+
* @param {string} params.resourceType - CloudFormation resource type (e.g., AWS::EC2::VPC)
|
|
20
|
+
* @param {string} params.region - AWS region
|
|
21
|
+
* @param {Object} [params.filters={}] - Optional filters (e.g., tags, names)
|
|
22
|
+
* @returns {Promise<Array<Object>>} Array of detected resources
|
|
23
|
+
* @returns {Promise<Array<Object>>} Resources with properties:
|
|
24
|
+
* - physicalId: string (actual cloud resource ID)
|
|
25
|
+
* - resourceType: string (CloudFormation resource type)
|
|
26
|
+
* - properties: Object (resource properties from cloud API)
|
|
27
|
+
* - tags: Object (resource tags, if supported)
|
|
28
|
+
* - createdTime: Date (if available)
|
|
29
|
+
* @throws {Error} If resource type is not supported
|
|
30
|
+
*/
|
|
31
|
+
async detectResources({ resourceType, region, filters = {} }) {
|
|
32
|
+
throw new Error(
|
|
33
|
+
'IResourceDetector.detectResources() must be implemented by adapter'
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Get details for a specific resource
|
|
39
|
+
*
|
|
40
|
+
* @param {Object} params
|
|
41
|
+
* @param {string} params.resourceType - CloudFormation resource type
|
|
42
|
+
* @param {string} params.physicalId - Physical resource ID
|
|
43
|
+
* @param {string} params.region - AWS region
|
|
44
|
+
* @returns {Promise<Object>} Resource details
|
|
45
|
+
* @returns {Promise<Object>} Resource with properties:
|
|
46
|
+
* - physicalId: string
|
|
47
|
+
* - resourceType: string
|
|
48
|
+
* - properties: Object (complete resource properties)
|
|
49
|
+
* - tags: Object
|
|
50
|
+
* - status: string (resource-specific status)
|
|
51
|
+
* - metadata: Object (additional resource metadata)
|
|
52
|
+
* @throws {Error} If resource does not exist
|
|
53
|
+
*/
|
|
54
|
+
async getResourceDetails({ resourceType, physicalId, region }) {
|
|
55
|
+
throw new Error(
|
|
56
|
+
'IResourceDetector.getResourceDetails() must be implemented by adapter'
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Check if a resource exists
|
|
62
|
+
*
|
|
63
|
+
* @param {Object} params
|
|
64
|
+
* @param {string} params.resourceType - CloudFormation resource type
|
|
65
|
+
* @param {string} params.physicalId - Physical resource ID
|
|
66
|
+
* @param {string} params.region - AWS region
|
|
67
|
+
* @returns {Promise<boolean>} True if resource exists
|
|
68
|
+
*/
|
|
69
|
+
async resourceExists({ resourceType, physicalId, region }) {
|
|
70
|
+
throw new Error(
|
|
71
|
+
'IResourceDetector.resourceExists() must be implemented by adapter'
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Get list of supported resource types
|
|
77
|
+
*
|
|
78
|
+
* @returns {Promise<Array<string>>} Array of supported CloudFormation resource types
|
|
79
|
+
*/
|
|
80
|
+
async getSupportedResourceTypes() {
|
|
81
|
+
throw new Error(
|
|
82
|
+
'IResourceDetector.getSupportedResourceTypes() must be implemented by adapter'
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Detect resources by tags
|
|
88
|
+
*
|
|
89
|
+
* @param {Object} params
|
|
90
|
+
* @param {Object} params.tags - Tags to filter by (key-value pairs)
|
|
91
|
+
* @param {string} params.region - AWS region
|
|
92
|
+
* @param {string[]} [params.resourceTypes] - Optional: limit to specific resource types
|
|
93
|
+
* @returns {Promise<Array<Object>>} Array of resources matching tags
|
|
94
|
+
* @returns {Promise<Array<Object>>} Resources with properties:
|
|
95
|
+
* - physicalId: string
|
|
96
|
+
* - resourceType: string
|
|
97
|
+
* - properties: Object
|
|
98
|
+
* - tags: Object
|
|
99
|
+
*/
|
|
100
|
+
async detectResourcesByTags({ tags, region, resourceTypes = [] }) {
|
|
101
|
+
throw new Error(
|
|
102
|
+
'IResourceDetector.detectResourcesByTags() must be implemented by adapter'
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Find orphaned resources (exist in cloud but not in any stack)
|
|
108
|
+
*
|
|
109
|
+
* @param {Object} params
|
|
110
|
+
* @param {string} params.region - AWS region
|
|
111
|
+
* @param {string[]} [params.resourceTypes] - Optional: limit to specific resource types
|
|
112
|
+
* @param {string[]} [params.excludePhysicalIds=[]] - Physical IDs to exclude from orphan check
|
|
113
|
+
* @returns {Promise<Array<Object>>} Array of orphaned resources
|
|
114
|
+
* @returns {Promise<Array<Object>>} Resources with properties:
|
|
115
|
+
* - physicalId: string
|
|
116
|
+
* - resourceType: string
|
|
117
|
+
* - properties: Object
|
|
118
|
+
* - tags: Object
|
|
119
|
+
* - isOrphaned: boolean (always true)
|
|
120
|
+
* - reason: string (explanation of why it's orphaned)
|
|
121
|
+
*/
|
|
122
|
+
async findOrphanedResources({ region, resourceTypes = [], excludePhysicalIds = [] }) {
|
|
123
|
+
throw new Error(
|
|
124
|
+
'IResourceDetector.findOrphanedResources() must be implemented by adapter'
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
module.exports = IResourceDetector;
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* IResourceImporter Port Interface
|
|
3
|
+
*
|
|
4
|
+
* Defines operations for importing existing cloud resources into CloudFormation stacks.
|
|
5
|
+
* This is used by the "frigg repair --import" command to fix orphaned resources.
|
|
6
|
+
*
|
|
7
|
+
* This is a port in the hexagonal architecture that will be implemented
|
|
8
|
+
* by provider-specific adapters (e.g., AWSResourceImporter).
|
|
9
|
+
*
|
|
10
|
+
* Purpose: Abstract CloudFormation resource import operations from the domain layer
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
class IResourceImporter {
|
|
14
|
+
/**
|
|
15
|
+
* Check if a resource type supports import
|
|
16
|
+
*
|
|
17
|
+
* @param {string} resourceType - CloudFormation resource type (e.g., AWS::EC2::VPC)
|
|
18
|
+
* @returns {Promise<boolean>} True if resource type can be imported
|
|
19
|
+
*/
|
|
20
|
+
async supportsImport(resourceType) {
|
|
21
|
+
throw new Error(
|
|
22
|
+
'IResourceImporter.supportsImport() must be implemented by adapter'
|
|
23
|
+
);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Get the identifier property for a resource type
|
|
28
|
+
* (e.g., "VpcId" for AWS::EC2::VPC, "BucketName" for AWS::S3::Bucket)
|
|
29
|
+
*
|
|
30
|
+
* @param {string} resourceType - CloudFormation resource type
|
|
31
|
+
* @returns {Promise<string>} Property name used as identifier for import
|
|
32
|
+
* @throws {Error} If resource type doesn't support import
|
|
33
|
+
*/
|
|
34
|
+
async getIdentifierProperty(resourceType) {
|
|
35
|
+
throw new Error(
|
|
36
|
+
'IResourceImporter.getIdentifierProperty() must be implemented by adapter'
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Validate that a resource can be imported
|
|
42
|
+
*
|
|
43
|
+
* @param {Object} params
|
|
44
|
+
* @param {string} params.resourceType - CloudFormation resource type
|
|
45
|
+
* @param {string} params.physicalId - Physical resource ID
|
|
46
|
+
* @param {string} params.region - AWS region
|
|
47
|
+
* @returns {Promise<Object>} Validation result
|
|
48
|
+
* @returns {Promise<Object>} Result with properties:
|
|
49
|
+
* - canImport: boolean
|
|
50
|
+
* - reason: string (explanation if cannot import)
|
|
51
|
+
* - warnings: Array<string> (potential issues with import)
|
|
52
|
+
*/
|
|
53
|
+
async validateImport({ resourceType, physicalId, region }) {
|
|
54
|
+
throw new Error(
|
|
55
|
+
'IResourceImporter.validateImport() must be implemented by adapter'
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Import a single resource into a stack
|
|
61
|
+
*
|
|
62
|
+
* @param {Object} params
|
|
63
|
+
* @param {StackIdentifier} params.stackIdentifier - Target stack
|
|
64
|
+
* @param {string} params.logicalId - Desired logical ID for the resource
|
|
65
|
+
* @param {string} params.resourceType - CloudFormation resource type
|
|
66
|
+
* @param {string} params.physicalId - Physical resource ID to import
|
|
67
|
+
* @param {Object} params.properties - Resource properties for template
|
|
68
|
+
* @returns {Promise<Object>} Import operation result
|
|
69
|
+
* @returns {Promise<Object>} Result with properties:
|
|
70
|
+
* - operationId: string (CloudFormation change set ID)
|
|
71
|
+
* - status: string (IN_PROGRESS, COMPLETE, FAILED)
|
|
72
|
+
* - message: string (status message)
|
|
73
|
+
* @throws {Error} If import fails validation or execution
|
|
74
|
+
*/
|
|
75
|
+
async importResource({ stackIdentifier, logicalId, resourceType, physicalId, properties }) {
|
|
76
|
+
throw new Error(
|
|
77
|
+
'IResourceImporter.importResource() must be implemented by adapter'
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Import multiple resources into a stack in a single operation
|
|
83
|
+
*
|
|
84
|
+
* @param {Object} params
|
|
85
|
+
* @param {StackIdentifier} params.stackIdentifier - Target stack
|
|
86
|
+
* @param {Array<Object>} params.resources - Resources to import
|
|
87
|
+
* @param {string} params.resources[].logicalId - Desired logical ID
|
|
88
|
+
* @param {string} params.resources[].resourceType - CloudFormation resource type
|
|
89
|
+
* @param {string} params.resources[].physicalId - Physical resource ID
|
|
90
|
+
* @param {Object} params.resources[].properties - Resource properties
|
|
91
|
+
* @returns {Promise<Object>} Import operation result
|
|
92
|
+
* @returns {Promise<Object>} Result with properties:
|
|
93
|
+
* - operationId: string (CloudFormation change set ID)
|
|
94
|
+
* - status: string (IN_PROGRESS, COMPLETE, FAILED)
|
|
95
|
+
* - importedCount: number
|
|
96
|
+
* - failedCount: number
|
|
97
|
+
* - message: string
|
|
98
|
+
* - details: Array<Object> (per-resource status)
|
|
99
|
+
* @throws {Error} If import fails validation or execution
|
|
100
|
+
*/
|
|
101
|
+
async importMultipleResources({ stackIdentifier, resources }) {
|
|
102
|
+
throw new Error(
|
|
103
|
+
'IResourceImporter.importMultipleResources() must be implemented by adapter'
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Get status of an import operation
|
|
109
|
+
*
|
|
110
|
+
* @param {string} operationId - CloudFormation change set ID
|
|
111
|
+
* @returns {Promise<Object>} Operation status
|
|
112
|
+
* @returns {Promise<Object>} Status with properties:
|
|
113
|
+
* - operationId: string
|
|
114
|
+
* - status: string (IN_PROGRESS, COMPLETE, FAILED)
|
|
115
|
+
* - progress: number (0-100)
|
|
116
|
+
* - message: string
|
|
117
|
+
* - completedTime: Date (if complete)
|
|
118
|
+
*/
|
|
119
|
+
async getImportStatus(operationId) {
|
|
120
|
+
throw new Error(
|
|
121
|
+
'IResourceImporter.getImportStatus() must be implemented by adapter'
|
|
122
|
+
);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Generate CloudFormation template snippet for an imported resource
|
|
127
|
+
*
|
|
128
|
+
* @param {Object} params
|
|
129
|
+
* @param {string} params.logicalId - Logical ID for the resource
|
|
130
|
+
* @param {string} params.resourceType - CloudFormation resource type
|
|
131
|
+
* @param {Object} params.properties - Current resource properties from cloud
|
|
132
|
+
* @returns {Promise<Object>} Template snippet for the resource
|
|
133
|
+
* @returns {Promise<Object>} Template snippet with CloudFormation resource definition
|
|
134
|
+
*/
|
|
135
|
+
async generateTemplateSnippet({ logicalId, resourceType, properties }) {
|
|
136
|
+
throw new Error(
|
|
137
|
+
'IResourceImporter.generateTemplateSnippet() must be implemented by adapter'
|
|
138
|
+
);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
module.exports = IResourceImporter;
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* IStackRepository Port Interface
|
|
3
|
+
*
|
|
4
|
+
* Defines operations for accessing CloudFormation stack information.
|
|
5
|
+
* This is a port in the hexagonal architecture that will be implemented
|
|
6
|
+
* by provider-specific adapters (e.g., AWSStackRepository).
|
|
7
|
+
*
|
|
8
|
+
* Purpose: Abstract CloudFormation API interactions from the domain layer
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
class IStackRepository {
|
|
12
|
+
/**
|
|
13
|
+
* Get stack information by identifier
|
|
14
|
+
*
|
|
15
|
+
* @param {StackIdentifier} identifier - Stack identifier
|
|
16
|
+
* @returns {Promise<Object>} Stack information
|
|
17
|
+
* @returns {Promise<Object>} Stack information with properties:
|
|
18
|
+
* - stackName: string
|
|
19
|
+
* - region: string
|
|
20
|
+
* - accountId: string
|
|
21
|
+
* - stackId: string (ARN)
|
|
22
|
+
* - status: string (CREATE_COMPLETE, UPDATE_COMPLETE, etc.)
|
|
23
|
+
* - creationTime: Date
|
|
24
|
+
* - lastUpdatedTime: Date
|
|
25
|
+
* - parameters: Object (key-value pairs)
|
|
26
|
+
* - outputs: Object (key-value pairs)
|
|
27
|
+
* - tags: Object (key-value pairs)
|
|
28
|
+
* @throws {Error} If stack does not exist
|
|
29
|
+
*/
|
|
30
|
+
async getStack(identifier) {
|
|
31
|
+
throw new Error('IStackRepository.getStack() must be implemented by adapter');
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* List all resources in a stack
|
|
36
|
+
*
|
|
37
|
+
* @param {StackIdentifier} identifier - Stack identifier
|
|
38
|
+
* @returns {Promise<Array>} Array of stack resources
|
|
39
|
+
* @returns {Promise<Array<Object>>} Array of resources with properties:
|
|
40
|
+
* - logicalId: string (CloudFormation logical ID)
|
|
41
|
+
* - physicalId: string (actual cloud resource ID)
|
|
42
|
+
* - resourceType: string (e.g., AWS::EC2::VPC)
|
|
43
|
+
* - status: string (CREATE_COMPLETE, UPDATE_COMPLETE, etc.)
|
|
44
|
+
* - lastUpdatedTime: Date
|
|
45
|
+
* - driftStatus: string (IN_SYNC, MODIFIED, DELETED, NOT_CHECKED)
|
|
46
|
+
* @throws {Error} If stack does not exist
|
|
47
|
+
*/
|
|
48
|
+
async listResources(identifier) {
|
|
49
|
+
throw new Error(
|
|
50
|
+
'IStackRepository.listResources() must be implemented by adapter'
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Get resource details from stack
|
|
56
|
+
*
|
|
57
|
+
* @param {StackIdentifier} identifier - Stack identifier
|
|
58
|
+
* @param {string} logicalId - Logical resource ID
|
|
59
|
+
* @returns {Promise<Object>} Resource details
|
|
60
|
+
* @returns {Promise<Object>} Resource with properties:
|
|
61
|
+
* - logicalId: string
|
|
62
|
+
* - physicalId: string
|
|
63
|
+
* - resourceType: string
|
|
64
|
+
* - status: string
|
|
65
|
+
* - properties: Object (current resource properties)
|
|
66
|
+
* - metadata: Object (resource metadata from template)
|
|
67
|
+
* @throws {Error} If stack or resource does not exist
|
|
68
|
+
*/
|
|
69
|
+
async getResource(identifier, logicalId) {
|
|
70
|
+
throw new Error('IStackRepository.getResource() must be implemented by adapter');
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Get the CloudFormation template for a stack
|
|
75
|
+
*
|
|
76
|
+
* @param {StackIdentifier} identifier - Stack identifier
|
|
77
|
+
* @returns {Promise<Object>} CloudFormation template as object
|
|
78
|
+
* @throws {Error} If stack does not exist
|
|
79
|
+
*/
|
|
80
|
+
async getTemplate(identifier) {
|
|
81
|
+
throw new Error('IStackRepository.getTemplate() must be implemented by adapter');
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Check if a stack exists
|
|
86
|
+
*
|
|
87
|
+
* @param {StackIdentifier} identifier - Stack identifier
|
|
88
|
+
* @returns {Promise<boolean>} True if stack exists
|
|
89
|
+
*/
|
|
90
|
+
async exists(identifier) {
|
|
91
|
+
throw new Error('IStackRepository.exists() must be implemented by adapter');
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Detect drift for the entire stack
|
|
96
|
+
*
|
|
97
|
+
* @param {StackIdentifier} identifier - Stack identifier
|
|
98
|
+
* @returns {Promise<Object>} Drift detection result
|
|
99
|
+
* @returns {Promise<Object>} Result with properties:
|
|
100
|
+
* - stackDriftStatus: string (DRIFTED, IN_SYNC, UNKNOWN, NOT_CHECKED)
|
|
101
|
+
* - driftedResourceCount: number
|
|
102
|
+
* - detectionTime: Date
|
|
103
|
+
* @throws {Error} If stack does not exist
|
|
104
|
+
*/
|
|
105
|
+
async detectStackDrift(identifier) {
|
|
106
|
+
throw new Error(
|
|
107
|
+
'IStackRepository.detectStackDrift() must be implemented by adapter'
|
|
108
|
+
);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Get drift details for a specific resource
|
|
113
|
+
*
|
|
114
|
+
* @param {StackIdentifier} identifier - Stack identifier
|
|
115
|
+
* @param {string} logicalId - Logical resource ID
|
|
116
|
+
* @returns {Promise<Object>} Resource drift details
|
|
117
|
+
* @returns {Promise<Object>} Drift with properties:
|
|
118
|
+
* - driftStatus: string (IN_SYNC, MODIFIED, DELETED, NOT_CHECKED)
|
|
119
|
+
* - expectedProperties: Object (from template)
|
|
120
|
+
* - actualProperties: Object (from cloud)
|
|
121
|
+
* - propertyDifferences: Array<Object> (list of drifted properties)
|
|
122
|
+
* @throws {Error} If stack or resource does not exist
|
|
123
|
+
*/
|
|
124
|
+
async getResourceDrift(identifier, logicalId) {
|
|
125
|
+
throw new Error(
|
|
126
|
+
'IStackRepository.getResourceDrift() must be implemented by adapter'
|
|
127
|
+
);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
module.exports = IStackRepository;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Health Domain - Application Layer Ports
|
|
3
|
+
*
|
|
4
|
+
* Port interfaces define how the application layer communicates with
|
|
5
|
+
* infrastructure adapters. These are part of the hexagonal architecture
|
|
6
|
+
* pattern, allowing provider-specific implementations (AWS, GCP, Azure, etc.)
|
|
7
|
+
* without changing the domain or application logic.
|
|
8
|
+
*
|
|
9
|
+
* Ports:
|
|
10
|
+
* - IStackRepository: CloudFormation stack operations
|
|
11
|
+
* - IResourceDetector: Cloud resource discovery (orphan detection)
|
|
12
|
+
* - IResourceImporter: Import resources into CloudFormation
|
|
13
|
+
* - IPropertyReconciler: Fix property drift (mutable properties)
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
const IStackRepository = require('./IStackRepository');
|
|
17
|
+
const IResourceDetector = require('./IResourceDetector');
|
|
18
|
+
const IResourceImporter = require('./IResourceImporter');
|
|
19
|
+
const IPropertyReconciler = require('./IPropertyReconciler');
|
|
20
|
+
|
|
21
|
+
module.exports = {
|
|
22
|
+
IStackRepository,
|
|
23
|
+
IResourceDetector,
|
|
24
|
+
IResourceImporter,
|
|
25
|
+
IPropertyReconciler,
|
|
26
|
+
};
|