@aifabrix/builder 2.52.1 → 2.53.1
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/lib/app/deploy-config.js +3 -2
- package/lib/channels/add-channel.js +11 -41
- package/lib/channels/approval-guides/index.js +3 -0
- package/lib/channels/approval-guides/microsoft-copilot.js +26 -17
- package/lib/channels/channel-add-existing.js +72 -0
- package/lib/channels/channel-runtime-resolver.js +5 -1
- package/lib/channels/channel-status-display.js +30 -3
- package/lib/channels/microsoft-copilot-entra-sso-form.js +73 -2
- package/lib/channels/microsoft-copilot-entra-sso.js +26 -5
- package/lib/channels/microsoft-copilot-limits.js +22 -3
- package/lib/channels/microsoft-copilot-package.js +59 -14
- package/lib/channels/microsoft-copilot-validate.js +40 -10
- package/lib/cli/attach-default-help-examples.js +102 -0
- package/lib/cli/index.js +2 -0
- package/lib/cli/setup-credential-deployment.js +7 -5
- package/lib/cli/setup-utility-validate.js +1 -0
- package/lib/commands/credential-push.js +37 -5
- package/lib/commands/package-score.js +72 -0
- package/lib/commands/role-assistant.js +46 -8
- package/lib/deployment/deployer-poll-run.js +2 -2
- package/lib/deployment/deployer.js +1 -1
- package/lib/deployment/environment.js +2 -2
- package/lib/external-system/deploy.js +11 -21
- package/lib/integration-definition/apply.js +12 -14
- package/lib/integration-definition/enrich-openapi-operations.js +9 -10
- package/lib/lab/package-parity-score.js +417 -0
- package/lib/programmatic/capability-registry.js +1 -0
- package/lib/programmatic/help-content/channel-onboarding.md +35 -0
- package/lib/programmatic/help-content/cip-overview.md +27 -0
- package/lib/programmatic/help-content/connected-system-ui.md +70 -0
- package/lib/programmatic/help-content/dimensions-guide.md +23 -0
- package/lib/programmatic/help-content/enterprise-sync-fabrix.md +123 -0
- package/lib/programmatic/help-content/overview.md +25 -0
- package/lib/programmatic/help-content/subscription-guide.md +27 -0
- package/lib/programmatic/help-content/workflow.md +50 -0
- package/lib/programmatic/openapi-component-schemas.js +1 -0
- package/lib/programmatic/openapi-descriptions.js +6 -0
- package/lib/programmatic/openapi-envelope-examples.js +11 -0
- package/lib/programmatic/openapi-operation-bindings.js +5 -0
- package/lib/programmatic/openapi-request-schemas.js +39 -13
- package/lib/programmatic/openapi-result-schemas.js +30 -0
- package/lib/programmatic/openapi-schema-descriptions-core.js +18 -0
- package/lib/programmatic/openapi-schema-descriptions-operations.js +19 -0
- package/lib/programmatic/persist-supplied-openapi.js +180 -0
- package/lib/programmatic/route-handler-map.js +6 -0
- package/lib/programmatic/run-integration-definition.js +13 -5
- package/lib/role-assistant/evidence-sync.js +55 -7
- package/lib/role-assistant/test-runner-attachments.js +118 -0
- package/lib/role-assistant/test-runner-bid-apply.js +100 -21
- package/lib/role-assistant/test-runner-bid-document.js +1 -1
- package/lib/role-assistant/test-runner-start-payload.js +96 -0
- package/lib/role-assistant/test-runner-workhub-answers.js +11 -7
- package/lib/role-assistant/test-runner-workhub-helpers.js +100 -0
- package/lib/role-assistant/test-runner-workhub-soft.js +86 -0
- package/lib/role-assistant/test-runner-workhub.js +68 -105
- package/lib/role-assistant/test-runner.js +5 -63
- package/lib/role-assistant/test-suite.js +33 -40
- package/lib/schema/external-datasource.schema.json +13 -4
- package/lib/utils/compose-generator.js +10 -1
- package/lib/utils/compose-traefik-extra-routes.js +107 -0
- package/lib/utils/docker-build.js +2 -2
- package/lib/utils/help-builder.js +2 -1
- package/lib/utils/promote-dev-docker-image.js +15 -10
- package/package.json +14 -16
- package/templates/README.md +12 -1
- package/templates/applications/dataplane/application.yaml +8 -1
- package/templates/applications/dataplane/env.template +1 -1
- package/templates/applications/miso-controller/application.yaml +2 -1
- package/templates/applications/miso-controller/rbac.yaml +1 -0
- package/templates/channels/microsoft-copilot/ai-plugin.template.json.hbs +6 -2
- package/templates/channels/microsoft-copilot/entra-sso-registration.template.md.hbs +8 -5
- package/templates/channels/microsoft-copilot/fi-fi.template.json.hbs +7 -0
- package/templates/channels/microsoft-copilot/install.template.md.hbs +74 -22
- package/templates/channels/microsoft-copilot/manifest.template.json.hbs +29 -14
- package/templates/channels/microsoft-copilot/oauth-plugin-vault-required.template.md.hbs +6 -2
- package/templates/python/docker-compose.hbs +36 -0
- package/templates/typescript/docker-compose.hbs +36 -0
- package/.cursor/plans/Archive/157.1-security-quality-violations-by-type.csv +0 -374
- package/.cursor/process/README.md +0 -9
- package/.cursor/rules/anchor-docs.mdc +0 -15
- package/.cursor/rules/assistant-driven-integration-build.mdc +0 -40
- package/.cursor/rules/cli-layout.mdc +0 -81
- package/.cursor/rules/cli-product-neutral.mdc +0 -43
- package/.cursor/rules/deploy-rules/docker-image-ip-policy.mdc +0 -49
- package/.cursor/rules/docs-rules.mdc +0 -32
- package/.cursor/rules/plan-rules/application-lifecycle.mdc +0 -30
- package/.cursor/rules/plan-rules/plan-frontmatter.mdc +0 -35
- package/.cursor/rules/plan-rules/plan-lifecycle.mdc +0 -31
- package/.cursor/rules/plan-rules/plan-validation-gate.mdc +0 -34
- package/.cursor/rules/project-rules.mdc +0 -913
- package/.cursor/skills/README.md +0 -15
- package/.dockerignore +0 -52
- package/.eslintignore +0 -22
- package/.markdownlint.json +0 -14
- package/anchor-docs/README.md +0 -10
- package/anchor-docs/_TEMPLATE +0 -24
- package/babel.config.js +0 -6
- package/bin/builder-api-cleanup.js +0 -26
- package/bin/builder-api-nest.js +0 -10
- package/bin/builder-api-spike.js +0 -19
- package/enterprise-knowledge/README.md +0 -50
- package/enterprise-knowledge/enterprise-integration-lifecycle/README.md +0 -22
- package/enterprise-knowledge/enterprise-integration-lifecycle/application.json +0 -15
- package/enterprise-knowledge/enterprise-integration-lifecycle/deploy.js +0 -69
- package/enterprise-knowledge/enterprise-integration-lifecycle/enterprise-integration-lifecycle-deploy.json +0 -52
- package/enterprise-knowledge/enterprise-integration-lifecycle/enterprise-integration-lifecycle-system.json +0 -29
- package/enterprise-knowledge/enterprise-integration-lifecycle/env.template +0 -2
- package/integration/README.md +0 -81
- package/integration/github/README.md +0 -53
- package/integration/github/application.json +0 -21
- package/integration/github/deploy.js +0 -72
- package/integration/github/env.template +0 -11
- package/integration/github/github-datasource-issue-comments.json +0 -239
- package/integration/github/github-datasource-issues.json +0 -399
- package/integration/github/github-datasource-repository.json +0 -354
- package/integration/github/github-deploy.json +0 -1202
- package/integration/github/github-system.json +0 -82
- package/integration/hubspot-test/README.md +0 -159
- package/integration/hubspot-test/application.json +0 -54
- package/integration/hubspot-test/companies.json +0 -2048
- package/integration/hubspot-test/create-hubspot.js +0 -523
- package/integration/hubspot-test/env.template +0 -4
- package/integration/hubspot-test/hubspot-test-datasource-company.json +0 -138
- package/integration/hubspot-test/hubspot-test-datasource-contact.json +0 -146
- package/integration/hubspot-test/hubspot-test-datasource-deal.json +0 -146
- package/integration/hubspot-test/hubspot-test-datasource-users.json +0 -76
- package/integration/hubspot-test/hubspot-test-deploy.json +0 -2160
- package/integration/hubspot-test/hubspot-test-system.json +0 -74
- package/integration/hubspot-test/rbac.json +0 -166
- package/integration/hubspot-test/test-artifacts/wizard-hubspot-credential-real.yaml +0 -20
- package/integration/hubspot-test/test-artifacts/wizard-hubspot-env-vars.yaml +0 -9
- package/integration/hubspot-test/test-artifacts/wizard-invalid-add-datasource.yaml +0 -5
- package/integration/hubspot-test/test-artifacts/wizard-invalid-app-name.yaml +0 -5
- package/integration/hubspot-test/test-artifacts/wizard-invalid-credential-create.yaml +0 -7
- package/integration/hubspot-test/test-artifacts/wizard-invalid-credential-select.yaml +0 -7
- package/integration/hubspot-test/test-artifacts/wizard-invalid-known-platform.yaml +0 -4
- package/integration/hubspot-test/test-artifacts/wizard-invalid-missing-app.yaml +0 -4
- package/integration/hubspot-test/test-artifacts/wizard-invalid-missing-source.yaml +0 -2
- package/integration/hubspot-test/test-artifacts/wizard-invalid-mode.yaml +0 -5
- package/integration/hubspot-test/test-artifacts/wizard-invalid-openapi-file.yaml +0 -5
- package/integration/hubspot-test/test-artifacts/wizard-invalid-openapi-url.yaml +0 -4
- package/integration/hubspot-test/test-artifacts/wizard-invalid-source.yaml +0 -4
- package/integration/hubspot-test/test-artifacts/wizard-valid-for-dimension-array-test.yaml +0 -5
- package/integration/hubspot-test/test-artifacts/wizard-valid-for-dimension-key-test.yaml +0 -5
- package/integration/hubspot-test/test-artifacts/wizard-valid-for-dimension-path-test.yaml +0 -5
- package/integration/hubspot-test/test-artifacts/wizard-valid-for-dimension-test.yaml +0 -5
- package/integration/hubspot-test/test-artifacts/wizard-valid-for-rbac-test.yaml +0 -5
- package/integration/hubspot-test/test-artifacts/wizard-valid-for-rbac-yaml-test.yaml +0 -5
- package/integration/hubspot-test/test-dataplane-down-helpers.js +0 -249
- package/integration/hubspot-test/test-dataplane-down-tests.js +0 -386
- package/integration/hubspot-test/test-dataplane-down.js +0 -209
- package/integration/hubspot-test/test.js +0 -1588
- package/integration/hubspot-test/wizard-hubspot-e2e.yaml +0 -16
- package/integration/hubspot-test/wizard-hubspot-platform.yaml +0 -8
- package/integration/hubspot-test/wizard-hubspot-test-headless.yaml +0 -23
- package/integration/roundtrip-test-local/README.md +0 -143
- package/integration/roundtrip-test-local/application.yaml +0 -13
- package/integration/roundtrip-test-local/env.template +0 -15
- package/integration/roundtrip-test-local/roundtrip-test-local-datasource-roundtrip-test-company.yaml +0 -14
- package/integration/roundtrip-test-local/roundtrip-test-local-deploy.json +0 -61
- package/integration/roundtrip-test-local/roundtrip-test-local-system.yaml +0 -25
- package/integration/roundtrip-test-local2/README.md +0 -143
- package/integration/roundtrip-test-local2/application.yaml +0 -13
- package/integration/roundtrip-test-local2/env.template +0 -15
- package/integration/roundtrip-test-local2/roundtrip-test-local2-datasource-company.yaml +0 -31
- package/integration/roundtrip-test-local2/roundtrip-test-local2-deploy.json +0 -86
- package/integration/roundtrip-test-local2/roundtrip-test-local2-system.yaml +0 -25
- package/integration/test/wizard.yaml +0 -8
- package/jest.config.coverage.js +0 -37
- package/jest.config.default.js +0 -11
- package/jest.config.integration.fixtures.js +0 -22
- package/jest.config.integration.js +0 -33
- package/jest.config.isolated.js +0 -11
- package/jest.config.manual.js +0 -30
- package/jest.global-live-fabrix-hooks.js +0 -36
- package/jest.isolated-projects.js +0 -283
- package/jest.projects.js +0 -79
- package/lib/integration-definition/openapi-enrichment/hubspot-crm-companies.json +0 -205
- package/packages/builder-api/README.md +0 -120
- package/packages/builder-api/package.json +0 -30
- package/packages/builder-api/src/app.module.ts +0 -7
- package/packages/builder-api/src/auth/auth.module.ts +0 -5
- package/packages/builder-api/src/external-system/external-system.module.ts +0 -5
- package/packages/builder-api/src/main.ts +0 -16
- package/packages/builder-api/tsconfig.json +0 -15
- package/scripts/check-builder-api-architecture-grep.js +0 -79
- package/scripts/check-datasource-test-run-schema-sync.js +0 -34
- package/scripts/ci-fix.sh +0 -19
- package/scripts/ci-simulate.sh +0 -19
- package/scripts/diagnose-cli.js +0 -150
- package/scripts/docker_image_content_gate.js +0 -47
- package/scripts/docker_image_content_gate.sh +0 -5
- package/scripts/export-builder-api-evidence.js +0 -262
- package/scripts/install-local.js +0 -490
- package/scripts/lib/builder-api-docker-staging-copy.js +0 -60
- package/scripts/lib/builder-api-docker-staging-core.js +0 -183
- package/scripts/lib/docker-image-content-gate-core.js +0 -166
- package/scripts/lib/plan-actor.js +0 -109
- package/scripts/lib/plan-config.js +0 -20
- package/scripts/lib/plan-gates.js +0 -154
- package/scripts/lib/plan-meta.js +0 -104
- package/scripts/lib/plan-parse.js +0 -162
- package/scripts/lib/plan-prevalidate-gates.js +0 -337
- package/scripts/lib/plan-run-core.js +0 -125
- package/scripts/lib/plan-run-report.js +0 -119
- package/scripts/plan-approve.js +0 -24
- package/scripts/plan-archive.js +0 -49
- package/scripts/plan-run.js +0 -13
- package/scripts/plan-set-actor.js +0 -21
- package/scripts/plan-validate.js +0 -80
- package/scripts/pnpm/af.mjs +0 -125
- package/scripts/pnpm/check-quiet.mjs +0 -32
- package/scripts/pnpm/dev-install.mjs +0 -29
- package/scripts/pnpm/dev-reload.mjs +0 -18
- package/scripts/pnpm/help.mjs +0 -55
- package/scripts/pnpm/lib/dev-reload-core.mjs +0 -189
- package/scripts/pnpm/lib/dev-reload-log-capture.mjs +0 -67
- package/scripts/pnpm/lib/dev-reload-log-utils.mjs +0 -32
- package/scripts/pnpm/lib/resolve-aifabrix-bin.mjs +0 -124
- package/scripts/pnpm/lib/resolve-app-url.mjs +0 -183
- package/scripts/pnpm/lib/spinner.mjs +0 -42
- package/scripts/pnpm-global-remove.js +0 -48
- package/scripts/stage_builder_api_for_docker.js +0 -65
- package/scripts/sync-builder-api-template.js +0 -44
- package/scripts/test-dataplane-bootstrap.js +0 -331
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Persist validated OpenAPI documents into workspace openapi/ (517.2).
|
|
3
|
+
* @fileoverview Runtime-authorized documents only — Builder never browses task store
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
'use strict';
|
|
7
|
+
|
|
8
|
+
const crypto = require('crypto');
|
|
9
|
+
const path = require('path');
|
|
10
|
+
const fs = require('../internal/fs-real-sync');
|
|
11
|
+
const { assertValidSystemKey } = require('./system-key');
|
|
12
|
+
const { ensureIntegrationDirectory } = require('./workspace-context');
|
|
13
|
+
|
|
14
|
+
function sha256Hex(buffer) {
|
|
15
|
+
return crypto.createHash('sha256').update(buffer).digest('hex');
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function safeOpenApiBasename(filename) {
|
|
19
|
+
const raw = String(filename || '').trim();
|
|
20
|
+
const base = path.basename(raw);
|
|
21
|
+
if (!base || base === '.' || base === '..') {
|
|
22
|
+
throw new Error('openapi.filename.invalid');
|
|
23
|
+
}
|
|
24
|
+
if (raw.includes('..') || raw.includes('/') || raw.includes('\\') || raw.includes('\0')) {
|
|
25
|
+
throw new Error('openapi.filename.pathTraversal');
|
|
26
|
+
}
|
|
27
|
+
return base.endsWith('.json') || base.endsWith('.yaml') || base.endsWith('.yml')
|
|
28
|
+
? base
|
|
29
|
+
: `${base}.json`;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function parseOpenApiDocument(body) {
|
|
33
|
+
if (body && typeof body === 'object' && !Array.isArray(body)) {
|
|
34
|
+
return body;
|
|
35
|
+
}
|
|
36
|
+
if (typeof body === 'string') {
|
|
37
|
+
const trimmed = body.trim();
|
|
38
|
+
if (!trimmed) {
|
|
39
|
+
throw new Error('openapi.empty');
|
|
40
|
+
}
|
|
41
|
+
if (trimmed.startsWith('{') || trimmed.startsWith('[')) {
|
|
42
|
+
return JSON.parse(trimmed);
|
|
43
|
+
}
|
|
44
|
+
throw new Error('openapi.yamlUnsupportedInSliceB');
|
|
45
|
+
}
|
|
46
|
+
throw new Error('openapi.body.required');
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function validateOpenApiDocument(doc) {
|
|
50
|
+
if (!doc || typeof doc !== 'object' || Array.isArray(doc)) {
|
|
51
|
+
return { ok: false, reason: 'openapi.invalid', detail: 'Document must be an object' };
|
|
52
|
+
}
|
|
53
|
+
const version = typeof doc.openapi === 'string' ? doc.openapi.trim() : '';
|
|
54
|
+
if (!/^3\./.test(version)) {
|
|
55
|
+
return {
|
|
56
|
+
ok: false,
|
|
57
|
+
reason: 'openapi.invalid',
|
|
58
|
+
detail: 'Top-level openapi must be a 3.x string'
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
if (!doc.paths || typeof doc.paths !== 'object' || Array.isArray(doc.paths)) {
|
|
62
|
+
return {
|
|
63
|
+
ok: false,
|
|
64
|
+
reason: 'openapi.invalid',
|
|
65
|
+
detail: 'paths object is required'
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
if (Object.keys(doc.paths).length === 0) {
|
|
69
|
+
return {
|
|
70
|
+
ok: false,
|
|
71
|
+
reason: 'openapi.invalid',
|
|
72
|
+
detail: 'paths must not be empty for persist'
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
return { ok: true };
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function documentBytes(doc) {
|
|
79
|
+
return Buffer.from(`${JSON.stringify(doc, null, 2)}\n`, 'utf8');
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function openapiDirForContext(ctx) {
|
|
83
|
+
ensureIntegrationDirectory(ctx);
|
|
84
|
+
return path.join(ctx.getIntegrationRoot(), ctx.systemKey, 'openapi');
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function relativeWorkspacePath(ctx, absolutePath) {
|
|
88
|
+
const root = ctx.getIntegrationRoot();
|
|
89
|
+
const rel = path.relative(root, absolutePath);
|
|
90
|
+
// root is .../integration; rel already includes <systemKey>/openapi/<file>
|
|
91
|
+
return path.join('integration', rel).split(path.sep).join('/');
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function persistOneDocument(ctx, row) {
|
|
95
|
+
let filename = 'unknown.json';
|
|
96
|
+
try {
|
|
97
|
+
filename = safeOpenApiBasename(row && (row.filename || row.name));
|
|
98
|
+
} catch (error) {
|
|
99
|
+
return {
|
|
100
|
+
rejected: {
|
|
101
|
+
filename: String((row && (row.filename || row.name)) || 'unknown'),
|
|
102
|
+
reason: 'openapi.filename.invalid',
|
|
103
|
+
detail: error.message || String(error)
|
|
104
|
+
}
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
let doc;
|
|
108
|
+
try {
|
|
109
|
+
doc = parseOpenApiDocument(row && row.body);
|
|
110
|
+
} catch (error) {
|
|
111
|
+
return {
|
|
112
|
+
rejected: {
|
|
113
|
+
filename,
|
|
114
|
+
reason: 'openapi.invalid',
|
|
115
|
+
detail: error.message || String(error)
|
|
116
|
+
}
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
const check = validateOpenApiDocument(doc);
|
|
120
|
+
if (!check.ok) {
|
|
121
|
+
return { rejected: { filename, reason: check.reason, detail: check.detail } };
|
|
122
|
+
}
|
|
123
|
+
const bytes = documentBytes(doc);
|
|
124
|
+
const digest = `sha256:${sha256Hex(bytes)}`;
|
|
125
|
+
if (row.digest && String(row.digest).trim() && String(row.digest).trim() !== digest) {
|
|
126
|
+
return {
|
|
127
|
+
rejected: {
|
|
128
|
+
filename,
|
|
129
|
+
reason: 'openapi.digestMismatch',
|
|
130
|
+
detail: 'Provided digest does not match serialized body'
|
|
131
|
+
}
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
const dir = openapiDirForContext(ctx);
|
|
135
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
136
|
+
const absolutePath = path.join(dir, filename);
|
|
137
|
+
fs.writeFileSync(absolutePath, bytes);
|
|
138
|
+
return {
|
|
139
|
+
accepted: {
|
|
140
|
+
filename,
|
|
141
|
+
path: relativeWorkspacePath(ctx, absolutePath),
|
|
142
|
+
digest
|
|
143
|
+
}
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* @param {import('./workspace-context').WorkspaceContext} ctx
|
|
149
|
+
* @param {Object} body
|
|
150
|
+
* @returns {{ accepted: Object[], rejected: Object[], workspaceRoot: string }}
|
|
151
|
+
*/
|
|
152
|
+
function persistSuppliedOpenApi(ctx, body) {
|
|
153
|
+
assertValidSystemKey(ctx.systemKey);
|
|
154
|
+
const documents = body && Array.isArray(body.documents) ? body.documents : null;
|
|
155
|
+
if (!documents || documents.length === 0) {
|
|
156
|
+
throw new Error('documents array is required');
|
|
157
|
+
}
|
|
158
|
+
const accepted = [];
|
|
159
|
+
const rejected = [];
|
|
160
|
+
for (const row of documents) {
|
|
161
|
+
const outcome = persistOneDocument(ctx, row);
|
|
162
|
+
if (outcome.accepted) {
|
|
163
|
+
accepted.push(outcome.accepted);
|
|
164
|
+
} else if (outcome.rejected) {
|
|
165
|
+
rejected.push(outcome.rejected);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
return {
|
|
169
|
+
accepted,
|
|
170
|
+
rejected,
|
|
171
|
+
workspaceRoot: ctx.getIntegrationRoot()
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
module.exports = {
|
|
176
|
+
persistSuppliedOpenApi,
|
|
177
|
+
validateOpenApiDocument,
|
|
178
|
+
safeOpenApiBasename,
|
|
179
|
+
parseOpenApiDocument
|
|
180
|
+
};
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
|
|
11
11
|
const { buildContext } = require('./route-handlers-context');
|
|
12
12
|
const { listSystems, createSystem, getSystemManifest, deleteSystem } = require('./workspace-ops');
|
|
13
|
+
const { persistSuppliedOpenApi } = require('./persist-supplied-openapi');
|
|
13
14
|
const { runWizard } = require('./run-wizard');
|
|
14
15
|
const {
|
|
15
16
|
applyIntegrationDefinitionInWorkspace
|
|
@@ -63,6 +64,11 @@ const HANDLER_MAP = {
|
|
|
63
64
|
}
|
|
64
65
|
return createSystem(buildContext(auth, key), key);
|
|
65
66
|
},
|
|
67
|
+
persistSuppliedOpenApi: (params, auth, body) =>
|
|
68
|
+
persistSuppliedOpenApi(
|
|
69
|
+
buildContext(auth, params.systemKey),
|
|
70
|
+
body && typeof body === 'object' ? body : {}
|
|
71
|
+
),
|
|
66
72
|
getSystemManifest: (params, auth) => getSystemManifest(buildContext(auth, params.systemKey)),
|
|
67
73
|
deleteSystem: (params, auth) => deleteSystem(buildContext(auth, params.systemKey)),
|
|
68
74
|
runWizard: (params, auth, body, query) =>
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
'use strict';
|
|
10
10
|
|
|
11
11
|
const path = require('path');
|
|
12
|
+
const fs = require('../internal/fs-real-sync');
|
|
12
13
|
const {
|
|
13
14
|
applyIntegrationDefinition,
|
|
14
15
|
withDefinitionHash
|
|
@@ -20,10 +21,17 @@ const {
|
|
|
20
21
|
} = require('./workspace-context');
|
|
21
22
|
const { touchWorkspaceMeta } = require('./workspace-meta');
|
|
22
23
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
)
|
|
24
|
+
/**
|
|
25
|
+
* Workspace openapi/ is the only product resolve root for BID openapiSourceRef
|
|
26
|
+
* (517.2 persist layout). Do not bind apply to ship-local enrichment fixtures.
|
|
27
|
+
* @param {import('./workspace-context').WorkspaceContext} ctx
|
|
28
|
+
* @returns {string}
|
|
29
|
+
*/
|
|
30
|
+
function workspaceOpenApiBaseDir(ctx) {
|
|
31
|
+
const dir = path.join(ctx.getIntegrationPath(), 'openapi');
|
|
32
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
33
|
+
return dir;
|
|
34
|
+
}
|
|
27
35
|
|
|
28
36
|
/**
|
|
29
37
|
* @param {import('./workspace-context').WorkspaceContext} ctx
|
|
@@ -54,7 +62,7 @@ async function applyIntegrationDefinitionInWorkspace(ctx, body = {}) {
|
|
|
54
62
|
outputDir: ctx.getIntegrationPath(),
|
|
55
63
|
force,
|
|
56
64
|
dryRun,
|
|
57
|
-
openapiBaseDir:
|
|
65
|
+
openapiBaseDir: workspaceOpenApiBaseDir(ctx)
|
|
58
66
|
});
|
|
59
67
|
touchWorkspaceMeta(ctx, {
|
|
60
68
|
lastOperation: 'integration-definition-apply',
|
|
@@ -27,6 +27,10 @@ const EVIDENCE_DIR_NAME = 'evidence';
|
|
|
27
27
|
const EVIDENCE_EXPORT_FILE = 'evidence.json';
|
|
28
28
|
const VALIDATION_FAILED = 'failed';
|
|
29
29
|
const VALIDATION_PASSED = 'passed';
|
|
30
|
+
/** Builder-keyed Category B catalogue — never import as Evidence (517.3 / 517.4.1). */
|
|
31
|
+
const SKIP_EVIDENCE_BASENAMES = new Set(['capability-business-catalog.json']);
|
|
32
|
+
/** PoC draft folders under evidence/ — never import as governed Evidence. */
|
|
33
|
+
const SKIP_EVIDENCE_DIR_NAMES = new Set(['candidates', 'candidate']);
|
|
30
34
|
|
|
31
35
|
const EVIDENCE_MUTABLE_KEYS = [
|
|
32
36
|
'key',
|
|
@@ -79,6 +83,23 @@ function isEvidenceFileName(fileName) {
|
|
|
79
83
|
return lower.endsWith('.json') || lower.endsWith('.yaml') || lower.endsWith('.yml');
|
|
80
84
|
}
|
|
81
85
|
|
|
86
|
+
/**
|
|
87
|
+
* Skip retired / forbidden evidence payloads (517.4.1 scrub).
|
|
88
|
+
* @param {string} filePath
|
|
89
|
+
* @param {unknown} parsed
|
|
90
|
+
* @returns {boolean}
|
|
91
|
+
*/
|
|
92
|
+
function shouldSkipEvidencePayload(filePath, parsed) {
|
|
93
|
+
const base = path.basename(String(filePath || '')).toLowerCase();
|
|
94
|
+
if (SKIP_EVIDENCE_BASENAMES.has(base)) {
|
|
95
|
+
return true;
|
|
96
|
+
}
|
|
97
|
+
if (parsed && typeof parsed === 'object' && !Array.isArray(parsed) && parsed._retired === true) {
|
|
98
|
+
return true;
|
|
99
|
+
}
|
|
100
|
+
return false;
|
|
101
|
+
}
|
|
102
|
+
|
|
82
103
|
function collectEvidenceFiles(dirPath) {
|
|
83
104
|
if (!fs.existsSync(dirPath)) {
|
|
84
105
|
return [];
|
|
@@ -88,6 +109,10 @@ function collectEvidenceFiles(dirPath) {
|
|
|
88
109
|
for (const entry of entries) {
|
|
89
110
|
const fullPath = path.join(dirPath, entry.name);
|
|
90
111
|
if (entry.isDirectory()) {
|
|
112
|
+
const dirName = String(entry.name || '').trim().toLowerCase();
|
|
113
|
+
if (SKIP_EVIDENCE_DIR_NAMES.has(dirName)) {
|
|
114
|
+
continue;
|
|
115
|
+
}
|
|
91
116
|
files.push(...collectEvidenceFiles(fullPath));
|
|
92
117
|
continue;
|
|
93
118
|
}
|
|
@@ -152,8 +177,13 @@ function sanitizeEvidenceRow(row, systemKey, options = {}) {
|
|
|
152
177
|
function loadEvidenceRowsFromFolder(evidencePath, systemKey) {
|
|
153
178
|
const files = collectEvidenceFiles(evidencePath);
|
|
154
179
|
const rows = [];
|
|
180
|
+
const skippedFiles = [];
|
|
155
181
|
for (const filePath of files) {
|
|
156
182
|
const parsed = parseEvidenceFile(filePath);
|
|
183
|
+
if (shouldSkipEvidencePayload(filePath, parsed)) {
|
|
184
|
+
skippedFiles.push(path.relative(evidencePath, filePath));
|
|
185
|
+
continue;
|
|
186
|
+
}
|
|
157
187
|
const extractedRows = extractEvidenceRows(parsed);
|
|
158
188
|
for (const row of extractedRows) {
|
|
159
189
|
if (!row || typeof row !== 'object' || Array.isArray(row)) {
|
|
@@ -169,7 +199,7 @@ function loadEvidenceRowsFromFolder(evidencePath, systemKey) {
|
|
|
169
199
|
rows.push(normalized);
|
|
170
200
|
}
|
|
171
201
|
}
|
|
172
|
-
return { files, rows };
|
|
202
|
+
return { files, rows, skippedFiles };
|
|
173
203
|
}
|
|
174
204
|
|
|
175
205
|
function resolveEvidenceDirectory(systemKey, pathOptions = {}) {
|
|
@@ -277,21 +307,25 @@ function buildFailureSummary(failures) {
|
|
|
277
307
|
return `${preview}${more}`;
|
|
278
308
|
}
|
|
279
309
|
|
|
280
|
-
function buildEvidenceDryRunSummary(evidencePath, files, rows) {
|
|
310
|
+
function buildEvidenceDryRunSummary(evidencePath, files, rows, skippedFiles = []) {
|
|
281
311
|
return {
|
|
282
312
|
skipped: false,
|
|
283
313
|
dryRun: true,
|
|
284
314
|
evidencePath,
|
|
285
315
|
filesDiscovered: files.length,
|
|
316
|
+
filesSkipped: skippedFiles.length,
|
|
317
|
+
skippedFiles: [...skippedFiles],
|
|
286
318
|
discovered: rows.length
|
|
287
319
|
};
|
|
288
320
|
}
|
|
289
321
|
|
|
290
|
-
function buildEvidenceImportSummary(evidencePath, files, rows, loadResult) {
|
|
322
|
+
function buildEvidenceImportSummary(evidencePath, files, rows, loadResult, skippedFiles = []) {
|
|
291
323
|
return {
|
|
292
324
|
skipped: false,
|
|
293
325
|
evidencePath,
|
|
294
326
|
filesDiscovered: files.length,
|
|
327
|
+
filesSkipped: skippedFiles.length,
|
|
328
|
+
skippedFiles: [...skippedFiles],
|
|
295
329
|
discovered: rows.length,
|
|
296
330
|
loaded: Number(loadResult.inserted || 0) + Number(loadResult.updated || 0),
|
|
297
331
|
validated: 0,
|
|
@@ -347,12 +381,18 @@ async function importRoleAssistantEvidence(params) {
|
|
|
347
381
|
return { skipped: true, reason: 'not-role-assistant' };
|
|
348
382
|
}
|
|
349
383
|
const evidencePath = resolveEvidenceDirectory(systemKey, pathOptions);
|
|
350
|
-
const { files, rows } = loadEvidenceRowsFromFolder(evidencePath, systemKey);
|
|
384
|
+
const { files, rows, skippedFiles } = loadEvidenceRowsFromFolder(evidencePath, systemKey);
|
|
351
385
|
if (rows.length === 0) {
|
|
352
|
-
return {
|
|
386
|
+
return {
|
|
387
|
+
skipped: true,
|
|
388
|
+
reason: 'no-evidence-files',
|
|
389
|
+
evidencePath,
|
|
390
|
+
filesSkipped: (skippedFiles || []).length,
|
|
391
|
+
skippedFiles: [...(skippedFiles || [])]
|
|
392
|
+
};
|
|
353
393
|
}
|
|
354
394
|
if (dryRun) {
|
|
355
|
-
return buildEvidenceDryRunSummary(evidencePath, files, rows);
|
|
395
|
+
return buildEvidenceDryRunSummary(evidencePath, files, rows, skippedFiles);
|
|
356
396
|
}
|
|
357
397
|
const { loadResult, refs } = await loadEvidenceAndResolveRefs(
|
|
358
398
|
dataplaneUrl,
|
|
@@ -360,7 +400,13 @@ async function importRoleAssistantEvidence(params) {
|
|
|
360
400
|
systemKey,
|
|
361
401
|
rows
|
|
362
402
|
);
|
|
363
|
-
const summary = buildEvidenceImportSummary(
|
|
403
|
+
const summary = buildEvidenceImportSummary(
|
|
404
|
+
evidencePath,
|
|
405
|
+
files,
|
|
406
|
+
rows,
|
|
407
|
+
loadResult,
|
|
408
|
+
skippedFiles
|
|
409
|
+
);
|
|
364
410
|
const failures = await runEvidenceLifecycleBatch(dataplaneUrl, authConfig, refs, summary);
|
|
365
411
|
appendUnresolvedEvidenceFailures(summary, failures, refs.length, rows.length);
|
|
366
412
|
throwWhenEvidenceLifecycleFailed(summary, failures);
|
|
@@ -443,6 +489,8 @@ function logRoleAssistantEvidenceExportSummary(systemKey, result, options = {})
|
|
|
443
489
|
|
|
444
490
|
module.exports = {
|
|
445
491
|
isDigitalWorkerModel,
|
|
492
|
+
shouldSkipEvidencePayload,
|
|
493
|
+
loadEvidenceRowsFromFolder,
|
|
446
494
|
importRoleAssistantEvidence,
|
|
447
495
|
exportRoleAssistantEvidence,
|
|
448
496
|
logRoleAssistantEvidenceImportSummary,
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Build work.start attachments from case YAML (paths / openapiDir).
|
|
3
|
+
* @fileoverview 517.1 — map suite OpenAPI files to ATT attachment shape
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
'use strict';
|
|
7
|
+
|
|
8
|
+
const path = require('path');
|
|
9
|
+
const fs = require('../internal/fs-real-sync');
|
|
10
|
+
|
|
11
|
+
function resolveCaseDirectory(testCase) {
|
|
12
|
+
const sourceFile =
|
|
13
|
+
testCase && typeof testCase.sourceFile === 'string' ? testCase.sourceFile.trim() : '';
|
|
14
|
+
if (!sourceFile) {
|
|
15
|
+
return process.cwd();
|
|
16
|
+
}
|
|
17
|
+
return path.dirname(path.resolve(sourceFile));
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function readJsonAttachmentBody(absolutePath) {
|
|
21
|
+
if (!fs.existsSync(absolutePath)) {
|
|
22
|
+
throw new Error(`Role assistant attachment file not found: ${absolutePath}`);
|
|
23
|
+
}
|
|
24
|
+
const text = fs.readFileSync(absolutePath, 'utf8');
|
|
25
|
+
try {
|
|
26
|
+
return JSON.parse(text);
|
|
27
|
+
} catch (error) {
|
|
28
|
+
throw new Error(`Role assistant attachment is not valid JSON: ${absolutePath}`);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function attachmentFromFile(absolutePath, meta = {}) {
|
|
33
|
+
const basename = path.basename(absolutePath);
|
|
34
|
+
const role = typeof meta.role === 'string' && meta.role.trim() ? meta.role.trim() : 'openapi';
|
|
35
|
+
return {
|
|
36
|
+
name: basename,
|
|
37
|
+
filename: basename,
|
|
38
|
+
mediaType: 'application/json',
|
|
39
|
+
role,
|
|
40
|
+
contributionPurpose: 'task',
|
|
41
|
+
body: readJsonAttachmentBody(absolutePath)
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function resolveRelativeAttachmentPath(caseDir, relativePath) {
|
|
46
|
+
if (typeof relativePath !== 'string' || !relativePath.trim()) {
|
|
47
|
+
throw new Error('Role assistant attachment path must be a non-empty string');
|
|
48
|
+
}
|
|
49
|
+
const absolutePath = path.resolve(caseDir, relativePath.trim());
|
|
50
|
+
return absolutePath;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function collectExplicitAttachments(caseDir, attachments) {
|
|
54
|
+
if (!Array.isArray(attachments) || attachments.length === 0) {
|
|
55
|
+
return [];
|
|
56
|
+
}
|
|
57
|
+
return attachments.map((row) => {
|
|
58
|
+
if (!row || typeof row !== 'object' || Array.isArray(row)) {
|
|
59
|
+
throw new Error('Role assistant input.attachments entries must be objects');
|
|
60
|
+
}
|
|
61
|
+
const rel = row.path || row.file || row.filename;
|
|
62
|
+
const absolutePath = resolveRelativeAttachmentPath(caseDir, rel);
|
|
63
|
+
return attachmentFromFile(absolutePath, row);
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function listOpenapiJsonFiles(absoluteDir, allowList) {
|
|
68
|
+
if (!fs.existsSync(absoluteDir)) {
|
|
69
|
+
throw new Error(`Role assistant openapiDir not found: ${absoluteDir}`);
|
|
70
|
+
}
|
|
71
|
+
const names = fs
|
|
72
|
+
.readdirSync(absoluteDir)
|
|
73
|
+
.filter((entry) => /\.json$/i.test(entry))
|
|
74
|
+
.sort();
|
|
75
|
+
if (Array.isArray(allowList) && allowList.length > 0) {
|
|
76
|
+
const wanted = new Set(allowList.map((row) => String(row).trim()).filter(Boolean));
|
|
77
|
+
return names.filter((name) => wanted.has(name));
|
|
78
|
+
}
|
|
79
|
+
return names;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function collectOpenapiDirAttachments(caseDir, input) {
|
|
83
|
+
if (typeof input.openapiDir !== 'string' || !input.openapiDir.trim()) {
|
|
84
|
+
return [];
|
|
85
|
+
}
|
|
86
|
+
const absoluteDir = path.resolve(caseDir, input.openapiDir.trim());
|
|
87
|
+
const files = listOpenapiJsonFiles(absoluteDir, input.openapiFiles);
|
|
88
|
+
if (files.length === 0) {
|
|
89
|
+
throw new Error(`Role assistant openapiDir has no matching *.json files: ${absoluteDir}`);
|
|
90
|
+
}
|
|
91
|
+
return files.map((name) => attachmentFromFile(path.join(absoluteDir, name), { role: 'openapi' }));
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Build ATT-shaped attachments for work.start from case input.
|
|
96
|
+
* @param {Object} testCase - Normalized case (may include sourceFile)
|
|
97
|
+
* @returns {Object[]|undefined}
|
|
98
|
+
*/
|
|
99
|
+
function buildStartAttachments(testCase) {
|
|
100
|
+
const input =
|
|
101
|
+
testCase && typeof testCase.input === 'object' && testCase.input && !Array.isArray(testCase.input)
|
|
102
|
+
? testCase.input
|
|
103
|
+
: {};
|
|
104
|
+
const caseDir = resolveCaseDirectory(testCase);
|
|
105
|
+
const fromExplicit = collectExplicitAttachments(caseDir, input.attachments);
|
|
106
|
+
const fromDir = collectOpenapiDirAttachments(caseDir, input);
|
|
107
|
+
const merged = [...fromExplicit, ...fromDir];
|
|
108
|
+
if (merged.length === 0) {
|
|
109
|
+
return undefined;
|
|
110
|
+
}
|
|
111
|
+
return merged;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
module.exports = {
|
|
115
|
+
buildStartAttachments,
|
|
116
|
+
resolveCaseDirectory,
|
|
117
|
+
attachmentFromFile
|
|
118
|
+
};
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Soft-loop BID apply after workspace.createsystem (suite 002 / plan 512.4).
|
|
3
|
-
* @fileoverview
|
|
4
|
-
*
|
|
3
|
+
* @fileoverview LAB-ONLY debt helper: POST Builder-api HTTP apply when the RA
|
|
4
|
+
* soft loop needs a package materialize. Product path is Decision Engine →
|
|
5
|
+
* capability.execute(template.applyintegrationdefinition) → COM/CIP.
|
|
6
|
+
* Do not treat this module as the product apply path once Runtime COM invoke
|
|
7
|
+
* works for the suite (plan 517.5). Non-product — lab helper only.
|
|
5
8
|
*/
|
|
6
9
|
|
|
7
10
|
'use strict';
|
|
@@ -12,6 +15,7 @@ const {
|
|
|
12
15
|
loadCaseBidDocument,
|
|
13
16
|
resolveTargetSystemKey
|
|
14
17
|
} = require('./test-runner-bid-document');
|
|
18
|
+
const { buildStartAttachments } = require('./test-runner-attachments');
|
|
15
19
|
|
|
16
20
|
/**
|
|
17
21
|
* @returns {string}
|
|
@@ -59,6 +63,7 @@ function extractBearer(authConfig) {
|
|
|
59
63
|
}
|
|
60
64
|
|
|
61
65
|
/**
|
|
66
|
+
* True when wait is for template.applyintegrationdefinition and a case BID exists.
|
|
62
67
|
* @param {Object} testCase
|
|
63
68
|
* @param {Object} wait
|
|
64
69
|
* @returns {boolean}
|
|
@@ -71,10 +76,95 @@ function shouldApplyBidAfterWait(testCase, wait) {
|
|
|
71
76
|
const capability = String(
|
|
72
77
|
wait.questionKey || wait.objectRef?.capabilityKey || wait.prompt || ''
|
|
73
78
|
).toLowerCase();
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
79
|
+
// Product path: DE → capability.execute(template.applyintegrationdefinition).
|
|
80
|
+
return capability.includes('applyintegrationdefinition');
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Lab-only soft HTTP BID apply. Hard cases (expected.soft !== true) must rely on
|
|
85
|
+
* Runtime COM/CIP apply after respond+continue — not Builder-api HTTP (517.5 / 517.4.7).
|
|
86
|
+
* @param {Object} testCase
|
|
87
|
+
* @param {Object} wait
|
|
88
|
+
* @returns {boolean}
|
|
89
|
+
*/
|
|
90
|
+
function shouldSoftHttpBidApply(testCase, wait) {
|
|
91
|
+
if (!shouldApplyBidAfterWait(testCase, wait)) {
|
|
92
|
+
return false;
|
|
93
|
+
}
|
|
94
|
+
return testCase?.expected?.soft === true;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* @param {Object} testCase
|
|
99
|
+
* @returns {Object[]|null}
|
|
100
|
+
*/
|
|
101
|
+
function collectOpenApiPersistDocuments(testCase) {
|
|
102
|
+
let attachments = [];
|
|
103
|
+
try {
|
|
104
|
+
attachments = buildStartAttachments(testCase) || [];
|
|
105
|
+
} catch (_err) {
|
|
106
|
+
return null;
|
|
107
|
+
}
|
|
108
|
+
const documents = attachments
|
|
109
|
+
.filter((row) => row && (row.role === 'openapi' || !row.role) && row.body)
|
|
110
|
+
.map((row) => ({
|
|
111
|
+
filename: row.filename || row.name,
|
|
112
|
+
body: row.body
|
|
113
|
+
}))
|
|
114
|
+
.filter((row) => row.filename);
|
|
115
|
+
return documents.length > 0 ? documents : null;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* @param {Response} res
|
|
120
|
+
* @param {string} label
|
|
121
|
+
* @returns {Promise<Object|null>}
|
|
122
|
+
*/
|
|
123
|
+
async function readBuilderApiJsonOrThrow(res, label) {
|
|
124
|
+
const text = await res.text();
|
|
125
|
+
let body = null;
|
|
126
|
+
try {
|
|
127
|
+
body = text ? JSON.parse(text) : null;
|
|
128
|
+
} catch (_err) {
|
|
129
|
+
body = { raw: text };
|
|
130
|
+
}
|
|
131
|
+
if (!res.ok) {
|
|
132
|
+
const err = new Error(
|
|
133
|
+
`${label} HTTP ${res.status}: ${typeof body === 'object' ? JSON.stringify(body).slice(0, 300) : text}`
|
|
134
|
+
);
|
|
135
|
+
err.status = res.status;
|
|
136
|
+
throw err;
|
|
137
|
+
}
|
|
138
|
+
return body && body.data ? body.data : body;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Lab debt: stage suite OpenAPI into workspace via 517.2 persist API before apply.
|
|
143
|
+
* Product path is DE → capability.execute(persistsuppliedopenapi).
|
|
144
|
+
* @param {Object} authConfig
|
|
145
|
+
* @param {Object} testCase
|
|
146
|
+
* @param {string} systemKey
|
|
147
|
+
* @returns {Promise<Object|null>}
|
|
148
|
+
*/
|
|
149
|
+
async function persistCaseOpenApiViaBuilderApi(authConfig, testCase, systemKey) {
|
|
150
|
+
const token = extractBearer(authConfig);
|
|
151
|
+
if (!token || !systemKey) {
|
|
152
|
+
return null;
|
|
153
|
+
}
|
|
154
|
+
const documents = collectOpenApiPersistDocuments(testCase);
|
|
155
|
+
if (!documents) {
|
|
156
|
+
return null;
|
|
157
|
+
}
|
|
158
|
+
const url = `${resolveBuilderApiBaseUrl()}/api/v1/builder/systems/${encodeURIComponent(systemKey)}/openapi/persist-supplied`;
|
|
159
|
+
const res = await fetch(url, {
|
|
160
|
+
method: 'POST',
|
|
161
|
+
headers: {
|
|
162
|
+
Authorization: `Bearer ${token}`,
|
|
163
|
+
'Content-Type': 'application/json'
|
|
164
|
+
},
|
|
165
|
+
body: JSON.stringify({ documents })
|
|
166
|
+
});
|
|
167
|
+
return readBuilderApiJsonOrThrow(res, 'OpenAPI persist failed');
|
|
78
168
|
}
|
|
79
169
|
|
|
80
170
|
/**
|
|
@@ -89,6 +179,7 @@ async function applyCaseBidViaBuilderApi(authConfig, testCase) {
|
|
|
89
179
|
if (!bid || !systemKey || !token) {
|
|
90
180
|
return null;
|
|
91
181
|
}
|
|
182
|
+
await persistCaseOpenApiViaBuilderApi(authConfig, testCase, systemKey);
|
|
92
183
|
const url = `${resolveBuilderApiBaseUrl()}/api/v1/builder/systems/${encodeURIComponent(systemKey)}/integration-definition/apply`;
|
|
93
184
|
const res = await fetch(url, {
|
|
94
185
|
method: 'POST',
|
|
@@ -98,25 +189,13 @@ async function applyCaseBidViaBuilderApi(authConfig, testCase) {
|
|
|
98
189
|
},
|
|
99
190
|
body: JSON.stringify({ integrationDefinition: bid, force: true })
|
|
100
191
|
});
|
|
101
|
-
|
|
102
|
-
let body = null;
|
|
103
|
-
try {
|
|
104
|
-
body = text ? JSON.parse(text) : null;
|
|
105
|
-
} catch (_err) {
|
|
106
|
-
body = { raw: text };
|
|
107
|
-
}
|
|
108
|
-
if (!res.ok) {
|
|
109
|
-
const err = new Error(
|
|
110
|
-
`BID apply failed HTTP ${res.status}: ${typeof body === 'object' ? JSON.stringify(body).slice(0, 300) : text}`
|
|
111
|
-
);
|
|
112
|
-
err.status = res.status;
|
|
113
|
-
throw err;
|
|
114
|
-
}
|
|
115
|
-
return body && body.data ? body.data : body;
|
|
192
|
+
return readBuilderApiJsonOrThrow(res, 'BID apply failed');
|
|
116
193
|
}
|
|
117
194
|
|
|
118
195
|
module.exports = {
|
|
119
196
|
resolveBuilderApiBaseUrl,
|
|
120
197
|
shouldApplyBidAfterWait,
|
|
198
|
+
shouldSoftHttpBidApply,
|
|
199
|
+
persistCaseOpenApiViaBuilderApi,
|
|
121
200
|
applyCaseBidViaBuilderApi
|
|
122
201
|
};
|