@spottoai/types-package 1.0.2-dev1138.2 → 1.0.2-dev1138.5
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/README.md +9 -0
- package/dist/accounts/accounts.d.ts +77 -4
- package/dist/accounts/accounts.d.ts.map +1 -1
- package/dist/accounts/accounts.js +2 -1
- package/dist/accounts/accounts.js.map +1 -1
- package/dist/accounts/azureManualOnboarding.d.ts +114 -0
- package/dist/accounts/azureManualOnboarding.d.ts.map +1 -0
- package/dist/accounts/azureManualOnboarding.js +12 -0
- package/dist/accounts/azureManualOnboarding.js.map +1 -0
- package/dist/accounts/azureSpSetup.d.ts +128 -31
- package/dist/accounts/azureSpSetup.d.ts.map +1 -1
- package/dist/accounts/azureSpSetup.js +15 -0
- package/dist/accounts/azureSpSetup.js.map +1 -1
- package/dist/accounts/azureSpSetupDurable.d.ts +8 -0
- package/dist/accounts/azureSpSetupDurable.d.ts.map +1 -1
- package/dist/accounts/azureSpSetupDurable.js +28 -0
- package/dist/accounts/azureSpSetupDurable.js.map +1 -1
- package/dist/accounts/index.d.ts +1 -0
- package/dist/accounts/index.d.ts.map +1 -1
- package/dist/accounts/index.js +1 -0
- package/dist/accounts/index.js.map +1 -1
- package/dist/accounts/readPermissions.d.ts +1 -1
- package/dist/accounts/readPermissions.d.ts.map +1 -1
- package/dist/accounts/readPermissions.js.map +1 -1
- package/dist/accounts/validation.d.ts +24 -0
- package/dist/accounts/validation.d.ts.map +1 -1
- package/dist/ai/index.d.ts +14 -1
- package/dist/ai/index.d.ts.map +1 -1
- package/dist/ai/index.js +12 -0
- package/dist/ai/index.js.map +1 -1
- package/dist/aws/commitments-planning.d.ts +5 -0
- package/dist/aws/commitments-planning.d.ts.map +1 -1
- package/dist/aws/commitments-planning.js +19 -0
- package/dist/aws/commitments-planning.js.map +1 -1
- package/dist/aws/estates.d.ts +1 -1
- package/dist/aws/estates.d.ts.map +1 -1
- package/dist/aws/estates.js +7 -1
- package/dist/aws/estates.js.map +1 -1
- package/dist/aws/index.d.ts +5 -0
- package/dist/aws/index.d.ts.map +1 -1
- package/dist/aws/index.js +5 -0
- package/dist/aws/index.js.map +1 -1
- package/dist/aws/onboarding.d.ts +3 -0
- package/dist/aws/onboarding.d.ts.map +1 -1
- package/dist/aws/organizationCommitments.d.ts +260 -0
- package/dist/aws/organizationCommitments.d.ts.map +1 -0
- package/dist/aws/organizationCommitments.js +53 -0
- package/dist/aws/organizationCommitments.js.map +1 -0
- package/dist/aws/organizationCommitmentsApiValidation.d.ts +21 -0
- package/dist/aws/organizationCommitmentsApiValidation.d.ts.map +1 -0
- package/dist/aws/organizationCommitmentsApiValidation.js +226 -0
- package/dist/aws/organizationCommitmentsApiValidation.js.map +1 -0
- package/dist/aws/organizationCommitmentsValidation.d.ts +12 -0
- package/dist/aws/organizationCommitmentsValidation.d.ts.map +1 -0
- package/dist/aws/organizationCommitmentsValidation.js +228 -0
- package/dist/aws/organizationCommitmentsValidation.js.map +1 -0
- package/dist/aws/portalOrganizationCommitmentsPlanningPublicArtifactValidation.d.ts +9 -0
- package/dist/aws/portalOrganizationCommitmentsPlanningPublicArtifactValidation.d.ts.map +1 -0
- package/dist/aws/portalOrganizationCommitmentsPlanningPublicArtifactValidation.js +407 -0
- package/dist/aws/portalOrganizationCommitmentsPlanningPublicArtifactValidation.js.map +1 -0
- package/dist/aws/portalOrganizationCommitmentsPlanningPublicArtifacts.d.ts +17 -0
- package/dist/aws/portalOrganizationCommitmentsPlanningPublicArtifacts.d.ts.map +1 -0
- package/dist/aws/portalOrganizationCommitmentsPlanningPublicArtifacts.js +6 -0
- package/dist/aws/portalOrganizationCommitmentsPlanningPublicArtifacts.js.map +1 -0
- package/dist/aws/requests.d.ts +13 -2
- package/dist/aws/requests.d.ts.map +1 -1
- package/dist/aws/requests.js +1 -1
- package/dist/aws/requests.js.map +1 -1
- package/dist/azure/commitmentsPlanning.d.ts +2 -2
- package/dist/azure/commitmentsPlanning.d.ts.map +1 -1
- package/dist/azure/financialAnalytics.d.ts +68 -2
- package/dist/azure/financialAnalytics.d.ts.map +1 -1
- package/dist/azure/financialAnalyticsDelivery.d.ts +2 -0
- package/dist/azure/financialAnalyticsDelivery.d.ts.map +1 -1
- package/dist/azure/financialAnalyticsDelivery.js +4 -1
- package/dist/azure/financialAnalyticsDelivery.js.map +1 -1
- package/dist/azure/financialAnalyticsDisplayKernel.d.ts +12 -0
- package/dist/azure/financialAnalyticsDisplayKernel.d.ts.map +1 -0
- package/dist/azure/financialAnalyticsDisplayKernel.js +144 -0
- package/dist/azure/financialAnalyticsDisplayKernel.js.map +1 -0
- package/dist/azure/financialAnalyticsValidation.d.ts.map +1 -1
- package/dist/azure/financialAnalyticsValidation.js +61 -7
- package/dist/azure/financialAnalyticsValidation.js.map +1 -1
- package/dist/azure/financialAuthorityProjectionKernel.d.ts +7 -0
- package/dist/azure/financialAuthorityProjectionKernel.d.ts.map +1 -0
- package/dist/azure/financialAuthorityProjectionKernel.js +165 -0
- package/dist/azure/financialAuthorityProjectionKernel.js.map +1 -0
- package/dist/azure/financialAuthorityView.d.ts +4 -0
- package/dist/azure/financialAuthorityView.d.ts.map +1 -1
- package/dist/azure/financialAuthorityView.js.map +1 -1
- package/dist/azure/financialAuthorityViewValidation.d.ts +3 -1
- package/dist/azure/financialAuthorityViewValidation.d.ts.map +1 -1
- package/dist/azure/financialAuthorityViewValidation.js +206 -2
- package/dist/azure/financialAuthorityViewValidation.js.map +1 -1
- package/dist/azure/financialChargeComposition.d.ts +81 -0
- package/dist/azure/financialChargeComposition.d.ts.map +1 -0
- package/dist/azure/financialChargeComposition.js +41 -0
- package/dist/azure/financialChargeComposition.js.map +1 -0
- package/dist/azure/financialChargeCompositionKernel.d.ts +3 -0
- package/dist/azure/financialChargeCompositionKernel.d.ts.map +1 -0
- package/dist/azure/financialChargeCompositionKernel.js +53 -0
- package/dist/azure/financialChargeCompositionKernel.js.map +1 -0
- package/dist/azure/financialChargeCompositionValidation.d.ts +8 -0
- package/dist/azure/financialChargeCompositionValidation.d.ts.map +1 -0
- package/dist/azure/financialChargeCompositionValidation.js +164 -0
- package/dist/azure/financialChargeCompositionValidation.js.map +1 -0
- package/dist/azure/financialDataflow.d.ts +8 -1
- package/dist/azure/financialDataflow.d.ts.map +1 -1
- package/dist/azure/financialDataflow.js.map +1 -1
- package/dist/azure/financialDataflowKernel.d.ts +2 -0
- package/dist/azure/financialDataflowKernel.d.ts.map +1 -1
- package/dist/azure/financialDataflowKernel.js +60 -7
- package/dist/azure/financialDataflowKernel.js.map +1 -1
- package/dist/azure/financialDataflowValidation.d.ts +1 -1
- package/dist/azure/financialDataflowValidation.d.ts.map +1 -1
- package/dist/azure/financialDataflowValidation.js +146 -28
- package/dist/azure/financialDataflowValidation.js.map +1 -1
- package/dist/azure/financialPolicy.d.ts +3 -0
- package/dist/azure/financialPolicy.d.ts.map +1 -1
- package/dist/azure/financialPolicy.js.map +1 -1
- package/dist/azure/financialPolicyValidation.d.ts.map +1 -1
- package/dist/azure/financialPolicyValidation.js +5 -0
- package/dist/azure/financialPolicyValidation.js.map +1 -1
- package/dist/azure/financialProjection.d.ts +11 -1
- package/dist/azure/financialProjection.d.ts.map +1 -1
- package/dist/azure/financialProjection.js.map +1 -1
- package/dist/azure/financialProjectionValidation.d.ts.map +1 -1
- package/dist/azure/financialProjectionValidation.js +43 -3
- package/dist/azure/financialProjectionValidation.js.map +1 -1
- package/dist/azure/financialSavingsAuthority.d.ts +25 -4
- package/dist/azure/financialSavingsAuthority.d.ts.map +1 -1
- package/dist/azure/financialSavingsAuthorityValidation.d.ts +6 -2
- package/dist/azure/financialSavingsAuthorityValidation.d.ts.map +1 -1
- package/dist/azure/financialSavingsAuthorityValidation.js +126 -2
- package/dist/azure/financialSavingsAuthorityValidation.js.map +1 -1
- package/dist/azure/financialSavingsCoordinateValidation.d.ts.map +1 -1
- package/dist/azure/financialSavingsCoordinateValidation.js +62 -19
- package/dist/azure/financialSavingsCoordinateValidation.js.map +1 -1
- package/dist/azure/financialSavingsSurfaceProjection.d.ts +58 -3
- package/dist/azure/financialSavingsSurfaceProjection.d.ts.map +1 -1
- package/dist/azure/financialSavingsSurfaceProjection.js +3 -1
- package/dist/azure/financialSavingsSurfaceProjection.js.map +1 -1
- package/dist/azure/financialSavingsSurfaceProjectionKernel.d.ts +14 -0
- package/dist/azure/financialSavingsSurfaceProjectionKernel.d.ts.map +1 -0
- package/dist/azure/financialSavingsSurfaceProjectionKernel.js +326 -0
- package/dist/azure/financialSavingsSurfaceProjectionKernel.js.map +1 -0
- package/dist/azure/financialSavingsSurfaceProjectionValidation.d.ts.map +1 -1
- package/dist/azure/financialSavingsSurfaceProjectionValidation.js +126 -12
- package/dist/azure/financialSavingsSurfaceProjectionValidation.js.map +1 -1
- package/dist/azure/financialScope.d.ts +6 -0
- package/dist/azure/financialScope.d.ts.map +1 -1
- package/dist/azure/financialScope.js +6 -0
- package/dist/azure/financialScope.js.map +1 -1
- package/dist/azure/governance.d.ts +145 -0
- package/dist/azure/governance.d.ts.map +1 -1
- package/dist/azure/governance.js +2 -1
- package/dist/azure/governance.js.map +1 -1
- package/dist/azure/metrics.d.ts +2 -2
- package/dist/azure/metrics.d.ts.map +1 -1
- package/dist/azure/payloads.d.ts +19 -0
- package/dist/azure/payloads.d.ts.map +1 -1
- package/dist/azure/prices.d.ts +16 -3
- package/dist/azure/prices.d.ts.map +1 -1
- package/dist/azure/recommendationState.d.ts +1 -1
- package/dist/azure/recommendationState.d.ts.map +1 -1
- package/dist/azure/recommendations.d.ts +15 -5
- package/dist/azure/recommendations.d.ts.map +1 -1
- package/dist/azure/reports.d.ts +3 -2
- package/dist/azure/reports.d.ts.map +1 -1
- package/dist/azure/reports.js.map +1 -1
- package/dist/azure/resources.d.ts +12 -0
- package/dist/azure/resources.d.ts.map +1 -1
- package/dist/azure/serviceRetirement.d.ts +33 -2
- package/dist/azure/serviceRetirement.d.ts.map +1 -1
- package/dist/azure/subscriptions.d.ts +13 -2
- package/dist/azure/subscriptions.d.ts.map +1 -1
- package/dist/azure/views.d.ts +26 -3
- package/dist/azure/views.d.ts.map +1 -1
- package/dist/azure/views.js.map +1 -1
- package/dist/azure/vmPatchManagement.d.ts +109 -0
- package/dist/azure/vmPatchManagement.d.ts.map +1 -0
- package/dist/azure/vmPatchManagement.js +7 -0
- package/dist/azure/vmPatchManagement.js.map +1 -0
- package/dist/common/provider.d.ts +53 -0
- package/dist/common/provider.d.ts.map +1 -1
- package/dist/common/provider.js.map +1 -1
- package/dist/environment/canonicalization.d.ts +12 -0
- package/dist/environment/canonicalization.d.ts.map +1 -0
- package/dist/environment/canonicalization.js +40 -0
- package/dist/environment/canonicalization.js.map +1 -0
- package/dist/environment/contracts.d.ts +196 -0
- package/dist/environment/contracts.d.ts.map +1 -0
- package/dist/environment/contracts.js +21 -0
- package/dist/environment/contracts.js.map +1 -0
- package/dist/environment/index.d.ts +5 -0
- package/dist/environment/index.d.ts.map +1 -0
- package/dist/environment/index.js +21 -0
- package/dist/environment/index.js.map +1 -0
- package/dist/environment/internal.d.ts +21 -0
- package/dist/environment/internal.d.ts.map +1 -0
- package/dist/environment/internal.js +113 -0
- package/dist/environment/internal.js.map +1 -0
- package/dist/environment/references.d.ts +18 -0
- package/dist/environment/references.d.ts.map +1 -0
- package/dist/environment/references.js +206 -0
- package/dist/environment/references.js.map +1 -0
- package/dist/environment/validation.d.ts +28 -0
- package/dist/environment/validation.d.ts.map +1 -0
- package/dist/environment/validation.js +320 -0
- package/dist/environment/validation.js.map +1 -0
- package/dist/esm/accounts/accounts.js +1 -0
- package/dist/esm/accounts/azureManualOnboarding.js +8 -0
- package/dist/esm/accounts/azureSpSetup.js +14 -1
- package/dist/esm/accounts/azureSpSetupDurable.js +27 -1
- package/dist/esm/accounts/index.js +1 -0
- package/dist/esm/ai/index.js +10 -1
- package/dist/esm/aws/commitments-planning.js +5 -0
- package/dist/esm/aws/estates.js +7 -1
- package/dist/esm/aws/index.js +5 -0
- package/dist/esm/aws/organizationCommitments.js +48 -0
- package/dist/esm/aws/organizationCommitmentsApiValidation.js +220 -0
- package/dist/esm/aws/organizationCommitmentsValidation.js +224 -0
- package/dist/esm/aws/portalOrganizationCommitmentsPlanningPublicArtifactValidation.js +402 -0
- package/dist/esm/aws/portalOrganizationCommitmentsPlanningPublicArtifacts.js +2 -0
- package/dist/esm/aws/requests.js +1 -1
- package/dist/esm/azure/financialAnalyticsDelivery.js +2 -0
- package/dist/esm/azure/financialAnalyticsDisplayKernel.js +139 -0
- package/dist/esm/azure/financialAnalyticsValidation.js +61 -7
- package/dist/esm/azure/financialAuthorityProjectionKernel.js +159 -0
- package/dist/esm/azure/financialAuthorityViewValidation.js +204 -1
- package/dist/esm/azure/financialChargeComposition.js +36 -0
- package/dist/esm/azure/financialChargeCompositionKernel.js +48 -0
- package/dist/esm/azure/financialChargeCompositionValidation.js +155 -0
- package/dist/esm/azure/financialDataflowKernel.js +60 -7
- package/dist/esm/azure/financialDataflowValidation.js +146 -28
- package/dist/esm/azure/financialPolicyValidation.js +5 -0
- package/dist/esm/azure/financialProjectionValidation.js +43 -3
- package/dist/esm/azure/financialSavingsAuthorityValidation.js +124 -2
- package/dist/esm/azure/financialSavingsCoordinateValidation.js +62 -19
- package/dist/esm/azure/financialSavingsSurfaceProjection.js +2 -0
- package/dist/esm/azure/financialSavingsSurfaceProjectionKernel.js +318 -0
- package/dist/esm/azure/financialSavingsSurfaceProjectionValidation.js +126 -12
- package/dist/esm/azure/financialScope.js +6 -0
- package/dist/esm/azure/governance.js +1 -0
- package/dist/esm/azure/vmPatchManagement.js +3 -0
- package/dist/esm/entries/commitments-planning.js +2 -2
- package/dist/esm/entries/environment.js +3 -0
- package/dist/esm/environment/canonicalization.js +34 -0
- package/dist/esm/environment/contracts.js +17 -0
- package/dist/esm/environment/index.js +4 -0
- package/dist/esm/environment/internal.js +97 -0
- package/dist/esm/environment/references.js +194 -0
- package/dist/esm/environment/validation.js +303 -0
- package/dist/esm/index.js +2 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/portfolio/portfolioOperations.d.ts +1 -1
- package/dist/portfolio/portfolioOperations.d.ts.map +1 -1
- package/package.json +15 -3
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
import { AWS_ORGANIZATION_COMMITMENTS_ISSUE_CODES, AWS_ORGANIZATION_COMMITMENTS_REFRESH_STAGES, AWS_ORGANIZATION_COMMITMENTS_REFRESH_STAGE_STATUSES, AWS_ORGANIZATION_COMMITMENTS_REFRESH_STATES, AWS_ORGANIZATION_COMMITMENTS_SCHEMA_VERSION, AWS_ORGANIZATION_COMMITMENTS_SCOPE_UNAVAILABLE_REASONS, } from './organizationCommitments.js';
|
|
2
|
+
import { asRecord, assertExactKeys, assertPublicJson, assertValue, isoTimestamp, nonNegativeInteger, requiredBoolean, requiredEnum, requiredString, validateGeneration, } from './portalPublicArtifactValidationCommon.js';
|
|
3
|
+
import { assertAccount } from './pluginPublicArtifactValidationHelpers.js';
|
|
4
|
+
const ORGANIZATION_ID = /^o-[a-z0-9]{10,32}$/u;
|
|
5
|
+
/** Validates one API-authored organization commitments refresh receipt. */
|
|
6
|
+
export function validateAwsOrganizationCommitmentsRefreshAcceptedResponse(value, expected) {
|
|
7
|
+
const response = asRecord(value, 'organizationCommitmentsAdmission');
|
|
8
|
+
const disposition = requiredEnum(response.disposition, ['accepted', 'already-running', 'cooldown'], 'organizationCommitmentsAdmission.disposition');
|
|
9
|
+
assertExactKeys(response, [
|
|
10
|
+
'schemaVersion',
|
|
11
|
+
'provider',
|
|
12
|
+
'scopeType',
|
|
13
|
+
'companyId',
|
|
14
|
+
'estateId',
|
|
15
|
+
'organizationId',
|
|
16
|
+
'managementAccountId',
|
|
17
|
+
'requestId',
|
|
18
|
+
'correlationId',
|
|
19
|
+
'manifestRevision',
|
|
20
|
+
'disposition',
|
|
21
|
+
'acceptedAt',
|
|
22
|
+
...(disposition === 'cooldown' ? ['nextEligibleAt'] : []),
|
|
23
|
+
], 'organizationCommitmentsAdmission');
|
|
24
|
+
validateOrganizationApiIdentity(response, expected, 'organizationCommitmentsAdmission');
|
|
25
|
+
assertValue(response.manifestRevision, expected.manifestRevision, 'organizationCommitmentsAdmission.manifestRevision');
|
|
26
|
+
requiredString(response.requestId, 'organizationCommitmentsAdmission.requestId');
|
|
27
|
+
requiredString(response.correlationId, 'organizationCommitmentsAdmission.correlationId');
|
|
28
|
+
const acceptedAt = isoTimestamp(response.acceptedAt, 'organizationCommitmentsAdmission.acceptedAt');
|
|
29
|
+
if (disposition === 'cooldown') {
|
|
30
|
+
const nextEligibleAt = isoTimestamp(response.nextEligibleAt, 'organizationCommitmentsAdmission.nextEligibleAt');
|
|
31
|
+
if (Date.parse(nextEligibleAt) <= Date.parse(acceptedAt)) {
|
|
32
|
+
throw new Error('organizationCommitmentsAdmission.nextEligibleAt must be later than acceptedAt.');
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
assertPublicJson(response, 'organizationCommitmentsAdmission');
|
|
36
|
+
return value;
|
|
37
|
+
}
|
|
38
|
+
/** Validates the safe organization scope selector response for one company. */
|
|
39
|
+
export function validateAwsOrganizationCommitmentsScopeListResponse(value, expectedCompanyId) {
|
|
40
|
+
const response = asRecord(value, 'organizationCommitmentsScopes');
|
|
41
|
+
assertExactKeys(response, ['schemaVersion', 'provider', 'companyId', 'organizations'], 'organizationCommitmentsScopes');
|
|
42
|
+
assertValue(response.schemaVersion, AWS_ORGANIZATION_COMMITMENTS_SCHEMA_VERSION, 'organizationCommitmentsScopes.schemaVersion');
|
|
43
|
+
assertValue(response.provider, 'AWS', 'organizationCommitmentsScopes.provider');
|
|
44
|
+
assertValue(response.companyId, requiredString(expectedCompanyId, 'expectedCompanyId'), 'organizationCommitmentsScopes.companyId');
|
|
45
|
+
if (!Array.isArray(response.organizations))
|
|
46
|
+
throw new Error('organizationCommitmentsScopes.organizations must be an array.');
|
|
47
|
+
const estateIds = [];
|
|
48
|
+
const organizationIds = [];
|
|
49
|
+
response.organizations.forEach((entry, index) => {
|
|
50
|
+
const field = `organizationCommitmentsScopes.organizations[${index}]`;
|
|
51
|
+
const scope = asRecord(entry, field);
|
|
52
|
+
assertExactKeys(scope, ['scopeType', 'estateId', 'name', 'organizationId', 'managementAccountId', 'accountCount', 'availability', 'canView', 'canRefresh', 'reason'], field);
|
|
53
|
+
assertValue(scope.scopeType, 'organization', `${field}.scopeType`);
|
|
54
|
+
estateIds.push(requiredString(scope.estateId, `${field}.estateId`));
|
|
55
|
+
requiredString(scope.name, `${field}.name`);
|
|
56
|
+
organizationIds.push(requiredOrganizationId(scope.organizationId, `${field}.organizationId`));
|
|
57
|
+
requiredAccountId(scope.managementAccountId, `${field}.managementAccountId`);
|
|
58
|
+
const accountCount = nonNegativeInteger(scope.accountCount, `${field}.accountCount`);
|
|
59
|
+
if (accountCount < 1)
|
|
60
|
+
throw new Error(`${field}.accountCount must include the management account.`);
|
|
61
|
+
const availability = requiredEnum(scope.availability, ['available', 'unavailable'], `${field}.availability`);
|
|
62
|
+
requiredBoolean(scope.canView, `${field}.canView`);
|
|
63
|
+
const canRefresh = requiredBoolean(scope.canRefresh, `${field}.canRefresh`);
|
|
64
|
+
if (availability === 'available') {
|
|
65
|
+
if (scope.reason !== undefined)
|
|
66
|
+
throw new Error(`${field}.reason is not allowed when the scope is available.`);
|
|
67
|
+
}
|
|
68
|
+
else {
|
|
69
|
+
requiredEnum(scope.reason, AWS_ORGANIZATION_COMMITMENTS_SCOPE_UNAVAILABLE_REASONS, `${field}.reason`);
|
|
70
|
+
if (canRefresh)
|
|
71
|
+
throw new Error(`${field}.canRefresh must be false when the scope is unavailable.`);
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
assertUniqueSorted(estateIds, 'organizationCommitmentsScopes.organizations estateId');
|
|
75
|
+
assertUnique(organizationIds, 'organizationCommitmentsScopes.organizations organizationId');
|
|
76
|
+
assertPublicJson(response, 'organizationCommitmentsScopes');
|
|
77
|
+
return value;
|
|
78
|
+
}
|
|
79
|
+
/** Validates one sanitized Blob-backed organization commitments status projection. */
|
|
80
|
+
export function validateAwsOrganizationCommitmentsRefreshStatusResponse(value, expected) {
|
|
81
|
+
const response = asRecord(value, 'organizationCommitmentsStatus');
|
|
82
|
+
assertExactKeys(response, [
|
|
83
|
+
'schemaVersion',
|
|
84
|
+
'provider',
|
|
85
|
+
'scopeType',
|
|
86
|
+
'companyId',
|
|
87
|
+
'estateId',
|
|
88
|
+
'organizationId',
|
|
89
|
+
'managementAccountId',
|
|
90
|
+
'targetManifestRevision',
|
|
91
|
+
'state',
|
|
92
|
+
'requestId',
|
|
93
|
+
'correlationId',
|
|
94
|
+
'requestedAt',
|
|
95
|
+
'updatedAt',
|
|
96
|
+
'stages',
|
|
97
|
+
'failureCode',
|
|
98
|
+
'retryable',
|
|
99
|
+
'nextEligibleAt',
|
|
100
|
+
'latestArtifact',
|
|
101
|
+
], 'organizationCommitmentsStatus');
|
|
102
|
+
validateOrganizationApiIdentity(response, expected, 'organizationCommitmentsStatus');
|
|
103
|
+
assertValue(response.targetManifestRevision, requiredString(expected.targetManifestRevision, 'expected.targetManifestRevision'), 'organizationCommitmentsStatus.targetManifestRevision');
|
|
104
|
+
const state = requiredEnum(response.state, AWS_ORGANIZATION_COMMITMENTS_REFRESH_STATES, 'organizationCommitmentsStatus.state');
|
|
105
|
+
isoTimestamp(response.updatedAt, 'organizationCommitmentsStatus.updatedAt');
|
|
106
|
+
if (state === 'not-started') {
|
|
107
|
+
for (const key of ['requestId', 'correlationId', 'requestedAt', 'failureCode', 'retryable']) {
|
|
108
|
+
if (response[key] !== undefined)
|
|
109
|
+
throw new Error(`organizationCommitmentsStatus.${key} is not allowed before a refresh starts.`);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
else {
|
|
113
|
+
const requestId = requiredString(response.requestId, 'organizationCommitmentsStatus.requestId');
|
|
114
|
+
if (expected.requestId !== undefined)
|
|
115
|
+
assertValue(requestId, expected.requestId, 'organizationCommitmentsStatus.requestId');
|
|
116
|
+
requiredString(response.correlationId, 'organizationCommitmentsStatus.correlationId');
|
|
117
|
+
isoTimestamp(response.requestedAt, 'organizationCommitmentsStatus.requestedAt');
|
|
118
|
+
}
|
|
119
|
+
if (response.failureCode !== undefined) {
|
|
120
|
+
requiredEnum(response.failureCode, AWS_ORGANIZATION_COMMITMENTS_ISSUE_CODES, 'organizationCommitmentsStatus.failureCode');
|
|
121
|
+
}
|
|
122
|
+
if (state === 'failed' && response.failureCode === undefined) {
|
|
123
|
+
throw new Error('organizationCommitmentsStatus.failureCode is required when the refresh failed.');
|
|
124
|
+
}
|
|
125
|
+
if (response.retryable !== undefined)
|
|
126
|
+
requiredBoolean(response.retryable, 'organizationCommitmentsStatus.retryable');
|
|
127
|
+
if (response.nextEligibleAt !== undefined)
|
|
128
|
+
isoTimestamp(response.nextEligibleAt, 'organizationCommitmentsStatus.nextEligibleAt');
|
|
129
|
+
validateStages(response.stages);
|
|
130
|
+
if (response.latestArtifact !== undefined)
|
|
131
|
+
validateLatestArtifact(response.latestArtifact, state);
|
|
132
|
+
if (state === 'fresh' && response.latestArtifact === undefined) {
|
|
133
|
+
throw new Error('organizationCommitmentsStatus.latestArtifact is required when the refresh is fresh.');
|
|
134
|
+
}
|
|
135
|
+
assertPublicJson(response, 'organizationCommitmentsStatus');
|
|
136
|
+
return value;
|
|
137
|
+
}
|
|
138
|
+
function validateStages(value) {
|
|
139
|
+
if (!Array.isArray(value) || value.length !== AWS_ORGANIZATION_COMMITMENTS_REFRESH_STAGES.length) {
|
|
140
|
+
throw new Error('organizationCommitmentsStatus.stages must contain every canonical stage exactly once.');
|
|
141
|
+
}
|
|
142
|
+
value.forEach((entry, index) => {
|
|
143
|
+
const field = `organizationCommitmentsStatus.stages[${index}]`;
|
|
144
|
+
const stage = asRecord(entry, field);
|
|
145
|
+
assertExactKeys(stage, ['id', 'status', 'completedCount', 'totalCount', 'failureCode', 'retryable', 'updatedAt'], field);
|
|
146
|
+
assertValue(stage.id, AWS_ORGANIZATION_COMMITMENTS_REFRESH_STAGES[index], `${field}.id`);
|
|
147
|
+
const status = requiredEnum(stage.status, AWS_ORGANIZATION_COMMITMENTS_REFRESH_STAGE_STATUSES, `${field}.status`);
|
|
148
|
+
if ((stage.completedCount === undefined) !== (stage.totalCount === undefined)) {
|
|
149
|
+
throw new Error(`${field}.completedCount and totalCount must be supplied together.`);
|
|
150
|
+
}
|
|
151
|
+
if (stage.completedCount !== undefined) {
|
|
152
|
+
const completed = nonNegativeInteger(stage.completedCount, `${field}.completedCount`);
|
|
153
|
+
const total = nonNegativeInteger(stage.totalCount, `${field}.totalCount`);
|
|
154
|
+
if (completed > total)
|
|
155
|
+
throw new Error(`${field}.completedCount must not exceed totalCount.`);
|
|
156
|
+
}
|
|
157
|
+
if (stage.failureCode !== undefined)
|
|
158
|
+
requiredEnum(stage.failureCode, AWS_ORGANIZATION_COMMITMENTS_ISSUE_CODES, `${field}.failureCode`);
|
|
159
|
+
if (status === 'failed' && stage.failureCode === undefined)
|
|
160
|
+
throw new Error(`${field}.failureCode is required when the stage failed.`);
|
|
161
|
+
if (stage.retryable !== undefined)
|
|
162
|
+
requiredBoolean(stage.retryable, `${field}.retryable`);
|
|
163
|
+
isoTimestamp(stage.updatedAt, `${field}.updatedAt`);
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
function validateLatestArtifact(value, refreshState) {
|
|
167
|
+
const artifact = asRecord(value, 'organizationCommitmentsStatus.latestArtifact');
|
|
168
|
+
assertExactKeys(artifact, ['state', 'generatedAt', 'artifactGeneration'], 'organizationCommitmentsStatus.latestArtifact');
|
|
169
|
+
const state = requiredEnum(artifact.state, ['available', 'stale', 'unavailable'], 'organizationCommitmentsStatus.latestArtifact.state');
|
|
170
|
+
if (state === 'unavailable') {
|
|
171
|
+
if (artifact.generatedAt !== undefined || artifact.artifactGeneration !== undefined) {
|
|
172
|
+
throw new Error('organizationCommitmentsStatus.latestArtifact unavailable state cannot claim generation evidence.');
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
else {
|
|
176
|
+
const generatedAt = isoTimestamp(artifact.generatedAt, 'organizationCommitmentsStatus.latestArtifact.generatedAt');
|
|
177
|
+
const generation = validateGeneration(artifact.artifactGeneration, 'organizationCommitmentsStatus.latestArtifact.artifactGeneration');
|
|
178
|
+
if (Date.parse(generatedAt) > Date.parse(generation.generatedAt)) {
|
|
179
|
+
throw new Error('organizationCommitmentsStatus.latestArtifact.generatedAt must not exceed its generation time.');
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
if (refreshState === 'fresh' && state !== 'available') {
|
|
183
|
+
throw new Error('organizationCommitmentsStatus fresh state requires an available latest artifact.');
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
function validateOrganizationApiIdentity(value, expected, field) {
|
|
187
|
+
assertValue(value.schemaVersion, AWS_ORGANIZATION_COMMITMENTS_SCHEMA_VERSION, `${field}.schemaVersion`);
|
|
188
|
+
assertValue(value.provider, 'AWS', `${field}.provider`);
|
|
189
|
+
assertValue(value.scopeType, 'organization', `${field}.scopeType`);
|
|
190
|
+
assertValue(value.companyId, requiredString(expected.companyId, 'expected.companyId'), `${field}.companyId`);
|
|
191
|
+
assertValue(value.estateId, requiredString(expected.estateId, 'expected.estateId'), `${field}.estateId`);
|
|
192
|
+
const organizationId = requiredOrganizationId(value.organizationId, `${field}.organizationId`);
|
|
193
|
+
const managementAccountId = requiredAccountId(value.managementAccountId, `${field}.managementAccountId`);
|
|
194
|
+
if (expected.organizationId !== undefined)
|
|
195
|
+
assertValue(organizationId, expected.organizationId, `${field}.organizationId`);
|
|
196
|
+
if (expected.managementAccountId !== undefined) {
|
|
197
|
+
assertValue(managementAccountId, expected.managementAccountId, `${field}.managementAccountId`);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
function requiredAccountId(value, field) {
|
|
201
|
+
const accountId = requiredString(value, field);
|
|
202
|
+
assertAccount(value, accountId, field);
|
|
203
|
+
return accountId;
|
|
204
|
+
}
|
|
205
|
+
function requiredOrganizationId(value, field) {
|
|
206
|
+
const organizationId = requiredString(value, field);
|
|
207
|
+
if (!ORGANIZATION_ID.test(organizationId))
|
|
208
|
+
throw new Error(`${field} must be a canonical AWS organization id.`);
|
|
209
|
+
return organizationId;
|
|
210
|
+
}
|
|
211
|
+
function assertUniqueSorted(values, field) {
|
|
212
|
+
assertUnique(values, field);
|
|
213
|
+
const sorted = [...values].sort((left, right) => left.localeCompare(right));
|
|
214
|
+
if (JSON.stringify(values) !== JSON.stringify(sorted))
|
|
215
|
+
throw new Error(`${field} must be sorted.`);
|
|
216
|
+
}
|
|
217
|
+
function assertUnique(values, field) {
|
|
218
|
+
if (new Set(values).size !== values.length)
|
|
219
|
+
throw new Error(`${field} must not contain duplicates.`);
|
|
220
|
+
}
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
import { ProviderName } from '../common/provider.js';
|
|
2
|
+
import { asRecord, assertAccount, assertExactKeys, assertValue, requiredEnum, requiredString } from './pluginPublicArtifactValidationHelpers.js';
|
|
3
|
+
const ORGANIZATION_ID = /^o-[a-z0-9]{10,32}$/u;
|
|
4
|
+
const FORBIDDEN_ORGANIZATION_COMMITMENTS_KEYS = new Set([
|
|
5
|
+
'billingScopeId',
|
|
6
|
+
'blobPath',
|
|
7
|
+
'checkpoint',
|
|
8
|
+
'cloudAccountId',
|
|
9
|
+
'connectionString',
|
|
10
|
+
'credentialHealth',
|
|
11
|
+
'credentialId',
|
|
12
|
+
'credentialReference',
|
|
13
|
+
'externalId',
|
|
14
|
+
'managementGroupId',
|
|
15
|
+
'pricingQuote',
|
|
16
|
+
'quotePolicy',
|
|
17
|
+
'resourceGroupId',
|
|
18
|
+
'roleArn',
|
|
19
|
+
'saga',
|
|
20
|
+
'sasToken',
|
|
21
|
+
'secretArn',
|
|
22
|
+
'secretReference',
|
|
23
|
+
'storageCapacity',
|
|
24
|
+
'storagePath',
|
|
25
|
+
'subscription',
|
|
26
|
+
'subscriptionId',
|
|
27
|
+
'tenantId',
|
|
28
|
+
'unlockFinancialLedger',
|
|
29
|
+
]);
|
|
30
|
+
/** Validates the multi-account identity boundary of an AWS organization commitments view. */
|
|
31
|
+
export function validateAwsOrganizationCommitmentsPlanningViewIdentity(value, expected) {
|
|
32
|
+
const view = asRecord(value, 'organizationCommitmentsPlanning');
|
|
33
|
+
const providerScope = asRecord(view.providerScope, 'organizationCommitmentsPlanning.providerScope');
|
|
34
|
+
assertExactKeys(providerScope, ['providerName', 'providerScopeId', 'scopeType', 'companyId', 'estateId', 'organizationId', 'managementAccountId'], 'organizationCommitmentsPlanning.providerScope');
|
|
35
|
+
assertValue(providerScope.providerName, ProviderName.Aws, 'organizationCommitmentsPlanning.providerScope.providerName');
|
|
36
|
+
assertValue(providerScope.scopeType, 'organization', 'organizationCommitmentsPlanning.providerScope.scopeType');
|
|
37
|
+
const companyId = requiredString(providerScope.companyId, 'organizationCommitmentsPlanning.providerScope.companyId');
|
|
38
|
+
const estateId = requiredString(providerScope.estateId, 'organizationCommitmentsPlanning.providerScope.estateId');
|
|
39
|
+
const organizationId = requiredOrganizationId(providerScope.organizationId, 'organizationCommitmentsPlanning.providerScope.organizationId');
|
|
40
|
+
assertValue(providerScope.providerScopeId, organizationId, 'organizationCommitmentsPlanning.providerScope.providerScopeId');
|
|
41
|
+
const managementAccountId = requiredAccountId(providerScope.managementAccountId, 'organizationCommitmentsPlanning.providerScope.managementAccountId');
|
|
42
|
+
assertValue(expected.companyId, companyId, 'expected.companyId');
|
|
43
|
+
assertValue(expected.estateId, estateId, 'expected.estateId');
|
|
44
|
+
assertValue(expected.organizationId, organizationId, 'expected.organizationId');
|
|
45
|
+
assertValue(expected.managementAccountId, managementAccountId, 'expected.managementAccountId');
|
|
46
|
+
assertValue(expected.manifestRevision, requiredString(view.manifestRevision, 'organizationCommitmentsPlanning.manifestRevision'), 'expected.manifestRevision');
|
|
47
|
+
const accountMembership = validateAccounts(view.accounts, managementAccountId);
|
|
48
|
+
validateExpectedMembers(expected.memberAccountIds, accountMembership.accountIds);
|
|
49
|
+
validateInventory(view.inventory, accountMembership.members);
|
|
50
|
+
validatePayerAggregates(view.payerAggregates, accountMembership.members, managementAccountId);
|
|
51
|
+
validatePurchaseRecommendations(view.purchaseRecommendations, accountMembership.members, managementAccountId);
|
|
52
|
+
validateAttribution(view.allocation, accountMembership.members, false, 'organizationCommitmentsPlanning.allocation');
|
|
53
|
+
validateAttribution(view.resourceAttribution, accountMembership.members, true, 'organizationCommitmentsPlanning.resourceAttribution');
|
|
54
|
+
validateBoundIdentities(view, accountMembership.members, organizationId, companyId, estateId, managementAccountId, 'organizationCommitmentsPlanning');
|
|
55
|
+
}
|
|
56
|
+
function validateAccounts(value, managementAccountId) {
|
|
57
|
+
if (!Array.isArray(value) || value.length === 0)
|
|
58
|
+
throw new Error('organizationCommitmentsPlanning.accounts must be a non-empty array.');
|
|
59
|
+
const accountIds = value.map((entry, index) => {
|
|
60
|
+
const field = `organizationCommitmentsPlanning.accounts[${index}]`;
|
|
61
|
+
const account = asRecord(entry, field);
|
|
62
|
+
assertExactKeys(account, ['accountId', 'displayName', 'role', 'inventoryStatus', 'lastSuccessfulRefreshAt'], field);
|
|
63
|
+
const accountId = requiredAccountId(account.accountId, `${field}.accountId`);
|
|
64
|
+
const role = requiredEnum(account.role, ['management', 'member'], `${field}.role`);
|
|
65
|
+
if ((accountId === managementAccountId) !== (role === 'management')) {
|
|
66
|
+
throw new Error(`${field}.role must identify the exact management account.`);
|
|
67
|
+
}
|
|
68
|
+
return accountId;
|
|
69
|
+
});
|
|
70
|
+
if (new Set(accountIds).size !== accountIds.length)
|
|
71
|
+
throw new Error('organizationCommitmentsPlanning.accounts must not contain duplicates.');
|
|
72
|
+
const sorted = [...accountIds].sort((left, right) => left.localeCompare(right));
|
|
73
|
+
if (JSON.stringify(sorted) !== JSON.stringify(accountIds))
|
|
74
|
+
throw new Error('organizationCommitmentsPlanning.accounts must be sorted by accountId.');
|
|
75
|
+
if (!accountIds.includes(managementAccountId))
|
|
76
|
+
throw new Error('organizationCommitmentsPlanning.accounts must contain the management account.');
|
|
77
|
+
return { accountIds, members: new Set(accountIds) };
|
|
78
|
+
}
|
|
79
|
+
function validateInventory(value, members) {
|
|
80
|
+
if (!Array.isArray(value))
|
|
81
|
+
throw new Error('organizationCommitmentsPlanning.inventory must be an array.');
|
|
82
|
+
value.forEach((entry, index) => {
|
|
83
|
+
const field = `organizationCommitmentsPlanning.inventory[${index}]`;
|
|
84
|
+
const item = asRecord(entry, field);
|
|
85
|
+
assertValue(item.sourceKind, 'aws-native', `${field}.sourceKind`);
|
|
86
|
+
assertValue(item.provider, ProviderName.Aws, `${field}.provider`);
|
|
87
|
+
requiredMember(item.ownerAccountId, members, `${field}.ownerAccountId`);
|
|
88
|
+
if (item.shape !== undefined)
|
|
89
|
+
validateAwsShape(item.shape, `${field}.shape`);
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
function validatePayerAggregates(value, members, managementAccountId) {
|
|
93
|
+
if (!Array.isArray(value))
|
|
94
|
+
throw new Error('organizationCommitmentsPlanning.payerAggregates must be an array.');
|
|
95
|
+
value.forEach((entry, index) => {
|
|
96
|
+
const field = `organizationCommitmentsPlanning.payerAggregates[${index}]`;
|
|
97
|
+
const aggregate = asRecord(entry, field);
|
|
98
|
+
const payerAccountId = requiredMember(aggregate.payerAccountId, members, `${field}.payerAccountId`);
|
|
99
|
+
assertValue(payerAccountId, managementAccountId, `${field}.payerAccountId`);
|
|
100
|
+
validateAwsSource(aggregate.source, `${field}.source`);
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
function validatePurchaseRecommendations(value, members, managementAccountId) {
|
|
104
|
+
if (value === undefined)
|
|
105
|
+
return;
|
|
106
|
+
if (!Array.isArray(value))
|
|
107
|
+
throw new Error('organizationCommitmentsPlanning.purchaseRecommendations must be an array.');
|
|
108
|
+
value.forEach((entry, index) => {
|
|
109
|
+
const field = `organizationCommitmentsPlanning.purchaseRecommendations[${index}]`;
|
|
110
|
+
const recommendation = asRecord(entry, field);
|
|
111
|
+
assertValue(recommendation.purchaseScope, 'payer', `${field}.purchaseScope`);
|
|
112
|
+
const payerAccountId = requiredMember(recommendation.payerAccountId, members, `${field}.payerAccountId`);
|
|
113
|
+
assertValue(payerAccountId, managementAccountId, `${field}.payerAccountId`);
|
|
114
|
+
if (recommendation.recommendedAccountId !== undefined) {
|
|
115
|
+
requiredMember(recommendation.recommendedAccountId, members, `${field}.recommendedAccountId`);
|
|
116
|
+
}
|
|
117
|
+
validateAwsSource(recommendation.source, `${field}.source`);
|
|
118
|
+
validateAwsShape(recommendation.targetShape, `${field}.targetShape`);
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
function validateAttribution(value, members, resource, field) {
|
|
122
|
+
const evidence = asRecord(value, field);
|
|
123
|
+
if (evidence.status === 'unavailable') {
|
|
124
|
+
assertExactKeys(evidence, ['status', 'reason'], field);
|
|
125
|
+
requiredEnum(evidence.reason, ['not-collected', 'not-proved', 'source-unavailable'], `${field}.reason`);
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
assertValue(evidence.status, 'available', `${field}.status`);
|
|
129
|
+
assertExactKeys(evidence, ['status', 'source', 'records'], field);
|
|
130
|
+
validateAwsSource(evidence.source, `${field}.source`, true);
|
|
131
|
+
if (!Array.isArray(evidence.records) || evidence.records.length === 0) {
|
|
132
|
+
throw new Error(`${field}.records must be non-empty when attribution is available.`);
|
|
133
|
+
}
|
|
134
|
+
evidence.records.forEach((entry, index) => {
|
|
135
|
+
const record = asRecord(entry, `${field}.records[${index}]`);
|
|
136
|
+
if (resource) {
|
|
137
|
+
requiredMember(record.accountId, members, `${field}.records[${index}].accountId`);
|
|
138
|
+
}
|
|
139
|
+
else {
|
|
140
|
+
requiredMember(record.beneficiaryAccountId, members, `${field}.records[${index}].beneficiaryAccountId`);
|
|
141
|
+
if (record.ownerAccountId !== undefined)
|
|
142
|
+
requiredMember(record.ownerAccountId, members, `${field}.records[${index}].ownerAccountId`);
|
|
143
|
+
}
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
function validateAwsSource(value, field, allowDerived = false) {
|
|
147
|
+
const source = asRecord(value, field);
|
|
148
|
+
const allowed = allowDerived ? ['aws-native', 'spotto-derived'] : ['aws-native'];
|
|
149
|
+
requiredEnum(source.sourceKind, allowed, `${field}.sourceKind`);
|
|
150
|
+
}
|
|
151
|
+
function validateAwsShape(value, field) {
|
|
152
|
+
const shape = asRecord(value, field);
|
|
153
|
+
assertValue(shape.provider, 'aws', `${field}.provider`);
|
|
154
|
+
}
|
|
155
|
+
function validateBoundIdentities(value, members, organizationId, companyId, estateId, managementAccountId, field) {
|
|
156
|
+
if (Array.isArray(value)) {
|
|
157
|
+
value.forEach((entry, index) => validateBoundIdentities(entry, members, organizationId, companyId, estateId, managementAccountId, `${field}[${index}]`));
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
if (!value || typeof value !== 'object') {
|
|
161
|
+
if (typeof value === 'string')
|
|
162
|
+
validateArnAccount(value, members, field);
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
for (const [key, child] of Object.entries(value)) {
|
|
166
|
+
if (FORBIDDEN_ORGANIZATION_COMMITMENTS_KEYS.has(key)) {
|
|
167
|
+
throw new Error(`${field}.${key} is not allowed in an AWS organization commitments artifact.`);
|
|
168
|
+
}
|
|
169
|
+
if (key === 'accountId' || key === 'ownerAccountId' || key === 'beneficiaryAccountId' || key === 'payerAccountId') {
|
|
170
|
+
requiredMember(child, members, `${field}.${key}`);
|
|
171
|
+
}
|
|
172
|
+
if (key === 'organizationId')
|
|
173
|
+
assertValue(child, organizationId, `${field}.${key}`);
|
|
174
|
+
if (key === 'companyId')
|
|
175
|
+
assertValue(child, companyId, `${field}.${key}`);
|
|
176
|
+
if (key === 'estateId')
|
|
177
|
+
assertValue(child, estateId, `${field}.${key}`);
|
|
178
|
+
if (key === 'managementAccountId')
|
|
179
|
+
assertAccount(child, managementAccountId, `${field}.${key}`);
|
|
180
|
+
if (key === 'providerName')
|
|
181
|
+
assertValue(child, ProviderName.Aws, `${field}.${key}`);
|
|
182
|
+
if (key === 'provider')
|
|
183
|
+
assertValue(child, 'aws', `${field}.${key}`);
|
|
184
|
+
if (key === 'sourceKind' && child === 'azure-native') {
|
|
185
|
+
throw new Error(`${field}.${key} cannot contain Azure-native evidence.`);
|
|
186
|
+
}
|
|
187
|
+
validateBoundIdentities(child, members, organizationId, companyId, estateId, managementAccountId, `${field}.${key}`);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
function validateArnAccount(value, members, field) {
|
|
191
|
+
if (!value.startsWith('arn:'))
|
|
192
|
+
return;
|
|
193
|
+
const arnAccountId = value.split(':', 6)[4];
|
|
194
|
+
if (arnAccountId && !members.has(arnAccountId))
|
|
195
|
+
throw new Error(`${field} contains an ARN outside the declared organization membership.`);
|
|
196
|
+
}
|
|
197
|
+
function requiredMember(value, members, field) {
|
|
198
|
+
const accountId = requiredAccountId(value, field);
|
|
199
|
+
if (!members.has(accountId))
|
|
200
|
+
throw new Error(`${field} must belong to the declared organization membership.`);
|
|
201
|
+
return accountId;
|
|
202
|
+
}
|
|
203
|
+
function requiredAccountId(value, field) {
|
|
204
|
+
const accountId = requiredString(value, field);
|
|
205
|
+
assertAccount(value, accountId, field);
|
|
206
|
+
return accountId;
|
|
207
|
+
}
|
|
208
|
+
function requiredOrganizationId(value, field) {
|
|
209
|
+
const organizationId = requiredString(value, field);
|
|
210
|
+
if (!ORGANIZATION_ID.test(organizationId))
|
|
211
|
+
throw new Error(`${field} must be a canonical AWS organization id.`);
|
|
212
|
+
return organizationId;
|
|
213
|
+
}
|
|
214
|
+
function validateExpectedMembers(expected, actual) {
|
|
215
|
+
const normalized = expected.map((accountId, index) => requiredAccountId(accountId, `expected.memberAccountIds[${index}]`));
|
|
216
|
+
if (new Set(normalized).size !== normalized.length)
|
|
217
|
+
throw new Error('expected.memberAccountIds must not contain duplicates.');
|
|
218
|
+
const sorted = [...normalized].sort((left, right) => left.localeCompare(right));
|
|
219
|
+
if (JSON.stringify(sorted) !== JSON.stringify(normalized))
|
|
220
|
+
throw new Error('expected.memberAccountIds must be sorted.');
|
|
221
|
+
if (JSON.stringify(normalized) !== JSON.stringify(actual)) {
|
|
222
|
+
throw new Error('expected.memberAccountIds must match the artifact membership exactly.');
|
|
223
|
+
}
|
|
224
|
+
}
|