@friggframework/devtools 2.0.0-next.62 → 2.0.0-next.63
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/CLAUDE.md +481 -0
- package/infrastructure/HEALTH.md +468 -0
- package/infrastructure/README.md +522 -0
- package/infrastructure/__tests__/fixtures/mock-aws-resources.js +391 -0
- package/infrastructure/__tests__/helpers/test-utils.js +277 -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 +147 -0
- package/infrastructure/docs/POSTGRES-CONFIGURATION.md +630 -0
- package/infrastructure/docs/PRE-DEPLOYMENT-HEALTH-CHECK-SPEC.md +1317 -0
- package/infrastructure/docs/WEBSOCKET-CONFIGURATION.md +105 -0
- package/infrastructure/docs/deployment-instructions.md +268 -0
- package/infrastructure/docs/generate-iam-command.md +278 -0
- package/infrastructure/docs/iam-policy-templates.md +193 -0
- 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 +701 -0
- package/infrastructure/domains/database/migration-builder.test.js +321 -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 +404 -0
- package/infrastructure/domains/integration/integration-builder.test.js +690 -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 +2051 -0
- package/infrastructure/domains/networking/vpc-builder.test.js +1960 -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 +505 -0
- package/infrastructure/domains/networking/vpc-resolver.test.js +801 -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/domains/security/iam-generator.js +816 -0
- package/infrastructure/domains/security/iam-generator.test.js +204 -0
- package/infrastructure/domains/security/kms-builder.js +415 -0
- package/infrastructure/domains/security/kms-builder.test.js +392 -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/security/templates/frigg-deployment-iam-stack.yaml +401 -0
- package/infrastructure/domains/security/templates/iam-policy-basic.json +218 -0
- package/infrastructure/domains/security/templates/iam-policy-full.json +288 -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 +672 -0
- package/infrastructure/domains/shared/cloudformation-discovery.test.js +985 -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 +579 -0
- package/infrastructure/domains/shared/providers/aws-provider-adapter.test.js +416 -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.enhanced.test.js +306 -0
- package/infrastructure/domains/shared/resource-discovery.js +233 -0
- package/infrastructure/domains/shared/resource-discovery.test.js +588 -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 +408 -0
- package/infrastructure/domains/shared/utilities/base-definition-factory.js.bak +338 -0
- package/infrastructure/domains/shared/utilities/base-definition-factory.test.js +291 -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 +159 -0
- package/infrastructure/domains/shared/utilities/prisma-layer-manager.test.js +444 -0
- package/infrastructure/domains/shared/validation/env-validator.js +78 -0
- package/infrastructure/domains/shared/validation/env-validator.test.js +173 -0
- package/infrastructure/domains/shared/validation/plugin-validator.js +187 -0
- package/infrastructure/domains/shared/validation/plugin-validator.test.js +323 -0
- package/infrastructure/esbuild.config.js +53 -0
- package/infrastructure/index.js +4 -0
- package/infrastructure/infrastructure-composer.js +117 -0
- package/infrastructure/infrastructure-composer.test.js +1895 -0
- package/infrastructure/integration.test.js +383 -0
- package/infrastructure/scripts/build-prisma-layer.js +701 -0
- package/infrastructure/scripts/build-prisma-layer.test.js +170 -0
- package/infrastructure/scripts/build-time-discovery.js +238 -0
- package/infrastructure/scripts/build-time-discovery.test.js +379 -0
- package/infrastructure/scripts/run-discovery.js +110 -0
- package/infrastructure/scripts/verify-prisma-layer.js +72 -0
- package/package.json +8 -7
|
@@ -0,0 +1,299 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Issue Entity
|
|
3
|
+
*
|
|
4
|
+
* Represents a problem detected in infrastructure health check
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
class Issue {
|
|
8
|
+
/**
|
|
9
|
+
* Valid issue types
|
|
10
|
+
*/
|
|
11
|
+
static TYPES = {
|
|
12
|
+
ORPHANED_RESOURCE: 'ORPHANED_RESOURCE',
|
|
13
|
+
MISSING_RESOURCE: 'MISSING_RESOURCE',
|
|
14
|
+
PROPERTY_MISMATCH: 'PROPERTY_MISMATCH',
|
|
15
|
+
DRIFTED_RESOURCE: 'DRIFTED_RESOURCE',
|
|
16
|
+
MISSING_TAG: 'MISSING_TAG',
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Valid severity levels
|
|
21
|
+
*/
|
|
22
|
+
static SEVERITIES = {
|
|
23
|
+
CRITICAL: 'critical',
|
|
24
|
+
WARNING: 'warning',
|
|
25
|
+
INFO: 'info',
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Create a new Issue
|
|
30
|
+
*
|
|
31
|
+
* @param {Object} params
|
|
32
|
+
* @param {string} params.type - Issue type (ORPHANED_RESOURCE, MISSING_RESOURCE, etc.)
|
|
33
|
+
* @param {string} params.severity - Severity level (critical, warning, info)
|
|
34
|
+
* @param {string} params.resourceType - CloudFormation resource type
|
|
35
|
+
* @param {string} params.resourceId - Resource identifier (physical or logical ID)
|
|
36
|
+
* @param {string} params.description - Human-readable description
|
|
37
|
+
* @param {string} [params.resolution] - Suggested resolution
|
|
38
|
+
* @param {boolean} [params.canAutoFix=false] - Whether issue can be automatically fixed
|
|
39
|
+
* @param {PropertyMismatch} [params.propertyMismatch] - Property mismatch details (for PROPERTY_MISMATCH type)
|
|
40
|
+
*/
|
|
41
|
+
constructor({
|
|
42
|
+
type,
|
|
43
|
+
severity,
|
|
44
|
+
resourceType,
|
|
45
|
+
resourceId,
|
|
46
|
+
description,
|
|
47
|
+
resolution = null,
|
|
48
|
+
canAutoFix = false,
|
|
49
|
+
propertyMismatch = null,
|
|
50
|
+
}) {
|
|
51
|
+
// Validate required fields
|
|
52
|
+
if (!type) {
|
|
53
|
+
throw new Error('type is required');
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (!severity) {
|
|
57
|
+
throw new Error('severity is required');
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (!resourceType) {
|
|
61
|
+
throw new Error('resourceType is required');
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (!resourceId) {
|
|
65
|
+
throw new Error('resourceId is required');
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (!description) {
|
|
69
|
+
throw new Error('description is required');
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Validate type
|
|
73
|
+
if (!Object.values(Issue.TYPES).includes(type)) {
|
|
74
|
+
throw new Error(`Invalid issue type: ${type}`);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Validate severity
|
|
78
|
+
if (!Object.values(Issue.SEVERITIES).includes(severity)) {
|
|
79
|
+
throw new Error(`Invalid severity: ${severity}`);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
this.type = type;
|
|
83
|
+
this.severity = severity;
|
|
84
|
+
this.resourceType = resourceType;
|
|
85
|
+
this.resourceId = resourceId;
|
|
86
|
+
this.description = description;
|
|
87
|
+
this.resolution = resolution;
|
|
88
|
+
this.canAutoFix = canAutoFix;
|
|
89
|
+
this.propertyMismatch = propertyMismatch;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Check if issue is an orphaned resource
|
|
94
|
+
* @returns {boolean}
|
|
95
|
+
*/
|
|
96
|
+
isOrphanedResource() {
|
|
97
|
+
return this.type === Issue.TYPES.ORPHANED_RESOURCE;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Check if issue is a missing resource
|
|
102
|
+
* @returns {boolean}
|
|
103
|
+
*/
|
|
104
|
+
isMissingResource() {
|
|
105
|
+
return this.type === Issue.TYPES.MISSING_RESOURCE;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Check if issue is a property mismatch
|
|
110
|
+
* @returns {boolean}
|
|
111
|
+
*/
|
|
112
|
+
isPropertyMismatch() {
|
|
113
|
+
return this.type === Issue.TYPES.PROPERTY_MISMATCH;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Check if issue is a drifted resource
|
|
118
|
+
* @returns {boolean}
|
|
119
|
+
*/
|
|
120
|
+
isDrifted() {
|
|
121
|
+
return this.type === Issue.TYPES.DRIFTED_RESOURCE;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Check if issue is critical severity
|
|
126
|
+
* @returns {boolean}
|
|
127
|
+
*/
|
|
128
|
+
isCritical() {
|
|
129
|
+
return this.severity === Issue.SEVERITIES.CRITICAL;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Check if issue is warning severity
|
|
134
|
+
* @returns {boolean}
|
|
135
|
+
*/
|
|
136
|
+
isWarning() {
|
|
137
|
+
return this.severity === Issue.SEVERITIES.WARNING;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Check if issue is info severity
|
|
142
|
+
* @returns {boolean}
|
|
143
|
+
*/
|
|
144
|
+
isInfo() {
|
|
145
|
+
return this.severity === Issue.SEVERITIES.INFO;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Get string representation
|
|
150
|
+
* @returns {string}
|
|
151
|
+
*/
|
|
152
|
+
toString() {
|
|
153
|
+
return `Issue: ${this.type} [${this.severity}] - ${this.resourceType} (${this.resourceId}): ${this.description}`;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Serialize to JSON
|
|
158
|
+
* @returns {Object}
|
|
159
|
+
*/
|
|
160
|
+
toJSON() {
|
|
161
|
+
return {
|
|
162
|
+
type: this.type,
|
|
163
|
+
severity: this.severity,
|
|
164
|
+
resourceType: this.resourceType,
|
|
165
|
+
resourceId: this.resourceId,
|
|
166
|
+
description: this.description,
|
|
167
|
+
resolution: this.resolution,
|
|
168
|
+
canAutoFix: this.canAutoFix,
|
|
169
|
+
propertyMismatch: this.propertyMismatch ? this.propertyMismatch.toJSON() : null,
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Create an orphaned resource issue
|
|
175
|
+
*
|
|
176
|
+
* @param {Object} params
|
|
177
|
+
* @param {string} params.resourceType - CloudFormation resource type
|
|
178
|
+
* @param {string} params.resourceId - Resource physical ID
|
|
179
|
+
* @param {string} params.description - Issue description
|
|
180
|
+
* @returns {Issue}
|
|
181
|
+
*/
|
|
182
|
+
static orphanedResource({ resourceType, resourceId, description }) {
|
|
183
|
+
return new Issue({
|
|
184
|
+
type: Issue.TYPES.ORPHANED_RESOURCE,
|
|
185
|
+
severity: Issue.SEVERITIES.CRITICAL,
|
|
186
|
+
resourceType,
|
|
187
|
+
resourceId,
|
|
188
|
+
description,
|
|
189
|
+
resolution: 'Import resource into CloudFormation stack using frigg repair --import',
|
|
190
|
+
canAutoFix: true,
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Create a missing resource issue
|
|
196
|
+
*
|
|
197
|
+
* @param {Object} params
|
|
198
|
+
* @param {string} params.resourceType - CloudFormation resource type
|
|
199
|
+
* @param {string} params.resourceId - Resource logical ID
|
|
200
|
+
* @param {string} params.description - Issue description
|
|
201
|
+
* @returns {Issue}
|
|
202
|
+
*/
|
|
203
|
+
static missingResource({ resourceType, resourceId, description }) {
|
|
204
|
+
return new Issue({
|
|
205
|
+
type: Issue.TYPES.MISSING_RESOURCE,
|
|
206
|
+
severity: Issue.SEVERITIES.CRITICAL,
|
|
207
|
+
resourceType,
|
|
208
|
+
resourceId,
|
|
209
|
+
description,
|
|
210
|
+
resolution: 'Verify resource was not manually deleted. May need to recreate or remove from stack definition.',
|
|
211
|
+
canAutoFix: false,
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* Format a value for display in issue descriptions
|
|
217
|
+
* Handles arrays, objects, and primitive types
|
|
218
|
+
*
|
|
219
|
+
* @private
|
|
220
|
+
* @param {*} value - Value to format
|
|
221
|
+
* @returns {string} Formatted value
|
|
222
|
+
*/
|
|
223
|
+
static _formatValue(value) {
|
|
224
|
+
if (value === null || value === undefined) {
|
|
225
|
+
return String(value);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// Handle arrays
|
|
229
|
+
if (Array.isArray(value)) {
|
|
230
|
+
// For arrays of objects (like Tags), show count and first few items
|
|
231
|
+
if (value.length > 0 && typeof value[0] === 'object') {
|
|
232
|
+
if (value.length <= 3) {
|
|
233
|
+
return JSON.stringify(value);
|
|
234
|
+
}
|
|
235
|
+
// Show first 2 items + count for long arrays
|
|
236
|
+
const preview = value.slice(0, 2);
|
|
237
|
+
return `${JSON.stringify(preview).slice(0, -1)}, ... (${value.length} total)]`;
|
|
238
|
+
}
|
|
239
|
+
// For simple arrays, stringify
|
|
240
|
+
return JSON.stringify(value);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
// Handle objects
|
|
244
|
+
if (typeof value === 'object') {
|
|
245
|
+
const keys = Object.keys(value);
|
|
246
|
+
if (keys.length === 0) {
|
|
247
|
+
return '{}';
|
|
248
|
+
}
|
|
249
|
+
if (keys.length <= 3) {
|
|
250
|
+
return JSON.stringify(value);
|
|
251
|
+
}
|
|
252
|
+
// For large objects, show keys count
|
|
253
|
+
return `{${keys.slice(0, 3).join(', ')}, ... (${keys.length} keys total)}`;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
// Primitives
|
|
257
|
+
return String(value);
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
/**
|
|
261
|
+
* Create a property mismatch issue
|
|
262
|
+
*
|
|
263
|
+
* @param {Object} params
|
|
264
|
+
* @param {string} params.resourceType - CloudFormation resource type
|
|
265
|
+
* @param {string} params.resourceId - Resource identifier
|
|
266
|
+
* @param {PropertyMismatch} params.mismatch - Property mismatch details
|
|
267
|
+
* @returns {Issue}
|
|
268
|
+
*/
|
|
269
|
+
static propertyMismatch({ resourceType, resourceId, mismatch }) {
|
|
270
|
+
const severity = mismatch.requiresReplacement()
|
|
271
|
+
? Issue.SEVERITIES.CRITICAL
|
|
272
|
+
: Issue.SEVERITIES.WARNING;
|
|
273
|
+
|
|
274
|
+
const canAutoFix = mismatch.canAutoFix();
|
|
275
|
+
|
|
276
|
+
// Format expected and actual values for display
|
|
277
|
+
const formattedExpected = Issue._formatValue(mismatch.expectedValue);
|
|
278
|
+
const formattedActual = Issue._formatValue(mismatch.actualValue);
|
|
279
|
+
|
|
280
|
+
const description = `Property mismatch: ${mismatch.propertyPath} (expected: ${formattedExpected}, actual: ${formattedActual})`;
|
|
281
|
+
|
|
282
|
+
const resolution = canAutoFix
|
|
283
|
+
? 'Can be auto-fixed using frigg repair --reconcile'
|
|
284
|
+
: 'Requires resource replacement - manual intervention needed';
|
|
285
|
+
|
|
286
|
+
return new Issue({
|
|
287
|
+
type: Issue.TYPES.PROPERTY_MISMATCH,
|
|
288
|
+
severity,
|
|
289
|
+
resourceType,
|
|
290
|
+
resourceId,
|
|
291
|
+
description,
|
|
292
|
+
resolution,
|
|
293
|
+
canAutoFix,
|
|
294
|
+
propertyMismatch: mismatch,
|
|
295
|
+
});
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
module.exports = Issue;
|