@aifabrix/builder 2.44.6 → 2.45.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/.cursor/rules/cli-layout.mdc +7 -3
- package/.cursor/rules/docs-rules.mdc +3 -1
- package/integration/hubspot-test/test-artifacts/wizard-hubspot-credential-real.yaml +1 -1
- package/integration/hubspot-test/test-artifacts/wizard-hubspot-env-vars.yaml +1 -1
- package/integration/hubspot-test/wizard-hubspot-e2e.yaml +1 -1
- package/jest.projects.js +100 -1
- package/lib/api/agent-metadata-validation.api.js +93 -0
- package/lib/api/audit-api-helpers.js +68 -0
- package/lib/api/audit.api.js +119 -0
- package/lib/api/auth-cache.api.js +44 -0
- package/lib/api/certificates.api.js +4 -4
- package/lib/api/credential.api.js +2 -2
- package/lib/api/dataplane-health.api.js +137 -0
- package/lib/api/dataplane-sync.api.js +27 -0
- package/lib/api/datasources-core.api.js +21 -18
- package/lib/api/datasources-extended.api.js +22 -22
- package/lib/api/dimensions.api.js +3 -0
- package/lib/api/external-systems.api.js +13 -13
- package/lib/api/governance-scenarios.api.js +39 -0
- package/lib/api/groups.api.js +79 -0
- package/lib/api/index.js +45 -2
- package/lib/api/pipeline.api.js +4 -4
- package/lib/api/protection.api.js +157 -0
- package/lib/api/records-bulk.api.js +30 -0
- package/lib/api/records-search-parse.js +49 -0
- package/lib/api/records-search.api.js +29 -0
- package/lib/api/types/agent-metadata-validation.types.js +67 -0
- package/lib/api/types/audit.types.js +72 -0
- package/lib/api/types/dataplane-health.types.js +27 -0
- package/lib/api/types/dimensions.types.js +2 -0
- package/lib/api/types/governance-scenarios.types.js +39 -0
- package/lib/api/types/protection.types.js +69 -0
- package/lib/api/types/records-bulk.types.js +35 -0
- package/lib/api/types/records-search.types.js +28 -0
- package/lib/api/user-groups.api.js +50 -0
- package/lib/api/users.api.js +95 -0
- package/lib/api/validation-run.api.js +3 -3
- package/lib/api/validation-runner.js +3 -3
- package/lib/api/wizard-platform.api.js +3 -3
- package/lib/api/wizard.api.js +21 -21
- package/lib/app/deploy.js +34 -19
- package/lib/app/helpers.js +3 -3
- package/lib/app/index.js +3 -3
- package/lib/app/register.js +7 -6
- package/lib/app/restart-display.js +52 -21
- package/lib/app/rotate-secret.js +7 -6
- package/lib/app/run-helpers.js +15 -8
- package/lib/app/run.js +57 -9
- package/lib/app/show-display.js +7 -0
- package/lib/app/show.js +87 -5
- package/lib/build/index.js +9 -5
- package/lib/cli/index.js +4 -2
- package/lib/cli/infra-guided.js +62 -28
- package/lib/cli/installation-log-command.js +73 -0
- package/lib/cli/setup-app.help.js +50 -2
- package/lib/cli/setup-app.js +44 -25
- package/lib/cli/setup-app.test-commands.js +80 -21
- package/lib/cli/setup-auth.js +124 -52
- package/lib/cli/setup-external-system.js +37 -3
- package/lib/cli/setup-infra-up-dataplane-action.js +111 -0
- package/lib/cli/setup-infra-up-platform-action.js +131 -0
- package/lib/cli/setup-infra.js +60 -119
- package/lib/cli/setup-platform.js +1 -1
- package/lib/cli/setup-utility-help-after.js +45 -0
- package/lib/cli/setup-utility-resolve.js +132 -0
- package/lib/cli/setup-utility.js +148 -116
- package/lib/commands/app-logs.js +81 -33
- package/lib/commands/auth-config.js +116 -18
- package/lib/commands/auth-status-dataplane-version.js +231 -0
- package/lib/commands/auth-status-display.js +94 -0
- package/lib/commands/auth-status.js +70 -6
- package/lib/commands/credential-push.js +10 -5
- package/lib/commands/datasource-load-export-cli.js +178 -0
- package/lib/commands/datasource-test-trust-cli.js +114 -0
- package/lib/commands/datasource-unified-test-cli.js +25 -17
- package/lib/commands/datasource-unified-test-cli.options.js +18 -3
- package/lib/commands/datasource-unified-test-e2e-cli-helpers.js +5 -5
- package/lib/commands/datasource-verify-audit-cli.js +96 -0
- package/lib/commands/datasource.js +68 -1
- package/lib/commands/dimension-value.js +43 -13
- package/lib/commands/dimension.js +38 -7
- package/lib/commands/identity-apply-sync.js +131 -0
- package/lib/commands/identity-group-cli.js +146 -0
- package/lib/commands/identity-membership-role-cache.js +175 -0
- package/lib/commands/identity-shared.js +94 -0
- package/lib/commands/identity-user-cli.js +158 -0
- package/lib/commands/identity.js +43 -0
- package/lib/commands/login.js +46 -16
- package/lib/commands/protection-cli-leaves.js +286 -0
- package/lib/commands/protection.js +50 -0
- package/lib/commands/repair.js +13 -3
- package/lib/commands/setup-modes.js +19 -6
- package/lib/commands/setup-prompts.js +41 -8
- package/lib/commands/setup.js +114 -9
- package/lib/commands/teardown.js +54 -5
- package/lib/commands/test-e2e-external.js +40 -9
- package/lib/commands/test-governance-cli-options.js +47 -0
- package/lib/commands/test-governance-command-action.js +55 -0
- package/lib/commands/test-governance-external.js +88 -0
- package/lib/commands/test-trust-cli-options.js +32 -0
- package/lib/commands/test-trust-command-action.js +118 -0
- package/lib/commands/test-trust-external.js +127 -0
- package/lib/commands/up-common.js +48 -14
- package/lib/commands/up-dataplane.js +21 -18
- package/lib/commands/up-miso.js +12 -8
- package/lib/commands/upload.js +38 -15
- package/lib/commands/wizard-core.js +3 -0
- package/lib/core/audit-logger.js +1 -34
- package/lib/core/config-admin-email.js +56 -0
- package/lib/core/config-device-dataplane.js +185 -0
- package/lib/core/config-normalize.js +60 -0
- package/lib/core/config-registered-controller-urls.js +54 -0
- package/lib/core/config.js +33 -50
- package/lib/core/secrets-ensure-infra.js +1 -1
- package/lib/core/secrets-env-content.js +86 -90
- package/lib/core/secrets-env-declarative-expand.js +170 -0
- package/lib/core/secrets-env-write.js +2 -0
- package/lib/core/secrets-load.js +106 -102
- package/lib/datasource/agent-trust-debug-log.js +45 -0
- package/lib/datasource/agent-trust-fetch.js +115 -0
- package/lib/datasource/agent-trust-map.js +101 -0
- package/lib/datasource/agent-trust-mock.js +44 -0
- package/lib/datasource/agent-trust-run.js +93 -0
- package/lib/datasource/agent-trust-timeout.js +25 -0
- package/lib/datasource/audit-evidence-display.js +80 -0
- package/lib/datasource/audit-evidence-extract-debug.js +103 -0
- package/lib/datasource/audit-evidence-extract.js +177 -0
- package/lib/datasource/audit-evidence-matrix-rows.js +158 -0
- package/lib/datasource/audit-evidence-operation-aliases.js +63 -0
- package/lib/datasource/audit-evidence-poll.js +109 -0
- package/lib/datasource/audit-evidence-run.js +177 -0
- package/lib/datasource/audit-evidence-verify-helpers.js +167 -0
- package/lib/datasource/audit-evidence-verify-matrix.js +177 -0
- package/lib/datasource/audit-evidence-verify.js +108 -0
- package/lib/datasource/bulk-loader-run-batches.js +88 -0
- package/lib/datasource/bulk-loader-service-retry.js +52 -0
- package/lib/datasource/bulk-loader-service.js +127 -0
- package/lib/datasource/capability/run-capability-dimension.js +2 -1
- package/lib/datasource/datasource-exporter-resolve.js +82 -0
- package/lib/datasource/datasource-exporter-service.js +144 -0
- package/lib/datasource/datasource-identifier.js +148 -0
- package/lib/datasource/datasource-load-export-context.js +92 -0
- package/lib/datasource/integration-context.js +6 -4
- package/lib/datasource/local-data-paths.js +145 -0
- package/lib/datasource/log-cleaner.js +241 -0
- package/lib/datasource/log-viewer.js +111 -6
- package/lib/datasource/record-file-parser.js +85 -0
- package/lib/datasource/record-mapper.js +129 -0
- package/lib/datasource/resolve-app.js +74 -55
- package/lib/datasource/test-e2e.js +26 -2
- package/lib/datasource/unified-validation-run-resolve.js +45 -12
- package/lib/datasource/unified-validation-run.js +43 -17
- package/lib/datasource/validate.js +37 -20
- package/lib/external-system/deploy.js +5 -1
- package/lib/external-system/test-system-level.js +25 -4
- package/lib/external-system/test.js +3 -1
- package/lib/governance/governance-pack-loader.js +70 -0
- package/lib/governance/governance-run-display.js +119 -0
- package/lib/identity/identity-apply-core.js +108 -0
- package/lib/identity/identity-apply-phases.js +244 -0
- package/lib/identity/identity-apply-service.js +77 -0
- package/lib/identity/identity-csv-parser.js +114 -0
- package/lib/internal/fs-real-sync.js +29 -1
- package/lib/internal/node-fs.js +2 -0
- package/lib/protection/auth-context.js +45 -0
- package/lib/protection/convert-batch.js +90 -0
- package/lib/protection/load.js +41 -0
- package/lib/protection/paths.js +158 -0
- package/lib/protection/preflight-datasource-ready.js +46 -0
- package/lib/protection/protection-create-probe-lines.js +142 -0
- package/lib/protection/protection-create-probes.js +95 -0
- package/lib/protection/protection-create-scaffold.js +281 -0
- package/lib/protection/protection-display-helpers.js +445 -0
- package/lib/protection/protection-display.js +215 -0
- package/lib/protection/protection-preset-registry.js +97 -0
- package/lib/protection/report-exit.js +62 -0
- package/lib/protection/resolve.js +202 -0
- package/lib/protection/run-commands-validate.js +130 -0
- package/lib/protection/run-commands.js +216 -0
- package/lib/protection/run-protection-create-helpers.js +134 -0
- package/lib/protection/run-protection-create-probes-flow.js +43 -0
- package/lib/protection/run-protection-create.js +191 -0
- package/lib/protection/scope.js +22 -0
- package/lib/protection/sync-after-upload.js +64 -0
- package/lib/protection/upload-batch.js +127 -0
- package/lib/protection/validate-batch.js +113 -0
- package/lib/protection/validate-local.js +65 -0
- package/lib/resolvers/dimension-file.js +29 -4
- package/lib/schema/application-schema.json +4 -0
- package/lib/schema/external-datasource.schema.json +47 -13
- package/lib/schema/flag-map-validation-run.json +2 -2
- package/lib/schema/infra.parameter.yaml +10 -0
- package/lib/schema/protection.schema.json +374 -0
- package/lib/schema/type/credential-field-registry.json +636 -0
- package/lib/schema/wizard-config.schema.json +1 -1
- package/lib/utils/agent-trust-run-display.js +231 -0
- package/lib/utils/agent-trust-run-exit.js +59 -0
- package/lib/utils/agent-trust-wait-ui.js +94 -0
- package/lib/utils/app-config-resolver.js +24 -1
- package/lib/utils/applications-config-defaults.js +206 -0
- package/lib/utils/auth-config-validator.js +2 -12
- package/lib/utils/bash-secret-env.js +1 -1
- package/lib/utils/cli-exit-codes.js +30 -0
- package/lib/utils/cli-sync-options.js +33 -0
- package/lib/utils/compose-generate-docker-compose.js +111 -6
- package/lib/utils/compose-generator.js +17 -8
- package/lib/utils/controller-url.js +50 -7
- package/lib/utils/credential-push-ui.js +90 -0
- package/lib/utils/dataplane-cli-version-gate.js +202 -0
- package/lib/utils/dataplane-cli-version-help.js +74 -0
- package/lib/utils/datasource-export-display.js +137 -0
- package/lib/utils/datasource-load-display.js +174 -0
- package/lib/utils/datasource-load-export-ui.js +120 -0
- package/lib/utils/datasource-test-run-capability-scope.js +3 -3
- package/lib/utils/datasource-test-run-display.js +3 -3
- package/lib/utils/datasource-test-run-tty-log.js +5 -1
- package/lib/utils/env-copy.js +99 -14
- package/lib/utils/env-template.js +5 -1
- package/lib/utils/health-check-url.js +18 -15
- package/lib/utils/health-check.js +7 -5
- package/lib/utils/help-builder.js +30 -24
- package/lib/utils/infra-optional-service-flags.js +69 -0
- package/lib/utils/installation-log-core.js +282 -0
- package/lib/utils/installation-log-record.js +237 -0
- package/lib/utils/installation-log.js +123 -0
- package/lib/utils/log-redaction.js +105 -0
- package/lib/utils/manifest-location.js +164 -0
- package/lib/utils/manifest-source-emit.js +184 -0
- package/lib/utils/paths.js +238 -89
- package/lib/utils/remote-secrets-loader.js +7 -1
- package/lib/utils/run-cli-flags.js +29 -0
- package/lib/utils/secrets-canonical.js +10 -3
- package/lib/utils/secrets-path.js +3 -4
- package/lib/utils/secrets-utils.js +20 -10
- package/lib/utils/semver-compare.js +127 -0
- package/lib/utils/system-builder-root.js +10 -2
- package/lib/utils/upload-pipeline-ui.js +91 -0
- package/lib/utils/url-declarative-public-base.js +80 -12
- package/lib/utils/url-declarative-resolve-build-urls.js +238 -0
- package/lib/utils/url-declarative-resolve-build.js +24 -393
- package/lib/utils/url-declarative-resolve-expand-token.js +189 -0
- package/lib/utils/url-declarative-resolve-load-doc.js +12 -3
- package/lib/utils/url-declarative-resolve-surface-state.js +102 -0
- package/lib/utils/url-declarative-resolve.js +47 -7
- package/lib/utils/url-declarative-runtime-base-path.js +21 -1
- package/lib/utils/urls-local-registry-scan.js +103 -0
- package/lib/utils/urls-local-registry.js +161 -90
- package/lib/utils/validation-poll-ui.js +14 -7
- package/lib/validation/wizard-config-validator.js +58 -19
- package/package.json +3 -1
- package/templates/applications/dataplane/application.yaml +4 -0
- package/templates/applications/dataplane/env.template +13 -1
- package/templates/applications/dataplane/rbac.yaml +4 -0
- package/templates/applications/miso-controller/application.yaml +2 -0
- package/templates/applications/miso-controller/env.template +27 -29
- package/templates/external-system/README.md.hbs +56 -8
- package/.npmrc.token +0 -1
|
@@ -13,7 +13,15 @@ const chalk = require('chalk');
|
|
|
13
13
|
const logger = require('../utils/logger');
|
|
14
14
|
const { resolveAppKeyForDatasource } = require('./resolve-app');
|
|
15
15
|
const { getIntegrationPath } = require('../utils/paths');
|
|
16
|
-
const {
|
|
16
|
+
const {
|
|
17
|
+
sectionTitle,
|
|
18
|
+
headerKeyValue,
|
|
19
|
+
formatBlockingError,
|
|
20
|
+
successGlyph,
|
|
21
|
+
failureGlyph
|
|
22
|
+
} = require('../utils/cli-test-layout-chalk');
|
|
23
|
+
const { statusGlyph } = require('../utils/datasource-test-run-display');
|
|
24
|
+
const { trustDecisionToRowStatus } = require('./agent-trust-map');
|
|
17
25
|
|
|
18
26
|
/**
|
|
19
27
|
* Get the path to the latest log file in a directory matching a glob-like pattern
|
|
@@ -48,7 +56,13 @@ async function getLatestLogPath(logsDir, pattern) {
|
|
|
48
56
|
*/
|
|
49
57
|
function isStructuralTestLogFileName(name) {
|
|
50
58
|
if (!name || typeof name !== 'string' || !name.endsWith('.json')) return false;
|
|
51
|
-
if (
|
|
59
|
+
if (
|
|
60
|
+
name.startsWith('test-e2e-') ||
|
|
61
|
+
name.startsWith('test-integration-') ||
|
|
62
|
+
name.startsWith('test-trust-')
|
|
63
|
+
) {
|
|
64
|
+
return false;
|
|
65
|
+
}
|
|
52
66
|
return name.startsWith('test-');
|
|
53
67
|
}
|
|
54
68
|
|
|
@@ -232,6 +246,85 @@ function structuralEnvelopeStatusLine(status) {
|
|
|
232
246
|
* @param {Object} data - Parsed JSON { request, response?, error? }
|
|
233
247
|
* @param {string} [fileName] - Log file name for header
|
|
234
248
|
*/
|
|
249
|
+
function trustLogGlyph(trustDecision) {
|
|
250
|
+
return statusGlyph(trustDecisionToRowStatus(trustDecision));
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
function logTrustWarningsSection(run) {
|
|
254
|
+
const warnings = Array.isArray(run.highLevelWarnings) ? run.highLevelWarnings : [];
|
|
255
|
+
if (!warnings.length) return;
|
|
256
|
+
logger.log(sectionTitle('Warnings:'));
|
|
257
|
+
for (const w of warnings) {
|
|
258
|
+
logger.log(` ${chalk.yellow('•')} ${chalk.white(String(w))}`);
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
function logTrustFindingsSection(run) {
|
|
263
|
+
const findings = Array.isArray(run.findings) ? run.findings : [];
|
|
264
|
+
if (!findings.length) return;
|
|
265
|
+
logger.log(sectionTitle('Findings:'));
|
|
266
|
+
const cap = 15;
|
|
267
|
+
for (let i = 0; i < Math.min(cap, findings.length); i += 1) {
|
|
268
|
+
const f = findings[i];
|
|
269
|
+
const code = f?.code || f?.stableCode || 'finding';
|
|
270
|
+
const msg = f?.message ? String(f.message) : '';
|
|
271
|
+
logger.log(` ${chalk.gray(`• ${code}`)} ${msg}`.trim());
|
|
272
|
+
}
|
|
273
|
+
if (findings.length > cap) {
|
|
274
|
+
logger.log(chalk.gray(` … and ${findings.length - cap} more (open JSON file for full list)`));
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
/**
|
|
279
|
+
* Format agent trust log (`datasource test-trust --debug` → `test-trust-*.json`).
|
|
280
|
+
* @param {Object} data - Parsed JSON { request, response?, error? }
|
|
281
|
+
* @param {string} [fileName] - Log file name for header
|
|
282
|
+
*/
|
|
283
|
+
function formatTrustLog(data, fileName) {
|
|
284
|
+
logger.log('');
|
|
285
|
+
logger.log(sectionTitle('Agent trust log'));
|
|
286
|
+
if (fileName) logger.log(chalk.gray(fileName));
|
|
287
|
+
const req = data.request || {};
|
|
288
|
+
logger.log('');
|
|
289
|
+
logger.log(sectionTitle('Request:'));
|
|
290
|
+
logger.log(headerKeyValue('datasourceKey:', String(req.datasourceKey ?? '—')));
|
|
291
|
+
if (req.revalidate !== undefined) {
|
|
292
|
+
logger.log(chalk.gray(` revalidate: ${req.revalidate}`));
|
|
293
|
+
}
|
|
294
|
+
if (data.error) {
|
|
295
|
+
logger.log('');
|
|
296
|
+
logger.log(formatBlockingError(`Error: ${data.error}`));
|
|
297
|
+
return;
|
|
298
|
+
}
|
|
299
|
+
const run = data.response || {};
|
|
300
|
+
logger.log('');
|
|
301
|
+
logger.log(sectionTitle('Response:'));
|
|
302
|
+
logger.log(headerKeyValue('System:', String(run.systemKey ?? '—')));
|
|
303
|
+
const confPct = Math.round((Number(run.confidence) || 0) * 100);
|
|
304
|
+
logger.log(
|
|
305
|
+
headerKeyValue(
|
|
306
|
+
'Trust:',
|
|
307
|
+
`${trustLogGlyph(run.trustDecision)} ${run.trustDecision ?? '—'} · ${confPct}% · ${run.validationStatus ?? '—'}`
|
|
308
|
+
)
|
|
309
|
+
);
|
|
310
|
+
if (run.validatedAt) {
|
|
311
|
+
logger.log(chalk.gray(` validatedAt: ${run.validatedAt}`));
|
|
312
|
+
}
|
|
313
|
+
if (run.inputHash) {
|
|
314
|
+
logger.log(chalk.gray(` inputHash: ${truncate(String(run.inputHash), 40)}`));
|
|
315
|
+
}
|
|
316
|
+
if (run.cacheHit !== undefined && run.cacheHit !== null) {
|
|
317
|
+
logger.log(chalk.gray(` cacheHit: ${run.cacheHit}`));
|
|
318
|
+
}
|
|
319
|
+
if (run.summary) {
|
|
320
|
+
logger.log('');
|
|
321
|
+
logger.log(chalk.white(' Summary'));
|
|
322
|
+
logger.log(` ${run.summary}`);
|
|
323
|
+
}
|
|
324
|
+
logTrustWarningsSection(run);
|
|
325
|
+
logTrustFindingsSection(run);
|
|
326
|
+
}
|
|
327
|
+
|
|
235
328
|
function formatStructuralTestLog(data, fileName) {
|
|
236
329
|
logger.log('');
|
|
237
330
|
logger.log(sectionTitle('Structural validation log'));
|
|
@@ -263,7 +356,7 @@ function formatStructuralTestLog(data, fileName) {
|
|
|
263
356
|
/**
|
|
264
357
|
* Format log content by type
|
|
265
358
|
* @param {Object} parsed - Parsed JSON log
|
|
266
|
-
* @param {'test'|'test-e2e'|'test-integration'} logType - Log type
|
|
359
|
+
* @param {'test'|'test-e2e'|'test-integration'|'test-trust'} logType - Log type
|
|
267
360
|
* @param {string} [fileName] - File name for header
|
|
268
361
|
*/
|
|
269
362
|
function formatLogContent(parsed, logType, fileName) {
|
|
@@ -271,11 +364,15 @@ function formatLogContent(parsed, logType, fileName) {
|
|
|
271
364
|
formatStructuralTestLog(parsed, fileName);
|
|
272
365
|
return;
|
|
273
366
|
}
|
|
367
|
+
if (logType === 'test-trust') {
|
|
368
|
+
formatTrustLog(parsed, fileName);
|
|
369
|
+
return;
|
|
370
|
+
}
|
|
274
371
|
if (logType === 'test-e2e') {
|
|
275
372
|
formatE2ELog(parsed, fileName);
|
|
276
|
-
|
|
277
|
-
formatIntegrationLog(parsed, fileName);
|
|
373
|
+
return;
|
|
278
374
|
}
|
|
375
|
+
formatIntegrationLog(parsed, fileName);
|
|
279
376
|
}
|
|
280
377
|
|
|
281
378
|
/**
|
|
@@ -313,7 +410,7 @@ function parseDatasourceLogJson(content, logPath) {
|
|
|
313
410
|
* @param {Object} options - Options
|
|
314
411
|
* @param {string} [options.app] - App key (optional, resolved from key if omitted)
|
|
315
412
|
* @param {string} [options.file] - Path to log file (overrides app resolution)
|
|
316
|
-
* @param {'test'|'test-e2e'|'test-integration'} options.logType - Log type
|
|
413
|
+
* @param {'test'|'test-e2e'|'test-integration'|'test-trust'} options.logType - Log type
|
|
317
414
|
* @throws {Error} When file not found or invalid JSON
|
|
318
415
|
*/
|
|
319
416
|
/* eslint-disable-next-line max-statements -- Resolve path, read, parse, format */
|
|
@@ -336,6 +433,13 @@ async function runLogViewer(datasourceKey, options) {
|
|
|
336
433
|
`No structural validation log found in ${logsDir}. Run: aifabrix datasource test <key> --debug`
|
|
337
434
|
);
|
|
338
435
|
}
|
|
436
|
+
} else if (logType === 'test-trust') {
|
|
437
|
+
logPath = await getLatestLogPath(logsDir, 'test-trust-');
|
|
438
|
+
if (!logPath) {
|
|
439
|
+
throw new Error(
|
|
440
|
+
`No test-trust log found in ${logsDir}. Run: aifabrix datasource test-trust <key> --debug`
|
|
441
|
+
);
|
|
442
|
+
}
|
|
339
443
|
} else {
|
|
340
444
|
const pattern = logType === 'test-e2e' ? 'test-e2e-' : 'test-integration-';
|
|
341
445
|
logPath = await getLatestLogPath(logsDir, pattern);
|
|
@@ -360,6 +464,7 @@ module.exports = {
|
|
|
360
464
|
formatE2ELog,
|
|
361
465
|
formatIntegrationLog,
|
|
362
466
|
formatStructuralTestLog,
|
|
467
|
+
formatTrustLog,
|
|
363
468
|
parseDatasourceLogJson,
|
|
364
469
|
runLogViewer
|
|
365
470
|
};
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Parse JSON array and NDJSON record fixture files (plan 144)
|
|
3
|
+
* @author AI Fabrix Team
|
|
4
|
+
* @version 1.0.0
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
'use strict';
|
|
8
|
+
|
|
9
|
+
const fs = require('fs');
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* @param {string} content
|
|
13
|
+
* @returns {Object[]}
|
|
14
|
+
*/
|
|
15
|
+
function parseNdjsonContent(content) {
|
|
16
|
+
const lines = String(content).split(/\r?\n/);
|
|
17
|
+
const records = [];
|
|
18
|
+
for (let i = 0; i < lines.length; i += 1) {
|
|
19
|
+
const line = lines[i].trim();
|
|
20
|
+
if (!line) continue;
|
|
21
|
+
let parsed;
|
|
22
|
+
try {
|
|
23
|
+
parsed = JSON.parse(line);
|
|
24
|
+
} catch (err) {
|
|
25
|
+
throw new Error(`NDJSON parse error at line ${i + 1}: ${err.message}`);
|
|
26
|
+
}
|
|
27
|
+
if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
|
|
28
|
+
throw new Error(`NDJSON line ${i + 1}: expected a JSON object`);
|
|
29
|
+
}
|
|
30
|
+
records.push(parsed);
|
|
31
|
+
}
|
|
32
|
+
return records;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* @param {string} content
|
|
37
|
+
* @returns {Object[]}
|
|
38
|
+
*/
|
|
39
|
+
function parseJsonArrayContent(content) {
|
|
40
|
+
let parsed;
|
|
41
|
+
try {
|
|
42
|
+
parsed = JSON.parse(content);
|
|
43
|
+
} catch (err) {
|
|
44
|
+
throw new Error(`JSON parse error: ${err.message}`);
|
|
45
|
+
}
|
|
46
|
+
if (!Array.isArray(parsed)) {
|
|
47
|
+
throw new Error('JSON file must be a top-level array of objects');
|
|
48
|
+
}
|
|
49
|
+
parsed.forEach((row, index) => {
|
|
50
|
+
if (!row || typeof row !== 'object' || Array.isArray(row)) {
|
|
51
|
+
throw new Error(`JSON array index ${index}: expected an object`);
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
return parsed;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Parse a local record fixture file.
|
|
59
|
+
* @param {string} filePath
|
|
60
|
+
* @param {'json'|'ndjson'} format
|
|
61
|
+
* @returns {Object[]}
|
|
62
|
+
*/
|
|
63
|
+
function parseRecordFile(filePath, format) {
|
|
64
|
+
const content = fs.readFileSync(filePath, 'utf8');
|
|
65
|
+
if (format === 'ndjson') {
|
|
66
|
+
return parseNdjsonContent(content);
|
|
67
|
+
}
|
|
68
|
+
return parseJsonArrayContent(content);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Rough payload size estimate for dry-run (bytes).
|
|
73
|
+
* @param {Object[]} records
|
|
74
|
+
* @returns {number}
|
|
75
|
+
*/
|
|
76
|
+
function estimatePayloadBytes(records) {
|
|
77
|
+
return Buffer.byteLength(JSON.stringify(records), 'utf8');
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
module.exports = {
|
|
81
|
+
parseRecordFile,
|
|
82
|
+
parseNdjsonContent,
|
|
83
|
+
parseJsonArrayContent,
|
|
84
|
+
estimatePayloadBytes
|
|
85
|
+
};
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Map fixture payloads to canonical bulk sync records (plan 144)
|
|
3
|
+
* @author AI Fabrix Team
|
|
4
|
+
* @version 1.0.0
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
'use strict';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* @param {Object} obj
|
|
11
|
+
* @returns {boolean}
|
|
12
|
+
*/
|
|
13
|
+
function isCanonicalBulkRecord(obj) {
|
|
14
|
+
return (
|
|
15
|
+
obj &&
|
|
16
|
+
typeof obj === 'object' &&
|
|
17
|
+
typeof obj.key === 'string' &&
|
|
18
|
+
obj.key.trim().length > 0 &&
|
|
19
|
+
typeof obj.recordType === 'string' &&
|
|
20
|
+
obj.recordType.trim().length > 0 &&
|
|
21
|
+
obj.metadata !== undefined &&
|
|
22
|
+
obj.metadata !== null &&
|
|
23
|
+
typeof obj.metadata === 'object' &&
|
|
24
|
+
!Array.isArray(obj.metadata)
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* @param {string[]} primaryKeyFields
|
|
30
|
+
* @param {Object} payload
|
|
31
|
+
* @returns {string}
|
|
32
|
+
*/
|
|
33
|
+
function buildKeyFromPrimaryKey(primaryKeyFields, payload) {
|
|
34
|
+
if (!primaryKeyFields.length) {
|
|
35
|
+
throw new Error('Datasource primaryKey is empty; provide records with a key field');
|
|
36
|
+
}
|
|
37
|
+
if (primaryKeyFields.length === 1) {
|
|
38
|
+
const field = primaryKeyFields[0];
|
|
39
|
+
const value = payload[field];
|
|
40
|
+
if (value === undefined || value === null || String(value).trim() === '') {
|
|
41
|
+
throw new Error(`Missing primary key field '${field}' in record payload`);
|
|
42
|
+
}
|
|
43
|
+
return String(value);
|
|
44
|
+
}
|
|
45
|
+
const parts = [];
|
|
46
|
+
for (const field of primaryKeyFields) {
|
|
47
|
+
const value = payload[field];
|
|
48
|
+
if (value === undefined || value === null || String(value).trim() === '') {
|
|
49
|
+
throw new Error(`Missing composite primary key field '${field}' in record payload`);
|
|
50
|
+
}
|
|
51
|
+
parts.push(`${field}=${String(value)}`);
|
|
52
|
+
}
|
|
53
|
+
return parts.join(',');
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* @param {Object} datasource
|
|
58
|
+
* @param {Object} payload
|
|
59
|
+
* @returns {import('../api/types/records-bulk.types').ExternalBulkRecord}
|
|
60
|
+
*/
|
|
61
|
+
function mapPayloadToBulkRecord(datasource, payload) {
|
|
62
|
+
const primaryKey = Array.isArray(datasource.primaryKey) ? datasource.primaryKey : [];
|
|
63
|
+
const recordType =
|
|
64
|
+
typeof datasource.resourceType === 'string' && datasource.resourceType.trim()
|
|
65
|
+
? datasource.resourceType.trim()
|
|
66
|
+
: 'record';
|
|
67
|
+
const key = buildKeyFromPrimaryKey(primaryKey, payload);
|
|
68
|
+
let displayName = '';
|
|
69
|
+
const labelKey = datasource.labelKey;
|
|
70
|
+
if (Array.isArray(labelKey) && labelKey.length > 0) {
|
|
71
|
+
displayName = labelKey
|
|
72
|
+
.map(field => payload[field])
|
|
73
|
+
.filter(v => v !== undefined && v !== null && String(v).trim() !== '')
|
|
74
|
+
.map(v => String(v))
|
|
75
|
+
.join(' ');
|
|
76
|
+
} else if (typeof labelKey === 'string' && labelKey.trim() && payload[labelKey] !== undefined && payload[labelKey] !== null) {
|
|
77
|
+
displayName = String(payload[labelKey]);
|
|
78
|
+
}
|
|
79
|
+
const metadata = { ...payload };
|
|
80
|
+
if (metadata.externalId === undefined) {
|
|
81
|
+
metadata.externalId = key;
|
|
82
|
+
}
|
|
83
|
+
return {
|
|
84
|
+
key,
|
|
85
|
+
displayName: displayName || '',
|
|
86
|
+
recordType,
|
|
87
|
+
metadata
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* @param {Object[]} rawRecords
|
|
93
|
+
* @param {Object} datasource
|
|
94
|
+
* @returns {import('../api/types/records-bulk.types').ExternalBulkRecord[]}
|
|
95
|
+
*/
|
|
96
|
+
function normalizeRecordsForBulk(rawRecords, datasource) {
|
|
97
|
+
if (!Array.isArray(rawRecords) || rawRecords.length === 0) {
|
|
98
|
+
throw new Error('Record file is empty after parse');
|
|
99
|
+
}
|
|
100
|
+
return rawRecords.map((row, index) => {
|
|
101
|
+
try {
|
|
102
|
+
if (isCanonicalBulkRecord(row)) {
|
|
103
|
+
const meta = { ...row.metadata };
|
|
104
|
+
if (meta.externalId === undefined) {
|
|
105
|
+
meta.externalId = row.key;
|
|
106
|
+
}
|
|
107
|
+
return {
|
|
108
|
+
key: String(row.key).trim(),
|
|
109
|
+
displayName:
|
|
110
|
+
row.displayName !== undefined && row.displayName !== null
|
|
111
|
+
? String(row.displayName)
|
|
112
|
+
: '',
|
|
113
|
+
recordType: String(row.recordType).trim(),
|
|
114
|
+
metadata: meta
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
return mapPayloadToBulkRecord(datasource, row);
|
|
118
|
+
} catch (err) {
|
|
119
|
+
throw new Error(`Record ${index + 1}: ${err.message}`);
|
|
120
|
+
}
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
module.exports = {
|
|
125
|
+
isCanonicalBulkRecord,
|
|
126
|
+
buildKeyFromPrimaryKey,
|
|
127
|
+
mapPayloadToBulkRecord,
|
|
128
|
+
normalizeRecordsForBulk
|
|
129
|
+
};
|
|
@@ -5,49 +5,82 @@
|
|
|
5
5
|
* @version 2.0.0
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
const path = require('path');
|
|
9
8
|
const fs = require('fs');
|
|
10
9
|
const {
|
|
11
10
|
getIntegrationPath,
|
|
12
11
|
listIntegrationAppNames,
|
|
13
12
|
resolveIntegrationAppKeyFromCwd
|
|
14
13
|
} = require('../utils/paths');
|
|
15
|
-
const {
|
|
16
|
-
|
|
14
|
+
const {
|
|
15
|
+
appHasDatasourceIdentifier,
|
|
16
|
+
pickIntegrationAppForDatasourceIdentifier
|
|
17
|
+
} = require('./datasource-identifier');
|
|
18
|
+
|
|
19
|
+
/** @deprecated Use appHasDatasourceIdentifier — kept for tests and backward compatibility */
|
|
20
|
+
function appHasDatasourceKey(appKey, datasourceKey) {
|
|
21
|
+
return appHasDatasourceIdentifier(appKey, datasourceKey);
|
|
22
|
+
}
|
|
17
23
|
|
|
18
24
|
/**
|
|
19
|
-
*
|
|
20
|
-
* @param {string}
|
|
21
|
-
* @
|
|
22
|
-
* @returns {boolean} True if this app has a datasource with that key
|
|
25
|
+
* @param {string} key
|
|
26
|
+
* @param {string} [explicitApp]
|
|
27
|
+
* @returns {{ appKey: string }|null}
|
|
23
28
|
*/
|
|
24
|
-
function
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
29
|
+
function resolveFromExplicitApp(key, explicitApp) {
|
|
30
|
+
if (!explicitApp || typeof explicitApp !== 'string' || !explicitApp.trim()) {
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
const appKey = explicitApp.trim();
|
|
34
|
+
if (fs.existsSync(getIntegrationPath(appKey))) {
|
|
35
|
+
return { appKey };
|
|
36
|
+
}
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* @param {string} key
|
|
42
|
+
* @param {string[]} appNames
|
|
43
|
+
* @returns {{ appKey: string }|null}
|
|
44
|
+
* @throws {Error}
|
|
45
|
+
*/
|
|
46
|
+
function resolveFromAppScan(key, appNames) {
|
|
47
|
+
const matches = appNames.filter(appName => appHasDatasourceIdentifier(appName, key));
|
|
48
|
+
if (matches.length === 1) {
|
|
49
|
+
return { appKey: matches[0] };
|
|
50
|
+
}
|
|
51
|
+
if (matches.length > 1) {
|
|
52
|
+
throw new Error(
|
|
53
|
+
`More than one app has this datasource; add --app <app>. Apps: ${matches.join(', ')}`
|
|
54
|
+
);
|
|
32
55
|
}
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
// skip unreadable or invalid files
|
|
46
|
-
}
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* @param {string} key
|
|
61
|
+
* @param {string[]} appNames
|
|
62
|
+
* @returns {{ appKey: string }|null}
|
|
63
|
+
*/
|
|
64
|
+
function resolveFromPrefixOrSegments(key, appNames) {
|
|
65
|
+
const prefixApp = pickIntegrationAppForDatasourceIdentifier(key, appNames);
|
|
66
|
+
if (prefixApp && appHasDatasourceIdentifier(prefixApp, key)) {
|
|
67
|
+
return { appKey: prefixApp };
|
|
47
68
|
}
|
|
48
|
-
|
|
69
|
+
const segments = key.split('-');
|
|
70
|
+
if (segments.length < 2) {
|
|
71
|
+
return null;
|
|
72
|
+
}
|
|
73
|
+
const candidate = segments.slice(0, -1).join('-');
|
|
74
|
+
const candidatePath = getIntegrationPath(candidate);
|
|
75
|
+
if (fs.existsSync(candidatePath) && appHasDatasourceIdentifier(candidate, key)) {
|
|
76
|
+
return { appKey: candidate };
|
|
77
|
+
}
|
|
78
|
+
return null;
|
|
49
79
|
}
|
|
50
80
|
|
|
81
|
+
const RESOLVE_APP_HINT =
|
|
82
|
+
'Could not determine app context. Use --app <app>, a datasource key, a datasource JSON filename (e.g. myapp-datasource-contacts), or run from integration/<systemKey>/ directory.';
|
|
83
|
+
|
|
51
84
|
/**
|
|
52
85
|
* Resolve app key for a datasource: explicit --app, cwd, scan by key, or parse key convention.
|
|
53
86
|
* @async
|
|
@@ -56,7 +89,6 @@ function appHasDatasourceKey(appKey, datasourceKey) {
|
|
|
56
89
|
* @returns {Promise<{appKey: string}>} Resolved app key
|
|
57
90
|
* @throws {Error} When app cannot be determined or multiple apps match
|
|
58
91
|
*/
|
|
59
|
-
/* eslint-disable-next-line max-statements -- Resolution order: explicit, cwd, scan, parse */
|
|
60
92
|
async function resolveAppKeyForDatasource(datasourceKey, explicitApp) {
|
|
61
93
|
if (!datasourceKey || typeof datasourceKey !== 'string') {
|
|
62
94
|
throw new Error('Datasource key is required');
|
|
@@ -66,41 +98,28 @@ async function resolveAppKeyForDatasource(datasourceKey, explicitApp) {
|
|
|
66
98
|
throw new Error('Datasource key cannot be empty');
|
|
67
99
|
}
|
|
68
100
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
return { appKey: explicitApp.trim() };
|
|
73
|
-
}
|
|
101
|
+
const explicit = resolveFromExplicitApp(key, explicitApp);
|
|
102
|
+
if (explicit) {
|
|
103
|
+
return explicit;
|
|
74
104
|
}
|
|
75
105
|
|
|
76
106
|
const fromCwd = resolveIntegrationAppKeyFromCwd();
|
|
77
|
-
if (fromCwd &&
|
|
107
|
+
if (fromCwd && appHasDatasourceIdentifier(fromCwd, key)) {
|
|
78
108
|
return { appKey: fromCwd };
|
|
79
109
|
}
|
|
80
110
|
|
|
81
111
|
const appNames = listIntegrationAppNames();
|
|
82
|
-
const
|
|
83
|
-
if (
|
|
84
|
-
return
|
|
85
|
-
}
|
|
86
|
-
if (matches.length > 1) {
|
|
87
|
-
throw new Error(
|
|
88
|
-
`More than one app has this datasource; add --app <app>. Apps: ${matches.join(', ')}`
|
|
89
|
-
);
|
|
112
|
+
const scanned = resolveFromAppScan(key, appNames);
|
|
113
|
+
if (scanned) {
|
|
114
|
+
return scanned;
|
|
90
115
|
}
|
|
91
116
|
|
|
92
|
-
const
|
|
93
|
-
if (
|
|
94
|
-
|
|
95
|
-
const candidatePath = getIntegrationPath(candidate);
|
|
96
|
-
if (fs.existsSync(candidatePath) && appHasDatasourceKey(candidate, key)) {
|
|
97
|
-
return { appKey: candidate };
|
|
98
|
-
}
|
|
117
|
+
const inferred = resolveFromPrefixOrSegments(key, appNames);
|
|
118
|
+
if (inferred) {
|
|
119
|
+
return inferred;
|
|
99
120
|
}
|
|
100
121
|
|
|
101
|
-
throw new Error(
|
|
102
|
-
'Could not determine app context. Use --app <app> or run from integration/<systemKey>/ directory.'
|
|
103
|
-
);
|
|
122
|
+
throw new Error(RESOLVE_APP_HINT);
|
|
104
123
|
}
|
|
105
124
|
|
|
106
125
|
module.exports = {
|
|
@@ -11,7 +11,7 @@ const chalk = require('chalk');
|
|
|
11
11
|
const logger = require('../utils/logger');
|
|
12
12
|
const { getIntegrationPath } = require('../utils/paths');
|
|
13
13
|
const { resolveAppKeyForDatasource } = require('./resolve-app');
|
|
14
|
-
const { infoLine } = require('../utils/cli-test-layout-chalk');
|
|
14
|
+
const { formatProgress, formatSuccessLine, infoLine } = require('../utils/cli-test-layout-chalk');
|
|
15
15
|
const { runUnifiedDatasourceValidation } = require('./unified-validation-run');
|
|
16
16
|
const { includeDebugForRequest } = require('../utils/validation-run-request');
|
|
17
17
|
const { e2eShapeFromEnvelope } = require('../utils/datasource-test-run-legacy-adapter');
|
|
@@ -41,7 +41,9 @@ function buildUnifiedE2eRunOptions(options, timeoutMs, pk) {
|
|
|
41
41
|
minRecordCount: options.minRecordCount,
|
|
42
42
|
capabilityKey: options.capabilityKey,
|
|
43
43
|
timeout: timeoutMs,
|
|
44
|
-
sync: options.sync === true
|
|
44
|
+
sync: options.sync === true,
|
|
45
|
+
authConfig: options.authConfig,
|
|
46
|
+
dataplaneUrl: options.dataplaneUrl || options.dataplane
|
|
45
47
|
};
|
|
46
48
|
}
|
|
47
49
|
|
|
@@ -51,6 +53,25 @@ function logE2eDatasourceBanner(datasourceKey, verbose) {
|
|
|
51
53
|
logger.log(infoLine(`🧪 Running E2E test for datasource: ${datasourceKey}`));
|
|
52
54
|
}
|
|
53
55
|
|
|
56
|
+
/**
|
|
57
|
+
* Compact per-datasource progress (default test-e2e / system rollup when not --verbose).
|
|
58
|
+
* @param {string} datasourceKey
|
|
59
|
+
* @param {boolean} verbose
|
|
60
|
+
*/
|
|
61
|
+
function logE2eDatasourceProgress(datasourceKey, verbose) {
|
|
62
|
+
if (verbose) return;
|
|
63
|
+
logger.log(formatProgress(`Running E2E validation for ${datasourceKey}…`));
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* @param {string} datasourceKey
|
|
68
|
+
* @param {boolean} verbose
|
|
69
|
+
*/
|
|
70
|
+
function logE2eDatasourceValidated(datasourceKey, verbose) {
|
|
71
|
+
if (verbose) return;
|
|
72
|
+
logger.log(formatSuccessLine(`${datasourceKey} validated`));
|
|
73
|
+
}
|
|
74
|
+
|
|
54
75
|
/**
|
|
55
76
|
* Resolve primaryKeyValue for request body: string as-is, or read and parse JSON from @path
|
|
56
77
|
* @param {string} [value]
|
|
@@ -174,6 +195,7 @@ async function runDatasourceTestE2E(datasourceKey, options = {}) {
|
|
|
174
195
|
const cliWallStartedAt = Date.now();
|
|
175
196
|
const { appKey } = await resolveAppKeyForDatasource(datasourceKey, options.app);
|
|
176
197
|
logE2eDatasourceBanner(datasourceKey, options.verbose);
|
|
198
|
+
logE2eDatasourceProgress(datasourceKey, options.verbose);
|
|
177
199
|
const pk = await resolvePrimaryKeyValue(options.primaryKeyValue);
|
|
178
200
|
const timeoutMs = resolveE2ePollTimeoutMs(options);
|
|
179
201
|
const requestMeta = buildE2eRequestMeta(datasourceKey, options, pk);
|
|
@@ -184,6 +206,8 @@ async function runDatasourceTestE2E(datasourceKey, options = {}) {
|
|
|
184
206
|
|
|
185
207
|
await throwIfUnifiedE2EBlocked(unifiedResult, appKey, options, requestMeta);
|
|
186
208
|
|
|
209
|
+
logE2eDatasourceValidated(datasourceKey, options.verbose);
|
|
210
|
+
|
|
187
211
|
const display = e2eShapeFromEnvelope(unifiedResult.envelope);
|
|
188
212
|
Object.assign(display, { datasourceTestRun: unifiedResult.envelope });
|
|
189
213
|
|