@aifabrix/builder 2.44.2 → 2.44.4
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/.npmrc.token +1 -1
- package/integration/roundtrip-test-local/README.md +1 -2
- package/integration/roundtrip-test-local2/README.md +1 -2
- package/jest.projects.js +12 -1
- package/lib/api/certificates.api.js +21 -3
- package/lib/api/types/certificates.types.js +1 -1
- package/lib/app/show-display.js +60 -16
- package/lib/certification/merge-certification-from-artifact.js +46 -16
- package/lib/certification/post-unified-cert-sync.js +13 -2
- package/lib/certification/sync-after-external-command.js +6 -3
- package/lib/certification/sync-system-certification.js +60 -14
- package/lib/cli/setup-app.test-commands.js +67 -35
- package/lib/cli/setup-utility.js +1 -1
- package/lib/commands/datasource-unified-test-cli.js +81 -46
- package/lib/commands/datasource-unified-test-cli.options.js +4 -2
- package/lib/commands/datasource.js +3 -31
- package/lib/commands/repair-datasource-keys.js +1 -1
- package/lib/commands/repair-datasource-openapi.js +57 -0
- package/lib/commands/repair-datasource.js +5 -0
- package/lib/commands/repair-internal.js +2 -4
- package/lib/commands/repair.js +1 -2
- package/lib/commands/test-e2e-external.js +5 -6
- package/lib/commands/upload.js +18 -4
- package/lib/commands/wizard-dataplane.js +14 -6
- package/lib/datasource/datasource-validate-display.js +162 -0
- package/lib/datasource/datasource-validate-summary.js +194 -0
- package/lib/datasource/test-e2e.js +65 -37
- package/lib/datasource/unified-validation-run-body.js +1 -2
- package/lib/datasource/validate.js +14 -6
- package/lib/external-system/test.js +12 -8
- package/lib/generator/external-controller-manifest.js +12 -2
- package/lib/schema/cip-capacity-display.fallback.json +7 -0
- package/lib/schema/datasource-test-run.schema.json +79 -1
- package/lib/schema/external-datasource.schema.json +94 -2
- package/lib/schema/external-system.schema.json +29 -6
- package/lib/schema/flag-map-validation-run.json +1 -2
- package/lib/schema/type/document-storage.json +83 -3
- package/lib/utils/configuration-env-resolver.js +38 -0
- package/lib/utils/dataplane-resolver.js +3 -2
- package/lib/utils/datasource-test-run-capacity-operations.js +149 -0
- package/lib/utils/datasource-test-run-debug-display.js +143 -1
- package/lib/utils/datasource-test-run-display.js +46 -33
- package/lib/utils/datasource-test-run-tty-log.js +6 -2
- package/lib/utils/datasource-test-run-tty-meta-lines.js +123 -0
- package/lib/utils/error-formatter.js +32 -2
- package/lib/utils/external-system-readiness-core.js +39 -0
- package/lib/utils/external-system-readiness-deploy-display.js +2 -3
- package/lib/utils/external-system-readiness-display-internals.js +3 -2
- package/lib/utils/external-system-system-test-tty.js +33 -9
- package/lib/utils/external-system-validators.js +62 -5
- package/lib/utils/load-cip-capacity-display-config.js +130 -0
- package/lib/utils/paths.js +10 -3
- package/lib/utils/schema-resolver.js +98 -2
- package/lib/utils/validation-run-poll.js +15 -4
- package/lib/utils/validation-run-request.js +4 -6
- package/lib/validation/dimension-display-helpers.js +60 -0
- package/lib/validation/external-manifest-validator.js +22 -15
- package/lib/validation/validate-display-log-helpers.js +39 -0
- package/lib/validation/validate-display.js +89 -83
- package/package.json +1 -1
- package/templates/applications/dataplane/env.template +2 -1
- package/templates/external-system/README.md.hbs +1 -2
package/.npmrc.token
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
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
|
|
@@ -123,8 +123,7 @@ Options:
|
|
|
123
123
|
-e, --env <env> Environment: dev, tst, or pro
|
|
124
124
|
-v, --verbose Show detailed step output and poll progress
|
|
125
125
|
--debug Include debug output and write log to integration/roundtrip-test-local/logs/
|
|
126
|
-
--
|
|
127
|
-
--record-id <id> Record ID for test (body recordId)
|
|
126
|
+
--no-run-scenarios Skip expanding testPayload.scenarios in capacity step
|
|
128
127
|
--no-cleanup Disable cleanup after test (body cleanup: false)
|
|
129
128
|
--primary-key-value <value|@path> Primary key value or path to JSON file (e.g. @pk.json) for body primaryKeyValue
|
|
130
129
|
--no-async Use sync mode (no polling); single POST, no asyncRun
|
|
@@ -123,8 +123,7 @@ Options:
|
|
|
123
123
|
-e, --env <env> Environment: dev, tst, or pro
|
|
124
124
|
-v, --verbose Show detailed step output and poll progress
|
|
125
125
|
--debug Include debug output and write log to integration/roundtrip-test-local2/logs/
|
|
126
|
-
--
|
|
127
|
-
--record-id <id> Record ID for test (body recordId)
|
|
126
|
+
--no-run-scenarios Skip expanding testPayload.scenarios in capacity step
|
|
128
127
|
--no-cleanup Disable cleanup after test (body cleanup: false)
|
|
129
128
|
--primary-key-value <value|@path> Primary key value or path to JSON file (e.g. @pk.json) for body primaryKeyValue
|
|
130
129
|
--no-async Use sync mode (no polling); single POST, no asyncRun
|
package/jest.projects.js
CHANGED
|
@@ -72,6 +72,9 @@ const defaultProject = {
|
|
|
72
72
|
'/tests/lib/datasource/log-viewer.test.js',
|
|
73
73
|
'\\\\tests\\\\lib\\\\datasource\\\\log-viewer.test.js',
|
|
74
74
|
'lib/datasource/log-viewer.test.js',
|
|
75
|
+
'/tests/lib/datasource/log-viewer-structural.test.js',
|
|
76
|
+
'\\\\tests\\\\lib\\\\datasource\\\\log-viewer-structural.test.js',
|
|
77
|
+
'lib/datasource/log-viewer-structural.test.js',
|
|
75
78
|
'/tests/lib/commands/parameters-validate.test.js',
|
|
76
79
|
'\\\\tests\\\\lib\\\\commands\\\\parameters-validate.test.js',
|
|
77
80
|
'lib/commands/parameters-validate.test.js',
|
|
@@ -190,6 +193,10 @@ const defaultProject = {
|
|
|
190
193
|
'\\\\tests\\\\lib\\\\validation\\\\schema-241-alignment.test.js',
|
|
191
194
|
'lib/validation/schema-241-alignment.test.js',
|
|
192
195
|
'schema-241-alignment\\.test\\.js',
|
|
196
|
+
'/tests/lib/utils/schema-resolver-order.test.js',
|
|
197
|
+
'\\\\tests\\\\lib\\\\utils\\\\schema-resolver-order.test.js',
|
|
198
|
+
'lib/utils/schema-resolver-order.test.js',
|
|
199
|
+
'schema-resolver-order\\.test\\.js',
|
|
193
200
|
'/tests/lib/app/app.test.js',
|
|
194
201
|
'\\\\tests\\\\lib\\\\app\\\\app.test.js',
|
|
195
202
|
'lib/app/app.test.js',
|
|
@@ -222,7 +229,10 @@ const isolatedProjects = [
|
|
|
222
229
|
makeIsolatedProject('datasource-validation-watch', [
|
|
223
230
|
'**/tests/lib/utils/datasource-validation-watch.test.js'
|
|
224
231
|
]),
|
|
225
|
-
makeIsolatedProject('log-viewer', [
|
|
232
|
+
makeIsolatedProject('log-viewer', [
|
|
233
|
+
'**/tests/lib/datasource/log-viewer.test.js',
|
|
234
|
+
'**/tests/lib/datasource/log-viewer-structural.test.js'
|
|
235
|
+
]),
|
|
226
236
|
makeIsolatedProject('datasource-test-run-schema-sync', [
|
|
227
237
|
'**/tests/lib/utils/datasource-test-run-schema-sync.test.js'
|
|
228
238
|
]),
|
|
@@ -285,6 +295,7 @@ const isolatedProjects = [
|
|
|
285
295
|
makeIsolatedProject('generator-validation', ['**/tests/lib/generator/generator-validation.test.js']),
|
|
286
296
|
makeIsolatedProject('secrets-databaselog', ['**/tests/lib/core/secrets-databaselog.test.js']),
|
|
287
297
|
makeIsolatedProject('schema-241-alignment', ['**/tests/lib/validation/schema-241-alignment.test.js']),
|
|
298
|
+
makeIsolatedProject('schema-resolver-order', ['**/tests/lib/utils/schema-resolver-order.test.js']),
|
|
288
299
|
makeIsolatedProject('app-module', ['**/tests/lib/app/app.test.js']),
|
|
289
300
|
makeIsolatedProject('admin-secrets', ['**/tests/lib/core/admin-secrets.test.js'])
|
|
290
301
|
];
|
|
@@ -5,6 +5,24 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
const { ApiClient } = require('./index');
|
|
8
|
+
const { normalizeDataplaneAuth } = require('./validation-run.api');
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* @param {Object} authConfig
|
|
12
|
+
* @returns {Object}
|
|
13
|
+
*/
|
|
14
|
+
function dataplaneAuthForBearerGet(authConfig) {
|
|
15
|
+
if (!authConfig || typeof authConfig !== 'object') {
|
|
16
|
+
return authConfig;
|
|
17
|
+
}
|
|
18
|
+
if (authConfig.token || authConfig.clientId) {
|
|
19
|
+
return authConfig;
|
|
20
|
+
}
|
|
21
|
+
if (authConfig.apiKey) {
|
|
22
|
+
return { ...authConfig, token: authConfig.apiKey, type: authConfig.type || 'bearer' };
|
|
23
|
+
}
|
|
24
|
+
return normalizeDataplaneAuth(authConfig);
|
|
25
|
+
}
|
|
8
26
|
|
|
9
27
|
/**
|
|
10
28
|
* Get active trusted integration certificate for a datasource.
|
|
@@ -18,7 +36,7 @@ const { ApiClient } = require('./index');
|
|
|
18
36
|
* @returns {Promise<Object>} API envelope `{ success, data?, status, ... }`
|
|
19
37
|
*/
|
|
20
38
|
async function getActiveIntegrationCertificate(dataplaneUrl, authConfig, systemKey, datasourceKey) {
|
|
21
|
-
const client = new ApiClient(dataplaneUrl, authConfig);
|
|
39
|
+
const client = new ApiClient(dataplaneUrl, dataplaneAuthForBearerGet(authConfig));
|
|
22
40
|
const path = `/api/v1/systems/${encodeURIComponent(systemKey)}/datasources/${encodeURIComponent(
|
|
23
41
|
datasourceKey
|
|
24
42
|
)}/certificates/active`;
|
|
@@ -36,7 +54,7 @@ async function getActiveIntegrationCertificate(dataplaneUrl, authConfig, systemK
|
|
|
36
54
|
* @returns {Promise<Object>}
|
|
37
55
|
*/
|
|
38
56
|
async function listIntegrationCertificates(dataplaneUrl, authConfig, params = {}) {
|
|
39
|
-
const client = new ApiClient(dataplaneUrl, authConfig);
|
|
57
|
+
const client = new ApiClient(dataplaneUrl, dataplaneAuthForBearerGet(authConfig));
|
|
40
58
|
return await client.get('/api/v1/certificates', { params });
|
|
41
59
|
}
|
|
42
60
|
|
|
@@ -51,7 +69,7 @@ async function listIntegrationCertificates(dataplaneUrl, authConfig, params = {}
|
|
|
51
69
|
* @returns {Promise<Object>}
|
|
52
70
|
*/
|
|
53
71
|
async function verifyIntegrationCertificate(dataplaneUrl, authConfig, body) {
|
|
54
|
-
const client = new ApiClient(dataplaneUrl, authConfig);
|
|
72
|
+
const client = new ApiClient(dataplaneUrl, dataplaneAuthForBearerGet(authConfig));
|
|
55
73
|
return await client.post('/api/v1/certificates/verify', { body: body || {} });
|
|
56
74
|
}
|
|
57
75
|
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
* @property {string} [issuedBy]
|
|
23
23
|
* @property {string} [licenseLevelIssuer]
|
|
24
24
|
* @property {string} [dataplaneVersion]
|
|
25
|
-
* @property {
|
|
25
|
+
* @property {'RS256'} [algorithm] Integration certificate signing algorithm (RS256 only)
|
|
26
26
|
* @property {string|null} [publicKey]
|
|
27
27
|
* @property {string|null} [publicKeyFingerprint]
|
|
28
28
|
* @property {Object} [metadata]
|
package/lib/app/show-display.js
CHANGED
|
@@ -11,7 +11,6 @@
|
|
|
11
11
|
|
|
12
12
|
const chalk = require('chalk');
|
|
13
13
|
const logger = require('../utils/logger');
|
|
14
|
-
const { truncatePublicKeyPreview } = require('./certification-show-enrich');
|
|
15
14
|
|
|
16
15
|
function logSourceAndHeader(summary) {
|
|
17
16
|
const isOffline = summary.source === 'offline';
|
|
@@ -263,23 +262,51 @@ function displayExternalAppBlock(summary) {
|
|
|
263
262
|
}
|
|
264
263
|
|
|
265
264
|
/**
|
|
266
|
-
*
|
|
267
|
-
* @param {
|
|
265
|
+
* Colored certification outcome from `status` string.
|
|
266
|
+
* @param {string} raw - lowercased status
|
|
267
|
+
* @returns {string}
|
|
268
268
|
*/
|
|
269
|
-
function
|
|
270
|
-
if (
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
269
|
+
function formatCertificationOutcome(raw) {
|
|
270
|
+
if (raw === 'passed') return chalk.green('passed');
|
|
271
|
+
if (raw === 'not_passed') return chalk.red('not_passed');
|
|
272
|
+
if (raw === 'pending') return chalk.yellow('pending');
|
|
273
|
+
return raw || '—';
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
/**
|
|
277
|
+
* Certification status line: enabled ✔/✖ plus outcome (passed / not_passed / pending).
|
|
278
|
+
* @param {Object} c - certification block from system file
|
|
279
|
+
* @returns {string}
|
|
280
|
+
*/
|
|
281
|
+
function formatCertificationStatusLine(c) {
|
|
282
|
+
const enabledTrue = c.enabled === true;
|
|
283
|
+
const enabledSym = enabledTrue ? chalk.green('✔') : chalk.red('✖');
|
|
284
|
+
const raw = (c.status && String(c.status).toLowerCase()) || '';
|
|
285
|
+
const outcome = formatCertificationOutcome(raw);
|
|
286
|
+
return ` Status: ${enabledSym} ${outcome}`;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
/**
|
|
290
|
+
* Local certification fields from system file (TTY block).
|
|
291
|
+
* @param {Object} c
|
|
292
|
+
*/
|
|
293
|
+
function logCertificationLocalFileDetails(c) {
|
|
294
|
+
logger.log(formatCertificationStatusLine(c));
|
|
295
|
+
const levelStr =
|
|
296
|
+
c.level === undefined || c.level === null ? '' : String(c.level).trim();
|
|
297
|
+
if (levelStr) {
|
|
298
|
+
logger.log(` Level: ${levelStr}`);
|
|
282
299
|
}
|
|
300
|
+
logger.log(` algorithm: ${c.algorithm ?? '—'}`);
|
|
301
|
+
logger.log(` issuer: ${c.issuer ?? '—'}`);
|
|
302
|
+
logger.log(` version: ${c.version ?? '—'}`);
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
/**
|
|
306
|
+
* Dataplane certification verify rows (when `--verify-cert` populated summary).
|
|
307
|
+
* @param {Object} summary
|
|
308
|
+
*/
|
|
309
|
+
function logCertificationVerifySection(summary) {
|
|
283
310
|
if (summary.certificationVerifyRows && summary.certificationVerifyRows.length > 0) {
|
|
284
311
|
logger.log('');
|
|
285
312
|
logger.log('🪪 Certification verify (dataplane)');
|
|
@@ -303,6 +330,23 @@ function logCertificationSection(summary) {
|
|
|
303
330
|
}
|
|
304
331
|
}
|
|
305
332
|
|
|
333
|
+
/**
|
|
334
|
+
* Local certification + optional verify rows (external integrations).
|
|
335
|
+
* @param {Object} summary
|
|
336
|
+
*/
|
|
337
|
+
function logCertificationSection(summary) {
|
|
338
|
+
if (!summary.isExternal) return;
|
|
339
|
+
logger.log('');
|
|
340
|
+
logger.log('🪪 Certification (local system file)');
|
|
341
|
+
const c = summary.localCertification;
|
|
342
|
+
if (!c || typeof c !== 'object') {
|
|
343
|
+
logger.log(' (none or unreadable)');
|
|
344
|
+
} else {
|
|
345
|
+
logCertificationLocalFileDetails(c);
|
|
346
|
+
}
|
|
347
|
+
logCertificationVerifySection(summary);
|
|
348
|
+
}
|
|
349
|
+
|
|
306
350
|
/**
|
|
307
351
|
* Format and print human-readable show output (offline or online).
|
|
308
352
|
* @param {Object} summary - Unified summary (buildOfflineSummaryFromDeployJson or buildOnlineSummary)
|
|
@@ -38,14 +38,14 @@ function pickArtifactForCertificationMerge(artifacts) {
|
|
|
38
38
|
}
|
|
39
39
|
|
|
40
40
|
/**
|
|
41
|
-
* @param {string} algorithmUpper
|
|
42
41
|
* @param {import('../api/types/certificates.types').CertificateArtifactResponse} art
|
|
42
|
+
* @param {Object} ex
|
|
43
43
|
* @returns {string}
|
|
44
44
|
*/
|
|
45
|
-
function
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
return
|
|
45
|
+
function resolvePublicKey(art, ex) {
|
|
46
|
+
const fromArt = trimOrEmpty(art.publicKey);
|
|
47
|
+
if (fromArt) return fromArt;
|
|
48
|
+
return trimOrEmpty(ex.publicKey);
|
|
49
49
|
}
|
|
50
50
|
|
|
51
51
|
/**
|
|
@@ -53,13 +53,34 @@ function hs256DevPublicKeyPlaceholder(algorithmUpper, art) {
|
|
|
53
53
|
* @param {Object} ex
|
|
54
54
|
* @returns {string}
|
|
55
55
|
*/
|
|
56
|
-
function
|
|
57
|
-
const fromArt = trimOrEmpty(art.
|
|
56
|
+
function resolvePublicKeyFingerprint(art, ex) {
|
|
57
|
+
const fromArt = trimOrEmpty(art.publicKeyFingerprint);
|
|
58
58
|
if (fromArt) return fromArt;
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
59
|
+
return trimOrEmpty(ex.publicKeyFingerprint);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* @param {string} raw
|
|
64
|
+
* @returns {string} Normalized digest or empty when invalid
|
|
65
|
+
*/
|
|
66
|
+
function normalizeContractHash(raw) {
|
|
67
|
+
const s = trimOrEmpty(raw);
|
|
68
|
+
return CONTRACT_HASH_PATTERN.test(s) ? s : '';
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* @param {import('../api/types/certificates.types').CertificateArtifactResponse} art
|
|
73
|
+
* @param {Object} ex
|
|
74
|
+
* @returns {string}
|
|
75
|
+
*/
|
|
76
|
+
function resolveContractHash(art, ex) {
|
|
77
|
+
return (
|
|
78
|
+
normalizeContractHash(art.contractHash) ||
|
|
79
|
+
normalizeContractHash(art.integrationHash) ||
|
|
80
|
+
normalizeContractHash(ex.contractHash) ||
|
|
81
|
+
normalizeContractHash(ex.integrationHash) ||
|
|
82
|
+
''
|
|
83
|
+
);
|
|
63
84
|
}
|
|
64
85
|
|
|
65
86
|
/**
|
|
@@ -92,6 +113,8 @@ function resolveVersion(art, ex) {
|
|
|
92
113
|
|
|
93
114
|
const CERTIFICATION_LEVELS = new Set(['BRONZE', 'SILVER', 'GOLD', 'PLATINUM']);
|
|
94
115
|
const CERTIFICATION_STATUSES = new Set(['passed', 'not_passed', 'pending']);
|
|
116
|
+
/** Matches external-system.schema.json `certification.contractHash` pattern. */
|
|
117
|
+
const CONTRACT_HASH_PATTERN = /^sha256:[0-9a-f]{64}$/;
|
|
95
118
|
|
|
96
119
|
/**
|
|
97
120
|
* @param {string} raw
|
|
@@ -138,9 +161,10 @@ function resolveStatus(_art, ex) {
|
|
|
138
161
|
}
|
|
139
162
|
|
|
140
163
|
/**
|
|
141
|
-
* Build `certification` object matching **external-system.schema.json** (required: enabled, publicKey, algorithm, issuer, version; optional status, level).
|
|
164
|
+
* Build `certification` object matching **external-system.schema.json** (required: enabled, publicKey, algorithm, issuer, version; optional status, level, publicKeyFingerprint, contractHash).
|
|
142
165
|
* Fills gaps from `existingCertification` when the artifact omits publishable fields (common when dataplane redacts `publicKey`).
|
|
143
|
-
*
|
|
166
|
+
* Dataplane issues **RS256** certificates with PEM `publicKey` and optional `publicKeyFingerprint` (sha256:… of SPKI); merge output uses **RS256** only.
|
|
167
|
+
* Optional **contractHash** is copied from the certificate `contractHash` or legacy `integrationHash` when it matches `sha256:` + 64 hex.
|
|
144
168
|
*
|
|
145
169
|
* @param {import('../api/types/certificates.types').CertificateArtifactResponse|null} artifact
|
|
146
170
|
* @param {Object|null|undefined} existingCertification - Current `system.certification`
|
|
@@ -160,13 +184,13 @@ function buildCertificationFromArtifact(artifact, existingCertification) {
|
|
|
160
184
|
const versionStr = resolveVersion(art, ex);
|
|
161
185
|
if (!versionStr) return null;
|
|
162
186
|
|
|
163
|
-
const
|
|
164
|
-
const
|
|
187
|
+
const publicKeyFingerprint = resolvePublicKeyFingerprint(art, ex);
|
|
188
|
+
const contractHash = resolveContractHash(art, ex);
|
|
165
189
|
|
|
166
190
|
const out = {
|
|
167
191
|
enabled: true,
|
|
168
192
|
publicKey,
|
|
169
|
-
algorithm,
|
|
193
|
+
algorithm: 'RS256',
|
|
170
194
|
issuer,
|
|
171
195
|
version: versionStr,
|
|
172
196
|
status: resolveStatus(art, ex)
|
|
@@ -175,6 +199,12 @@ function buildCertificationFromArtifact(artifact, existingCertification) {
|
|
|
175
199
|
if (level) {
|
|
176
200
|
out.level = level;
|
|
177
201
|
}
|
|
202
|
+
if (publicKeyFingerprint) {
|
|
203
|
+
out.publicKeyFingerprint = publicKeyFingerprint;
|
|
204
|
+
}
|
|
205
|
+
if (contractHash) {
|
|
206
|
+
out.contractHash = contractHash;
|
|
207
|
+
}
|
|
178
208
|
return out;
|
|
179
209
|
}
|
|
180
210
|
|
|
@@ -10,6 +10,7 @@ const chalk = require('chalk');
|
|
|
10
10
|
const logger = require('../utils/logger');
|
|
11
11
|
const { trySyncCertificationFromDataplaneForExternalApp } = require('./sync-after-external-command');
|
|
12
12
|
const { cliOptsSkipCertSync } = require('./cli-cert-sync-skip');
|
|
13
|
+
const { resolveDebugDisplayMode } = require('../utils/datasource-test-run-debug-display');
|
|
13
14
|
|
|
14
15
|
/**
|
|
15
16
|
* @async
|
|
@@ -17,14 +18,24 @@ const { cliOptsSkipCertSync } = require('./cli-cert-sync-skip');
|
|
|
17
18
|
* @param {string} datasourceKey
|
|
18
19
|
* @param {Object} options - CLI flags (app, noCertSync)
|
|
19
20
|
* @param {string} label - Log label
|
|
21
|
+
* @param {Object|null} [envelope] - Last DatasourceTestRun (optional); used for certificateIssuance failed hint
|
|
20
22
|
* @returns {Promise<void>}
|
|
21
23
|
*/
|
|
22
|
-
async function afterUnifiedValidationCertSync(exitCode, datasourceKey, options, label) {
|
|
24
|
+
async function afterUnifiedValidationCertSync(exitCode, datasourceKey, options, label, envelope = null) {
|
|
23
25
|
if (exitCode !== 0 || cliOptsSkipCertSync(options)) return;
|
|
24
26
|
try {
|
|
25
27
|
const { resolveAppKeyForDatasource } = require('../datasource/resolve-app');
|
|
26
28
|
const { appKey } = await resolveAppKeyForDatasource(datasourceKey, options.app);
|
|
27
|
-
|
|
29
|
+
let issuanceFailureHint = null;
|
|
30
|
+
if (envelope && envelope.certificateIssuance && envelope.certificateIssuance.status === 'failed') {
|
|
31
|
+
const ci = envelope.certificateIssuance;
|
|
32
|
+
issuanceFailureHint = [ci.reasonCode, ci.message].filter(Boolean).join(': ');
|
|
33
|
+
}
|
|
34
|
+
const verboseCertHints = resolveDebugDisplayMode(options.debug) !== null;
|
|
35
|
+
await trySyncCertificationFromDataplaneForExternalApp(appKey, label, {
|
|
36
|
+
issuanceFailureHint,
|
|
37
|
+
verboseCertHints
|
|
38
|
+
});
|
|
28
39
|
} catch (e) {
|
|
29
40
|
logger.log(chalk.yellow(`⚠ Certification sync (${label}) skipped: ${e.message}`));
|
|
30
41
|
}
|
|
@@ -16,9 +16,10 @@ const logger = require('../utils/logger');
|
|
|
16
16
|
* @async
|
|
17
17
|
* @param {string} appKey - Integration / system key
|
|
18
18
|
* @param {string} label - Short label for logs (e.g. "validate", "datasource test")
|
|
19
|
+
* @param {Object} [extra] - Optional `{ issuanceFailureHint }` from last DatasourceTestRun.certificateIssuance (failed)
|
|
19
20
|
* @returns {Promise<void>}
|
|
20
21
|
*/
|
|
21
|
-
async function trySyncCertificationFromDataplaneForExternalApp(appKey, label) {
|
|
22
|
+
async function trySyncCertificationFromDataplaneForExternalApp(appKey, label, extra = {}) {
|
|
22
23
|
try {
|
|
23
24
|
const { detectAppType } = require('../utils/paths');
|
|
24
25
|
const t = await detectAppType(appKey).catch(() => null);
|
|
@@ -29,7 +30,7 @@ async function trySyncCertificationFromDataplaneForExternalApp(appKey, label) {
|
|
|
29
30
|
const { maybeSyncSystemCertificationFromDataplane } = require('./sync-system-certification');
|
|
30
31
|
|
|
31
32
|
validateSystemKeyFormat(appKey);
|
|
32
|
-
const { dataplaneUrl, authConfig } = await resolveDataplaneAndAuth(appKey);
|
|
33
|
+
const { dataplaneUrl, authConfig } = await resolveDataplaneAndAuth(appKey, { silent: true });
|
|
33
34
|
if (!authConfig.token) {
|
|
34
35
|
logger.log(chalk.gray(`Certification sync (${label}) skipped: no Bearer token (run aifabrix login).`));
|
|
35
36
|
return;
|
|
@@ -42,7 +43,9 @@ async function trySyncCertificationFromDataplaneForExternalApp(appKey, label) {
|
|
|
42
43
|
systemKey: manifest.key,
|
|
43
44
|
dataplaneUrl,
|
|
44
45
|
authConfig,
|
|
45
|
-
datasourceKeys: dsKeys
|
|
46
|
+
datasourceKeys: dsKeys,
|
|
47
|
+
issuanceFailureHint: extra && extra.issuanceFailureHint ? String(extra.issuanceFailureHint).trim() : null,
|
|
48
|
+
verboseCertHints: extra && extra.verboseCertHints === true
|
|
46
49
|
});
|
|
47
50
|
} catch (e) {
|
|
48
51
|
logger.log(chalk.yellow(`⚠ Certification sync (${label}) skipped: ${e.message}`));
|
|
@@ -84,15 +84,43 @@ function tryWriteSystemCertification(systemFilePath, systemObj, nextCert) {
|
|
|
84
84
|
/**
|
|
85
85
|
* @param {Object|null} chosen
|
|
86
86
|
* @param {'no_active'|'no_public_key'} detail
|
|
87
|
+
* @param {string|null} [issuanceFailureHint] - From last DatasourceTestRun.certificateIssuance when status failed
|
|
88
|
+
* @param {boolean} [verboseCertHints] - Extra gray lines and auto-issue detail (CLI `--debug`)
|
|
87
89
|
*/
|
|
88
|
-
function logSkippedCertification(chosen, detail) {
|
|
90
|
+
function logSkippedCertification(chosen, detail, issuanceFailureHint, verboseCertHints = false) {
|
|
89
91
|
if (detail === 'no_active' || !chosen) {
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
)
|
|
92
|
+
const hint = issuanceFailureHint && String(issuanceFailureHint).trim();
|
|
93
|
+
const certNotPassed =
|
|
94
|
+
hint &&
|
|
95
|
+
(hint.includes('CERTIFICATION_NOT_PASSED') ||
|
|
96
|
+
hint.toLowerCase().includes('certification did not pass'));
|
|
97
|
+
if (certNotPassed) {
|
|
98
|
+
logger.log(
|
|
99
|
+
chalk.yellow(
|
|
100
|
+
'⚠ Certification not written: no active integration certificate because certification did not pass on the dataplane issuance validation pass.'
|
|
101
|
+
)
|
|
102
|
+
);
|
|
103
|
+
if (verboseCertHints) {
|
|
104
|
+
logger.log(
|
|
105
|
+
chalk.gray(
|
|
106
|
+
' E2E can be green for every datasource while auto-issue still fails: issuance re-validates the whole system before signing.'
|
|
107
|
+
)
|
|
108
|
+
);
|
|
109
|
+
if (hint) {
|
|
110
|
+
logger.log(chalk.gray(` Auto-issue detail: ${hint}`));
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
} else {
|
|
114
|
+
logger.log(
|
|
115
|
+
chalk.yellow(
|
|
116
|
+
'⚠ Certification not written: dataplane has no active trusted certificate for this system/datasource scope yet. ' +
|
|
117
|
+
'Fix auto-issue or signing (see hint), or run unified validation then upload or run cert sync again.'
|
|
118
|
+
)
|
|
119
|
+
);
|
|
120
|
+
if (hint && verboseCertHints) {
|
|
121
|
+
logger.log(chalk.gray(` Auto-issue detail: ${hint}`));
|
|
122
|
+
}
|
|
123
|
+
}
|
|
96
124
|
return;
|
|
97
125
|
}
|
|
98
126
|
logger.log(
|
|
@@ -111,13 +139,19 @@ function logSkippedCertification(chosen, detail) {
|
|
|
111
139
|
* @param {string} params.dataplaneUrl
|
|
112
140
|
* @param {Object} params.authConfig
|
|
113
141
|
* @param {string[]} params.datasourceKeys
|
|
142
|
+
* @param {boolean} [params.verboseCertHints]
|
|
114
143
|
* @returns {Promise<{ written: boolean, reason?: string }>}
|
|
115
144
|
*/
|
|
116
145
|
async function syncSystemCertificationFromDataplane(params) {
|
|
117
|
-
const { systemKey, dataplaneUrl, authConfig, datasourceKeys } = params;
|
|
146
|
+
const { systemKey, dataplaneUrl, authConfig, datasourceKeys, issuanceFailureHint, verboseCertHints } = params;
|
|
118
147
|
const resolved = resolvePrimarySystemFilePath(systemKey);
|
|
119
148
|
if (!resolved) return { written: false, reason: 'no_system_file' };
|
|
120
|
-
|
|
149
|
+
const hasBearerLike =
|
|
150
|
+
authConfig &&
|
|
151
|
+
typeof authConfig === 'object' &&
|
|
152
|
+
((typeof authConfig.token === 'string' && authConfig.token.trim()) ||
|
|
153
|
+
(typeof authConfig.apiKey === 'string' && authConfig.apiKey.trim()));
|
|
154
|
+
if (!dataplaneUrl || !authConfig || !hasBearerLike) {
|
|
121
155
|
return { written: false, reason: 'no_auth' };
|
|
122
156
|
}
|
|
123
157
|
|
|
@@ -137,7 +171,7 @@ async function syncSystemCertificationFromDataplane(params) {
|
|
|
137
171
|
const nextCert = buildCertificationFromArtifact(chosen, existing);
|
|
138
172
|
if (!nextCert) {
|
|
139
173
|
const detail = chosen ? 'no_public_key' : 'no_active';
|
|
140
|
-
logSkippedCertification(chosen, detail);
|
|
174
|
+
logSkippedCertification(chosen, detail, issuanceFailureHint, verboseCertHints === true);
|
|
141
175
|
return { written: false, reason: 'incomplete_certification', detail };
|
|
142
176
|
}
|
|
143
177
|
|
|
@@ -154,7 +188,8 @@ async function syncSystemCertificationFromDataplane(params) {
|
|
|
154
188
|
* @param {boolean} [params.noCertSync]
|
|
155
189
|
* @returns {Promise<void>}
|
|
156
190
|
*/
|
|
157
|
-
function logCertificationSyncNotWritten(r, label) {
|
|
191
|
+
function logCertificationSyncNotWritten(r, label, verboseCertHints = false) {
|
|
192
|
+
if (r && r.reason === 'incomplete_certification' && !verboseCertHints) return;
|
|
158
193
|
const prefix = label ? ` (${label})` : '';
|
|
159
194
|
const map = {
|
|
160
195
|
no_system_file: `No *-system* file found under integration folder for this key${prefix}.`,
|
|
@@ -168,21 +203,32 @@ function logCertificationSyncNotWritten(r, label) {
|
|
|
168
203
|
}
|
|
169
204
|
|
|
170
205
|
async function maybeSyncSystemCertificationFromDataplane(params) {
|
|
171
|
-
const {
|
|
206
|
+
const {
|
|
207
|
+
label,
|
|
208
|
+
noCertSync,
|
|
209
|
+
systemKey,
|
|
210
|
+
dataplaneUrl,
|
|
211
|
+
authConfig,
|
|
212
|
+
datasourceKeys,
|
|
213
|
+
issuanceFailureHint,
|
|
214
|
+
verboseCertHints
|
|
215
|
+
} = params;
|
|
172
216
|
if (noCertSync === true) return;
|
|
173
217
|
try {
|
|
174
218
|
const r = await syncSystemCertificationFromDataplane({
|
|
175
219
|
systemKey,
|
|
176
220
|
dataplaneUrl,
|
|
177
221
|
authConfig,
|
|
178
|
-
datasourceKeys
|
|
222
|
+
datasourceKeys,
|
|
223
|
+
issuanceFailureHint,
|
|
224
|
+
verboseCertHints
|
|
179
225
|
});
|
|
180
226
|
if (r.written) {
|
|
181
227
|
logger.log(
|
|
182
228
|
chalk.gray(`Updated certification block from dataplane${label ? ` (${label})` : ''} in system file.`)
|
|
183
229
|
);
|
|
184
230
|
} else if (r.reason) {
|
|
185
|
-
logCertificationSyncNotWritten(r, label);
|
|
231
|
+
logCertificationSyncNotWritten(r, label, verboseCertHints === true);
|
|
186
232
|
}
|
|
187
233
|
} catch (e) {
|
|
188
234
|
logger.log(chalk.yellow(`⚠ Certification sync skipped: ${e.message}`));
|
|
@@ -54,6 +54,70 @@ function setupTestCommand(program) {
|
|
|
54
54
|
});
|
|
55
55
|
}
|
|
56
56
|
|
|
57
|
+
/**
|
|
58
|
+
* First failed `certificateIssuance` across E2E datasource rows (for cert-sync hint).
|
|
59
|
+
* @param {Array<{ key?: string, datasourceTestRun?: { certificateIssuance?: { status?: string, reasonCode?: string, message?: string } } }>} results
|
|
60
|
+
* @returns {string|null}
|
|
61
|
+
*/
|
|
62
|
+
function firstIssuanceFailureHintFromE2eResults(results) {
|
|
63
|
+
const failed = [];
|
|
64
|
+
for (const row of results || []) {
|
|
65
|
+
const ci = row.datasourceTestRun && row.datasourceTestRun.certificateIssuance;
|
|
66
|
+
if (ci && ci.status === 'failed') {
|
|
67
|
+
failed.push({ key: row.key, ci });
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
if (failed.length === 0) return null;
|
|
71
|
+
const firstRc = failed[0].ci.reasonCode;
|
|
72
|
+
const sameAll = failed.every(f => f.ci.reasonCode === firstRc);
|
|
73
|
+
if (sameAll && failed.length > 1) {
|
|
74
|
+
const parts = [firstRc, failed[0].ci.message].filter(Boolean);
|
|
75
|
+
return `${parts.join(': ')} (${failed.length} datasource scopes; first: ${failed[0].key})`;
|
|
76
|
+
}
|
|
77
|
+
const parts = [failed[0].ci.reasonCode, failed[0].ci.message].filter(Boolean);
|
|
78
|
+
return `[${failed[0].key}] ${parts.join(': ')}`.trim();
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* External integration E2E: run, display, exit on aggregate, optional cert sync.
|
|
83
|
+
* @param {string} appName
|
|
84
|
+
* @param {Object} options
|
|
85
|
+
* @returns {Promise<void>}
|
|
86
|
+
*/
|
|
87
|
+
async function runExternalIntegrationE2EAndCertSync(appName, options) {
|
|
88
|
+
const { runTestE2EForExternalSystem } = require('../commands/test-e2e-external');
|
|
89
|
+
const { success, results } = await runTestE2EForExternalSystem(appName, {
|
|
90
|
+
env: options.env,
|
|
91
|
+
debug: options.debug,
|
|
92
|
+
verbose: options.verbose,
|
|
93
|
+
async: options.async !== false,
|
|
94
|
+
sync: options.sync === true
|
|
95
|
+
});
|
|
96
|
+
const { displayIntegrationTestResults } = require('../utils/external-system-display');
|
|
97
|
+
const datasourceResults = results.map(r => ({
|
|
98
|
+
key: r.key,
|
|
99
|
+
success: r.success,
|
|
100
|
+
error: r.error,
|
|
101
|
+
skipped: false,
|
|
102
|
+
datasourceTestRun: r.datasourceTestRun
|
|
103
|
+
}));
|
|
104
|
+
displayIntegrationTestResults(
|
|
105
|
+
{ systemKey: appName, success, datasourceResults },
|
|
106
|
+
options.verbose,
|
|
107
|
+
{ debug: options.debug, runType: 'e2e' }
|
|
108
|
+
);
|
|
109
|
+
const { computeSystemExitCodeFromDatasourceRows } = require('../utils/datasource-test-run-exit');
|
|
110
|
+
const exitCode = computeSystemExitCodeFromDatasourceRows(datasourceResults, {
|
|
111
|
+
warningsAsErrors: options.warningsAsErrors === true,
|
|
112
|
+
requireCert: options.requireCert === true
|
|
113
|
+
});
|
|
114
|
+
if (exitCode !== 0) process.exit(exitCode);
|
|
115
|
+
if (cliOptsSkipCertSync(options)) return;
|
|
116
|
+
const { trySyncCertificationFromDataplaneForExternalApp } = require('../certification/sync-after-external-command');
|
|
117
|
+
const issuanceFailureHint = firstIssuanceFailureHintFromE2eResults(results);
|
|
118
|
+
await trySyncCertificationFromDataplaneForExternalApp(appName, 'test-e2e', { issuanceFailureHint });
|
|
119
|
+
}
|
|
120
|
+
|
|
57
121
|
async function runTestE2ECommand(appName, options) {
|
|
58
122
|
const pathsUtil = require('../utils/paths');
|
|
59
123
|
const appType = await pathsUtil.detectAppType(appName).catch(() => null);
|
|
@@ -64,40 +128,7 @@ async function runTestE2ECommand(appName, options) {
|
|
|
64
128
|
);
|
|
65
129
|
}
|
|
66
130
|
if (appType && appType.baseDir === 'integration') {
|
|
67
|
-
|
|
68
|
-
const { success, results } = await runTestE2EForExternalSystem(appName, {
|
|
69
|
-
env: options.env,
|
|
70
|
-
debug: options.debug,
|
|
71
|
-
verbose: options.verbose,
|
|
72
|
-
async: options.async !== false,
|
|
73
|
-
sync: options.sync === true
|
|
74
|
-
});
|
|
75
|
-
const { displayIntegrationTestResults } = require('../utils/external-system-display');
|
|
76
|
-
const datasourceResults = results.map(r => ({
|
|
77
|
-
key: r.key,
|
|
78
|
-
success: r.success,
|
|
79
|
-
error: r.error,
|
|
80
|
-
skipped: false,
|
|
81
|
-
datasourceTestRun: r.datasourceTestRun
|
|
82
|
-
}));
|
|
83
|
-
displayIntegrationTestResults(
|
|
84
|
-
{
|
|
85
|
-
systemKey: appName,
|
|
86
|
-
success,
|
|
87
|
-
datasourceResults
|
|
88
|
-
},
|
|
89
|
-
options.verbose,
|
|
90
|
-
{ debug: options.debug, runType: 'e2e' }
|
|
91
|
-
);
|
|
92
|
-
const { computeSystemExitCodeFromDatasourceRows } = require('../utils/datasource-test-run-exit');
|
|
93
|
-
const exitCode = computeSystemExitCodeFromDatasourceRows(datasourceResults, {
|
|
94
|
-
warningsAsErrors: options.warningsAsErrors === true,
|
|
95
|
-
requireCert: options.requireCert === true
|
|
96
|
-
});
|
|
97
|
-
if (exitCode !== 0) process.exit(exitCode);
|
|
98
|
-
if (cliOptsSkipCertSync(options)) return;
|
|
99
|
-
const { trySyncCertificationFromDataplaneForExternalApp } = require('../certification/sync-after-external-command');
|
|
100
|
-
await trySyncCertificationFromDataplaneForExternalApp(appName, 'test-e2e');
|
|
131
|
+
await runExternalIntegrationE2EAndCertSync(appName, options);
|
|
101
132
|
return;
|
|
102
133
|
}
|
|
103
134
|
const { runAppTestE2e } = require('../commands/app-test');
|
|
@@ -190,6 +221,7 @@ function setupInstallTestE2eLintCommands(program) {
|
|
|
190
221
|
}
|
|
191
222
|
|
|
192
223
|
module.exports = {
|
|
193
|
-
setupInstallTestE2eLintCommands
|
|
224
|
+
setupInstallTestE2eLintCommands,
|
|
225
|
+
firstIssuanceFailureHintFromE2eResults
|
|
194
226
|
};
|
|
195
227
|
|